From e2acf99e3e284bcfa364cd15156cdf275b081f45 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Pekka=20Hyv=C3=B6nen?= Date: Wed, 25 Oct 2017 14:09:11 +0300 Subject: [PATCH] Refactored the GridDragger to match FW style Unit tests, Test UI and documentation to come next. --- ...getIndex.java => DropIndexCalculator.java} | 20 +- .../ui/components/grid/GridDragSource.java | 10 + .../ui/components/grid/GridDragger.java | 397 +++++++++++++----- .../ui/components/grid/GridDropTarget.java | 10 + ...er.java => SourceDataProviderUpdater.java} | 30 +- ...er.java => TargetDataProviderUpdater.java} | 34 +- 6 files changed, 357 insertions(+), 144 deletions(-) rename server/src/main/java/com/vaadin/ui/components/grid/{GridDropTargetIndex.java => DropIndexCalculator.java} (75%) rename server/src/main/java/com/vaadin/ui/components/grid/{GridSourceWriter.java => SourceDataProviderUpdater.java} (59%) rename server/src/main/java/com/vaadin/ui/components/grid/{GridTargetWriter.java => TargetDataProviderUpdater.java} (54%) diff --git a/server/src/main/java/com/vaadin/ui/components/grid/GridDropTargetIndex.java b/server/src/main/java/com/vaadin/ui/components/grid/DropIndexCalculator.java similarity index 75% rename from server/src/main/java/com/vaadin/ui/components/grid/GridDropTargetIndex.java rename to server/src/main/java/com/vaadin/ui/components/grid/DropIndexCalculator.java index c9df2ee45d..375b80f1ad 100644 --- a/server/src/main/java/com/vaadin/ui/components/grid/GridDropTargetIndex.java +++ b/server/src/main/java/com/vaadin/ui/components/grid/DropIndexCalculator.java @@ -18,21 +18,23 @@ package com.vaadin.ui.components.grid; import java.io.Serializable; /** - * An event listener for a GridDragger Drop. - * - * Used to calculate the target index of the dropped items. - * + * A handler for calculating the index of the dropped items on the drop target + * grid. + * * @author Stephan Knitelius - * @since 8.1 - * + * @author Vaadin Ltd + * @since + * @see GridDragger * @param * the bean type */ -public interface GridDropTargetIndex extends Serializable { +@FunctionalInterface +public interface DropIndexCalculator extends Serializable { /** * Called when Items are dropped onto a target grid. - * - * @param event the GridDropEvent. + * + * @param event + * the GridDropEvent. * @return index the target index. */ public int calculateDropIndex(GridDropEvent event); diff --git a/server/src/main/java/com/vaadin/ui/components/grid/GridDragSource.java b/server/src/main/java/com/vaadin/ui/components/grid/GridDragSource.java index 80b8daf898..fd7a2f8ffa 100644 --- a/server/src/main/java/com/vaadin/ui/components/grid/GridDragSource.java +++ b/server/src/main/java/com/vaadin/ui/components/grid/GridDragSource.java @@ -97,6 +97,16 @@ public class GridDragSource extends DragSourceExtension> { defaultGridGenerator); } + /** + * Gets the grid this extension has been attached to. + * + * @return the grid for this extension + * @since + */ + public Grid getGrid() { + return getParent(); + } + @Override protected void registerDragSourceRpc() { registerRpc(new GridDragSourceRpc() { diff --git a/server/src/main/java/com/vaadin/ui/components/grid/GridDragger.java b/server/src/main/java/com/vaadin/ui/components/grid/GridDragger.java index bcc49e62bb..07554256e4 100644 --- a/server/src/main/java/com/vaadin/ui/components/grid/GridDragger.java +++ b/server/src/main/java/com/vaadin/ui/components/grid/GridDragger.java @@ -15,127 +15,145 @@ */ package com.vaadin.ui.components.grid; -import com.vaadin.data.provider.ListDataProvider; -import com.vaadin.shared.ui.grid.DropLocation; -import com.vaadin.shared.ui.grid.DropMode; -import com.vaadin.ui.Grid; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Set; +import com.vaadin.data.provider.ListDataProvider; +import com.vaadin.shared.ui.dnd.DropEffect; +import com.vaadin.shared.ui.grid.DropLocation; +import com.vaadin.shared.ui.grid.DropMode; +import com.vaadin.ui.Grid; + /** * Allows dragging rows for reordering within a Grid and between separate Grids. *

* When dragging a selected row, all the visible selected rows are dragged. Note * that ONLY visible rows are taken into account. + *

+ * NOTE: this helper works only with {@link ListDataProvider} on both grids. + * If you have another data provider, you should customize data provider + * updating on drop with + * {@link #setSourceDataProviderUpdater(SourceDataProviderUpdater)} and + * {@link #setTargetGridDropHandler(TargetDataProviderUpdater)}. * - * @param The Grid bean type. + * @param + * The Grid bean type. * @author Stephan Knitelius - * @since 8.1 + * @author Vaadin Ltd + * @since */ public class GridDragger implements Serializable { private final GridDropTarget gridDropTarget; private final GridDragSource gridDragSource; - private GridDropTargetIndex gridDropTargetIndex = null; - private GridSourceWriter gridSourceWriter = null; - private GridTargetWriter gridTargetWriter = null; + private DropIndexCalculator dropTargetIndexCalculator = null; + private SourceDataProviderUpdater sourceDataProviderUpdater = null; + private TargetDataProviderUpdater targetDataProviderUpdater = null; /** * Set of items currently being dragged. */ private Set draggedItems; - private boolean addToEnd = false; + private boolean addItemsToEnd = false; private boolean removeFromSource = true; /** * Extends a Grid and makes it's row orderable by dragging entries up or * down. * - * @param grid Grid to be extended. + * @param grid + * Grid to be extended. */ public GridDragger(Grid grid) { this(grid, grid, DropMode.ON_TOP_OR_BETWEEN); } /** - * Extends the Grid and makes it's row orderable by dragging entries up or - * down. + * Enables DnD reordering the rows in the given grid. + *

+ * NOTE: this only works when the grid has a + * {@link ListDataProvider}. Use the custom handlers + * {@link #setSourceDataProviderUpdater(SourceDataProviderUpdater)} and + * {@link #setTargetGridDropHandler(TargetDataProviderUpdater)} for other + * data providers. * - * @param grid Grid to be extended. - * @param dropMode DropMode to be used. + * @param grid + * the grid to enable row DnD reordering on + * @param dropMode + * DropMode to be used. */ public GridDragger(Grid grid, DropMode dropMode) { this(grid, grid, dropMode); } /** - * Extends the source and target grid so that rows can be dragged from the - * source to the target grid. + * Enables DnD moving of rows from the source grid to the target grid. + *

+ * {@link DropMode#ON_TOP_OR_BETWEEN} is used. + *

+ * NOTE: this only works when the grids have a + * {@link ListDataProvider}. Use the custom handlers + * {@link #setSourceDataProviderUpdater(SourceDataProviderUpdater)} and + * {@link #setTargetGridDropHandler(TargetDataProviderUpdater)} for other + * data providers. * - * @param source Grid dragged from. - * @param target Grid dropped to. + * @param source + * the source grid dragged from. + * @param target + * the target grid dropped to. */ public GridDragger(Grid source, Grid target) { this(source, target, DropMode.ON_TOP_OR_BETWEEN); } /** - * Extends the grid so that items can be reordered, use the gridTargetWriter - * to write to non-standard DataProvider. - * - * @param grid Grid to be reorderable. - * @param gridTargetWriter callback for writing to custom DataProvider. - */ - public GridDragger(Grid grid, GridTargetWriter gridTargetWriter) { - this(grid, grid, gridTargetWriter, null); - } - - /** - * Extends the source and target grid so that items can be reordered, use - * the gridTargetWriter to write to non-standard DataProviders. - * - * @param source Grid dragged from. - * @param target Grid dragged to. - * @param gridTargetWriter callback for writing to custom target - * DataProvider. - */ - public GridDragger(Grid source, Grid target, GridTargetWriter gridTargetWriter) { - this(source, target, gridTargetWriter, null); - } - - /** - * Extends the source and target grid so that items can be reordered, use - * the gridTargetWriter to write to non-standard DataProviders and - * gridSourceWriter to update source Grid. + * Enables DnD moving of rows from the source grid to the target grid with + * the custom data provider updaters. + *

+ * {@link DropMode#ON_TOP_OR_BETWEEN} is used. * - * @param source Grid dragged from. - * @param target Grid dragged to. - * @param gridTargetWriter callback for writing to custom target - * DataProvider. - * @param gridSourceWriter callback for updating custom source DataProvider. + * @param source + * grid dragged from + * @param target + * grid dragged to + * @param targetDataProviderUpdater + * handler for updating target grid data provider + * @param sourceDataProviderUpdater + * handler for updating source grid data provider */ - public GridDragger(Grid source, Grid target, GridTargetWriter gridTargetWriter, GridSourceWriter gridSourceWriter) { + public GridDragger(Grid source, Grid target, + TargetDataProviderUpdater targetDataProviderUpdater, + SourceDataProviderUpdater sourceDataProviderUpdater) { this(source, target, DropMode.ON_TOP_OR_BETWEEN); - this.gridTargetWriter = gridTargetWriter; - this.gridSourceWriter = gridSourceWriter; + this.targetDataProviderUpdater = targetDataProviderUpdater; + this.sourceDataProviderUpdater = sourceDataProviderUpdater; } /** - * Extends a the source and target grid so that rows can be dragged from the - * source to the target grid. + * Enables DnD moving of rows from the source grid to the target grid with + * the given drop mode. + *

+ * NOTE: this only works when the grids have a + * {@link ListDataProvider}. Use the other constructors or custom + * handlers {@link #setSourceDataProviderUpdater(SourceDataProviderUpdater)} + * and {@link #setTargetGridDropHandler(TargetDataProviderUpdater)} for + * other data providers. * - * @param target Grid to be extended. - * @param dropMode DropMode to be used. + * @param source + * the drag source grid + * @param target + * the drop target grid + * @param dropMode + * the drop mode to use */ public GridDragger(Grid source, Grid target, DropMode dropMode) { - checkAndInitalizeGridWriter(source, target); - - gridDragSource = new GridDragSource(source); + gridDragSource = new GridDragSource<>(source); - gridDropTarget = new GridDropTarget(target, dropMode); + gridDropTarget = new GridDropTarget<>(target, dropMode); gridDragSource.addGridDragStartListener(event -> { draggedItems = event.getDraggedItems(); @@ -143,95 +161,248 @@ public class GridDragger implements Serializable { gridDropTarget.addGridDropListener(event -> { if (removeFromSource) { - gridSourceWriter.removeItems(draggedItems); + handleSourceGridDrop(draggedItems); } - int index = gridDropTargetIndex.calculateDropIndex(event); - gridTargetWriter.addItems(index, draggedItems); + int index = calculateDropIndex(event); + handleTargetGridDrop(index, draggedItems); }); } - public void setGridTargetWriter(GridTargetWriter gridTargetWriter) { - this.gridTargetWriter = gridTargetWriter; + /** + * Sets the target data provider updater, which handles adding the dropped + * items to the target grid. + *

+ * By default, items are added to the index where they were dropped on for + * any {@link ListDataProvider}. If another type of data provider is used, + * this updater should be set to handle updating instead. + * + * @param targetDataProviderUpdater + * the target drop handler to set, or {@code null} to remove + */ + public void setTargetGridDropHandler( + TargetDataProviderUpdater targetDataProviderUpdater) { + this.targetDataProviderUpdater = targetDataProviderUpdater; + } + + /** + * Returns the target grid data provider updater. + * + * @return target grid drop handler + */ + public TargetDataProviderUpdater getTargetGridDropHandler() { + return targetDataProviderUpdater; + } + + /** + * Sets the source data provider updater, which handles removing items from + * the drag source grid. + *

+ * By default the items are removed from any {@link ListDataProvider}. If + * another type of data provider is used, this updater should be set to + * handle updating instead. + *

+ * NOTE: this is not triggered when + * {@link #setRemoveItemsFromSourceGrid(boolean)} has been set to + * {@code false} + * + * @param sourceDataProviderUpdater + * the drag source data provider updater to set, or {@code null} + * to remove + */ + public void setSourceDataProviderUpdater( + SourceDataProviderUpdater sourceDataProviderUpdater) { + this.sourceDataProviderUpdater = sourceDataProviderUpdater; + } + + /** + * Returns the source grid data provider updater. + *

+ * Default is {@code null} and the items are just removed from the source + * grid, which only works for {@link ListDataProvider}. + * + * @return the source grid drop handler + */ + public SourceDataProviderUpdater getSourceGridDropHandler() { + return sourceDataProviderUpdater; } - public void setGridSourceWriter(GridSourceWriter gridSourceWriter) { - this.gridSourceWriter = gridSourceWriter; + /** + * Sets the drop index calculator for the target grid. With this callback + * you can have a custom drop location instead of the actual one. + *

+ * By default, items are placed on the index they are dropped into in the + * target grid. + *

+ * NOTE: this will override {@link #setAddItemsToEnd(boolean)}. + * + * @param dropIndexCalculator + * the drop index calculator + */ + public void setDropIndexCalculator( + DropIndexCalculator dropIndexCalculator) { + this.dropTargetIndexCalculator = dropIndexCalculator; } - public void setGrid(GridDropTargetIndex gridDropTargetIndex) { - this.gridDropTargetIndex = gridDropTargetIndex; + /** + * Gets the drop index calculator. + *

+ * Default is {@code null} and the dropped items are placed on the drop + * location. + * + * @return the drop index calculator + */ + public DropIndexCalculator getDropIndexCalculator() { + return dropTargetIndexCalculator; } /** - * Exposes the GridDropTarget to perform customizations such as - * DropEffect.MOVE. + * Returns the drop target grid to allow performing customizations such as + * altering {@link DropEffect}. + * + * @return the drop target grid */ public GridDropTarget getGridDropTarget() { return gridDropTarget; } /** - * Exposes the GridDragSource for customizations. + * Returns the drag source grid, exposing it for customizations. + * + * @return the drag source grid */ public GridDragSource getGridDragSource() { return gridDragSource; } /** - * By default items are dropped into the selected position. Set addToEnd - * will add the items to the end of the grid instead. + * Sets whether the items should be always added to the end instead of the + * dropped position in target grid. + *

+ * The default is {@code false} (added to dropped position). + *

+ * NOTE: this applies only when no custom index calculator is set with + * {@link #setDropIndexCalculator(DropIndexCalculator)}. + * + * @param addItemsToEnd + * {@code true} for adding items to the end of the grid, + * {@code false} for adding to the dropped position + */ + public void setAddItemsToEnd(boolean addItemsToEnd) { + this.addItemsToEnd = addItemsToEnd; + } + + /** + * Returns whether items are added to end instead of selected position. + *

+ * NOTE: this applies only when no custom index calculator is set with + * {@link #setDropIndexCalculator(DropIndexCalculator)}. * - * @param addToEnd add items to end of Grid. + * @return return whether items are added to end or to the dropped position */ - public void setAddToEnd(boolean addToEnd) { - this.addToEnd = addToEnd; + public boolean isAddItemsToEnd() { + return addItemsToEnd; } /** - * By default the dragged Items are removed from the source Grid. + * Sets whether the items should be removed from the source grid or not. + *

+ * Default value is {@code true} and the dropped items are removed from the + * source grid. + *

+ * NOTE: when this is set to {@code false}, any custom handler with + * {@link #setSourceDataProviderUpdater(SourceDataProviderUpdater)} is not + * triggered on drop. * - * @param removeFromSource set to false to keep items in source Grid. + * @param removeFromSource + * {@code true} to remove dropped items, {@code false} to not + * remove. */ - public void removeFromSourceGrid(boolean removeFromSource) { + public void setRemoveItemsFromSourceGrid(boolean removeFromSource) { this.removeFromSource = removeFromSource; } /** - * Checks if custom implementations have been set otherwise the default - * ListDataProvider implementation is used. + * Returns whether dropped items are removed from the source grid or not. + *

+ * Default value is {@code true} and the dropped items are removed from the + * source grid. + *

+ * NOTE: when this is set to {@code false}, any custom handler with + * {@link #setSourceDataProviderUpdater(SourceDataProviderUpdater)} is not + * triggered on drop. + * + * @return {@code true} to remove dropped items, {@code false} to not + * remove. */ - private void checkAndInitalizeGridWriter(final Grid source, final Grid target) { - if (gridSourceWriter == null) { - this.gridSourceWriter = (items) -> { - ListDataProvider listDataProvider = (ListDataProvider) source.getDataProvider(); - List sourceItems = new ArrayList(listDataProvider.getItems()); - sourceItems.removeAll(items); - source.setItems(sourceItems); - }; + public boolean isRemoveItemsFromSourceGrid() { + return removeFromSource; + } + + private void handleSourceGridDrop(final Collection droppedItems) { + Grid source = getGridDragSource().getGrid(); + if (getSourceGridDropHandler() == null) { + if (!(source.getDataProvider() instanceof ListDataProvider)) { + throwIllegalStateException(true); + } + ListDataProvider listDataProvider = (ListDataProvider) source + .getDataProvider(); + List sourceItems = new ArrayList<>(listDataProvider.getItems()); + sourceItems.removeAll(droppedItems); + source.setItems(sourceItems); + } else { + getSourceGridDropHandler().removeItems(source.getDataProvider(), + droppedItems); } - if (gridDropTargetIndex == null) { - this.gridDropTargetIndex = event -> { - if (!addToEnd) { - ListDataProvider targetDataProvider = (ListDataProvider) target.getDataProvider(); - List items = new ArrayList(targetDataProvider.getItems()); - int index = items.size(); - if (event.getDropTargetRow().isPresent()) { - index = items.indexOf(event.getDropTargetRow().get()) - + (event.getDropLocation() == DropLocation.BELOW ? 1 : 0); - } - return index; - } - return Integer.MAX_VALUE; - }; + } + + private void handleTargetGridDrop(final int index, + Collection droppedItems) { + Grid target = getGridDropTarget().getGrid(); + if (targetDataProviderUpdater == null) { + if (!(target.getDataProvider() instanceof ListDataProvider)) { + throwIllegalStateException(false); + } + ListDataProvider listDataProvider = (ListDataProvider) target + .getDataProvider(); + List targetItems = new ArrayList<>(listDataProvider.getItems()); + targetItems.addAll(index, droppedItems); + target.setItems(targetItems); + } else { + getTargetGridDropHandler().onDrop(target.getDataProvider(), index, + droppedItems); } - if (gridTargetWriter == null) { - this.gridTargetWriter = (index, items) -> { - ListDataProvider listDataProvider = (ListDataProvider) target.getDataProvider(); - List targetItems = new ArrayList(listDataProvider.getItems()); - targetItems.addAll(index, items); - target.setItems(targetItems); - }; + } + + private int calculateDropIndex(GridDropEvent event) { + if (getDropIndexCalculator() == null) { + if (!addItemsToEnd) { + ListDataProvider targetDataProvider = (ListDataProvider) getGridDropTarget() + .getGrid().getDataProvider(); + List items = new ArrayList<>(targetDataProvider.getItems()); + int index = items.size(); + if (event.getDropTargetRow().isPresent()) { + index = items.indexOf(event.getDropTargetRow().get()) + + (event.getDropLocation() == DropLocation.BELOW ? 1 + : 0); + } + return index; + } + return Integer.MAX_VALUE; + } else { + return getDropIndexCalculator().calculateDropIndex(event); } } + + private static void throwIllegalStateException(boolean sourceGrid) { + throw new IllegalStateException( + new StringBuilder().append(sourceGrid ? "Source " : "Target ") + .append("grid does not have a ListDataProvider, cannot automatically ") + .append(sourceGrid ? "remove " : "add ") + .append("items. Use GridDragger.set") + .append(sourceGrid ? "Source" : "Target") + .append("GridDropHandler(...) to customize how to handle updating the data provider.") + .toString()); + } + } diff --git a/server/src/main/java/com/vaadin/ui/components/grid/GridDropTarget.java b/server/src/main/java/com/vaadin/ui/components/grid/GridDropTarget.java index edb1534a05..de83bd56ce 100644 --- a/server/src/main/java/com/vaadin/ui/components/grid/GridDropTarget.java +++ b/server/src/main/java/com/vaadin/ui/components/grid/GridDropTarget.java @@ -54,6 +54,16 @@ public class GridDropTarget extends DropTargetExtension> { setDropMode(dropMode); } + /** + * Gets the grid this extension has been attached to. + * + * @return the grid for this extension + * @since + */ + public Grid getGrid() { + return getParent(); + } + /** * Sets the drop mode of this drop target. *

diff --git a/server/src/main/java/com/vaadin/ui/components/grid/GridSourceWriter.java b/server/src/main/java/com/vaadin/ui/components/grid/SourceDataProviderUpdater.java similarity index 59% rename from server/src/main/java/com/vaadin/ui/components/grid/GridSourceWriter.java rename to server/src/main/java/com/vaadin/ui/components/grid/SourceDataProviderUpdater.java index a8c5d74077..a9be2b2282 100644 --- a/server/src/main/java/com/vaadin/ui/components/grid/GridSourceWriter.java +++ b/server/src/main/java/com/vaadin/ui/components/grid/SourceDataProviderUpdater.java @@ -18,23 +18,31 @@ package com.vaadin.ui.components.grid; import java.io.Serializable; import java.util.Collection; +import com.vaadin.data.provider.DataProvider; + /** - * An event listener for a GridDragger Drop. - * - * Used to write updates to the source Grid DataProvider after a - * Drag operation. - * + * A handler for source grid data provider updater for {@link GridDragger}. + * + * Used to handle updates to the source grid's {@link DataProvider} after a + * drop. + * * @author Stephan Knitelius - * @since 8.1 - * + * @author Vaadin Ltd + * @since + * * @param * the bean type */ -public interface GridSourceWriter extends Serializable { +@FunctionalInterface +public interface SourceDataProviderUpdater extends Serializable { /** * Called when Items have been dragged. - * - * @param items dragged items. + * + * @param dataProvider + * the data provider for the source grid + * @param items + * dragged items. */ - public void removeItems(Collection items); + public void removeItems(DataProvider dataProvider, + Collection items); } diff --git a/server/src/main/java/com/vaadin/ui/components/grid/GridTargetWriter.java b/server/src/main/java/com/vaadin/ui/components/grid/TargetDataProviderUpdater.java similarity index 54% rename from server/src/main/java/com/vaadin/ui/components/grid/GridTargetWriter.java rename to server/src/main/java/com/vaadin/ui/components/grid/TargetDataProviderUpdater.java index d7b00476ad..37373b8fdd 100644 --- a/server/src/main/java/com/vaadin/ui/components/grid/GridTargetWriter.java +++ b/server/src/main/java/com/vaadin/ui/components/grid/TargetDataProviderUpdater.java @@ -18,23 +18,35 @@ package com.vaadin.ui.components.grid; import java.io.Serializable; import java.util.Collection; +import com.vaadin.data.provider.DataProvider; + /** - * An event listener for a GridDragger Drop. - * - * Used to write the updates to the target Grid DataProvider. - * + * A handler for target grid data provider updater for {@link GridDragger}. + * + * Used to handle updates to the target grid's {@link DataProvider} after a + * drop. + * * @author Stephan Knitelius - * @since 8.1 - * + * @author Vaadin Ltd + * @since + * * @param * the bean type */ -public interface GridTargetWriter extends Serializable { +@FunctionalInterface +public interface TargetDataProviderUpdater extends Serializable { + /** * Called when items have been dropped on the target Grid. - * - * @param index the Target index Integer.MAX when items should be added to end. - * @param items items to be added. + * + * @param dataProvider + * the target grid data provider + * @param index + * the Target index Integer.MAX when items should be added to + * end. + * @param items + * items to be added. */ - public void addItems(int index, Collection items); + public void onDrop(DataProvider dataProvider, int index, + Collection items); } -- 2.39.5