Skip to content

Commit

Permalink
Implement DetailsGenerators for Grid
Browse files Browse the repository at this point in the history
Change-Id: I09057b990f10bde6cf72a16677e58cb2bc9a7029
  • Loading branch information
Teemu Suo-Anttila authored and Vaadin Code Review committed Aug 29, 2016
1 parent 0109540 commit b4861ed
Show file tree
Hide file tree
Showing 10 changed files with 1,076 additions and 15 deletions.
@@ -0,0 +1,216 @@
/*
* Copyright 2000-2016 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.client.connectors.grid;

import java.util.HashMap;
import java.util.Map;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorMap;
import com.vaadin.client.LayoutManager;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.data.DataChangeHandler;
import com.vaadin.client.extensions.AbstractExtensionConnector;
import com.vaadin.client.widget.grid.HeightAwareDetailsGenerator;
import com.vaadin.client.widgets.Grid;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.grid.GridState;
import com.vaadin.ui.Grid.DetailsManager;

import elemental.json.JsonObject;

/**
* Connector class for {@link DetailsManager} of the Grid component.
*
* @author Vaadin Ltd
* @since
*/
@Connect(DetailsManager.class)
public class DetailsManagerConnector extends AbstractExtensionConnector {

/* Map for tracking which details are open on which row */
private Map<Integer, String> indexToDetailConnectorId = new HashMap<>();
/* Boolean flag to avoid multiple refreshes */
private boolean refreshing;
/* Registration for data change handler. */
private Registration dataChangeRegistration;

/**
* DataChangeHandler for updating the visibility of detail widgets.
*/
private final class DetailsChangeHandler implements DataChangeHandler {
@Override
public void resetDataAndSize(int estimatedNewDataSize) {
// Full clean up
indexToDetailConnectorId.clear();
}

@Override
public void dataUpdated(int firstRowIndex, int numberOfRows) {
for (int i = 0; i < numberOfRows; ++i) {
int index = firstRowIndex + i;
detachIfNeeded(index, getDetailsComponentConnectorId(index));
}
// Deferred opening of new ones.
refreshDetails();
}

/* The remaining methods will do a full refresh for now */

@Override
public void dataRemoved(int firstRowIndex, int numberOfRows) {
refreshDetails();
}

@Override
public void dataAvailable(int firstRowIndex, int numberOfRows) {
refreshDetails();
}

@Override
public void dataAdded(int firstRowIndex, int numberOfRows) {
refreshDetails();
}
}

/**
* Height aware details generator for client-side Grid.
*/
private class CustomDetailsGenerator
implements HeightAwareDetailsGenerator {

@Override
public Widget getDetails(int rowIndex) {
String id = getDetailsComponentConnectorId(rowIndex);
if (id == null) {
return null;
}

return getConnector(id).getWidget();
}

@Override
public double getDetailsHeight(int rowIndex) {
// Case of null is handled in the getDetails method and this method
// will not called if it returns null.
String id = getDetailsComponentConnectorId(rowIndex);
ComponentConnector componentConnector = getConnector(id);

getLayoutManager().setNeedsMeasureRecursively(componentConnector);
getLayoutManager().layoutNow();

return getLayoutManager().getOuterHeightDouble(
componentConnector.getWidget().getElement());
}

private ComponentConnector getConnector(String id) {
return (ComponentConnector) ConnectorMap.get(getConnection())
.getConnector(id);
}
}

@Override
protected void extend(ServerConnector target) {
getWidget().setDetailsGenerator(new CustomDetailsGenerator());
dataChangeRegistration = getWidget().getDataSource()
.addDataChangeHandler(new DetailsChangeHandler());
}

private void detachIfNeeded(int rowIndex, String id) {
if (indexToDetailConnectorId.containsKey(rowIndex)) {
if (indexToDetailConnectorId.get(rowIndex).equals(id)) {
return;
}

// New Details component, hide old one
getWidget().setDetailsVisible(rowIndex, false);
indexToDetailConnectorId.remove(rowIndex);
}
}

@Override
public void onUnregister() {
super.onUnregister();

dataChangeRegistration.remove();
dataChangeRegistration = null;

indexToDetailConnectorId.clear();
}

@Override
public GridConnector getParent() {
return (GridConnector) super.getParent();
}

private Grid<JsonObject> getWidget() {
return getParent().getWidget();
}

/**
* Returns the connector id for a details component.
*
* @param rowIndex
* the row index of details component
* @return connector id; {@code null} if row or id is not found
*/
private String getDetailsComponentConnectorId(int rowIndex) {
JsonObject row = getParent().getWidget().getDataSource()
.getRow(rowIndex);

if (row == null || !row.hasKey(GridState.JSONKEY_DETAILS_VISIBLE)
|| row.getString(GridState.JSONKEY_DETAILS_VISIBLE).isEmpty()) {
return null;
}

return row.getString(GridState.JSONKEY_DETAILS_VISIBLE);
}

private LayoutManager getLayoutManager() {
return LayoutManager.get(getConnection());
}

/**
* Schedules a deferred opening for new details components.
*/
private void refreshDetails() {
if (refreshing) {
return;
}

refreshing = true;
Scheduler.get().scheduleFinally(this::refreshDetailsVisibility);
}

private void refreshDetailsVisibility() {
for (int i = 0; i < getWidget().getDataSource().size(); ++i) {
String id = getDetailsComponentConnectorId(i);

detachIfNeeded(i, id);

if (id == null) {
continue;
}

indexToDetailConnectorId.put(i, id);
getWidget().setDetailsVisible(i, true);
}
refreshing = false;
}
}
Expand Up @@ -16,11 +16,17 @@
package com.vaadin.client.connectors.grid;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gwt.event.shared.HandlerRegistration;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorHierarchyChangeEvent;
import com.vaadin.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler;
import com.vaadin.client.DeferredWorker;
import com.vaadin.client.HasComponentsConnector;
import com.vaadin.client.connectors.AbstractListingConnector;
import com.vaadin.client.data.DataSource;
import com.vaadin.client.ui.SimpleManagedLayout;
Expand All @@ -43,9 +49,12 @@
*/
@Connect(com.vaadin.ui.Grid.class)
public class GridConnector extends AbstractListingConnector
implements SimpleManagedLayout, DeferredWorker {
implements HasComponentsConnector, SimpleManagedLayout, DeferredWorker {

/* Map to keep track of all added columns */
private Map<Column<?, JsonObject>, String> columnToIdMap = new HashMap<>();
/* Child component list for HasComponentsConnector */
private List<ComponentConnector> childComponents;

@Override
public Grid<JsonObject> getWidget() {
Expand Down Expand Up @@ -133,4 +142,33 @@ private void handleSortEvent(SortEvent<JsonObject> event) {
sortDirections.toArray(new SortDirection[0]),
event.isUserOriginated());
}

/* HasComponentsConnector */

@Override
public void updateCaption(ComponentConnector connector) {
// Details components don't support captions.
}

@Override
public List<ComponentConnector> getChildComponents() {
if (childComponents == null) {
return Collections.emptyList();
}

return childComponents;
}

@Override
public void setChildComponents(List<ComponentConnector> children) {
this.childComponents = children;

}

@Override
public HandlerRegistration addConnectorHierarchyChangeHandler(
ConnectorHierarchyChangeHandler handler) {
return ensureHandlerManager()
.addHandler(ConnectorHierarchyChangeEvent.TYPE, handler);
}
}

0 comments on commit b4861ed

Please sign in to comment.