From c3af8b1873d77afb279453cd997ed75be4771ed8 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Thu, 8 Sep 2016 16:46:35 +0300 Subject: [PATCH] Add ItemClick events to Grid Change-Id: I9d761245e99cde62d0e56b89e5cbb0398fd4c363 --- .../client/connectors/grid/GridConnector.java | 39 ++++++ server/src/main/java/com/vaadin/ui/Grid.java | 116 +++++++++++++++++- .../components/grid/basics/GridBasics.java | 32 ++++- .../grid/basics/GridBasicDetailsTest.java | 22 ++++ 4 files changed, 206 insertions(+), 3 deletions(-) diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java index 43be3f0623..863cee0c88 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java @@ -22,17 +22,23 @@ import java.util.List; 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; @@ -43,6 +49,7 @@ import com.vaadin.shared.data.selection.SelectionModel; 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; @@ -59,12 +66,40 @@ public class GridConnector extends AbstractListingConnector> 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, String> columnToIdMap = new HashMap<>(); /* Child component list for HasComponentsConnector */ private List childComponents; private SpaceSelectHandler spaceSelectHandler; private ClickSelectHandler clickSelectHandler; + private ItemClickHandler itemClickHandler = new ItemClickHandler(); /** * Gets the string identifier of a {@link Column} in this grid. @@ -114,6 +149,10 @@ public class GridConnector return null; }); + /* Item click events */ + getWidget().addBodyClickHandler(itemClickHandler); + getWidget().addBodyDoubleClickHandler(itemClickHandler); + layout(); } diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java index 2a4f473536..c895ad08c3 100644 --- a/server/src/main/java/com/vaadin/ui/Grid.java +++ b/server/src/main/java/com/vaadin/ui/Grid.java @@ -16,6 +16,7 @@ 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; @@ -33,13 +34,17 @@ import java.util.function.Function; 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; @@ -47,6 +52,7 @@ import com.vaadin.shared.ui.grid.HeightMode; 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; @@ -64,6 +70,95 @@ import elemental.json.JsonValue; public class Grid extends AbstractListing> 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 + * the grid bean type + */ + public static class ItemClick extends ConnectorEvent { + + private final T item; + private final Column 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 source, Column 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 getColumn() { + return column; + } + + /** + * Returns the source Grid. + * + * @return the grid + */ + @Override + public Grid getSource() { + return (Grid) super.getSource(); + } + + /** + * Returns the mouse event details. + * + * @return the mouse event details + */ + public MouseEventDetails getMouseEventDetails() { + return mouseEventDetails; + } + } + + /** + * A listener for item click events. + * + * @param + * the grid bean type + * + * @see ItemClick + * @see Registration + */ + @FunctionalInterface + public interface ItemClickListener extends EventListener> { + /** + * 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 event); + } + /** * A callback interface for generating style names for an item. * @@ -179,7 +274,10 @@ public class Grid extends AbstractListing> @Override public void itemClick(String rowKey, String columnId, MouseEventDetails details) { - // TODO Auto-generated method stub + Column column = columnKeys.containsKey(columnId) + ? columnKeys.get(columnId) : null; + T item = getDataCommunicator().getKeyMapper().get(rowKey); + fireEvent(new ItemClick<>(Grid.this, column, item, details)); } @Override @@ -1019,6 +1117,22 @@ public class Grid extends AbstractListing> 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 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); diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java index 8c4aa4eec0..c82a434e23 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java @@ -11,16 +11,17 @@ import java.util.stream.Stream; 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; @@ -49,7 +50,7 @@ public class GridBasics extends AbstractTestUIWithLog { @Override public Component apply(DataObject dataObj) { - CssLayout cssLayout = new CssLayout(); + VerticalLayout cssLayout = new VerticalLayout(); cssLayout.setHeight("200px"); cssLayout.setWidth("100%"); @@ -201,6 +202,33 @@ public class GridBasics extends AbstractTestUIWithLog { + 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) { diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicDetailsTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicDetailsTest.java index a0f36703da..c1f28e27cf 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicDetailsTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicDetailsTest.java @@ -298,4 +298,26 @@ public class GridBasicDetailsTest extends GridBasicsTest { 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")); + } + } -- 2.39.5