diff options
author | Patrik Lindström <patrik@vaadin.com> | 2014-02-11 15:36:24 +0200 |
---|---|---|
committer | Henrik Paul <henrik@vaadin.com> | 2014-02-18 15:47:40 +0000 |
commit | d2027d8344313b048bf3b82e2a988ab40b0bb596 (patch) | |
tree | c387db0400b72af93bcb4205e241229e44a4cb29 /client/src | |
parent | 0ad5587c2f5dba0678995402dab482e81867f366 (diff) | |
download | vaadin-framework-d2027d8344313b048bf3b82e2a988ab40b0bb596.tar.gz vaadin-framework-d2027d8344313b048bf3b82e2a988ab40b0bb596.zip |
Implement programmatic scrolling (#13327)
Further changes required for this, included in the same patch:
- created GridClientRpc interface
- created test case UI for server-side controlled Grid programmatic
scrolling
- refactored getScrollPos logic into Escalator and moved
ScrollDestination enum to shared package
Change-Id: Ibf72a4f75831807d83fb5941597a6ce3fda08e17
Diffstat (limited to 'client/src')
5 files changed, 250 insertions, 159 deletions
diff --git a/client/src/com/vaadin/client/ui/grid/Escalator.java b/client/src/com/vaadin/client/ui/grid/Escalator.java index a1d895c2dd..77a8c2dbd9 100644 --- a/client/src/com/vaadin/client/ui/grid/Escalator.java +++ b/client/src/com/vaadin/client/ui/grid/Escalator.java @@ -52,6 +52,7 @@ import com.vaadin.client.ui.grid.PositionFunction.WebkitTranslate3DPosition; import com.vaadin.client.ui.grid.ScrollbarBundle.HorizontalScrollbarBundle; import com.vaadin.client.ui.grid.ScrollbarBundle.VerticalScrollbarBundle; import com.vaadin.shared.ui.grid.Range; +import com.vaadin.shared.ui.grid.ScrollDestination; import com.vaadin.shared.util.SharedUtil; /*- @@ -548,6 +549,80 @@ public class Escalator extends Widget { private static final int ROW_HEIGHT_PX = 20; + /** + * ScrollDestination case-specific handling logic. + */ + private static double getScrollPos(final ScrollDestination destination, + final double targetStartPx, final double targetEndPx, + final double viewportStartPx, final double viewportEndPx, + final int padding) { + + final double viewportLength = viewportEndPx - viewportStartPx; + + switch (destination) { + + /* + * Scroll as little as possible to show the target element. If the + * element fits into view, this works as START or END depending on the + * current scroll position. If the element does not fit into view, this + * works as START. + */ + case ANY: { + final double startScrollPos = targetStartPx - padding; + final double endScrollPos = targetEndPx + padding - viewportLength; + + if (startScrollPos < viewportStartPx) { + return startScrollPos; + } else if (targetEndPx + padding > viewportEndPx) { + return endScrollPos; + } else { + // NOOP, it's already visible + return viewportStartPx; + } + } + + /* + * Scrolls so that the element is shown at the end of the viewport. The + * viewport will, however, not scroll before its first element. + */ + case END: { + return targetEndPx + padding - viewportLength; + } + + /* + * Scrolls so that the element is shown in the middle of the viewport. + * The viewport will, however, not scroll beyond its contents, given + * more elements than what the viewport is able to show at once. Under + * no circumstances will the viewport scroll before its first element. + */ + case MIDDLE: { + final double targetMiddle = targetStartPx + + (targetEndPx - targetStartPx) / 2; + return targetMiddle - viewportLength / 2; + } + + /* + * Scrolls so that the element is shown at the start of the viewport. + * The viewport will, however, not scroll beyond its contents. + */ + case START: { + return targetStartPx - padding; + } + + /* + * Throw an error if we're here. This can only mean that + * ScrollDestination has been carelessly amended.. + */ + default: { + throw new IllegalArgumentException( + "Internal: ScrollDestination has been modified, " + + "but Escalator.getScrollPos has not been updated " + + "to match new values."); + } + } + + } + /** An inner class that handles all logic related to scrolling. */ private class Scroller extends JsniWorkaround { private double lastScrollTop = 0; @@ -900,7 +975,7 @@ public class Escalator extends Widget { viewportEndPx -= Util.getNativeScrollbarSize(); } - final double scrollLeft = destination.getScrollPos(targetStartPx, + final double scrollLeft = getScrollPos(destination, targetStartPx, targetEndPx, viewportStartPx, viewportEndPx, padding); /* @@ -921,7 +996,7 @@ public class Escalator extends Widget { final double viewportEndPx = viewportStartPx + body.calculateHeight(); - final double scrollTop = destination.getScrollPos(targetStartPx, + final double scrollTop = getScrollPos(destination, targetStartPx, targetEndPx, viewportStartPx, viewportEndPx, padding); /* @@ -3307,33 +3382,6 @@ public class Escalator extends Widget { /** * Scrolls the body horizontally so that the column at the given index is - * visible. - * - * @param columnIndex - * the index of the column to scroll to - * @param destination - * where the column should be aligned visually after scrolling - * @throws IndexOutOfBoundsException - * if {@code columnIndex} is not a valid index for an existing - * column - * @throws IllegalArgumentException - * if the column is frozen - */ - public void scrollToColumn(final int columnIndex, - final ScrollDestination destination) - throws IndexOutOfBoundsException, IllegalArgumentException { - verifyValidColumnIndex(columnIndex); - - if (columnIndex < columnConfiguration.frozenColumns) { - throw new IllegalArgumentException("The given column index " - + columnIndex + " is frozen."); - } - - scroller.scrollToColumn(columnIndex, destination, 0); - } - - /** - * Scrolls the body horizontally so that the column at the given index is * visible and there is at least {@code padding} pixels to the given scroll * destination. * @@ -3348,14 +3396,15 @@ public class Escalator extends Widget { * if {@code columnIndex} is not a valid index for an existing * column * @throws IllegalArgumentException - * if {@code destination} is {@link ScrollDestination#MIDDLE}, - * because having a padding on a centered column is undefined - * behavior or if the column is frozen + * if {@code destination} is {@link ScrollDestination#MIDDLE} + * and padding is nonzero, because having a padding on a + * centered column is undefined behavior, or if the column is + * frozen */ public void scrollToColumn(final int columnIndex, final ScrollDestination destination, final int padding) throws IndexOutOfBoundsException, IllegalArgumentException { - if (destination == ScrollDestination.MIDDLE) { + if (destination == ScrollDestination.MIDDLE && padding != 0) { throw new IllegalArgumentException( "You cannot have a padding with a MIDDLE destination"); } @@ -3379,26 +3428,6 @@ public class Escalator extends Widget { } /** - * Scrolls the body vertically so that the row at the given index is - * visible. - * - * @param rowIndex - * the index of the row to scroll to - * @param destination - * where the row should be aligned visually after scrolling - * @throws IndexOutOfBoundsException - * if {@code rowIndex} is not a valid index for an existing - * logical row - */ - public void scrollToRow(final int rowIndex, - final ScrollDestination destination) - throws IndexOutOfBoundsException { - verifyValidRowIndex(rowIndex); - - scroller.scrollToRow(rowIndex, destination, 0); - } - - /** * Scrolls the body vertically so that the row at the given index is visible * and there is at least {@literal padding} pixels to the given scroll * destination. @@ -3413,14 +3442,14 @@ public class Escalator extends Widget { * @throws IndexOutOfBoundsException * if {@code rowIndex} is not a valid index for an existing row * @throws IllegalArgumentException - * if {@code destination} is {@link ScrollDestination#MIDDLE}, - * because having a padding on a centered row is undefined - * behavior + * if {@code destination} is {@link ScrollDestination#MIDDLE} + * and padding is nonzero, because having a padding on a + * centered row is undefined behavior */ public void scrollToRow(final int rowIndex, final ScrollDestination destination, final int padding) throws IndexOutOfBoundsException, IllegalArgumentException { - if (destination == ScrollDestination.MIDDLE) { + if (destination == ScrollDestination.MIDDLE && padding != 0) { throw new IllegalArgumentException( "You cannot have a padding with a MIDDLE destination"); } diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index 015028765b..02aa194655 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -18,6 +18,7 @@ package com.vaadin.client.ui.grid; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.logging.Logger; import com.google.gwt.core.shared.GWT; import com.google.gwt.dom.client.Element; @@ -28,6 +29,8 @@ import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.data.DataChangeHandler; import com.vaadin.client.data.DataSource; import com.vaadin.client.ui.grid.renderers.TextRenderer; +import com.vaadin.shared.ui.grid.GridConstants; +import com.vaadin.shared.ui.grid.ScrollDestination; import com.vaadin.shared.util.SharedUtil; /** @@ -1225,4 +1228,91 @@ public class Grid<T> extends Composite { */ return escalator.addRowVisibilityChangeHandler(handler); } + + /** + * Scrolls to a certain row, using {@link ScrollDestination#ANY}. + * + * @param rowIndex + * zero-based index of the row to scroll to. + * @throws IllegalArgumentException + * if rowIndex is below zero, or above the maximum value + * supported by the data source. + */ + public void scrollToRow(int rowIndex) throws IllegalArgumentException { + scrollToRow(rowIndex, ScrollDestination.ANY, + GridConstants.DEFAULT_PADDING); + } + + /** + * Scrolls to a certain row, using user-specified scroll destination. + * + * @param rowIndex + * zero-based index of the row to scroll to. + * @param destination + * desired destination placement of scrolled-to-row. See + * {@link ScrollDestination} for more information. + * @throws IllegalArgumentException + * if rowIndex is below zero, or above the maximum value + * supported by the data source. + */ + public void scrollToRow(int rowIndex, ScrollDestination destination) + throws IllegalArgumentException { + scrollToRow(rowIndex, destination, + destination == ScrollDestination.MIDDLE ? 0 + : GridConstants.DEFAULT_PADDING); + } + + /** + * Scrolls to a certain row using only user-specified parameters. + * + * @param rowIndex + * zero-based index of the row to scroll to. + * @param destination + * desired destination placement of scrolled-to-row. See + * {@link ScrollDestination} for more information. + * @param paddingPx + * number of pixels to overscroll. Behavior depends on + * destination. + * @throws IllegalArgumentException + * if {@code destination} is {@link ScrollDestination#MIDDLE} + * and padding is nonzero, because having a padding on a + * centered row is undefined behavior, or if rowIndex is below + * zero or above the row count of the data source. + */ + private void scrollToRow(int rowIndex, ScrollDestination destination, + int paddingPx) throws IllegalArgumentException { + int maxsize = escalator.getBody().getRowCount() - 1; + + if (rowIndex < 0) { + throw new IllegalArgumentException("Row index (" + rowIndex + + ") is below zero!"); + } + + if (rowIndex > maxsize) { + throw new IllegalArgumentException("Row index (" + rowIndex + + ") is above maximum (" + maxsize + ")!"); + } + + escalator.scrollToRow(rowIndex, destination, paddingPx); + } + + /** + * Scrolls to the beginning of the very first row. + */ + public void scrollToStart() { + scrollToRow(0, ScrollDestination.START); + } + + /** + * Scrolls to the end of the very last row. + */ + public void scrollToEnd() { + scrollToRow(escalator.getBody().getRowCount() - 1, + ScrollDestination.END); + } + + private static final Logger getLogger() { + return Logger.getLogger(Grid.class.getName()); + } + } diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index f04326c7e6..5e0664667d 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -29,9 +29,11 @@ import com.vaadin.client.ui.AbstractComponentConnector; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.grid.ColumnGroupRowState; import com.vaadin.shared.ui.grid.ColumnGroupState; +import com.vaadin.shared.ui.grid.GridClientRpc; import com.vaadin.shared.ui.grid.GridColumnState; import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.shared.ui.grid.ScrollDestination; /** * Connects the client side {@link Grid} widget with the server side @@ -96,6 +98,23 @@ public class GridConnector extends AbstractComponentConnector { event.getVisibleRowCount()); } }); + + registerRpc(GridClientRpc.class, new GridClientRpc() { + @Override + public void scrollToStart() { + getWidget().scrollToStart(); + } + + @Override + public void scrollToEnd() { + getWidget().scrollToEnd(); + } + + @Override + public void scrollToRow(int row, ScrollDestination destination) { + getWidget().scrollToRow(row, destination); + } + }); } @Override diff --git a/client/src/com/vaadin/client/ui/grid/ScrollDestination.java b/client/src/com/vaadin/client/ui/grid/ScrollDestination.java deleted file mode 100644 index e14f50ff7c..0000000000 --- a/client/src/com/vaadin/client/ui/grid/ScrollDestination.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2000-2013 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.ui.grid; - -/** - * The destinations that are supported in an Escalator when scrolling rows or - * columns into view. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public enum ScrollDestination { - - /** - * Scroll as little as possible to show the target element. If the element - * fits into view, this works as START or END depending on the current - * scroll position. If the element does not fit into view, this works as - * START. - */ - ANY { - @Override - double getScrollPos(final double targetStartPx, - final double targetEndPx, final double viewportStartPx, - final double viewportEndPx, final int padding) { - - final double startScrollPos = targetStartPx - padding; - final double viewportLength = viewportEndPx - viewportStartPx; - final double endScrollPos = targetEndPx + padding - viewportLength; - - if (startScrollPos < viewportStartPx) { - return startScrollPos; - } else if (targetEndPx + padding > viewportEndPx) { - return endScrollPos; - } else { - // NOOP, it's already visible - return viewportStartPx; - } - } - }, - - /** - * Scrolls so that the element is shown at the start of the viewport. The - * viewport will, however, not scroll beyond its contents. - */ - START { - @Override - double getScrollPos(final double targetStartPx, - final double targetEndPx, final double viewportStartPx, - final double viewportEndPx, final int padding) { - return targetStartPx - padding; - } - }, - - /** - * Scrolls so that the element is shown in the middle of the viewport. The - * viewport will, however, not scroll beyond its contents, given more - * elements than what the viewport is able to show at once. Under no - * circumstances will the viewport scroll before its first element. - */ - MIDDLE { - @Override - double getScrollPos(final double targetStartPx, - final double targetEndPx, final double viewportStartPx, - final double viewportEndPx, final int padding) { - final double targetMiddle = targetStartPx - + (targetEndPx - targetStartPx) / 2; - final double viewportLength = viewportEndPx - viewportStartPx; - return targetMiddle - viewportLength / 2; - } - }, - - /** - * Scrolls so that the element is shown at the end of the viewport. The - * viewport will, however, not scroll before its first element. - */ - END { - @Override - double getScrollPos(final double targetStartPx, - final double targetEndPx, final double viewportStartPx, - final double viewportEndPx, final int padding) { - final double viewportLength = viewportEndPx - viewportStartPx; - return targetEndPx + padding - viewportLength; - } - }; - - abstract double getScrollPos(final double targetStartPx, - final double targetEndPx, final double viewportStartPx, - final double viewportEndPx, final int padding); -} diff --git a/client/src/com/vaadin/shared/ui/grid/ScrollDestination.java b/client/src/com/vaadin/shared/ui/grid/ScrollDestination.java new file mode 100644 index 0000000000..decc2fab5f --- /dev/null +++ b/client/src/com/vaadin/shared/ui/grid/ScrollDestination.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2013 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.shared.ui.grid; + +/** + * Enumeration, specifying the destinations that are supported when scrolling + * rows or columns into view. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public enum ScrollDestination { + + /** + * Scroll as little as possible to show the target element. If the element + * fits into view, this works as START or END depending on the current + * scroll position. If the element does not fit into view, this works as + * START. + */ + ANY, + + /** + * Scrolls so that the element is shown at the start of the viewport. The + * viewport will, however, not scroll beyond its contents. + */ + START, + + /** + * Scrolls so that the element is shown in the middle of the viewport. The + * viewport will, however, not scroll beyond its contents, given more + * elements than what the viewport is able to show at once. Under no + * circumstances will the viewport scroll before its first element. + */ + MIDDLE, + + /** + * Scrolls so that the element is shown at the end of the viewport. The + * viewport will, however, not scroll before its first element. + */ + END + +} |