* Refactored LoadingIndicator to a separate class on client side to enable customization and to remove clutter from ApplicationConnection Change-Id: I12e94294beed9c65a5710bdfe2486bc0f1b92bd9tags/7.1.0.beta1
@@ -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 | |||
@@ -1251,20 +1184,25 @@ 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) |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; |
@@ -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; | |||
} | |||
} |