]> source.dussan.org Git - vaadin-framework.git/commitdiff
Add scrollToRow/Column, also some final declarations (#12645)
authorHenrik Paul <henrik@vaadin.com>
Mon, 11 Nov 2013 13:04:45 +0000 (15:04 +0200)
committerHenrik Paul <henrik@vaadin.com>
Mon, 11 Nov 2013 13:04:45 +0000 (15:04 +0200)
Change-Id: I4142edff9bc078c35ee70643fc3368bec10003f9

client/src/com/vaadin/client/ui/grid/Escalator.java
client/src/com/vaadin/client/ui/grid/ScrollDestination.java
uitest/src/com/vaadin/tests/components/grid/GridTest.java
uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridClientRpc.java
uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridConnector.java
uitest/src/com/vaadin/tests/widgetset/client/grid/VTestGrid.java
uitest/src/com/vaadin/tests/widgetset/server/grid/TestGrid.java

index a67b701683b6543bf9ece5f407ba903ae57ee9f3..051203ecb6be4d293948383f3705a8870ab2fe9c 100644 (file)
@@ -211,6 +211,10 @@ public class Escalator extends Widget {
      * escalator DOM). NOTE: these bits can most often also be identified by
      * searching for code that call scrollElem.getScrollTop();.
      */
+    /*
+     * [[frozencol]]: This needs to be re-inspected once frozen columns are
+     * being implemented.
+     */
 
     private static final int ROW_HEIGHT_PX = 20;
     private static final int COLUMN_WIDTH_PX = 100;
@@ -432,6 +436,54 @@ public class Escalator extends Widget {
                 element.detachEvent("onmousewheel", this.@com.vaadin.client.ui.grid.JsniWorkaround::mousewheelListenerFunction);
             }
         }-*/;
+
+        public void scrollToColumn(final int columnIndex,
+                final ScrollDestination destination, final int padding) {
+            // TODO [[colwidth]]
+            final int targetStartPx = COLUMN_WIDTH_PX * columnIndex;
+            final int targetEndPx = targetStartPx + COLUMN_WIDTH_PX;
+
+            /*
+             * TODO [[frozencol]]: offset startPx by the pixels occupied by
+             * frozen columns.
+             */
+            final int viewportStartPx = getScrollLeft();
+            int viewportEndPx = viewportStartPx + getElement().getOffsetWidth();
+            if (needsVerticalScrollbars()) {
+                viewportEndPx -= Util.getNativeScrollbarSize();
+            }
+
+            final double scrollLeft = destination.getScrollPos(targetStartPx,
+                    targetEndPx, viewportStartPx, viewportEndPx, padding);
+
+            /*
+             * note that it doesn't matter if the scroll would go beyond the
+             * content, since the browser will adjust for that, and everything
+             * fall into line accordingly.
+             */
+            setScrollLeft((int) scrollLeft);
+        }
+
+        public void scrollToRow(final int rowIndex,
+                final ScrollDestination destination, final int padding) {
+            // TODO [[rowheight]]
+            final int targetStartPx = ROW_HEIGHT_PX * rowIndex;
+            final int targetEndPx = targetStartPx + ROW_HEIGHT_PX;
+
+            final double viewportStartPx = getScrollTop();
+            final double viewportEndPx = viewportStartPx
+                    + body.calculateHeight();
+
+            final double scrollTop = destination.getScrollPos(targetStartPx,
+                    targetEndPx, viewportStartPx, viewportEndPx, padding);
+
+            /*
+             * note that it doesn't matter if the scroll would go beyond the
+             * content, since the browser will adjust for that, and everything
+             * falls into line accordingly.
+             */
+            setScrollTop(scrollTop);
+        }
     }
 
     private static class CellImpl implements Cell {
@@ -2100,12 +2152,17 @@ public class Escalator extends Widget {
      * @throws IndexOutOfBoundsException
      *             if {@code columnIndex} is not a valid index for an existing
      *             column
+     * @throws IllegalArgumentException
+     *             if {@code columnIndex} indicates a column that is set to be
+     *             frozen
      */
     public void scrollToColumn(final int columnIndex,
             final ScrollDestination destination)
             throws IndexOutOfBoundsException {
-        // FIXME [[escalator]]
-        throw new UnsupportedOperationException("Not implemented yet");
+        // TODO [[frozencol]] throw IAE if frozen
+        verifyValidColumnIndex(columnIndex);
+
+        scroller.scrollToColumn(columnIndex, destination, 0);
     }
 
     /**
@@ -2126,13 +2183,31 @@ public class Escalator extends Widget {
      * @throws IllegalArgumentException
      *             if {@code destination} is {@link ScrollDestination#MIDDLE},
      *             because having a padding on a centered column is undefined
-     *             behavior.
+     *             behavior
+     * @throws IllegalArgumentException
+     *             if {@code columnIndex} indicates a column that is set to be
+     *             frozen
      */
     public void scrollToColumn(final int columnIndex,
             final ScrollDestination destination, final int padding)
             throws IndexOutOfBoundsException, IllegalArgumentException {
-        // FIXME [[escalator]]
-        throw new UnsupportedOperationException("Not implemented yet");
+        // TODO [[frozencol]] throw IAE if frozen
+        if (destination == ScrollDestination.MIDDLE) {
+            throw new IllegalArgumentException(
+                    "You cannot have a padding with a MIDDLE destination");
+        }
+        verifyValidColumnIndex(columnIndex);
+
+        scroller.scrollToColumn(columnIndex, destination, padding);
+    }
+
+    private void verifyValidColumnIndex(final int columnIndex)
+            throws IndexOutOfBoundsException {
+        if (columnIndex < 0
+                || columnIndex >= columnConfiguration.getColumnCount()) {
+            throw new IndexOutOfBoundsException("The given column index "
+                    + columnIndex + " does not exist.");
+        }
     }
 
     /**
@@ -2150,8 +2225,9 @@ public class Escalator extends Widget {
     public void scrollToRow(final int rowIndex,
             final ScrollDestination destination)
             throws IndexOutOfBoundsException {
-        // FIXME [[escalator]]
-        throw new UnsupportedOperationException("Not implemented yet");
+        verifyValidRowIndex(rowIndex);
+
+        scroller.scrollToRow(rowIndex, destination, 0);
     }
 
     /**
@@ -2161,7 +2237,6 @@ public class Escalator extends Widget {
      * 
      * @param rowIndex
      *            the index of the logical row to scroll to
-     * 
      * @param destination
      *            where the row should be aligned visually after scrolling
      * @param padding
@@ -2177,8 +2252,20 @@ public class Escalator extends Widget {
     public void scrollToRow(final int rowIndex,
             final ScrollDestination destination, final int padding)
             throws IndexOutOfBoundsException, IllegalArgumentException {
-        // FIXME [[escalator]]
-        throw new UnsupportedOperationException("Not implemented yet");
+        if (destination == ScrollDestination.MIDDLE) {
+            throw new IllegalArgumentException(
+                    "You cannot have a padding with a MIDDLE destination");
+        }
+        verifyValidRowIndex(rowIndex);
+
+        scroller.scrollToRow(rowIndex, destination, padding);
+    }
+
+    private void verifyValidRowIndex(final int rowIndex) {
+        if (rowIndex < 0 || rowIndex >= body.getRowCount()) {
+            throw new IndexOutOfBoundsException("The given row index "
+                    + rowIndex + " does not exist.");
+        }
     }
 
     private void recalculateElementSizes() {
index 8910216ac9a8c3977455c0d65843df56a062f0e3..e14f50ff7c453645d729f3785b35b485410587ce 100644 (file)
@@ -23,23 +23,80 @@ package com.vaadin.client.ui.grid;
  * @author Vaadin Ltd
  */
 public enum ScrollDestination {
+
     /**
-     * "scrollIntoView" i.e. 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.
+     * 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,
+    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 view port.
+     * Scrolls so that the element is shown at the start of the viewport. The
+     * viewport will, however, not scroll beyond its contents.
      */
-    START,
+    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 view port.
+     * 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,
+    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 view port.
+     * Scrolls so that the element is shown at the end of the viewport. The
+     * viewport will, however, not scroll before its first element.
      */
-    END
-}
\ No newline at end of file
+    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);
+}
index aab3c66acfc1958b2c7c0ec37344717a256d5ab9..27b1d6387d8362e4886b9be5b0157eed135dc5ae 100644 (file)
@@ -25,6 +25,7 @@ import com.vaadin.ui.Button;
 import com.vaadin.ui.Button.ClickEvent;
 import com.vaadin.ui.HorizontalLayout;
 import com.vaadin.ui.Layout;
+import com.vaadin.ui.NativeSelect;
 import com.vaadin.ui.TextField;
 
 /**
@@ -48,9 +49,9 @@ public class GridTest extends AbstractTestUI {
                     @Override
                     @SuppressWarnings("boxing")
                     public void buttonClick(final ClickEvent event) {
-                        int offset = Integer.valueOf(insertRowsOffset
+                        final int offset = Integer.valueOf(insertRowsOffset
                                 .getValue());
-                        int amount = Integer.valueOf(insertRowsAmount
+                        final int amount = Integer.valueOf(insertRowsAmount
                                 .getValue());
                         grid.insertRows(offset, amount);
                     }
@@ -67,9 +68,9 @@ public class GridTest extends AbstractTestUI {
                     @Override
                     @SuppressWarnings("boxing")
                     public void buttonClick(final ClickEvent event) {
-                        int offset = Integer.valueOf(removeRowsOffset
+                        final int offset = Integer.valueOf(removeRowsOffset
                                 .getValue());
-                        int amount = Integer.valueOf(removeRowsAmount
+                        final int amount = Integer.valueOf(removeRowsAmount
                                 .getValue());
                         grid.removeRows(offset, amount);
                     }
@@ -86,9 +87,9 @@ public class GridTest extends AbstractTestUI {
                     @Override
                     @SuppressWarnings("boxing")
                     public void buttonClick(final ClickEvent event) {
-                        int offset = Integer.valueOf(insertColumnsOffset
+                        final int offset = Integer.valueOf(insertColumnsOffset
                                 .getValue());
-                        int amount = Integer.valueOf(insertColumnsAmount
+                        final int amount = Integer.valueOf(insertColumnsAmount
                                 .getValue());
                         grid.insertColumns(offset, amount);
                     }
@@ -105,14 +106,88 @@ public class GridTest extends AbstractTestUI {
                     @Override
                     @SuppressWarnings("boxing")
                     public void buttonClick(final ClickEvent event) {
-                        int offset = Integer.valueOf(removeColumnsOffset
+                        final int offset = Integer.valueOf(removeColumnsOffset
                                 .getValue());
-                        int amount = Integer.valueOf(removeColumnsAmount
+                        final int amount = Integer.valueOf(removeColumnsAmount
                                 .getValue());
                         grid.removeColumns(offset, amount);
                     }
                 }));
         addComponent(removeColumnsLayout);
+
+        final HorizontalLayout rowScroll = new HorizontalLayout();
+        final NativeSelect destination = new NativeSelect();
+        destination.setNullSelectionAllowed(false);
+        destination.addItem("any");
+        destination.setValue("any");
+        destination.addItem("start");
+        destination.addItem("end");
+        destination.addItem("middle");
+        rowScroll.addComponent(destination);
+        final TextField rowIndex = new TextField();
+        rowScroll.addComponent(rowIndex);
+        final TextField rowPadding = new TextField();
+        rowScroll.addComponent(rowPadding);
+        rowScroll.addComponent(new Button("scroll to row",
+                new Button.ClickListener() {
+                    @Override
+                    public void buttonClick(final ClickEvent event) {
+                        int index;
+                        try {
+                            index = Integer.valueOf(rowIndex.getValue());
+                        } catch (NumberFormatException e) {
+                            index = 0;
+                        }
+
+                        int padding;
+                        try {
+                            padding = Integer.valueOf(rowPadding.getValue());
+                        } catch (NumberFormatException e) {
+                            padding = 0;
+                        }
+
+                        grid.scrollToRow(index,
+                                (String) destination.getValue(), padding);
+                    }
+                }));
+        addComponent(rowScroll);
+
+        final HorizontalLayout colScroll = new HorizontalLayout();
+        final NativeSelect colDestination = new NativeSelect();
+        colDestination.setNullSelectionAllowed(false);
+        colDestination.addItem("any");
+        colDestination.setValue("any");
+        colDestination.addItem("start");
+        colDestination.addItem("end");
+        colDestination.addItem("middle");
+        colScroll.addComponent(colDestination);
+        final TextField colIndex = new TextField();
+        colScroll.addComponent(colIndex);
+        final TextField colPadding = new TextField();
+        colScroll.addComponent(colPadding);
+        colScroll.addComponent(new Button("scroll to column",
+                new Button.ClickListener() {
+                    @Override
+                    public void buttonClick(final ClickEvent event) {
+                        int index;
+                        try {
+                            index = Integer.valueOf(colIndex.getValue());
+                        } catch (NumberFormatException e) {
+                            index = 0;
+                        }
+
+                        int padding;
+                        try {
+                            padding = Integer.valueOf(colPadding.getValue());
+                        } catch (NumberFormatException e) {
+                            padding = 0;
+                        }
+
+                        grid.scrollToColumn(index,
+                                (String) colDestination.getValue(), padding);
+                    }
+                }));
+        addComponent(colScroll);
     }
 
     @Override
index 878e04ef399de6a43729e876237c976d646e86eb..3c831d2cb496a79d4bdce5e41915a4fd932b09e5 100644 (file)
@@ -25,4 +25,8 @@ public interface TestGridClientRpc extends ClientRpc {
     void insertColumns(int offset, int amount);
 
     void removeColumns(int offset, int amount);
+
+    void scrollToRow(int index, String destination, int padding);
+
+    void scrollToColumn(int index, String destination, int padding);
 }
index 382d01e04e4978cb59c73b566548062368b880c6..80eeeb6849844300eb01307b6ee5d891fc4ba701 100644 (file)
@@ -16,6 +16,7 @@
 package com.vaadin.tests.widgetset.client.grid;
 
 import com.vaadin.client.ui.AbstractComponentConnector;
+import com.vaadin.client.ui.grid.ScrollDestination;
 import com.vaadin.shared.ui.Connect;
 import com.vaadin.tests.widgetset.server.grid.TestGrid;
 
@@ -50,6 +51,33 @@ public class TestGridConnector extends AbstractComponentConnector {
                 getWidget().getColumnConfiguration().insertColumns(offset,
                         amount);
             }
+
+            @Override
+            public void scrollToRow(int index, String destination, int padding) {
+                getWidget().scrollToRow(index, getDestination(destination),
+                        padding);
+            }
+
+            @Override
+            public void scrollToColumn(int index, String destination,
+                    int padding) {
+                getWidget().scrollToColumn(index, getDestination(destination),
+                        padding);
+            }
+
+            private ScrollDestination getDestination(String destination) {
+                final ScrollDestination d;
+                if (destination.equals("start")) {
+                    d = ScrollDestination.START;
+                } else if (destination.equals("middle")) {
+                    d = ScrollDestination.MIDDLE;
+                } else if (destination.equals("end")) {
+                    d = ScrollDestination.END;
+                } else {
+                    d = ScrollDestination.ANY;
+                }
+                return d;
+            }
         });
     }
 
index b3dff6733882cde99b3195cd775d5e6abdf7a0d3..04fe5561be519b7fa1dcabe4a83dde95ea3eb5f4 100644 (file)
@@ -6,6 +6,7 @@ import com.vaadin.client.ui.grid.CellRenderer;
 import com.vaadin.client.ui.grid.ColumnConfiguration;
 import com.vaadin.client.ui.grid.Escalator;
 import com.vaadin.client.ui.grid.RowContainer;
+import com.vaadin.client.ui.grid.ScrollDestination;
 
 public class VTestGrid extends Composite {
     public static class HeaderRenderer implements CellRenderer {
@@ -58,7 +59,7 @@ public class VTestGrid extends Composite {
     public VTestGrid() {
         initWidget(escalator);
         final ColumnConfiguration cConf = escalator.getColumnConfiguration();
-        cConf.insertColumns(cConf.getColumnCount(), 5);
+        cConf.insertColumns(cConf.getColumnCount(), 10);
 
         final RowContainer h = escalator.getHeader();
         h.setCellRenderer(new HeaderRenderer());
@@ -84,4 +85,22 @@ public class VTestGrid extends Composite {
     public ColumnConfiguration getColumnConfiguration() {
         return escalator.getColumnConfiguration();
     }
+
+    public void scrollToRow(int index, ScrollDestination destination,
+            int padding) {
+        if (padding != 0) {
+            escalator.scrollToRow(index, destination, padding);
+        } else {
+            escalator.scrollToRow(index, destination);
+        }
+    }
+
+    public void scrollToColumn(int index, ScrollDestination destination,
+            int padding) {
+        if (padding != 0) {
+            escalator.scrollToColumn(index, destination, padding);
+        } else {
+            escalator.scrollToColumn(index, destination);
+        }
+    }
 }
index 7fc20420d2d106a9e3c98391d3f235d2e16ce983..c4a642aafec88f3f7ee7f4c00f02768775c4147e 100644 (file)
@@ -53,4 +53,12 @@ public class TestGrid extends AbstractComponent {
     private TestGridClientRpc rpc() {
         return getRpcProxy(TestGridClientRpc.class);
     }
+
+    public void scrollToRow(int index, String destination, int padding) {
+        rpc().scrollToRow(index, destination, padding);
+    }
+
+    public void scrollToColumn(int index, String destination, int padding) {
+        rpc().scrollToColumn(index, destination, padding);
+    }
 }