]> source.dussan.org Git - vaadin-framework.git/commitdiff
refactored pagedown/up and home/end navigation, should fix excessive server visits...
authorMatti Tahvonen <matti.tahvonen@itmill.com>
Fri, 3 Sep 2010 14:54:50 +0000 (14:54 +0000)
committerMatti Tahvonen <matti.tahvonen@itmill.com>
Fri, 3 Sep 2010 14:54:50 +0000 (14:54 +0000)
svn changeset:14713/svn branch:6.4

src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java

index 1120c5cf1dff95749a7e15e2421edde24083b34e..59ac9996927e4e1b1cfdcc83e6a81f7299b9d072 100644 (file)
@@ -842,29 +842,26 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
             selectionChanged = false;
         }
 
-        // This is called when the Home button has been pressed and the pages
-        // changes
-        if (selectFirstItemInNextRender) {
-            selectFirstRenderedRow(false);
-            selectFirstItemInNextRender = false;
-        }
-        // The same if the table is not selectable
-        if (focusFirstItemInNextRender) {
-            selectFirstRenderedRow(true);
-            focusFirstItemInNextRender = false;
+        /*
+         * This is called when the Home or page up button has been pressed in
+         * selectable mode and the next selected row was not yet rendered in the
+         * client
+         */
+        if (selectFirstItemInNextRender || focusFirstItemInNextRender) {
+            selectFirstRenderedRowInViewPort(focusFirstItemInNextRender);
+            selectFirstItemInNextRender = focusFirstItemInNextRender = false;
         }
 
-        // This is called when the End button has been pressed and the pages
-        // changes
-        if (selectLastItemInNextRender) {
-            selectLastRenderedRow(false);
-            selectLastItemInNextRender = false;
-        }
-        // the same if not selectable
-        if (focusLastItemInNextRender) {
-            selectLastRenderedRow(true);
-            focusLastItemInNextRender = false;
+        /*
+         * This is called when the page down or end button has been pressed in
+         * selectable mode and the next selected row was not yet rendered in the
+         * client
+         */
+        if (selectLastItemInNextRender || focusLastItemInNextRender) {
+            selectLastRenderedRowInViewPort(focusLastItemInNextRender);
+            selectLastItemInNextRender = focusLastItemInNextRender = false;
         }
+        multiselectPending = false;
 
         if (focusedRow != null) {
             if (!focusedRow.isAttached()) {
@@ -894,42 +891,48 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
     }
 
     /**
-     * Selects the last rendered row in the table
+     * Selects the last row visible in the table
      * 
      * @param focusOnly
      *            Should the focus only be moved to the last row
      */
-    private void selectLastRenderedRow(boolean focusOnly) {
-        VScrollTableRow row = null;
-        Iterator<Widget> it = scrollBody.iterator();
-        while (it.hasNext()) {
-            row = (VScrollTableRow) it.next();
-        }
-        if (row != null) {
-            setRowFocus(row);
-            if (!focusOnly) {
-                deselectAll();
-                selectFocusedRow(false, false);
-                sendSelectedRows();
-            }
+    private void selectLastRenderedRowInViewPort(boolean focusOnly) {
+        int index = firstRowInViewPort + getFullyVisibleRowCount();
+        VScrollTableRow lastRowInViewport = scrollBody.getRowByRowIndex(index);
+        if (lastRowInViewport == null) {
+            // this should not happen in normal situations (white space at the
+            // end of viewport). Select the last rendered as a fallback.
+            lastRowInViewport = scrollBody.getRowByRowIndex(scrollBody
+                    .getLastRendered());
+            if (lastRowInViewport == null) {
+                return; // empty table
+            }
+        }
+        setRowFocus(lastRowInViewport);
+        if (!focusOnly) {
+            selectFocusedRow(false, multiselectPending);
+            sendSelectedRows();
         }
-
     }
 
     /**
-     * Selects the first rendered row
+     * Selects the first row visible in the table
      * 
      * @param focusOnly
      *            Should the focus only be moved to the first row
      */
-    private void selectFirstRenderedRow(boolean focusOnly) {
-        setRowFocus((VScrollTableRow) scrollBody.iterator().next());
+    private void selectFirstRenderedRowInViewPort(boolean focusOnly) {
+        int index = firstRowInViewPort;
+        VScrollTableRow firstInViewport = scrollBody.getRowByRowIndex(index);
+        if (firstInViewport == null) {
+            // this should not happen in normal situations
+            return;
+        }
+        setRowFocus(firstInViewport);
         if (!focusOnly) {
-            deselectAll();
-            selectFocusedRow(false, false);
+            selectFocusedRow(false, multiselectPending);
             sendSelectedRows();
         }
-
     }
 
     private void setCacheRate(double d) {
@@ -1609,10 +1612,10 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
 
         @Override
         public void run() {
-            if (client.hasActiveRequest()) {
+            if (client.hasActiveRequest() || navKeyDown) {
                 // if client connection is busy, don't bother loading it more
+                ApplicationConnection.getConsole().log("Postponed rowfetch");
                 schedule(250);
-
             } else {
 
                 int firstToBeRendered = scrollBody.firstRendered;
@@ -1669,10 +1672,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
             return reqFirstRow;
         }
 
-        public int getReqRows() {
-            return reqRows;
-        }
-
         /**
          * Sends request to refresh content at this position.
          */
@@ -3169,7 +3168,11 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
 
         public VScrollTableRow getRowByRowIndex(int indexInTable) {
             int internalIndex = indexInTable - firstRendered;
-            return (VScrollTableRow) renderedRows.get(internalIndex);
+            if (internalIndex >= 0 && internalIndex < renderedRows.size()) {
+                return (VScrollTableRow) renderedRows.get(internalIndex);
+            } else {
+                return null;
+            }
         }
 
         /**
@@ -4578,6 +4581,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
     private int scrollLeft;
     private int scrollTop;
     private VScrollTableDropHandler dropHandler;
+    private boolean navKeyDown;
+    private boolean multiselectPending;
 
     /**
      * @return border top + border bottom of the scrollable area of table
@@ -5095,85 +5100,190 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
 
         // Page Down navigation
         if (keycode == getNavigationPageDownKey()) {
-            int rowHeight = (int) scrollBody.getRowHeight();
-            int offset = pageLength * rowHeight - rowHeight;
-            scrollBodyPanel.setScrollPosition(scrollBodyPanel
-                    .getScrollPosition() + offset);
             if (isSelectable()) {
-                if (!moveFocusDown(pageLength - 2)) {
-                    final int lastRendered = scrollBody.getLastRendered();
-                    if (lastRendered == totalRows - 1) {
-                        selectLastRenderedRow(false);
+                /*
+                 * If selectable we plagiate MSW behaviour: first scroll to the
+                 * end of current view. If at the end, scroll down one page
+                 * length and keep the selected row in the bottom part of
+                 * visible area.
+                 */
+                if (!isFocusAtTheEndOfTable()) {
+                    VScrollTableRow lastVisibleRowInViewPort = scrollBody
+                            .getRowByRowIndex(firstRowInViewPort
+                                    + getFullyVisibleRowCount() - 1);
+                    if (lastVisibleRowInViewPort != null
+                            && lastVisibleRowInViewPort != focusedRow) {
+                        // focused row is not at the end of the table, move
+                        // focus and select the last visible row
+                        setRowFocus(lastVisibleRowInViewPort);
+                        selectFocusedRow(ctrl, shift);
+                        sendSelectedRows();
                     } else {
-                        selectLastItemInNextRender = true;
+                        int indexOfToBeFocused = focusedRow.getIndex()
+                                + getFullyVisibleRowCount();
+                        if (indexOfToBeFocused >= totalRows) {
+                            indexOfToBeFocused = totalRows - 1;
+                        }
+                        VScrollTableRow toBeFocusedRow = scrollBody
+                                .getRowByRowIndex(indexOfToBeFocused);
+
+                        if (toBeFocusedRow != null) {
+                            /*
+                             * if the next focused row is rendered
+                             */
+                            setRowFocus(toBeFocusedRow);
+                            selectFocusedRow(ctrl, shift);
+                            // TODO needs scrollintoview ?
+                            sendSelectedRows();
+                        } else {
+                            // scroll down by pixels and return, to wait for
+                            // new rows, then select the last item in the
+                            // viewport
+                            selectLastItemInNextRender = true;
+                            multiselectPending = shift;
+                            scrollByPagelenght(1);
+                        }
                     }
-                } else {
-                    selectFocusedRow(false, false);
-                    sendSelectedRows();
                 }
+            } else {
+                /* No selections, go page down by scrolling */
+                scrollByPagelenght(1);
             }
             return true;
         }
 
         // Page Up navigation
         if (keycode == getNavigationPageUpKey()) {
-            int rowHeight = (int) scrollBody.getRowHeight();
-            int offset = pageLength * rowHeight - rowHeight;
-            scrollBodyPanel.setScrollPosition(scrollBodyPanel
-                    .getScrollPosition() - offset);
             if (isSelectable()) {
-                if (!moveFocusUp(pageLength - 2)) {
-                    final int firstRendered = scrollBody.getFirstRendered();
-                    if (firstRendered == 0) {
-                        selectFirstRenderedRow(false);
+                /*
+                 * If selectable we plagiate MSW behaviour: first scroll to the
+                 * end of current view. If at the end, scroll down one page
+                 * length and keep the selected row in the bottom part of
+                 * visible area.
+                 */
+                if (!isFocusAtTheBeginningOfTable()) {
+                    VScrollTableRow firstVisibleRowInViewPort = scrollBody
+                            .getRowByRowIndex(firstRowInViewPort);
+                    if (firstVisibleRowInViewPort != null
+                            && firstVisibleRowInViewPort != focusedRow) {
+                        // focus is not at the beginning of the table, move
+                        // focus and select the first visible row
+                        setRowFocus(firstVisibleRowInViewPort);
+                        selectFocusedRow(ctrl, shift);
+                        sendSelectedRows();
                     } else {
-                        selectFirstItemInNextRender = true;
+                        int indexOfToBeFocused = focusedRow.getIndex()
+                                - getFullyVisibleRowCount();
+                        if (indexOfToBeFocused < 0) {
+                            indexOfToBeFocused = 0;
+                        }
+                        VScrollTableRow toBeFocusedRow = scrollBody
+                                .getRowByRowIndex(indexOfToBeFocused);
+
+                        if (toBeFocusedRow != null) { // if the next focused row
+                                                      // is rendered
+                            setRowFocus(toBeFocusedRow);
+                            selectFocusedRow(ctrl, shift);
+                            // TODO needs scrollintoview ?
+                            sendSelectedRows();
+                        } else {
+                            // unless waiting for the next rowset already
+                            // scroll down by pixels and return, to wait for
+                            // new rows, then select the last item in the
+                            // viewport
+                            selectFirstItemInNextRender = true;
+                            multiselectPending = shift;
+                            scrollByPagelenght(-1);
+                        }
                     }
-                } else {
-                    selectFocusedRow(false, false);
-                    sendSelectedRows();
                 }
+            } else {
+                /* No selections, go page up by scrolling */
+                scrollByPagelenght(-1);
             }
+
             return true;
         }
 
         // Goto start navigation
         if (keycode == getNavigationStartKey()) {
+            scrollBodyPanel.setScrollPosition(0);
             if (isSelectable()) {
-                final int firstRendered = scrollBody.getFirstRendered();
-                boolean focusOnly = ctrl;
-                if (firstRendered == 0) {
-                    selectFirstRenderedRow(focusOnly);
-                } else if (focusOnly) {
-                    focusFirstItemInNextRender = true;
+                if (focusedRow != null && focusedRow.getIndex() == 0) {
+                    return false;
                 } else {
-                    selectFirstItemInNextRender = true;
+                    VScrollTableRow rowByRowIndex = (VScrollTableRow) scrollBody
+                            .iterator().next();
+                    if (rowByRowIndex.getIndex() == 0) {
+                        setRowFocus(rowByRowIndex);
+                        selectFocusedRow(ctrl, shift);
+                        sendSelectedRows();
+                    } else {
+                        // first row of table will come in next row fetch
+                        if (ctrl) {
+                            focusFirstItemInNextRender = true;
+                        } else {
+                            selectFirstItemInNextRender = true;
+                            multiselectPending = shift;
+                        }
+                    }
                 }
             }
-            scrollBodyPanel.setScrollPosition(0);
             return true;
         }
 
         // Goto end navigation
         if (keycode == getNavigationEndKey()) {
+            scrollBodyPanel.setScrollPosition(scrollBody.getOffsetHeight());
             if (isSelectable()) {
                 final int lastRendered = scrollBody.getLastRendered();
-                boolean focusOnly = ctrl;
-                if (lastRendered == totalRows - 1) {
-                    selectLastRenderedRow(focusOnly);
-                } else if (focusOnly) {
-                    focusLastItemInNextRender = true;
+                if (lastRendered + 1 == totalRows) {
+                    VScrollTableRow rowByRowIndex = scrollBody
+                            .getRowByRowIndex(lastRendered);
+                    if (focusedRow != rowByRowIndex) {
+                        setRowFocus(rowByRowIndex);
+                        selectFocusedRow(ctrl, shift);
+                        sendSelectedRows();
+                    }
                 } else {
-                    selectLastItemInNextRender = true;
+                    if (ctrl) {
+                        focusLastItemInNextRender = true;
+                    } else {
+                        selectLastItemInNextRender = true;
+                        multiselectPending = shift;
+                    }
                 }
             }
-            scrollBodyPanel.setScrollPosition(scrollBody.getOffsetHeight());
             return true;
         }
 
         return false;
     }
 
+    private boolean isFocusAtTheBeginningOfTable() {
+        return focusedRow.getIndex() == 0;
+    }
+
+    private boolean isFocusAtTheEndOfTable() {
+        return focusedRow.getIndex() + 1 >= totalRows;
+    }
+
+    private int getFullyVisibleRowCount() {
+        return (int) (scrollBodyPanel.getOffsetHeight() / scrollBody
+                .getRowHeight());
+    }
+
+    private void scrollByPagelenght(int i) {
+        int pixels = i
+                * (int) (getFullyVisibleRowCount() * scrollBody.getRowHeight());
+        int newPixels = scrollBodyPanel.getScrollPosition() + pixels;
+        if (newPixels < 0) {
+            newPixels = 0;
+        } // else if too high, NOP (all know browsers accept illegally big
+          // values here)
+        scrollBodyPanel.setScrollPosition(newPixels);
+    }
+
     /*
      * (non-Javadoc)
      * 
@@ -5214,6 +5324,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
             if (handleNavigation(event.getNativeEvent().getKeyCode(),
                     event.isControlKeyDown() || event.isMetaKeyDown(),
                     event.isShiftKeyDown())) {
+                navKeyDown = true;
                 event.preventDefault();
             }
 
@@ -5261,6 +5372,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
     public void onBlur(BlurEvent event) {
         scrollBodyPanel.removeStyleName("focused");
         hasFocus = false;
+        navKeyDown = false;
 
         // Unfocus any row
         setRowFocus(null);
@@ -5358,6 +5470,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
                 scrollingVelocityTimer = null;
                 scrollingVelocity = 10;
             }
+            navKeyDown = false;
         }
     }
 
@@ -5370,6 +5483,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
         return keyCode == getNavigationUpKey()
                 || keyCode == getNavigationDownKey()
                 || keyCode == getNavigationPageUpKey()
-                || keyCode == getNavigationPageDownKey();
+                || keyCode == getNavigationPageDownKey()
+                || keyCode == getNavigationEndKey()
+                || keyCode == getNavigationStartKey();
     }
 }