From: John Alhroos Date: Tue, 4 May 2010 13:02:44 +0000 (+0000) Subject: Changes in Table: X-Git-Tag: 6.7.0.beta1~1695 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=2924b070b44a3f140dca181204187c31d5ac8739;p=vaadin-framework.git Changes in Table: - Changed keyboard selection mechanism - Added keyboard control with Home,End,Page Up and Page Down - Added focus outline svn changeset:13023/svn branch:6.4 --- diff --git a/WebContent/VAADIN/themes/base/styles.css b/WebContent/VAADIN/themes/base/styles.css index 393f4dc141..6a7f0255c3 100644 --- a/WebContent/VAADIN/themes/base/styles.css +++ b/WebContent/VAADIN/themes/base/styles.css @@ -1,5 +1,5 @@ -.v-theme-version:after {content:"6_4_0_dev-20100420";} -.v-theme-version-6_4_0_dev-20100420 {display: none;} +.v-theme-version:after {content:"6_4_0_dev-20100504";} +.v-theme-version-6_4_0_dev-20100504 {display: none;} /* Automatically compiled css file from subdirectories. */ .v-absolutelayout-wrapper { @@ -1423,6 +1423,9 @@ div.v-progressindicator-indeterminate-disabled { .v-ie7 .v-table { overflow: visible; } +.v-table-body.focused{ + border-color: #388ddd; +} .v-table-header-wrap { overflow: hidden; border: 1px solid #aaa; @@ -1452,10 +1455,9 @@ div.v-progressindicator-indeterminate-disabled { border-top: none; background: #efefef; } -.v-table-footer table, -.v-table-table { +.v-table-footer table{ border-spacing: 0; - border-collapse: separate; + border-collapse: collapse; margin: 0; padding: 0; border: 0; @@ -1575,6 +1577,10 @@ div.v-progressindicator-indeterminate-disabled { display: block; text-align: center; } +.v-table-body:focus, +.v-table-body-wrapper:focus{ + outline: none; +} /* row in column selector */ .v-on { diff --git a/WebContent/VAADIN/themes/base/table/table.css b/WebContent/VAADIN/themes/base/table/table.css index 421aad5488..7fdcd5b98a 100644 --- a/WebContent/VAADIN/themes/base/table/table.css +++ b/WebContent/VAADIN/themes/base/table/table.css @@ -29,6 +29,9 @@ .v-ie7 .v-table { overflow: visible; } +.v-table-body.focused{ + border-color: #388ddd; +} .v-table-header-wrap { overflow: hidden; border: 1px solid #aaa; diff --git a/WebContent/VAADIN/themes/reindeer/styles.css b/WebContent/VAADIN/themes/reindeer/styles.css index 20c7bb00b0..42f18208cb 100644 --- a/WebContent/VAADIN/themes/reindeer/styles.css +++ b/WebContent/VAADIN/themes/reindeer/styles.css @@ -1,5 +1,5 @@ -.v-theme-version:after {content:"6_4_0_dev-20100420";} -.v-theme-version-6_4_0_dev-20100420 {display: none;} +.v-theme-version:after {content:"6_4_0_dev-20100504";} +.v-theme-version-6_4_0_dev-20100504 {display: none;} /* Automatically compiled css file from subdirectories. */ .v-absolutelayout-wrapper { @@ -1423,6 +1423,9 @@ div.v-progressindicator-indeterminate-disabled { .v-ie7 .v-table { overflow: visible; } +.v-table-body.focused{ + border-color: #388ddd; +} .v-table-header-wrap { overflow: hidden; border: 1px solid #aaa; @@ -1452,10 +1455,9 @@ div.v-progressindicator-indeterminate-disabled { border-top: none; background: #efefef; } -.v-table-footer table, -.v-table-table { +.v-table-footer table{ border-spacing: 0; - border-collapse: separate; + border-collapse: collapse; margin: 0; padding: 0; border: 0; @@ -1575,6 +1577,10 @@ div.v-progressindicator-indeterminate-disabled { display: block; text-align: center; } +.v-table-body:focus, +.v-table-body-wrapper:focus{ + outline: none; +} /* row in column selector */ .v-on { @@ -4240,6 +4246,17 @@ td.v-datefield-calendarpanel-nextyear { .v-table .v-selected .v-table-cell-content { border-right-color: #466c90; } +.v-table-body:focus{ + outline: none; +} +.v-table .v-table-focus .v-table-cell-content{ + border-top: 1px dotted black; + border-bottom: 1px dotted black; +} +.v-table .v-table-focus .v-table-cell-content .v-table-cell-wrapper{ + padding-top:2px; + padding-bottom:2px; +} .v-table-column-selector { width: 16px; height: 20px; diff --git a/WebContent/VAADIN/themes/runo/styles.css b/WebContent/VAADIN/themes/runo/styles.css index 3c6e578d37..ccaa635e66 100644 --- a/WebContent/VAADIN/themes/runo/styles.css +++ b/WebContent/VAADIN/themes/runo/styles.css @@ -1,5 +1,5 @@ -.v-theme-version:after {content:"6_4_0_dev-20100429";} -.v-theme-version-6_4_0_dev-20100429 {display: none;} +.v-theme-version:after {content:"6_4_0_dev-20100504";} +.v-theme-version-6_4_0_dev-20100504 {display: none;} /* Automatically compiled css file from subdirectories. */ .v-absolutelayout-wrapper { @@ -1423,6 +1423,9 @@ div.v-progressindicator-indeterminate-disabled { .v-ie7 .v-table { overflow: visible; } +.v-table-body.focused{ + border-color: #388ddd; +} .v-table-header-wrap { overflow: hidden; border: 1px solid #aaa; diff --git a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java index 17b822a204..b7feba223a 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java @@ -145,12 +145,33 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, * are selecting a range which is over several pages long */ private int lastSelectedRowKey = -1; - + + /* + * These are used when jumping between pages when pressing Home and End + */ + private boolean selectLastItemInNextRender = false; + private boolean selectFirstItemInNextRender = false; + private boolean focusFirstItemInNextRender = false; + private boolean focusLastItemInNextRender = false; + /* * The currently focused row */ private VScrollTableRow focusedRow; + /* + * Flag for notifying when the selection has changed and should be sent to + * the server + */ + private boolean selectionChanged = false; + + /* + * The speed (in pixels) which the scrolling scrolls vertically/horizontally + */ + private int scrollingVelocity = 10; + + private Timer scrollingVelocityTimer = null;; + /** * Represents a select range of rows */ @@ -187,6 +208,18 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, public String toString() { return startRowKey + "-" + endRowKey; } + + public boolean inRange(int key) { + return key >= startRowKey && key <= endRowKey; + } + + public int getStartKey() { + return startRowKey; + } + + public int getEndKey() { + return endRowKey; + } }; private final HashSet selectedRowRanges = new HashSet(); @@ -246,15 +279,13 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, private int multiselectmode; - - public VScrollTable() { bodyContainerFocus.setStyleName(CLASSNAME + "-body-wrapper"); /* - * Firefox handler auto-repeat works correctly only if we use a key - * press handler, other browsers handle it correctly when using a key - * down handler + * Firefox auto-repeat works correctly only if we use a key press + * handler, other browsers handle it correctly when using a key down + * handler */ if (BrowserInfo.get().isGecko()) { bodyContainerFocus.addKeyPressHandler(this); @@ -275,24 +306,64 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, add(tFoot); rowRequestHandler = new RowRequestHandler(); + + /* + * We need to use the sinkEvents method to catch the keyUp events so we + * can cache a single shift. KeyUpHandler cannot do this. + */ + sinkEvents(Event.ONKEYUP); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt.user + * .client.Event) + */ + @Override + public void onBrowserEvent(Event event) { + if (event.getTypeInt() == Event.ONKEYUP) { + if (event.getKeyCode() == KeyCodes.KEY_SHIFT) { + sendSelectedRows(); + } else if ((event.getKeyCode() == getNavigationUpKey() + || event.getKeyCode() == getNavigationDownKey() + || event.getKeyCode() == getNavigationPageUpKey() || event + .getKeyCode() == getNavigationPageDownKey()) + && !event.getShiftKey()) { + sendSelectedRows(); + } + + scrollingVelocityTimer.cancel(); + scrollingVelocityTimer = null; + scrollingVelocity = 10; + } + } + + /** + * Moves the focus one step down + * + * @return Returns true if succeeded + */ + private boolean moveFocusDown() { + return moveFocusDown(0); } /** - * Moves the selection head one row downloads + * Moves the focus down by 1+offset rows * * @return Returns true if succeeded, else false if the selection could not * be move downwards */ - private boolean moveSelectionDown() { + private boolean moveFocusDown(int offset) { if (selectMode > VScrollTable.SELECT_MODE_NONE) { - if (focusedRow == null) { - setRowFocus((VScrollTableRow) scrollBody.iterator().next()); - return true; + if (focusedRow == null && scrollBody.iterator().hasNext()) { + return setRowFocus((VScrollTableRow) scrollBody.iterator() + .next()); } else { - VScrollTableRow next = getNextRow(focusedRow); + VScrollTableRow next = getNextRow(focusedRow, offset); if (next != null) { - setRowFocus(next); - return true; + return setRowFocus(next); } } } @@ -301,22 +372,30 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, } /** - * Moves the selection head one row upwards + * Moves the selection one step up + * + * @return Returns true if succeeded + */ + private boolean moveFocusUp() { + return moveFocusUp(0); + } + + /** + * Moves the focus row upwards * * @return Returns true if succeeded, else false if the selection could not * be move upwards * */ - private boolean moveSelectionUp() { + private boolean moveFocusUp(int offset) { if (selectMode > VScrollTable.SELECT_MODE_NONE) { - if (focusedRow == null) { - setRowFocus((VScrollTableRow) scrollBody.iterator().next()); - return true; + if (focusedRow == null && scrollBody.iterator().hasNext()) { + return setRowFocus((VScrollTableRow) scrollBody.iterator() + .next()); } else { - VScrollTableRow prev = getPreviousRow(focusedRow); + VScrollTableRow prev = getPreviousRow(focusedRow, offset); if (prev != null) { - setRowFocus(prev); - return true; + return setRowFocus(prev); } } } @@ -335,21 +414,80 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, */ private void selectFocusedRow(boolean ctrlSelect, boolean shiftSelect) { if (focusedRow != null) { - if (ctrlSelect && !shiftSelect && selectMode == SELECT_MODE_MULTI) { - focusedRow.toggleSelection(ctrlSelect); - } else if (!ctrlSelect && shiftSelect - && selectMode == SELECT_MODE_MULTI) { - focusedRow.toggleShiftSelection(true); - } else if (ctrlSelect && shiftSelect - && selectMode == SELECT_MODE_MULTI) { - focusedRow.toggleShiftSelection(false); - } else { + // Arrows moves the selection and clears previous selections + if (selectMode > SELECT_MODE_NONE && !ctrlSelect && !shiftSelect) { deselectAll(); - focusedRow.toggleSelection(true); + focusedRow.toggleSelection(!ctrlSelect); + } + + // Ctrl+arrows moves selection head + else if (selectMode > SELECT_MODE_NONE && ctrlSelect + && !shiftSelect) { + // No selection, only selection head is moved + } + + // Shift+arrows selection selects a range + else if (selectMode == SELECT_MODE_MULTI && !ctrlSelect + && shiftSelect) { + focusedRow.toggleShiftSelection(shiftSelect); } } } + /** + * Sends the selection to the server + */ + private void sendSelectedRows(){ + // Don't send anything if selection has not changed + if (!selectionChanged) { + return; + } + + // Reset selection changed flag + selectionChanged = false; + + // Note: changing the immediateness of this + // might + // require changes to "clickEvent" immediateness + // also. + if (multiselectmode == MULTISELECT_MODE_DEFAULT) { + // Convert ranges to a set of strings + Set ranges = new HashSet(); + for (SelectionRange range : selectedRowRanges) { + ranges.add(range.toString()); + } + + /* + * Clear ranges since they are transformed + * on the server side to row selections + */ + if (immediate) { + selectedRowRanges.clear(); + } + + // Send the selected row ranges + client + .updateVariable( + paintableId, + "selectedRanges", + ranges + .toArray(new String[selectedRowRanges + .size()]), + false); + } + + // Send the selected rows + client + .updateVariable( + paintableId, + "selected", + selectedRowKeys + .toArray(new String[selectedRowKeys + .size()]), + immediate); + + } + /** * Get the key that moves the selection head upwards. By default it is the * up arrow key but by overriding this you can change the key to whatever @@ -405,6 +543,57 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, return 32; } + /** + * Get the key the moves the selection one page up in the table. By default + * this is the Page Up key but by overriding this you can change the key to + * whatever you want. + * + * @return + */ + protected int getNavigationPageUpKey() { + return KeyCodes.KEY_PAGEUP; + } + + /** + * Get the key the moves the selection one page down in the table. By + * default this is the Page Down key but by overriding this you can change + * the key to whatever you want. + * + * @return + */ + protected int getNavigationPageDownKey() { + return KeyCodes.KEY_PAGEDOWN; + } + + /** + * Get the key the moves the selection to the beginning of the table. By + * default this is the Home key but by overriding this you can change the + * key to whatever you want. + * + * @return + */ + protected int getNavigationStartKey() { + return KeyCodes.KEY_HOME; + } + + /** + * Get the key the moves the selection to the end of the table. By default + * this is the End key but by overriding this you can change the key to + * whatever you want. + * + * @return + */ + protected int getNavigationEndKey() { + return KeyCodes.KEY_END; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL(com.vaadin.terminal + * .gwt.client.UIDL, com.vaadin.terminal.gwt.client.ApplicationConnection) + */ @SuppressWarnings("unchecked") public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { rendering = true; @@ -590,10 +779,62 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, hideScrollPositionAnnotation(); purgeUnregistryBag(); + + // This is called when the Home button has been pressed and the pages + // changes + if (selectFirstItemInNextRender) { + selectFirstRenderedRow(false); + selectFirstItemInNextRender = false; + } + + if (focusFirstItemInNextRender) { + selectFirstRenderedRow(true); + focusFirstItemInNextRender = false; + } + + // This is called when the End button has been pressed and the pages + // changes + if (selectLastItemInNextRender) { + selectLastRenderedRow(false); + selectLastItemInNextRender = false; + } + + if (focusLastItemInNextRender) { + selectLastRenderedRow(true); + focusLastItemInNextRender = false; + } + rendering = false; headerChangedDuringUpdate = false; } + private void selectLastRenderedRow(boolean focusOnly) { + VScrollTableRow row = null; + Iterator it = scrollBody.iterator(); + while (it.hasNext()) { + row = (VScrollTableRow) it.next(); + } + if (row != null) { + setRowFocus(row); + if (!focusOnly) { + deselectAll(); + selectFocusedRow(false, false); + sendSelectedRows(); + } + } + + } + + private void selectFirstRenderedRow(boolean focusOnly) { + setRowFocus((VScrollTableRow) scrollBody.iterator().next()); + if (!focusOnly) { + deselectAll(); + selectFocusedRow(false, false); + sendSelectedRows(); + } + + } + private void setCacheRate(double d) { if (cache_rate != d) { cache_rate = d; @@ -812,25 +1053,27 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, } return null; } - + /** * Returns the next row to the given row * * @param row * The row to calculate from + * * @return The next row or null if no row exists */ - private VScrollTableRow getNextRow(VScrollTableRow row){ + private VScrollTableRow getNextRow(VScrollTableRow row, int offset) { final Iterator it = scrollBody.iterator(); VScrollTableRow r = null; while (it.hasNext()) { r = (VScrollTableRow) it.next(); if(r == row){ - if (it.hasNext()) { - return (VScrollTableRow) it.next(); - } else { - break; + r = null; + while (offset >= 0 && it.hasNext()) { + r = (VScrollTableRow) it.next(); + offset--; } + return r; } } @@ -844,18 +1087,22 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, * The row to calculate from * @return The previous row or null if no row exists */ - private VScrollTableRow getPreviousRow(VScrollTableRow row) { + private VScrollTableRow getPreviousRow(VScrollTableRow row, int offset) { final Iterator it = scrollBody.iterator(); + final Iterator offsetIt = scrollBody.iterator(); VScrollTableRow r = null; VScrollTableRow prev = null; while (it.hasNext()) { r = (VScrollTableRow) it.next(); + if (offset < 0) { + prev = (VScrollTableRow) offsetIt.next(); + } if (r == row) { return prev; - } else { - prev = r; } + offset--; } + return null; } @@ -3449,35 +3696,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, .setPropertyJSO("onselectstart", null); } - - // Note: changing the immediateness of this - // might - // require changes to "clickEvent" immediateness - // also. - if (multiselectmode == MULTISELECT_MODE_DEFAULT) { - Set ranges = new HashSet(); - for (SelectionRange range : selectedRowRanges) { - ranges.add(range.toString()); - } - client - .updateVariable( - paintableId, - "selectedRanges", - ranges - .toArray(new String[selectedRowRanges - .size()]), - false); - } - - // Send the selected rows - client - .updateVariable( - paintableId, - "selected", - selectedRowKeys - .toArray(new String[selectedRowKeys - .size()]), - immediate); + sendSelectedRows(); } break; case Event.ONCONTEXTMENU: @@ -3641,6 +3860,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, */ public void toggleSelection(boolean ctrlSelect) { selected = !selected; + selectionChanged = true; if (selected) { if (ctrlSelect) { lastSelectedRowKey = rowKey; @@ -3650,6 +3870,40 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, } else { removeStyleName("v-selected"); selectedRowKeys.remove(String.valueOf(rowKey)); + removeKeyFromSelectedRange(rowKey); + } + } + + /** + * Removes a key from a range if the key is found in a selected + * range + * + * @param key + * The key to remove + */ + private void removeKeyFromSelectedRange(int key){ + for(SelectionRange range : selectedRowRanges){ + if (range.inRange(key)) { + int start = range.getStartKey(); + int end = range.getEndKey(); + + if (start < key && end > key) { + selectedRowRanges.add(new SelectionRange(start, + key - 1)); + selectedRowRanges.add(new SelectionRange(key + 1, + end)); + } else if (start == key && start < end) { + selectedRowRanges.add(new SelectionRange(start + 1, + end)); + } else if (end == key && start < end) { + selectedRowRanges.add(new SelectionRange(start, + end - 1)); + } + + selectedRowRanges.remove(range); + + break; + } } } @@ -3690,19 +3944,31 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, } // Select the range (not including this row) - for (int r = startKey; r <= endKey; r++) { - if (r != rowKey) { - VScrollTableRow row = getRenderedRowByKey(String - .valueOf(r)); - if (row != null && !row.isSelected()) { - row.toggleSelection(false); - selectedRowKeys.add(String.valueOf(r)); - } - } + VScrollTableRow startRow = getRenderedRowByKey(String + .valueOf(startKey)); + VScrollTableRow endRow = getRenderedRowByKey(String + .valueOf(endKey)); + + // If start row is null then we have a multipage selection from + // above + if (startRow == null) { + startRow = (VScrollTableRow) scrollBody.iterator().next(); } - // Toggle clicked rows selection - toggleSelection(false); + Iterator rows = scrollBody.iterator(); + boolean startSelection = false; + while (rows.hasNext()) { + VScrollTableRow row = (VScrollTableRow) rows.next(); + if (row == startRow || startSelection) { + startSelection = true; + row.toggleSelection(false); + selectedRowKeys.add(row.getKey()); + } + + if (row == endRow && row != null) { + startSelection = false; + } + } // Add range selectedRowRanges.add(new SelectionRange(startKey, endKey)); @@ -4376,11 +4642,12 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, * * @param row * The row to where the selection head should move + * @return Returns true if focus was moved successfully, else false */ - private void setRowFocus(VScrollTableRow row) { + private boolean setRowFocus(VScrollTableRow row) { if (selectMode == SELECT_MODE_NONE) { - return; + return false; } // Remove previous selection @@ -4389,8 +4656,16 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, } if (row != null) { - // Apply focus style to new selection + + // Trying to set focus on already focused row + if (row == focusedRow) { + return false; + } + + // Set new focused row focusedRow = row; + + // Apply focus style to new selection focusedRow.addStyleName(CLASSNAME_SELECTION_FOCUS); // Scroll up or down if needed @@ -4407,7 +4682,13 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, .getScrollPosition() - focusedRow.getOffsetHeight()); } + + return true; + } else { + focusedRow = null; } + + return false; } /** @@ -4417,64 +4698,149 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, * The keyboard event received */ private void handleNavigation(Event event) { + if (event.getKeyCode() == KeyCodes.KEY_TAB) { + // Do not handle tab key + return; + } // Down navigation if (selectMode == SELECT_MODE_NONE && event.getKeyCode() == getNavigationDownKey()) { - bodyContainer - .setScrollPosition(bodyContainer.getScrollPosition() + 5); - event.preventDefault(); + bodyContainer.setScrollPosition(bodyContainer.getScrollPosition() + + scrollingVelocity); } else if (event.getKeyCode() == getNavigationDownKey()) { - if (moveSelectionDown()) { - if (event.getShiftKey()) { - selectFocusedRow(false, true); - } - event.preventDefault(); + if (selectMode == SELECT_MODE_MULTI && moveFocusDown()) { + selectFocusedRow(event.getCtrlKey() || event.getMetaKey(), + event.getShiftKey()); + } else if (selectMode == SELECT_MODE_SINGLE && !event.getShiftKey() + && moveFocusDown()) { + selectFocusedRow(event.getCtrlKey() || event.getMetaKey(), + event.getShiftKey()); } } // Up navigation if (selectMode == SELECT_MODE_NONE && event.getKeyCode() == getNavigationUpKey()) { - bodyContainer - .setScrollPosition(bodyContainer.getScrollPosition() - 5); - event.preventDefault(); + bodyContainer.setScrollPosition(bodyContainer.getScrollPosition() + - scrollingVelocity); } else if (event.getKeyCode() == getNavigationUpKey()) { - if (moveSelectionUp()) { - if (event.getShiftKey()) { - selectFocusedRow(false, true); - } - event.preventDefault(); + if (selectMode == SELECT_MODE_MULTI && moveFocusUp()) { + selectFocusedRow(event.getCtrlKey() || event.getMetaKey(), + event.getShiftKey()); + } else if (selectMode == SELECT_MODE_SINGLE && !event.getShiftKey() + && moveFocusUp()) { + selectFocusedRow(event.getCtrlKey() || event.getMetaKey(), + event.getShiftKey()); } + } - // Left navigation - } else if (event.getKeyCode() == getNavigationLeftKey()) { + // Left navigation + if (event.getKeyCode() == getNavigationLeftKey()) { bodyContainer.setHorizontalScrollPosition(bodyContainer - .getHorizontalScrollPosition() - 5); - event.preventDefault(); + .getHorizontalScrollPosition() + - scrollingVelocity); // Right navigation } else if (event.getKeyCode() == getNavigationRightKey()) { bodyContainer.setHorizontalScrollPosition(bodyContainer - .getHorizontalScrollPosition() + 5); - event.preventDefault(); + .getHorizontalScrollPosition() + + scrollingVelocity); } // Select navigation if (selectMode > SELECT_MODE_NONE - && event.getKeyCode() == getNavigationSelectKey()) { - selectFocusedRow(event.getCtrlKey() || event.getMetaKey(), - event.getShiftKey()); + && event.getKeyCode() == getNavigationSelectKey()) { + if (selectMode == SELECT_MODE_SINGLE) { + boolean wasSelected = focusedRow.isSelected(); + deselectAll(); + lastSelectedRowKey = -1; + if (!wasSelected) { + focusedRow.toggleSelection(true); + } - event.preventDefault(); + } else { + focusedRow.toggleSelection(true); + } + + sendSelectedRows(); + } + + // Page Down navigation + if (event.getKeyCode() == getNavigationPageDownKey()) { + int rowHeight = (int) scrollBody.getRowHeight(); + int offset = pageLength * rowHeight - rowHeight; + bodyContainer.setScrollPosition(bodyContainer.getScrollPosition() + + offset); + if (selectMode > SELECT_MODE_NONE) { + if (!moveFocusDown(pageLength - 2)) { + final int lastRendered = scrollBody.getLastRendered(); + if (lastRendered == totalRows - 1) { + selectLastRenderedRow(false); + } else { + selectLastItemInNextRender = true; + } + } else { + selectFocusedRow(false, false); + sendSelectedRows(); + } + } + } - // Send the selected rows - client - .updateVariable(paintableId, "selected", - selectedRowKeys - .toArray(new String[selectedRowKeys - .size()]), immediate); + // Page Up navigation + if (event.getKeyCode() == getNavigationPageUpKey()) { + int rowHeight = (int) scrollBody.getRowHeight(); + int offset = pageLength * rowHeight - rowHeight; + bodyContainer.setScrollPosition(bodyContainer.getScrollPosition() + - offset); + if (selectMode > SELECT_MODE_NONE) { + if (!moveFocusUp(pageLength - 2)) { + final int firstRendered = scrollBody.getFirstRendered(); + if (firstRendered == 0) { + selectFirstRenderedRow(false); + } else { + selectFirstItemInNextRender = true; + } + } else { + selectFocusedRow(false, false); + sendSelectedRows(); + } + } + } + + // Goto start navigation + if (event.getKeyCode() == getNavigationStartKey()) { + if (selectMode > SELECT_MODE_NONE) { + final int firstRendered = scrollBody.getFirstRendered(); + boolean focusOnly = event.getCtrlKey() || event.getMetaKey(); + if (firstRendered == 0) { + selectFirstRenderedRow(focusOnly); + } else if (focusOnly) { + focusFirstItemInNextRender = true; + } else { + selectFirstItemInNextRender = true; + } + } + bodyContainer.setScrollPosition(0); + } + + // Goto end navigation + if (event.getKeyCode() == getNavigationEndKey()) { + if (selectMode > SELECT_MODE_NONE) { + final int lastRendered = scrollBody.getLastRendered(); + boolean focusOnly = event.getCtrlKey() || event.getMetaKey(); + if (lastRendered == totalRows - 1) { + selectLastRenderedRow(focusOnly); + } else if (focusOnly) { + focusLastItemInNextRender = true; + } else { + selectLastItemInNextRender = true; + } } + bodyContainer.setScrollPosition(scrollBody.getOffsetHeight()); + } + + event.preventDefault(); } /* @@ -4486,6 +4852,17 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, */ public void onKeyPress(KeyPressEvent event) { handleNavigation((Event) event.getNativeEvent().cast()); + + // Start the velocityTimer + if (scrollingVelocityTimer == null) { + scrollingVelocityTimer = new Timer() { + @Override + public void run() { + scrollingVelocity++; + } + }; + scrollingVelocityTimer.scheduleRepeating(100); + } } /* @@ -4497,6 +4874,17 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, */ public void onKeyDown(KeyDownEvent event) { handleNavigation((Event) event.getNativeEvent().cast()); + + // Start the velocityTimer + if (scrollingVelocityTimer == null) { + scrollingVelocityTimer = new Timer() { + @Override + public void run() { + scrollingVelocity++; + } + }; + scrollingVelocityTimer.scheduleRepeating(100); + } } /* @@ -4507,10 +4895,9 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, * .dom.client.FocusEvent) */ public void onFocus(FocusEvent event) { - // Move focus from wrapper to container in FF, ignored in other browsers - if (BrowserInfo.get().isFF3()) { - bodyContainer.getElement().focus(); - } + bodyContainer.addStyleName("focused"); + + // Focus a row if no row is in focus if (focusedRow == null) { setRowFocus((VScrollTableRow) scrollBody.iterator().next()); } @@ -4524,7 +4911,12 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, * .dom.client.BlurEvent) */ public void onBlur(BlurEvent event) { + bodyContainer.removeStyleName("focused"); + + // Unfocus any row setRowFocus(null); } + + } diff --git a/tests/src/com/vaadin/tests/components/table/KeyControl.java b/tests/src/com/vaadin/tests/components/table/KeyControl.java index 19a5a0eca6..a0a13e4858 100644 --- a/tests/src/com/vaadin/tests/components/table/KeyControl.java +++ b/tests/src/com/vaadin/tests/components/table/KeyControl.java @@ -64,7 +64,8 @@ public class KeyControl extends TestBase { table2.addListener(new Table.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { - String value = table2.getValue().toString(); + String value = table2.getValue() == null ? "No selected items" + : table2.getValue().toString(); selected2.setValue(value); } }); @@ -87,7 +88,8 @@ public class KeyControl extends TestBase { table3.addListener(new Table.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { Set value = (Set) table3.getValue(); - selected3.setValue(value); + selected3.setValue(value.size() == 0 ? "No selected items" + : value); } }); diff --git a/tests/src/com/vaadin/tests/components/table/TableContextMenuOnField.java b/tests/src/com/vaadin/tests/components/table/TableContextMenuOnField.java new file mode 100644 index 0000000000..4c6f658a24 --- /dev/null +++ b/tests/src/com/vaadin/tests/components/table/TableContextMenuOnField.java @@ -0,0 +1,93 @@ +package com.vaadin.tests.components.table; + +import com.vaadin.event.Action; +import com.vaadin.event.LayoutEvents.LayoutClickEvent; +import com.vaadin.event.LayoutEvents.LayoutClickListener; +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.Button; +import com.vaadin.ui.Component; +import com.vaadin.ui.Link; +import com.vaadin.ui.Table; +import com.vaadin.ui.TextField; +import com.vaadin.ui.VerticalLayout; + +public class TableContextMenuOnField extends TestBase { + + private static final Action ACTION_MYACTION = new Action("Action!!"); + + @Override + protected void setup() { + Table table = new Table(); + table.setSelectable(true); + table.setMultiSelect(true); + + table.addActionHandler(new Action.Handler() { + public void handleAction(Action action, Object sender, Object target) { + // TODO Auto-generated method stub + + } + + public Action[] getActions(Object target, Object sender) { + return new Action[] { ACTION_MYACTION }; + } + }); + + table.addGeneratedColumn("layout", new Table.ColumnGenerator() { + + public Component generateCell(Table source, Object itemId, + Object columnId) { + + VerticalLayout layout = new VerticalLayout(); + layout.addComponent(new TextField()); + + layout.addListener(new LayoutClickListener() { + + public void layoutClick(LayoutClickEvent event) { + getMainWindow().showNotification("HELLO"); + + } + }); + + return layout; + } + }); + + table.addGeneratedColumn("textfield", new Table.ColumnGenerator() { + public Component generateCell(Table source, Object itemId, + Object columnId) { + return new TextField(); + } + }); + + table.addGeneratedColumn("link", new Table.ColumnGenerator() { + public Component generateCell(Table source, Object itemId, + Object columnId) { + return new Link("Link", null); + } + }); + + table.addGeneratedColumn("button", new Table.ColumnGenerator() { + public Component generateCell(Table source, Object itemId, + Object columnId) { + return new Button("Button"); + } + }); + + table.addItem(); + table.addItem(); + table.addItem(); + addComponent(table); + } + + @Override + protected String getDescription() { + return "Right clicking on an item without a context menu should bring" + + "up the Tables context menu"; + } + + @Override + protected Integer getTicketNumber() { + return 4264; + } + +}