diff options
author | Johannes Dahlström <johannesd@vaadin.com> | 2014-08-19 16:23:04 +0300 |
---|---|---|
committer | Johannes Dahlström <johannesd@vaadin.com> | 2014-08-29 08:54:15 +0000 |
commit | 63735b6f70d41c642f3d043bbba88093e6c2fba7 (patch) | |
tree | 43bc4308ef1edc6a4f1ebdb865be8629d2308126 /client | |
parent | 1de6a75497bae315dfcb70c272225a6d3ea0af19 (diff) | |
download | vaadin-framework-63735b6f70d41c642f3d043bbba88093e6c2fba7.tar.gz vaadin-framework-63735b6f70d41c642f3d043bbba88093e6c2fba7.zip |
Initial client-side editor row implementation (#13334)
Only shows a bare grey row for now. Enter opens editor for
active row, Esc hides editor.
TODO
* Double-click to edit does not work
* Scrolling to edit hidden row does not work
* EditorRowHandler to bind data+widgets
* Server-side integration
* Commit/discard buttons
Change-Id: I0ae678b086493b0e46ab7c1db99eb92049318d6f
Diffstat (limited to 'client')
-rw-r--r-- | client/src/com/vaadin/client/ui/grid/EditorRow.java | 207 | ||||
-rw-r--r-- | client/src/com/vaadin/client/ui/grid/Escalator.java | 2 | ||||
-rw-r--r-- | client/src/com/vaadin/client/ui/grid/Grid.java | 135 |
3 files changed, 309 insertions, 35 deletions
diff --git a/client/src/com/vaadin/client/ui/grid/EditorRow.java b/client/src/com/vaadin/client/ui/grid/EditorRow.java new file mode 100644 index 0000000000..45374962b7 --- /dev/null +++ b/client/src/com/vaadin/client/ui/grid/EditorRow.java @@ -0,0 +1,207 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.grid; + +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.dom.client.TableCellElement; +import com.google.gwt.dom.client.TableRowElement; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.user.client.DOM; +import com.vaadin.client.ui.grid.Escalator.AbstractRowContainer; +import com.vaadin.shared.ui.grid.ScrollDestination; + +/** + * An editor UI for Grid rows. A single Grid row at a time can be opened for + * editing. + * + * @since + * @author Vaadin Ltd + */ +public class EditorRow<T> { + + public static final int KEYCODE_SHOW = KeyCodes.KEY_ENTER; + public static final int KEYCODE_HIDE = KeyCodes.KEY_ESCAPE; + + public enum State { + INACTIVE, ACTIVATING, ACTIVE, COMMITTING + } + + private DivElement editorOverlay = DivElement.as(DOM.createDiv()); + + private Grid<T> grid; + + private boolean enabled = false; + private State state = State.INACTIVE; + private int rowIndex = -1; + private String styleName = null; + + public int getRow() { + return rowIndex; + } + + /** + * Opens the editor over the row with the given index. + * + * @param rowIndex + * the index of the row to be edited + * + * @throws IllegalStateException + * if this editor row is not enabled or if it is already in edit + * mode + */ + public void editRow(int rowIndex) { + if (!enabled) { + throw new IllegalStateException( + "Cannot edit row: EditorRow is not enabled"); + } + if (state != State.INACTIVE) { + throw new IllegalStateException( + "Cannot edit row: EditorRow already in edit mode"); + } + + this.rowIndex = rowIndex; + + state = State.ACTIVATING; + + boolean rowVisible = grid.getEscalator().getVisibleRowRange() + .contains(rowIndex); + + if (!rowVisible) { + grid.scrollToRow(rowIndex, ScrollDestination.MIDDLE); + } else { + grid.getEscalator().getBody().refreshRows(rowIndex, 1); + } + } + + /** + * Cancels the currently active edit and hides the editor. + * + * @throws IllegalStateException + * if this editor row is not in edit mode + */ + public void cancel() { + if (state == State.INACTIVE) { + throw new IllegalStateException( + "Cannot cancel edit: EditorRow is not in edit mode"); + } + state = State.INACTIVE; + hideOverlay(); + + grid.getEscalator().getBody().refreshRows(rowIndex, 1); + } + + public boolean isEnabled() { + return enabled; + } + + /** + * Sets the enabled state of this editor row. + * + * @param enabled + * true if enabled, false otherwise + * + * @throws IllegalStateException + * if in edit mode and trying to disable + */ + public void setEnabled(boolean enabled) { + if (enabled == false && state != State.INACTIVE) { + throw new IllegalStateException( + "Cannot disable: EditorRow is in edit mode"); + } + this.enabled = enabled; + } + + protected void setGrid(Grid<T> grid) { + this.grid = grid; + } + + protected State getState() { + return state; + } + + protected void setState(State state) { + this.state = state; + } + + /** + * Opens the editor overlay over the given table row. + * + * @param tr + * the row to be edited + */ + protected void showOverlay(TableRowElement tr) { + + DivElement tableWrapper = DivElement.as(tr.getParentElement() + .getParentElement().getParentElement()); + + AbstractRowContainer body = (AbstractRowContainer) grid.getEscalator() + .getBody(); + + int rowTop = body.getRowTop(tr); + int bodyTop = body.getElement().getAbsoluteTop(); + int wrapperTop = tableWrapper.getAbsoluteTop(); + + setBounds(editorOverlay, tr.getOffsetLeft(), rowTop + bodyTop + - wrapperTop, tr.getOffsetWidth(), tr.getOffsetHeight()); + + for (int i = 0; i < tr.getCells().getLength(); i++) { + Element cell = createCell(tr.getCells().getItem(i)); + editorOverlay.appendChild(cell); + } + + tableWrapper.appendChild(editorOverlay); + } + + protected void hideOverlay() { + editorOverlay.removeFromParent(); + } + + protected void setStylePrimaryName(String primaryName) { + if (styleName != null) { + editorOverlay.removeClassName(styleName); + } + styleName = primaryName + "-editor-row"; + editorOverlay.addClassName(styleName); + } + + /** + * Creates an editor row cell corresponding to the given table cell. The + * returned element is empty and has the same dimensions and position as the + * table cell. + * + * @param td + * the table cell used as a reference + * @return an editor row cell corresponding to the given cell + */ + protected Element createCell(TableCellElement td) { + DivElement cell = DivElement.as(DOM.createDiv()); + setBounds(cell, td.getOffsetLeft(), td.getOffsetTop(), + td.getOffsetWidth(), td.getOffsetHeight()); + return cell; + } + + private static void setBounds(Element e, int left, int top, int width, + int height) { + Style style = e.getStyle(); + style.setLeft(left, Unit.PX); + style.setTop(top, Unit.PX); + style.setWidth(width, Unit.PX); + style.setHeight(height, Unit.PX); + } +} diff --git a/client/src/com/vaadin/client/ui/grid/Escalator.java b/client/src/com/vaadin/client/ui/grid/Escalator.java index ceff55e303..f686ec03ca 100644 --- a/client/src/com/vaadin/client/ui/grid/Escalator.java +++ b/client/src/com/vaadin/client/ui/grid/Escalator.java @@ -1058,7 +1058,7 @@ public class Escalator extends Widget { } } - private abstract class AbstractRowContainer implements RowContainer { + protected abstract class AbstractRowContainer implements RowContainer { private EscalatorUpdater updater = EscalatorUpdater.NULL; diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index 9c12b37bb5..89bf7fbc2b 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -49,6 +49,7 @@ import com.vaadin.client.Util; import com.vaadin.client.data.DataChangeHandler; import com.vaadin.client.data.DataSource; import com.vaadin.client.ui.SubPartAware; +import com.vaadin.client.ui.grid.EditorRow.State; import com.vaadin.client.ui.grid.GridFooter.FooterRow; import com.vaadin.client.ui.grid.GridHeader.HeaderRow; import com.vaadin.client.ui.grid.GridStaticSection.StaticCell; @@ -665,6 +666,8 @@ public class Grid<T> extends Composite implements private final LazySorter lazySorter = new LazySorter(); + private final EditorRow<T> editorRow = GWT.create(EditorRow.class); + /** * Enumeration for easy setting of selection mode. */ @@ -937,7 +940,7 @@ public class Grid<T> extends Composite implements @Override public void update(Row row, Iterable<FlyweightCell> cellsToUpdate) { int rowIndex = row.getRow(); - Element rowElement = row.getElement(); + TableRowElement rowElement = row.getElement(); T rowData = dataSource.getRow(rowIndex); boolean hasData = rowData != null; @@ -972,8 +975,8 @@ public class Grid<T> extends Composite implements Renderer renderer = column.getRenderer(); - // Hide cell content if needed if (renderer instanceof ComplexRenderer) { + // Hide cell content if needed ComplexRenderer clxRenderer = (ComplexRenderer) renderer; if (hasData) { if (!usedToHaveData) { @@ -999,6 +1002,13 @@ public class Grid<T> extends Composite implements cell.getElement().removeAllChildren(); } } + + if (rowIndex == editorRow.getRow()) { + if (editorRow.getState() == State.ACTIVATING) { + editorRow.setState(State.ACTIVE); + editorRow.showOverlay(rowElement); + } + } } @Override @@ -1225,6 +1235,8 @@ public class Grid<T> extends Composite implements footer.setGrid(this); + editorRow.setGrid(this); + setSelectionMode(SelectionMode.SINGLE); escalator.addScrollHandler(new ScrollHandler() { @@ -1267,6 +1279,8 @@ public class Grid<T> extends Composite implements public void setStylePrimaryName(String style) { super.setStylePrimaryName(style); escalator.setStylePrimaryName(style); + editorRow.setStylePrimaryName(style); + rowHasDataStyleName = getStylePrimaryName() + "-row-has-data"; rowSelectedStyleName = getStylePrimaryName() + "-row-selected"; cellActiveStyleName = getStylePrimaryName() + "-cell-active"; @@ -1582,6 +1596,14 @@ public class Grid<T> extends Composite implements return footer; } + public EditorRow<T> getEditorRow() { + return editorRow; + } + + protected Escalator getEscalator() { + return escalator; + } + /** * {@inheritDoc} * <p> @@ -1930,45 +1952,90 @@ public class Grid<T> extends Composite implements @Override public void onBrowserEvent(Event event) { super.onBrowserEvent(event); + EventTarget target = event.getEventTarget(); - if (Element.is(target)) { - Element e = Element.as(target); - RowContainer container = escalator.findRowContainer(e); - Cell cell = null; - if (container != null) { - cell = container.getCell(e); - if (cell != null) { - // FIXME getFromVisibleIndex??? - GridColumn<?, T> gridColumn = columns.get(cell.getColumn()); - - if (container == escalator.getHeader()) { - if (getHeader().getRow(cell.getRow()).isDefault()) { - handleDefaultRowEvent(cell, event); - } - } else if (container == escalator.getFooter()) { - // NOP - } else if (gridColumn.getRenderer() instanceof ComplexRenderer) { - ComplexRenderer<?> cplxRenderer = (ComplexRenderer<?>) gridColumn - .getRenderer(); - if (cplxRenderer.getConsumedEvents().contains( - event.getType())) { - if (cplxRenderer.onBrowserEvent(cell, event)) { - return; - } - } - } + + if (!Element.is(target)) { + return; + } + + Element e = Element.as(target); + RowContainer container = escalator.findRowContainer(e); + Cell cell = container != null ? container.getCell(e) : null; + + if (doEditorRowEvent(event, container, cell)) { + return; + } + + if (container == escalator.getHeader()) { + if (getHeader().getRow(cell.getRow()).isDefault()) { + handleDefaultRowEvent(cell, event); + } + } + + if (doRendererEvent(event, container, cell)) { + return; + } + + if (doActiveCellEvent(event, container, cell)) { + return; + } + } + + private boolean doEditorRowEvent(Event event, RowContainer container, + Cell cell) { + if (editorRow.getState() != State.INACTIVE) { + if (event.getTypeInt() == Event.ONKEYDOWN + && event.getKeyCode() == EditorRow.KEYCODE_HIDE) { + editorRow.cancel(); + } + return true; + } + if (editorRow.isEnabled()) { + if (event.getTypeInt() == Event.ONDBLCLICK) { + if (container == escalator.getBody() && cell != null) { + editorRow.editRow(cell.getRow()); + return true; } + } else if (event.getTypeInt() == Event.ONKEYDOWN + && event.getKeyCode() == EditorRow.KEYCODE_SHOW) { + editorRow.editRow(activeCellHandler.activeRow); + return true; } + } + return false; + } + + private boolean doRendererEvent(Event event, RowContainer container, + Cell cell) { + + if (container == escalator.getBody() && cell != null) { + GridColumn<?, T> gridColumn = getColumnFromVisibleIndex(cell + .getColumn()); - Collection<String> navigation = activeCellHandler - .getNavigationEvents(); - if (navigation.contains(event.getType()) - && (Util.getFocusedElement() == getElement() || cell != null)) { - activeCellHandler.handleNavigationEvent(event, cell); + if (gridColumn.getRenderer() instanceof ComplexRenderer) { + ComplexRenderer<?> cplxRenderer = (ComplexRenderer<?>) gridColumn + .getRenderer(); + if (cplxRenderer.getConsumedEvents().contains(event.getType())) { + if (cplxRenderer.onBrowserEvent(cell, event)) { + return true; + } + } } + } + return false; + } - handleGridNavigation(event, cell); + private boolean doActiveCellEvent(Event event, RowContainer container, + Cell cell) { + Collection<String> navigation = activeCellHandler.getNavigationEvents(); + if (navigation.contains(event.getType()) + && (Util.getFocusedElement() == getElement() || cell != null)) { + activeCellHandler.handleNavigationEvent(event, cell); } + handleGridNavigation(event, cell); + + return false; } private void handleGridNavigation(Event event, Cell cell) { |