summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorTeemu Suo-Anttila <teemusa@vaadin.com>2014-07-16 15:44:27 +0300
committerTeemu Suo-Anttila <teemusa@vaadin.com>2014-07-18 10:17:23 +0300
commita9c124cc19b4e1bf1c8736e209b8e066a002da6f (patch)
tree5fb6c0f1c97b71c697bc6d6f8e384d23ec8fc2a2 /client
parent3626012f2ebf51c5433eb2671b5404e583a0f892 (diff)
downloadvaadin-framework-a9c124cc19b4e1bf1c8736e209b8e066a002da6f.tar.gz
vaadin-framework-a9c124cc19b4e1bf1c8736e209b8e066a002da6f.zip
Implement active cell keyboard navigation for Grid (#13334)
Change-Id: I38b759f24fa35432d5bc330b06a64caaa7ef3c9e
Diffstat (limited to 'client')
-rw-r--r--client/src/com/vaadin/client/ui/grid/Grid.java297
1 files changed, 237 insertions, 60 deletions
diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java
index 539c18f8c8..5ef94a296c 100644
--- a/client/src/com/vaadin/client/ui/grid/Grid.java
+++ b/client/src/com/vaadin/client/ui/grid/Grid.java
@@ -32,6 +32,7 @@ import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.TableCellElement;
import com.google.gwt.dom.client.Touch;
+import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.touch.client.Point;
import com.google.gwt.user.client.DOM;
@@ -100,6 +101,222 @@ import com.vaadin.shared.util.SharedUtil;
public class Grid<T> extends Composite implements
HasSelectionChangeHandlers<T>, SubPartAware {
+ private class ActiveCellHandler {
+
+ private RowContainer container = escalator.getBody();
+ private int activeRow = 0;
+ private int activeColumn = 0;
+ private Element cellWithActiveStyle = null;
+ private Element rowWithActiveStyle = null;
+
+ public ActiveCellHandler() {
+ sinkEvents(getNavigationEvents());
+ }
+
+ /**
+ * Sets style names for given cell when needed.
+ */
+ public void updateActiveCellStyle(FlyweightCell cell) {
+ int cellRow = cell.getRow();
+ int cellColumn = cell.getColumn();
+ RowContainer cellContainer = escalator.findRowContainer(cell
+ .getElement());
+
+ if (cellContainer == container) {
+ // Cell is in the current container
+ if (cellRow == activeRow && cellColumn == activeColumn) {
+ if (cellWithActiveStyle != cell.getElement()) {
+ // Cell is correct but it does not have active style
+ if (cellWithActiveStyle != null) {
+ // Remove old active style
+ setStyleName(cellWithActiveStyle,
+ cellActiveStyleName, false);
+ }
+ cellWithActiveStyle = cell.getElement();
+ // Add active style to correct cell.
+ setStyleName(cellWithActiveStyle, cellActiveStyleName,
+ true);
+ }
+ } else if (cellWithActiveStyle == cell.getElement()) {
+ // Due to escalator reusing cells, a new cell has the same
+ // element but is not the active cell.
+ setStyleName(cellWithActiveStyle, cellActiveStyleName,
+ false);
+ cellWithActiveStyle = null;
+ }
+ }
+
+ if (cellContainer == escalator.getHeader()
+ || cellContainer == escalator.getFooter()) {
+ // Correct header and footer column also needs highlighting
+ setStyleName(cell.getElement(), headerFooterActiveStyleName,
+ cellColumn == activeColumn);
+ }
+ }
+
+ /**
+ * Sets active row style name for given row if needed.
+ *
+ * @param row
+ * a row object
+ */
+ public void updateActiveRowStyle(Row row) {
+ if (activeRow == row.getRow() && container == escalator.getBody()) {
+ if (row.getElement() != rowWithActiveStyle) {
+ // Row should have active style but does not have it.
+ if (rowWithActiveStyle != null) {
+ setStyleName(rowWithActiveStyle, rowActiveStyleName,
+ false);
+ }
+ rowWithActiveStyle = row.getElement();
+ setStyleName(rowWithActiveStyle, rowActiveStyleName, true);
+ }
+ } else if (rowWithActiveStyle == row.getElement()
+ || (container != escalator.getBody() && rowWithActiveStyle != null)) {
+ // Remove active style.
+ setStyleName(rowWithActiveStyle, rowActiveStyleName, false);
+ rowWithActiveStyle = null;
+ }
+ }
+
+ /**
+ * Sets currently active cell to a cell in given container with given
+ * indices.
+ *
+ * @param row
+ * new active row
+ * @param column
+ * new active column
+ * @param container
+ * new container
+ */
+ private void setActiveCell(int row, int column, RowContainer container) {
+ if (row == activeRow && column == activeColumn
+ && container == this.container) {
+ return;
+ }
+
+ int oldRow = activeRow;
+ int oldColumn = activeColumn;
+ activeRow = row;
+ activeColumn = column;
+
+ if (container == escalator.getBody()) {
+ scrollToRow(activeRow);
+ }
+ escalator.scrollToColumn(activeColumn, ScrollDestination.ANY, 10);
+
+ if (this.container == container) {
+ if (container != escalator.getBody()) {
+ if (oldColumn == activeColumn && oldRow != activeRow) {
+ refreshRow(oldRow);
+ } else if (oldColumn != activeColumn) {
+ refreshHeader();
+ refreshFooter();
+ }
+ } else {
+ if (oldRow != activeRow) {
+ refreshRow(oldRow);
+ }
+
+ if (oldColumn != activeColumn) {
+ refreshHeader();
+ refreshFooter();
+ }
+ }
+ } else {
+ RowContainer oldContainer = this.container;
+ this.container = container;
+
+ if (oldColumn != activeColumn) {
+ refreshHeader();
+ refreshFooter();
+ if (oldContainer == escalator.getBody()) {
+ oldContainer.refreshRows(oldRow, 1);
+ }
+ } else {
+ oldContainer.refreshRows(oldRow, 1);
+ }
+ }
+ refreshRow(activeRow);
+ }
+
+ /**
+ * Sets currently active cell used for keyboard navigation. Note that
+ * active cell is not JavaScript {@code document.activeElement}.
+ *
+ * @param cell
+ * a cell object
+ */
+ public void setActiveCell(Cell cell) {
+ setActiveCell(cell.getRow(), cell.getColumn(),
+ escalator.findRowContainer(cell.getElement()));
+ }
+
+ /**
+ * Gets list of events that can be used for active cell navigation.
+ *
+ * @return list of navigation related event types
+ */
+ public Collection<String> getNavigationEvents() {
+ return Arrays.asList(BrowserEvents.KEYDOWN, BrowserEvents.CLICK);
+ }
+
+ /**
+ * Handle events that can change the currently active cell.
+ */
+ public void handleNavigationEvent(Event event, Cell cell) {
+ if (event.getType().equals(BrowserEvents.CLICK)
+ && event.getButton() == NativeEvent.BUTTON_LEFT
+ && cell != null) {
+ setActiveCell(cell);
+ getElement().focus();
+ } else if (event.getType().equals(BrowserEvents.KEYDOWN)) {
+ int keyCode = event.getKeyCode();
+ if (keyCode == 0) {
+ keyCode = event.getCharCode();
+ }
+ int newRow = activeRow;
+ int newColumn = activeColumn;
+ RowContainer newContainer = container;
+
+ switch (event.getKeyCode()) {
+ case KeyCodes.KEY_DOWN:
+ newRow += 1;
+ break;
+ case KeyCodes.KEY_UP:
+ newRow -= 1;
+ break;
+ case KeyCodes.KEY_RIGHT:
+ newColumn += 1;
+ break;
+ case KeyCodes.KEY_LEFT:
+ newColumn -= 1;
+ break;
+ }
+
+ if (newRow < 0) {
+ newRow = 0;
+ } else if (newRow >= container.getRowCount()) {
+ newRow = container.getRowCount() - 1;
+ }
+
+ if (newColumn < 0) {
+ newColumn = 0;
+ } else if (newColumn >= getColumnCount()) {
+ newColumn = getColumnCount() - 1;
+ }
+
+ setActiveCell(newRow, newColumn, newContainer);
+ }
+
+ }
+
+ private void refreshRow(int row) {
+ container.refreshRows(row, 1);
+ }
+ }
+
private class SelectionColumn extends GridColumn<Boolean, T> {
private boolean initDone = false;
@@ -242,18 +459,14 @@ public class Grid<T> extends Composite implements
private String rowSelectedStyleName;
private String cellActiveStyleName;
private String rowActiveStyleName;
- private String headerFooterFocusedStyleName;
+ private String headerFooterActiveStyleName;
/**
* Current selection model.
*/
private SelectionModel<T> selectionModel;
- /**
- * Current active cell.
- */
- private int activeRow = 0;
- private int activeColumn = 0;
+ private final ActiveCellHandler activeCellHandler;
/**
* Enumeration for easy setting of selection mode.
@@ -1017,9 +1230,7 @@ public class Grid<T> extends Composite implements
.render(cell, getColumnValue(column));
}
- setStyleName(cell.getElement(),
- headerFooterFocusedStyleName,
- activeColumn == cell.getColumn());
+ activeCellHandler.updateActiveCellStyle(cell);
}
} else if (columnGroupRows.size() > 0) {
@@ -1058,9 +1269,7 @@ public class Grid<T> extends Composite implements
cellElement.setInnerHTML(null);
cell.setColSpan(1);
- setStyleName(cell.getElement(),
- headerFooterFocusedStyleName,
- activeColumn == cell.getColumn());
+ activeCellHandler.updateActiveCellStyle(cell);
}
}
}
@@ -1088,6 +1297,8 @@ public class Grid<T> extends Composite implements
*/
public Grid() {
initWidget(escalator);
+ getElement().setTabIndex(0);
+ activeCellHandler = new ActiveCellHandler();
setStylePrimaryName("v-grid");
@@ -1098,7 +1309,6 @@ public class Grid<T> extends Composite implements
refreshHeader();
refreshFooter();
- sinkEvents(Event.ONMOUSEDOWN);
setSelectionMode(SelectionMode.SINGLE);
escalator
@@ -1132,7 +1342,7 @@ public class Grid<T> extends Composite implements
rowHasDataStyleName = getStylePrimaryName() + "-row-has-data";
rowSelectedStyleName = getStylePrimaryName() + "-row-selected";
cellActiveStyleName = getStylePrimaryName() + "-cell-active";
- headerFooterFocusedStyleName = getStylePrimaryName() + "-header-active";
+ headerFooterActiveStyleName = getStylePrimaryName() + "-header-active";
rowActiveStyleName = getStylePrimaryName() + "-row-active";
}
@@ -1151,6 +1361,8 @@ public class Grid<T> extends Composite implements
int colIndex = -1;
for (FlyweightCell cell : cellsToUpdate) {
+ activeCellHandler.updateActiveCellStyle(cell);
+
if (colIndex == -1) {
colIndex = cell.getColumn();
}
@@ -1244,8 +1456,7 @@ public class Grid<T> extends Composite implements
setStyleName(rowElement, rowSelectedStyleName, false);
}
- setStyleName(rowElement, rowActiveStyleName,
- rowIndex == activeRow);
+ activeCellHandler.updateActiveRowStyle(row);
for (FlyweightCell cell : cellsToUpdate) {
GridColumn<?, T> column = getColumnFromVisibleIndex(cell
@@ -1254,8 +1465,7 @@ public class Grid<T> extends Composite implements
assert column != null : "Column was not found from cell ("
+ cell.getColumn() + "," + cell.getRow() + ")";
- setStyleName(cell.getElement(), cellActiveStyleName,
- isActiveCell(cell));
+ activeCellHandler.updateActiveCellStyle(cell);
Renderer renderer = column.getRenderer();
@@ -1288,11 +1498,6 @@ public class Grid<T> extends Composite implements
}
}
- private boolean isActiveCell(FlyweightCell cell) {
- return cell.getRow() == activeRow
- && cell.getColumn() == activeColumn;
- }
-
@Override
public void preDetach(Row row, Iterable<FlyweightCell> cellsToDetach) {
for (FlyweightCell cell : cellsToDetach) {
@@ -2122,8 +2327,9 @@ public class Grid<T> extends Composite implements
if (Element.is(target)) {
Element e = Element.as(target);
RowContainer container = escalator.findRowContainer(e);
+ Cell cell = null;
if (container != null) {
- Cell cell = container.getCell(e);
+ cell = container.getCell(e);
if (cell != null) {
GridColumn<?, T> gridColumn = columns.get(cell.getColumn());
@@ -2145,14 +2351,13 @@ public class Grid<T> extends Composite implements
}
}
}
-
- // TODO: Support active cells in Headers and Footers,
- // 14.07.2014, Teemu Suo-Anttila
- if (event.getTypeInt() == Event.ONMOUSEDOWN) {
- setActiveCell(cell);
- }
}
}
+
+ if (activeCellHandler.getNavigationEvents().contains(
+ event.getType())) {
+ activeCellHandler.handleNavigationEvent(event, cell);
+ }
}
}
@@ -2255,13 +2460,13 @@ public class Grid<T> extends Composite implements
if (this.selectColumnRenderer != null) {
removeColumnSkipSelectionColumnCheck(selectionColumn);
- --activeColumn;
+ --activeCellHandler.activeColumn;
}
this.selectColumnRenderer = selectColumnRenderer;
if (selectColumnRenderer != null) {
- ++activeColumn;
+ ++activeCellHandler.activeColumn;
selectionColumn = new SelectionColumn(selectColumnRenderer);
// FIXME: this needs to be done elsewhere, requires design...
@@ -2508,32 +2713,4 @@ public class Grid<T> extends Composite implements
fireEvent(new SortEvent<T>(this,
Collections.unmodifiableList(sortOrder)));
}
-
- /**
- * Set currently active cell used for keyboard navigation. Note that active
- * cell is not {@code activeElement}.
- *
- * @param cell
- * a cell object
- */
- public void setActiveCell(Cell cell) {
- int oldRow = activeRow;
- int oldColumn = activeColumn;
-
- activeRow = cell.getRow();
- activeColumn = cell.getColumn();
-
- if (oldRow != activeRow) {
- escalator.getBody().refreshRows(oldRow, 1);
- escalator.getBody().refreshRows(activeRow, 1);
- }
-
- if (oldColumn != activeColumn) {
- if (oldRow == activeRow) {
- escalator.getBody().refreshRows(oldRow, 1);
- }
- refreshHeader();
- refreshFooter();
- }
- }
}