Adding a LoadingStateEvent to CellList and CellTable so users can receive an event when the data is loaded. Previously, LoadingState was a package protected enum used to show the loading indicator in CellTable and empty list message in CellList. Now, LoadingState is a first class citizen (an interface so users can define their own loading state). Both CellList and CellTable now support setting a custom loading indicator Widget and a custom Widget to display when the table is empty.
Review at http://gwt-code-reviews.appspot.com/1338809
Review by: pdr@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9687 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/cellview/client/AbstractHasData.java b/user/src/com/google/gwt/user/cellview/client/AbstractHasData.java
index 7275c1a..6c94271 100644
--- a/user/src/com/google/gwt/user/cellview/client/AbstractHasData.java
+++ b/user/src/com/google/gwt/user/cellview/client/AbstractHasData.java
@@ -20,6 +20,7 @@
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
+import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
@@ -28,7 +29,7 @@
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
-import com.google.gwt.user.cellview.client.HasDataPresenter.LoadingState;
+import com.google.gwt.user.cellview.client.LoadingStateChangeEvent.LoadingState;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Focusable;
@@ -125,7 +126,7 @@
public void setLoadingState(LoadingState state) {
hasData.isRefreshing = true;
- hasData.setLoadingState(state);
+ hasData.onLoadingStateChanged(state);
hasData.isRefreshing = false;
}
@@ -136,8 +137,7 @@
// Use an anonymous class to override ValueChangeEvents's protected
// constructor. We can't call ValueChangeEvent.fire() because this class
// doesn't implement HasValueChangeHandlers.
- hasData.fireEvent(new ValueChangeEvent<List<T>>(
- hasData.getVisibleItems()) {
+ hasData.fireEvent(new ValueChangeEvent<List<T>>(hasData.getVisibleItems()) {
});
}
}
@@ -279,8 +279,7 @@
CellBasedWidgetImpl.get().sinkEvents(this, eventTypes);
// Add a default selection event manager.
- selectionManagerReg = addCellPreviewHandler(
- DefaultSelectionEventManager.<T> createDefaultManager());
+ selectionManagerReg = addCellPreviewHandler(DefaultSelectionEventManager.<T> createDefaultManager());
}
public HandlerRegistration addCellPreviewHandler(
@@ -288,6 +287,18 @@
return presenter.addCellPreviewHandler(handler);
}
+ /**
+ * Add a {@link LoadingStateChangeEvent.Handler} to be notified of changes in
+ * the loading state.
+ *
+ * @param handler the handle
+ * @return the registration for the handler
+ */
+ public HandlerRegistration addLoadingStateChangeHandler(
+ LoadingStateChangeEvent.Handler handler) {
+ return presenter.addLoadingStateChangeHandler(handler);
+ }
+
public HandlerRegistration addRangeChangeHandler(
RangeChangeEvent.Handler handler) {
return presenter.addRangeChangeHandler(handler);
@@ -750,6 +761,16 @@
protected void onFocus() {
}
+ /**
+ * Called when the loading state changes. By default, this implementation
+ * fires a {@link LoadingStateChangeEvent}.
+ *
+ * @param state the new loading state
+ */
+ protected void onLoadingStateChanged(LoadingState state) {
+ fireEvent(new LoadingStateChangeEvent(state));
+ }
+
@Override
protected void onUnload() {
isFocused = false;
@@ -870,15 +891,51 @@
return addHandler(handler, ValueChangeEvent.getType());
}
+ /**
+ * Adopt the specified widget.
+ *
+ * @param child the child to adopt
+ */
+ native void adopt(Widget child) /*-{
+ child.@com.google.gwt.user.client.ui.Widget::setParent(Lcom/google/gwt/user/client/ui/Widget;)(this);
+ }-*/;
+
+ /**
+ * Attach a child.
+ *
+ * @param child the child to attach
+ */
+ native void doAttach(Widget child) /*-{
+ child.@com.google.gwt.user.client.ui.Widget::onAttach()();
+ }-*/;
+
+ /**
+ * Detach a child.
+ *
+ * @param child the child to detach
+ */
+ native void doDetach(Widget child) /*-{
+ child.@com.google.gwt.user.client.ui.Widget::onDetach()();
+ }-*/;
+
HasDataPresenter<T> getPresenter() {
return presenter;
}
/**
- * Set the current loading state of the data.
- *
- * @param state the loading state
+ * Show or hide an element.
+ *
+ * @param element the element
+ * @param show true to show, false to hide
*/
- void setLoadingState(LoadingState state) {
+ void showOrHide(Element element, boolean show) {
+ if (element == null) {
+ return;
+ }
+ if (show) {
+ element.getStyle().clearDisplay();
+ } else {
+ element.getStyle().setDisplay(Display.NONE);
+ }
}
}
diff --git a/user/src/com/google/gwt/user/cellview/client/CellList.java b/user/src/com/google/gwt/user/cellview/client/CellList.java
index c7100d1..7a85a9d 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellList.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellList.java
@@ -1,12 +1,12 @@
/*
* Copyright 2010 Google Inc.
- *
+ *
* 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
@@ -24,7 +24,6 @@
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
-import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.CssResource.ImportedWithPrefix;
@@ -35,18 +34,24 @@
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
-import com.google.gwt.user.cellview.client.HasDataPresenter.LoadingState;
+import com.google.gwt.user.cellview.client.LoadingStateChangeEvent.LoadingState;
import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.AttachDetachException;
+import com.google.gwt.user.client.ui.DeckPanel;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.CellPreviewEvent;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.SelectionModel;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* A single column list of cells.
- *
+ *
* <p>
* <h3>Examples</h3>
* <dl>
@@ -58,7 +63,7 @@
* <dd>{@example com.google.gwt.examples.view.KeyProviderExample}</dd>
* </dl>
* </p>
- *
+ *
* @param <T> the data type of list items
*/
public class CellList<T> extends AbstractHasData<T> {
@@ -148,9 +153,14 @@
private final Cell<T> cell;
private boolean cellIsEditing;
private final Element childContainer;
-
private SafeHtml emptyListMessage = SafeHtmlUtils.fromSafeConstant("");
- private final Element emptyMessageElem;
+ private final SimplePanel emptyListWidgetContainer = new SimplePanel();
+ private final SimplePanel loadingIndicatorContainer = new SimplePanel();
+
+ /**
+ * A {@link DeckPanel} to hold widgets associated with various loading states.
+ */
+ private final DeckPanel messagesPanel = new DeckPanel();
private final Style style;
@@ -158,7 +168,7 @@
/**
* Construct a new {@link CellList}.
- *
+ *
* @param cell the cell used to render each item
*/
public CellList(final Cell<T> cell) {
@@ -167,7 +177,7 @@
/**
* Construct a new {@link CellList} with the specified {@link Resources}.
- *
+ *
* @param cell the cell used to render each item
* @param resources the resources used for this widget
*/
@@ -176,26 +186,28 @@
}
/**
- * Construct a new {@link CellList} with the specified {@link ProvidesKey key provider}.
- *
+ * Construct a new {@link CellList} with the specified {@link ProvidesKey key
+ * provider}.
+ *
* @param cell the cell used to render each item
* @param keyProvider an instance of ProvidesKey<T>, or null if the record
- * object should act as its own key
+ * object should act as its own key
*/
public CellList(final Cell<T> cell, ProvidesKey<T> keyProvider) {
this(cell, getDefaultResources(), keyProvider);
}
/**
- * Construct a new {@link CellList} with the specified {@link Resources}
- * and {@link ProvidesKey key provider}.
- *
+ * Construct a new {@link CellList} with the specified {@link Resources} and
+ * {@link ProvidesKey key provider}.
+ *
* @param cell the cell used to render each item
* @param resources the resources used for this widget
* @param keyProvider an instance of ProvidesKey<T>, or null if the record
- * object should act as its own key
+ * object should act as its own key
*/
- public CellList(final Cell<T> cell, Resources resources, ProvidesKey<T> keyProvider) {
+ public CellList(final Cell<T> cell, Resources resources,
+ ProvidesKey<T> keyProvider) {
super(Document.get().createDivElement(), DEFAULT_PAGE_SIZE, keyProvider);
this.cell = cell;
this.style = resources.cellListStyle();
@@ -207,15 +219,16 @@
addStyleName(widgetStyle);
}
- // Create the DOM hierarchy.
+ // Add the child container.
childContainer = Document.get().createDivElement();
-
- emptyMessageElem = Document.get().createDivElement();
- showOrHide(emptyMessageElem, false);
-
DivElement outerDiv = getElement().cast();
outerDiv.appendChild(childContainer);
- outerDiv.appendChild(emptyMessageElem);
+
+ // Attach the message panel.
+ outerDiv.appendChild(messagesPanel.getElement());
+ adopt(messagesPanel);
+ messagesPanel.add(emptyListWidgetContainer);
+ messagesPanel.add(loadingIndicatorContainer);
// Sink events that the cell consumes.
CellBasedWidgetImpl.get().sinkEvents(this, cell.getConsumedEvents());
@@ -223,18 +236,38 @@
/**
* Get the message that is displayed when there is no data.
- *
+ *
* @return the empty message
* @see #setEmptyListMessage(SafeHtml)
+ * @deprecated as of GWT 2.3, use {@link #getEmptyListWidget()} instead
*/
+ @Deprecated
public SafeHtml getEmptyListMessage() {
return emptyListMessage;
}
/**
+ * Get the widget displayed when the list has no rows.
+ *
+ * @return the empty list widget
+ */
+ public Widget getEmptyListWidget() {
+ return emptyListWidgetContainer.getWidget();
+ }
+
+ /**
+ * Get the widget displayed when the data is loading.
+ *
+ * @return the loading indicator
+ */
+ public Widget getLoadingIndicator() {
+ return loadingIndicatorContainer.getWidget();
+ }
+
+ /**
* Get the {@link Element} for the specified index. If the element has not
* been created, null is returned.
- *
+ *
* @param indexOnPage the index on the page
* @return the element, or null if it doesn't exists
* @throws IndexOutOfBoundsException if the index is outside of the current
@@ -251,18 +284,40 @@
/**
* Set the message to display when there is no data.
- *
+ *
* @param html the message to display when there are no results
* @see #getEmptyListMessage()
+ * @deprecated as of GWT 2.3, use
+ * {@link #setEmptyDataWidget(com.google.gwt.user.client.ui.Widget)}
+ * instead
*/
+ @Deprecated
public void setEmptyListMessage(SafeHtml html) {
this.emptyListMessage = html;
- emptyMessageElem.setInnerHTML(html.asString());
+ setEmptyListWidget(html == null ? null : new HTML(html));
+ }
+
+ /**
+ * Set the widget to display when the list has no rows.
+ *
+ * @param widget the empty data widget
+ */
+ public void setEmptyListWidget(Widget widget) {
+ emptyListWidgetContainer.setWidget(widget);
+ }
+
+ /**
+ * Set the widget to display when the data is loading.
+ *
+ * @param widget the loading indicator
+ */
+ public void setLoadingIndicator(Widget widget) {
+ loadingIndicatorContainer.setWidget(widget);
}
/**
* Set the value updater to use when cells modify items.
- *
+ *
* @param valueUpdater the {@link ValueUpdater}
*/
public void setValueUpdater(ValueUpdater<T> valueUpdater) {
@@ -274,6 +329,24 @@
return cell.dependsOnSelection();
}
+ @Override
+ protected void doAttachChildren() {
+ try {
+ doAttach(messagesPanel);
+ } catch (Throwable e) {
+ throw new AttachDetachException(Collections.singleton(e));
+ }
+ }
+
+ @Override
+ protected void doDetachChildren() {
+ try {
+ doDetach(messagesPanel);
+ } catch (Throwable e) {
+ throw new AttachDetachException(Collections.singleton(e));
+ }
+ }
+
/**
* Called when a user action triggers selection.
*
@@ -323,7 +396,7 @@
/**
* Get the parent element that wraps the cell from the list item. Override
* this method if you add structure to the element.
- *
+ *
* @param item the row element that wraps the list item
* @return the parent element of the cell
*/
@@ -429,6 +502,35 @@
}
}
+ /**
+ * Called when the loading state changes.
+ *
+ * @param state the new loading state
+ */
+ @Override
+ protected void onLoadingStateChanged(LoadingState state) {
+ Widget message = null;
+ if (state == LoadingState.LOADING) {
+ // Loading indicator.
+ message = loadingIndicatorContainer;
+ } else if (state == LoadingState.LOADED && getPresenter().isEmpty()) {
+ // Empty table.
+ message = emptyListWidgetContainer;
+ }
+
+ // Switch out the message to display.
+ if (message != null) {
+ messagesPanel.showWidget(messagesPanel.getWidgetIndex(message));
+ }
+
+ // Show the correct container.
+ showOrHide(getChildContainer(), message == null);
+ messagesPanel.setVisible(message != null);
+
+ // Fire an event.
+ super.onLoadingStateChanged(state);
+ }
+
@Override
protected void renderRowValues(SafeHtmlBuilder sb, List<T> values, int start,
SelectionModel<? super T> selectionModel) {
@@ -515,24 +617,4 @@
protected void setSelected(Element elem, boolean selected) {
setStyleName(elem, style.cellListSelectedItem(), selected);
}
-
- @Override
- void setLoadingState(LoadingState state) {
- showOrHide(emptyMessageElem, state == LoadingState.EMPTY);
- // TODO(jlabanca): Add a loading icon.
- }
-
- /**
- * Show or hide an element.
- *
- * @param element the element
- * @param show true to show, false to hide
- */
- private void showOrHide(Element element, boolean show) {
- if (show) {
- element.getStyle().clearDisplay();
- } else {
- element.getStyle().setDisplay(Display.NONE);
- }
- }
}
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTable.css b/user/src/com/google/gwt/user/cellview/client/CellTable.css
index 6a73c90..061aba8 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellTable.css
+++ b/user/src/com/google/gwt/user/cellview/client/CellTable.css
@@ -132,7 +132,6 @@
border: selectionBorderWidth solid #d7dde8;
}
-@sprite .cellTableLoading {
- gwt-image: 'cellTableLoading';
+.cellTableLoading {
margin: 30px;
}
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTable.java b/user/src/com/google/gwt/user/cellview/client/CellTable.java
index 4da9d35..c01d333 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellTable.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellTable.java
@@ -25,7 +25,6 @@
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.NodeList;
-import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.TableLayout;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.TableCellElement;
@@ -47,11 +46,15 @@
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.cellview.client.ColumnSortList.ColumnSortInfo;
-import com.google.gwt.user.cellview.client.HasDataPresenter.LoadingState;
+import com.google.gwt.user.cellview.client.LoadingStateChangeEvent.LoadingState;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.DeckPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
import com.google.gwt.user.client.ui.HasVerticalAlignment.VerticalAlignmentConstant;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.CellPreviewEvent;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.SelectionModel;
@@ -317,9 +320,6 @@
@Template("<div style=\"outline:none;\" tabindex=\"{0}\" accessKey=\"{1}\">{2}</div>")
SafeHtml divFocusableWithKey(int tabIndex, char accessKey, SafeHtml contents);
- @Template("<div class=\"{0}\"></div>")
- SafeHtml loading(String loading);
-
@Template("<table><tbody>{0}</tbody></table>")
SafeHtml tbody(SafeHtml rowHtml);
@@ -493,6 +493,7 @@
*/
private boolean dependsOnSelection;
+ private final SimplePanel emptyTableWidgetContainer = new SimplePanel();
private final List<Header<?>> footers = new ArrayList<Header<?>>();
/**
@@ -510,6 +511,12 @@
private boolean isInteractive;
private int keyboardSelectedColumn = 0;
+ private final SimplePanel loadingIndicatorContainer = new SimplePanel();
+
+ /**
+ * A {@link DeckPanel} to hold widgets associated with various loading states.
+ */
+ private final DeckPanel messagesPanel = new DeckPanel();
private final Resources resources;
private RowStyles<T> rowStyles;
@@ -520,6 +527,7 @@
private final TableElement table;
private final TableSectionElement tbody;
private final TableSectionElement tbodyLoading;
+ private final TableCellElement tbodyLoadingCell;
private final TableSectionElement tfoot;
private final TableSectionElement thead;
private boolean updatingSortList;
@@ -621,17 +629,23 @@
tfoot = table.createTFoot();
setStyleName(this.style.cellTableWidget());
- // Create the loading indicator.
+ // Attach the messages panel.
{
- TableCellElement td = Document.get().createTDElement();
+ tbodyLoadingCell = Document.get().createTDElement();
TableRowElement tr = Document.get().createTRElement();
tbodyLoading.appendChild(tr);
- tr.appendChild(td);
- td.setAlign("center");
- td.setInnerHTML(template.loading(style.cellTableLoading()).asString());
- setLoadingIconVisible(false);
+ tr.appendChild(tbodyLoadingCell);
+ tbodyLoadingCell.setAlign("center");
+ tbodyLoadingCell.appendChild(messagesPanel.getElement());
+ adopt(messagesPanel);
+ messagesPanel.add(emptyTableWidgetContainer);
+ messagesPanel.add(loadingIndicatorContainer);
+ loadingIndicatorContainer.setStyleName(style.cellTableLoading());
}
+ // Set the default loading indicator.
+ setLoadingIndicator(new Image(resources.cellTableLoading()));
+
// Sink events.
Set<String> eventTypes = new HashSet<String>();
eventTypes.add("mouseover");
@@ -800,6 +814,15 @@
}
/**
+ * Get the widget displayed when the table has no rows.
+ *
+ * @return the empty table widget
+ */
+ public Widget getEmptyTableWidget() {
+ return emptyTableWidgetContainer.getWidget();
+ }
+
+ /**
* Return the height of the table header.
*
* @return an int representing the header height
@@ -810,6 +833,15 @@
}
/**
+ * Get the widget displayed when the data is loading.
+ *
+ * @return the loading indicator
+ */
+ public Widget getLoadingIndicator() {
+ return loadingIndicatorContainer.getWidget();
+ }
+
+ /**
* Get the {@link TableRowElement} for the specified row. If the row element
* has not been created, null is returned.
*
@@ -1066,6 +1098,24 @@
}
/**
+ * Set the widget to display when the table has no rows.
+ *
+ * @param widget the empty table widget
+ */
+ public void setEmptyTableWidget(Widget widget) {
+ emptyTableWidgetContainer.setWidget(widget);
+ }
+
+ /**
+ * Set the widget to display when the data is loading.
+ *
+ * @param widget the loading indicator
+ */
+ public void setLoadingIndicator(Widget widget) {
+ loadingIndicatorContainer.setWidget(widget);
+ }
+
+ /**
* Sets the object used to determine how a row is styled; the change will take
* effect the next time that the table is rendered.
*
@@ -1322,6 +1372,38 @@
}
}
+ /**
+ * Called when the loading state changes.
+ *
+ * @param state the new loading state
+ */
+ @Override
+ protected void onLoadingStateChanged(LoadingState state) {
+ Widget message = null;
+ if (state == LoadingState.LOADING) {
+ // Loading indicator.
+ message = loadingIndicatorContainer;
+ } else if (state == LoadingState.LOADED && getPresenter().isEmpty()) {
+ // Empty table.
+ message = emptyTableWidgetContainer;
+ }
+
+ // Switch out the message to display.
+ if (message != null) {
+ messagesPanel.showWidget(messagesPanel.getWidgetIndex(message));
+ }
+
+ // Adjust the colspan of the messages panel container.
+ tbodyLoadingCell.setColSpan(Math.max(1, columns.size()));
+
+ // Show the correct container.
+ showOrHide(getChildContainer(), message == null);
+ showOrHide(tbodyLoading, message != null);
+
+ // Fire an event.
+ super.onLoadingStateChanged(state);
+ }
+
@Override
protected void renderRowValues(SafeHtmlBuilder sb, List<T> values, int start,
SelectionModel<? super T> selectionModel) {
@@ -1501,11 +1583,6 @@
style.cellTableSelectedRowCell(), selected);
}
- @Override
- void setLoadingState(LoadingState state) {
- setLoadingIconVisible(state == LoadingState.LOADING);
- }
-
/**
* Check that the specified column is within bounds.
*
@@ -1889,26 +1966,6 @@
}
/**
- * Show or hide the loading icon.
- *
- * @param visible true to show, false to hide.
- */
- private void setLoadingIconVisible(boolean visible) {
- // Clear the current data.
- if (visible) {
- tbody.getStyle().setDisplay(Display.NONE);
- } else {
- tbody.getStyle().clearDisplay();
- }
-
- // Update the colspan.
- TableCellElement td = tbodyLoading.getRows().getItem(0).getCells().getItem(
- 0);
- td.setColSpan(Math.max(1, columns.size()));
- setVisible(tbodyLoading, visible);
- }
-
- /**
* Apply a style to a row and all cells in the row.
*
* @param tr the row element
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTableBasic.css b/user/src/com/google/gwt/user/cellview/client/CellTableBasic.css
index c34771c..8f98e26 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellTableBasic.css
+++ b/user/src/com/google/gwt/user/cellview/client/CellTableBasic.css
@@ -137,7 +137,6 @@
background: #d7dde8;
}
-@sprite .cellTableLoading {
- gwt-image: 'cellTableLoading';
+.cellTableLoading {
margin: 30px;
}
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java b/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java
index 22c0938..ae77b7c 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java
@@ -37,8 +37,8 @@
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
-import com.google.gwt.user.cellview.client.HasDataPresenter.LoadingState;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
+import com.google.gwt.user.cellview.client.LoadingStateChangeEvent.LoadingState;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.impl.FocusImpl;
import com.google.gwt.view.client.CellPreviewEvent;
@@ -252,7 +252,8 @@
public void setLoadingState(LoadingState state) {
nodeView.updateImage(state == LoadingState.LOADING);
- showOrHide(nodeView.emptyMessageElem, state == LoadingState.EMPTY);
+ showOrHide(nodeView.emptyMessageElem, state == LoadingState.LOADED
+ && presenter.isEmpty());
}
/**
diff --git a/user/src/com/google/gwt/user/cellview/client/HasDataPresenter.java b/user/src/com/google/gwt/user/cellview/client/HasDataPresenter.java
index ee3789c..b514593 100644
--- a/user/src/com/google/gwt/user/cellview/client/HasDataPresenter.java
+++ b/user/src/com/google/gwt/user/cellview/client/HasDataPresenter.java
@@ -23,7 +23,7 @@
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
-import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
+import com.google.gwt.user.cellview.client.LoadingStateChangeEvent.LoadingState;
import com.google.gwt.view.client.CellPreviewEvent;
import com.google.gwt.view.client.HasData;
import com.google.gwt.view.client.HasKeyProvider;
@@ -83,16 +83,6 @@
}
/**
- * The loading state of the data.
- */
- static enum LoadingState {
- LOADING, // Waiting for data to load.
- PARTIALLY_LOADED, // Partial page data loaded.
- LOADED, // All page data loaded.
- EMPTY; // The data size is 0.
- }
-
- /**
* The view that this presenter presents.
*
* @param <T> the data type
@@ -474,6 +464,11 @@
return view.addHandler(handler, CellPreviewEvent.getType());
}
+ public HandlerRegistration addLoadingStateChangeHandler(
+ LoadingStateChangeEvent.Handler handler) {
+ return view.addHandler(handler, LoadingStateChangeEvent.TYPE);
+ }
+
public HandlerRegistration addRangeChangeHandler(
RangeChangeEvent.Handler handler) {
return view.addHandler(handler, RangeChangeEvent.getType());
@@ -657,6 +652,16 @@
return pendingState != null;
}
+ /**
+ * Check whether or not the data set is empty. That is, the row count is
+ * exactly 0.
+ *
+ * @return true if data set is empty
+ */
+ public boolean isEmpty() {
+ return isRowCountExact() && getRowCount() == 0;
+ }
+
public boolean isRowCountExact() {
return getCurrentState().isRowCountExact();
}
@@ -1512,9 +1517,7 @@
private void updateLoadingState() {
int cacheSize = getVisibleItemCount();
int curPageSize = isRowCountExact() ? getCurrentPageSize() : getPageSize();
- if (getRowCount() == 0 && isRowCountExact()) {
- view.setLoadingState(LoadingState.EMPTY);
- } else if (cacheSize >= curPageSize) {
+ if (cacheSize >= curPageSize) {
view.setLoadingState(LoadingState.LOADED);
} else if (cacheSize == 0) {
view.setLoadingState(LoadingState.LOADING);
diff --git a/user/src/com/google/gwt/user/cellview/client/LoadingStateChangeEvent.java b/user/src/com/google/gwt/user/cellview/client/LoadingStateChangeEvent.java
new file mode 100644
index 0000000..16fc719
--- /dev/null
+++ b/user/src/com/google/gwt/user/cellview/client/LoadingStateChangeEvent.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * 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.google.gwt.user.cellview.client;
+
+import com.google.gwt.event.shared.EventHandler;
+import com.google.gwt.event.shared.GwtEvent;
+
+/**
+ * An event used to indicate that the data loading state has changed.
+ */
+public class LoadingStateChangeEvent extends
+ GwtEvent<LoadingStateChangeEvent.Handler> {
+
+ /**
+ * Implemented by handlers of {@link LoadingStateChangeEvent}.
+ */
+ public interface Handler extends EventHandler {
+ /**
+ * Called when a {@link LoadingStateChangeEvent} is fired.
+ *
+ * @param event the {@link LoadingStateChangeEvent}
+ */
+ void onLoadingStateChanged(LoadingStateChangeEvent event);
+ }
+
+ /**
+ * Represents the current status of the data being loaded.
+ */
+ public static interface LoadingState {
+ /**
+ * Indicates that the data has started to load.
+ */
+ LoadingState LOADING = new DefaultLoadingState();
+
+ /**
+ * Indicates that part of the data set has been loaded, but more data is
+ * still pending.
+ */
+ LoadingState PARTIALLY_LOADED = new DefaultLoadingState();
+
+ /**
+ * Indicates that the data set has been completely loaded.
+ */
+ LoadingState LOADED = new DefaultLoadingState();
+ }
+
+ /**
+ * Default implementation of {@link LoadingState}.
+ */
+ private static class DefaultLoadingState implements LoadingState {
+ }
+
+ /**
+ * A singleton instance of Type.
+ */
+ public static final Type<Handler> TYPE = new Type<Handler>();
+
+ private final LoadingState state;
+
+ /**
+ * Construct a new {@link LoadingStateChangeEvent}.
+ *
+ * @param state the new state
+ */
+ public LoadingStateChangeEvent(LoadingState state) {
+ this.state = state;
+ }
+
+ @Override
+ public Type<Handler> getAssociatedType() {
+ return TYPE;
+ }
+
+ /**
+ * Get the new {@link LoadingState} associated with this event.
+ *
+ * @return the {@link LoadingState}
+ */
+ public LoadingState getLoadingState() {
+ return state;
+ }
+
+ @Override
+ protected void dispatch(Handler handler) {
+ handler.onLoadingStateChanged(this);
+ }
+}
diff --git a/user/test/com/google/gwt/user/cellview/client/CellListTest.java b/user/test/com/google/gwt/user/cellview/client/CellListTest.java
index cc42e22..98b5b9e 100644
--- a/user/test/com/google/gwt/user/cellview/client/CellListTest.java
+++ b/user/test/com/google/gwt/user/cellview/client/CellListTest.java
@@ -21,6 +21,8 @@
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.view.client.ProvidesKey;
@@ -89,6 +91,39 @@
assertEquals(10, rendered.size());
}
+ @SuppressWarnings("deprecation")
+ public void testSetEmptyListWidget() {
+ CellList<String> cellList = createAbstractHasData(new TextCell());
+
+ // Set a widget.
+ Label l = new Label("Empty");
+ cellList.setEmptyListWidget(l);
+ assertEquals(l, cellList.getEmptyListWidget());
+
+ // Set a message.
+ SafeHtml message = SafeHtmlUtils.fromString("empty");
+ cellList.setEmptyListMessage(message);
+ assertEquals(message, cellList.getEmptyListMessage());
+ assertNotSame(l, cellList.getEmptyListWidget());
+
+ // Null widget.
+ cellList.setEmptyListWidget(null);
+ assertNull(cellList.getEmptyListWidget());
+ }
+
+ public void testSetLoadingIndicator() {
+ CellList<String> cellList = createAbstractHasData(new TextCell());
+
+ // Set a widget.
+ Label l = new Label("Loading");
+ cellList.setLoadingIndicator(l);
+ assertEquals(l, cellList.getLoadingIndicator());
+
+ // Null widget.
+ cellList.setLoadingIndicator(null);
+ assertNull(cellList.getLoadingIndicator());
+ }
+
@Override
protected CellList<String> createAbstractHasData(Cell<String> cell) {
return new CellList<String>(cell);
diff --git a/user/test/com/google/gwt/user/cellview/client/CellTableTest.java b/user/test/com/google/gwt/user/cellview/client/CellTableTest.java
index 9147ffd..6c27ac2 100644
--- a/user/test/com/google/gwt/user/cellview/client/CellTableTest.java
+++ b/user/test/com/google/gwt/user/cellview/client/CellTableTest.java
@@ -33,6 +33,7 @@
import com.google.gwt.user.cellview.client.CellTable.Style;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
+import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import java.util.ArrayList;
@@ -424,6 +425,32 @@
assertEquals("", col1.getStyle().getWidth());
}
+ public void testSetEmptyListWidget() {
+ CellTable<String> table = createAbstractHasData(new TextCell());
+
+ // Set a widget.
+ Label l = new Label("Empty");
+ table.setEmptyTableWidget(l);
+ assertEquals(l, table.getEmptyTableWidget());
+
+ // Null widget.
+ table.setEmptyTableWidget(null);
+ assertNull(table.getEmptyTableWidget());
+ }
+
+ public void testSetLoadingIndicator() {
+ CellTable<String> table = createAbstractHasData(new TextCell());
+
+ // Set a widget.
+ Label l = new Label("Loading");
+ table.setLoadingIndicator(l);
+ assertEquals(l, table.getLoadingIndicator());
+
+ // Null widget.
+ table.setLoadingIndicator(null);
+ assertNull(table.getLoadingIndicator());
+ }
+
public void testSetTableLayoutFixed() {
CellTable<String> table = createAbstractHasData(new TextCell());
assertNotSame("fixed",
diff --git a/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java b/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java
index 1ff0fa6..358a5c9 100644
--- a/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java
+++ b/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java
@@ -23,10 +23,10 @@
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
-import com.google.gwt.user.cellview.client.HasDataPresenter.LoadingState;
import com.google.gwt.user.cellview.client.HasDataPresenter.View;
import com.google.gwt.user.cellview.client.HasKeyboardPagingPolicy.KeyboardPagingPolicy;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
+import com.google.gwt.user.cellview.client.LoadingStateChangeEvent.LoadingState;
import com.google.gwt.view.client.HasData;
import com.google.gwt.view.client.MockHasData;
import com.google.gwt.view.client.MockHasData.MockRangeChangeHandler;
@@ -497,6 +497,31 @@
assertEquals(5, presenter.getCurrentPageSize());
}
+ public void testIsEmpty() {
+ HasData<String> listView = new MockHasData<String>();
+ MockView<String> view = new MockView<String>();
+ HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView,
+ view, 10, null);
+
+ // Non-zero row count.
+ presenter.setRowCount(1, true);
+ populatePresenter(presenter);
+ presenter.flush();
+ assertFalse(presenter.isEmpty());
+
+ // Zero row count with unknown size.
+ presenter.setRowCount(0, false);
+ populatePresenter(presenter);
+ presenter.flush();
+ assertFalse(presenter.isEmpty());
+
+ // Zero row count with known size.
+ presenter.setRowCount(0, true);
+ populatePresenter(presenter);
+ presenter.flush();
+ assertFalse(presenter.isEmpty());
+ }
+
public void testKeyboardNavigationChangePage() {
HasData<String> listView = new MockHasData<String>();
MockView<String> view = new MockView<String>();
@@ -1246,7 +1271,7 @@
assertEquals(0, presenter.getRowCount());
assertTrue(presenter.isRowCountExact());
presenter.flush();
- view.assertLoadingState(LoadingState.EMPTY);
+ view.assertLoadingState(LoadingState.LOADED);
}
public void testSetRowCountNoBoolean() {