diff options
author | Henrik Paul <henrik@vaadin.com> | 2015-03-10 17:02:02 +0200 |
---|---|---|
committer | Pekka Hyvönen <pekka@vaadin.com> | 2015-03-17 21:53:20 +0000 |
commit | a1619ee73dc18eecda22056541826a3c8bb65a5c (patch) | |
tree | 398476a7cd0192faed7393cbc1a9e65ac4df87c9 /client/src | |
parent | 84c143dd76ed1d27d03c0d695e7218b477d008fe (diff) | |
download | vaadin-framework-a1619ee73dc18eecda22056541826a3c8bb65a5c.tar.gz vaadin-framework-a1619ee73dc18eecda22056541826a3c8bb65a5c.zip |
Grid's Details can now be Components (#16644)
Change-Id: If67dd2e86cf41c57f208a3691e2cb7a5a29c133c
Diffstat (limited to 'client/src')
-rw-r--r-- | client/src/com/vaadin/client/connectors/GridConnector.java | 154 | ||||
-rw-r--r-- | client/src/com/vaadin/client/data/AbstractRemoteDataSource.java | 1 |
2 files changed, 149 insertions, 6 deletions
diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index f476982c15..70ad2504d8 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -29,12 +29,14 @@ import java.util.Set; import java.util.logging.Logger; import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.RepeatingCommand; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.NativeEvent; -import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; +import com.vaadin.client.DeferredWorker; import com.vaadin.client.MouseEventDetailsBuilder; import com.vaadin.client.annotations.OnStateChange; import com.vaadin.client.communication.StateChangeEvent; @@ -72,8 +74,10 @@ import com.vaadin.client.widgets.Grid.FooterCell; import com.vaadin.client.widgets.Grid.FooterRow; import com.vaadin.client.widgets.Grid.HeaderCell; import com.vaadin.client.widgets.Grid.HeaderRow; +import com.vaadin.shared.Connector; import com.vaadin.shared.data.sort.SortDirection; import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.grid.ConnectorIndexChange; import com.vaadin.shared.ui.grid.EditorClientRpc; import com.vaadin.shared.ui.grid.EditorServerRpc; import com.vaadin.shared.ui.grid.GridClientRpc; @@ -103,7 +107,8 @@ import elemental.json.JsonValue; */ @Connect(com.vaadin.ui.Grid.class) public class GridConnector extends AbstractHasComponentsConnector implements - SimpleManagedLayout, RpcDataSourceConnector.DetailsListener { + SimpleManagedLayout, RpcDataSourceConnector.DetailsListener, + DeferredWorker { private static final class CustomCellStyleGenerator implements CellStyleGenerator<JsonObject> { @@ -362,11 +367,119 @@ public class GridConnector extends AbstractHasComponentsConnector implements } } - private class CustomDetailsGenerator implements DetailsGenerator { + private static class CustomDetailsGenerator implements DetailsGenerator { + + private final Map<Integer, ComponentConnector> indexToDetailsMap = new HashMap<Integer, ComponentConnector>(); + @Override + @SuppressWarnings("boxing") public Widget getDetails(int rowIndex) { - // TODO - return new Label("[todo]"); + ComponentConnector componentConnector = indexToDetailsMap + .get(rowIndex); + if (componentConnector != null) { + return componentConnector.getWidget(); + } else { + return null; + } + } + + public void setDetailsConnectorChanges(Set<ConnectorIndexChange> changes) { + /* + * To avoid overwriting connectors while moving them about, we'll + * take all the affected connectors, first all remove those that are + * removed or moved, then we add back those that are moved or added. + */ + + /* Remove moved/removed connectors from bookkeeping */ + for (ConnectorIndexChange change : changes) { + Integer oldIndex = change.getOldIndex(); + Connector removedConnector = indexToDetailsMap.remove(oldIndex); + + Connector connector = change.getConnector(); + assert removedConnector == null || connector == null + || removedConnector.equals(connector) : "Index " + + oldIndex + " points to " + removedConnector + + " while " + connector + " was expected"; + } + + /* Add moved/added connectors to bookkeeping */ + for (ConnectorIndexChange change : changes) { + Integer newIndex = change.getNewIndex(); + ComponentConnector connector = (ComponentConnector) change + .getConnector(); + + if (connector != null) { + assert newIndex != null : "An existing connector has a missing new index."; + + ComponentConnector prevConnector = indexToDetailsMap.put( + newIndex, connector); + + assert prevConnector == null : "Connector collision at index " + + newIndex + + " between old " + + prevConnector + + " and new " + connector; + } + } + } + } + + @SuppressWarnings("boxing") + private class DetailsConnectorFetcher implements DeferredWorker { + + /** A flag making sure that we don't call scheduleFinally many times. */ + private boolean fetcherHasBeenCalled = false; + + /** A rolling counter for unique values. */ + private int detailsFetchCounter = 0; + + /** A collection that tracks the amount of requests currently underway. */ + private Set<Integer> pendingFetches = new HashSet<Integer>(5); + + private final ScheduledCommand lazyDetailsFetcher = new ScheduledCommand() { + @Override + public void execute() { + int currentFetchId = detailsFetchCounter++; + pendingFetches.add(currentFetchId); + getRpcProxy(GridServerRpc.class).sendDetailsComponents( + currentFetchId); + fetcherHasBeenCalled = false; + + assert assertRequestDoesNotTimeout(currentFetchId); + } + }; + + public void schedule() { + if (!fetcherHasBeenCalled) { + Scheduler.get().scheduleFinally(lazyDetailsFetcher); + fetcherHasBeenCalled = true; + } + } + + public void responseReceived(int fetchId) { + boolean success = pendingFetches.remove(fetchId); + assert success : "Received a response with an unidentified fetch id"; + } + + @Override + public boolean isWorkPending() { + return fetcherHasBeenCalled || !pendingFetches.isEmpty(); + } + + private boolean assertRequestDoesNotTimeout(final int fetchId) { + /* + * This method will not be compiled without asserts enabled. This + * only makes sure that any request does not time out. + * + * TODO Should this be an explicit check? Is it worth the overhead? + */ + new Timer() { + @Override + public void run() { + assert !pendingFetches.contains(fetchId); + } + }.schedule(1000); + return true; } } @@ -417,6 +530,10 @@ public class GridConnector extends AbstractHasComponentsConnector implements private String lastKnownTheme = null; + private final CustomDetailsGenerator customDetailsGenerator = new CustomDetailsGenerator(); + + private final DetailsConnectorFetcher detailsConnectorFetcher = new DetailsConnectorFetcher(); + @Override @SuppressWarnings("unchecked") public Grid<JsonObject> getWidget() { @@ -469,6 +586,24 @@ public class GridConnector extends AbstractHasComponentsConnector implements public void recalculateColumnWidths() { getWidget().recalculateColumnWidths(); } + + @Override + public void setDetailsConnectorChanges( + Set<ConnectorIndexChange> connectorChanges, int fetchId) { + customDetailsGenerator + .setDetailsConnectorChanges(connectorChanges); + + // refresh moved/added details rows + for (ConnectorIndexChange change : connectorChanges) { + Integer newIndex = change.getNewIndex(); + if (newIndex != null) { + int index = newIndex.intValue(); + getWidget().setDetailsVisible(index, false); + getWidget().setDetailsVisible(index, true); + } + } + detailsConnectorFetcher.responseReceived(fetchId); + } }); getWidget().addSelectionHandler(internalSelectionChangeHandler); @@ -512,7 +647,7 @@ public class GridConnector extends AbstractHasComponentsConnector implements getWidget().setEditorHandler(new CustomEditorHandler()); - getWidget().setDetailsGenerator(new CustomDetailsGenerator()); + getWidget().setDetailsGenerator(customDetailsGenerator); getLayoutManager().registerDependency(this, getWidget().getElement()); @@ -1017,5 +1152,12 @@ public class GridConnector extends AbstractHasComponentsConnector implements } else { getWidget().setDetailsVisible(rowIndex, false); } + + detailsConnectorFetcher.schedule(); + } + + @Override + public boolean isWorkPending() { + return detailsConnectorFetcher.isWorkPending(); } } diff --git a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java index 1de271c646..0ac4c33c83 100644 --- a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java +++ b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java @@ -570,6 +570,7 @@ public abstract class AbstractRemoteDataSource<T> implements DataSource<T> { Profiler.leave("AbstractRemoteDataSource.insertRowData"); } + @SuppressWarnings("boxing") private void moveRowFromIndexToIndex(int oldIndex, int newIndex) { T row = indexToRowMap.remove(oldIndex); if (indexToRowMap.containsKey(newIndex)) { |