Browse Source

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
tags/7.1.0.beta1
Artur Signell 11 years ago
parent
commit
ce2df6d103

+ 21
- 83
client/src/com/vaadin/client/ApplicationConnection.java View File

@@ -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)

+ 218
- 0
client/src/com/vaadin/client/VLoadingIndicator.java View File

@@ -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;
}

}

+ 10
- 0
client/src/com/vaadin/client/ui/ui/UIConnector.java View File

@@ -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);
}
}
}

+ 159
- 0
server/src/com/vaadin/ui/LoadingIndicator.java View File

@@ -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;
}

}

+ 12
- 0
server/src/com/vaadin/ui/UI.java View File

@@ -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;
}

}

+ 7
- 0
shared/src/com/vaadin/shared/ui/ui/UIState.java View File

@@ -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;

+ 99
- 0
uitest/src/com/vaadin/tests/components/ui/LoadingIndicatorConfigurationTest.java View File

@@ -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;
}

}

Loading…
Cancel
Save