import java.util.Map;
import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
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.MouseEventDetailsBuilder;
import com.vaadin.client.TooltipInfo;
import com.vaadin.client.connectors.AbstractListingConnector;
import com.vaadin.client.data.DataSource;
import com.vaadin.client.ui.SimpleManagedLayout;
import com.vaadin.client.widget.grid.CellReference;
+import com.vaadin.client.widget.grid.events.BodyClickHandler;
+import com.vaadin.client.widget.grid.events.BodyDoubleClickHandler;
+import com.vaadin.client.widget.grid.events.GridClickEvent;
+import com.vaadin.client.widget.grid.events.GridDoubleClickEvent;
import com.vaadin.client.widget.grid.selection.ClickSelectHandler;
import com.vaadin.client.widget.grid.selection.SpaceSelectHandler;
import com.vaadin.client.widget.grid.sort.SortEvent;
import com.vaadin.shared.data.selection.SelectionModel.Single;
import com.vaadin.shared.data.sort.SortDirection;
import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.grid.GridConstants;
import com.vaadin.shared.ui.grid.GridServerRpc;
import com.vaadin.shared.ui.grid.GridState;
extends AbstractListingConnector<SelectionModel<JsonObject>>
implements HasComponentsConnector, SimpleManagedLayout, DeferredWorker {
+ private class ItemClickHandler
+ implements BodyClickHandler, BodyDoubleClickHandler {
+
+ @Override
+ public void onClick(GridClickEvent event) {
+ if (hasEventListener(GridConstants.ITEM_CLICK_EVENT_ID)) {
+ fireItemClick(event.getTargetCell(), event.getNativeEvent());
+ }
+ }
+
+ @Override
+ public void onDoubleClick(GridDoubleClickEvent event) {
+ if (hasEventListener(GridConstants.ITEM_CLICK_EVENT_ID)) {
+ fireItemClick(event.getTargetCell(), event.getNativeEvent());
+ }
+ }
+
+ private void fireItemClick(CellReference<?> cell,
+ NativeEvent mouseEvent) {
+ String rowKey = getRowKey((JsonObject) cell.getRow());
+ String columnId = columnToIdMap.get(cell.getColumn());
+ getRpcProxy(GridServerRpc.class).itemClick(rowKey, columnId,
+ MouseEventDetailsBuilder
+ .buildMouseEventDetails(mouseEvent));
+ }
+ }
+
/* 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;
private SpaceSelectHandler<JsonObject> spaceSelectHandler;
private ClickSelectHandler<JsonObject> clickSelectHandler;
+ private ItemClickHandler itemClickHandler = new ItemClickHandler();
/**
* Gets the string identifier of a {@link Column} in this grid.
return null;
});
+ /* Item click events */
+ getWidget().addBodyClickHandler(itemClickHandler);
+ getWidget().addBodyDoubleClickHandler(itemClickHandler);
+
layout();
}
package com.vaadin.ui;
import java.io.Serializable;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Stream;
import com.vaadin.data.selection.SingleSelection;
+import com.vaadin.event.ConnectorEvent;
+import com.vaadin.event.EventListener;
import com.vaadin.server.KeyMapper;
import com.vaadin.server.data.SortOrder;
import com.vaadin.shared.MouseEventDetails;
+import com.vaadin.shared.Registration;
import com.vaadin.shared.data.DataCommunicatorConstants;
import com.vaadin.shared.data.selection.SelectionModel;
import com.vaadin.shared.data.sort.SortDirection;
import com.vaadin.shared.ui.grid.ColumnState;
+import com.vaadin.shared.ui.grid.GridConstants;
import com.vaadin.shared.ui.grid.GridConstants.Section;
import com.vaadin.shared.ui.grid.GridServerRpc;
import com.vaadin.shared.ui.grid.GridState;
import com.vaadin.ui.renderers.AbstractRenderer;
import com.vaadin.ui.renderers.Renderer;
import com.vaadin.ui.renderers.TextRenderer;
+import com.vaadin.util.ReflectTools;
import elemental.json.Json;
import elemental.json.JsonObject;
public class Grid<T> extends AbstractListing<T, SelectionModel<T>>
implements HasComponents {
+ @Deprecated
+ private static final Method ITEM_CLICK_METHOD = ReflectTools
+ .findMethod(ItemClickListener.class, "accept", ItemClick.class);
+
+ /**
+ * An event fired when an item in the Grid has been clicked.
+ *
+ * @param <T>
+ * the grid bean type
+ */
+ public static class ItemClick<T> extends ConnectorEvent {
+
+ private final T item;
+ private final Column<T, ?> column;
+ private final MouseEventDetails mouseEventDetails;
+
+ /**
+ * Creates a new {@code ItemClick} event containing the given item and
+ * Column originating from the given Grid.
+ *
+ */
+ public ItemClick(Grid<T> source, Column<T, ?> column, T item,
+ MouseEventDetails mouseEventDetails) {
+ super(source);
+ this.column = column;
+ this.item = item;
+ this.mouseEventDetails = mouseEventDetails;
+ }
+
+ /**
+ * Returns the clicked item.
+ *
+ * @return the clicked item
+ */
+ public T getItem() {
+ return item;
+ }
+
+ /**
+ * Returns the clicked column.
+ *
+ * @return the clicked column
+ */
+ public Column<T, ?> getColumn() {
+ return column;
+ }
+
+ /**
+ * Returns the source Grid.
+ *
+ * @return the grid
+ */
+ @Override
+ public Grid<T> getSource() {
+ return (Grid<T>) super.getSource();
+ }
+
+ /**
+ * Returns the mouse event details.
+ *
+ * @return the mouse event details
+ */
+ public MouseEventDetails getMouseEventDetails() {
+ return mouseEventDetails;
+ }
+ }
+
+ /**
+ * A listener for item click events.
+ *
+ * @param <T>
+ * the grid bean type
+ *
+ * @see ItemClick
+ * @see Registration
+ */
+ @FunctionalInterface
+ public interface ItemClickListener<T> extends EventListener<ItemClick<T>> {
+ /**
+ * Invoked when this listener receives a item click event from a Grid to
+ * which it has been added.
+ *
+ * @param event
+ * the received event, not null
+ */
+ @Override
+ public void accept(ItemClick<T> event);
+ }
+
/**
* A callback interface for generating style names for an item.
*
@Override
public void itemClick(String rowKey, String columnId,
MouseEventDetails details) {
- // TODO Auto-generated method stub
+ Column<T, ?> column = columnKeys.containsKey(columnId)
+ ? columnKeys.get(columnId) : null;
+ T item = getDataCommunicator().getKeyMapper().get(rowKey);
+ fireEvent(new ItemClick<>(Grid.this, column, item, details));
}
@Override
return descriptionGenerator;
}
+ /**
+ * Adds an item click listener. The listener is called when an item of this
+ * {@code Grid} is clicked.
+ *
+ * @param listener
+ * the item click listener, not null
+ * @return a registration for the listener
+ */
+ public Registration addItemClickListener(
+ ItemClickListener<? super T> listener) {
+ Objects.requireNonNull(listener, "listener cannot be null");
+ addListener(GridConstants.ITEM_CLICK_EVENT_ID, ItemClick.class,
+ listener, ITEM_CLICK_METHOD);
+ return () -> removeListener(ItemClick.class, listener);
+ }
+
@Override
protected GridState getState() {
return getState(true);
import com.vaadin.annotations.Widgetset;
import com.vaadin.data.selection.SingleSelection;
import com.vaadin.server.VaadinRequest;
+import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.grid.HeightMode;
import com.vaadin.tests.components.AbstractTestUIWithLog;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
-import com.vaadin.ui.CssLayout;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Grid.DetailsGenerator;
import com.vaadin.ui.Grid.StyleGenerator;
import com.vaadin.ui.Label;
import com.vaadin.ui.MenuBar;
+import com.vaadin.ui.MenuBar.Command;
import com.vaadin.ui.MenuBar.MenuItem;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Panel;
@Override
public Component apply(DataObject dataObj) {
- CssLayout cssLayout = new CssLayout();
+ VerticalLayout cssLayout = new VerticalLayout();
cssLayout.setHeight("200px");
cssLayout.setWidth("100%");
+ t.getRowNumber() + ", Column 0"
: null)))
.setCheckable(true);
+ stateMenu.addItem("Item click listener", new Command() {
+
+ private Registration registration = null;
+
+ @Override
+ public void menuSelected(MenuItem selectedItem) {
+ removeRegistration();
+ if (selectedItem.isChecked()) {
+ registration = grid.addItemClickListener(e -> {
+ grid.setDetailsVisible(e.getItem(),
+ !grid.isDetailsVisible(e.getItem()));
+ log("Item click on row " + e.getItem().getRowNumber()
+ + ", Column '" + e.getColumn().getCaption()
+ + "'");
+ });
+ log("Registered an item click listener.");
+ }
+ }
+
+ private void removeRegistration() {
+ if (registration != null) {
+ registration.remove();
+ registration = null;
+ log("Removed an item click listener.");
+ }
+ }
+ }).setCheckable(true);
}
private void createRowStyleMenu(MenuItem rowStyleMenu) {
assertFalse(logContainsText("AssertionError"));
}
+ @Test
+ public void testOpenDetailsWithItemClickHandler() {
+ selectMenuPath(DETAILS_GENERATOR_PERSISTING);
+ selectMenuPath("Component", "State", "Item click listener");
+ assertTrue("No item click listener registered",
+ logContainsText("Registered an item click listener."));
+ getGridElement().getCell(0, 1).click();
+ assertTrue("Details should open on click",
+ getGridElement().getDetails(0).getText().contains("One"));
+ assertTrue("Item click listener should log itself",
+ logContainsText("Item click on row 0, Column 'Column 1'"));
+
+ selectMenuPath("Component", "State", "Item click listener");
+ assertTrue("No removal of item click listener logged",
+ logContainsText("Removed an item click listener."));
+
+ getGridElement().getCell(0, 1).click();
+ assertTrue(
+ "Details should remain open, no item click listener to hide it",
+ getGridElement().getDetails(0).getText().contains("One"));
+ }
+
}