summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Alhroos <john.ahlroos@itmill.com>2010-05-04 13:02:44 +0000
committerJohn Alhroos <john.ahlroos@itmill.com>2010-05-04 13:02:44 +0000
commit2924b070b44a3f140dca181204187c31d5ac8739 (patch)
tree8310b5d934538ed79dd8966b9985fffe4d948b11
parent26d3c1eaeb32461d754ae8c8a754115cca4f9806 (diff)
downloadvaadin-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
-rw-r--r--WebContent/VAADIN/themes/base/styles.css16
-rw-r--r--WebContent/VAADIN/themes/base/table/table.css3
-rw-r--r--WebContent/VAADIN/themes/reindeer/styles.css27
-rw-r--r--WebContent/VAADIN/themes/runo/styles.css7
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java632
-rw-r--r--tests/src/com/vaadin/tests/components/table/KeyControl.java6
-rw-r--r--tests/src/com/vaadin/tests/components/table/TableContextMenuOnField.java93
7 files changed, 650 insertions, 134 deletions
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<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);
}
+
+
}
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<String> value = (Set<String>) 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;
+ }
+
+}