diff options
author | Artur Signell <artur@vaadin.com> | 2013-03-25 23:37:45 +0200 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2013-04-03 06:42:55 +0000 |
commit | ce2df6d10370c669a512e96f0693fc37cf02aca1 (patch) | |
tree | 5b12cf84ba967b17d96c0a5913030e23a491eeb6 | |
parent | 9586a30b64bfd1e8645574d9d7b6568d52dc9e25 (diff) | |
download | vaadin-framework-ce2df6d10370c669a512e96f0693fc37cf02aca1.tar.gz vaadin-framework-ce2df6d10370c669a512e96f0693fc37cf02aca1.zip |
Enable setting loading indicator delays from the server (#7448)
* Refactored LoadingIndicator to a separate class on client side to enable customization and to remove clutter from ApplicationConnection
Change-Id: I12e94294beed9c65a5710bdfe2486bc0f1b92bd9
-rw-r--r-- | client/src/com/vaadin/client/ApplicationConnection.java | 104 | ||||
-rw-r--r-- | client/src/com/vaadin/client/VLoadingIndicator.java | 218 | ||||
-rw-r--r-- | client/src/com/vaadin/client/ui/ui/UIConnector.java | 10 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/LoadingIndicator.java | 159 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/UI.java | 12 | ||||
-rw-r--r-- | shared/src/com/vaadin/shared/ui/ui/UIState.java | 7 | ||||
-rw-r--r-- | uitest/src/com/vaadin/tests/components/ui/LoadingIndicatorConfigurationTest.java | 99 |
7 files changed, 526 insertions, 83 deletions
diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index a043ec6c0b..d59abc892a 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -177,11 +177,6 @@ public class ApplicationConnection { private VContextMenu contextMenu = null; - private Timer loadTimer; - private Timer loadTimer2; - private Timer loadTimer3; - private Element loadElement; - private final UIConnector uIConnector; protected boolean applicationRunning = false; @@ -378,6 +373,8 @@ public class ApplicationConnection { private CommunicationErrorHandler communicationErrorDelegate = null; + private VLoadingIndicator loadingIndicator; + public static class MultiStepDuration extends Duration { private int previousStep = elapsedMillis(); @@ -404,6 +401,8 @@ public class ApplicationConnection { layoutManager = GWT.create(LayoutManager.class); layoutManager.setConnection(this); tooltip = GWT.create(VTooltip.class); + loadingIndicator = GWT.create(VLoadingIndicator.class); + loadingIndicator.setConnection(this); } public void init(WidgetSet widgetSet, ApplicationConfiguration cnf) { @@ -436,7 +435,7 @@ public class ApplicationConnection { tooltip.setOwner(uIConnector.getWidget()); - showLoadingIndicator(); + getLoadingIndicator().trigger(); scheduleHeartbeat(); @@ -987,7 +986,7 @@ public class ApplicationConnection { */ protected boolean isCSSLoaded() { return cssLoaded - || DOM.getElementPropertyInt(loadElement, "offsetHeight") != 0; + || getLoadingIndicator().getElement().getOffsetHeight() != 0; } /** @@ -1085,25 +1084,7 @@ public class ApplicationConnection { } hasActiveRequest = true; requestStartTime = new Date(); - // show initial throbber - if (loadTimer == null) { - loadTimer = new Timer() { - @Override - public void run() { - /* - * IE7 does not properly cancel the event with - * loadTimer.cancel() so we have to check that we really - * should make it visible - */ - if (loadTimer != null) { - showLoadingIndicator(); - } - - } - }; - // First one kicks in at 300ms - } - loadTimer.schedule(300); + loadingIndicator.trigger(); eventBus.fireEvent(new RequestStartingEvent(this)); } @@ -1129,7 +1110,7 @@ public class ApplicationConnection { @Override public void execute() { if (!hasActiveRequest()) { - hideLoadingIndicator(); + getLoadingIndicator().hide(); // If on Liferay and session expiration management is in // use, extend session duration on each request. @@ -1182,54 +1163,6 @@ public class ApplicationConnection { } } - private void showLoadingIndicator() { - // show initial throbber - if (loadElement == null) { - loadElement = DOM.createDiv(); - DOM.setStyleAttribute(loadElement, "position", "absolute"); - DOM.appendChild(uIConnector.getWidget().getElement(), loadElement); - VConsole.log("inserting load indicator"); - } - DOM.setElementProperty(loadElement, "className", "v-loading-indicator"); - DOM.setStyleAttribute(loadElement, "display", "block"); - // Initialize other timers - loadTimer2 = new Timer() { - @Override - public void run() { - DOM.setElementProperty(loadElement, "className", - "v-loading-indicator-delay"); - } - }; - // Second one kicks in at 1500ms from request start - loadTimer2.schedule(1200); - - loadTimer3 = new Timer() { - @Override - public void run() { - DOM.setElementProperty(loadElement, "className", - "v-loading-indicator-wait"); - } - }; - // Third one kicks in at 5000ms from request start - loadTimer3.schedule(4700); - } - - private void hideLoadingIndicator() { - if (loadTimer != null) { - loadTimer.cancel(); - loadTimer = null; - } - if (loadTimer2 != null) { - loadTimer2.cancel(); - loadTimer3.cancel(); - loadTimer2 = null; - loadTimer3 = null; - } - if (loadElement != null) { - DOM.setStyleAttribute(loadElement, "display", "none"); - } - } - /** * Checks if deferred commands are (potentially) still being executed as a * result of an update from the server. Returns true if a deferred command @@ -1252,19 +1185,24 @@ public class ApplicationConnection { } /** + * Returns the loading indicator used by this ApplicationConnection + * + * @return The loading indicator for this ApplicationConnection + */ + public VLoadingIndicator getLoadingIndicator() { + return loadingIndicator; + } + + /** * Determines whether or not the loading indicator is showing. * * @return true if the loading indicator is visible + * @deprecated As of 7.1. Use {@link #getLoadingIndicator()} and + * {@link VLoadingIndicator#isVisible()}.isVisible() instead. */ + @Deprecated public boolean isLoadingIndicatorVisible() { - if (loadElement == null) { - return false; - } - if (loadElement.getStyle().getProperty("display").equals("none")) { - return false; - } - - return true; + return getLoadingIndicator().isVisible(); } private static native ValueMap parseJSONResponse(String jsonText) diff --git a/client/src/com/vaadin/client/VLoadingIndicator.java b/client/src/com/vaadin/client/VLoadingIndicator.java new file mode 100644 index 0000000000..ca29d6a042 --- /dev/null +++ b/client/src/com/vaadin/client/VLoadingIndicator.java @@ -0,0 +1,218 @@ +package com.vaadin.client; + +import com.google.gwt.dom.client.Style.Display; +import com.google.gwt.dom.client.Style.Position; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Timer; + +/** + * Class representing the loading indicator for Vaadin applications. The loading + * indicator has four states: "triggered", "initial", "delay" and "wait". When + * {@link #trigger()} is called the indicator moves to its "triggered" state and + * then transitions from one state to the next when the timeouts specified using + * the set*StateDelay methods occur. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class VLoadingIndicator { + + private static final String PRIMARY_STYLE_NAME = "v-loading-indicator"; + + private ApplicationConnection connection; + + private int initialStateDelay = 300; + private int delayStateDelay = 1500; + private int waitStateDelay = 5000; + + private Timer initialTimer = new Timer() { + @Override + public void run() { + show(); + } + }; + private Timer delayStateTimer = new Timer() { + @Override + public void run() { + getElement().setClassName(PRIMARY_STYLE_NAME + "-delay"); + } + }; + private Timer waitStateTimer = new Timer() { + @Override + public void run() { + getElement().setClassName(PRIMARY_STYLE_NAME + "-wait"); + } + }; + + private Element element; + + /** + * Returns the delay (in ms) which must pass before the loading indicator + * moves into the "initial" state and is shown to the user + * + * @return The delay (in ms) until moving into the "initial" state. Counted + * from when {@link #trigger()} is called. + */ + public int getInitialStateDelay() { + return initialStateDelay; + } + + /** + * Sets the delay (in ms) which must pass before the loading indicator moves + * into the "initial" state and is shown to the user + * + * @param initialStateDelay + * The delay (in ms) until moving into the "initial" state. + * Counted from when {@link #trigger()} is called. + */ + public void setInitialStateDelay(int initialStateDelay) { + this.initialStateDelay = initialStateDelay; + } + + /** + * Returns the delay (in ms) which must pass before the loading indicator + * moves to its "delay" state. + * + * @return The delay (in ms) until the loading indicator moves into its + * "delay" state. Counted from when {@link #trigger()} is called. + */ + public int getDelayStateDelay() { + return delayStateDelay; + } + + /** + * Sets the delay (in ms) which must pass before the loading indicator moves + * to its "delay" state. + * + * @param delayStateDelay + * The delay (in ms) until the loading indicator moves into its + * "delay" state. Counted from when {@link #trigger()} is called. + */ + public void setDelayStateDelay(int delayStateDelay) { + this.delayStateDelay = delayStateDelay; + } + + /** + * Returns the delay (in ms) which must pass before the loading indicator + * moves to its "wait" state. + * + * @return The delay (in ms) until the loading indicator moves into its + * "wait" state. Counted from when {@link #trigger()} is called. + */ + public int getWaitStateDelay() { + return waitStateDelay; + } + + /** + * Sets the delay (in ms) which must pass before the loading indicator moves + * to its "wait" state. + * + * @param loadingIndicatorThirdDelay + * The delay (in ms) from the event until changing the loading + * indicator into its "wait" state. Counted from when + * {@link #trigger()} is called. + */ + public void setWaitStateDelay(int loadingIndicatorThirdDelay) { + waitStateDelay = loadingIndicatorThirdDelay; + } + + /** + * Triggers displaying of this loading indicator. The loading indicator will + * actually be shown by {@link #show()} when the initial delay (as specified + * by {@link #getInitialStateDelay()}) has passed. + * <p> + * The loading indicator will be hidden if shown when calling this method. + * </p> + */ + public void trigger() { + hide(); + initialTimer.schedule(getInitialStateDelay()); + } + + /** + * Shows the loading indicator in its standard state and triggers timers for + * transitioning into the "delay" and "wait" states. + */ + public void show() { + // Reset possible style name and display mode + getElement().setClassName(PRIMARY_STYLE_NAME); + getElement().getStyle().setDisplay(Display.BLOCK); + + // Schedule the "delay" loading indicator + int delayStateTimerDelay = getDelayStateDelay() + - getInitialStateDelay(); + if (delayStateTimerDelay >= 0) { + delayStateTimer.schedule(delayStateTimerDelay); + } + + // Schedule the "wait" loading indicator + int waitStateTimerDelay = getWaitStateDelay() - getInitialStateDelay(); + if (waitStateTimerDelay >= 0) { + waitStateTimer.schedule(waitStateTimerDelay); + } + } + + /** + * Returns the {@link ApplicationConnection} which uses this loading + * indicator + * + * @return The ApplicationConnection for this loading indicator + */ + public ApplicationConnection getConnection() { + return connection; + } + + /** + * Sets the {@link ApplicationConnection} which uses this loading indicator. + * Only used internally. + * + * @param connection + * The ApplicationConnection for this loading indicator + */ + void setConnection(ApplicationConnection connection) { + this.connection = connection; + } + + /** + * Hides the loading indicator (if visible). Cancels any possibly running + * timers. + */ + public void hide() { + initialTimer.cancel(); + delayStateTimer.cancel(); + waitStateTimer.cancel(); + + getElement().getStyle().setDisplay(Display.NONE); + } + + /** + * Returns whether or not the loading indicator is showing. + * + * @return true if the loading indicator is visible, false otherwise + */ + public boolean isVisible() { + if (getElement().getStyle().getDisplay() + .equals(Display.NONE.getCssName())) { + return false; + } + + return true; + } + + /** + * Returns the root element of the loading indicator + * + * @return The loading indicator DOM element + */ + public Element getElement() { + if (element == null) { + element = DOM.createDiv(); + element.getStyle().setPosition(Position.ABSOLUTE); + getConnection().getUIConnector().getWidget().getElement() + .appendChild(element); + } + return element; + } + +} diff --git a/client/src/com/vaadin/client/ui/ui/UIConnector.java b/client/src/com/vaadin/client/ui/ui/UIConnector.java index b8b7786d21..f4524882fa 100644 --- a/client/src/com/vaadin/client/ui/ui/UIConnector.java +++ b/client/src/com/vaadin/client/ui/ui/UIConnector.java @@ -615,5 +615,15 @@ public class UIConnector extends AbstractSingleComponentContainerConnector getConnection().getVTooltip().setMaxWidth( getState().tooltipConfiguration.maxWidth); } + + if (stateChangeEvent + .hasPropertyChanged("loadingIndicatorConfiguration")) { + getConnection().getLoadingIndicator().setInitialStateDelay( + getState().loadingIndicatorConfiguration.initialDelay); + getConnection().getLoadingIndicator().setWaitStateDelay( + getState().loadingIndicatorConfiguration.waitStateDelay); + getConnection().getLoadingIndicator().setDelayStateDelay( + getState().loadingIndicatorConfiguration.delayStateDelay); + } } } diff --git a/server/src/com/vaadin/ui/LoadingIndicator.java b/server/src/com/vaadin/ui/LoadingIndicator.java new file mode 100644 index 0000000000..5740ee772d --- /dev/null +++ b/server/src/com/vaadin/ui/LoadingIndicator.java @@ -0,0 +1,159 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui; + +import java.io.Serializable; + +import com.vaadin.shared.ui.ui.UIState.LoadingIndicatorConfiguration; + +/** + * Provides method for configuring the loading indicator. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public interface LoadingIndicator extends Serializable { + /** + * Sets the delay before the loading indicator is shown. The default is + * 300ms. + * + * @param initialDelay + * The initial delay (in ms) + */ + public void setInitialDelay(int initialDelay); + + /** + * Returns the delay before the loading indicator is shown. + * + * @return The initial delay (in ms) + */ + public int getInitialDelay(); + + /** + * Sets the delay before the loading indicator goes into the "delay" state. + * The delay is calculated from the time when the loading indicator was + * triggered. The default is 1500ms. + * + * @param delayStateDelay + * The delay before going into the "delay" state (in ms) + */ + public void setDelayStateDelay(int delayStateDelay); + + /** + * Returns the delay before the loading indicator goes into the "delay" + * state. The delay is calculated from the time when the loading indicator + * was triggered. + * + * @return The delay before going into the "delay" state (in ms) + */ + public int getDelayStateDelay(); + + /** + * Sets the delay before the loading indicator goes into the "wait" state. + * The delay is calculated from the time when the loading indicator was + * triggered. The default is 5000ms. + * + * @param waitStateDelay + * The delay before going into the "wait" state (in ms) + */ + public void setWaitStateDelay(int waitStateDelay); + + /** + * Returns the delay before the loading indicator goes into the "wait" + * state. The delay is calculated from the time when the loading indicator + * was triggered. + * + * @return The delay before going into the "wait" state (in ms) + */ + public int getWaitStateDelay(); +} + +class LoadingIndicatorImpl implements LoadingIndicator { + private UI ui; + + public LoadingIndicatorImpl(UI ui) { + this.ui = ui; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.LoadingIndicator#setInitialDelay(int) + */ + @Override + public void setInitialDelay(int initialDelay) { + getState().initialDelay = initialDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.LoadingIndicator#getInitialDelay() + */ + @Override + public int getInitialDelay() { + return getState(false).initialDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.LoadingIndicator#setDelayStateDelay(int) + */ + @Override + public void setDelayStateDelay(int delayStateDelay) { + getState().delayStateDelay = delayStateDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.LoadingIndicator#getDelayStateDelay() + */ + @Override + public int getDelayStateDelay() { + return getState(false).delayStateDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.LoadingIndicator#setWaitStateDelay(int) + */ + @Override + public void setWaitStateDelay(int waitStateDelay) { + getState().waitStateDelay = waitStateDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.LoadingIndicator#getWaitStateDelay() + */ + @Override + public int getWaitStateDelay() { + return getState(false).waitStateDelay; + } + + private LoadingIndicatorConfiguration getState() { + return ui.getState().loadingIndicatorConfiguration; + } + + private LoadingIndicatorConfiguration getState(boolean markAsDirty) { + return ui.getState(markAsDirty).loadingIndicatorConfiguration; + } + +} diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index e9499da7f3..a20c2b2087 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -116,6 +116,8 @@ public abstract class UI extends AbstractSingleComponentContainer implements private Page page = new Page(this); + private LoadingIndicator loadingIndicator = new LoadingIndicatorImpl(this); + /** * Scroll Y position. */ @@ -1106,4 +1108,14 @@ public abstract class UI extends AbstractSingleComponentContainer implements public Tooltip getTooltip() { return tooltip; } + + /** + * Retrieves the object used for configuring the loading indicator. + * + * @return The instance used for configuring the loading indicator + */ + public LoadingIndicator getLoadingIndicator() { + return loadingIndicator; + } + } diff --git a/shared/src/com/vaadin/shared/ui/ui/UIState.java b/shared/src/com/vaadin/shared/ui/ui/UIState.java index cc6897dd0d..d5ee4c30e6 100644 --- a/shared/src/com/vaadin/shared/ui/ui/UIState.java +++ b/shared/src/com/vaadin/shared/ui/ui/UIState.java @@ -21,6 +21,13 @@ import com.vaadin.shared.ui.TabIndexState; public class UIState extends TabIndexState { public TooltipConfiguration tooltipConfiguration = new TooltipConfiguration(); + public LoadingIndicatorConfiguration loadingIndicatorConfiguration = new LoadingIndicatorConfiguration(); + + public static class LoadingIndicatorConfiguration implements Serializable { + public int initialDelay = 300; + public int delayStateDelay = 1500; + public int waitStateDelay = 5000; + } public static class TooltipConfiguration implements Serializable { public int openDelay = 750; diff --git a/uitest/src/com/vaadin/tests/components/ui/LoadingIndicatorConfigurationTest.java b/uitest/src/com/vaadin/tests/components/ui/LoadingIndicatorConfigurationTest.java new file mode 100644 index 0000000000..0f15ff2fe0 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/LoadingIndicatorConfigurationTest.java @@ -0,0 +1,99 @@ +package com.vaadin.tests.components.ui; + +import com.vaadin.data.Property; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.NativeButton; +import com.vaadin.ui.TextField; + +public class LoadingIndicatorConfigurationTest extends AbstractTestUIWithLog { + + private TextField initialDelay; + private TextField delayStateDelay; + private TextField waitStateDelay; + + @Override + protected void setup(VaadinRequest request) { + final TextField delayField = new TextField("Delay (ms)"); + delayField.setConverter(Integer.class); + delayField.setConvertedValue(1000); + + NativeButton delayButton = new NativeButton("Wait"); + delayButton.addClickListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + try { + Thread.sleep((Integer) delayField.getConvertedValue()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + + initialDelay = createIntegerTextField("Initial delay (ms)", + getState().loadingIndicatorConfiguration.initialDelay); + initialDelay.addValueChangeListener(new Property.ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + getLoadingIndicator().setInitialDelay( + (Integer) initialDelay.getConvertedValue()); + } + }); + delayStateDelay = createIntegerTextField("Delay state delay (ms)", + getState().loadingIndicatorConfiguration.delayStateDelay); + delayStateDelay + .addValueChangeListener(new Property.ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + getLoadingIndicator().setDelayStateDelay( + (Integer) delayStateDelay.getConvertedValue()); + } + }); + waitStateDelay = createIntegerTextField("Wait state delay (ms)", + getState().loadingIndicatorConfiguration.waitStateDelay); + waitStateDelay + .addValueChangeListener(new Property.ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + getLoadingIndicator().setWaitStateDelay( + (Integer) waitStateDelay.getConvertedValue()); + } + }); + + getLayout() + .addComponents(initialDelay, delayStateDelay, waitStateDelay); + + HorizontalLayout hl = new HorizontalLayout(); + hl.setMargin(true); + hl.setDefaultComponentAlignment(Alignment.BOTTOM_RIGHT); + hl.addComponents(delayField, delayButton); + addComponent(hl); + + } + + private TextField createIntegerTextField(String caption, int initialValue) { + TextField tf = new TextField(caption); + tf.setId(caption); + tf.setConverter(Integer.class); + tf.setImmediate(true); + tf.setConvertedValue(initialValue); + return tf; + } + + @Override + protected String getTestDescription() { + return "Tests that loading indicator delay can be configured"; + } + + @Override + protected Integer getTicketNumber() { + return 7448; + } + +} |