diff options
author | John Alhroos <john.ahlroos@itmill.com> | 2010-05-04 13:02:44 +0000 |
---|---|---|
committer | John Alhroos <john.ahlroos@itmill.com> | 2010-05-04 13:02:44 +0000 |
commit | 2924b070b44a3f140dca181204187c31d5ac8739 (patch) | |
tree | 8310b5d934538ed79dd8966b9985fffe4d948b11 /src | |
parent | 26d3c1eaeb32461d754ae8c8a754115cca4f9806 (diff) | |
download | vaadin-framework-2924b070b44a3f140dca181204187c31d5ac8739.tar.gz vaadin-framework-2924b070b44a3f140dca181204187c31d5ac8739.zip |
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
Diffstat (limited to 'src')
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java | 632 |
1 files changed, 512 insertions, 120 deletions
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<SelectionRange> selectedRowRanges = new HashSet<SelectionRange>(); @@ -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,22 +414,81 @@ 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<String> ranges = new HashSet<String>(); + 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 * you want. @@ -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<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 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<Widget> 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<Widget> it = scrollBody.iterator(); + final Iterator<Widget> 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<String> ranges = new HashSet<String>(); - 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<Widget> 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); } + + } |