From 1042bfde5f306c9b710f6cf4cf8d6b232e0301da Mon Sep 17 00:00:00 2001 From: Johannes Dahlström Date: Tue, 19 May 2015 15:20:12 +0300 Subject: Refactor focus related server API to class AbstractFocusable Change-Id: I58159d6f47d38230d583bd0cd61673bfbce02955 --- server/src/com/vaadin/ui/AbstractFocusable.java | 134 ++++++++++++++++++++++++ server/src/com/vaadin/ui/Button.java | 105 +------------------ 2 files changed, 135 insertions(+), 104 deletions(-) create mode 100644 server/src/com/vaadin/ui/AbstractFocusable.java (limited to 'server/src/com/vaadin/ui') diff --git a/server/src/com/vaadin/ui/AbstractFocusable.java b/server/src/com/vaadin/ui/AbstractFocusable.java new file mode 100644 index 0000000000..b9705cef6a --- /dev/null +++ b/server/src/com/vaadin/ui/AbstractFocusable.java @@ -0,0 +1,134 @@ +/* + * 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.ui; + +import com.vaadin.event.FieldEvents.BlurEvent; +import com.vaadin.event.FieldEvents.BlurListener; +import com.vaadin.event.FieldEvents.BlurNotifier; +import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl; +import com.vaadin.event.FieldEvents.FocusEvent; +import com.vaadin.event.FieldEvents.FocusListener; +import com.vaadin.event.FieldEvents.FocusNotifier; +import com.vaadin.shared.ui.TabIndexState; +import com.vaadin.ui.Component.Focusable; + +/** + * An abstract base class for focusable components. Includes API for setting the + * tab index, programmatic focusing, and adding focus and blur listeners. + * + * @since + * @author Vaadin Ltd + */ +public abstract class AbstractFocusable extends AbstractComponent implements + Focusable, FocusNotifier, BlurNotifier { + + protected AbstractFocusable() { + registerRpc(new FocusAndBlurServerRpcImpl(this) { + @Override + protected void fireEvent(Event event) { + AbstractFocusable.this.fireEvent(event); + } + }); + } + + @Override + public void addBlurListener(BlurListener listener) { + addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, + BlurListener.blurMethod); + } + + /** + * @deprecated As of 7.0, replaced by {@link #addBlurListener(BlurListener)} + */ + @Override + @Deprecated + public void addListener(BlurListener listener) { + addBlurListener(listener); + } + + @Override + public void removeBlurListener(BlurListener listener) { + removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeBlurListener(BlurListener)} + */ + @Override + @Deprecated + public void removeListener(BlurListener listener) { + removeBlurListener(listener); + + } + + @Override + public void addFocusListener(FocusListener listener) { + addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, + FocusListener.focusMethod); + + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addFocusListener(FocusListener)} + */ + @Override + @Deprecated + public void addListener(FocusListener listener) { + addFocusListener(listener); + } + + @Override + public void removeFocusListener(FocusListener listener) { + removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeFocusListener(FocusListener)} + */ + @Override + @Deprecated + public void removeListener(FocusListener listener) { + removeFocusListener(listener); + } + + @Override + public void focus() { + super.focus(); + } + + @Override + public int getTabIndex() { + return getState(false).tabIndex; + } + + @Override + public void setTabIndex(int tabIndex) { + getState().tabIndex = tabIndex; + } + + @Override + protected TabIndexState getState() { + return (TabIndexState) super.getState(); + } + + @Override + protected TabIndexState getState(boolean markAsDirty) { + return (TabIndexState) super.getState(markAsDirty); + } +} diff --git a/server/src/com/vaadin/ui/Button.java b/server/src/com/vaadin/ui/Button.java index 6beb6ed686..a918780a60 100644 --- a/server/src/com/vaadin/ui/Button.java +++ b/server/src/com/vaadin/ui/Button.java @@ -24,12 +24,7 @@ import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import com.vaadin.event.Action; -import com.vaadin.event.FieldEvents; -import com.vaadin.event.FieldEvents.BlurEvent; -import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl; -import com.vaadin.event.FieldEvents.FocusEvent; -import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.ShortcutAction; import com.vaadin.event.ShortcutAction.KeyCode; import com.vaadin.event.ShortcutAction.ModifierKey; @@ -38,7 +33,6 @@ import com.vaadin.server.Resource; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.ui.button.ButtonServerRpc; import com.vaadin.shared.ui.button.ButtonState; -import com.vaadin.ui.Component.Focusable; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.util.ReflectTools; @@ -50,8 +44,7 @@ import com.vaadin.util.ReflectTools; * @since 3.0 */ @SuppressWarnings("serial") -public class Button extends AbstractComponent implements - FieldEvents.BlurNotifier, FieldEvents.FocusNotifier, Focusable, +public class Button extends AbstractFocusable implements Action.ShortcutNotifier { private ButtonServerRpc rpc = new ButtonServerRpc() { @@ -72,20 +65,11 @@ public class Button extends AbstractComponent implements } }; - FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(this) { - - @Override - protected void fireEvent(Event event) { - Button.this.fireEvent(event); - } - }; - /** * Creates a new push button. */ public Button() { registerRpc(rpc); - registerRpc(focusBlurRpc); } /** @@ -393,67 +377,6 @@ public class Button extends AbstractComponent implements fireEvent(new Button.ClickEvent(this, details)); } - @Override - public void addBlurListener(BlurListener listener) { - addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, - BlurListener.blurMethod); - } - - /** - * @deprecated As of 7.0, replaced by {@link #addBlurListener(BlurListener)} - **/ - @Override - @Deprecated - public void addListener(BlurListener listener) { - addBlurListener(listener); - } - - @Override - public void removeBlurListener(BlurListener listener) { - removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeBlurListener(BlurListener)} - **/ - @Override - @Deprecated - public void removeListener(BlurListener listener) { - removeBlurListener(listener); - } - - @Override - public void addFocusListener(FocusListener listener) { - addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, - FocusListener.focusMethod); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addFocusListener(FocusListener)} - **/ - @Override - @Deprecated - public void addListener(FocusListener listener) { - addFocusListener(listener); - } - - @Override - public void removeFocusListener(FocusListener listener) { - removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeFocusListener(FocusListener)} - **/ - @Override - @Deprecated - public void removeListener(FocusListener listener) { - removeFocusListener(listener); - } - /* * Actions */ @@ -575,32 +498,6 @@ public class Button extends AbstractComponent implements getState().disableOnClick = disableOnClick; } - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.Component.Focusable#getTabIndex() - */ - @Override - public int getTabIndex() { - return getState(false).tabIndex; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.Component.Focusable#setTabIndex(int) - */ - @Override - public void setTabIndex(int tabIndex) { - getState().tabIndex = tabIndex; - } - - @Override - public void focus() { - // Overridden only to make public - super.focus(); - } - @Override protected ButtonState getState() { return (ButtonState) super.getState(); -- cgit v1.2.3 From 26b06bd6361002faa9eb718fb082b4eb71e8b242 Mon Sep 17 00:00:00 2001 From: Johannes Dahlström Date: Tue, 12 May 2015 17:01:14 +0300 Subject: Add focus API to Grid - Server-side Grid extends AbstractFocusable - Programmatic focus, tab index, focus/blur listeners - Client-side Grid implements GWT and Vaadin Focusable - Programmatic focus, tab index, access keys Change-Id: Ic8b35ba91f82d5fba8f819897774dc89f94ecf7b --- .../vaadin/client/connectors/GridConnector.java | 32 ++++++++- client/src/com/vaadin/client/widgets/Grid.java | 40 ++++++++++- server/src/com/vaadin/ui/Grid.java | 2 +- .../src/com/vaadin/shared/ui/grid/GridState.java | 4 +- .../grid/basicfeatures/GridBasicFeatures.java | 1 - .../grid/basicfeatures/server/GridFocusTest.java | 79 ++++++++++++++++++++++ 6 files changed, 150 insertions(+), 8 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridFocusTest.java (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index d31baaa665..3eb92ab94c 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -32,11 +32,17 @@ import java.util.logging.Logger; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.event.dom.client.BlurEvent; +import com.google.gwt.event.dom.client.BlurHandler; +import com.google.gwt.event.dom.client.FocusEvent; +import com.google.gwt.event.dom.client.FocusHandler; +import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.DeferredWorker; +import com.vaadin.client.EventHelper; import com.vaadin.client.MouseEventDetailsBuilder; import com.vaadin.client.annotations.OnStateChange; import com.vaadin.client.communication.StateChangeEvent; @@ -80,6 +86,7 @@ import com.vaadin.client.widgets.Grid.FooterRow; import com.vaadin.client.widgets.Grid.HeaderCell; import com.vaadin.client.widgets.Grid.HeaderRow; import com.vaadin.shared.Connector; +import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc; import com.vaadin.shared.data.sort.SortDirection; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.grid.DetailsConnectorChange; @@ -112,7 +119,7 @@ import elemental.json.JsonValue; */ @Connect(com.vaadin.ui.Grid.class) public class GridConnector extends AbstractHasComponentsConnector implements - SimpleManagedLayout, DeferredWorker { + SimpleManagedLayout, DeferredWorker, FocusHandler, BlurHandler { private static final class CustomCellStyleGenerator implements CellStyleGenerator { @@ -757,6 +764,9 @@ public class GridConnector extends AbstractHasComponentsConnector implements private final LazyDetailsScrollAdjuster lazyDetailsScrollAdjuster = new LazyDetailsScrollAdjuster(); + private HandlerRegistration focusHandlerRegistration = null; + private HandlerRegistration blurHandlerRegistration = null; + @Override @SuppressWarnings("unchecked") public Grid getWidget() { @@ -982,6 +992,12 @@ public class GridConnector extends AbstractHasComponentsConnector implements getWidget().resetSizesFromDom(); lastKnownTheme = activeTheme; } + + // Focus and blur events + focusHandlerRegistration = EventHelper.updateFocusHandler(this, + focusHandlerRegistration); + blurHandlerRegistration = EventHelper.updateBlurHandler(this, + blurHandlerRegistration); } private void updateSelectDeselectAllowed() { @@ -1418,4 +1434,18 @@ public class GridConnector extends AbstractHasComponentsConnector implements public DetailsListener getDetailsListener() { return detailsListener; } + + @Override + public void onFocus(FocusEvent event) { + // EventHelper.updateFocusHandler ensures that this is called only when + // there is a listener on server side + getRpcProxy(FocusAndBlurServerRpc.class).focus(); + } + + @Override + public void onBlur(BlurEvent event) { + // EventHelper.updateFocusHandler ensures that this is called only when + // there is a listener on server side + getRpcProxy(FocusAndBlurServerRpc.class).blur(); + } } diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index c20bdb0bd4..9768a589f6 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -74,12 +74,14 @@ import com.google.gwt.user.client.ui.ResizeComposite; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.BrowserInfo; import com.vaadin.client.DeferredWorker; +import com.vaadin.client.Focusable; import com.vaadin.client.WidgetUtil; import com.vaadin.client.data.DataChangeHandler; import com.vaadin.client.data.DataSource; import com.vaadin.client.renderers.ComplexRenderer; import com.vaadin.client.renderers.Renderer; import com.vaadin.client.renderers.WidgetRenderer; +import com.vaadin.client.ui.FocusUtil; import com.vaadin.client.ui.SubPartAware; import com.vaadin.client.ui.dd.DragAndDropHandler; import com.vaadin.client.widget.escalator.Cell; @@ -203,8 +205,8 @@ import com.vaadin.shared.util.SharedUtil; * @author Vaadin Ltd */ public class Grid extends ResizeComposite implements - HasSelectionHandlers, SubPartAware, DeferredWorker, HasWidgets, - HasEnabled { + HasSelectionHandlers, SubPartAware, DeferredWorker, Focusable, + com.google.gwt.user.client.ui.Focusable, HasWidgets, HasEnabled { /** * Enum describing different sections of Grid. @@ -6158,6 +6160,14 @@ public class Grid extends ResizeComposite implements return; } + String eventType = event.getType(); + + if (eventType.equals(BrowserEvents.FOCUS) + || eventType.equals(BrowserEvents.BLUR)) { + super.onBrowserEvent(event); + return; + } + EventTarget target = event.getEventTarget(); if (!Element.is(target) || isOrContainsInSpacer(Element.as(target))) { @@ -6168,7 +6178,6 @@ public class Grid extends ResizeComposite implements RowContainer container = escalator.findRowContainer(e); Cell cell; - String eventType = event.getType(); if (container == null) { if (eventType.equals(BrowserEvents.KEYDOWN) || eventType.equals(BrowserEvents.KEYUP) @@ -7773,4 +7782,29 @@ public class Grid extends ResizeComposite implements sidebar.close(); } } + + @Override + public int getTabIndex() { + return FocusUtil.getTabIndex(this); + } + + @Override + public void setAccessKey(char key) { + FocusUtil.setAccessKey(this, key); + } + + @Override + public void setFocus(boolean focused) { + FocusUtil.setFocus(this, focused); + } + + @Override + public void setTabIndex(int index) { + FocusUtil.setTabIndex(this, index); + } + + @Override + public void focus() { + setFocus(true); + } } diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index 075020b312..534146e3d7 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -172,7 +172,7 @@ import elemental.json.JsonValue; * @since 7.4 * @author Vaadin Ltd */ -public class Grid extends AbstractComponent implements SelectionNotifier, +public class Grid extends AbstractFocusable implements SelectionNotifier, SortNotifier, SelectiveRenderer, ItemClickNotifier { /** diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index e039f70988..bb35659591 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -19,9 +19,9 @@ package com.vaadin.shared.ui.grid; import java.util.ArrayList; import java.util.List; -import com.vaadin.shared.AbstractComponentState; import com.vaadin.shared.annotations.DelegateToWidget; import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.shared.ui.TabIndexState; /** * The shared state for the {@link com.vaadin.ui.components.grid.Grid} component @@ -29,7 +29,7 @@ import com.vaadin.shared.data.sort.SortDirection; * @since 7.4 * @author Vaadin Ltd */ -public class GridState extends AbstractComponentState { +public class GridState extends TabIndexState { /** * A description of which of the three bundled SelectionModels is currently diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java index 18f2d02e93..8d8be84169 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -627,7 +627,6 @@ public class GridBasicFeatures extends AbstractComponentTest { } } }); - createBooleanAction("Single select allow deselect", "State", singleSelectAllowDeselect, new Command() { @Override diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridFocusTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridFocusTest.java new file mode 100644 index 0000000000..ca9d78409c --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridFocusTest.java @@ -0,0 +1,79 @@ +/* + * 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.tests.components.grid.basicfeatures.server; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.testbench.elements.MenuBarElement; +import com.vaadin.tests.components.grid.basicfeatures.GridBasicFeaturesTest; + +/** + * Test for server-side Grid focus features. + * + * @since + * @author Vaadin Ltd + */ +public class GridFocusTest extends GridBasicFeaturesTest { + + @Before + public void setUp() { + openTestURL(); + } + + @Test + public void testFocusListener() { + selectMenuPath("Component", "Listeners", "Focus listener"); + + getGridElement().click(); + + assertTrue("Focus listener should be invoked", + getLogRow(0).contains("FocusEvent")); + } + + @Test + public void testBlurListener() { + selectMenuPath("Component", "Listeners", "Blur listener"); + + getGridElement().click(); + $(MenuBarElement.class).first().click(); + + assertTrue("Blur listener should be invoked", + getLogRow(0).contains("BlurEvent")); + } + + @Test + public void testProgrammaticFocus() { + selectMenuPath("Component", "State", "Set focus"); + + assertTrue("Grid cell (0, 0) should be focused", getGridElement() + .getCell(0, 0).isFocused()); + } + + @Test + public void testTabIndex() { + assertEquals(getGridElement().getAttribute("tabindex"), "0"); + + selectMenuPath("Component", "State", "Tab index", "-1"); + assertEquals(getGridElement().getAttribute("tabindex"), "-1"); + + selectMenuPath("Component", "State", "Tab index", "10"); + assertEquals(getGridElement().getAttribute("tabindex"), "10"); + } +} -- cgit v1.2.3 From fad6e73e62c53802092e824c67dd901e73683e6a Mon Sep 17 00:00:00 2001 From: patrik Date: Mon, 18 May 2015 14:36:18 +0300 Subject: Add grid editor events (#17451) Change-Id: Iebc4aece2a5be6b51289f5c2abf2d54d146621ae --- .../vaadin/client/connectors/GridConnector.java | 30 +++++ .../widget/grid/events/EditorCloseEvent.java | 34 +++++ .../client/widget/grid/events/EditorEvent.java | 108 +++++++++++++++ .../widget/grid/events/EditorEventHandler.java | 49 +++++++ .../client/widget/grid/events/EditorMoveEvent.java | 34 +++++ .../client/widget/grid/events/EditorOpenEvent.java | 34 +++++ client/src/com/vaadin/client/widgets/Grid.java | 35 ++++- server/src/com/vaadin/ui/Grid.java | 145 +++++++++++++++++++++ .../com/vaadin/shared/ui/grid/GridConstants.java | 15 +++ .../com/vaadin/shared/ui/grid/GridServerRpc.java | 25 ++++ .../grid/basicfeatures/GridBasicFeatures.java | 29 +++++ 11 files changed, 537 insertions(+), 1 deletion(-) create mode 100644 client/src/com/vaadin/client/widget/grid/events/EditorCloseEvent.java create mode 100644 client/src/com/vaadin/client/widget/grid/events/EditorEvent.java create mode 100644 client/src/com/vaadin/client/widget/grid/events/EditorEventHandler.java create mode 100644 client/src/com/vaadin/client/widget/grid/events/EditorMoveEvent.java create mode 100644 client/src/com/vaadin/client/widget/grid/events/EditorOpenEvent.java (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index 47b21a3429..2542e80075 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -60,6 +60,10 @@ import com.vaadin.client.widget.grid.events.ColumnReorderEvent; import com.vaadin.client.widget.grid.events.ColumnReorderHandler; import com.vaadin.client.widget.grid.events.ColumnVisibilityChangeEvent; import com.vaadin.client.widget.grid.events.ColumnVisibilityChangeHandler; +import com.vaadin.client.widget.grid.events.EditorCloseEvent; +import com.vaadin.client.widget.grid.events.EditorEventHandler; +import com.vaadin.client.widget.grid.events.EditorMoveEvent; +import com.vaadin.client.widget.grid.events.EditorOpenEvent; import com.vaadin.client.widget.grid.events.GridClickEvent; import com.vaadin.client.widget.grid.events.GridDoubleClickEvent; import com.vaadin.client.widget.grid.events.SelectAllEvent; @@ -912,6 +916,32 @@ public class GridConnector extends AbstractHasComponentsConnector implements getWidget().setDetailsGenerator(customDetailsGenerator); getLayoutManager().registerDependency(this, getWidget().getElement()); + getWidget().addEditorEventHandler(new EditorEventHandler() { + @Override + public void onEditorOpen(EditorOpenEvent e) { + if (hasEventListener(GridConstants.EDITOR_OPEN_EVENT_ID)) { + String rowKey = getRowKey((JsonObject) e.getRow()); + getRpcProxy(GridServerRpc.class).editorOpen(rowKey); + } + } + + @Override + public void onEditorMove(EditorMoveEvent e) { + if (hasEventListener(GridConstants.EDITOR_MOVE_EVENT_ID)) { + String rowKey = getRowKey((JsonObject) e.getRow()); + getRpcProxy(GridServerRpc.class).editorMove(rowKey); + } + } + + @Override + public void onEditorClose(EditorCloseEvent e) { + if (hasEventListener(GridConstants.EDITOR_CLOSE_EVENT_ID)) { + String rowKey = getRowKey((JsonObject) e.getRow()); + getRpcProxy(GridServerRpc.class).editorClose(rowKey); + } + } + }); + layout(); } diff --git a/client/src/com/vaadin/client/widget/grid/events/EditorCloseEvent.java b/client/src/com/vaadin/client/widget/grid/events/EditorCloseEvent.java new file mode 100644 index 0000000000..99f59aa82a --- /dev/null +++ b/client/src/com/vaadin/client/widget/grid/events/EditorCloseEvent.java @@ -0,0 +1,34 @@ +/* + * 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.widget.grid.events; + +import com.vaadin.client.widget.grid.CellReference; + +/** + * Event that gets fired when an open editor is closed (and not reopened + * elsewhere) + */ +public class EditorCloseEvent extends EditorEvent { + + public EditorCloseEvent(CellReference cell) { + super(cell); + } + + @Override + protected void dispatch(EditorEventHandler handler) { + handler.onEditorClose(this); + } +} \ No newline at end of file diff --git a/client/src/com/vaadin/client/widget/grid/events/EditorEvent.java b/client/src/com/vaadin/client/widget/grid/events/EditorEvent.java new file mode 100644 index 0000000000..eb34033197 --- /dev/null +++ b/client/src/com/vaadin/client/widget/grid/events/EditorEvent.java @@ -0,0 +1,108 @@ +/* + * 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.widget.grid.events; + +import com.google.gwt.event.shared.GwtEvent; +import com.vaadin.client.widget.grid.CellReference; +import com.vaadin.client.widgets.Grid; +import com.vaadin.client.widgets.Grid.Column; + +/** + * Base class for editor events. + */ +public abstract class EditorEvent extends GwtEvent { + public static final Type TYPE = new Type(); + + private CellReference cell; + + protected EditorEvent(CellReference cell) { + this.cell = cell; + } + + @Override + public Type getAssociatedType() { + return TYPE; + } + + /** + * Get a reference to the Grid that fired this Event. + * + * @return a Grid reference + */ + @SuppressWarnings("unchecked") + public Grid getGrid() { + return (Grid) cell.getGrid(); + } + + /** + * Get a reference to the cell that was active when this Event was fired. + * NOTE: do NOT rely on this information remaining accurate after + * leaving the event handler. + * + * @return a cell reference + */ + @SuppressWarnings("unchecked") + public CellReference getCell() { + return (CellReference) cell; + } + + /** + * Get a reference to the row that was active when this Event was fired. + * NOTE: do NOT rely on this information remaining accurate after + * leaving the event handler. + * + * @return a row data object + */ + @SuppressWarnings("unchecked") + public T getRow() { + return (T) cell.getRow(); + } + + /** + * Get the index of the row that was active when this Event was fired. NOTE: + * do NOT rely on this information remaining accurate after leaving + * the event handler. + * + * @return an integer value + */ + public int getRowIndex() { + return cell.getRowIndex(); + } + + /** + * Get a reference to the column that was active when this Event was fired. + * NOTE: do NOT rely on this information remaining accurate after + * leaving the event handler. + * + * @return a column object + */ + @SuppressWarnings("unchecked") + public Column getColumn() { + return (Column) cell.getColumn(); + } + + /** + * Get the index of the column that was active when this Event was fired. + * NOTE: do NOT rely on this information remaining accurate after + * leaving the event handler. + * + * @return an integer value + */ + public int getColumnIndex() { + return cell.getColumnIndex(); + } + +} \ No newline at end of file diff --git a/client/src/com/vaadin/client/widget/grid/events/EditorEventHandler.java b/client/src/com/vaadin/client/widget/grid/events/EditorEventHandler.java new file mode 100644 index 0000000000..4f9396a9f1 --- /dev/null +++ b/client/src/com/vaadin/client/widget/grid/events/EditorEventHandler.java @@ -0,0 +1,49 @@ +/* + * 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.widget.grid.events; + +import com.google.gwt.event.shared.EventHandler; + +/** + * Common handler interface for editor events + */ +public interface EditorEventHandler extends EventHandler { + + /** + * Action to perform when the editor has been opened + * + * @param e + * an editor open event object + */ + public void onEditorOpen(EditorOpenEvent e); + + /** + * Action to perform when the editor is re-opened on another row + * + * @param e + * an editor move event object + */ + public void onEditorMove(EditorMoveEvent e); + + /** + * Action to perform when the editor is closed + * + * @param e + * an editor close event object + */ + public void onEditorClose(EditorCloseEvent e); + +} \ No newline at end of file diff --git a/client/src/com/vaadin/client/widget/grid/events/EditorMoveEvent.java b/client/src/com/vaadin/client/widget/grid/events/EditorMoveEvent.java new file mode 100644 index 0000000000..0e5e2dcd7b --- /dev/null +++ b/client/src/com/vaadin/client/widget/grid/events/EditorMoveEvent.java @@ -0,0 +1,34 @@ +/* + * 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.widget.grid.events; + +import com.vaadin.client.widget.grid.CellReference; + +/** + * Event that gets fired when an already open editor is closed and re-opened on + * another row + */ +public class EditorMoveEvent extends EditorEvent { + + public EditorMoveEvent(CellReference cell) { + super(cell); + } + + @Override + protected void dispatch(EditorEventHandler handler) { + handler.onEditorMove(this); + } +} \ No newline at end of file diff --git a/client/src/com/vaadin/client/widget/grid/events/EditorOpenEvent.java b/client/src/com/vaadin/client/widget/grid/events/EditorOpenEvent.java new file mode 100644 index 0000000000..df0171945f --- /dev/null +++ b/client/src/com/vaadin/client/widget/grid/events/EditorOpenEvent.java @@ -0,0 +1,34 @@ +/* + * 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.widget.grid.events; + +import com.vaadin.client.widget.grid.CellReference; + +/** + * Event that gets fired when the editor is opened + */ +public class EditorOpenEvent extends EditorEvent { + + public EditorOpenEvent(CellReference cell) { + super(cell); + } + + @Override + protected void dispatch(EditorEventHandler handler) { + handler.onEditorOpen(this); + } + +} \ No newline at end of file diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 9768a589f6..21cf84cc0d 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -120,6 +120,11 @@ import com.vaadin.client.widget.grid.events.ColumnReorderEvent; import com.vaadin.client.widget.grid.events.ColumnReorderHandler; import com.vaadin.client.widget.grid.events.ColumnVisibilityChangeEvent; import com.vaadin.client.widget.grid.events.ColumnVisibilityChangeHandler; +import com.vaadin.client.widget.grid.events.EditorCloseEvent; +import com.vaadin.client.widget.grid.events.EditorEvent; +import com.vaadin.client.widget.grid.events.EditorEventHandler; +import com.vaadin.client.widget.grid.events.EditorMoveEvent; +import com.vaadin.client.widget.grid.events.EditorOpenEvent; import com.vaadin.client.widget.grid.events.FooterClickHandler; import com.vaadin.client.widget.grid.events.FooterDoubleClickHandler; import com.vaadin.client.widget.grid.events.FooterKeyDownHandler; @@ -1188,6 +1193,7 @@ public class Grid extends ResizeComposite implements + " remember to call success() or fail()?"); } }; + private final EditorRequestImpl.RequestCallback bindRequestCallback = new EditorRequestImpl.RequestCallback() { @Override public void onSuccess(EditorRequest request) { @@ -1313,6 +1319,7 @@ public class Grid extends ResizeComposite implements handler.cancel(request); state = State.INACTIVE; updateSelectionCheckboxesAsNeeded(true); + grid.fireEvent(new EditorCloseEvent(grid.eventCell)); } private void updateSelectionCheckboxesAsNeeded(boolean isEnabled) { @@ -5706,6 +5713,19 @@ public class Grid extends ResizeComposite implements return editor; } + /** + * Add handler for editor open/move/close events + * + * @param handler + * editor handler object + * @return a {@link HandlerRegistration} object that can be used to remove + * the event handler + */ + public HandlerRegistration addEditorEventHandler(EditorEventHandler handler) { + return addHandler(handler, EditorEvent.TYPE); + + } + protected Escalator getEscalator() { return escalator; } @@ -6285,12 +6305,25 @@ public class Grid extends ResizeComposite implements } if (container == escalator.getBody() && editor.isEnabled()) { + + boolean wasOpen = editor.getState() != Editor.State.INACTIVE; + boolean opened = false; + if (event.getTypeInt() == Event.ONDBLCLICK) { editor.editRow(eventCell.getRowIndex()); - return true; + opened = true; } else if (event.getTypeInt() == Event.ONKEYDOWN && event.getKeyCode() == Editor.KEYCODE_SHOW) { editor.editRow(cellFocusHandler.rowWithFocus); + opened = true; + } + + if (opened) { + if (wasOpen) { + fireEvent(new EditorMoveEvent(eventCell)); + } else { + fireEvent(new EditorOpenEvent(eventCell)); + } return true; } } diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index 534146e3d7..21bfd1b68f 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -57,6 +57,7 @@ import com.vaadin.data.RpcDataProviderExtension.DetailComponentManager; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.fieldgroup.DefaultFieldGroupFieldFactory; import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.fieldgroup.FieldGroup.BindException; import com.vaadin.data.fieldgroup.FieldGroup.CommitException; import com.vaadin.data.fieldgroup.FieldGroupFieldFactory; import com.vaadin.data.sort.Sort; @@ -510,6 +511,100 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, } + /** + * Interface for an editor event listener + */ + public interface EditorListener extends Serializable { + + public static final Method EDITOR_OPEN_METHOD = ReflectTools + .findMethod(EditorListener.class, "editorOpened", + EditorOpenEvent.class); + public static final Method EDITOR_MOVE_METHOD = ReflectTools + .findMethod(EditorListener.class, "editorMoved", + EditorMoveEvent.class); + public static final Method EDITOR_CLOSE_METHOD = ReflectTools + .findMethod(EditorListener.class, "editorClosed", + EditorCloseEvent.class); + + /** + * Called when an editor is opened + * + * @param e + * an editor open event object + */ + public void editorOpened(EditorOpenEvent e); + + /** + * Called when an editor is reopened without closing it first + * + * @param e + * an editor move event object + */ + public void editorMoved(EditorMoveEvent e); + + /** + * Called when an editor is closed + * + * @param e + * an editor close event object + */ + public void editorClosed(EditorCloseEvent e); + + } + + /** + * Base class for editor related events + */ + public static abstract class EditorEvent extends Component.Event { + + private Object itemID; + + protected EditorEvent(Grid source, Object itemID) { + super(source); + this.itemID = itemID; + } + + /** + * Get the item (row) for which this editor was opened + */ + public Object getItem() { + return itemID; + } + + } + + /** + * This event gets fired when an editor is opened + */ + public static class EditorOpenEvent extends EditorEvent { + + public EditorOpenEvent(Grid source, Object itemID) { + super(source, itemID); + } + } + + /** + * This event gets fired when an editor is opened while another row is being + * edited (i.e. editor focus moves elsewhere) + */ + public static class EditorMoveEvent extends EditorEvent { + + public EditorMoveEvent(Grid source, Object itemID) { + super(source, itemID); + } + } + + /** + * This event gets fired when an editor is dismissed or closed by other + * means. + */ + public static class EditorCloseEvent extends EditorEvent { + + public EditorCloseEvent(Grid source, Object itemID) { + super(source, itemID); + } + } + /** * Default error handler for the editor * @@ -3808,6 +3903,24 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, detailComponentManager.getAndResetConnectorChanges(), fetchId); } + + @Override + public void editorOpen(String rowKey) { + fireEvent(new EditorOpenEvent(Grid.this, getKeyMapper() + .getItemId(rowKey))); + } + + @Override + public void editorMove(String rowKey) { + fireEvent(new EditorMoveEvent(Grid.this, getKeyMapper() + .getItemId(rowKey))); + } + + @Override + public void editorClose(String rowKey) { + fireEvent(new EditorCloseEvent(Grid.this, getKeyMapper() + .getItemId(rowKey))); + } }); registerRpc(new EditorServerRpc() { @@ -5875,6 +5988,37 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, return getState(false).editorCancelCaption; } + /** + * Add an editor event listener + * + * @param listener + * the event listener object to add + */ + public void addEditorListener(EditorListener listener) { + addListener(GridConstants.EDITOR_OPEN_EVENT_ID, EditorOpenEvent.class, + listener, EditorListener.EDITOR_OPEN_METHOD); + addListener(GridConstants.EDITOR_MOVE_EVENT_ID, EditorMoveEvent.class, + listener, EditorListener.EDITOR_MOVE_METHOD); + addListener(GridConstants.EDITOR_CLOSE_EVENT_ID, + EditorCloseEvent.class, listener, + EditorListener.EDITOR_CLOSE_METHOD); + } + + /** + * Remove an editor event listener + * + * @param listener + * the event listener object to remove + */ + public void removeEditorListener(EditorListener listener) { + removeListener(GridConstants.EDITOR_OPEN_EVENT_ID, + EditorOpenEvent.class, listener); + removeListener(GridConstants.EDITOR_MOVE_EVENT_ID, + EditorMoveEvent.class, listener); + removeListener(GridConstants.EDITOR_CLOSE_EVENT_ID, + EditorCloseEvent.class, listener); + } + @Override public void addItemClickListener(ItemClickListener listener) { addListener(GridConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, @@ -6169,6 +6313,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, result.add("footer-visible"); result.add("editor-error-handler"); result.add("height-mode"); + return result; } } diff --git a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java index 0606e4b1cc..5b2ac96975 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java @@ -74,4 +74,19 @@ public final class GridConstants implements Serializable { /** The default cancel button caption in the editor */ public static final String DEFAULT_CANCEL_CAPTION = "Cancel"; + + /** + * Event ID constant for editor open event + */ + public static final String EDITOR_OPEN_EVENT_ID = "editorOpen"; + + /** + * Event ID constant for editor move event + */ + public static final String EDITOR_MOVE_EVENT_ID = "editorMove"; + + /** + * Event ID constant for editor close event + */ + public static final String EDITOR_CLOSE_EVENT_ID = "editorClose"; } diff --git a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java index dca55c11c4..99b339765a 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java @@ -36,6 +36,31 @@ public interface GridServerRpc extends ServerRpc { void sort(String[] columnIds, SortDirection[] directions, boolean userOriginated); + /** + * Informs the server that the editor was opened (fresh) on a certain row + * + * @param rowKey + * a key identifying item the editor was opened on + */ + void editorOpen(String rowKey); + + /** + * Informs the server that the editor was reopened (without closing it in + * between) on another row + * + * @param rowKey + * a key identifying item the editor was opened on + */ + void editorMove(String rowKey); + + /** + * Informs the server that the editor was closed + * + * @param rowKey + * a key identifying item the editor was opened on + */ + void editorClose(String rowKey); + /** * Informs the server that an item has been clicked in Grid. * diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java index 8d8be84169..181f99a2c7 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -57,6 +57,10 @@ import com.vaadin.ui.Grid.ColumnReorderListener; import com.vaadin.ui.Grid.ColumnVisibilityChangeEvent; import com.vaadin.ui.Grid.ColumnVisibilityChangeListener; import com.vaadin.ui.Grid.DetailsGenerator; +import com.vaadin.ui.Grid.EditorCloseEvent; +import com.vaadin.ui.Grid.EditorListener; +import com.vaadin.ui.Grid.EditorMoveEvent; +import com.vaadin.ui.Grid.EditorOpenEvent; import com.vaadin.ui.Grid.FooterCell; import com.vaadin.ui.Grid.HeaderCell; import com.vaadin.ui.Grid.HeaderRow; @@ -401,6 +405,7 @@ public class GridBasicFeatures extends AbstractComponentTest { } protected void createGridActions() { + LinkedHashMap primaryStyleNames = new LinkedHashMap(); primaryStyleNames.put("v-grid", "v-grid"); primaryStyleNames.put("v-escalator", "v-escalator"); @@ -1218,6 +1223,30 @@ public class GridBasicFeatures extends AbstractComponentTest { c.setEditorCancelCaption("ʃǝɔuɐↃ"); } }, null); + + createClickAction("Add editor state listener", "Editor", + new Command() { + @Override + public void execute(Grid grid, String value, Object data) { + grid.addEditorListener(new EditorListener() { + @Override + public void editorOpened(EditorOpenEvent e) { + log("Editor opened"); + } + + @Override + public void editorMoved(EditorMoveEvent e) { + log("Editor moved"); + } + + @Override + public void editorClosed(EditorCloseEvent e) { + log("Editor closed"); + } + }); + } + }, null); + } @SuppressWarnings("boxing") -- cgit v1.2.3 From 469a53e1256f143f506f8a3b59ef7fc505353495 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Mon, 25 May 2015 12:09:23 +0300 Subject: Add unbuffered editing mode to Grid Save and cancel buttons are hidden and the backing field group is set to unbuffered mode. Change-Id: I7da46ae3f1b84cc5ac8c918be38919962aff88ed --- client/src/com/vaadin/client/widgets/Grid.java | 38 ++++++++++++++++++++-- server/src/com/vaadin/ui/Grid.java | 33 +++++++++++++++++++ .../src/com/vaadin/shared/ui/grid/GridState.java | 4 +++ .../grid/basicfeatures/GridBasicFeatures.java | 8 +++++ .../grid/basicfeatures/server/GridEditorTest.java | 37 +++++++++++++++++++++ 5 files changed, 118 insertions(+), 2 deletions(-) (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 21cf84cc0d..bf5494291f 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -1225,6 +1225,7 @@ public class Grid extends ResizeComposite implements /** A set of all the columns that display an error flag. */ private final Set> columnErrors = new HashSet>(); + private boolean buffered = true; public Editor() { saveButton = new Button(); @@ -1504,8 +1505,10 @@ public class Grid extends ResizeComposite implements messageAndButtonsWrapper.appendChild(buttonsWrapper); } - attachWidget(saveButton, buttonsWrapper); - attachWidget(cancelButton, buttonsWrapper); + if (isBuffered()) { + attachWidget(saveButton, buttonsWrapper); + attachWidget(cancelButton, buttonsWrapper); + } updateHorizontalScrollPosition(); @@ -1715,6 +1718,14 @@ public class Grid extends ResizeComposite implements public boolean isEditorColumnError(Column column) { return columnErrors.contains(column); } + + public void setBuffered(boolean buffered) { + this.buffered = buffered; + } + + public boolean isBuffered() { + return buffered; + } } public static abstract class AbstractGridKeyEvent @@ -7840,4 +7851,27 @@ public class Grid extends ResizeComposite implements public void focus() { setFocus(true); } + + /** + * Sets the buffered editor mode. + * + * @since + * @param editorUnbuffered + * true to enable buffered editor, + * false to disable it + */ + public void setEditorBuffered(boolean editorBuffered) { + editor.setBuffered(editorBuffered); + } + + /** + * Gets the buffered editor mode. + * + * @since + * @return true if buffered editor is enabled, + * false otherwise + */ + public boolean isEditorBuffered() { + return editor.isBuffered(); + } } diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index 21bfd1b68f..fc5adbbff0 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -6019,6 +6019,39 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, EditorCloseEvent.class, listener); } + /** + * Sets the buffered editor mode. The default mode is buffered ( + * true). + * + * @since + * @param editorBuffered + * true to enable buffered editor, + * false to disable it + * @throws IllegalStateException + * If editor is active while attempting to change the buffered + * mode. + */ + public void setEditorBuffered(boolean editorBuffered) + throws IllegalStateException { + if (isEditorActive()) { + throw new IllegalStateException( + "Can't change editor unbuffered mode while editor is active."); + } + getState().editorBuffered = editorBuffered; + editorFieldGroup.setBuffered(editorBuffered); + } + + /** + * Gets the buffered editor mode. + * + * @since + * @return true if buffered editor is enabled, + * false otherwise + */ + public boolean isEditorBuffered() { + return getState().editorBuffered; + } + @Override public void addItemClickListener(ItemClickListener listener) { addListener(GridConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index bb35659591..3faa4b441c 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -154,6 +154,10 @@ public class GridState extends TabIndexState { /** The enabled state of the editor interface */ public boolean editorEnabled = false; + /** Buffered editor mode */ + @DelegateToWidget + public boolean editorBuffered = true; + /** Whether row data might contain generated row styles */ public boolean hasRowStyleGenerator; /** Whether row data might contain generated cell styles */ diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java index 181f99a2c7..ba19d7ee72 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -1176,6 +1176,14 @@ public class GridBasicFeatures extends AbstractComponentTest { } }); + createBooleanAction("Buffered mode", "Editor", true, + new Command() { + @Override + public void execute(Grid c, Boolean value, Object data) { + c.setEditorBuffered(value); + } + }); + createClickAction("Edit item 5", "Editor", new Command() { @Override public void execute(Grid c, String value, Object data) { diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java index f37a94358a..8a8792c351 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java @@ -47,6 +47,8 @@ public class GridEditorTest extends GridBasicFeaturesTest { "Editor", "Edit item 100" }; private static final String[] TOGGLE_EDIT_ENABLED = new String[] { "Component", "Editor", "Enabled" }; + private static final String[] TOGGLE_EDITOR_BUFFERED_ENABLED = new String[] { + "Component", "Editor", "Buffered mode" }; @Before public void setUp() { @@ -302,6 +304,41 @@ public class GridEditorTest extends GridBasicFeaturesTest { getGridElement().getEditor().isEditable(3)); } + @Test + public void testEditorUnbufferedShowsNoButtons() { + selectMenuPath(TOGGLE_EDITOR_BUFFERED_ENABLED); + selectMenuPath(EDIT_ITEM_5); + + assertEditorOpen(); + + boolean saveButtonFound = true; + try { + getSaveButton(); + } catch (NoSuchElementException e) { + saveButtonFound = false; + } + assertFalse("Save button should not be visible in unbuffered mode.", + saveButtonFound); + + boolean cancelButtonFound = true; + try { + getCancelButton(); + } catch (NoSuchElementException e) { + cancelButtonFound = false; + } + assertFalse("Cancel button should not be visible in unbuffered mode.", + cancelButtonFound); + } + + @Test + public void testEditorUnbufferedWhileOpen() { + selectMenuPath(EDIT_ITEM_5); + selectMenuPath(TOGGLE_EDITOR_BUFFERED_ENABLED); + assertEditorOpen(); + boolean thrown = logContainsText("Exception occured, java.lang.IllegalStateException"); + assertTrue("IllegalStateException thrown", thrown); + } + private WebElement getSaveButton() { return getDriver().findElement(By.className("v-grid-editor-save")); } -- cgit v1.2.3 From feab1153761049b4f833174586d158bef22eb15c Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Tue, 26 May 2015 15:57:16 +0300 Subject: Let mouse click move editor in unbuffered mode Change-Id: I90a01ee7877aec35835145fb8b9c2dd49899dc5a --- client/src/com/vaadin/client/widgets/Grid.java | 35 ++++- server/src/com/vaadin/ui/Grid.java | 4 +- .../server/GridEditorBufferedTest.java | 153 ++++++++++++++++++ .../grid/basicfeatures/server/GridEditorTest.java | 173 ++------------------- .../server/GridEditorUnbufferedTest.java | 88 +++++++++++ 5 files changed, 279 insertions(+), 174 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java create mode 100644 uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index bf5494291f..4616c43902 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -1425,6 +1425,13 @@ public class Grid extends ResizeComposite implements } } + protected void hide() { + hideOverlay(); + grid.getEscalator().setScrollLocked(Direction.VERTICAL, false); + state = State.INACTIVE; + updateSelectionCheckboxesAsNeeded(true); + } + protected void setGrid(final Grid grid) { assert grid != null : "Grid cannot be null"; assert this.grid == null : "Can only attach editor to Grid once"; @@ -5063,7 +5070,7 @@ public class Grid extends ResizeComposite implements sinkEvents(getHeader().getConsumedEvents()); sinkEvents(Arrays.asList(BrowserEvents.KEYDOWN, BrowserEvents.KEYUP, BrowserEvents.KEYPRESS, BrowserEvents.DBLCLICK, - BrowserEvents.MOUSEDOWN)); + BrowserEvents.MOUSEDOWN, BrowserEvents.CLICK)); // Make ENTER and SHIFT+ENTER in the header perform sorting addHeaderKeyUpHandler(new HeaderKeyUpHandler() { @@ -6306,31 +6313,43 @@ public class Grid extends ResizeComposite implements } private boolean handleEditorEvent(Event event, RowContainer container) { + int type = event.getTypeInt(); + boolean editorIsActive = editor.getState() != Editor.State.INACTIVE; - if (editor.getState() != Editor.State.INACTIVE) { - if (event.getTypeInt() == Event.ONKEYDOWN + if (editorIsActive) { + // React to closing by keyboard in buffered and unbuffered mode + if (type == Event.ONKEYDOWN && event.getKeyCode() == Editor.KEYCODE_HIDE) { editor.cancel(); + return true; + } + // Swallow all other events in buffered mode and everything except + // ONCLICK in unbuffered mode + if (editor.isBuffered() || type != Event.ONCLICK) { + return true; } - return true; } if (container == escalator.getBody() && editor.isEnabled()) { - boolean wasOpen = editor.getState() != Editor.State.INACTIVE; boolean opened = false; - if (event.getTypeInt() == Event.ONDBLCLICK) { + if (editorIsActive && !editor.isBuffered() && type == Event.ONCLICK) { + editor.hide(); + cellFocusHandler.setCellFocus(eventCell); + editor.editRow(eventCell.getRowIndex()); + opened = true; + } else if (type == Event.ONDBLCLICK) { editor.editRow(eventCell.getRowIndex()); opened = true; - } else if (event.getTypeInt() == Event.ONKEYDOWN + } else if (type == Event.ONKEYDOWN && event.getKeyCode() == Editor.KEYCODE_SHOW) { editor.editRow(cellFocusHandler.rowWithFocus); opened = true; } if (opened) { - if (wasOpen) { + if (editorIsActive) { fireEvent(new EditorMoveEvent(eventCell)); } else { fireEvent(new EditorOpenEvent(eventCell)); diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index fc5adbbff0..43e82560df 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -3930,13 +3930,13 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, boolean success = false; try { Object id = getContainerDataSource().getIdByIndex(rowIndex); - if (editedItemId == null) { + if (!isEditorBuffered() || editedItemId == null) { editedItemId = id; } if (editedItemId.equals(id)) { - success = true; doEditItem(); + success = true; } } catch (Exception e) { handleError(e); diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java new file mode 100644 index 0000000000..49a5174f8d --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java @@ -0,0 +1,153 @@ +/* + * 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.tests.components.grid.basicfeatures.server; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import com.vaadin.shared.ui.grid.GridConstants; +import com.vaadin.testbench.elements.GridElement.GridEditorElement; +import com.vaadin.testbench.elements.NotificationElement; + +public class GridEditorBufferedTest extends GridEditorTest { + + @Override + @Before + public void setUp() { + super.setUp(); + } + + @Test + public void testSave() { + selectMenuPath(EDIT_ITEM_100); + + WebElement textField = getEditorWidgets().get(0); + + textField.click(); + + textField.sendKeys(" changed"); + + WebElement saveButton = getEditor().findElement( + By.className("v-grid-editor-save")); + + saveButton.click(); + + assertEquals("(100, 0) changed", getGridElement().getCell(100, 0) + .getText()); + } + + @Test + public void testProgrammaticSave() { + selectMenuPath(EDIT_ITEM_100); + + WebElement textField = getEditorWidgets().get(0); + + textField.click(); + + textField.sendKeys(" changed"); + + selectMenuPath("Component", "Editor", "Save"); + + assertEquals("(100, 0) changed", getGridElement().getCell(100, 0) + .getText()); + } + + @Test + public void testInvalidEdition() { + selectMenuPath(EDIT_ITEM_5); + assertFalse(logContainsText("Exception occured, java.lang.IllegalStateException")); + + GridEditorElement editor = getGridElement().getEditor(); + + assertFalse( + "Field 7 should not have been marked with an error before error", + editor.isFieldErrorMarked(7)); + + WebElement intField = editor.getField(7); + intField.clear(); + intField.sendKeys("banana phone"); + editor.save(); + + assertEquals("Column 7: Could not convert value to Integer", + editor.getErrorMessage()); + assertTrue("Field 7 should have been marked with an error after error", + editor.isFieldErrorMarked(7)); + editor.cancel(); + + selectMenuPath(EDIT_ITEM_100); + assertFalse("Exception should not exist", + isElementPresent(NotificationElement.class)); + assertEquals("There should be no editor error message", null, + editor.getErrorMessage()); + } + + @Test + public void testEditorInDisabledGrid() { + int originalScrollPos = getGridVerticalScrollPos(); + + selectMenuPath(EDIT_ITEM_5); + assertEditorOpen(); + + selectMenuPath("Component", "State", "Enabled"); + assertEditorOpen(); + + GridEditorElement editor = getGridElement().getEditor(); + editor.save(); + assertEditorOpen(); + + editor.cancel(); + assertEditorOpen(); + + selectMenuPath("Component", "State", "Enabled"); + + scrollGridVerticallyTo(100); + assertEquals("Grid shouldn't scroll vertically while editing", + originalScrollPos, getGridVerticalScrollPos()); + } + + @Test + public void testCaptionChange() { + selectMenuPath(EDIT_ITEM_5); + assertEquals("Save button caption should've been \"" + + GridConstants.DEFAULT_SAVE_CAPTION + "\" to begin with", + GridConstants.DEFAULT_SAVE_CAPTION, getSaveButton().getText()); + assertEquals("Cancel button caption should've been \"" + + GridConstants.DEFAULT_CANCEL_CAPTION + "\" to begin with", + GridConstants.DEFAULT_CANCEL_CAPTION, getCancelButton() + .getText()); + + selectMenuPath("Component", "Editor", "Change save caption"); + assertNotEquals( + "Save button caption should've changed while editor is open", + GridConstants.DEFAULT_SAVE_CAPTION, getSaveButton().getText()); + + getCancelButton().click(); + + selectMenuPath("Component", "Editor", "Change cancel caption"); + selectMenuPath(EDIT_ITEM_5); + assertNotEquals( + "Cancel button caption should've changed while editor is closed", + GridConstants.DEFAULT_CANCEL_CAPTION, getCancelButton() + .getText()); + } +} diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java index 8a8792c351..b32d50024d 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java @@ -17,7 +17,6 @@ package com.vaadin.tests.components.grid.basicfeatures.server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -32,23 +31,18 @@ import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; -import com.vaadin.shared.ui.grid.GridConstants; import com.vaadin.testbench.elements.GridElement.GridCellElement; -import com.vaadin.testbench.elements.GridElement.GridEditorElement; -import com.vaadin.testbench.elements.NotificationElement; import com.vaadin.tests.components.grid.basicfeatures.GridBasicFeatures; import com.vaadin.tests.components.grid.basicfeatures.GridBasicFeaturesTest; -public class GridEditorTest extends GridBasicFeaturesTest { +public abstract class GridEditorTest extends GridBasicFeaturesTest { - private static final String[] EDIT_ITEM_5 = new String[] { "Component", + protected static final String[] EDIT_ITEM_5 = new String[] { "Component", "Editor", "Edit item 5" }; - private static final String[] EDIT_ITEM_100 = new String[] { "Component", + protected static final String[] EDIT_ITEM_100 = new String[] { "Component", "Editor", "Edit item 100" }; - private static final String[] TOGGLE_EDIT_ENABLED = new String[] { + protected static final String[] TOGGLE_EDIT_ENABLED = new String[] { "Component", "Editor", "Enabled" }; - private static final String[] TOGGLE_EDITOR_BUFFERED_ENABLED = new String[] { - "Component", "Editor", "Buffered mode" }; @Before public void setUp() { @@ -129,112 +123,22 @@ public class GridEditorTest extends GridBasicFeaturesTest { assertEquals("100", widgets.get(8).getAttribute("value")); } - @Test - public void testSave() { - selectMenuPath(EDIT_ITEM_100); - - WebElement textField = getEditorWidgets().get(0); - - textField.click(); - - textField.sendKeys(" changed"); - - WebElement saveButton = getEditor().findElement( - By.className("v-grid-editor-save")); - - saveButton.click(); - - assertEquals("(100, 0) changed", getGridElement().getCell(100, 0) - .getText()); - } - - @Test - public void testProgrammaticSave() { - selectMenuPath(EDIT_ITEM_100); - - WebElement textField = getEditorWidgets().get(0); - - textField.click(); - - textField.sendKeys(" changed"); - - selectMenuPath("Component", "Editor", "Save"); - - assertEquals("(100, 0) changed", getGridElement().getCell(100, 0) - .getText()); - } - - @Test - public void testCaptionChange() { - selectMenuPath(EDIT_ITEM_5); - assertEquals("Save button caption should've been \"" - + GridConstants.DEFAULT_SAVE_CAPTION + "\" to begin with", - GridConstants.DEFAULT_SAVE_CAPTION, getSaveButton().getText()); - assertEquals("Cancel button caption should've been \"" - + GridConstants.DEFAULT_CANCEL_CAPTION + "\" to begin with", - GridConstants.DEFAULT_CANCEL_CAPTION, getCancelButton() - .getText()); - - selectMenuPath("Component", "Editor", "Change save caption"); - assertNotEquals( - "Save button caption should've changed while editor is open", - GridConstants.DEFAULT_SAVE_CAPTION, getSaveButton().getText()); - - getCancelButton().click(); - - selectMenuPath("Component", "Editor", "Change cancel caption"); - selectMenuPath(EDIT_ITEM_5); - assertNotEquals( - "Cancel button caption should've changed while editor is closed", - GridConstants.DEFAULT_CANCEL_CAPTION, getCancelButton() - .getText()); - } - - private void assertEditorOpen() { + protected void assertEditorOpen() { assertNotNull("Editor is supposed to be open", getEditor()); assertEquals("Unexpected number of widgets", GridBasicFeatures.EDITABLE_COLUMNS, getEditorWidgets().size()); } - private void assertEditorClosed() { + protected void assertEditorClosed() { assertNull("Editor is supposed to be closed", getEditor()); } - private List getEditorWidgets() { + protected List getEditorWidgets() { assertNotNull(getEditor()); return getEditor().findElements(By.className("v-textfield")); } - @Test - public void testInvalidEdition() { - selectMenuPath(EDIT_ITEM_5); - assertFalse(logContainsText("Exception occured, java.lang.IllegalStateException")); - - GridEditorElement editor = getGridElement().getEditor(); - - assertFalse( - "Field 7 should not have been marked with an error before error", - editor.isFieldErrorMarked(7)); - - WebElement intField = editor.getField(7); - intField.clear(); - intField.sendKeys("banana phone"); - editor.save(); - - assertEquals("Column 7: Could not convert value to Integer", - editor.getErrorMessage()); - assertTrue("Field 7 should have been marked with an error after error", - editor.isFieldErrorMarked(7)); - editor.cancel(); - - selectMenuPath(EDIT_ITEM_100); - assertFalse("Exception should not exist", - isElementPresent(NotificationElement.class)); - assertEquals("There should be no editor error message", null, - editor.getErrorMessage()); - } - @Test public void testNoScrollAfterEditByAPI() { int originalScrollPos = getGridVerticalScrollPos(); @@ -271,30 +175,6 @@ public class GridEditorTest extends GridBasicFeaturesTest { originalScrollPos, getGridVerticalScrollPos()); } - @Test - public void testEditorInDisabledGrid() { - int originalScrollPos = getGridVerticalScrollPos(); - - selectMenuPath(EDIT_ITEM_5); - assertEditorOpen(); - - selectMenuPath("Component", "State", "Enabled"); - assertEditorOpen(); - - GridEditorElement editor = getGridElement().getEditor(); - editor.save(); - assertEditorOpen(); - - editor.cancel(); - assertEditorOpen(); - - selectMenuPath("Component", "State", "Enabled"); - - scrollGridVerticallyTo(100); - assertEquals("Grid shouldn't scroll vertically while editing", - originalScrollPos, getGridVerticalScrollPos()); - } - @Test public void testUneditableColumn() { selectMenuPath(EDIT_ITEM_5); @@ -304,46 +184,11 @@ public class GridEditorTest extends GridBasicFeaturesTest { getGridElement().getEditor().isEditable(3)); } - @Test - public void testEditorUnbufferedShowsNoButtons() { - selectMenuPath(TOGGLE_EDITOR_BUFFERED_ENABLED); - selectMenuPath(EDIT_ITEM_5); - - assertEditorOpen(); - - boolean saveButtonFound = true; - try { - getSaveButton(); - } catch (NoSuchElementException e) { - saveButtonFound = false; - } - assertFalse("Save button should not be visible in unbuffered mode.", - saveButtonFound); - - boolean cancelButtonFound = true; - try { - getCancelButton(); - } catch (NoSuchElementException e) { - cancelButtonFound = false; - } - assertFalse("Cancel button should not be visible in unbuffered mode.", - cancelButtonFound); - } - - @Test - public void testEditorUnbufferedWhileOpen() { - selectMenuPath(EDIT_ITEM_5); - selectMenuPath(TOGGLE_EDITOR_BUFFERED_ENABLED); - assertEditorOpen(); - boolean thrown = logContainsText("Exception occured, java.lang.IllegalStateException"); - assertTrue("IllegalStateException thrown", thrown); - } - - private WebElement getSaveButton() { + protected WebElement getSaveButton() { return getDriver().findElement(By.className("v-grid-editor-save")); } - private WebElement getCancelButton() { + protected WebElement getCancelButton() { return getDriver().findElement(By.className("v-grid-editor-cancel")); } } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java new file mode 100644 index 0000000000..821239f1dc --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java @@ -0,0 +1,88 @@ +/* + * 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.tests.components.grid.basicfeatures.server; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.NoSuchElementException; + +public class GridEditorUnbufferedTest extends GridEditorTest { + + private static final String[] TOGGLE_EDITOR_BUFFERED = new String[] { + "Component", "Editor", "Buffered mode" }; + + @Override + @Before + public void setUp() { + super.setUp(); + selectMenuPath(TOGGLE_EDITOR_BUFFERED); + } + + @Test + public void testEditorShowsNoButtons() { + selectMenuPath(EDIT_ITEM_5); + + assertEditorOpen(); + + boolean saveButtonFound = true; + try { + getSaveButton(); + } catch (NoSuchElementException e) { + saveButtonFound = false; + } + assertFalse("Save button should not be visible in unbuffered mode.", + saveButtonFound); + + boolean cancelButtonFound = true; + try { + getCancelButton(); + } catch (NoSuchElementException e) { + cancelButtonFound = false; + } + assertFalse("Cancel button should not be visible in unbuffered mode.", + cancelButtonFound); + } + + @Test + public void testToggleEditorUnbufferedWhileOpen() { + selectMenuPath(EDIT_ITEM_5); + assertEditorOpen(); + selectMenuPath(TOGGLE_EDITOR_BUFFERED); + boolean thrown = logContainsText("Exception occured, java.lang.IllegalStateException"); + assertTrue("IllegalStateException thrown", thrown); + } + + @Test + public void testEditorMove() { + selectMenuPath(EDIT_ITEM_5); + + assertEditorOpen(); + + String firstFieldValue = getEditorWidgets().get(0) + .getAttribute("value"); + assertTrue("Editor is not at correct row index (5)", + "(5, 0)".equals(firstFieldValue)); + + getGridElement().getCell(10, 0).click(); + firstFieldValue = getEditorWidgets().get(0).getAttribute("value"); + + assertTrue("Editor is not at correct row index (10)", + "(10, 0)".equals(firstFieldValue)); + } +} -- cgit v1.2.3 From 1b67d65d9718745edc69a51c082e917a2bbee2e0 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Tue, 2 Jun 2015 15:08:03 +0300 Subject: Allow programmatically opening editor in unbuffered mode ..when the editor is already open on another row Change-Id: Ibec04dc7b1559149cf33e36fada8d676d943fc72 --- client/src/com/vaadin/client/widgets/Grid.java | 24 +++++++++++----------- server/src/com/vaadin/ui/Grid.java | 9 +++++--- .../server/GridEditorBufferedTest.java | 16 +++++++++++++++ .../server/GridEditorUnbufferedTest.java | 14 +++++++++++++ 4 files changed, 48 insertions(+), 15 deletions(-) (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index db3cea58dc..834280d89d 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -1209,7 +1209,6 @@ public class Grid extends ResizeComposite implements assert rowIndex == request.getRowIndex() : "Request row index " + request.getRowIndex() + " did not match the saved row index " + rowIndex; - showOverlay(); } } @@ -1316,7 +1315,8 @@ public class Grid extends ResizeComposite implements * @throws IllegalStateException * if this editor is not enabled * @throws IllegalStateException - * if this editor is already in edit mode + * if this editor is already in edit mode and in buffered + * mode * * @since 7.5 */ @@ -1326,8 +1326,10 @@ public class Grid extends ResizeComposite implements "Cannot edit row: editor is not enabled"); } if (state != State.INACTIVE) { - throw new IllegalStateException( - "Cannot edit row: editor already in edit mode"); + if (isBuffered()) { + throw new IllegalStateException( + "Cannot edit row: editor already in edit mode"); + } } this.rowIndex = rowIndex; @@ -1474,13 +1476,6 @@ public class Grid extends ResizeComposite implements } } - protected void hide() { - hideOverlay(); - grid.getEscalator().setScrollLocked(Direction.VERTICAL, false); - state = State.INACTIVE; - updateSelectionCheckboxesAsNeeded(true); - } - protected void setGrid(final Grid grid) { assert grid != null : "Grid cannot be null"; assert this.grid == null : "Can only attach editor to Grid once"; @@ -1539,6 +1534,8 @@ public class Grid extends ResizeComposite implements * @since 7.5 */ protected void showOverlay() { + // Ensure overlay is hidden initially + hideOverlay(); DivElement gridElement = DivElement.as(grid.getElement()); @@ -1646,6 +1643,10 @@ public class Grid extends ResizeComposite implements } protected void hideOverlay() { + if (editorOverlay.getParentElement() == null) { + return; + } + for (Widget w : columnToWidget.values()) { setParent(w, null); } @@ -6492,7 +6493,6 @@ public class Grid extends ResizeComposite implements return true; } else if (editorIsActive && moveEvent) { - editor.hide(); cellFocusHandler.setCellFocus(eventCell); editor.editRow(eventCell.getRowIndex(), diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index 74f58ecf74..68676e5435 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -3383,7 +3383,8 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, private final String nullRepresentation; - protected AbstractRenderer(Class presentationType, String nullRepresentation) { + protected AbstractRenderer(Class presentationType, + String nullRepresentation) { this.presentationType = presentationType; this.nullRepresentation = nullRepresentation; } @@ -3428,6 +3429,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, /** * Null representation for the renderer + * * @return a textual representation of {@code null} */ protected String getNullRepresentation() { @@ -5819,7 +5821,8 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * @param itemId * the id of the item to edit * @throws IllegalStateException - * if the editor is not enabled or already editing an item + * if the editor is not enabled or already editing an item in + * buffered mode * @throws IllegalArgumentException * if the {@code itemId} is not in the backing container * @see #setEditorEnabled(boolean) @@ -5828,7 +5831,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, IllegalArgumentException { if (!isEditorEnabled()) { throw new IllegalStateException("Item editor is not enabled"); - } else if (editedItemId != null) { + } else if (isEditorBuffered() && editedItemId != null) { throw new IllegalStateException("Editing item + " + itemId + " failed. Item editor is already editing item " + editedItemId); diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java index 606fe783d4..728c32af5d 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java @@ -214,4 +214,20 @@ public class GridEditorBufferedTest extends GridEditorTest { getGridElement().getCell(4, 0).doubleClick(); assertEditorClosed(); } + + @Test + public void testProgrammaticOpeningWhenOpen() { + selectMenuPath(EDIT_ITEM_5); + assertEditorOpen(); + assertEquals("Editor should edit row 5", "(5, 0)", getEditorWidgets() + .get(0).getAttribute("value")); + + selectMenuPath(EDIT_ITEM_100); + boolean thrown = logContainsText("Exception occured, java.lang.IllegalStateException"); + assertTrue("IllegalStateException thrown", thrown); + + assertEditorOpen(); + assertEquals("Editor should still edit row 5", "(5, 0)", + getEditorWidgets().get(0).getAttribute("value")); + } } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java index 90a4c8b0b1..4725d24903 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java @@ -15,6 +15,7 @@ */ package com.vaadin.tests.components.grid.basicfeatures.server; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -162,4 +163,17 @@ public class GridEditorUnbufferedTest extends GridEditorTest { getGridElement().getCell(4, 0).doubleClick(); assertEditorClosed(); } + + @Test + public void testProgrammaticOpeningWhenOpen() { + selectMenuPath(EDIT_ITEM_5); + assertEditorOpen(); + assertEquals("Editor should edit row 5", "(5, 0)", getEditorWidgets() + .get(0).getAttribute("value")); + + selectMenuPath(EDIT_ITEM_100); + assertEditorOpen(); + assertEquals("Editor should edit row 100", "(100, 0)", + getEditorWidgets().get(0).getAttribute("value")); + } } \ No newline at end of file -- cgit v1.2.3 From e13f2d53bc9deca2797c1699b974059a5f992616 Mon Sep 17 00:00:00 2001 From: Henri Sara Date: Thu, 2 Jul 2015 09:44:27 +0300 Subject: Update @since tags for 7.6 Change-Id: I764ebdbf656aecede222d5a5362ac6abcdbcb949 --- client/src/com/vaadin/client/ui/ConnectorFocusAndBlurHandler.java | 2 +- client/src/com/vaadin/client/widgets/Grid.java | 4 ++-- server/src/com/vaadin/ui/AbstractFocusable.java | 2 +- server/src/com/vaadin/ui/Grid.java | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/ui/ConnectorFocusAndBlurHandler.java b/client/src/com/vaadin/client/ui/ConnectorFocusAndBlurHandler.java index 817d070a9f..8272f99d25 100644 --- a/client/src/com/vaadin/client/ui/ConnectorFocusAndBlurHandler.java +++ b/client/src/com/vaadin/client/ui/ConnectorFocusAndBlurHandler.java @@ -32,7 +32,7 @@ import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc; * to transmit received events to the server. Events are only handled if there * is a corresponding listener on the server side. * - * @since + * @since 7.6 * @author Vaadin Ltd */ public class ConnectorFocusAndBlurHandler implements StateChangeHandler, diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index c46e8d03e8..1665a76c7a 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -8124,7 +8124,7 @@ public class Grid extends ResizeComposite implements /** * Sets the buffered editor mode. * - * @since + * @since 7.6 * @param editorUnbuffered * true to enable buffered editor, * false to disable it @@ -8136,7 +8136,7 @@ public class Grid extends ResizeComposite implements /** * Gets the buffered editor mode. * - * @since + * @since 7.6 * @return true if buffered editor is enabled, * false otherwise */ diff --git a/server/src/com/vaadin/ui/AbstractFocusable.java b/server/src/com/vaadin/ui/AbstractFocusable.java index b9705cef6a..ad3b96f29b 100644 --- a/server/src/com/vaadin/ui/AbstractFocusable.java +++ b/server/src/com/vaadin/ui/AbstractFocusable.java @@ -29,7 +29,7 @@ import com.vaadin.ui.Component.Focusable; * An abstract base class for focusable components. Includes API for setting the * tab index, programmatic focusing, and adding focus and blur listeners. * - * @since + * @since 7.6 * @author Vaadin Ltd */ public abstract class AbstractFocusable extends AbstractComponent implements diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index 64f26b6205..80af88e32b 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -6115,7 +6115,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * Sets the buffered editor mode. The default mode is buffered ( * true). * - * @since + * @since 7.6 * @param editorBuffered * true to enable buffered editor, * false to disable it @@ -6136,7 +6136,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, /** * Gets the buffered editor mode. * - * @since + * @since 7.6 * @return true if buffered editor is enabled, * false otherwise */ -- cgit v1.2.3 From 4fce93896627b862587533e4bd3b71b0e7a3a6b2 Mon Sep 17 00:00:00 2001 From: Johannes Dahlström Date: Wed, 8 Jul 2015 16:52:21 +0300 Subject: Close Grid editor on container item set change Also allow user sorting when in unbuffered mode. Change-Id: Ibe1c1770647529b63c6e3c7fc9509562449b54a4 --- client/src/com/vaadin/client/widgets/Grid.java | 5 +- server/src/com/vaadin/ui/Grid.java | 23 ++++++- .../grid/basicfeatures/GridBasicFeatures.java | 76 ++++++++++++---------- .../server/GridEditorBufferedTest.java | 10 +++ .../grid/basicfeatures/server/GridEditorTest.java | 18 +++++ .../server/GridEditorUnbufferedTest.java | 8 +++ .../basicfeatures/server/GridStructureTest.java | 2 +- 7 files changed, 103 insertions(+), 39 deletions(-) (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 5cbfd5ea75..a0b0b08aaa 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -6627,7 +6627,7 @@ public class Grid extends ResizeComposite implements return true; - } else if (editorIsActive && moveEvent) { + } else if (editorIsActive && !editor.isBuffered() && moveEvent) { cellFocusHandler.setCellFocus(eventCell); editor.editRow(eventCell.getRowIndex(), @@ -6644,7 +6644,8 @@ public class Grid extends ResizeComposite implements return true; } - return editorIsActive; + // Swallow events if editor is open and buffered (modal) + return editor.isBuffered() && editorIsActive; } private boolean handleRendererEvent(Event event, RowContainer container) { diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index 80af88e32b..f7d49e6419 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -45,6 +45,9 @@ import com.google.gwt.thirdparty.guava.common.collect.Sets; import com.google.gwt.thirdparty.guava.common.collect.Sets.SetView; import com.vaadin.data.Container; import com.vaadin.data.Container.Indexed; +import com.vaadin.data.Container.ItemSetChangeEvent; +import com.vaadin.data.Container.ItemSetChangeListener; +import com.vaadin.data.Container.ItemSetChangeNotifier; import com.vaadin.data.Container.PropertySetChangeEvent; import com.vaadin.data.Container.PropertySetChangeListener; import com.vaadin.data.Container.PropertySetChangeNotifier; @@ -3608,6 +3611,13 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, } }; + private final ItemSetChangeListener editorClosingItemSetListener = new ItemSetChangeListener() { + @Override + public void containerItemSetChange(ItemSetChangeEvent event) { + cancelEditor(); + } + }; + private RpcDataProviderExtension datasourceExtension; /** @@ -4111,10 +4121,10 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, removeExtension(datasourceExtension); } - datasource = container; - resetEditor(); + datasource = container; + // // Adjust sort order // @@ -4161,6 +4171,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, ((PropertySetChangeNotifier) datasource) .addPropertySetChangeListener(propertyListener); } + /* * activeRowHandler will be updated by the client-side request that * occurs on container change - no need to actively re-insert any @@ -5886,6 +5897,10 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, f.markAsDirtyRecursive(); } + if (datasource instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) datasource) + .addItemSetChangeListener(editorClosingItemSetListener); + } } private void setEditorField(Object propertyId, Field field) { @@ -5934,6 +5949,10 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, editorActive = false; editorFieldGroup.discard(); editorFieldGroup.setItemDataSource(null); + if (datasource instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) datasource) + .removeItemSetChangeListener(editorClosingItemSetListener); + } } void resetEditor() { diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java index 35e39c9986..272ff1c9ae 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -387,46 +387,54 @@ public class GridBasicFeatures extends AbstractComponentTest { } private void addFilterActions() { - createClickAction("Column 1 starts with \"(23\"", "Filter", - new Command() { - @Override - public void execute(Grid grid, Void value, Object data) { - ds.addContainerFilter(new Filter() { + createBooleanAction("Column 1 starts with \"(23\"", "Filter", false, + new Command() { + Filter filter = new Filter() { + @Override + public boolean passesFilter(Object itemId, Item item) { + return item.getItemProperty("Column 1").getValue() + .toString().startsWith("(23"); + } - @Override - public boolean passesFilter(Object itemId, Item item) - throws UnsupportedOperationException { - return item.getItemProperty("Column 1") - .getValue().toString() - .startsWith("(23"); - } + @Override + public boolean appliesToProperty(Object propertyId) { + return propertyId.equals("Column 1"); + } + }; - @Override - public boolean appliesToProperty(Object propertyId) { - return propertyId.equals("Column 1"); - } - }); + @Override + public void execute(Grid grid, Boolean value, Object data) { + if (value) { + ds.addContainerFilter(filter); + } else { + ds.removeContainerFilter(filter); + } } - }, null); + }); - createClickAction("Add impassable filter", "Filter", - new Command() { - @Override - public void execute(Grid c, Void value, Object data) { - ds.addContainerFilter(new Filter() { - @Override - public boolean passesFilter(Object itemId, Item item) - throws UnsupportedOperationException { - return false; - } + createBooleanAction("Impassable filter", "Filter", false, + new Command() { + Filter filter = new Filter() { + @Override + public boolean passesFilter(Object itemId, Item item) { + return false; + } - @Override - public boolean appliesToProperty(Object propertyId) { - return true; - } - }); + @Override + public boolean appliesToProperty(Object propertyId) { + return true; + } + }; + + @Override + public void execute(Grid c, Boolean value, Object data) { + if (value) { + ds.addContainerFilter(filter); + } else { + ds.removeContainerFilter(filter); + } } - }, null); + }); } protected void createGridActions() { diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java index 728c32af5d..0f2fe54696 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java @@ -230,4 +230,14 @@ public class GridEditorBufferedTest extends GridEditorTest { assertEquals("Editor should still edit row 5", "(5, 0)", getEditorWidgets().get(0).getAttribute("value")); } + + @Test + public void testUserSortDisabled() { + selectMenuPath(EDIT_ITEM_5); + + getGridElement().getHeaderCell(0, 0).click(); + + assertEditorOpen(); + assertEquals("(2, 0)", getGridElement().getCell(2, 0).getText()); + } } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java index dbb903ff98..3582038e61 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java @@ -269,6 +269,24 @@ public abstract class GridEditorTest extends GridBasicFeaturesTest { editorPos == editor.getLocation().getY()); } + @Test + public void testEditorClosedOnSort() { + selectMenuPath(EDIT_ITEM_5); + + selectMenuPath("Component", "State", "Sort by column", "Column 0, ASC"); + + assertEditorClosed(); + } + + @Test + public void testEditorClosedOnFilter() { + selectMenuPath(EDIT_ITEM_5); + + selectMenuPath("Component", "Filter", "Column 1 starts with \"(23\""); + + assertEditorClosed(); + } + protected WebElement getSaveButton() { return getDriver().findElement(BY_EDITOR_SAVE); } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java index 87c6cc805d..1058fe2d74 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java @@ -194,4 +194,12 @@ public class GridEditorUnbufferedTest extends GridEditorTest { "Modified", getEditorWidgets().get(2).getAttribute("value")); } + @Test + public void testEditorClosedOnUserSort() { + selectMenuPath(EDIT_ITEM_5); + + getGridElement().getHeaderCell(0, 0).click(); + + assertEditorClosed(); + } } \ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridStructureTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridStructureTest.java index 5d72d481c7..f44f39689c 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridStructureTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridStructureTest.java @@ -274,7 +274,7 @@ public class GridStructureTest extends GridBasicFeaturesTest { @Test public void testBareItemSetChangeRemovingAllRows() throws Exception { openTestURL(); - selectMenuPath("Component", "Filter", "Add impassable filter"); + selectMenuPath("Component", "Filter", "Impassable filter"); assertFalse("A notification shouldn't have been displayed", $(NotificationElement.class).exists()); assertTrue("No body cells should've been found", getGridElement() -- cgit v1.2.3 From 40dcbc3cfaa438c9b879720c9012331dd85ca694 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Thu, 2 Jul 2015 17:34:27 +0300 Subject: Refactor RpcDataProviderExtension to use DataGenerators Change-Id: I8c809b6fac827df730c6622fb6790410c6c5bd81 --- .../vaadin/client/connectors/GridConnector.java | 33 +--- server/src/com/vaadin/data/DataGenerator.java | 49 +++++ .../com/vaadin/data/RpcDataProviderExtension.java | 220 +++++++-------------- server/src/com/vaadin/ui/Grid.java | 147 +++++++++++++- .../vaadin/tests/server/renderer/RendererTest.java | 26 +-- .../src/com/vaadin/shared/ui/grid/GridState.java | 5 - 6 files changed, 281 insertions(+), 199 deletions(-) create mode 100644 server/src/com/vaadin/data/DataGenerator.java (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index 052d8ee368..bee9cedc43 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -38,7 +38,6 @@ import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.DeferredWorker; import com.vaadin.client.MouseEventDetailsBuilder; -import com.vaadin.client.annotations.OnStateChange; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.connectors.RpcDataSourceConnector.DetailsListener; import com.vaadin.client.connectors.RpcDataSourceConnector.RpcDataSource; @@ -119,8 +118,8 @@ import elemental.json.JsonValue; public class GridConnector extends AbstractHasComponentsConnector implements SimpleManagedLayout, DeferredWorker { - private static final class CustomCellStyleGenerator implements - CellStyleGenerator { + private static final class CustomStyleGenerator implements + CellStyleGenerator, RowStyleGenerator { @Override public String getStyle(CellReference cellReference) { JsonObject row = cellReference.getRow(); @@ -146,10 +145,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements } } - } - - private static final class CustomRowStyleGenerator implements - RowStyleGenerator { @Override public String getStyle(RowReference rowReference) { JsonObject row = rowReference.getRow(); @@ -159,7 +154,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements return null; } } - } /** @@ -734,6 +728,7 @@ public class GridConnector extends AbstractHasComponentsConnector implements private String lastKnownTheme = null; private final CustomDetailsGenerator customDetailsGenerator = new CustomDetailsGenerator(); + private final CustomStyleGenerator styleGenerator = new CustomStyleGenerator(); private final DetailsConnectorFetcher detailsConnectorFetcher = new DetailsConnectorFetcher( getRpcProxy(GridServerRpc.class)); @@ -873,6 +868,10 @@ public class GridConnector extends AbstractHasComponentsConnector implements getWidget().addBodyClickHandler(itemClickHandler); getWidget().addBodyDoubleClickHandler(itemClickHandler); + /* Style Generators */ + getWidget().setCellStyleGenerator(styleGenerator); + getWidget().setRowStyleGenerator(styleGenerator); + getWidget().addSortHandler(new SortHandler() { @Override public void sort(SortEvent event) { @@ -1289,24 +1288,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements } } - @OnStateChange("hasCellStyleGenerator") - private void onCellStyleGeneratorChange() { - if (getState().hasCellStyleGenerator) { - getWidget().setCellStyleGenerator(new CustomCellStyleGenerator()); - } else { - getWidget().setCellStyleGenerator(null); - } - } - - @OnStateChange("hasRowStyleGenerator") - private void onRowStyleGeneratorChange() { - if (getState().hasRowStyleGenerator) { - getWidget().setRowStyleGenerator(new CustomRowStyleGenerator()); - } else { - getWidget().setRowStyleGenerator(null); - } - } - private void updateSelectionFromState() { boolean changed = false; diff --git a/server/src/com/vaadin/data/DataGenerator.java b/server/src/com/vaadin/data/DataGenerator.java new file mode 100644 index 0000000000..f025623a3e --- /dev/null +++ b/server/src/com/vaadin/data/DataGenerator.java @@ -0,0 +1,49 @@ +/* + * 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.data; + +import java.io.Serializable; + +import com.vaadin.ui.Grid.AbstractGridExtension; +import com.vaadin.ui.Grid.AbstractRenderer; + +import elemental.json.JsonObject; + +/** + * Interface for {@link AbstractGridExtension}s that allows adding data to row + * objects being sent to client by the {@link RpcDataProviderExtension}. + *

+ * {@link AbstractRenderer} implements this interface to provide encoded data to + * client for {@link Renderer}s automatically. + * + * @since + * @author Vaadin Ltd + */ +public interface DataGenerator extends Serializable { + + /** + * Adds data to row object for given item and item id being sent to client. + * + * @param itemId + * item id of item + * @param item + * item being sent to client + * @param rowData + * row object being sent to client + */ + public void generateData(Object itemId, Item item, JsonObject rowData); + +} diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index 9d18736ba8..71b597ff1d 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -23,11 +23,9 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.logging.Logger; import com.google.gwt.thirdparty.guava.common.collect.BiMap; import com.google.gwt.thirdparty.guava.common.collect.HashBiMap; @@ -43,10 +41,8 @@ import com.vaadin.data.Container.ItemSetChangeNotifier; import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.data.Property.ValueChangeNotifier; -import com.vaadin.data.util.converter.Converter; import com.vaadin.server.AbstractExtension; import com.vaadin.server.ClientConnector; -import com.vaadin.server.KeyMapper; import com.vaadin.shared.data.DataProviderRpc; import com.vaadin.shared.data.DataRequestRpc; import com.vaadin.shared.ui.grid.DetailsConnectorChange; @@ -56,13 +52,9 @@ import com.vaadin.shared.ui.grid.Range; import com.vaadin.shared.util.SharedUtil; import com.vaadin.ui.Component; import com.vaadin.ui.Grid; -import com.vaadin.ui.Grid.CellReference; -import com.vaadin.ui.Grid.CellStyleGenerator; import com.vaadin.ui.Grid.Column; import com.vaadin.ui.Grid.DetailsGenerator; import com.vaadin.ui.Grid.RowReference; -import com.vaadin.ui.Grid.RowStyleGenerator; -import com.vaadin.ui.renderers.Renderer; import elemental.json.Json; import elemental.json.JsonArray; @@ -93,7 +85,7 @@ public class RpcDataProviderExtension extends AbstractExtension { * itemId ⇆ key mapping is not needed anymore. In other words, this * doesn't leak memory. */ - public class DataProviderKeyMapper implements Serializable { + public class DataProviderKeyMapper implements Serializable, DataGenerator { private final BiMap itemIdToKey = HashBiMap.create(); private Set pinnedItemIds = new HashSet(); private long rollingIndex = 0; @@ -125,7 +117,7 @@ public class RpcDataProviderExtension extends AbstractExtension { for (Object itemId : itemSet) { itemIdToKey.put(itemId, getKey(itemId)); - if (visibleDetails.contains(itemId)) { + if (detailComponentManager.visibleDetails.contains(itemId)) { detailComponentManager.createDetails(itemId, indexOf(itemId)); } @@ -280,6 +272,16 @@ public class RpcDataProviderExtension extends AbstractExtension { public boolean isPinned(Object itemId) { return pinnedItemIds.contains(itemId); } + + /** + * {@inheritDoc} + * + * @since + */ + @Override + public void generateData(Object itemId, Item item, JsonObject rowData) { + rowData.put(GridState.JSONKEY_ROWKEY, getKey(itemId)); + } } /** @@ -601,7 +603,7 @@ public class RpcDataProviderExtension extends AbstractExtension { * @since 7.5.0 * @author Vaadin Ltd */ - public static final class DetailComponentManager implements Serializable { + public static final class DetailComponentManager implements DataGenerator { /** * This map represents all the components that have been requested for * each item id. @@ -649,6 +651,12 @@ public class RpcDataProviderExtension extends AbstractExtension { */ private final Map emptyDetails = Maps.newHashMap(); + /** + * This map represents all the details that are user-defined as visible. + * This does not reflect the status in the DOM. + */ + private Set visibleDetails = new HashSet(); + private Grid grid; /** @@ -856,6 +864,18 @@ public class RpcDataProviderExtension extends AbstractExtension { } this.grid = grid; } + + /** + * {@inheritDoc} + * + * @since + */ + @Override + public void generateData(Object itemId, Item item, JsonObject rowData) { + if (visibleDetails.contains(itemId)) { + rowData.put(GridState.JSONKEY_DETAILS_VISIBLE, true); + } + } } private final Indexed container; @@ -939,14 +959,9 @@ public class RpcDataProviderExtension extends AbstractExtension { private final DataProviderKeyMapper keyMapper = new DataProviderKeyMapper(); - private KeyMapper columnKeys; - /** RpcDataProvider should send the current cache again. */ private boolean refreshCache = false; - private RowReference rowReference; - private CellReference cellReference; - /** Set of updated item ids */ private Set updatedItemIds = new LinkedHashSet(); @@ -959,14 +974,10 @@ public class RpcDataProviderExtension extends AbstractExtension { /** Size possibly changed with a bare ItemSetChangeEvent */ private boolean bareItemSetTriggeredSizeChange = false; - /** - * This map represents all the details that are user-defined as visible. - * This does not reflect the status in the DOM. - */ - private Set visibleDetails = new HashSet(); - private final DetailComponentManager detailComponentManager = new DetailComponentManager(); + private Set dataGenerators = new LinkedHashSet(); + /** * Creates a new data provider using the given container. * @@ -1006,6 +1017,8 @@ public class RpcDataProviderExtension extends AbstractExtension { .addItemSetChangeListener(itemListener); } + addDataGenerator(keyMapper); + addDataGenerator(detailComponentManager); } /** @@ -1090,73 +1103,14 @@ public class RpcDataProviderExtension extends AbstractExtension { private JsonValue getRowData(Collection columns, Object itemId) { Item item = container.getItem(itemId); - JsonObject rowData = Json.createObject(); - - Grid grid = getGrid(); - - for (Column column : columns) { - Object propertyId = column.getPropertyId(); - - Object propertyValue = item.getItemProperty(propertyId).getValue(); - JsonValue encodedValue = encodeValue(propertyValue, - column.getRenderer(), column.getConverter(), - grid.getLocale()); - - rowData.put(columnKeys.key(propertyId), encodedValue); - } - final JsonObject rowObject = Json.createObject(); - rowObject.put(GridState.JSONKEY_DATA, rowData); - rowObject.put(GridState.JSONKEY_ROWKEY, keyMapper.getKey(itemId)); - - if (visibleDetails.contains(itemId)) { - rowObject.put(GridState.JSONKEY_DETAILS_VISIBLE, true); - } - - rowReference.set(itemId); - - CellStyleGenerator cellStyleGenerator = grid.getCellStyleGenerator(); - if (cellStyleGenerator != null) { - setGeneratedCellStyles(cellStyleGenerator, rowObject, columns); - } - RowStyleGenerator rowStyleGenerator = grid.getRowStyleGenerator(); - if (rowStyleGenerator != null) { - setGeneratedRowStyles(rowStyleGenerator, rowObject); + for (DataGenerator dg : dataGenerators) { + dg.generateData(itemId, item, rowObject); } return rowObject; } - private void setGeneratedCellStyles(CellStyleGenerator generator, - JsonObject rowObject, Collection columns) { - JsonObject cellStyles = null; - for (Column column : columns) { - Object propertyId = column.getPropertyId(); - cellReference.set(propertyId); - String style = generator.getStyle(cellReference); - if (style != null && !style.isEmpty()) { - if (cellStyles == null) { - cellStyles = Json.createObject(); - } - - String columnKey = columnKeys.key(propertyId); - cellStyles.put(columnKey, style); - } - } - if (cellStyles != null) { - rowObject.put(GridState.JSONKEY_CELLSTYLES, cellStyles); - } - - } - - private void setGeneratedRowStyles(RowStyleGenerator generator, - JsonObject rowObject) { - String rowStyle = generator.getStyle(rowReference); - if (rowStyle != null && !rowStyle.isEmpty()) { - rowObject.put(GridState.JSONKEY_ROWSTYLE, rowStyle); - } - } - /** * Makes the data source available to the given {@link Grid} component. * @@ -1165,12 +1119,37 @@ public class RpcDataProviderExtension extends AbstractExtension { * @param columnKeys * the key mapper for columns */ - public void extend(Grid component, KeyMapper columnKeys) { - this.columnKeys = columnKeys; + public void extend(Grid component) { detailComponentManager.setGrid(component); super.extend(component); } + /** + * Adds a {@link DataGenerator} for this {@code RpcDataProviderExtension}. + * DataGenerators are called when sending row data to client. If given + * DataGenerator is already added, this method does nothing. + * + * @since + * @param generator + * generator to add + */ + public void addDataGenerator(DataGenerator generator) { + dataGenerators.add(generator); + } + + /** + * Removes a {@link DataGenerator} from this + * {@code RpcDataProviderExtension}. If given DataGenerator is not added to + * this data provider, this method does nothing. + * + * @since + * @param generator + * generator to remove + */ + public void removeDataGenerator(DataGenerator generator) { + dataGenerators.remove(generator); + } + /** * Informs the client side that new rows have been inserted into the data * source. @@ -1281,11 +1260,7 @@ public class RpcDataProviderExtension extends AbstractExtension { .removeItemSetChangeListener(itemListener); } - } else if (parent instanceof Grid) { - Grid grid = (Grid) parent; - rowReference = new RowReference(grid); - cellReference = new CellReference(rowReference); - } else { + } else if (!(parent instanceof Grid)) { throw new IllegalStateException( "Grid is the only accepted parent type"); } @@ -1321,62 +1296,6 @@ public class RpcDataProviderExtension extends AbstractExtension { return (Grid) getParent(); } - /** - * Converts and encodes the given data model property value using the given - * converter and renderer. This method is public only for testing purposes. - * - * @param renderer - * the renderer to use - * @param converter - * the converter to use - * @param modelValue - * the value to convert and encode - * @param locale - * the locale to use in conversion - * @return an encoded value ready to be sent to the client - */ - public static JsonValue encodeValue(Object modelValue, - Renderer renderer, Converter converter, Locale locale) { - Class presentationType = renderer.getPresentationType(); - T presentationValue; - - if (converter == null) { - try { - presentationValue = presentationType.cast(modelValue); - } catch (ClassCastException e) { - if (presentationType == String.class) { - // If there is no converter, just fallback to using - // toString(). - // modelValue can't be null as Class.cast(null) will always - // succeed - presentationValue = (T) modelValue.toString(); - } else { - throw new Converter.ConversionException( - "Unable to convert value of type " - + modelValue.getClass().getName() - + " to presentation type " - + presentationType.getName() - + ". No converter is set and the types are not compatible."); - } - } - } else { - assert presentationType.isAssignableFrom(converter - .getPresentationType()); - @SuppressWarnings("unchecked") - Converter safeConverter = (Converter) converter; - presentationValue = safeConverter.convertToPresentation(modelValue, - safeConverter.getPresentationType(), locale); - } - - JsonValue encodedValue = renderer.encode(presentationValue); - - return encodedValue; - } - - private static Logger getLogger() { - return Logger.getLogger(RpcDataProviderExtension.class.getName()); - } - /** * Marks a row's details to be visible or hidden. *

@@ -1394,7 +1313,7 @@ public class RpcDataProviderExtension extends AbstractExtension { final boolean modified; if (visible) { - modified = visibleDetails.add(itemId); + modified = detailComponentManager.visibleDetails.add(itemId); /* * We don't want to create the component here, since the component @@ -1405,7 +1324,7 @@ public class RpcDataProviderExtension extends AbstractExtension { */ } else { - modified = visibleDetails.remove(itemId); + modified = detailComponentManager.visibleDetails.remove(itemId); /* * Here we can try to destroy the component no matter what. The @@ -1435,7 +1354,7 @@ public class RpcDataProviderExtension extends AbstractExtension { * visible in the DOM */ public boolean isDetailsVisible(Object itemId) { - return visibleDetails.contains(itemId); + return detailComponentManager.visibleDetails.contains(itemId); } /** @@ -1444,7 +1363,8 @@ public class RpcDataProviderExtension extends AbstractExtension { * @since 7.5.0 */ public void refreshDetails() { - for (Object itemId : ImmutableSet.copyOf(visibleDetails)) { + for (Object itemId : ImmutableSet + .copyOf(detailComponentManager.visibleDetails)) { detailComponentManager.refresh(itemId); } } diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index f7d49e6419..af89064411 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -31,6 +31,7 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -52,6 +53,7 @@ import com.vaadin.data.Container.PropertySetChangeEvent; import com.vaadin.data.Container.PropertySetChangeListener; import com.vaadin.data.Container.PropertySetChangeNotifier; import com.vaadin.data.Container.Sortable; +import com.vaadin.data.DataGenerator; import com.vaadin.data.Item; import com.vaadin.data.Property; import com.vaadin.data.RpcDataProviderExtension; @@ -1547,6 +1549,60 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, public String getStyle(CellReference cellReference); } + /** + * Class for generating all row and cell related data for the essential + * parts of Grid. + */ + private class RowDataGenerator implements DataGenerator { + + @Override + public void generateData(Object itemId, Item item, JsonObject rowData) { + RowReference r = new RowReference(Grid.this); + r.set(itemId); + + if (rowStyleGenerator != null) { + String style = rowStyleGenerator.getStyle(r); + if (style != null && !style.isEmpty()) { + rowData.put(GridState.JSONKEY_ROWSTYLE, style); + } + } + + JsonObject cellStyles = Json.createObject(); + JsonObject cellData = Json.createObject(); + for (Column column : getColumns()) { + Object propertyId = column.getPropertyId(); + String columnId = columnKeys.key(propertyId); + + cellData.put(columnId, getRendererData(column, item)); + + if (cellStyleGenerator != null) { + CellReference c = new CellReference(r); + c.set(propertyId); + + String style = cellStyleGenerator.getStyle(c); + if (style != null && !style.isEmpty()) { + cellStyles.put(columnId, style); + } + } + } + + if (cellStyleGenerator != null && cellStyles.keys().length > 0) { + rowData.put(GridState.JSONKEY_CELLSTYLES, cellStyles); + } + rowData.put(GridState.JSONKEY_DATA, cellData); + } + + private JsonValue getRendererData(Column column, Item item) { + Converter converter = column.getConverter(); + Object propertyId = column.getPropertyId(); + Object modelValue = item.getItemProperty(propertyId).getValue(); + Renderer renderer = column.getRenderer(); + + return AbstractRenderer.encodeValue(modelValue, renderer, + converter, getLocale()); + } + } + /** * Abstract base class for Grid header and footer sections. * @@ -3462,10 +3518,67 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, return JsonCodec.encode(value, null, type, getUI().getConnectorTracker()).getEncodedValue(); } + + /** + * Converts and encodes the given data model property value using the + * given converter and renderer. This method is public only for testing + * purposes. + * + * @param renderer + * the renderer to use + * @param converter + * the converter to use + * @param modelValue + * the value to convert and encode + * @param locale + * the locale to use in conversion + * @return an encoded value ready to be sent to the client + */ + public static JsonValue encodeValue(Object modelValue, + Renderer renderer, Converter converter, Locale locale) { + Class presentationType = renderer.getPresentationType(); + T presentationValue; + + if (converter == null) { + try { + presentationValue = presentationType.cast(modelValue); + } catch (ClassCastException e) { + if (presentationType == String.class) { + // If there is no converter, just fallback to using + // toString(). modelValue can't be null as + // Class.cast(null) will always succeed + presentationValue = (T) modelValue.toString(); + } else { + throw new Converter.ConversionException( + "Unable to convert value of type " + + modelValue.getClass().getName() + + " to presentation type " + + presentationType.getName() + + ". No converter is set and the types are not compatible."); + } + } + } else { + assert presentationType.isAssignableFrom(converter + .getPresentationType()); + @SuppressWarnings("unchecked") + Converter safeConverter = (Converter) converter; + presentationValue = safeConverter + .convertToPresentation(modelValue, + safeConverter.getPresentationType(), locale); + } + + JsonValue encodedValue = renderer.encode(presentationValue); + + return encodedValue; + } } /** * An abstract base class for server-side Grid extensions. + *

+ * Note: If the extension is an instance of {@link DataGenerator} it will + * automatically register itself to {@link RpcDataProviderExtension} of + * extended Grid. On remove this registration is automatically removed. * * @since 7.5 */ @@ -3490,6 +3603,26 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, extend(grid); } + @Override + protected void extend(AbstractClientConnector target) { + super.extend(target); + + if (this instanceof DataGenerator) { + getParentGrid().datasourceExtension + .addDataGenerator((DataGenerator) this); + } + } + + @Override + public void remove() { + if (this instanceof DataGenerator) { + getParentGrid().datasourceExtension + .removeDataGenerator((DataGenerator) this); + } + + super.remove(); + } + /** * Gets the item id for a row key. *

@@ -3531,9 +3664,14 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, if (getParent() instanceof Grid) { Grid grid = (Grid) getParent(); return grid; + } else if (getParent() == null) { + throw new IllegalStateException( + "Renderer is not attached to any parent"); } else { throw new IllegalStateException( - "Renderers can be used only with Grid"); + "Renderers can be used only with Grid. Extended " + + getParent().getClass().getSimpleName() + + " instead"); } } } @@ -4153,7 +4291,8 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, } datasourceExtension = new RpcDataProviderExtension(container); - datasourceExtension.extend(this, columnKeys); + datasourceExtension.extend(this); + datasourceExtension.addDataGenerator(new RowDataGenerator()); detailComponentManager = datasourceExtension .getDetailComponentManager(); @@ -5620,8 +5759,6 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, */ public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) { this.cellStyleGenerator = cellStyleGenerator; - getState().hasCellStyleGenerator = (cellStyleGenerator != null); - datasourceExtension.refreshCache(); } @@ -5644,8 +5781,6 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, */ public void setRowStyleGenerator(RowStyleGenerator rowStyleGenerator) { this.rowStyleGenerator = rowStyleGenerator; - getState().hasRowStyleGenerator = (rowStyleGenerator != null); - datasourceExtension.refreshCache(); } diff --git a/server/tests/src/com/vaadin/tests/server/renderer/RendererTest.java b/server/tests/src/com/vaadin/tests/server/renderer/RendererTest.java index eb07fae07f..cea8df0ba6 100644 --- a/server/tests/src/com/vaadin/tests/server/renderer/RendererTest.java +++ b/server/tests/src/com/vaadin/tests/server/renderer/RendererTest.java @@ -15,8 +15,17 @@ */ package com.vaadin.tests.server.renderer; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import java.util.Date; +import java.util.Locale; + +import org.junit.Before; +import org.junit.Test; + import com.vaadin.data.Item; -import com.vaadin.data.RpcDataProviderExtension; import com.vaadin.data.util.IndexedContainer; import com.vaadin.data.util.converter.Converter; import com.vaadin.data.util.converter.StringToIntegerConverter; @@ -24,22 +33,15 @@ import com.vaadin.server.VaadinSession; import com.vaadin.tests.server.component.grid.TestGrid; import com.vaadin.tests.util.AlwaysLockedVaadinSession; import com.vaadin.ui.Grid; +import com.vaadin.ui.Grid.AbstractRenderer; import com.vaadin.ui.Grid.Column; import com.vaadin.ui.renderers.ButtonRenderer; import com.vaadin.ui.renderers.DateRenderer; import com.vaadin.ui.renderers.HtmlRenderer; import com.vaadin.ui.renderers.NumberRenderer; import com.vaadin.ui.renderers.TextRenderer; -import elemental.json.JsonValue; -import org.junit.Before; -import org.junit.Test; - -import java.util.Date; -import java.util.Locale; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; +import elemental.json.JsonValue; public class RendererTest { @@ -259,7 +261,7 @@ public class RendererTest { } private JsonValue render(Column column, Object value) { - return RpcDataProviderExtension.encodeValue(value, - column.getRenderer(), column.getConverter(), grid.getLocale()); + return AbstractRenderer.encodeValue(value, column.getRenderer(), + column.getConverter(), grid.getLocale()); } } diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index c4121cbf45..c455ffe23b 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -160,11 +160,6 @@ public class GridState extends TabIndexState { @DelegateToWidget public boolean editorBuffered = true; - /** Whether row data might contain generated row styles */ - public boolean hasRowStyleGenerator; - /** Whether row data might contain generated cell styles */ - public boolean hasCellStyleGenerator; - /** The caption for the save button in the editor */ @DelegateToWidget public String editorSaveCaption = GridConstants.DEFAULT_SAVE_CAPTION; -- cgit v1.2.3 From e288b0d159e4116b863836c4486a7bf289da16eb Mon Sep 17 00:00:00 2001 From: Johannes Dahlström Date: Fri, 10 Jul 2015 15:19:36 +0300 Subject: Prevent Grid editor move in unbuffered mode if validation errors in fields Change-Id: I37f3c21f4464c8f83308a741ed51485f7bd0375a --- client/src/com/vaadin/client/widgets/Grid.java | 68 +++++++++++++--------- server/src/com/vaadin/ui/Grid.java | 39 ++++++++----- .../grid/basicfeatures/GridBasicFeatures.java | 1 + .../server/GridEditorBufferedTest.java | 31 ++++++++-- .../grid/basicfeatures/server/GridEditorTest.java | 3 +- .../server/GridEditorUnbufferedTest.java | 32 +++++++--- 6 files changed, 117 insertions(+), 57 deletions(-) (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 86d2ed5f00..0555df3c1f 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -1159,8 +1159,17 @@ public class Grid extends ResizeComposite implements private int columnIndex = -1; private String styleName = null; + /* + * Used to track Grid horizontal scrolling + */ private HandlerRegistration scrollHandler; + /* + * Used to open editor once Grid has vertically scrolled to the proper + * position and data is available + */ + private HandlerRegistration dataAvailableHandler; + private final Button saveButton; private final Button cancelButton; @@ -1223,9 +1232,7 @@ public class Grid extends ResizeComposite implements state = State.ACTIVE; bindTimeout.cancel(); - assert rowIndex == request.getRowIndex() : "Request row index " - + request.getRowIndex() - + " did not match the saved row index " + rowIndex; + rowIndex = request.getRowIndex(); showOverlay(); } } @@ -1233,16 +1240,17 @@ public class Grid extends ResizeComposite implements @Override public void onError(EditorRequest request) { if (state == State.BINDING) { - state = State.INACTIVE; + if (rowIndex == -1) { + doCancel(); + } else { + state = State.ACTIVE; + } bindTimeout.cancel(); // TODO show something in the DOM as well? getLogger().warning( "An error occurred while trying to show the " + "Grid editor"); - grid.getEscalator().setScrollLocked(Direction.VERTICAL, - false); - updateSelectionCheckboxesAsNeeded(true); } } }; @@ -1337,7 +1345,7 @@ public class Grid extends ResizeComposite implements * * @since 7.5 */ - public void editRow(int rowIndex, int columnIndex) { + public void editRow(final int rowIndex, int columnIndex) { if (!enabled) { throw new IllegalStateException( "Cannot edit row: editor is not enabled"); @@ -1349,14 +1357,23 @@ public class Grid extends ResizeComposite implements } } - this.rowIndex = rowIndex; this.columnIndex = columnIndex; - state = State.ACTIVATING; if (grid.getEscalator().getVisibleRowRange().contains(rowIndex)) { - show(); + show(rowIndex); } else { + hideOverlay(); + dataAvailableHandler = grid + .addDataAvailableHandler(new DataAvailableHandler() { + @Override + public void onDataAvailable(DataAvailableEvent event) { + if (event.getAvailableRows().contains(rowIndex)) { + show(rowIndex); + dataAvailableHandler.removeHandler(); + } + } + }); grid.scrollToRow(rowIndex, ScrollDestination.MIDDLE); } } @@ -1379,13 +1396,16 @@ public class Grid extends ResizeComposite implements throw new IllegalStateException( "Cannot cancel edit: editor is not in edit mode"); } - hideOverlay(); - grid.getEscalator().setScrollLocked(Direction.VERTICAL, false); + handler.cancel(new EditorRequestImpl(grid, rowIndex, null)); + doCancel(); + } - EditorRequest request = new EditorRequestImpl(grid, rowIndex, - null); - handler.cancel(request); + private void doCancel() { + hideOverlay(); state = State.INACTIVE; + rowIndex = -1; + columnIndex = -1; + grid.getEscalator().setScrollLocked(Direction.VERTICAL, false); updateSelectionCheckboxesAsNeeded(true); grid.fireEvent(new EditorCloseEvent(grid.eventCell)); } @@ -1480,7 +1500,7 @@ public class Grid extends ResizeComposite implements this.enabled = enabled; } - protected void show() { + protected void show(int rowIndex) { if (state == State.ACTIVATING) { state = State.BINDING; bindTimeout.schedule(BIND_TIMEOUT_MS); @@ -1498,15 +1518,6 @@ public class Grid extends ResizeComposite implements assert this.grid == null : "Can only attach editor to Grid once"; this.grid = grid; - - grid.addDataAvailableHandler(new DataAvailableHandler() { - @Override - public void onDataAvailable(DataAvailableEvent event) { - if (event.getAvailableRows().contains(rowIndex)) { - show(); - } - } - }); } protected State getState() { @@ -6616,6 +6627,7 @@ public class Grid extends ResizeComposite implements && key == Editor.KEYCODE_HIDE; if (!editorIsActive && editor.isEnabled() && openEvent) { + editor.editRow(eventCell.getRowIndex(), eventCell.getColumnIndexDOM()); fireEvent(new EditorOpenEvent(eventCell)); @@ -6624,16 +6636,16 @@ public class Grid extends ResizeComposite implements return true; } else if (editorIsActive && !editor.isBuffered() && moveEvent) { - cellFocusHandler.setCellFocus(eventCell); + cellFocusHandler.setCellFocus(eventCell); editor.editRow(eventCell.getRowIndex(), eventCell.getColumnIndexDOM()); - fireEvent(new EditorMoveEvent(eventCell)); return true; } else if (editorIsActive && closeEvent) { + editor.cancel(); FocusUtil.setFocus(this, true); diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index 251ec0f678..f025b6e1c0 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -4129,28 +4129,37 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, @Override public void bind(int rowIndex) { - Exception exception = null; try { Object id = getContainerDataSource().getIdByIndex(rowIndex); - if (!isEditorBuffered() || editedItemId == null) { - editedItemId = id; - } - if (editedItemId.equals(id)) { - doEditItem(); + final boolean opening = editedItemId == null; + + final boolean moving = !opening && !editedItemId.equals(id); + + final boolean allowMove = !isEditorBuffered() + && getEditorFieldGroup().isValid(); + + if (opening || !moving || allowMove) { + doBind(id); + } else { + failBind(null); } } catch (Exception e) { - exception = e; + failBind(e); } + } - if (exception != null) { - handleError(exception); - doCancelEditor(); - getEditorRpc().confirmBind(false); - } else { - doEditItem(); - getEditorRpc().confirmBind(true); + private void doBind(Object id) { + editedItemId = id; + doEditItem(); + getEditorRpc().confirmBind(true); + } + + private void failBind(Exception e) { + if (e != null) { + handleError(e); } + getEditorRpc().confirmBind(false); } @Override @@ -6004,7 +6013,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, if (!isEditorEnabled()) { throw new IllegalStateException("Item editor is not enabled"); } else if (isEditorBuffered() && editedItemId != null) { - throw new IllegalStateException("Editing item + " + itemId + throw new IllegalStateException("Editing item " + itemId + " failed. Item editor is already editing item " + editedItemId); } else if (!getContainerDataSource().containsId(itemId)) { diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java index 272ff1c9ae..3154fd2a85 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -298,6 +298,7 @@ public class GridBasicFeatures extends AbstractComponentTest { new NumberRenderer(new DecimalFormat("0,000.00", DecimalFormatSymbols.getInstance(new Locale("fi", "FI"))))); + grid.getColumn(getColumnProperty(col++)).setRenderer( new DateRenderer(new SimpleDateFormat("dd.MM.yy HH:mm"))); grid.getColumn(getColumnProperty(col++)).setRenderer( diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java index 0f2fe54696..57f4b877df 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorBufferedTest.java @@ -163,7 +163,7 @@ public class GridEditorBufferedTest extends GridEditorTest { } @Test - public void testNoScrollAfterEditByAPI() { + public void testScrollDisabledOnProgrammaticOpen() { int originalScrollPos = getGridVerticalScrollPos(); selectMenuPath(EDIT_ITEM_5); @@ -175,7 +175,7 @@ public class GridEditorBufferedTest extends GridEditorTest { } @Test - public void testNoScrollAfterEditByMouse() { + public void testScrollDisabledOnMouseOpen() { int originalScrollPos = getGridVerticalScrollPos(); GridCellElement cell_5_0 = getGridElement().getCell(5, 0); @@ -188,7 +188,7 @@ public class GridEditorBufferedTest extends GridEditorTest { } @Test - public void testNoScrollAfterEditByKeyboard() { + public void testScrollDisabledOnKeyboardOpen() { int originalScrollPos = getGridVerticalScrollPos(); GridCellElement cell_5_0 = getGridElement().getCell(5, 0); @@ -216,7 +216,28 @@ public class GridEditorBufferedTest extends GridEditorTest { } @Test - public void testProgrammaticOpeningWhenOpen() { + public void testMouseOpeningDisabledWhenOpen() { + selectMenuPath(EDIT_ITEM_5); + + getGridElement().getCell(4, 0).doubleClick(); + + assertEquals("Editor should still edit row 5", "(5, 0)", + getEditorWidgets().get(0).getAttribute("value")); + } + + @Test + public void testKeyboardOpeningDisabledWhenOpen() { + selectMenuPath(EDIT_ITEM_5); + + new Actions(getDriver()).click(getGridElement().getCell(4, 0)) + .sendKeys(Keys.ENTER).perform(); + + assertEquals("Editor should still edit row 5", "(5, 0)", + getEditorWidgets().get(0).getAttribute("value")); + } + + @Test + public void testProgrammaticOpeningDisabledWhenOpen() { selectMenuPath(EDIT_ITEM_5); assertEditorOpen(); assertEquals("Editor should edit row 5", "(5, 0)", getEditorWidgets() @@ -232,7 +253,7 @@ public class GridEditorBufferedTest extends GridEditorTest { } @Test - public void testUserSortDisabled() { + public void testUserSortDisabledWhenOpen() { selectMenuPath(EDIT_ITEM_5); getGridElement().getHeaderCell(0, 0).click(); diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java index 3582038e61..e7eb78c35e 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java @@ -123,7 +123,6 @@ public abstract class GridEditorTest extends GridBasicFeaturesTest { } protected void assertEditorOpen() { - assertNotNull("Editor is supposed to be open", getEditor()); assertEquals("Unexpected number of widgets", GridBasicFeatures.EDITABLE_COLUMNS, getEditorWidgets().size()); } @@ -133,7 +132,7 @@ public abstract class GridEditorTest extends GridBasicFeaturesTest { } protected List getEditorWidgets() { - assertNotNull(getEditor()); + assertNotNull("Editor is supposed to be open", getEditor()); return getEditor().findElements(By.className("v-textfield")); } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java index 1058fe2d74..08094b57e3 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorUnbufferedTest.java @@ -72,14 +72,32 @@ public class GridEditorUnbufferedTest extends GridEditorTest { String firstFieldValue = getEditorWidgets().get(0) .getAttribute("value"); - assertTrue("Editor is not at correct row index (5)", - "(5, 0)".equals(firstFieldValue)); + assertEquals("Editor should be at row 5", "(5, 0)", firstFieldValue); getGridElement().getCell(10, 0).click(); firstFieldValue = getEditorWidgets().get(0).getAttribute("value"); - assertTrue("Editor is not at correct row index (10)", - "(10, 0)".equals(firstFieldValue)); + assertEquals("Editor should be at row 10", "(10, 0)", firstFieldValue); + } + + @Test + public void testValidationErrorPreventsMove() { + // Because of "out of view" issues, we need to move this for easy access + selectMenuPath("Component", "Columns", "Column 7", "Column 7 Width", + "50px"); + for (int i = 0; i < 6; ++i) { + selectMenuPath("Component", "Columns", "Column 7", "Move left"); + } + + selectMenuPath(EDIT_ITEM_5); + + getEditorWidgets().get(1).click(); + getEditorWidgets().get(1).sendKeys("not a number"); + + getGridElement().getCell(10, 0).click(); + + assertEquals("Editor should not move from row 5", "(5, 0)", + getEditorWidgets().get(0).getAttribute("value")); } @Test @@ -96,7 +114,7 @@ public class GridEditorUnbufferedTest extends GridEditorTest { } @Test - public void testScrollAfterEditByAPI() { + public void testScrollEnabledOnProgrammaticOpen() { int originalScrollPos = getGridVerticalScrollPos(); selectMenuPath(EDIT_ITEM_5); @@ -108,7 +126,7 @@ public class GridEditorUnbufferedTest extends GridEditorTest { } @Test - public void testScrollAfterEditByMouse() { + public void testScrollEnabledOnMouseOpen() { int originalScrollPos = getGridVerticalScrollPos(); GridCellElement cell_5_0 = getGridElement().getCell(5, 0); @@ -121,7 +139,7 @@ public class GridEditorUnbufferedTest extends GridEditorTest { } @Test - public void testScrollAfterEditByKeyboard() { + public void testScrollEnabledOnKeyboardOpen() { int originalScrollPos = getGridVerticalScrollPos(); GridCellElement cell_5_0 = getGridElement().getCell(5, 0); -- cgit v1.2.3 From 80058d9429940c376c63c086b1cf79848fe1a699 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Thu, 2 Jul 2015 16:37:01 +0300 Subject: Add row and cell description generators to Grid (#18481) Change-Id: I940399d986eb6970df687880645fafc157dab432 --- .../vaadin/client/connectors/GridConnector.java | 40 ++++ client/src/com/vaadin/client/widgets/Grid.java | 22 +++ server/src/com/vaadin/ui/Grid.java | 217 +++++++++++++++++---- .../src/com/vaadin/shared/ui/grid/GridState.java | 17 ++ .../grid/basicfeatures/GridBasicFeatures.java | 43 ++++ .../GridDescriptionGeneratorTest.java | 74 +++++++ 6 files changed, 380 insertions(+), 33 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridDescriptionGeneratorTest.java (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index d42041670e..3c83fb2c24 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -31,6 +31,7 @@ import java.util.logging.Logger; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Widget; @@ -38,6 +39,7 @@ import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.DeferredWorker; import com.vaadin.client.MouseEventDetailsBuilder; +import com.vaadin.client.TooltipInfo; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.connectors.RpcDataSourceConnector.DetailsListener; import com.vaadin.client.connectors.RpcDataSourceConnector.RpcDataSource; @@ -1456,4 +1458,42 @@ public class GridConnector extends AbstractHasComponentsConnector implements public DetailsListener getDetailsListener() { return detailsListener; } + + @Override + public boolean hasTooltip() { + return getState().hasDescriptions || super.hasTooltip(); + } + + @Override + public TooltipInfo getTooltipInfo(Element element) { + CellReference cell = getWidget().getCellReference(element); + + if (cell != null) { + JsonObject row = cell.getRow(); + if (row == null) { + return null; + } + + Column column = cell.getColumn(); + if (!(column instanceof CustomGridColumn)) { + // Selection checkbox column + return null; + } + CustomGridColumn c = (CustomGridColumn) column; + + JsonObject cellDescriptions = row + .getObject(GridState.JSONKEY_CELLDESCRIPTION); + + if (cellDescriptions != null && cellDescriptions.hasKey(c.id)) { + return new TooltipInfo(cellDescriptions.getString(c.id)); + } else if (row.hasKey(GridState.JSONKEY_ROWDESCRIPTION)) { + return new TooltipInfo( + row.getString(GridState.JSONKEY_ROWDESCRIPTION)); + } else { + return null; + } + } + + return super.getTooltipInfo(element); + } } diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 0555df3c1f..cf05e7e53a 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -8173,4 +8173,26 @@ public class Grid extends ResizeComposite implements public EventCellReference getEventCell() { return eventCell; } + + /** + * Returns a CellReference for the cell to which the given element belongs + * to. + * + * @since + * @param element + * Element to find from the cell's content. + * @return CellReference or null if cell was not found. + */ + public CellReference getCellReference(Element element) { + RowContainer container = getEscalator().findRowContainer(element); + if (container != null) { + Cell cell = container.getCell(element); + if (cell != null) { + EventCellReference cellRef = new EventCellReference(this); + cellRef.set(cell, getSectionFromContainer(container)); + return cellRef; + } + } + return null; + } } diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index f025b6e1c0..ab4236fdf0 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -1514,39 +1514,88 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, } /** - * Callback interface for generating custom style names for data rows + * A callback interface for generating custom style names for Grid rows. * * @see Grid#setRowStyleGenerator(RowStyleGenerator) */ public interface RowStyleGenerator extends Serializable { /** - * Called by Grid to generate a style name for a row + * Called by Grid to generate a style name for a row. * - * @param rowReference - * The row to generate a style for + * @param row + * the row to generate a style for * @return the style name to add to this row, or {@code null} to not set * any style */ - public String getStyle(RowReference rowReference); + public String getStyle(RowReference row); } /** - * Callback interface for generating custom style names for cells + * A callback interface for generating custom style names for Grid cells. * * @see Grid#setCellStyleGenerator(CellStyleGenerator) */ public interface CellStyleGenerator extends Serializable { /** - * Called by Grid to generate a style name for a column + * Called by Grid to generate a style name for a column. * - * @param cellReference - * The cell to generate a style for + * @param cell + * the cell to generate a style for * @return the style name to add to this cell, or {@code null} to not * set any style */ - public String getStyle(CellReference cellReference); + public String getStyle(CellReference cell); + } + + /** + * A callback interface for generating optional descriptions (tooltips) for + * Grid rows. If a description is generated for a row, it is used for all + * the cells in the row for which a {@link CellDescriptionGenerator cell + * description} is not generated. + * + * @see Grid#setRowDescriptionGenerator(CellDescriptionGenerator) + * + * @since + */ + public interface RowDescriptionGenerator extends Serializable { + + /** + * Called by Grid to generate a description (tooltip) for a row. The + * description may contain HTML which is rendered directly; if this is + * not desired the returned string must be escaped by the implementing + * method. + * + * @param row + * the row to generate a description for + * @return the row description or {@code null} for no description + */ + public String getDescription(RowReference row); + } + + /** + * A callback interface for generating optional descriptions (tooltips) for + * Grid cells. If a cell has both a {@link RowDescriptionGenerator row + * description} and a cell description, the latter has precedence. + * + * @see Grid#setCellDescriptionGenerator(CellDescriptionGenerator) + * + * @since + */ + public interface CellDescriptionGenerator extends Serializable { + + /** + * Called by Grid to generate a description (tooltip) for a cell. The + * description may contain HTML which is rendered directly; if this is + * not desired the returned string must be escaped by the implementing + * method. + * + * @param cell + * the cell to generate a description for + * @return the cell description or {@code null} for no description + */ + public String getDescription(CellReference cell); } /** @@ -1555,51 +1604,83 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, */ private class RowDataGenerator implements DataGenerator { + private void put(String key, String value, JsonObject object) { + if (value != null && !value.isEmpty()) { + object.put(key, value); + } + } + @Override public void generateData(Object itemId, Item item, JsonObject rowData) { - RowReference r = new RowReference(Grid.this); - r.set(itemId); + RowReference row = new RowReference(Grid.this); + row.set(itemId); if (rowStyleGenerator != null) { - String style = rowStyleGenerator.getStyle(r); - if (style != null && !style.isEmpty()) { - rowData.put(GridState.JSONKEY_ROWSTYLE, style); - } + String style = rowStyleGenerator.getStyle(row); + put(GridState.JSONKEY_ROWSTYLE, style, rowData); + } + + if (rowDescriptionGenerator != null) { + String description = rowDescriptionGenerator + .getDescription(row); + put(GridState.JSONKEY_ROWDESCRIPTION, description, rowData); + } JsonObject cellStyles = Json.createObject(); JsonObject cellData = Json.createObject(); - for (Column column : getColumns()) { - Object propertyId = column.getPropertyId(); - String columnId = columnKeys.key(propertyId); + JsonObject cellDescriptions = Json.createObject(); - cellData.put(columnId, getRendererData(column, item)); + CellReference cell = new CellReference(row); - if (cellStyleGenerator != null) { - CellReference c = new CellReference(r); - c.set(propertyId); + for (Column column : getColumns()) { + cell.set(column.getPropertyId()); - String style = cellStyleGenerator.getStyle(c); - if (style != null && !style.isEmpty()) { - cellStyles.put(columnId, style); - } - } + writeData(cell, cellData); + writeStyles(cell, cellStyles); + writeDescriptions(cell, cellDescriptions); + } + + if (cellDescriptionGenerator != null + && cellDescriptions.keys().length > 0) { + rowData.put(GridState.JSONKEY_CELLDESCRIPTION, cellDescriptions); } if (cellStyleGenerator != null && cellStyles.keys().length > 0) { rowData.put(GridState.JSONKEY_CELLSTYLES, cellStyles); } + rowData.put(GridState.JSONKEY_DATA, cellData); } - private JsonValue getRendererData(Column column, Item item) { + private void writeStyles(CellReference cell, JsonObject styles) { + if (cellStyleGenerator != null) { + String style = cellStyleGenerator.getStyle(cell); + put(columnKeys.key(cell.getPropertyId()), style, styles); + } + } + + private void writeDescriptions(CellReference cell, + JsonObject descriptions) { + if (cellDescriptionGenerator != null) { + String description = cellDescriptionGenerator + .getDescription(cell); + put(columnKeys.key(cell.getPropertyId()), description, + descriptions); + } + } + + private void writeData(CellReference cell, JsonObject data) { + Column column = getColumn(cell.getPropertyId()); Converter converter = column.getConverter(); - Object propertyId = column.getPropertyId(); - Object modelValue = item.getItemProperty(propertyId).getValue(); Renderer renderer = column.getRenderer(); - return AbstractRenderer.encodeValue(modelValue, renderer, - converter, getLocale()); + Item item = cell.getItem(); + Object modelValue = item.getItemProperty(cell.getPropertyId()) + .getValue(); + + data.put(columnKeys.key(cell.getPropertyId()), AbstractRenderer + .encodeValue(modelValue, renderer, converter, getLocale())); } } @@ -3781,6 +3862,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, private CellStyleGenerator cellStyleGenerator; private RowStyleGenerator rowStyleGenerator; + private CellDescriptionGenerator cellDescriptionGenerator; + private RowDescriptionGenerator rowDescriptionGenerator; + /** * true if Grid is using the internal IndexedContainer created * in Grid() constructor, or false if the user has set their @@ -5759,6 +5843,73 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, return getRpcProxy(EditorClientRpc.class); } + /** + * Sets the {@code CellDescriptionGenerator} instance for generating + * optional descriptions (tooltips) for individual Grid cells. If a + * {@link RowDescriptionGenerator} is also set, the row description it + * generates is displayed for cells for which {@code generator} returns + * null. + * + * @param generator + * the description generator to use or {@code null} to remove a + * previously set generator if any + * + * @see #setRowDescriptionGenerator(RowDescriptionGenerator) + * + * @since + */ + public void setCellDescriptionGenerator(CellDescriptionGenerator generator) { + cellDescriptionGenerator = generator; + getState().hasDescriptions = (generator != null || rowDescriptionGenerator != null); + datasourceExtension.refreshCache(); + } + + /** + * Returns the {@code CellDescriptionGenerator} instance used to generate + * descriptions (tooltips) for Grid cells. + * + * @return the description generator or {@code null} if no generator is set + * + * @since + */ + public CellDescriptionGenerator getCellDescriptionGenerator() { + return cellDescriptionGenerator; + } + + /** + * Sets the {@code RowDescriptionGenerator} instance for generating optional + * descriptions (tooltips) for Grid rows. If a + * {@link CellDescriptionGenerator} is also set, the row description + * generated by {@code generator} is used for cells for which the cell + * description generator returns null. + * + * + * @param generator + * the description generator to use or {@code null} to remove a + * previously set generator if any + * + * @see #setCellDescriptionGenerator(CellDescriptionGenerator) + * + * @since + */ + public void setRowDescriptionGenerator(RowDescriptionGenerator generator) { + rowDescriptionGenerator = generator; + getState().hasDescriptions = (generator != null || cellDescriptionGenerator != null); + datasourceExtension.refreshCache(); + } + + /** + * Returns the {@code RowDescriptionGenerator} instance used to generate + * descriptions (tooltips) for Grid rows + * + * @return the description generator or {@code} null if no generator is set + * + * @since + */ + public RowDescriptionGenerator getRowDescriptionGenerator() { + return rowDescriptionGenerator; + } + /** * Sets the style generator that is used for generating styles for cells * diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index c455ffe23b..0d0a5d3e9f 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -102,6 +102,20 @@ public class GridState extends TabIndexState { */ public static final String JSONKEY_CELLSTYLES = "cs"; + /** + * The key in which a row's description can be found + * + * @see com.vaadin.shared.data.DataProviderRpc#setRowData(int, String) + */ + public static final String JSONKEY_ROWDESCRIPTION = "rd"; + + /** + * The key in which a cell's description can be found + * + * @see com.vaadin.shared.data.DataProviderRpc#setRowData(int, String) + */ + public static final String JSONKEY_CELLDESCRIPTION = "cd"; + /** * The key that tells whether details are visible for the row. * @@ -160,6 +174,9 @@ public class GridState extends TabIndexState { @DelegateToWidget public boolean editorBuffered = true; + /** Whether rows and/or cells have generated descriptions (tooltips) */ + public boolean hasDescriptions; + /** The caption for the save button in the editor */ @DelegateToWidget public String editorSaveCaption = GridConstants.DEFAULT_SAVE_CAPTION; diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java index 3154fd2a85..33a54b1c9a 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -52,6 +52,7 @@ import com.vaadin.ui.Component; import com.vaadin.ui.CssLayout; import com.vaadin.ui.Field; import com.vaadin.ui.Grid; +import com.vaadin.ui.Grid.CellDescriptionGenerator; import com.vaadin.ui.Grid.CellReference; import com.vaadin.ui.Grid.CellStyleGenerator; import com.vaadin.ui.Grid.Column; @@ -68,6 +69,7 @@ import com.vaadin.ui.Grid.FooterCell; import com.vaadin.ui.Grid.HeaderCell; import com.vaadin.ui.Grid.HeaderRow; import com.vaadin.ui.Grid.MultiSelectionModel; +import com.vaadin.ui.Grid.RowDescriptionGenerator; import com.vaadin.ui.Grid.RowReference; import com.vaadin.ui.Grid.RowStyleGenerator; import com.vaadin.ui.Grid.SelectionMode; @@ -130,6 +132,27 @@ public class GridBasicFeatures extends AbstractComponentTest { } }; + private RowDescriptionGenerator rowDescriptionGenerator = new RowDescriptionGenerator() { + + @Override + public String getDescription(RowReference row) { + return "Row tooltip for row " + row.getItemId(); + } + }; + + private CellDescriptionGenerator cellDescriptionGenerator = new CellDescriptionGenerator() { + + @Override + public String getDescription(CellReference cell) { + if ("Column 0".equals(cell.getPropertyId())) { + return "Cell tooltip for row " + cell.getItemId() + + ", column 0"; + } else { + return null; + } + } + }; + private ItemClickListener editorOpeningItemClickListener = new ItemClickListener() { @Override @@ -629,6 +652,25 @@ public class GridBasicFeatures extends AbstractComponentTest { } }); + createBooleanAction("Row description generator", "State", false, + new Command() { + + @Override + public void execute(Grid c, Boolean value, Object data) { + c.setRowDescriptionGenerator(value ? rowDescriptionGenerator + : null); + } + }); + + createBooleanAction("Cell description generator", "State", false, + new Command() { + @Override + public void execute(Grid c, Boolean value, Object data) { + c.setCellDescriptionGenerator(value ? cellDescriptionGenerator + : null); + } + }); + LinkedHashMap frozenOptions = new LinkedHashMap(); for (int i = -1; i <= COLUMNS; i++) { frozenOptions.put(String.valueOf(i), Integer.valueOf(i)); @@ -673,6 +715,7 @@ public class GridBasicFeatures extends AbstractComponentTest { } }); + createBooleanAction("EditorOpeningItemClickListener", "State", false, new Command() { diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridDescriptionGeneratorTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridDescriptionGeneratorTest.java new file mode 100644 index 0000000000..ed712361a6 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridDescriptionGeneratorTest.java @@ -0,0 +1,74 @@ +/* + * 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.tests.components.grid.basicfeatures; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.openqa.selenium.By; + +public class GridDescriptionGeneratorTest extends GridBasicFeaturesTest { + + @Test + public void testCellDescription() { + openTestURL(); + selectMenuPath("Component", "State", "Cell description generator"); + + getGridElement().getCell(1, 0).showTooltip(); + String tooltipText = findElement(By.className("v-tooltip-text")) + .getText(); + assertEquals("Tooltip text", "Cell tooltip for row 1, column 0", + tooltipText); + + getGridElement().getCell(1, 1).showTooltip(); + assertTrue("Tooltip should not be present in cell (1, 1) ", + findElement(By.className("v-tooltip-text")).getText().isEmpty()); + } + + @Test + public void testRowDescription() { + openTestURL(); + selectMenuPath("Component", "State", "Row description generator"); + + getGridElement().getCell(5, 3).showTooltip(); + String tooltipText = findElement(By.className("v-tooltip-text")) + .getText(); + assertEquals("Tooltip text", "Row tooltip for row 5", tooltipText); + + getGridElement().getCell(15, 3).showTooltip(); + tooltipText = findElement(By.className("v-tooltip-text")).getText(); + assertEquals("Tooltip text", "Row tooltip for row 15", tooltipText); + } + + @Test + public void testRowAndCellDescription() { + openTestURL(); + selectMenuPath("Component", "State", "Row description generator"); + selectMenuPath("Component", "State", "Cell description generator"); + + getGridElement().getCell(5, 0).showTooltip(); + String tooltipText = findElement(By.className("v-tooltip-text")) + .getText(); + assertEquals("Tooltip text", "Cell tooltip for row 5, column 0", + tooltipText); + + getGridElement().getCell(5, 3).showTooltip(); + tooltipText = findElement(By.className("v-tooltip-text")).getText(); + assertEquals("Tooltip text", "Row tooltip for row 5", tooltipText); + } + +} -- cgit v1.2.3 From 4a10a70fbecdd52758ebc73512974501a02d5fdd Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Mon, 20 Jul 2015 09:21:39 +0300 Subject: Fix DetailsRow communication use connector IDs (#18493) Details are now initialized when they are made visible. The old way of requesting when seen caused a lot of problems when moving stuff around. Now uses less communication, but reserves a bit extra resources due to all details components being in the hierarchy. Change-Id: I1c1163bdc306f5b86e5e0f6e2bbf2801e65c2243 --- .../vaadin/client/connectors/GridConnector.java | 395 ++++++--------------- .../client/connectors/RpcDataSourceConnector.java | 13 - .../com/vaadin/data/RpcDataProviderExtension.java | 241 ++----------- server/src/com/vaadin/ui/Grid.java | 17 +- .../com/vaadin/shared/ui/grid/GridClientRpc.java | 17 - .../com/vaadin/shared/ui/grid/GridServerRpc.java | 17 - .../components/grid/GridDetailsDetachTest.java | 24 ++ .../server/GridDetailsServerTest.java | 6 +- 8 files changed, 167 insertions(+), 563 deletions(-) (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index ef52a429e7..4c2e8ab4e1 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -19,13 +19,13 @@ package com.vaadin.client.connectors; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.logging.Logger; @@ -38,6 +38,7 @@ import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.DeferredWorker; import com.vaadin.client.MouseEventDetailsBuilder; +import com.vaadin.client.ServerConnector; import com.vaadin.client.annotations.OnStateChange; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.connectors.RpcDataSourceConnector.DetailsListener; @@ -79,10 +80,8 @@ import com.vaadin.client.widgets.Grid.FooterCell; import com.vaadin.client.widgets.Grid.FooterRow; import com.vaadin.client.widgets.Grid.HeaderCell; import com.vaadin.client.widgets.Grid.HeaderRow; -import com.vaadin.shared.Connector; import com.vaadin.shared.data.sort.SortDirection; import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.grid.DetailsConnectorChange; import com.vaadin.shared.ui.grid.EditorClientRpc; import com.vaadin.shared.ui.grid.EditorServerRpc; import com.vaadin.shared.ui.grid.GridClientRpc; @@ -420,255 +419,107 @@ public class GridConnector extends AbstractHasComponentsConnector implements } }; - private static class CustomDetailsGenerator implements DetailsGenerator { + private class CustomDetailsGenerator implements DetailsGenerator { - private final Map indexToDetailsMap = new HashMap(); + private final Map idToDetailsMap = new HashMap(); + private final Map idToRowIndex = new HashMap(); @Override - @SuppressWarnings("boxing") public Widget getDetails(int rowIndex) { - ComponentConnector componentConnector = indexToDetailsMap - .get(rowIndex); - if (componentConnector != null) { - return componentConnector.getWidget(); - } else { - return null; - } - } - - public void setDetailsConnectorChanges( - Set changes) { - /* - * To avoid overwriting connectors while moving them about, we'll - * take all the affected connectors, first all remove those that are - * removed or moved, then we add back those that are moved or added. - */ + JsonObject row = getWidget().getDataSource().getRow(rowIndex); - /* Remove moved/removed connectors from bookkeeping */ - for (DetailsConnectorChange change : changes) { - Integer oldIndex = change.getOldIndex(); - Connector removedConnector = indexToDetailsMap.remove(oldIndex); - - Connector connector = change.getConnector(); - assert removedConnector == null || connector == null - || removedConnector.equals(connector) : "Index " - + oldIndex + " points to " + removedConnector - + " while " + connector + " was expected"; - } - - /* Add moved/added connectors to bookkeeping */ - for (DetailsConnectorChange change : changes) { - Integer newIndex = change.getNewIndex(); - ComponentConnector connector = (ComponentConnector) change - .getConnector(); - - if (connector != null) { - assert newIndex != null : "An existing connector has a missing new index."; - - ComponentConnector prevConnector = indexToDetailsMap.put( - newIndex, connector); - - assert prevConnector == null : "Connector collision at index " - + newIndex - + " between old " - + prevConnector - + " and new " + connector; - } + if (!row.hasKey(GridState.JSONKEY_DETAILS_VISIBLE) + || row.getString(GridState.JSONKEY_DETAILS_VISIBLE) + .isEmpty()) { + return null; } - } - } - - @SuppressWarnings("boxing") - private static class DetailsConnectorFetcher implements DeferredWorker { - private static final int FETCH_TIMEOUT_MS = 5000; + String id = row.getString(GridState.JSONKEY_DETAILS_VISIBLE); + ComponentConnector componentConnector = idToDetailsMap.get(id); + idToRowIndex.put(id, rowIndex); - public interface Listener { - void fetchHasBeenScheduled(int id); - - void fetchHasReturned(int id); + return componentConnector.getWidget(); } - /** A flag making sure that we don't call scheduleFinally many times. */ - private boolean fetcherHasBeenCalled = false; - - /** A rolling counter for unique values. */ - private int detailsFetchCounter = 0; - - /** A collection that tracks the amount of requests currently underway. */ - private Set pendingFetches = new HashSet(5); - - private final ScheduledCommand lazyDetailsFetcher = new ScheduledCommand() { - @Override - public void execute() { - int currentFetchId = detailsFetchCounter++; - pendingFetches.add(currentFetchId); - rpc.sendDetailsComponents(currentFetchId); - fetcherHasBeenCalled = false; - - if (listener != null) { - listener.fetchHasBeenScheduled(currentFetchId); + public void updateConnectorHierarchy(List children) { + Set connectorIds = new HashSet(); + for (ServerConnector child : children) { + if (child instanceof ComponentConnector) { + connectorIds.add(child.getConnectorId()); + idToDetailsMap.put(child.getConnectorId(), + (ComponentConnector) child); } - - assert assertRequestDoesNotTimeout(currentFetchId); - } - }; - - private DetailsConnectorFetcher.Listener listener = null; - - private final GridServerRpc rpc; - - public DetailsConnectorFetcher(GridServerRpc rpc) { - assert rpc != null : "RPC was null"; - this.rpc = rpc; - } - - public void schedule() { - if (!fetcherHasBeenCalled) { - Scheduler.get().scheduleFinally(lazyDetailsFetcher); - fetcherHasBeenCalled = true; } - } - public void responseReceived(int fetchId) { - - if (fetchId < 0) { - /* Ignore negative fetchIds (they're pushed, not fetched) */ - return; + Set removedDetails = new HashSet(); + for (Entry entry : idToDetailsMap + .entrySet()) { + ComponentConnector connector = entry.getValue(); + String id = connector.getConnectorId(); + if (!connectorIds.contains(id)) { + removedDetails.add(entry.getKey()); + if (idToRowIndex.containsKey(id)) { + getWidget().setDetailsVisible(idToRowIndex.get(id), + false); + } + } } - boolean success = pendingFetches.remove(fetchId); - assert success : "Received a response with an unidentified fetch id"; - - if (listener != null) { - listener.fetchHasReturned(fetchId); + for (String id : removedDetails) { + idToDetailsMap.remove(id); + idToRowIndex.remove(id); } } - - @Override - public boolean isWorkPending() { - return fetcherHasBeenCalled || !pendingFetches.isEmpty(); - } - - private boolean assertRequestDoesNotTimeout(final int fetchId) { - /* - * This method will not be compiled without asserts enabled. This - * only makes sure that any request does not time out. - * - * TODO Should this be an explicit check? Is it worth the overhead? - */ - new Timer() { - @Override - public void run() { - assert !pendingFetches.contains(fetchId) : "Fetch id " - + fetchId + " timed out."; - } - }.schedule(FETCH_TIMEOUT_MS); - return true; - } - - public void setListener(DetailsConnectorFetcher.Listener listener) { - // if more are needed, feel free to convert this into a collection. - this.listener = listener; - } } /** - * The functionality that makes sure that the scroll position is still kept - * up-to-date even if more details are being fetched lazily. + * Class for handling scrolling issues with open details. + * + * @since + * @author Vaadin Ltd */ - private class LazyDetailsScrollAdjuster implements DeferredWorker { + private class LazyDetailsScroller implements DeferredWorker { - private static final int SCROLL_TO_END_ID = -2; - private static final int NO_SCROLL_SCHEDULED = -1; - - private class ScrollStopChecker implements DeferredWorker { - private final ScheduledCommand checkCommand = new ScheduledCommand() { - @Override - public void execute() { - isScheduled = false; - if (queuedFetches.isEmpty()) { - currentRow = NO_SCROLL_SCHEDULED; - destination = null; - } - } - }; - - private boolean isScheduled = false; - - public void schedule() { - if (isScheduled) { - return; - } - Scheduler.get().scheduleDeferred(checkCommand); - isScheduled = true; - } - - @Override - public boolean isWorkPending() { - return isScheduled; - } - } - - private DetailsConnectorFetcher.Listener fetcherListener = new DetailsConnectorFetcher.Listener() { - @Override - @SuppressWarnings("boxing") - public void fetchHasBeenScheduled(int id) { - if (currentRow != NO_SCROLL_SCHEDULED) { - queuedFetches.add(id); - } - } + /* Timer value tested to work in our test cluster with slow IE8s. */ + private static final int DISABLE_LAZY_SCROLL_TIMEOUT = 1500; + /* + * Cancels details opening scroll after timeout. Avoids any unexpected + * scrolls via details opening. + */ + private Timer disableScroller = new Timer() { @Override - @SuppressWarnings("boxing") - public void fetchHasReturned(int id) { - if (currentRow == NO_SCROLL_SCHEDULED - || queuedFetches.isEmpty()) { - return; - } - - queuedFetches.remove(id); - if (currentRow == SCROLL_TO_END_ID) { - getWidget().scrollToEnd(); - } else { - getWidget().scrollToRow(currentRow, destination); - } - - /* - * Schedule a deferred call whether we should stop adjusting for - * scrolling. - * - * This is done deferredly just because we can't be absolutely - * certain whether this most recent scrolling won't cascade into - * further lazy details loading (perhaps deferredly). - */ - scrollStopChecker.schedule(); + public void run() { + targetRow = -1; } }; - private int currentRow = NO_SCROLL_SCHEDULED; - private final Set queuedFetches = new HashSet(); - private final ScrollStopChecker scrollStopChecker = new ScrollStopChecker(); - private ScrollDestination destination; - - public LazyDetailsScrollAdjuster() { - detailsConnectorFetcher.setListener(fetcherListener); - } + private Integer targetRow = -1; + private ScrollDestination destination = null; - public void adjustForEnd() { - currentRow = SCROLL_TO_END_ID; + public void scrollToRow(Integer row, ScrollDestination dest) { + targetRow = row; + destination = dest; + disableScroller.schedule(DISABLE_LAZY_SCROLL_TIMEOUT); } - public void adjustFor(int row, ScrollDestination destination) { - currentRow = row; - this.destination = destination; + /** + * Inform LazyDetailsScroller that a details row has opened on a row. + * + * @since + * @param rowIndex + * index of row with details now open + */ + public void detailsOpened(int rowIndex) { + if (targetRow == rowIndex) { + getWidget().scrollToRow(targetRow, destination); + disableScroller.run(); + } } @Override public boolean isWorkPending() { - return currentRow != NO_SCROLL_SCHEDULED - || !queuedFetches.isEmpty() - || scrollStopChecker.isWorkPending(); + return disableScroller.isRunning(); } } @@ -730,39 +581,46 @@ public class GridConnector extends AbstractHasComponentsConnector implements private final CustomDetailsGenerator customDetailsGenerator = new CustomDetailsGenerator(); - private final DetailsConnectorFetcher detailsConnectorFetcher = new DetailsConnectorFetcher( - getRpcProxy(GridServerRpc.class)); - private final DetailsListener detailsListener = new DetailsListener() { @Override public void reapplyDetailsVisibility(final int rowIndex, final JsonObject row) { - Scheduler.get().scheduleDeferred(new ScheduledCommand() { - @Override - public void execute() { - if (hasDetailsOpen(row)) { - getWidget().setDetailsVisible(rowIndex, true); - detailsConnectorFetcher.schedule(); - } else { + if (hasDetailsOpen(row)) { + // Command for opening details row. + ScheduledCommand openDetails = new ScheduledCommand() { + @Override + public void execute() { + // Re-apply to force redraw. getWidget().setDetailsVisible(rowIndex, false); + getWidget().setDetailsVisible(rowIndex, true); + lazyDetailsScroller.detailsOpened(rowIndex); } + }; + + if (initialChange) { + Scheduler.get().scheduleDeferred(openDetails); + } else { + Scheduler.get().scheduleFinally(openDetails); } - }); + } else { + getWidget().setDetailsVisible(rowIndex, false); + } } private boolean hasDetailsOpen(JsonObject row) { return row.hasKey(GridState.JSONKEY_DETAILS_VISIBLE) - && row.getBoolean(GridState.JSONKEY_DETAILS_VISIBLE); - } - - @Override - public void closeDetails(int rowIndex) { - getWidget().setDetailsVisible(rowIndex, false); + && row.getString(GridState.JSONKEY_DETAILS_VISIBLE) != null; } }; - private final LazyDetailsScrollAdjuster lazyDetailsScrollAdjuster = new LazyDetailsScrollAdjuster(); + private final LazyDetailsScroller lazyDetailsScroller = new LazyDetailsScroller(); + + /* + * Initially details need to behave a bit differently to allow some + * escalator magic. + */ + private boolean initialChange; @Override @SuppressWarnings("unchecked") @@ -797,11 +655,13 @@ public class GridConnector extends AbstractHasComponentsConnector implements @Override public void scrollToEnd() { - lazyDetailsScrollAdjuster.adjustForEnd(); Scheduler.get().scheduleFinally(new ScheduledCommand() { @Override public void execute() { getWidget().scrollToEnd(); + // Scrolls further if details opens. + lazyDetailsScroller.scrollToRow(dataSource.size() - 1, + ScrollDestination.END); } }); } @@ -809,11 +669,12 @@ public class GridConnector extends AbstractHasComponentsConnector implements @Override public void scrollToRow(final int row, final ScrollDestination destination) { - lazyDetailsScrollAdjuster.adjustFor(row, destination); Scheduler.get().scheduleFinally(new ScheduledCommand() { @Override public void execute() { getWidget().scrollToRow(row, destination); + // Scrolls a bit further if details opens. + lazyDetailsScroller.scrollToRow(row, destination); } }); } @@ -822,51 +683,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements public void recalculateColumnWidths() { getWidget().recalculateColumnWidths(); } - - @Override - @SuppressWarnings("boxing") - public void setDetailsConnectorChanges( - Set connectorChanges, int fetchId) { - customDetailsGenerator - .setDetailsConnectorChanges(connectorChanges); - - List removedFirst = new ArrayList( - connectorChanges); - Collections.sort(removedFirst, - DetailsConnectorChange.REMOVED_FIRST_COMPARATOR); - - // refresh moved/added details rows - for (DetailsConnectorChange change : removedFirst) { - Integer oldIndex = change.getOldIndex(); - Integer newIndex = change.getNewIndex(); - - assert oldIndex == null || oldIndex >= 0 : "Got an " - + "invalid old index: " + oldIndex - + " (connector: " + change.getConnector() + ")"; - assert newIndex == null || newIndex >= 0 : "Got an " - + "invalid new index: " + newIndex - + " (connector: " + change.getConnector() + ")"; - - if (oldIndex != null) { - /* Close the old/removed index */ - getWidget().setDetailsVisible(oldIndex, false); - - if (change.isShouldStillBeVisible()) { - getWidget().setDetailsVisible(oldIndex, true); - } - } - - if (newIndex != null) { - /* - * Since the component was lazy loaded, we need to - * refresh the details by toggling it. - */ - getWidget().setDetailsVisible(newIndex, false); - getWidget().setDetailsVisible(newIndex, true); - } - } - detailsConnectorFetcher.responseReceived(fetchId); - } }); getWidget().addSelectionHandler(internalSelectionChangeHandler); @@ -918,17 +734,12 @@ public class GridConnector extends AbstractHasComponentsConnector implements layout(); } - @Override - public void onUnregister() { - customDetailsGenerator.indexToDetailsMap.clear(); - - super.onUnregister(); - } - @Override public void onStateChanged(final StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); + initialChange = stateChangeEvent.isInitialStateChange(); + // Column updates if (stateChangeEvent.hasPropertyChanged("columns")) { @@ -1411,6 +1222,7 @@ public class GridConnector extends AbstractHasComponentsConnector implements @Override public void onConnectorHierarchyChange( ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) { + customDetailsGenerator.updateConnectorHierarchy(getChildren()); } public String getColumnId(Grid.Column column) { @@ -1427,8 +1239,7 @@ public class GridConnector extends AbstractHasComponentsConnector implements @Override public boolean isWorkPending() { - return detailsConnectorFetcher.isWorkPending() - || lazyDetailsScrollAdjuster.isWorkPending(); + return lazyDetailsScroller.isWorkPending(); } /** diff --git a/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java b/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java index 627ee74eca..c1b9f13ef4 100644 --- a/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java +++ b/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java @@ -64,14 +64,6 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { * @see GridState#JSONKEY_DETAILS_VISIBLE */ void reapplyDetailsVisibility(int rowIndex, JsonObject row); - - /** - * Closes details for a row. - * - * @param rowIndex - * the index of the row for which to close details - */ - void closeDetails(int rowIndex); } public class RpcDataSource extends AbstractRemoteDataSource { @@ -221,11 +213,6 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { rowData.get(i)); } } - - @Override - protected void onDropFromCache(int rowIndex) { - detailsListener.closeDetails(rowIndex); - } } private final RpcDataSource dataSource = new RpcDataSource(); diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index b3c7972b52..6f8a7e8f7b 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -25,7 +25,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.logging.Logger; @@ -49,11 +48,9 @@ import com.vaadin.server.ClientConnector; import com.vaadin.server.KeyMapper; import com.vaadin.shared.data.DataProviderRpc; import com.vaadin.shared.data.DataRequestRpc; -import com.vaadin.shared.ui.grid.DetailsConnectorChange; import com.vaadin.shared.ui.grid.GridClientRpc; import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.Range; -import com.vaadin.shared.util.SharedUtil; import com.vaadin.ui.Component; import com.vaadin.ui.Grid; import com.vaadin.ui.Grid.CellReference; @@ -119,16 +116,11 @@ public class RpcDataProviderExtension extends AbstractExtension { } for (Object itemId : itemsRemoved) { - detailComponentManager.destroyDetails(itemId); itemIdToKey.remove(itemId); } for (Object itemId : itemSet) { itemIdToKey.put(itemId, getKey(itemId)); - if (visibleDetails.contains(itemId)) { - detailComponentManager.createDetails(itemId, - indexOf(itemId)); - } } } @@ -620,34 +612,11 @@ public class RpcDataProviderExtension extends AbstractExtension { private final Map visibleDetailsComponents = Maps .newHashMap(); - /** A lookup map for which row contains which details component. */ - private BiMap rowIndexToDetails = HashBiMap - .create(); - - /** - * A copy of {@link #rowIndexToDetails} from its last stable state. Used - * for creating a diff against {@link #rowIndexToDetails}. - * - * @see #getAndResetConnectorChanges() - */ - private BiMap prevRowIndexToDetails = HashBiMap - .create(); - - /** - * A set keeping track on components that have been created, but not - * attached. They should be attached at some later point in time. - *

- * This isn't strictly requried, but it's a handy explicit log. You - * could find out the same thing by taking out all the other components - * and checking whether Grid is their parent or not. - */ - private final Set unattachedComponents = Sets.newHashSet(); - /** * Keeps tabs on all the details that did not get a component during * {@link #createDetails(Object, int)}. */ - private final Map emptyDetails = Maps.newHashMap(); + private final Set emptyDetails = Sets.newHashSet(); private Grid grid; @@ -661,19 +630,16 @@ public class RpcDataProviderExtension extends AbstractExtension { * the item id for which to create the details component. * Assumed not null and that a component is not * currently present for this item previously - * @param rowIndex - * the row index for {@code itemId} * @throws IllegalStateException * if the current details generator provides a component * that was manually attached, or if the same instance has * already been provided */ - public void createDetails(Object itemId, int rowIndex) - throws IllegalStateException { + public void createDetails(Object itemId) throws IllegalStateException { assert itemId != null : "itemId was null"; - Integer newRowIndex = Integer.valueOf(rowIndex); - if (visibleDetailsComponents.containsKey(itemId)) { + if (visibleDetailsComponents.containsKey(itemId) + || emptyDetails.contains(itemId)) { // Don't overwrite existing components return; } @@ -684,58 +650,26 @@ public class RpcDataProviderExtension extends AbstractExtension { DetailsGenerator detailsGenerator = grid.getDetailsGenerator(); Component details = detailsGenerator.getDetails(rowReference); if (details != null) { - String generatorName = detailsGenerator.getClass().getName(); if (details.getParent() != null) { - throw new IllegalStateException(generatorName + String name = detailsGenerator.getClass().getName(); + throw new IllegalStateException(name + " generated a details component that already " - + "was attached. (itemId: " + itemId + ", row: " - + rowIndex + ", component: " + details); - } - - if (rowIndexToDetails.containsValue(details)) { - throw new IllegalStateException(generatorName - + " provided a details component that already " - + "exists in Grid. (itemId: " + itemId + ", row: " - + rowIndex + ", component: " + details); + + "was attached. (itemId: " + itemId + + ", component: " + details + ")"); } visibleDetailsComponents.put(itemId, details); - rowIndexToDetails.put(newRowIndex, details); - unattachedComponents.add(details); - assert !emptyDetails.containsKey(itemId) : "Bookeeping thinks " + details.setParent(grid); + grid.markAsDirty(); + + assert !emptyDetails.contains(itemId) : "Bookeeping thinks " + "itemId is empty even though we just created a " + "component for it (" + itemId + ")"; } else { - assert assertItemIdHasNotMovedAndNothingIsOverwritten(itemId, - newRowIndex); - emptyDetails.put(itemId, newRowIndex); - } - - /* - * Don't attach the components here. It's done by - * GridServerRpc.sendDetailsComponents in a separate roundtrip. - */ - } - - private boolean assertItemIdHasNotMovedAndNothingIsOverwritten( - Object itemId, Integer newRowIndex) { - - Integer oldRowIndex = emptyDetails.get(itemId); - if (!SharedUtil.equals(oldRowIndex, newRowIndex)) { - - assert !emptyDetails.containsKey(itemId) : "Unexpected " - + "change of empty details row index for itemId " - + itemId + " from " + oldRowIndex + " to " - + newRowIndex; - - assert !emptyDetails.containsValue(newRowIndex) : "Bookkeeping" - + " already had another itemId for this empty index " - + "(index: " + newRowIndex + ", new itemId: " + itemId - + ")"; + emptyDetails.add(itemId); } - return true; } /** @@ -756,8 +690,6 @@ public class RpcDataProviderExtension extends AbstractExtension { return; } - rowIndexToDetails.inverse().remove(removedComponent); - removedComponent.setParent(null); grid.markAsDirty(); } @@ -773,81 +705,12 @@ public class RpcDataProviderExtension extends AbstractExtension { public Collection getComponents() { Set components = new HashSet( visibleDetailsComponents.values()); - components.removeAll(unattachedComponents); return components; } - /** - * Gets information on how the connectors have changed. - *

- * This method only returns the changes that have been made between two - * calls of this method. I.e. Calling this method once will reset the - * state for the next state. - *

- * Used internally by the Grid object. - * - * @return information on how the connectors have changed - */ - public Set getAndResetConnectorChanges() { - Set changes = new HashSet(); - - // populate diff with added/changed - for (Entry entry : rowIndexToDetails.entrySet()) { - Component component = entry.getValue(); - assert component != null : "rowIndexToDetails contains a null component"; - - Integer newIndex = entry.getKey(); - Integer oldIndex = prevRowIndexToDetails.inverse().get( - component); - - /* - * only attach components. Detaching already happened in - * destroyDetails. - */ - if (newIndex != null && oldIndex == null) { - assert unattachedComponents.contains(component) : "unattachedComponents does not contain component for index " - + newIndex + " (" + component + ")"; - component.setParent(grid); - unattachedComponents.remove(component); - } - - if (!SharedUtil.equals(oldIndex, newIndex)) { - changes.add(new DetailsConnectorChange(component, oldIndex, - newIndex, emptyDetails.containsKey(component))); - } - } - - // populate diff with removed - for (Entry entry : prevRowIndexToDetails - .entrySet()) { - Integer oldIndex = entry.getKey(); - Component component = entry.getValue(); - Integer newIndex = rowIndexToDetails.inverse().get(component); - if (newIndex == null) { - changes.add(new DetailsConnectorChange(null, oldIndex, - null, emptyDetails.containsValue(oldIndex))); - } - } - - // reset diff map - prevRowIndexToDetails = HashBiMap.create(rowIndexToDetails); - - return changes; - } - public void refresh(Object itemId) { - Component component = visibleDetailsComponents.get(itemId); - Integer rowIndex = null; - if (component != null) { - rowIndex = rowIndexToDetails.inverse().get(component); - destroyDetails(itemId); - } else { - rowIndex = emptyDetails.remove(itemId); - } - - assert rowIndex != null : "Given itemId does not map to an " - + "existing detail row (" + itemId + ")"; - createDetails(itemId, rowIndex.intValue()); + destroyDetails(itemId); + createDetails(itemId); } void setGrid(Grid grid) { @@ -927,17 +790,13 @@ public class RpcDataProviderExtension extends AbstractExtension { listener.removeListener(); } - // Wipe clean all details. - HashSet detailItemIds = new HashSet( - detailComponentManager.visibleDetailsComponents - .keySet()); - for (Object itemId : detailItemIds) { - detailComponentManager.destroyDetails(itemId); - } - listeners.clear(); activeRowHandler.activeRange = Range.withLength(0, 0); + for (Object itemId : visibleDetails) { + detailComponentManager.destroyDetails(itemId); + } + /* Mark as dirty to push changes in beforeClientResponse */ bareItemSetTriggeredSizeChange = true; markAsDirty(); @@ -1118,7 +977,14 @@ public class RpcDataProviderExtension extends AbstractExtension { rowObject.put(GridState.JSONKEY_ROWKEY, keyMapper.getKey(itemId)); if (visibleDetails.contains(itemId)) { - rowObject.put(GridState.JSONKEY_DETAILS_VISIBLE, true); + // Double check to be sure details component exists. + detailComponentManager.createDetails(itemId); + Component detailsComponent = detailComponentManager.visibleDetailsComponents + .get(itemId); + rowObject.put( + GridState.JSONKEY_DETAILS_VISIBLE, + (detailsComponent != null ? detailsComponent + .getConnectorId() : "")); } rowReference.set(itemId); @@ -1254,15 +1120,11 @@ public class RpcDataProviderExtension extends AbstractExtension { private void internalUpdateRowData(Object itemId) { int index = container.indexOfId(itemId); - if (index >= 0) { + if (activeRowHandler.activeRange.contains(index)) { JsonValue row = getRowData(getGrid().getColumns(), itemId); JsonArray rowArray = Json.createArray(); rowArray.set(0, row); rpc.setRowData(index, rowArray); - - if (isDetailsVisible(itemId)) { - detailComponentManager.createDetails(itemId, index); - } } } @@ -1399,37 +1261,21 @@ public class RpcDataProviderExtension extends AbstractExtension { * hide */ public void setDetailsVisible(Object itemId, boolean visible) { - final boolean modified; - if (visible) { - modified = visibleDetails.add(itemId); + visibleDetails.add(itemId); /* - * We don't want to create the component here, since the component - * might be out of view, and thus we don't know where the details - * should end up on the client side. This is also a great thing to - * optimize away, so that in case a lot of things would be opened at - * once, a huge chunk of data doesn't get sent over immediately. + * This might be an issue with a huge number of open rows, but as of + * now this works in most of the cases. */ - + detailComponentManager.createDetails(itemId); } else { - modified = visibleDetails.remove(itemId); + visibleDetails.remove(itemId); - /* - * Here we can try to destroy the component no matter what. The - * component has been removed and should be detached from the - * component hierarchy. The details row will be closed on the client - * side automatically. - */ detailComponentManager.destroyDetails(itemId); } - int rowIndex = indexOf(itemId); - boolean modifiedRowIsActive = activeRowHandler.activeRange - .contains(rowIndex); - if (modified && modifiedRowIsActive) { - updateRowData(itemId); - } + updateRowData(itemId); } /** @@ -1454,18 +1300,10 @@ public class RpcDataProviderExtension extends AbstractExtension { public void refreshDetails() { for (Object itemId : ImmutableSet.copyOf(visibleDetails)) { detailComponentManager.refresh(itemId); + updateRowData(itemId); } } - private int indexOf(Object itemId) { - /* - * It would be great if we could optimize this method away, since the - * normal usage of Grid doesn't need any indices to be known. It was - * already optimized away once, maybe we can do away with these as well. - */ - return container.indexOfId(itemId); - } - /** * Gets the detail component manager for this data provider * @@ -1475,13 +1313,4 @@ public class RpcDataProviderExtension extends AbstractExtension { public DetailComponentManager getDetailComponentManager() { return detailComponentManager; } - - @Override - public void detach() { - for (Object itemId : ImmutableSet.copyOf(visibleDetails)) { - detailComponentManager.destroyDetails(itemId); - } - - super.detach(); - } } diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index e9469c5bca..ea973bb3ba 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -3281,7 +3281,7 @@ public class Grid extends AbstractComponent implements SelectionNotifier, * currently extends the AbstractExtension superclass, but this fact should * be regarded as an implementation detail and subject to change in a future * major or minor Vaadin revision. - * + * * @param * the type this renderer knows how to present */ @@ -3354,7 +3354,7 @@ public class Grid extends AbstractComponent implements SelectionNotifier, * is desired. For instance, a {@code Renderer} could first turn a * date value into a formatted string and return * {@code encode(dateString, String.class)}. - * + * * @param value * the value to be encoded * @param type @@ -3369,7 +3369,7 @@ public class Grid extends AbstractComponent implements SelectionNotifier, /** * An abstract base class for server-side Grid extensions. - * + * * @since 7.5 */ public static abstract class AbstractGridExtension extends @@ -3384,7 +3384,7 @@ public class Grid extends AbstractComponent implements SelectionNotifier, /** * Constructs a new Grid extension and extends given Grid. - * + * * @param grid * a grid instance */ @@ -3856,13 +3856,6 @@ public class Grid extends AbstractComponent implements SelectionNotifier, userOriginated); } } - - @Override - public void sendDetailsComponents(int fetchId) { - getRpcProxy(GridClientRpc.class).setDetailsConnectorChanges( - detailComponentManager.getAndResetConnectorChanges(), - fetchId); - } }); registerRpc(new EditorServerRpc() { @@ -6063,8 +6056,6 @@ public class Grid extends AbstractComponent implements SelectionNotifier, this.detailsGenerator = detailsGenerator; datasourceExtension.refreshDetails(); - getRpcProxy(GridClientRpc.class).setDetailsConnectorChanges( - detailComponentManager.getAndResetConnectorChanges(), -1); } /** diff --git a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java index 3c6d993482..ac1b1d5a78 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java @@ -15,8 +15,6 @@ */ package com.vaadin.shared.ui.grid; -import java.util.Set; - import com.vaadin.shared.communication.ClientRpc; /** @@ -57,19 +55,4 @@ public interface GridClientRpc extends ClientRpc { * Command client Grid to recalculate column widths. */ public void recalculateColumnWidths(); - - /** - * Informs the GridConnector on how the indexing of details connectors has - * changed. - * - * @since 7.5.0 - * @param connectorChanges - * the indexing changes of details connectors - * @param fetchId - * the id of the request for fetching the changes. A negative - * number indicates a push (not requested by the client side) - */ - public void setDetailsConnectorChanges( - Set connectorChanges, int fetchId); - } diff --git a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java index dca55c11c4..5c91f2b746 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java @@ -60,23 +60,6 @@ public interface GridServerRpc extends ServerRpc { void columnsReordered(List newColumnOrder, List oldColumnOrder); - /** - * This is a trigger for Grid to send whatever has changed regarding the - * details components. - *

- * The components can't be sent eagerly, since they are generated as a side - * effect in - * {@link com.vaadin.data.RpcDataProviderExtension#beforeClientResponse(boolean)} - * , and that is too late to change the hierarchy. So we need this - * round-trip to work around that limitation. - * - * @since 7.5.0 - * @param fetchId - * an unique identifier for the request - * @see com.vaadin.ui.Grid#setDetailsVisible(Object, boolean) - */ - void sendDetailsComponents(int fetchId); - /** * Informs the server that the column's visibility has been changed. * diff --git a/uitest/src/com/vaadin/tests/components/grid/GridDetailsDetachTest.java b/uitest/src/com/vaadin/tests/components/grid/GridDetailsDetachTest.java index fc79fd1b68..7406daeacd 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridDetailsDetachTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridDetailsDetachTest.java @@ -70,4 +70,28 @@ public class GridDetailsDetachTest extends MultiBrowserTest { Assert.assertEquals("Spacer content not visible", "Extra data for Bean 5", spacers.get(1).getText()); } + + @Test + public void testDetachAndImmediateReattach() { + setDebug(true); + openTestURL(); + + $(GridElement.class).first().getCell(3, 0).click(); + $(GridElement.class).first().getCell(5, 0).click(); + + assertNoErrorNotifications(); + + // Detach and Re-attach Grid + $(ButtonElement.class).get(1).click(); + + assertNoErrorNotifications(); + + List spacers = findElements(By.className("v-grid-spacer")); + Assert.assertEquals("Not enough spacers in DOM", 2, spacers.size()); + Assert.assertEquals("Spacer content not visible", + "Extra data for Bean 3", spacers.get(0).getText()); + Assert.assertEquals("Spacer content not visible", + "Extra data for Bean 5", spacers.get(1).getText()); + } + } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridDetailsServerTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridDetailsServerTest.java index 4ea64073f3..326dbcd55f 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridDetailsServerTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridDetailsServerTest.java @@ -22,7 +22,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; @@ -196,14 +195,11 @@ public class GridDetailsServerTest extends GridBasicFeaturesTest { assertEquals("Two", getGridElement().getDetails(0).getText()); } - @Ignore("This use case is not currently supported by Grid. If the detail " - + "is out of view, the component is detached from the UI and a " - + "new instance is generated when scrolled back. Support will " - + "maybe be incorporated at a later time") @Test public void hierarchyChangesWorkInDetailsWhileOutOfView() { selectMenuPath(DETAILS_GENERATOR_HIERARCHICAL); selectMenuPath(OPEN_FIRST_ITEM_DETAILS); + assertEquals("One", getGridElement().getDetails(0).getText()); scrollGridVerticallyTo(10000); selectMenuPath(CHANGE_HIERARCHY); scrollGridVerticallyTo(0); -- cgit v1.2.3 From 15de371e30c6a0213f733289095eeff14d790a17 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Wed, 29 Jul 2015 08:48:55 +0300 Subject: Use correct state classes for Vertical and HorizontalLayout Change-Id: Ifa52a2ff7225076129de9b0f964b82704221dccd --- server/src/com/vaadin/ui/HorizontalLayout.java | 7 +++++++ server/src/com/vaadin/ui/VerticalLayout.java | 7 +++++++ 2 files changed, 14 insertions(+) (limited to 'server/src/com/vaadin/ui') diff --git a/server/src/com/vaadin/ui/HorizontalLayout.java b/server/src/com/vaadin/ui/HorizontalLayout.java index 54569570b9..616fa09225 100644 --- a/server/src/com/vaadin/ui/HorizontalLayout.java +++ b/server/src/com/vaadin/ui/HorizontalLayout.java @@ -15,6 +15,8 @@ */ package com.vaadin.ui; +import com.vaadin.shared.ui.orderedlayout.HorizontalLayoutState; + /** * Horizontal layout * @@ -48,4 +50,9 @@ public class HorizontalLayout extends AbstractOrderedLayout { addComponents(children); } + @Override + protected HorizontalLayoutState getState() { + return (HorizontalLayoutState) super.getState(); + } + } diff --git a/server/src/com/vaadin/ui/VerticalLayout.java b/server/src/com/vaadin/ui/VerticalLayout.java index 12819e50bc..7002fbc7e6 100644 --- a/server/src/com/vaadin/ui/VerticalLayout.java +++ b/server/src/com/vaadin/ui/VerticalLayout.java @@ -15,6 +15,8 @@ */ package com.vaadin.ui; +import com.vaadin.shared.ui.orderedlayout.VerticalLayoutState; + /** * Vertical layout * @@ -48,4 +50,9 @@ public class VerticalLayout extends AbstractOrderedLayout { this(); addComponents(children); } + + @Override + protected VerticalLayoutState getState() { + return (VerticalLayoutState) super.getState(); + } } -- cgit v1.2.3 From 3c7eab0d5810a16a31778b22af22e8a34251db1a Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Thu, 9 Jul 2015 23:23:35 +0300 Subject: Update Select all -CheckBox from server and partial selections (#17590) Change-Id: I8f4986455029fc3b997ec5fee8916fa118a487ca --- .../vaadin/client/connectors/GridConnector.java | 21 ++++++ client/src/com/vaadin/client/widgets/Grid.java | 78 +++++++++++++--------- server/src/com/vaadin/ui/Grid.java | 17 +++++ .../com/vaadin/shared/ui/grid/GridClientRpc.java | 11 +++ .../grid/basicfeatures/GridBasicFeatures.java | 20 ++++++ .../basicfeatures/server/GridSelectionTest.java | 40 +++++++++++ 6 files changed, 155 insertions(+), 32 deletions(-) (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index a99b227abe..15acbc0d5a 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -33,6 +33,7 @@ import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; @@ -681,6 +682,26 @@ public class GridConnector extends AbstractHasComponentsConnector implements public void recalculateColumnWidths() { getWidget().recalculateColumnWidths(); } + + @Override + public void setSelectAll(boolean allSelected) { + if (selectionModel instanceof SelectionModel.Multi + && selectionModel.getSelectionColumnRenderer() != null) { + final Widget widget; + try { + HeaderRow defaultHeaderRow = getWidget() + .getDefaultHeaderRow(); + if (defaultHeaderRow != null) { + widget = defaultHeaderRow.getCell( + getWidget().getColumn(0)).getWidget(); + ((CheckBox) widget).setValue(allSelected, false); + } + } catch (Exception e) { + getLogger().warning( + "Problems finding select all checkbox."); + } + } + } }); getWidget().addSelectionHandler(internalSelectionChangeHandler); diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 377943ed61..8db6e9a55a 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -2377,6 +2377,7 @@ public class Grid extends ResizeComposite implements private boolean initDone = false; private boolean selected = false; + private CheckBox selectAllCheckBox; SelectionColumn(final Renderer selectColumnRenderer) { super(selectColumnRenderer); @@ -2401,41 +2402,55 @@ public class Grid extends ResizeComposite implements * exist. */ final SelectionModel.Multi model = (Multi) getSelectionModel(); - final CheckBox checkBox = GWT.create(CheckBox.class); - checkBox.addValueChangeHandler(new ValueChangeHandler() { - @Override - public void onValueChange(ValueChangeEvent event) { - if (event.getValue()) { - fireEvent(new SelectAllEvent(model)); - selected = true; - } else { - model.deselectAll(); - selected = false; - } - } - }); - checkBox.setValue(selected); - selectionCell.setWidget(checkBox); - // Select all with space when "select all" cell is active - addHeaderKeyUpHandler(new HeaderKeyUpHandler() { - @Override - public void onKeyUp(GridKeyUpEvent event) { - if (event.getNativeKeyCode() != KeyCodes.KEY_SPACE) { - return; - } - HeaderRow targetHeaderRow = getHeader().getRow( - event.getFocusedCell().getRowIndex()); - if (!targetHeaderRow.isDefault()) { - return; + if (selectAllCheckBox == null) { + selectAllCheckBox = GWT.create(CheckBox.class); + selectAllCheckBox + .addValueChangeHandler(new ValueChangeHandler() { + + @Override + public void onValueChange( + ValueChangeEvent event) { + if (event.getValue()) { + fireEvent(new SelectAllEvent(model)); + selected = true; + } else { + model.deselectAll(); + selected = false; + } + } + }); + selectAllCheckBox.setValue(selected); + + // Select all with space when "select all" cell is active + addHeaderKeyUpHandler(new HeaderKeyUpHandler() { + @Override + public void onKeyUp(GridKeyUpEvent event) { + if (event.getNativeKeyCode() != KeyCodes.KEY_SPACE) { + return; + } + HeaderRow targetHeaderRow = getHeader().getRow( + event.getFocusedCell().getRowIndex()); + if (!targetHeaderRow.isDefault()) { + return; + } + if (event.getFocusedCell().getColumn() == SelectionColumn.this) { + // Send events to ensure state is updated + selectAllCheckBox.setValue( + !selectAllCheckBox.getValue(), true); + } } - if (event.getFocusedCell().getColumn() == SelectionColumn.this) { - // Send events to ensure row selection state is - // updated - checkBox.setValue(!checkBox.getValue(), true); + }); + } else { + for (HeaderRow row : header.getRows()) { + if (row.getCell(this).getType() == GridStaticCellType.WIDGET) { + // Detach from old header. + row.getCell(this).setText(""); } } - }); + } + + selectionCell.setWidget(selectAllCheckBox); } @Override @@ -2497,7 +2512,6 @@ public class Grid extends ResizeComposite implements super.setEditable(editable); return this; } - } /** diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index ea973bb3ba..3e4a78db9d 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -1068,6 +1068,8 @@ public class Grid extends AbstractComponent implements SelectionNotifier, private int selectionLimit = DEFAULT_MAX_SELECTIONS; + private boolean allSelected; + @Override public boolean select(final Object... itemIds) throws IllegalArgumentException { @@ -1113,6 +1115,9 @@ public class Grid extends AbstractComponent implements SelectionNotifier, } fireSelectionEvent(oldSelection, selection); } + + updateAllSelectedState(); + return selectionWillChange; } @@ -1178,6 +1183,9 @@ public class Grid extends AbstractComponent implements SelectionNotifier, selection.removeAll(itemIds); fireSelectionEvent(oldSelection, selection); } + + updateAllSelectedState(); + return hasCommonElements; } @@ -1258,6 +1266,8 @@ public class Grid extends AbstractComponent implements SelectionNotifier, fireSelectionEvent(oldSelection, selection); } + updateAllSelectedState(); + return changed; } @@ -1271,6 +1281,13 @@ public class Grid extends AbstractComponent implements SelectionNotifier, "Vararg array of itemIds may not be null"); } } + + private void updateAllSelectedState() { + if (allSelected != selection.size() >= selectionLimit) { + allSelected = selection.size() >= selectionLimit; + grid.getRpcProxy(GridClientRpc.class).setSelectAll(allSelected); + } + } } /** diff --git a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java index ac1b1d5a78..cf4e95d078 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java @@ -55,4 +55,15 @@ public interface GridClientRpc extends ClientRpc { * Command client Grid to recalculate column widths. */ public void recalculateColumnWidths(); + + /** + * Informs the Grid that all items have been selected or not selected on the + * server side. + * + * @since + * @param allSelected + * true to check the select all checkbox, + * false to uncheck it. + */ + public void setSelectAll(boolean allSelected); } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java index ef51cdf446..6511866897 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -683,6 +683,26 @@ public class GridBasicFeatures extends AbstractComponentTest { c.setColumnReorderingAllowed(value); } }); + + createClickAction("Select all", "State", new Command() { + @Override + public void execute(Grid c, String value, Object data) { + SelectionModel selectionModel = c.getSelectionModel(); + if (selectionModel instanceof SelectionModel.Multi) { + ((SelectionModel.Multi) selectionModel).selectAll(); + } + } + }, null); + + createClickAction("Select none", "State", new Command() { + @Override + public void execute(Grid c, String value, Object data) { + SelectionModel selectionModel = c.getSelectionModel(); + if (selectionModel instanceof SelectionModel.Multi) { + ((SelectionModel.Multi) selectionModel).deselectAll(); + } + } + }, null); } protected void createHeaderActions() { diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSelectionTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSelectionTest.java index 9953bbcae0..1f3085d273 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSelectionTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSelectionTest.java @@ -20,8 +20,10 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; import org.openqa.selenium.Keys; +import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.support.ui.ExpectedCondition; import com.vaadin.testbench.By; import com.vaadin.testbench.elements.GridElement; @@ -335,6 +337,44 @@ public class GridSelectionTest extends GridBasicFeaturesTest { getRow(5).isSelected()); } + @Test + public void testServerSideSelectTogglesSelectAllCheckBox() { + openTestURL(); + + setSelectionModelMulti(); + GridCellElement header = getGridElement().getHeaderCell(0, 0); + + WebElement selectAll = header.findElement(By.tagName("input")); + + selectMenuPath("Component", "State", "Select all"); + waitUntilCheckBoxValue(selectAll, true); + assertTrue("Select all CheckBox wasn't selected as expected", + selectAll.isSelected()); + + selectMenuPath("Component", "State", "Select none"); + waitUntilCheckBoxValue(selectAll, false); + assertFalse("Select all CheckBox was selected unexpectedly", + selectAll.isSelected()); + + selectMenuPath("Component", "State", "Select all"); + waitUntilCheckBoxValue(selectAll, true); + getGridElement().getCell(5, 0).click(); + waitUntilCheckBoxValue(selectAll, false); + assertFalse("Select all CheckBox was selected unexpectedly", + selectAll.isSelected()); + } + + private void waitUntilCheckBoxValue(final WebElement checkBoxElememnt, + final boolean expectedValue) { + waitUntil(new ExpectedCondition() { + @Override + public Boolean apply(WebDriver input) { + return expectedValue ? checkBoxElememnt.isSelected() + : !checkBoxElememnt.isSelected(); + } + }, 5); + } + private void setSelectionModelMulti() { selectMenuPath("Component", "State", "Selection mode", "multi"); } -- cgit v1.2.3 From 554bdab01e987b09a78b23d049c0f9f9b2a2ec72 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Fri, 3 Jul 2015 15:52:06 +0300 Subject: Keep non-editable & selection columns visible in editor Change-Id: Ib3090d43e97667707590a2bca5f4641b72d7cd7f --- WebContent/VAADIN/themes/base/grid/grid.scss | 4 ++ .../VAADIN/themes/valo/components/_grid.scss | 4 ++ client/src/com/vaadin/client/widgets/Grid.java | 51 ++++++++++++++++++++++ server/src/com/vaadin/ui/Grid.java | 3 ++ .../basicfeatures/client/GridEditorClientTest.java | 2 +- .../grid/basicfeatures/server/GridEditorTest.java | 15 ++++--- 6 files changed, 72 insertions(+), 7 deletions(-) (limited to 'server/src/com/vaadin/ui') diff --git a/WebContent/VAADIN/themes/base/grid/grid.scss b/WebContent/VAADIN/themes/base/grid/grid.scss index f587dfef4f..6b3b017070 100644 --- a/WebContent/VAADIN/themes/base/grid/grid.scss +++ b/WebContent/VAADIN/themes/base/grid/grid.scss @@ -396,6 +396,10 @@ $v-grid-details-border-bottom-stripe: 1px solid darken($v-grid-row-background-co min-width: 100%; max-width: 100%; } + + &.not-editable.#{$primaryStyleName}-cell { + float: none; + } } .error::before { diff --git a/WebContent/VAADIN/themes/valo/components/_grid.scss b/WebContent/VAADIN/themes/valo/components/_grid.scss index d00ddf30a4..11134262eb 100644 --- a/WebContent/VAADIN/themes/valo/components/_grid.scss +++ b/WebContent/VAADIN/themes/valo/components/_grid.scss @@ -119,6 +119,10 @@ $v-grid-details-border-bottom-stripe: $v-grid-cell-horizontal-border !default; vertical-align: middle; } + &.not-editable.#{$primary-stylename}-cell { + float: none; + } + .error::before { border-top: round($v-unit-size / 4) solid $v-error-indicator-color; border-right: round($v-unit-size / 4) solid transparent; diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 18407dccc9..a17c59c795 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -80,6 +80,7 @@ import com.vaadin.client.Focusable; import com.vaadin.client.WidgetUtil; import com.vaadin.client.data.DataChangeHandler; import com.vaadin.client.data.DataSource; +import com.vaadin.client.data.DataSource.RowHandle; import com.vaadin.client.renderers.ComplexRenderer; import com.vaadin.client.renderers.Renderer; import com.vaadin.client.renderers.WidgetRenderer; @@ -1265,6 +1266,7 @@ public class Grid extends ResizeComposite implements private double originalTop; /** Original scroll position of grid when editor was opened */ private double originalScrollTop; + private RowHandle pinnedRowHandle; public Editor() { saveButton = new Button(); @@ -1626,6 +1628,50 @@ public class Grid extends ResizeComposite implements } } else { cell.addClassName(NOT_EDITABLE_CLASS_NAME); + cell.addClassName(tr.getCells().getItem(i).getClassName()); + // If the focused stylename is present it should not be + // inherited by the editor cell as it is not useful in the + // editor and would look broken without additional style + // rules. This is a bit of a hack. + cell.removeClassName(grid.cellFocusStyleName); + + if (column == grid.selectionColumn) { + // Duplicate selection column CheckBox + + pinnedRowHandle = grid.getDataSource().getHandle( + grid.getDataSource().getRow(rowIndex)); + pinnedRowHandle.pin(); + + // We need to duplicate the selection CheckBox for the + // editor overlay since the original one is hidden by + // the overlay + final CheckBox checkBox = GWT.create(CheckBox.class); + checkBox.setValue(grid.isSelected(pinnedRowHandle + .getRow())); + checkBox.sinkEvents(Event.ONCLICK); + + checkBox.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + T row = pinnedRowHandle.getRow(); + if (grid.isSelected(row)) { + grid.deselect(row); + } else { + grid.select(row); + } + } + }); + attachWidget(checkBox, cell); + columnToWidget.put(column, checkBox); + + // Only enable CheckBox in non-buffered mode + checkBox.setEnabled(!isBuffered()); + + } else if (!(column.getRenderer() instanceof WidgetRenderer)) { + // Copy non-widget content directly + cell.setInnerHTML(tr.getCells().getItem(i) + .getInnerHTML()); + } } } @@ -1697,6 +1743,11 @@ public class Grid extends ResizeComposite implements return; } + if (pinnedRowHandle != null) { + pinnedRowHandle.unpin(); + pinnedRowHandle = null; + } + for (Widget w : columnToWidget.values()) { setParent(w, null); } diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index ab4236fdf0..eabd3e00c7 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -6150,6 +6150,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * Opens the editor interface for the provided item. Scrolls the Grid to * bring the item to view if it is not already visible. * + * Note that any cell content rendered by a WidgetRenderer will not be + * visible in the editor row. + * * @param itemId * the id of the item to edit * @throws IllegalStateException diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridEditorClientTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridEditorClientTest.java index 43fe29bc02..58d6559bfb 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridEditorClientTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridEditorClientTest.java @@ -143,7 +143,7 @@ public class GridEditorClientTest extends GridBasicClientFeaturesTest { List selectorDivs = editorCells.findElements(By .cssSelector("div")); - assertTrue("selector column cell should've been empty", selectorDivs + assertFalse("selector column cell should've had contents", selectorDivs .get(0).getAttribute("innerHTML").isEmpty()); assertFalse("normal column cell shoul've had contents", selectorDivs .get(1).getAttribute("innerHTML").isEmpty()); diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java index f7592ce922..0a77d690a4 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java @@ -197,13 +197,16 @@ public abstract class GridEditorTest extends GridBasicFeaturesTest { GridEditorElement editor = getGridElement().getEditor(); assertFalse("Uneditable column should not have an editor widget", editor.isEditable(3)); - assertEquals( - "Not editable cell did not contain correct classname", - "not-editable", - editor.findElements(By.className("v-grid-editor-cells")).get(1) - .findElements(By.xpath("./div")).get(3) - .getAttribute("class")); + String classNames = editor + .findElements(By.className("v-grid-editor-cells")).get(1) + .findElements(By.xpath("./div")).get(3).getAttribute("class"); + + assertTrue("Noneditable cell should contain not-editable classname", + classNames.contains("not-editable")); + + assertTrue("Noneditable cell should contain v-grid-cell classname", + classNames.contains("v-grid-cell")); } @Test -- cgit v1.2.3 From 029cb1a81bf66488df1f8d5a376dd7b1a0c03bd2 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 4 Aug 2015 12:33:30 +0300 Subject: Handle renderer exceptions gracefully (#18554) Change-Id: I46557f6c6f8543ab61b863145863824785d14784 --- .../com/vaadin/data/RpcDataProviderExtension.java | 9 ++++- .../src/com/vaadin/ui/renderers/ImageRenderer.java | 2 +- .../components/grid/GridWithBrokenRenderer.java | 43 ++++++++++++++++++++++ .../grid/GridWithBrokenRendererTest.java | 41 +++++++++++++++++++++ 4 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/grid/GridWithBrokenRenderer.java create mode 100644 uitest/src/com/vaadin/tests/components/grid/GridWithBrokenRendererTest.java (limited to 'server/src/com/vaadin/ui') diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index 6f8a7e8f7b..98394c45df 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.logging.Level; import java.util.logging.Logger; import com.google.gwt.thirdparty.guava.common.collect.BiMap; @@ -1238,7 +1239,13 @@ public class RpcDataProviderExtension extends AbstractExtension { safeConverter.getPresentationType(), locale); } - JsonValue encodedValue = renderer.encode(presentationValue); + JsonValue encodedValue; + try { + encodedValue = renderer.encode(presentationValue); + } catch (Exception e) { + getLogger().log(Level.SEVERE, "Unable to encode data", e); + encodedValue = renderer.encode(null); + } return encodedValue; } diff --git a/server/src/com/vaadin/ui/renderers/ImageRenderer.java b/server/src/com/vaadin/ui/renderers/ImageRenderer.java index 2fb872583e..ad7d5cae2b 100644 --- a/server/src/com/vaadin/ui/renderers/ImageRenderer.java +++ b/server/src/com/vaadin/ui/renderers/ImageRenderer.java @@ -58,7 +58,7 @@ public class ImageRenderer extends ClickableRenderer { if (!(resource == null || resource instanceof ExternalResource || resource instanceof ThemeResource)) { throw new IllegalArgumentException( "ImageRenderer only supports ExternalResource and ThemeResource (" - + resource.getClass().getSimpleName() + "given )"); + + resource.getClass().getSimpleName() + " given)"); } return encode(ResourceReference.create(resource, this, null), diff --git a/uitest/src/com/vaadin/tests/components/grid/GridWithBrokenRenderer.java b/uitest/src/com/vaadin/tests/components/grid/GridWithBrokenRenderer.java new file mode 100644 index 0000000000..4d44eeb248 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridWithBrokenRenderer.java @@ -0,0 +1,43 @@ +/* + * 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.tests.components.grid; + +import com.vaadin.server.ClassResource; +import com.vaadin.server.Resource; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.tests.integration.FlagSeResource; +import com.vaadin.ui.Grid; +import com.vaadin.ui.renderers.ImageRenderer; + +public class GridWithBrokenRenderer extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + final Grid grid = new Grid(); + grid.addColumn("short", String.class); + grid.addColumn("icon", Resource.class); + grid.addColumn("country", String.class); + + grid.getColumn("icon").setRenderer(new ImageRenderer()); + addComponent(grid); + + grid.addRow("FI", new ClassResource("fi.gif"), "Finland"); + grid.addRow("SE", new FlagSeResource(), "Sweden"); + + } + +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridWithBrokenRendererTest.java b/uitest/src/com/vaadin/tests/components/grid/GridWithBrokenRendererTest.java new file mode 100644 index 0000000000..011c8c92ce --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridWithBrokenRendererTest.java @@ -0,0 +1,41 @@ +/* + * 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.tests.components.grid; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class GridWithBrokenRendererTest extends SingleBrowserTest { + + @Test + public void ensureRendered() { + openTestURL(); + GridElement grid = $(GridElement.class).first(); + assertRow(grid, 0, "FI", "", "Finland"); + assertRow(grid, 1, "SE", "", "Sweden"); + } + + private void assertRow(GridElement grid, int row, String... texts) { + for (int column = 0; column < texts.length; column++) { + Assert.assertEquals("Cell " + row + "," + column, texts[column], + grid.getCell(row, column).getText()); + } + + } +} -- cgit v1.2.3 From d40df1dc68f166bc23609d631420a34d1a0f2adf Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Sat, 4 Jul 2015 16:25:43 +0300 Subject: Column collapse events for Table (#6914) Change-Id: Ifeb081086a4231f75f07f4d26c56ec22e72ce5d1 --- server/src/com/vaadin/ui/Table.java | 89 +++++++++++++++- .../com/vaadin/shared/ui/table/TableConstants.java | 1 + .../table/ColumnCollapsingAndColumnExpansion.java | 64 ++++++------ .../ColumnCollapsingAndColumnExpansionTest.java | 112 +++++++++++++++++++++ .../tests/components/table/CustomTableElement.java | 57 +++++++++++ .../table/ColumnCollapsingAndColumnExpansion.html | 97 ------------------ 6 files changed, 291 insertions(+), 129 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansionTest.java create mode 100644 uitest/src/com/vaadin/tests/components/table/CustomTableElement.java delete mode 100644 uitest/tb2/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansion.html (limited to 'server/src/com/vaadin/ui') diff --git a/server/src/com/vaadin/ui/Table.java b/server/src/com/vaadin/ui/Table.java index 42c4beab6c..2cd4084ad9 100644 --- a/server/src/com/vaadin/ui/Table.java +++ b/server/src/com/vaadin/ui/Table.java @@ -69,6 +69,7 @@ import com.vaadin.shared.util.SharedUtil; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.declarative.DesignException; +import com.vaadin.util.ReflectTools; /** *

@@ -1310,6 +1311,8 @@ public class Table extends AbstractSelect implements Action.Container, * the desired collapsedness. * @throws IllegalStateException * if column collapsing is not allowed + * @throws IllegalArgumentException + * if the property id does not exist */ public void setColumnCollapsed(Object propertyId, boolean collapsed) throws IllegalStateException { @@ -1319,11 +1322,19 @@ public class Table extends AbstractSelect implements Action.Container, if (collapsed && noncollapsibleColumns.contains(propertyId)) { throw new IllegalStateException("The column is noncollapsible!"); } + if (!getContainerPropertyIds().contains(propertyId)) { + throw new IllegalArgumentException("Property '" + propertyId + + "' was not found in the container"); + } if (collapsed) { - collapsedColumns.add(propertyId); + if (collapsedColumns.add(propertyId)) { + fireColumnCollapseEvent(propertyId); + } } else { - collapsedColumns.remove(propertyId); + if (collapsedColumns.remove(propertyId)) { + fireColumnCollapseEvent(propertyId); + } } // Assures the visual refresh @@ -3182,6 +3193,10 @@ public class Table extends AbstractSelect implements Action.Container, } } + private void fireColumnCollapseEvent(Object propertyId) { + fireEvent(new ColumnCollapseEvent(this, propertyId)); + } + private void fireColumnResizeEvent(Object propertyId, int previousWidth, int currentWidth) { /* @@ -5741,6 +5756,53 @@ public class Table extends AbstractSelect implements Action.Container, public void columnReorder(ColumnReorderEvent event); } + /** + * This event is fired when the collapse state of a column changes + */ + public static class ColumnCollapseEvent extends Component.Event { + + public static final Method METHOD = ReflectTools.findMethod( + ColumnCollapseListener.class, "columnCollapseStateChange", + ColumnCollapseEvent.class); + private Object propertyId; + + /** + * Constructor + * + * @param source + * The source of the event + * @param propertyId + * The id of the coumn + */ + public ColumnCollapseEvent(Component source, Object propertyId) { + super(source); + this.propertyId = propertyId; + } + + /** + * Gets the id of the column whose collapse state changed + * + * @return the property id of the column + */ + public Object getPropertyId() { + return propertyId; + } + } + + /** + * Interface for listening to column collapse events. + */ + public interface ColumnCollapseListener extends Serializable { + + /** + * This method is triggered when the collapse state for a column has + * changed + * + * @param event + */ + public void columnCollapseStateChange(ColumnCollapseEvent event); + } + /** * Adds a column reorder listener to the Table. A column reorder listener is * called when a user reorders columns. @@ -5782,6 +5844,29 @@ public class Table extends AbstractSelect implements Action.Container, removeColumnReorderListener(listener); } + /** + * Adds a column collapse listener to the Table. A column collapse listener + * is called when the collapsed state of a column changes. + * + * @param listener + * The listener to attach + */ + public void addColumnCollapseListener(ColumnCollapseListener listener) { + addListener(TableConstants.COLUMN_COLLAPSE_EVENT_ID, + ColumnCollapseEvent.class, listener, ColumnCollapseEvent.METHOD); + } + + /** + * Removes a column reorder listener from the Table. + * + * @param listener + * The listener to remove + */ + public void removeColumnCollapseListener(ColumnCollapseListener listener) { + removeListener(TableConstants.COLUMN_COLLAPSE_EVENT_ID, + ColumnCollapseEvent.class, listener); + } + /** * Set the item description generator which generates tooltips for cells and * rows in the Table diff --git a/shared/src/com/vaadin/shared/ui/table/TableConstants.java b/shared/src/com/vaadin/shared/ui/table/TableConstants.java index fd1c61c772..e782492e9d 100644 --- a/shared/src/com/vaadin/shared/ui/table/TableConstants.java +++ b/shared/src/com/vaadin/shared/ui/table/TableConstants.java @@ -23,6 +23,7 @@ public class TableConstants implements Serializable { public static final String FOOTER_CLICK_EVENT_ID = "handleFooterClick"; public static final String COLUMN_RESIZE_EVENT_ID = "columnResize"; public static final String COLUMN_REORDER_EVENT_ID = "columnReorder"; + public static final String COLUMN_COLLAPSE_EVENT_ID = "columnCollapse"; @Deprecated public static final String ATTRIBUTE_PAGEBUFFER_FIRST = "pb-ft"; diff --git a/uitest/src/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansion.java b/uitest/src/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansion.java index 08b65e2ef5..8d1a9fc7df 100644 --- a/uitest/src/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansion.java +++ b/uitest/src/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansion.java @@ -2,21 +2,22 @@ package com.vaadin.tests.components.table; import com.vaadin.event.Action; import com.vaadin.event.Action.Handler; -import com.vaadin.tests.components.TestBase; -import com.vaadin.ui.Alignment; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Table; -import com.vaadin.ui.TextField; +import com.vaadin.ui.Table.ColumnCollapseEvent; +import com.vaadin.ui.Table.ColumnCollapseListener; -public class ColumnCollapsingAndColumnExpansion extends TestBase { +public class ColumnCollapsingAndColumnExpansion extends AbstractTestUIWithLog { private Table table; @Override - public void setup() { + protected void setup(VaadinRequest request) { table = new Table(); @@ -50,41 +51,44 @@ public class ColumnCollapsingAndColumnExpansion extends TestBase { "cell " + 2 + "-" + y, "cell " + 3 + "-" + y, }, new Object()); } - - addComponent(table); - - HorizontalLayout hl = new HorizontalLayout(); - final TextField tf = new TextField("Column name (ColX)"); - Button hide = new Button("Collapse", new ClickListener() { + table.addColumnCollapseListener(new ColumnCollapseListener() { @Override - public void buttonClick(ClickEvent event) { - table.setColumnCollapsed(tf.getValue(), true); - } - - }); + public void columnCollapseStateChange(ColumnCollapseEvent event) { + log("Collapse state for " + event.getPropertyId() + + " changed to " + + table.isColumnCollapsed(event.getPropertyId())); - Button show = new Button("Show", new ClickListener() { - - @Override - public void buttonClick(ClickEvent event) { - table.setColumnCollapsed(tf.getValue(), false); } - }); + addComponent(table); - hl.addComponent(tf); - hl.addComponent(hide); - hl.addComponent(show); - hl.setComponentAlignment(tf, Alignment.BOTTOM_LEFT); - hl.setComponentAlignment(hide, Alignment.BOTTOM_LEFT); - hl.setComponentAlignment(show, Alignment.BOTTOM_LEFT); - addComponent(hl); + for (int i = 1; i <= 3; i++) { + HorizontalLayout hl = new HorizontalLayout(); + final String id = "Col" + i; + Button hide = new Button("Collapse " + id, new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + table.setColumnCollapsed(id, true); + } + }); + + Button show = new Button("Show " + id, new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + table.setColumnCollapsed(id, false); + } + }); + + hl.addComponent(hide); + hl.addComponent(show); + addComponent(hl); + } } @Override - protected String getDescription() { + protected String getTestDescription() { return "After hiding column 2 the remaining columns (1 and 3) should use all available space in the table"; } diff --git a/uitest/src/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansionTest.java b/uitest/src/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansionTest.java new file mode 100644 index 0000000000..739851de2f --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansionTest.java @@ -0,0 +1,112 @@ +/* + * 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.tests.components.table; + +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.parallel.BrowserUtil; +import com.vaadin.tests.components.table.CustomTableElement.ContextMenuElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class ColumnCollapsingAndColumnExpansionTest extends MultiBrowserTest { + + @Test + public void expandCorrectlyAfterCollapse() throws IOException { + openTestURL(); + + CustomTableElement table = $(CustomTableElement.class).first(); + + // Hide col2 through UI + table.openCollapseMenu().getItem(1).click(); + compareScreen(table, "col1-col3"); + + // Hide col1 using button + ButtonElement hide1 = $(ButtonElement.class).caption("Collapse Col1") + .first(); + hide1.click(); + compareScreen(table, "col3"); + + // Show column 2 using context menu (first action) + if (BrowserUtil.isIE8(getDesiredCapabilities())) { + // IE8 can context click but the popup is off screen so it can't be + // interacted with... + ButtonElement show2 = $(ButtonElement.class).caption("Show Col2") + .first(); + show2.click(); + } else { + contextClick(table.getCell(0, 0)); + ContextMenuElement contextMenu = table.getContextMenu(); + WebElement i = contextMenu.getItem(0); + i.click(); + } + compareScreen(table, "col2-col3"); + + // Show column 1 again + ButtonElement show1 = $(ButtonElement.class).caption("Show Col1") + .first(); + show1.click(); + + compareScreen(table, "col1-col2-col3"); + } + + private void contextClick(TestBenchElement e) { + if (e.isPhantomJS()) { + JavascriptExecutor js = e.getCommandExecutor(); + String scr = "var element=arguments[0];" + + "var ev = document.createEvent('HTMLEvents');" + + "ev.initEvent('contextmenu', true, false);" + + "element.dispatchEvent(ev);"; + js.executeScript(scr, e); + } else { + e.contextClick(); + } + + } + + @Test + public void collapseEvents() { + openTestURL(); + CustomTableElement table = $(CustomTableElement.class).first(); + + // Through menu + table.openCollapseMenu().getItem(0).click(); + Assert.assertEquals("1. Collapse state for Col1 changed to true", + getLogRow(0)); + + // Through button + $(ButtonElement.class).caption("Collapse Col2").first().click(); + Assert.assertEquals("2. Collapse state for Col2 changed to true", + getLogRow(0)); + + // Show through menu + table.openCollapseMenu().getItem(1).click(); + Assert.assertEquals("3. Collapse state for Col1 changed to false", + getLogRow(0)); + + // Show through button + $(ButtonElement.class).caption("Show Col2").first().click(); + Assert.assertEquals("4. Collapse state for Col2 changed to false", + getLogRow(0)); + + } +} diff --git a/uitest/src/com/vaadin/tests/components/table/CustomTableElement.java b/uitest/src/com/vaadin/tests/components/table/CustomTableElement.java new file mode 100644 index 0000000000..f1df98fb38 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/CustomTableElement.java @@ -0,0 +1,57 @@ +/* + * 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.tests.components.table; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.elements.TableElement; +import com.vaadin.testbench.elementsbase.AbstractElement; +import com.vaadin.testbench.elementsbase.ServerClass; + +@ServerClass("com.vaadin.ui.Table") +public class CustomTableElement extends TableElement { + + public CollapseMenu openCollapseMenu() { + getCollapseMenuToggle().click(); + WebElement cm = getDriver().findElement( + By.xpath("//*[@id='PID_VAADIN_CM']")); + return wrapElement(cm, getCommandExecutor()).wrap(CollapseMenu.class); + } + + public static class CollapseMenu extends ContextMenuElement { + } + + public WebElement getCollapseMenuToggle() { + return findElement(By.className("v-table-column-selector")); + } + + public static class ContextMenuElement extends AbstractElement { + + public WebElement getItem(int index) { + return findElement(By.xpath(".//table//tr[" + (index + 1) + + "]//td/*")); + } + + } + + public ContextMenuElement getContextMenu() { + WebElement cm = getDriver().findElement(By.className("v-contextmenu")); + return wrapElement(cm, getCommandExecutor()).wrap( + ContextMenuElement.class); + } + +} diff --git a/uitest/tb2/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansion.html b/uitest/tb2/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansion.html deleted file mode 100644 index 4dc63721a1..0000000000 --- a/uitest/tb2/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansion.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - -ColumnCollapsingAndColumnExpansion - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ColumnCollapsingAndColumnExpansion
open/run/com.vaadin.tests.components.table.ColumnCollapsingAndColumnExpansion?restartApplication
screenCapturecol1-col2-col3-visible
clickvaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[0]/domChild[1]
mouseClick//tr[2]/td/span/div23,2
screenCapturecol2-hidden
enterCharactervaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VTextField[0]Col1
clickvaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]
screenCapturecol1-col2-hidden
contextmenuvaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[0]/domChild[0]
mouseClickvaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VContextMenu[0]#option011,6
enterCharactervaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VTextField[0]Col2
screenCapturecol1-hidden
enterCharactervaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VTextField[0]Col1
clickvaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]
screenCapturecol1-col2-col3-visible-again
- - -- cgit v1.2.3 From e007f059b28f686109705ad1972cfdb6adc7ec43 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 14 Jul 2015 19:33:49 +0300 Subject: Use correct classname for ValoTheme.SCROLL_INDICATOR (#14802) Use constants in Valo tests to ensure they are correct Change-Id: Ie8234a45a6f81551cdc4de2e81141aba704f4b6b --- server/src/com/vaadin/ui/themes/ValoTheme.java | 2 +- .../com/vaadin/tests/themes/valo/Accordions.java | 5 +- .../tests/themes/valo/AlignTopIconInButton.java | 3 +- .../vaadin/tests/themes/valo/ButtonsAndLinks.java | 47 ++++++------- .../com/vaadin/tests/themes/valo/CalendarTest.java | 10 +-- .../com/vaadin/tests/themes/valo/CheckBoxes.java | 25 +++---- .../com/vaadin/tests/themes/valo/ColorPickers.java | 5 +- .../com/vaadin/tests/themes/valo/ComboBoxes.java | 19 +++--- .../com/vaadin/tests/themes/valo/CommonParts.java | 79 +++++++++++----------- .../com/vaadin/tests/themes/valo/DateFields.java | 19 +++--- .../src/com/vaadin/tests/themes/valo/Dragging.java | 15 ++-- uitest/src/com/vaadin/tests/themes/valo/Forms.java | 31 +++++---- .../src/com/vaadin/tests/themes/valo/Labels.java | 27 ++++---- .../src/com/vaadin/tests/themes/valo/MenuBars.java | 31 +++++---- .../vaadin/tests/themes/valo/NativeSelects.java | 5 +- .../tests/themes/valo/NotificationStyleTest.java | 4 +- .../src/com/vaadin/tests/themes/valo/Panels.java | 29 ++++---- .../com/vaadin/tests/themes/valo/PopupViews.java | 5 +- .../src/com/vaadin/tests/themes/valo/Sliders.java | 15 ++-- .../com/vaadin/tests/themes/valo/SplitPanels.java | 9 +-- .../src/com/vaadin/tests/themes/valo/Tables.java | 51 +++++++------- .../com/vaadin/tests/themes/valo/Tabsheets.java | 9 +-- .../com/vaadin/tests/themes/valo/TextFields.java | 73 ++++++++++---------- uitest/src/com/vaadin/tests/themes/valo/Trees.java | 5 +- .../vaadin/tests/themes/valo/ValoMenuLayout.java | 5 +- .../com/vaadin/tests/themes/valo/ValoThemeUI.java | 26 +++---- 26 files changed, 289 insertions(+), 265 deletions(-) (limited to 'server/src/com/vaadin/ui') diff --git a/server/src/com/vaadin/ui/themes/ValoTheme.java b/server/src/com/vaadin/ui/themes/ValoTheme.java index 1285bf7f67..3a9986c632 100644 --- a/server/src/com/vaadin/ui/themes/ValoTheme.java +++ b/server/src/com/vaadin/ui/themes/ValoTheme.java @@ -709,7 +709,7 @@ public class ValoTheme { * area is scrolled. Suitable with the {@link #PANEL_BORDERLESS} style. Can * be combined with any other Panel style. */ - public static final String PANEL_SCROLL_INDICATOR = "scroll-indicator"; + public static final String PANEL_SCROLL_INDICATOR = "scroll-divider"; /** * Inset panel style. Can be combined with any other Panel style. diff --git a/uitest/src/com/vaadin/tests/themes/valo/Accordions.java b/uitest/src/com/vaadin/tests/themes/valo/Accordions.java index c32be01d8d..3c7ad2ad33 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/Accordions.java +++ b/uitest/src/com/vaadin/tests/themes/valo/Accordions.java @@ -21,13 +21,14 @@ import com.vaadin.ui.Accordion; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class Accordions extends VerticalLayout implements View { public Accordions() { setMargin(true); Label h1 = new Label("Accordions"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout row = new HorizontalLayout(); @@ -38,7 +39,7 @@ public class Accordions extends VerticalLayout implements View { row.addComponent(getAccordion("Normal")); Accordion ac = getAccordion("Borderless"); - ac.addStyleName("borderless"); + ac.addStyleName(ValoTheme.ACCORDION_BORDERLESS); row.addComponent(ac); } diff --git a/uitest/src/com/vaadin/tests/themes/valo/AlignTopIconInButton.java b/uitest/src/com/vaadin/tests/themes/valo/AlignTopIconInButton.java index dc257fb3ec..eb4be8e0d8 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/AlignTopIconInButton.java +++ b/uitest/src/com/vaadin/tests/themes/valo/AlignTopIconInButton.java @@ -20,6 +20,7 @@ import com.vaadin.server.ThemeResource; import com.vaadin.server.VaadinRequest; import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.ui.Button; +import com.vaadin.ui.themes.ValoTheme; /** * Test UI for image icon in button with 'icon-align-top' style. @@ -34,7 +35,7 @@ public class AlignTopIconInButton extends AbstractTestUI { Button button = new Button(); button.setIcon(new ThemeResource("../runo/icons/16/document.png")); addComponent(button); - button.addStyleName("icon-align-top"); + button.addStyleName(ValoTheme.BUTTON_ICON_ALIGN_TOP); button.setCaption("caption"); } diff --git a/uitest/src/com/vaadin/tests/themes/valo/ButtonsAndLinks.java b/uitest/src/com/vaadin/tests/themes/valo/ButtonsAndLinks.java index 9ed48896eb..ee88595ba7 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/ButtonsAndLinks.java +++ b/uitest/src/com/vaadin/tests/themes/valo/ButtonsAndLinks.java @@ -25,6 +25,7 @@ import com.vaadin.ui.Label; import com.vaadin.ui.Link; import com.vaadin.ui.NativeButton; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; /** * @@ -39,11 +40,11 @@ public class ButtonsAndLinks extends VerticalLayout implements View { setMargin(true); Label h1 = new Label("Buttons"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); @@ -55,30 +56,30 @@ public class ButtonsAndLinks extends VerticalLayout implements View { row.addComponent(button); button = new Button("Primary"); - button.addStyleName("primary"); + button.addStyleName(ValoTheme.BUTTON_PRIMARY); row.addComponent(button); button = new Button("Friendly"); - button.addStyleName("friendly"); + button.addStyleName(ValoTheme.BUTTON_FRIENDLY); row.addComponent(button); button = new Button("Danger"); - button.addStyleName("danger"); + button.addStyleName(ValoTheme.BUTTON_DANGER); row.addComponent(button); TestIcon testIcon = new TestIcon(10); button = new Button("Small"); - button.addStyleName("small"); + button.addStyleName(ValoTheme.BUTTON_SMALL); button.setIcon(testIcon.get()); row.addComponent(button); button = new Button("Large"); - button.addStyleName("large"); + button.addStyleName(ValoTheme.BUTTON_LARGE); button.setIcon(testIcon.get()); row.addComponent(button); button = new Button("Top"); - button.addStyleName("icon-align-top"); + button.addStyleName(ValoTheme.BUTTON_ICON_ALIGN_TOP); button.setIcon(testIcon.get()); row.addComponent(button); @@ -87,7 +88,7 @@ public class ButtonsAndLinks extends VerticalLayout implements View { row.addComponent(button); button = new Button("Image icon"); - button.addStyleName("icon-align-right"); + button.addStyleName(ValoTheme.BUTTON_ICON_ALIGN_RIGHT); button.setIcon(testIcon.get(true)); row.addComponent(button); @@ -97,36 +98,36 @@ public class ButtonsAndLinks extends VerticalLayout implements View { button = new Button(); button.setIcon(testIcon.get()); - button.addStyleName("icon-only"); + button.addStyleName(ValoTheme.BUTTON_ICON_ONLY); row.addComponent(button); button = new Button("Borderless"); button.setIcon(testIcon.get()); - button.addStyleName("borderless"); + button.addStyleName(ValoTheme.BUTTON_BORDERLESS); row.addComponent(button); button = new Button("Borderless, colored"); button.setIcon(testIcon.get()); - button.addStyleName("borderless-colored"); + button.addStyleName(ValoTheme.BUTTON_BORDERLESS_COLORED); row.addComponent(button); button = new Button("Quiet"); button.setIcon(testIcon.get()); - button.addStyleName("quiet"); + button.addStyleName(ValoTheme.BUTTON_QUIET); row.addComponent(button); button = new Button("Link style"); button.setIcon(testIcon.get()); - button.addStyleName("link"); + button.addStyleName(ValoTheme.BUTTON_LINK); row.addComponent(button); button = new Button("Icon on right"); button.setIcon(testIcon.get()); - button.addStyleName("icon-align-right"); + button.addStyleName(ValoTheme.BUTTON_ICON_ALIGN_RIGHT); row.addComponent(button); CssLayout group = new CssLayout(); - group.addStyleName("v-component-group"); + group.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP); row.addComponent(group); button = new Button("One"); @@ -137,22 +138,22 @@ public class ButtonsAndLinks extends VerticalLayout implements View { group.addComponent(button); button = new Button("Tiny"); - button.addStyleName("tiny"); + button.addStyleName(ValoTheme.BUTTON_TINY); row.addComponent(button); button = new Button("Huge"); - button.addStyleName("huge"); + button.addStyleName(ValoTheme.BUTTON_HUGE); row.addComponent(button); NativeButton nbutton = new NativeButton("Native"); row.addComponent(nbutton); h1 = new Label("Links"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); @@ -167,16 +168,16 @@ public class ButtonsAndLinks extends VerticalLayout implements View { row.addComponent(link); link = new Link("Small", new ExternalResource("https://vaadin.com")); - link.addStyleName("small"); + link.addStyleName(ValoTheme.LINK_SMALL); row.addComponent(link); link = new Link("Large", new ExternalResource("https://vaadin.com")); - link.addStyleName("large"); + link.addStyleName(ValoTheme.LINK_LARGE); row.addComponent(link); link = new Link(null, new ExternalResource("https://vaadin.com")); link.setIcon(testIcon.get()); - link.addStyleName("large"); + link.addStyleName(ValoTheme.LINK_LARGE); row.addComponent(link); link = new Link("Disabled", new ExternalResource("https://vaadin.com")); diff --git a/uitest/src/com/vaadin/tests/themes/valo/CalendarTest.java b/uitest/src/com/vaadin/tests/themes/valo/CalendarTest.java index 280ddf98b7..e18665f2fa 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/CalendarTest.java +++ b/uitest/src/com/vaadin/tests/themes/valo/CalendarTest.java @@ -286,7 +286,7 @@ public class CalendarTest extends GridLayout implements View { hl.addComponent(captionLabel); CssLayout group = new CssLayout(); - group.addStyleName("v-component-group"); + group.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP); group.addComponent(dayButton); group.addComponent(weekButton); group.addComponent(monthButton); @@ -922,7 +922,7 @@ public class CalendarTest extends GridLayout implements View { scheduleEventPopup.setModal(true); scheduleEventPopup.center(); - scheduleEventFieldLayout.addStyleName("light"); + scheduleEventFieldLayout.addStyleName(ValoTheme.FORMLAYOUT_LIGHT); scheduleEventFieldLayout.setMargin(false); layout.addComponent(scheduleEventFieldLayout); @@ -939,7 +939,7 @@ public class CalendarTest extends GridLayout implements View { } } }); - applyEventButton.addStyleName("primary"); + applyEventButton.addStyleName(ValoTheme.BUTTON_PRIMARY); Button cancel = new Button("Cancel", new ClickListener() { private static final long serialVersionUID = 1L; @@ -958,7 +958,7 @@ public class CalendarTest extends GridLayout implements View { deleteCalendarEvent(); } }); - deleteEventButton.addStyleName("borderless"); + deleteEventButton.addStyleName(ValoTheme.BUTTON_BORDERLESS); scheduleEventPopup.addCloseListener(new Window.CloseListener() { private static final long serialVersionUID = 1L; @@ -970,7 +970,7 @@ public class CalendarTest extends GridLayout implements View { }); HorizontalLayout buttons = new HorizontalLayout(); - buttons.addStyleName("v-window-bottom-toolbar"); + buttons.addStyleName(ValoTheme.WINDOW_BOTTOM_TOOLBAR); buttons.setWidth("100%"); buttons.setSpacing(true); buttons.addComponent(deleteEventButton); diff --git a/uitest/src/com/vaadin/tests/themes/valo/CheckBoxes.java b/uitest/src/com/vaadin/tests/themes/valo/CheckBoxes.java index 9a889b3bda..f77cf9a315 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/CheckBoxes.java +++ b/uitest/src/com/vaadin/tests/themes/valo/CheckBoxes.java @@ -23,17 +23,18 @@ import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.OptionGroup; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class CheckBoxes extends VerticalLayout implements View { public CheckBoxes() { setMargin(true); Label h1 = new Label("Check Boxes"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); @@ -72,11 +73,11 @@ public class CheckBoxes extends VerticalLayout implements View { row.addComponent(check); check = new CheckBox("Small", true); - check.addStyleName("small"); + check.addStyleName(ValoTheme.CHECKBOX_SMALL); row.addComponent(check); check = new CheckBox("Large", true); - check.addStyleName("large"); + check.addStyleName(ValoTheme.CHECKBOX_LARGE); row.addComponent(check); check = new CheckBox("Disabled", true); @@ -90,11 +91,11 @@ public class CheckBoxes extends VerticalLayout implements View { row.addComponent(check); h1 = new Label("Option Groups"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); @@ -124,7 +125,7 @@ public class CheckBoxes extends VerticalLayout implements View { row.addComponent(options); options = new OptionGroup("Choose one, small"); - options.addStyleName("small"); + options.addStyleName(ValoTheme.OPTIONGROUP_SMALL); options.setMultiSelect(false); options.addItem("Option One"); options.addItem("Option Two"); @@ -136,7 +137,7 @@ public class CheckBoxes extends VerticalLayout implements View { row.addComponent(options); options = new OptionGroup("Choose many, small"); - options.addStyleName("small"); + options.addStyleName(ValoTheme.OPTIONGROUP_SMALL); options.setMultiSelect(true); options.addItem("Option One"); options.addItem("Option Two"); @@ -148,7 +149,7 @@ public class CheckBoxes extends VerticalLayout implements View { row.addComponent(options); options = new OptionGroup("Choose one, large"); - options.addStyleName("large"); + options.addStyleName(ValoTheme.OPTIONGROUP_LARGE); options.setMultiSelect(false); options.addItem("Option One"); options.addItem("Option Two"); @@ -160,7 +161,7 @@ public class CheckBoxes extends VerticalLayout implements View { row.addComponent(options); options = new OptionGroup("Choose many, large"); - options.addStyleName("large"); + options.addStyleName(ValoTheme.OPTIONGROUP_LARGE); options.setMultiSelect(true); options.addItem("Option One"); options.addItem("Option Two"); @@ -172,7 +173,7 @@ public class CheckBoxes extends VerticalLayout implements View { row.addComponent(options); options = new OptionGroup("Horizontal items"); - options.addStyleName("horizontal"); + options.addStyleName(ValoTheme.OPTIONGROUP_HORIZONTAL); options.addItem("Option One"); two = options.addItem("Option Two, with a longer caption"); options.addItem("Option Three"); @@ -185,7 +186,7 @@ public class CheckBoxes extends VerticalLayout implements View { options = new OptionGroup("Horizontal items, explicit width"); options.setMultiSelect(true); options.setWidth("500px"); - options.addStyleName("horizontal"); + options.addStyleName(ValoTheme.OPTIONGROUP_HORIZONTAL); options.addItem("Option One"); two = options.addItem("Option Two, with a longer caption"); options.addItem("Option Three"); diff --git a/uitest/src/com/vaadin/tests/themes/valo/ColorPickers.java b/uitest/src/com/vaadin/tests/themes/valo/ColorPickers.java index a7fd60ea51..8e32b07ebd 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/ColorPickers.java +++ b/uitest/src/com/vaadin/tests/themes/valo/ColorPickers.java @@ -23,17 +23,18 @@ import com.vaadin.ui.ColorPicker; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class ColorPickers extends VerticalLayout implements View { public ColorPickers() { setMargin(true); Label h1 = new Label("Color Pickers"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); diff --git a/uitest/src/com/vaadin/tests/themes/valo/ComboBoxes.java b/uitest/src/com/vaadin/tests/themes/valo/ComboBoxes.java index 98a9cad83c..4a88d87cd2 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/ComboBoxes.java +++ b/uitest/src/com/vaadin/tests/themes/valo/ComboBoxes.java @@ -25,17 +25,18 @@ import com.vaadin.ui.CssLayout; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class ComboBoxes extends VerticalLayout implements View { public ComboBoxes() { setMargin(true); Label h1 = new Label("Combo Boxes"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); @@ -52,7 +53,7 @@ public class ComboBoxes extends VerticalLayout implements View { CssLayout group = new CssLayout(); group.setCaption("Grouped with a Button"); - group.addStyleName("v-component-group"); + group.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP); row.addComponent(group); combo = new ComboBox(); @@ -104,7 +105,7 @@ public class ComboBoxes extends VerticalLayout implements View { combo.setNullSelectionAllowed(false); combo.select("Option One"); combo.setComponentError(new UserError("Fix it, now!")); - combo.addStyleName("borderless"); + combo.addStyleName(ValoTheme.COMBOBOX_BORDERLESS); row.addComponent(combo); combo = new ComboBox("Disabled"); @@ -144,7 +145,7 @@ public class ComboBoxes extends VerticalLayout implements View { combo.setContainerDataSource(ValoThemeUI.generateContainer(200, false)); combo.setItemCaptionPropertyId(ValoThemeUI.CAPTION_PROPERTY); combo.setItemIconPropertyId(ValoThemeUI.ICON_PROPERTY); - combo.addStyleName("small"); + combo.addStyleName(ValoTheme.COMBOBOX_SMALL); row.addComponent(combo); combo = new ComboBox("Large"); @@ -152,7 +153,7 @@ public class ComboBoxes extends VerticalLayout implements View { combo.setContainerDataSource(ValoThemeUI.generateContainer(200, false)); combo.setItemCaptionPropertyId(ValoThemeUI.CAPTION_PROPERTY); combo.setItemIconPropertyId(ValoThemeUI.ICON_PROPERTY); - combo.addStyleName("large"); + combo.addStyleName(ValoTheme.COMBOBOX_LARGE); row.addComponent(combo); combo = new ComboBox("Borderless"); @@ -160,7 +161,7 @@ public class ComboBoxes extends VerticalLayout implements View { combo.addItem("Option One"); combo.addItem("Option Two"); combo.addItem("Option Three"); - combo.addStyleName("borderless"); + combo.addStyleName(ValoTheme.COMBOBOX_BORDERLESS); row.addComponent(combo); combo = new ComboBox("Tiny"); @@ -168,7 +169,7 @@ public class ComboBoxes extends VerticalLayout implements View { combo.setContainerDataSource(ValoThemeUI.generateContainer(200, false)); combo.setItemCaptionPropertyId(ValoThemeUI.CAPTION_PROPERTY); combo.setItemIconPropertyId(ValoThemeUI.ICON_PROPERTY); - combo.addStyleName("tiny"); + combo.addStyleName(ValoTheme.COMBOBOX_TINY); row.addComponent(combo); combo = new ComboBox("Huge"); @@ -176,7 +177,7 @@ public class ComboBoxes extends VerticalLayout implements View { combo.setContainerDataSource(ValoThemeUI.generateContainer(200, false)); combo.setItemCaptionPropertyId(ValoThemeUI.CAPTION_PROPERTY); combo.setItemIconPropertyId(ValoThemeUI.ICON_PROPERTY); - combo.addStyleName("huge"); + combo.addStyleName(ValoTheme.COMBOBOX_HUGE); row.addComponent(combo); } diff --git a/uitest/src/com/vaadin/tests/themes/valo/CommonParts.java b/uitest/src/com/vaadin/tests/themes/valo/CommonParts.java index 52cc43ac28..ea7c42ba2e 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/CommonParts.java +++ b/uitest/src/com/vaadin/tests/themes/valo/CommonParts.java @@ -51,13 +51,14 @@ import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Window; import com.vaadin.ui.Window.CloseEvent; import com.vaadin.ui.Window.CloseListener; +import com.vaadin.ui.themes.ValoTheme; public class CommonParts extends VerticalLayout implements View { public CommonParts() { setMargin(true); Label h1 = new Label("Common UI Elements"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); GridLayout row = new GridLayout(2, 3); @@ -83,7 +84,7 @@ public class CommonParts extends VerticalLayout implements View { CssLayout group = new CssLayout(); group.setCaption("Show the loading indicator for…"); - group.addStyleName("v-component-group"); + group.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP); content.addComponent(group); Button loading = new Button("0.8"); loading.addClickListener(new ClickListener() { @@ -127,13 +128,13 @@ public class CommonParts extends VerticalLayout implements View { Label spinnerDesc = new Label( "The theme also provides a mixin that you can use to include a spinner anywhere in your application. Below is a Label with a custom style name, for which the spinner mixin is added."); - spinnerDesc.addStyleName("small"); + spinnerDesc.addStyleName(ValoTheme.LABEL_SMALL); spinnerDesc.setCaption("Spinner"); content.addComponent(spinnerDesc); if (!ValoThemeUI.isTestMode()) { Label spinner = new Label(); - spinner.addStyleName("spinner"); + spinner.addStyleName(ValoTheme.LABEL_SPINNER); content.addComponent(spinner); } @@ -172,7 +173,7 @@ public class CommonParts extends VerticalLayout implements View { addComponent(title); description.setInputPrompt("Description for the notification"); - description.addStyleName("small"); + description.addStyleName(ValoTheme.TEXTAREA_SMALL); description.addValueChangeListener(new ValueChangeListener() { @Override public void valueChange(ValueChangeEvent event) { @@ -217,7 +218,7 @@ public class CommonParts extends VerticalLayout implements View { type.addItem("Error", typeCommand).setCheckable(true); type.addItem("System", typeCommand).setCheckable(true); addComponent(type); - type.addStyleName("small"); + type.addStyleName(ValoTheme.MENUBAR_SMALL); Command styleCommand = new Command() { @Override @@ -249,16 +250,16 @@ public class CommonParts extends VerticalLayout implements View { style.addItem("Small", styleCommand).setCheckable(true); style.addItem("Closable", styleCommand).setCheckable(true); addComponent(style); - style.addStyleName("small"); + style.addStyleName(ValoTheme.MENUBAR_SMALL); CssLayout group = new CssLayout(); group.setCaption("Fade delay"); - group.addStyleName("v-component-group"); + group.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP); addComponent(group); delay.setInputPrompt("Infinite"); - delay.addStyleName("align-right"); - delay.addStyleName("small"); + delay.addStyleName(ValoTheme.TEXTFIELD_ALIGN_RIGHT); + delay.addStyleName(ValoTheme.TEXTFIELD_SMALL); delay.setWidth("7em"); delay.addValueChangeListener(new ValueChangeListener() { @Override @@ -284,8 +285,8 @@ public class CommonParts extends VerticalLayout implements View { }); clear.setIcon(FontAwesome.TIMES_CIRCLE); clear.addStyleName("last"); - clear.addStyleName("small"); - clear.addStyleName("icon-only"); + clear.addStyleName(ValoTheme.BUTTON_SMALL); + clear.addStyleName(ValoTheme.BUTTON_ICON_ONLY); group.addComponent(clear); group.addComponent(new Label("  msec", ContentMode.HTML)); @@ -301,7 +302,7 @@ public class CommonParts extends VerticalLayout implements View { notification.show(Page.getCurrent()); } }); - pos.addStyleName("small"); + pos.addStyleName(ValoTheme.BUTTON_SMALL); grid.addComponent(pos); pos = new Button("", new ClickListener() { @@ -311,7 +312,7 @@ public class CommonParts extends VerticalLayout implements View { notification.show(Page.getCurrent()); } }); - pos.addStyleName("small"); + pos.addStyleName(ValoTheme.BUTTON_SMALL); grid.addComponent(pos); pos = new Button("", new ClickListener() { @@ -321,7 +322,7 @@ public class CommonParts extends VerticalLayout implements View { notification.show(Page.getCurrent()); } }); - pos.addStyleName("small"); + pos.addStyleName(ValoTheme.BUTTON_SMALL); grid.addComponent(pos); pos = new Button("", new ClickListener() { @@ -331,7 +332,7 @@ public class CommonParts extends VerticalLayout implements View { notification.show(Page.getCurrent()); } }); - pos.addStyleName("small"); + pos.addStyleName(ValoTheme.BUTTON_SMALL); grid.addComponent(pos); pos = new Button("", new ClickListener() { @@ -341,7 +342,7 @@ public class CommonParts extends VerticalLayout implements View { notification.show(Page.getCurrent()); } }); - pos.addStyleName("small"); + pos.addStyleName(ValoTheme.BUTTON_SMALL); grid.addComponent(pos); pos = new Button("", new ClickListener() { @@ -351,7 +352,7 @@ public class CommonParts extends VerticalLayout implements View { notification.show(Page.getCurrent()); } }); - pos.addStyleName("small"); + pos.addStyleName(ValoTheme.BUTTON_SMALL); grid.addComponent(pos); pos = new Button("", new ClickListener() { @@ -361,7 +362,7 @@ public class CommonParts extends VerticalLayout implements View { notification.show(Page.getCurrent()); } }); - pos.addStyleName("small"); + pos.addStyleName(ValoTheme.BUTTON_SMALL); grid.addComponent(pos); pos = new Button("", new ClickListener() { @@ -371,7 +372,7 @@ public class CommonParts extends VerticalLayout implements View { notification.show(Page.getCurrent()); } }); - pos.addStyleName("small"); + pos.addStyleName(ValoTheme.BUTTON_SMALL); grid.addComponent(pos); pos = new Button("", new ClickListener() { @@ -381,7 +382,7 @@ public class CommonParts extends VerticalLayout implements View { notification.show(Page.getCurrent()); } }); - pos.addStyleName("small"); + pos.addStyleName(ValoTheme.BUTTON_SMALL); grid.addComponent(pos); } @@ -397,35 +398,35 @@ public class CommonParts extends VerticalLayout implements View { { setSpacing(true); setMargin(true); - addStyleName("wrapping"); + addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); addComponent(new Label( "Try out different tooltips/descriptions by hovering over the labels.")); Label label = new Label("Simple"); - label.addStyleName("bold"); + label.addStyleName(ValoTheme.LABEL_BOLD); label.setDescription("Simple tooltip message"); addComponent(label); label = new Label("Long"); - label.addStyleName("bold"); + label.addStyleName(ValoTheme.LABEL_BOLD); label.setDescription("Long tooltip message. Inmensae subtilitatis, obscuris et malesuada fames. Salutantibus vitae elit libero, a pharetra augue."); addComponent(label); label = new Label("HTML tooltip"); - label.addStyleName("bold"); + label.addStyleName(ValoTheme.LABEL_BOLD); label.setDescription("

Ut enim ad minim veniam, quis nostrud exercitation

Morbi fringilla convallis sapien, id pulvinar odio volutpat. Vivamus sagittis lacus vel augue laoreet rutrum faucibus. Donec sed odio operae, eu vulputate felis rhoncus. At nos hinc posthac, sitientis piros Afros. Tu quoque, Brute, fili mi, nihil timor populi, nihil!

Gallia est omnis divisa in partes tres, quarum. Praeterea iter est quasdam res quas ex communi. Cum ceteris in veneratione tui montes, nascetur mus. Quam temere in vitiis, legem sancimus haerentia. Idque Caesaris facere voluntate liceret: sese habere.

"); addComponent(label); label = new Label("With an error message"); - label.addStyleName("bold"); + label.addStyleName(ValoTheme.LABEL_BOLD); label.setDescription("Simple tooltip message"); label.setComponentError(new UserError( "Something terrible has happened")); addComponent(label); label = new Label("With a long error message"); - label.addStyleName("bold"); + label.addStyleName(ValoTheme.LABEL_BOLD); label.setDescription("Simple tooltip message"); label.setComponentError(new UserError( "

Contra legem facit qui id facit quod lex prohibet Tityre, tu patulae recubans sub tegmine fagi dolor. Tityre, tu patulae recubans sub tegmine fagi dolor. Prima luce, cum quibus mons aliud consensu ab eo. Quid securi etiam tamquam eu fugiat nulla pariatur. Fabio vel iudice vincam, sunt in culpa qui officia. Nihil hic munitissimus habendi senatus locus, nihil horum?

Plura mihi bona sunt, inclinet, amari petere vellent. Integer legentibus erat a ante historiarum dapibus. Quam diu etiam furor iste tuus nos eludet? Nec dubitamus multa iter quae et nos invenerat. Quisque ut dolor gravida, placerat libero vel, euismod. Quae vero auctorem tractata ab fiducia dicuntur.

", @@ -434,7 +435,7 @@ public class CommonParts extends VerticalLayout implements View { addComponent(label); label = new Label("Error message only"); - label.addStyleName("bold"); + label.addStyleName(ValoTheme.LABEL_BOLD); label.setComponentError(new UserError( "Something terrible has happened")); addComponent(label); @@ -479,7 +480,7 @@ public class CommonParts extends VerticalLayout implements View { Alignment.TOP_RIGHT); toolbar = toolbarLayout; } - toolbar.addStyleName("v-window-top-toolbar"); + toolbar.addStyleName(ValoTheme.WINDOW_TOP_TOOLBAR); root.addComponent(toolbar); } @@ -498,7 +499,7 @@ public class CommonParts extends VerticalLayout implements View { "Another"); tabs.addTab(new Label(" ", ContentMode.HTML), "One more"); - tabs.addStyleName("padded-tabbar"); + tabs.addStyleName(ValoTheme.TABSHEET_PADDED_TABBAR); tabs.addSelectedTabChangeListener(new SelectedTabChangeListener() { @Override public void selectedTabChange( @@ -514,9 +515,9 @@ public class CommonParts extends VerticalLayout implements View { } else if (!autoHeight) { Panel p = new Panel(); p.setSizeFull(); - p.addStyleName("borderless"); + p.addStyleName(ValoTheme.PANEL_BORDERLESS); if (!toolbarVisible || !toolbarLayout) { - p.addStyleName("scroll-divider"); + p.addStyleName(ValoTheme.PANEL_SCROLL_INDICATOR); } VerticalLayout l = new VerticalLayout(); l.addComponent(new Label( @@ -538,13 +539,13 @@ public class CommonParts extends VerticalLayout implements View { HorizontalLayout footer = new HorizontalLayout(); footer.setWidth("100%"); footer.setSpacing(true); - footer.addStyleName("v-window-bottom-toolbar"); + footer.addStyleName(ValoTheme.WINDOW_BOTTOM_TOOLBAR); Label footerText = new Label("Footer text"); footerText.setSizeUndefined(); Button ok = new Button("OK"); - ok.addStyleName("primary"); + ok.addStyleName(ValoTheme.BUTTON_PRIMARY); Button cancel = new Button("Cancel"); @@ -614,7 +615,7 @@ public class CommonParts extends VerticalLayout implements View { if (selectedItem.getText() .equals("Borderless Toolbars")) { - toolbarStyle = selectedItem.isChecked() ? "borderless" + toolbarStyle = selectedItem.isChecked() ? ValoTheme.MENUBAR_BORDERLESS : null; } @@ -630,7 +631,7 @@ public class CommonParts extends VerticalLayout implements View { MenuItem option = options.addItem("Footer", optionsCommand); option.setCheckable(true); option.setChecked(true); - options.addStyleName("small"); + options.addStyleName(ValoTheme.MENUBAR_SMALL); addComponent(options); options = new MenuBar(); @@ -643,7 +644,7 @@ public class CommonParts extends VerticalLayout implements View { .setCheckable(true); options.addItem("Borderless Toolbars", optionsCommand) .setCheckable(true); - options.addStyleName("small"); + options.addStyleName(ValoTheme.MENUBAR_SMALL); addComponent(options); Command optionsCommand2 = new Command() { @@ -671,7 +672,7 @@ public class CommonParts extends VerticalLayout implements View { options.addItem("Resizable", optionsCommand2) .setCheckable(true); options.addItem("Modal", optionsCommand2).setCheckable(true); - options.addStyleName("small"); + options.addStyleName(ValoTheme.MENUBAR_SMALL); addComponent(options); final Button show = new Button("Open Window", @@ -684,7 +685,7 @@ public class CommonParts extends VerticalLayout implements View { event.getButton().setEnabled(false); } }); - show.addStyleName("primary"); + show.addStyleName(ValoTheme.BUTTON_PRIMARY); addComponent(show); final CheckBox hidden = new CheckBox("Hidden"); diff --git a/uitest/src/com/vaadin/tests/themes/valo/DateFields.java b/uitest/src/com/vaadin/tests/themes/valo/DateFields.java index 4b29f83621..9c95b7400c 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/DateFields.java +++ b/uitest/src/com/vaadin/tests/themes/valo/DateFields.java @@ -35,17 +35,18 @@ import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.InlineDateField; import com.vaadin.ui.Label; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class DateFields extends VerticalLayout implements View { public DateFields() { setMargin(true); Label h1 = new Label("Date Fields"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); @@ -61,12 +62,12 @@ public class DateFields extends VerticalLayout implements View { date = new DateField("Error, borderless"); setDate(date); date.setComponentError(new UserError("Fix it, now!")); - date.addStyleName("borderless"); + date.addStyleName(ValoTheme.DATEFIELD_BORDERLESS); row.addComponent(date); CssLayout group = new CssLayout(); group.setCaption("Grouped with a Button"); - group.addStyleName("v-component-group"); + group.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP); row.addComponent(group); final DateField date2 = new DateField(); @@ -143,19 +144,19 @@ public class DateFields extends VerticalLayout implements View { date = new DateField("Small"); setDate(date); date.setResolution(Resolution.DAY); - date.addStyleName("small"); + date.addStyleName(ValoTheme.DATEFIELD_SMALL); row.addComponent(date); date = new DateField("Large"); setDate(date); date.setResolution(Resolution.DAY); - date.addStyleName("large"); + date.addStyleName(ValoTheme.DATEFIELD_LARGE); row.addComponent(date); date = new DateField("Borderless"); setDate(date); date.setResolution(Resolution.DAY); - date.addStyleName("borderless"); + date.addStyleName(ValoTheme.DATEFIELD_BORDERLESS); row.addComponent(date); date = new DateField("Week numbers"); @@ -179,13 +180,13 @@ public class DateFields extends VerticalLayout implements View { date = new DateField("Tiny"); setDate(date); date.setResolution(Resolution.DAY); - date.addStyleName("tiny"); + date.addStyleName(ValoTheme.DATEFIELD_TINY); row.addComponent(date); date = new DateField("Huge"); setDate(date); date.setResolution(Resolution.DAY); - date.addStyleName("huge"); + date.addStyleName(ValoTheme.DATEFIELD_HUGE); row.addComponent(date); date = new InlineDateField("Date picker"); diff --git a/uitest/src/com/vaadin/tests/themes/valo/Dragging.java b/uitest/src/com/vaadin/tests/themes/valo/Dragging.java index 27bdea7d8a..8de518be23 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/Dragging.java +++ b/uitest/src/com/vaadin/tests/themes/valo/Dragging.java @@ -46,6 +46,7 @@ import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar.MenuItem; import com.vaadin.ui.Notification; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; /** * @@ -61,7 +62,7 @@ public class Dragging extends VerticalLayout implements View { setSpacing(true); Label h1 = new Label("Dragging Components"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); MenuBar options = new MenuBar(); @@ -74,9 +75,9 @@ public class Dragging extends VerticalLayout implements View { @Override public void menuSelected(MenuItem selectedItem) { if (selectedItem.isChecked()) { - sample.removeStyleName("no-vertical-drag-hints"); + sample.removeStyleName(ValoTheme.DRAG_AND_DROP_WRAPPER_NO_VERTICAL_DRAG_HINTS); } else { - sample.addStyleName("no-vertical-drag-hints"); + sample.addStyleName(ValoTheme.DRAG_AND_DROP_WRAPPER_NO_VERTICAL_DRAG_HINTS); } } }); @@ -87,9 +88,9 @@ public class Dragging extends VerticalLayout implements View { @Override public void menuSelected(MenuItem selectedItem) { if (selectedItem.isChecked()) { - sample.removeStyleName("no-horizontal-drag-hints"); + sample.removeStyleName(ValoTheme.DRAG_AND_DROP_WRAPPER_NO_HORIZONTAL_DRAG_HINTS); } else { - sample.addStyleName("no-horizontal-drag-hints"); + sample.addStyleName(ValoTheme.DRAG_AND_DROP_WRAPPER_NO_HORIZONTAL_DRAG_HINTS); } } }); @@ -100,9 +101,9 @@ public class Dragging extends VerticalLayout implements View { @Override public void menuSelected(MenuItem selectedItem) { if (selectedItem.isChecked()) { - sample.removeStyleName("no-box-drag-hints"); + sample.removeStyleName(ValoTheme.DRAG_AND_DROP_WRAPPER_NO_BOX_DRAG_HINTS); } else { - sample.addStyleName("no-box-drag-hints"); + sample.addStyleName(ValoTheme.DRAG_AND_DROP_WRAPPER_NO_BOX_DRAG_HINTS); } } }); diff --git a/uitest/src/com/vaadin/tests/themes/valo/Forms.java b/uitest/src/com/vaadin/tests/themes/valo/Forms.java index 90a6c51496..91fe473d60 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/Forms.java +++ b/uitest/src/com/vaadin/tests/themes/valo/Forms.java @@ -36,6 +36,7 @@ import com.vaadin.ui.RichTextArea; import com.vaadin.ui.TextArea; import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; /** * @@ -48,18 +49,18 @@ public class Forms extends VerticalLayout implements View { setMargin(true); Label title = new Label("Forms"); - title.addStyleName("h1"); + title.addStyleName(ValoTheme.LABEL_H1); addComponent(title); final FormLayout form = new FormLayout(); form.setMargin(false); form.setWidth("800px"); - form.addStyleName("light"); + form.addStyleName(ValoTheme.FORMLAYOUT_LIGHT); addComponent(form); Label section = new Label("Personal Info"); - section.addStyleName("h2"); - section.addStyleName("colored"); + section.addStyleName(ValoTheme.LABEL_H2); + section.addStyleName(ValoTheme.LABEL_COLORED); form.addComponent(section); StringGenerator sg = new StringGenerator(); @@ -81,12 +82,12 @@ public class Forms extends VerticalLayout implements View { sex.addItem("Female"); sex.addItem("Male"); sex.select("Male"); - sex.addStyleName("horizontal"); + sex.addStyleName(ValoTheme.OPTIONGROUP_HORIZONTAL); form.addComponent(sex); section = new Label("Contact Info"); - section.addStyleName("h3"); - section.addStyleName("colored"); + section.addStyleName(ValoTheme.LABEL_H3); + section.addStyleName(ValoTheme.LABEL_COLORED); form.addComponent(section); TextField email = new TextField("Email"); @@ -120,14 +121,14 @@ public class Forms extends VerticalLayout implements View { period.addItem("Montly"); period.setNullSelectionAllowed(false); period.select("Weekly"); - period.addStyleName("small"); + period.addStyleName(ValoTheme.COMBOBOX_SMALL); period.setWidth("10em"); wrap.addComponent(period); form.addComponent(wrap); section = new Label("Additional Info"); - section.addStyleName("h4"); - section.addStyleName("colored"); + section.addStyleName(ValoTheme.LABEL_H4); + section.addStyleName(ValoTheme.LABEL_COLORED); form.addComponent(section); TextField website = new TextField("Website"); @@ -156,15 +157,15 @@ public class Forms extends VerticalLayout implements View { if (readOnly) { bio.setReadOnly(false); form.setReadOnly(false); - form.removeStyleName("light"); + form.removeStyleName(ValoTheme.FORMLAYOUT_LIGHT); event.getButton().setCaption("Save"); - event.getButton().addStyleName("primary"); + event.getButton().addStyleName(ValoTheme.BUTTON_PRIMARY); } else { bio.setReadOnly(true); form.setReadOnly(true); - form.addStyleName("light"); + form.addStyleName(ValoTheme.FORMLAYOUT_LIGHT); event.getButton().setCaption("Edit"); - event.getButton().removeStyleName("primary"); + event.getButton().removeStyleName(ValoTheme.BUTTON_PRIMARY); } } }); @@ -177,7 +178,7 @@ public class Forms extends VerticalLayout implements View { footer.addComponent(edit); Label lastModified = new Label("Last modified by you a minute ago"); - lastModified.addStyleName("light"); + lastModified.addStyleName(ValoTheme.LABEL_LIGHT); footer.addComponent(lastModified); } diff --git a/uitest/src/com/vaadin/tests/themes/valo/Labels.java b/uitest/src/com/vaadin/tests/themes/valo/Labels.java index b5bab3a1d3..9954979d50 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/Labels.java +++ b/uitest/src/com/vaadin/tests/themes/valo/Labels.java @@ -23,6 +23,7 @@ import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Panel; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; /** * @@ -34,7 +35,7 @@ public class Labels extends VerticalLayout implements View { setMargin(true); Label h1 = new Label("Labels"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout split = new HorizontalLayout(); @@ -46,16 +47,16 @@ public class Labels extends VerticalLayout implements View { split.addComponent(left); Label huge = new Label("Huge type for display text."); - huge.addStyleName("huge"); + huge.addStyleName(ValoTheme.LABEL_HUGE); left.addComponent(huge); Label large = new Label( "Large type for introductory text. Etiam at risus et justo dignissim congue. Donec congue lacinia dui, a porttitor lectus condimentum laoreet. Nunc eu."); - large.addStyleName("large"); + large.addStyleName(ValoTheme.LABEL_LARGE); left.addComponent(large); Label h2 = new Label("Subtitle"); - h2.addStyleName("h2"); + h2.addStyleName(ValoTheme.LABEL_H2); left.addComponent(h2); Label normal = new Label( @@ -64,20 +65,20 @@ public class Labels extends VerticalLayout implements View { left.addComponent(normal); Label h3 = new Label("Small Title"); - h3.addStyleName("h3"); + h3.addStyleName(ValoTheme.LABEL_H3); left.addComponent(h3); Label small = new Label( "Small type for additional text. Etiam at risus et justo dignissim congue. Donec congue lacinia dui, a porttitor lectus condimentum laoreet. Nunc eu."); - small.addStyleName("small"); + small.addStyleName(ValoTheme.LABEL_SMALL); left.addComponent(small); Label tiny = new Label("Tiny type for minor text."); - tiny.addStyleName("tiny"); + tiny.addStyleName(ValoTheme.LABEL_TINY); left.addComponent(tiny); Label h4 = new Label("Section Title"); - h4.addStyleName("h4"); + h4.addStyleName(ValoTheme.LABEL_H4); left.addComponent(h4); normal = new Label( @@ -94,25 +95,25 @@ public class Labels extends VerticalLayout implements View { Label label = new Label( "Bold type for prominent text. Etiam at risus et justo dignissim congue. Donec congue lacinia dui, a porttitor lectus condimentum laoreet. Nunc eu."); - label.addStyleName("bold"); + label.addStyleName(ValoTheme.LABEL_BOLD); right.addComponent(label); label = new Label( "Light type for subtle text. Etiam at risus et justo dignissim congue. Donec congue lacinia dui, a porttitor lectus condimentum laoreet. Nunc eu."); - label.addStyleName("light"); + label.addStyleName(ValoTheme.LABEL_LIGHT); right.addComponent(label); label = new Label( "Colored type for highlighted text. Etiam at risus et justo dignissim congue. Donec congue lacinia dui, a porttitor lectus condimentum laoreet. Nunc eu."); - label.addStyleName("colored"); + label.addStyleName(ValoTheme.LABEL_COLORED); right.addComponent(label); label = new Label("A label for success"); - label.addStyleName("success"); + label.addStyleName(ValoTheme.LABEL_SUCCESS); right.addComponent(label); label = new Label("A label for failure"); - label.addStyleName("failure"); + label.addStyleName(ValoTheme.LABEL_FAILURE); right.addComponent(label); } diff --git a/uitest/src/com/vaadin/tests/themes/valo/MenuBars.java b/uitest/src/com/vaadin/tests/themes/valo/MenuBars.java index 4a0130931e..fc74166b29 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/MenuBars.java +++ b/uitest/src/com/vaadin/tests/themes/valo/MenuBars.java @@ -25,6 +25,7 @@ import com.vaadin.ui.MenuBar.Command; import com.vaadin.ui.MenuBar.MenuItem; import com.vaadin.ui.Notification; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class MenuBars extends VerticalLayout implements View { public MenuBars() { @@ -32,7 +33,7 @@ public class MenuBars extends VerticalLayout implements View { setSpacing(true); Label h1 = new Label("Menu Bars"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); MenuBar menuBar = getMenuBar(); @@ -41,37 +42,37 @@ public class MenuBars extends VerticalLayout implements View { menuBar = getMenuBar(); menuBar.setCaption("Small style"); - menuBar.addStyleName("small"); + menuBar.addStyleName(ValoTheme.MENUBAR_SMALL); addComponent(menuBar); menuBar = getMenuBar(); menuBar.setCaption("Borderless style"); - menuBar.addStyleName("borderless"); + menuBar.addStyleName(ValoTheme.MENUBAR_BORDERLESS); addComponent(menuBar); menuBar = getMenuBar(); menuBar.setCaption("Small borderless style"); - menuBar.addStyleName("borderless"); - menuBar.addStyleName("small"); + menuBar.addStyleName(ValoTheme.MENUBAR_BORDERLESS); + menuBar.addStyleName(ValoTheme.MENUBAR_SMALL); addComponent(menuBar); Label h2 = new Label("Drop Down Button"); - h2.addStyleName("h2"); + h2.addStyleName(ValoTheme.LABEL_H2); addComponent(h2); HorizontalLayout wrap = new HorizontalLayout(); - wrap.addStyleName("wrapping"); + wrap.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); wrap.setSpacing(true); addComponent(wrap); wrap.addComponent(getMenuButton("Normal", false)); MenuBar split = getMenuButton("Small", false); - split.addStyleName("small"); + split.addStyleName(ValoTheme.MENUBAR_SMALL); wrap.addComponent(split); split = getMenuButton("Borderless", false); - split.addStyleName("borderless"); + split.addStyleName(ValoTheme.MENUBAR_BORDERLESS); wrap.addComponent(split); split = getMenuButton("Themed", false); @@ -80,26 +81,26 @@ public class MenuBars extends VerticalLayout implements View { split = getMenuButton("Small", false); split.addStyleName("color1"); - split.addStyleName("small"); + split.addStyleName(ValoTheme.MENUBAR_SMALL); wrap.addComponent(split); h2 = new Label("Split Button"); - h2.addStyleName("h2"); + h2.addStyleName(ValoTheme.LABEL_H2); addComponent(h2); wrap = new HorizontalLayout(); - wrap.addStyleName("wrapping"); + wrap.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); wrap.setSpacing(true); addComponent(wrap); wrap.addComponent(getMenuButton("Normal", true)); split = getMenuButton("Small", true); - split.addStyleName("small"); + split.addStyleName(ValoTheme.MENUBAR_SMALL); wrap.addComponent(split); split = getMenuButton("Borderless", true); - split.addStyleName("borderless"); + split.addStyleName(ValoTheme.MENUBAR_BORDERLESS); wrap.addComponent(split); split = getMenuButton("Themed", true); @@ -108,7 +109,7 @@ public class MenuBars extends VerticalLayout implements View { split = getMenuButton("Small", true); split.addStyleName("color1"); - split.addStyleName("small"); + split.addStyleName(ValoTheme.MENUBAR_SMALL); wrap.addComponent(split); } diff --git a/uitest/src/com/vaadin/tests/themes/valo/NativeSelects.java b/uitest/src/com/vaadin/tests/themes/valo/NativeSelects.java index 284f7c8d6e..e9c1c78049 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/NativeSelects.java +++ b/uitest/src/com/vaadin/tests/themes/valo/NativeSelects.java @@ -23,17 +23,18 @@ import com.vaadin.ui.ListSelect; import com.vaadin.ui.NativeSelect; import com.vaadin.ui.TwinColSelect; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class NativeSelects extends VerticalLayout implements View { public NativeSelects() { setMargin(true); Label h1 = new Label("Selects"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); diff --git a/uitest/src/com/vaadin/tests/themes/valo/NotificationStyleTest.java b/uitest/src/com/vaadin/tests/themes/valo/NotificationStyleTest.java index 7adae9ce65..5f9542b6f3 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/NotificationStyleTest.java +++ b/uitest/src/com/vaadin/tests/themes/valo/NotificationStyleTest.java @@ -27,6 +27,7 @@ import org.openqa.selenium.support.ui.ExpectedCondition; import com.vaadin.testbench.elements.ButtonElement; import com.vaadin.tests.tb3.MultiBrowserTest; +import com.vaadin.ui.themes.ValoTheme; /** * Test for H1 and P elements styles in Notifications. @@ -45,7 +46,8 @@ public class NotificationStyleTest extends MultiBrowserTest { waitUntil(notificationPresentCondition(), 2); WebElement notification = findElement(By.className("v-Notification")); - List headers = notification.findElements(By.tagName("h1")); + List headers = notification.findElements(By + .tagName(ValoTheme.LABEL_H1)); String textAlign = headers.get(0).getCssValue("text-align"); String textAlignInnerHeader = headers.get(1).getCssValue("text-align"); Assert.assertNotEquals("Styles for notification defined h1 tag " diff --git a/uitest/src/com/vaadin/tests/themes/valo/Panels.java b/uitest/src/com/vaadin/tests/themes/valo/Panels.java index 8a17244693..d98daf7b05 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/Panels.java +++ b/uitest/src/com/vaadin/tests/themes/valo/Panels.java @@ -27,17 +27,18 @@ import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar.MenuItem; import com.vaadin.ui.Panel; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class Panels extends VerticalLayout implements View { public Panels() { setMargin(true); Label h1 = new Label("Panels & Layout panels"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); TestIcon testIcon = new TestIcon(60); @@ -74,33 +75,33 @@ public class Panels extends VerticalLayout implements View { panel = new Panel("Borderless style"); panel.setIcon(testIcon.get()); - panel.addStyleName("borderless"); + panel.addStyleName(ValoTheme.PANEL_BORDERLESS); panel.setContent(panelContent()); row.addComponent(panel); panel = new Panel("Borderless + scroll divider"); panel.setIcon(testIcon.get()); - panel.addStyleName("borderless"); - panel.addStyleName("scroll-divider"); + panel.addStyleName(ValoTheme.PANEL_BORDERLESS); + panel.addStyleName(ValoTheme.PANEL_SCROLL_INDICATOR); panel.setContent(panelContentScroll()); panel.setHeight("17em"); row.addComponent(panel); panel = new Panel("Well style"); panel.setIcon(testIcon.get()); - panel.addStyleName("well"); + panel.addStyleName(ValoTheme.PANEL_WELL); panel.setContent(panelContent()); row.addComponent(panel); CssLayout layout = new CssLayout(); layout.setIcon(testIcon.get()); layout.setCaption("Panel style layout"); - layout.addStyleName("card"); + layout.addStyleName(ValoTheme.LAYOUT_CARD); layout.addComponent(panelContent()); row.addComponent(layout); layout = new CssLayout(); - layout.addStyleName("card"); + layout.addStyleName(ValoTheme.LAYOUT_CARD); row.addComponent(layout); HorizontalLayout panelCaption = new HorizontalLayout(); panelCaption.addStyleName("v-panel-caption"); @@ -112,13 +113,13 @@ public class Panels extends VerticalLayout implements View { Button action = new Button(); action.setIcon(FontAwesome.PENCIL); - action.addStyleName("borderless-colored"); - action.addStyleName("small"); - action.addStyleName("icon-only"); + action.addStyleName(ValoTheme.BUTTON_BORDERLESS_COLORED); + action.addStyleName(ValoTheme.BUTTON_SMALL); + action.addStyleName(ValoTheme.BUTTON_ICON_ONLY); panelCaption.addComponent(action); MenuBar dropdown = new MenuBar(); - dropdown.addStyleName("borderless"); - dropdown.addStyleName("small"); + dropdown.addStyleName(ValoTheme.MENUBAR_BORDERLESS); + dropdown.addStyleName(ValoTheme.MENUBAR_SMALL); MenuItem addItem = dropdown.addItem("", FontAwesome.CHEVRON_DOWN, null); addItem.setStyleName("icon-only"); addItem.addItem("Settings", null); @@ -134,7 +135,7 @@ public class Panels extends VerticalLayout implements View { layout = new CssLayout(); layout.setIcon(testIcon.get()); layout.setCaption("Well style layout"); - layout.addStyleName("well"); + layout.addStyleName(ValoTheme.LAYOUT_WELL); layout.addComponent(panelContent()); row.addComponent(layout); } diff --git a/uitest/src/com/vaadin/tests/themes/valo/PopupViews.java b/uitest/src/com/vaadin/tests/themes/valo/PopupViews.java index c15270400c..58988c06d6 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/PopupViews.java +++ b/uitest/src/com/vaadin/tests/themes/valo/PopupViews.java @@ -24,17 +24,18 @@ import com.vaadin.ui.Label; import com.vaadin.ui.PopupView; import com.vaadin.ui.PopupView.Content; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class PopupViews extends VerticalLayout implements View { public PopupViews() { setMargin(true); Label h1 = new Label("Popup Views"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); diff --git a/uitest/src/com/vaadin/tests/themes/valo/Sliders.java b/uitest/src/com/vaadin/tests/themes/valo/Sliders.java index 8ed846e39f..9642cb5ccf 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/Sliders.java +++ b/uitest/src/com/vaadin/tests/themes/valo/Sliders.java @@ -23,17 +23,18 @@ import com.vaadin.ui.Label; import com.vaadin.ui.ProgressBar; import com.vaadin.ui.Slider; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class Sliders extends VerticalLayout implements View { public Sliders() { setMargin(true); Label h1 = new Label("Sliders"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); @@ -67,7 +68,7 @@ public class Sliders extends VerticalLayout implements View { slider = new Slider("No indicator"); slider.setValue(50.0); slider.setWidth("200px"); - slider.addStyleName("no-indicator"); + slider.addStyleName(ValoTheme.SLIDER_NO_INDICATOR); row.addComponent(slider); slider = new Slider("With ticks (not in IE8 & IE9)"); @@ -119,7 +120,7 @@ public class Sliders extends VerticalLayout implements View { slider = new Slider("No indicator"); slider.setValue(50.0); slider.setHeight("200px"); - slider.addStyleName("no-indicator"); + slider.addStyleName(ValoTheme.SLIDER_NO_INDICATOR); slider.setOrientation(SliderOrientation.VERTICAL); row.addComponent(slider); @@ -137,11 +138,11 @@ public class Sliders extends VerticalLayout implements View { row.addComponent(slider); h1 = new Label("Progress Bars"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); @@ -154,7 +155,7 @@ public class Sliders extends VerticalLayout implements View { pb2 = new ProgressBar(); pb2.setCaption("Point style"); pb2.setWidth("300px"); - pb2.addStyleName("point"); + pb2.addStyleName(ValoTheme.PROGRESSBAR_POINT); // pb2.setValue(0.6f); row.addComponent(pb2); diff --git a/uitest/src/com/vaadin/tests/themes/valo/SplitPanels.java b/uitest/src/com/vaadin/tests/themes/valo/SplitPanels.java index 9a6d86ae04..4983bc5813 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/SplitPanels.java +++ b/uitest/src/com/vaadin/tests/themes/valo/SplitPanels.java @@ -23,20 +23,21 @@ import com.vaadin.ui.HorizontalSplitPanel; import com.vaadin.ui.Label; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.VerticalSplitPanel; +import com.vaadin.ui.themes.ValoTheme; public class SplitPanels extends VerticalLayout implements View { public SplitPanels() { setMargin(true); Label h1 = new Label("Split Panels"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); addComponent(new Label( "Outlines are just to show the areas of the SplitPanels. They are not part of the actual component style.")); HorizontalLayout row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); row.setMargin(new MarginInfo(true, false, false, false)); addComponent(row); @@ -61,7 +62,7 @@ public class SplitPanels extends VerticalLayout implements View { sp.setCaption("Large style"); sp.setWidth("300px"); sp.setHeight("200px"); - sp.addStyleName("large"); + sp.addStyleName(ValoTheme.SPLITPANEL_LARGE); sp.setFirstComponent(getContent()); sp.setSecondComponent(getContent()); row.addComponent(sp); @@ -70,7 +71,7 @@ public class SplitPanels extends VerticalLayout implements View { sp2.setCaption("Large style"); sp2.setWidth("300px"); sp2.setHeight("200px"); - sp2.addStyleName("large"); + sp2.addStyleName(ValoTheme.SPLITPANEL_LARGE); sp2.setFirstComponent(getContent()); sp2.setSecondComponent(getContent()); row.addComponent(sp2); diff --git a/uitest/src/com/vaadin/tests/themes/valo/Tables.java b/uitest/src/com/vaadin/tests/themes/valo/Tables.java index fb6638ee7d..071e6b746a 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/Tables.java +++ b/uitest/src/com/vaadin/tests/themes/valo/Tables.java @@ -42,6 +42,7 @@ import com.vaadin.ui.Table.TableDragMode; import com.vaadin.ui.TextField; import com.vaadin.ui.TreeTable; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class Tables extends VerticalLayout implements View { @@ -72,11 +73,11 @@ public class Tables extends VerticalLayout implements View { setSpacing(true); Label h1 = new Label("Tables"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout wrap = new HorizontalLayout(); - wrap.addStyleName("wrapping"); + wrap.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); wrap.setSpacing(true); addComponent(wrap); @@ -189,9 +190,9 @@ public class Tables extends VerticalLayout implements View { Object columnId) { TextField tf = new TextField(); tf.setInputPrompt("Type here…"); - // tf.addStyleName("compact"); + // tf.addStyleName(ValoTheme.TABLE_COMPACT); if ((Integer) itemId % 2 == 0) { - tf.addStyleName("borderless"); + tf.addStyleName(ValoTheme.TABLE_BORDERLESS); } return tf; } @@ -203,9 +204,9 @@ public class Tables extends VerticalLayout implements View { public Object generateCell(Table source, Object itemId, Object columnId) { DateField tf = new DateField(); - tf.addStyleName("compact"); + tf.addStyleName(ValoTheme.TABLE_COMPACT); if ((Integer) itemId % 2 == 0) { - tf.addStyleName("borderless"); + tf.addStyleName(ValoTheme.DATEFIELD_BORDERLESS); } return tf; } @@ -218,9 +219,9 @@ public class Tables extends VerticalLayout implements View { Object columnId) { ComboBox tf = new ComboBox(); tf.setInputPrompt("Select"); - tf.addStyleName("compact"); + tf.addStyleName(ValoTheme.TABLE_COMPACT); if ((Integer) itemId % 2 == 0) { - tf.addStyleName("borderless"); + tf.addStyleName(ValoTheme.DATEFIELD_BORDERLESS); } return tf; } @@ -232,7 +233,7 @@ public class Tables extends VerticalLayout implements View { public Object generateCell(Table source, Object itemId, Object columnId) { Button b = new Button("Button"); - b.addStyleName("small"); + b.addStyleName(ValoTheme.BUTTON_SMALL); return b; } }); @@ -244,7 +245,7 @@ public class Tables extends VerticalLayout implements View { Object columnId) { Label label = new Label("Label component"); label.setSizeUndefined(); - label.addStyleName("bold"); + label.addStyleName(ValoTheme.LABEL_BOLD); return label; } }); @@ -267,7 +268,7 @@ public class Tables extends VerticalLayout implements View { OptionGroup op = new OptionGroup(); op.addItem("Male"); op.addItem("Female"); - op.addStyleName("horizontal"); + op.addStyleName(ValoTheme.OPTIONGROUP_HORIZONTAL); return op; } }); @@ -321,45 +322,45 @@ public class Tables extends VerticalLayout implements View { expandRatios ? 1.0f : 0); if (!stripes) { - table.addStyleName("no-stripes"); + table.addStyleName(ValoTheme.TABLE_NO_STRIPES); } else { - table.removeStyleName("no-stripes"); + table.removeStyleName(ValoTheme.TABLE_NO_STRIPES); } if (!verticalLines) { - table.addStyleName("no-vertical-lines"); + table.addStyleName(ValoTheme.TABLE_NO_VERTICAL_LINES); } else { - table.removeStyleName("no-vertical-lines"); + table.removeStyleName(ValoTheme.TABLE_NO_VERTICAL_LINES); } if (!horizontalLines) { - table.addStyleName("no-horizontal-lines"); + table.addStyleName(ValoTheme.TABLE_NO_HORIZONTAL_LINES); } else { - table.removeStyleName("no-horizontal-lines"); + table.removeStyleName(ValoTheme.TABLE_NO_HORIZONTAL_LINES); } if (borderless) { - table.addStyleName("borderless"); + table.addStyleName(ValoTheme.TABLE_BORDERLESS); } else { - table.removeStyleName("borderless"); + table.removeStyleName(ValoTheme.TABLE_BORDERLESS); } if (!headers) { - table.addStyleName("no-header"); + table.addStyleName(ValoTheme.TABLE_NO_HEADER); } else { - table.removeStyleName("no-header"); + table.removeStyleName(ValoTheme.TABLE_NO_HEADER); } if (compact) { - table.addStyleName("compact"); + table.addStyleName(ValoTheme.TABLE_COMPACT); } else { - table.removeStyleName("compact"); + table.removeStyleName(ValoTheme.TABLE_COMPACT); } if (small) { - table.addStyleName("small"); + table.addStyleName(ValoTheme.TABLE_SMALL); } else { - table.removeStyleName("small"); + table.removeStyleName(ValoTheme.TABLE_SMALL); } if (!rowIndex && !rowCaption && rowIcon) { diff --git a/uitest/src/com/vaadin/tests/themes/valo/Tabsheets.java b/uitest/src/com/vaadin/tests/themes/valo/Tabsheets.java index 5e77292471..421da5ffe7 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/Tabsheets.java +++ b/uitest/src/com/vaadin/tests/themes/valo/Tabsheets.java @@ -28,6 +28,7 @@ import com.vaadin.ui.TabSheet.SelectedTabChangeEvent; import com.vaadin.ui.TabSheet.SelectedTabChangeListener; import com.vaadin.ui.TabSheet.Tab; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class Tabsheets extends VerticalLayout implements View { @@ -37,12 +38,12 @@ public class Tabsheets extends VerticalLayout implements View { setMargin(true); Label h1 = new Label("Tabs"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout wrap = new HorizontalLayout(); wrap.setSpacing(true); - wrap.addStyleName("wrapping"); + wrap.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); addComponent(wrap); final CheckBox closable = new CheckBox("Closable"); @@ -66,12 +67,12 @@ public class Tabsheets extends VerticalLayout implements View { wrap.addComponent(disable); Label h3 = new Label("Additional Styles"); - h3.addStyleName("h3"); + h3.addStyleName(ValoTheme.LABEL_H3); addComponent(h3); wrap = new HorizontalLayout(); wrap.setSpacing(true); - wrap.addStyleName("wrapping"); + wrap.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); wrap.setMargin(new MarginInfo(false, false, true, false)); addComponent(wrap); diff --git a/uitest/src/com/vaadin/tests/themes/valo/TextFields.java b/uitest/src/com/vaadin/tests/themes/valo/TextFields.java index 347a683673..be6e430b23 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/TextFields.java +++ b/uitest/src/com/vaadin/tests/themes/valo/TextFields.java @@ -28,6 +28,7 @@ import com.vaadin.ui.RichTextArea; import com.vaadin.ui.TextArea; import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class TextFields extends VerticalLayout implements View { private TestIcon testIcon = new TestIcon(140); @@ -36,11 +37,11 @@ public class TextFields extends VerticalLayout implements View { setMargin(true); Label h1 = new Label("Text Fields"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); @@ -72,7 +73,7 @@ public class TextFields extends VerticalLayout implements View { tf = new TextField("Error, borderless"); tf.setValue("Something’s wrong"); tf.setComponentError(new UserError("Fix it, now!")); - tf.addStyleName("borderless"); + tf.addStyleName(ValoTheme.TEXTFIELD_BORDERLESS); row.addComponent(tf); tf = new TextField("Read-only"); @@ -83,121 +84,121 @@ public class TextFields extends VerticalLayout implements View { tf = new TextField("Small"); tf.setValue("Field value"); - tf.addStyleName("small"); + tf.addStyleName(ValoTheme.TEXTFIELD_SMALL); row.addComponent(tf); tf = new TextField("Large"); tf.setValue("Field value"); - tf.addStyleName("large"); + tf.addStyleName(ValoTheme.TEXTFIELD_LARGE); tf.setIcon(testIcon.get(true)); row.addComponent(tf); tf = new TextField("Icon inside"); tf.setInputPrompt("Ooh, an icon"); - tf.addStyleName("inline-icon"); + tf.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); tf.setIcon(testIcon.get()); row.addComponent(tf); tf = new TextField("Large, Icon inside"); tf.setInputPrompt("Ooh, an icon"); - tf.addStyleName("large"); - tf.addStyleName("inline-icon"); + tf.addStyleName(ValoTheme.TEXTFIELD_LARGE); + tf.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); tf.setIcon(testIcon.get()); row.addComponent(tf); tf = new TextField("Small, Icon inside"); tf.setInputPrompt("Ooh, an icon"); - tf.addStyleName("small"); - tf.addStyleName("inline-icon"); + tf.addStyleName(ValoTheme.TEXTFIELD_SMALL); + tf.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); tf.setIcon(testIcon.get()); row.addComponent(tf); tf = new TextField("16px supported by default"); tf.setInputPrompt("Image icon"); - tf.addStyleName("inline-icon"); + tf.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); tf.setIcon(testIcon.get(true, 16)); row.addComponent(tf); tf = new TextField(); tf.setValue("Font, no caption"); - tf.addStyleName("inline-icon"); + tf.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); tf.setIcon(testIcon.get()); row.addComponent(tf); tf = new TextField(); tf.setValue("Image, no caption"); - tf.addStyleName("inline-icon"); + tf.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); tf.setIcon(testIcon.get(true, 16)); row.addComponent(tf); CssLayout group = new CssLayout(); - group.addStyleName("v-component-group"); + group.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP); row.addComponent(group); tf = new TextField(); tf.setInputPrompt("Grouped with a button"); - tf.addStyleName("inline-icon"); + tf.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); tf.setIcon(testIcon.get()); tf.setWidth("260px"); group.addComponent(tf); Button button = new Button("Do It"); - // button.addStyleName("primary"); + // button.addStyleName(ValoTheme.BUTTON_PRIMARY); group.addComponent(button); tf = new TextField("Borderless"); tf.setInputPrompt("Write here…"); - tf.addStyleName("inline-icon"); - tf.addStyleName("borderless"); + tf.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); + tf.addStyleName(ValoTheme.TEXTFIELD_BORDERLESS); tf.setIcon(testIcon.get()); row.addComponent(tf); tf = new TextField("Right-aligned"); tf.setValue("1,234"); - tf.addStyleName("align-right"); + tf.addStyleName(ValoTheme.TEXTFIELD_ALIGN_RIGHT); row.addComponent(tf); tf = new TextField("Centered"); tf.setInputPrompt("Guess what?"); - tf.addStyleName("align-center"); + tf.addStyleName(ValoTheme.TEXTFIELD_ALIGN_CENTER); row.addComponent(tf); PasswordField pwf = new PasswordField("Password"); pwf.setInputPrompt("Secret words"); - pwf.addStyleName("inline-icon"); + pwf.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); pwf.setIcon(FontAwesome.LOCK); row.addComponent(pwf); pwf = new PasswordField("Password, right-aligned"); pwf.setInputPrompt("Secret words"); - pwf.addStyleName("inline-icon"); - pwf.addStyleName("align-right"); + pwf.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); + pwf.addStyleName(ValoTheme.TEXTFIELD_ALIGN_RIGHT); pwf.setIcon(FontAwesome.LOCK); row.addComponent(pwf); pwf = new PasswordField("Password, centered"); pwf.setInputPrompt("Secret words"); - pwf.addStyleName("inline-icon"); - pwf.addStyleName("align-center"); + pwf.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); + pwf.addStyleName(ValoTheme.TEXTFIELD_ALIGN_CENTER); pwf.setIcon(FontAwesome.LOCK); row.addComponent(pwf); tf = new TextField("Tiny"); tf.setValue("Field value"); - tf.addStyleName("tiny"); + tf.addStyleName(ValoTheme.TEXTFIELD_TINY); row.addComponent(tf); tf = new TextField("Huge"); tf.setValue("Field value"); - tf.addStyleName("huge"); + tf.addStyleName(ValoTheme.TEXTFIELD_HUGE); row.addComponent(tf); h1 = new Label("Text Areas"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); @@ -228,37 +229,37 @@ public class TextFields extends VerticalLayout implements View { row.addComponent(ta); ta = new TextArea("Small"); - ta.addStyleName("small"); + ta.addStyleName(ValoTheme.TEXTAREA_SMALL); ta.setInputPrompt("Write your comment…"); row.addComponent(ta); ta = new TextArea("Large"); - ta.addStyleName("large"); + ta.addStyleName(ValoTheme.TEXTAREA_LARGE); ta.setInputPrompt("Write your comment…"); row.addComponent(ta); ta = new TextArea("Borderless"); - ta.addStyleName("borderless"); + ta.addStyleName(ValoTheme.TEXTAREA_BORDERLESS); ta.setInputPrompt("Write your comment…"); row.addComponent(ta); ta = new TextArea("Right-aligned"); - ta.addStyleName("align-right"); + ta.addStyleName(ValoTheme.TEXTAREA_ALIGN_RIGHT); ta.setValue("Field value, spanning multiple lines of text"); row.addComponent(ta); ta = new TextArea("Centered"); - ta.addStyleName("align-center"); + ta.addStyleName(ValoTheme.TEXTAREA_ALIGN_CENTER); ta.setValue("Field value, spanning multiple lines of text"); row.addComponent(ta); ta = new TextArea("Tiny"); - ta.addStyleName("tiny"); + ta.addStyleName(ValoTheme.TEXTAREA_TINY); ta.setInputPrompt("Write your comment…"); row.addComponent(ta); ta = new TextArea("Huge"); - ta.addStyleName("huge"); + ta.addStyleName(ValoTheme.TEXTAREA_HUGE); ta.setInputPrompt("Write your comment…"); row.addComponent(ta); diff --git a/uitest/src/com/vaadin/tests/themes/valo/Trees.java b/uitest/src/com/vaadin/tests/themes/valo/Trees.java index cb5657660a..02846d8921 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/Trees.java +++ b/uitest/src/com/vaadin/tests/themes/valo/Trees.java @@ -28,17 +28,18 @@ import com.vaadin.ui.Notification; import com.vaadin.ui.Tree; import com.vaadin.ui.Tree.TreeDragMode; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; public class Trees extends VerticalLayout implements View { public Trees() { setMargin(true); Label h1 = new Label("Trees"); - h1.addStyleName("h1"); + h1.addStyleName(ValoTheme.LABEL_H1); addComponent(h1); HorizontalLayout row = new HorizontalLayout(); - row.addStyleName("wrapping"); + row.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING); row.setSpacing(true); addComponent(row); diff --git a/uitest/src/com/vaadin/tests/themes/valo/ValoMenuLayout.java b/uitest/src/com/vaadin/tests/themes/valo/ValoMenuLayout.java index 3a3baa686c..0e62f983a6 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/ValoMenuLayout.java +++ b/uitest/src/com/vaadin/tests/themes/valo/ValoMenuLayout.java @@ -19,6 +19,7 @@ import com.vaadin.ui.Component; import com.vaadin.ui.ComponentContainer; import com.vaadin.ui.CssLayout; import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.themes.ValoTheme; /** * @@ -34,7 +35,7 @@ public class ValoMenuLayout extends HorizontalLayout { public ValoMenuLayout() { setSizeFull(); - menuArea.setPrimaryStyleName("valo-menu"); + menuArea.setPrimaryStyleName(ValoTheme.MENU_ROOT); contentArea.setPrimaryStyleName("valo-content"); contentArea.addStyleName("v-scrollable"); @@ -49,7 +50,7 @@ public class ValoMenuLayout extends HorizontalLayout { } public void addMenu(Component menu) { - menu.addStyleName("valo-menu-part"); + menu.addStyleName(ValoTheme.MENU_PART); menuArea.addComponent(menu); } diff --git a/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUI.java b/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUI.java index 988b3487bd..3bf6fd7ca3 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUI.java +++ b/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUI.java @@ -194,29 +194,29 @@ public class ValoThemeUI extends UI { Component buildTestMenu() { CssLayout menu = new CssLayout(); - menu.addStyleName("large-icons"); + menu.addStyleName(ValoTheme.MENU_PART_LARGE_ICONS); Label logo = new Label("Va"); logo.setSizeUndefined(); - logo.setPrimaryStyleName("valo-menu-logo"); + logo.setPrimaryStyleName(ValoTheme.MENU_LOGO); menu.addComponent(logo); Button b = new Button( "Reference 3"); b.setIcon(FontAwesome.TH_LIST); - b.setPrimaryStyleName("valo-menu-item"); + b.setPrimaryStyleName(ValoTheme.MENU_ITEM); b.addStyleName("selected"); b.setHtmlContentAllowed(true); menu.addComponent(b); b = new Button("API"); b.setIcon(FontAwesome.BOOK); - b.setPrimaryStyleName("valo-menu-item"); + b.setPrimaryStyleName(ValoTheme.MENU_ITEM); menu.addComponent(b); b = new Button("Examples 12"); b.setIcon(FontAwesome.TABLE); - b.setPrimaryStyleName("valo-menu-item"); + b.setPrimaryStyleName(ValoTheme.MENU_ITEM); b.setHtmlContentAllowed(true); menu.addComponent(b); @@ -250,7 +250,7 @@ public class ValoThemeUI extends UI { HorizontalLayout top = new HorizontalLayout(); top.setWidth("100%"); top.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT); - top.addStyleName("valo-menu-title"); + top.addStyleName(ValoTheme.MENU_TITLE); menu.addComponent(top); menu.addComponent(createThemeSelect()); @@ -297,8 +297,8 @@ public class ValoThemeUI extends UI { for (final Entry item : menuItems.entrySet()) { if (item.getKey().equals("labels")) { label = new Label("Components", ContentMode.HTML); - label.setPrimaryStyleName("valo-menu-subtitle"); - label.addStyleName("h4"); + label.setPrimaryStyleName(ValoTheme.MENU_SUBTITLE); + label.addStyleName(ValoTheme.LABEL_H4); label.setSizeUndefined(); menuItemsLayout.addComponent(label); } @@ -308,8 +308,8 @@ public class ValoThemeUI extends UI { + ""); count = 0; label = new Label("Containers", ContentMode.HTML); - label.setPrimaryStyleName("valo-menu-subtitle"); - label.addStyleName("h4"); + label.setPrimaryStyleName(ValoTheme.MENU_SUBTITLE); + label.addStyleName(ValoTheme.LABEL_H4); label.setSizeUndefined(); menuItemsLayout.addComponent(label); } @@ -319,8 +319,8 @@ public class ValoThemeUI extends UI { + ""); count = 0; label = new Label("Other", ContentMode.HTML); - label.setPrimaryStyleName("valo-menu-subtitle"); - label.addStyleName("h4"); + label.setPrimaryStyleName(ValoTheme.MENU_SUBTITLE); + label.addStyleName(ValoTheme.LABEL_H4); label.setSizeUndefined(); menuItemsLayout.addComponent(label); } @@ -335,7 +335,7 @@ public class ValoThemeUI extends UI { + " 123"); } b.setHtmlContentAllowed(true); - b.setPrimaryStyleName("valo-menu-item"); + b.setPrimaryStyleName(ValoTheme.MENU_ITEM); b.setIcon(testIcon.get()); menuItemsLayout.addComponent(b); count++; -- cgit v1.2.3 From c413b480663e9b3cbda00faf14eb4df224c6a10a Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Mon, 24 Aug 2015 14:38:19 +0300 Subject: Fix Table ColumnCollapseEvents to work with generated columns (#6914) Change-Id: Id2039cc4869695a8f4bbaa8f25f44f804433e909 --- server/src/com/vaadin/ui/Table.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'server/src/com/vaadin/ui') diff --git a/server/src/com/vaadin/ui/Table.java b/server/src/com/vaadin/ui/Table.java index 2cd4084ad9..10d1c45ab6 100644 --- a/server/src/com/vaadin/ui/Table.java +++ b/server/src/com/vaadin/ui/Table.java @@ -1322,7 +1322,8 @@ public class Table extends AbstractSelect implements Action.Container, if (collapsed && noncollapsibleColumns.contains(propertyId)) { throw new IllegalStateException("The column is noncollapsible!"); } - if (!getContainerPropertyIds().contains(propertyId)) { + if (!getContainerPropertyIds().contains(propertyId) + && !columnGenerators.containsKey(propertyId)) { throw new IllegalArgumentException("Property '" + propertyId + "' was not found in the container"); } @@ -5772,7 +5773,7 @@ public class Table extends AbstractSelect implements Action.Container, * @param source * The source of the event * @param propertyId - * The id of the coumn + * The id of the column */ public ColumnCollapseEvent(Component source, Object propertyId) { super(source); -- cgit v1.2.3 From b6f2bb0ceeb2e903e3c457f480ebd66450597644 Mon Sep 17 00:00:00 2001 From: Johannes Dahlström Date: Wed, 26 Aug 2015 16:47:19 +0300 Subject: Fix empty @since tags for 7.6.0.alpha4 Change-Id: I9f223ec2d49a4a851f5e5808cc325c52717191ee --- client/src/com/vaadin/client/ComputedStyle.java | 4 ++-- .../com/vaadin/client/connectors/RpcDataSourceConnector.java | 2 +- .../src/com/vaadin/client/data/AbstractRemoteDataSource.java | 2 +- client/src/com/vaadin/client/widgets/Grid.java | 2 +- server/src/com/vaadin/data/DataGenerator.java | 2 +- server/src/com/vaadin/data/RpcDataProviderExtension.java | 12 ++++++------ .../server/communication/AtmospherePushConnection.java | 2 +- server/src/com/vaadin/ui/Grid.java | 12 ++++++------ shared/src/com/vaadin/shared/data/DataProviderRpc.java | 2 +- shared/src/com/vaadin/shared/data/DataRequestRpc.java | 2 +- 10 files changed, 21 insertions(+), 21 deletions(-) (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/ComputedStyle.java b/client/src/com/vaadin/client/ComputedStyle.java index 676d18d245..66404eeeed 100644 --- a/client/src/com/vaadin/client/ComputedStyle.java +++ b/client/src/com/vaadin/client/ComputedStyle.java @@ -335,7 +335,7 @@ public class ComputedStyle { /** * Returns the sum of the top and bottom margin * - * @since + * @since 7.6 * @return the sum of the top and bottom margin */ public double getMarginHeight() { @@ -348,7 +348,7 @@ public class ComputedStyle { /** * Returns the sum of the top and bottom margin * - * @since + * @since 7.6 * @return the sum of the left and right margin */ public double getMarginWidth() { diff --git a/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java b/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java index 79838f3252..78aaebaca1 100644 --- a/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java +++ b/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java @@ -229,7 +229,7 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { /** * Updates row data based on row key. * - * @since + * @since 7.6 * @param row * new row object */ diff --git a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java index 256bc5ff6a..2438bec8df 100644 --- a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java +++ b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java @@ -360,7 +360,7 @@ public abstract class AbstractRemoteDataSource implements DataSource { * dropped from the cache. DataSource no longer has anything in the given * index. * - * @since + * @since 7.6 * @param rowIndex * the index of the dropped row * @param removed diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 580d0da72b..45d14fac30 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -8302,7 +8302,7 @@ public class Grid extends ResizeComposite implements * Returns a CellReference for the cell to which the given element belongs * to. * - * @since + * @since 7.6 * @param element * Element to find from the cell's content. * @return CellReference or null if cell was not found. diff --git a/server/src/com/vaadin/data/DataGenerator.java b/server/src/com/vaadin/data/DataGenerator.java index f025623a3e..a5333b8523 100644 --- a/server/src/com/vaadin/data/DataGenerator.java +++ b/server/src/com/vaadin/data/DataGenerator.java @@ -29,7 +29,7 @@ import elemental.json.JsonObject; * {@link AbstractRenderer} implements this interface to provide encoded data to * client for {@link Renderer}s automatically. * - * @since + * @since 7.6 * @author Vaadin Ltd */ public interface DataGenerator extends Serializable { diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index 2ec100eba6..f5d712f6b2 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -243,7 +243,7 @@ public class RpcDataProviderExtension extends AbstractExtension { /** * {@inheritDoc} * - * @since + * @since 7.6 */ @Override public void generateData(Object itemId, Item item, JsonObject rowData) { @@ -253,7 +253,7 @@ public class RpcDataProviderExtension extends AbstractExtension { /** * Removes all inactive item id to key mapping from the key mapper. * - * @since + * @since 7.6 */ public void dropInactiveItems() { Collection active = activeItemHandler.getActiveItemIds(); @@ -270,7 +270,7 @@ public class RpcDataProviderExtension extends AbstractExtension { /** * Class for keeping track of current items and ValueChangeListeners. * - * @since + * @since 7.6 */ private class ActiveItemHandler implements Serializable { @@ -545,7 +545,7 @@ public class RpcDataProviderExtension extends AbstractExtension { /** * {@inheritDoc} * - * @since + * @since 7.6 */ @Override public void generateData(Object itemId, Item item, JsonObject rowData) { @@ -823,7 +823,7 @@ public class RpcDataProviderExtension extends AbstractExtension { * DataGenerators are called when sending row data to client. If given * DataGenerator is already added, this method does nothing. * - * @since + * @since 7.6 * @param generator * generator to add */ @@ -836,7 +836,7 @@ public class RpcDataProviderExtension extends AbstractExtension { * {@code RpcDataProviderExtension}. If given DataGenerator is not added to * this data provider, this method does nothing. * - * @since + * @since 7.6 * @param generator * generator to remove */ diff --git a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java index e1bc8e212b..05e4f6479f 100644 --- a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java +++ b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java @@ -359,7 +359,7 @@ public class AtmospherePushConnection implements PushConnection { * Internal method used for reconfiguring loggers to show all Atmosphere log * messages in the console. * - * @since + * @since 7.6 */ public static void enableAtmosphereDebugLogging() { Level level = Level.FINEST; diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index c7ad9632fa..cea89d24e8 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -1574,7 +1574,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * * @see Grid#setRowDescriptionGenerator(CellDescriptionGenerator) * - * @since + * @since 7.6 */ public interface RowDescriptionGenerator extends Serializable { @@ -1598,7 +1598,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * * @see Grid#setCellDescriptionGenerator(CellDescriptionGenerator) * - * @since + * @since 7.6 */ public interface CellDescriptionGenerator extends Serializable { @@ -5877,7 +5877,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * * @see #setRowDescriptionGenerator(RowDescriptionGenerator) * - * @since + * @since 7.6 */ public void setCellDescriptionGenerator(CellDescriptionGenerator generator) { cellDescriptionGenerator = generator; @@ -5891,7 +5891,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * * @return the description generator or {@code null} if no generator is set * - * @since + * @since 7.6 */ public CellDescriptionGenerator getCellDescriptionGenerator() { return cellDescriptionGenerator; @@ -5911,7 +5911,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * * @see #setCellDescriptionGenerator(CellDescriptionGenerator) * - * @since + * @since 7.6 */ public void setRowDescriptionGenerator(RowDescriptionGenerator generator) { rowDescriptionGenerator = generator; @@ -5925,7 +5925,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * * @return the description generator or {@code} null if no generator is set * - * @since + * @since 7.6 */ public RowDescriptionGenerator getRowDescriptionGenerator() { return rowDescriptionGenerator; diff --git a/shared/src/com/vaadin/shared/data/DataProviderRpc.java b/shared/src/com/vaadin/shared/data/DataProviderRpc.java index bddbdd113d..28e50d9747 100644 --- a/shared/src/com/vaadin/shared/data/DataProviderRpc.java +++ b/shared/src/com/vaadin/shared/data/DataProviderRpc.java @@ -97,7 +97,7 @@ public interface DataProviderRpc extends ClientRpc { * Informs the client that a row has updated. The client-side DataSource * will map the given data to correct index if it should be in the cache. * - * @since + * @since 7.6 * @param row * the updated row data */ diff --git a/shared/src/com/vaadin/shared/data/DataRequestRpc.java b/shared/src/com/vaadin/shared/data/DataRequestRpc.java index 041e92d05c..b66965fae9 100644 --- a/shared/src/com/vaadin/shared/data/DataRequestRpc.java +++ b/shared/src/com/vaadin/shared/data/DataRequestRpc.java @@ -63,7 +63,7 @@ public interface DataRequestRpc extends ServerRpc { /** * Informs the server that items have been dropped from the client cache. * - * @since + * @since 7.6 * @param rowKeys * array of dropped keys mapping to items */ -- cgit v1.2.3 From 1d36db6d112c818a9c75e7cd10eb6b3519119406 Mon Sep 17 00:00:00 2001 From: patrik Date: Thu, 6 Aug 2015 14:08:38 +0300 Subject: Add better keyboard Close Shortcut API for Window (#17383) Change-Id: I29c7d288fe35f6801cf3576ba06751adce821340 --- WebContent/release-notes.html | 5 + client/src/com/vaadin/client/ui/VWindow.java | 13 +- server/src/com/vaadin/event/ShortcutAction.java | 30 ++- server/src/com/vaadin/ui/Window.java | 238 +++++++++++++++++---- .../vaadin/tests/design/DesignFormatterTest.java | 4 +- .../component/button/ButtonDeclarativeTest.java | 2 +- .../component/window/WindowDeclarativeTest.java | 39 +++- .../components/window/WindowCloseShortcuts.java | 199 +++++++++++++++++ 8 files changed, 467 insertions(+), 63 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/window/WindowCloseShortcuts.java (limited to 'server/src/com/vaadin/ui') diff --git a/WebContent/release-notes.html b/WebContent/release-notes.html index b7cdba7887..61511b3002 100644 --- a/WebContent/release-notes.html +++ b/WebContent/release-notes.html @@ -109,6 +109,11 @@

Incompatible or Behavior-altering Changes in @version-minor@

    +
  • Window close shortcuts have been changed, and ESCAPE is no longer a hard-coded default, but rather a soft one, + and can be removed. If the
    close-shortcut
    attribute of the
    v-window
    element is present, + it must list all close shortcuts, including ESCAPE, separated by whitespace. Existing, unchanged code should + behave as before. See ticket #17383 for more information + on the reasoning behind the change.
  • The push path has been changed from /PUSH/ to /PUSH to be compatible with JSR 356.
  • Widgetset files and other pre-compressed resources are sent as gzip to compatible browsers. This may interfere with custom response compression solutions that do not respect the Content-Encoding response header.
  • diff --git a/client/src/com/vaadin/client/ui/VWindow.java b/client/src/com/vaadin/client/ui/VWindow.java index e34e12a20b..2d2d6ecee1 100644 --- a/client/src/com/vaadin/client/ui/VWindow.java +++ b/client/src/com/vaadin/client/ui/VWindow.java @@ -44,8 +44,6 @@ import com.google.gwt.event.dom.client.FocusHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; -import com.google.gwt.event.dom.client.KeyUpEvent; -import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.event.dom.client.ScrollEvent; import com.google.gwt.event.dom.client.ScrollHandler; import com.google.gwt.event.shared.HandlerRegistration; @@ -79,8 +77,7 @@ import com.vaadin.shared.ui.window.WindowRole; * @author Vaadin Ltd */ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, - ScrollHandler, KeyDownHandler, KeyUpHandler, FocusHandler, BlurHandler, - Focusable { + ScrollHandler, KeyDownHandler, FocusHandler, BlurHandler, Focusable { private static ArrayList windowOrder = new ArrayList(); @@ -221,7 +218,6 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, constructDOM(); contentPanel.addScrollHandler(this); contentPanel.addKeyDownHandler(this); - contentPanel.addKeyUpHandler(this); contentPanel.addFocusHandler(this); contentPanel.addBlurHandler(this); } @@ -1343,13 +1339,6 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, } } - @Override - public void onKeyUp(KeyUpEvent event) { - if (isClosable() && event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) { - onCloseClick(); - } - } - @Override public void onBlur(BlurEvent event) { if (client.hasEventListeners(this, EventId.BLUR)) { diff --git a/server/src/com/vaadin/event/ShortcutAction.java b/server/src/com/vaadin/event/ShortcutAction.java index 09accae1c7..dd511c23c0 100644 --- a/server/src/com/vaadin/event/ShortcutAction.java +++ b/server/src/com/vaadin/event/ShortcutAction.java @@ -17,6 +17,7 @@ package com.vaadin.event; import java.io.Serializable; +import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -55,7 +56,7 @@ public class ShortcutAction extends Action { private final int keyCode; - private final int[] modifiers; + private int[] modifiers; /** * Creates a shortcut that reacts to the given {@link KeyCode} and @@ -73,7 +74,7 @@ public class ShortcutAction extends Action { public ShortcutAction(String caption, int kc, int... m) { super(caption); keyCode = kc; - modifiers = m; + setModifiers(m); } /** @@ -94,7 +95,7 @@ public class ShortcutAction extends Action { public ShortcutAction(String caption, Resource icon, int kc, int... m) { super(caption, icon); keyCode = kc; - modifiers = m; + setModifiers(m); } /** @@ -190,7 +191,7 @@ public class ShortcutAction extends Action { // Given modifiers override this indicated in the caption if (modifierKeys != null) { - modifiers = modifierKeys; + setModifiers(modifierKeys); } else { // Read modifiers from caption int[] mod = new int[match.length() - 1]; @@ -208,13 +209,30 @@ public class ShortcutAction extends Action { break; } } - modifiers = mod; + setModifiers(mod); } } else { keyCode = -1; - modifiers = modifierKeys; + setModifiers(modifierKeys); } + + } + + /** + * When setting modifiers, make sure that modifiers is a valid array AND + * that it's sorted. + * + * @param modifiers + * the modifier keys for this shortcut + */ + private void setModifiers(int... modifiers) { + if (modifiers == null) { + this.modifiers = new int[0]; + } else { + this.modifiers = modifiers; + } + Arrays.sort(this.modifiers); } /** diff --git a/server/src/com/vaadin/ui/Window.java b/server/src/com/vaadin/ui/Window.java index 61ba5826b8..fd5565f900 100644 --- a/server/src/com/vaadin/ui/Window.java +++ b/server/src/com/vaadin/ui/Window.java @@ -21,6 +21,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -74,7 +75,7 @@ import com.vaadin.util.ReflectTools; * @author Vaadin Ltd. * @since 3.0 */ -@SuppressWarnings("serial") +@SuppressWarnings({ "serial", "deprecation" }) public class Window extends Panel implements FocusNotifier, BlurNotifier, LegacyComponent { @@ -101,6 +102,11 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, } }; + /** + * Holds registered CloseShortcut instances for query and later removal + */ + private List closeShortcuts = new ArrayList(4); + /** * Creates a new, empty window */ @@ -130,6 +136,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, super(caption, content); registerRpc(rpc); setSizeUndefined(); + setCloseShortcut(KeyCode.ESCAPE); } /* ********************************************************************* */ @@ -828,14 +835,22 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, } } - /* - * Actions - */ - protected CloseShortcut closeShortcut; - /** - * Makes is possible to close the window by pressing the given - * {@link KeyCode} and (optional) {@link ModifierKey}s.
    + * This is the old way of adding a keyboard shortcut to close a + * {@link Window} - to preserve compatibility with existing code under the + * new functionality, this method now first removes all registered close + * shortcuts, then adds the default ESCAPE shortcut key, and then attempts + * to add the shortcut provided as parameters to this method. This method, + * and its companion {@link #removeCloseShortcut()}, are now considered + * deprecated, as their main function is to preserve exact backwards + * compatibility with old code. For all new code, use the new keyboard + * shortcuts API: {@link #addCloseShortcut(int,int...)}, + * {@link #removeCloseShortcut(int,int...)}, + * {@link #removeAllCloseShortcuts()}, {@link #hasCloseShortcut(int,int...)} + * and {@link #getCloseShortcuts()}. + *

    + * Original description: Makes it possible to close the window by pressing + * the given {@link KeyCode} and (optional) {@link ModifierKey}s.
    * Note that this shortcut only reacts while the window has focus, closing * itself - if you want to close a window from a UI, use * {@link UI#addAction(com.vaadin.event.Action)} of the UI instead. @@ -843,28 +858,136 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, * @param keyCode * the keycode for invoking the shortcut * @param modifiers - * the (optional) modifiers for invoking the shortcut, null for - * none + * the (optional) modifiers for invoking the shortcut. Can be set + * to null to be explicit about not having modifiers. + * + * @deprecated Use {@link #addCloseShortcut(int, int...)} instead. */ + @Deprecated public void setCloseShortcut(int keyCode, int... modifiers) { - if (closeShortcut != null) { - removeAction(closeShortcut); - } - closeShortcut = new CloseShortcut(this, keyCode, modifiers); - addAction(closeShortcut); + removeCloseShortcut(); + addCloseShortcut(keyCode, modifiers); } /** - * Removes the keyboard shortcut previously set with - * {@link #setCloseShortcut(int, int...)}. + * Removes all keyboard shortcuts previously set with + * {@link #setCloseShortcut(int, int...)} and + * {@link #addCloseShortcut(int, int...)}, then adds the default + * {@link KeyCode#ESCAPE} shortcut. + *

    + * This is the old way of removing the (single) keyboard close shortcut, and + * is retained only for exact backwards compatibility. For all new code, use + * the new keyboard shortcuts API: {@link #addCloseShortcut(int,int...)}, + * {@link #removeCloseShortcut(int,int...)}, + * {@link #removeAllCloseShortcuts()}, {@link #hasCloseShortcut(int,int...)} + * and {@link #getCloseShortcuts()}. + * + * @deprecated Use {@link #removeCloseShortcut(int, int...)} instead. */ + @Deprecated public void removeCloseShortcut() { - if (closeShortcut != null) { - removeAction(closeShortcut); - closeShortcut = null; + for (int i = 0; i < closeShortcuts.size(); ++i) { + CloseShortcut sc = closeShortcuts.get(i); + removeAction(sc); + } + closeShortcuts.clear(); + addCloseShortcut(KeyCode.ESCAPE); + } + + /** + * Adds a close shortcut - pressing this key while holding down all (if any) + * modifiers specified while this Window is in focus will close the Window. + * + * @since + * @param keyCode + * the keycode for invoking the shortcut + * @param modifiers + * the (optional) modifiers for invoking the shortcut. Can be set + * to null to be explicit about not having modifiers. + */ + public void addCloseShortcut(int keyCode, int... modifiers) { + + // Ignore attempts to re-add existing shortcuts + if (hasCloseShortcut(keyCode, modifiers)) { + return; + } + + // Actually add the shortcut + CloseShortcut shortcut = new CloseShortcut(this, keyCode, modifiers); + addAction(shortcut); + closeShortcuts.add(shortcut); + } + + /** + * Removes a close shortcut previously added with + * {@link #addCloseShortcut(int, int...)}. + * + * @since + * @param keyCode + * the keycode for invoking the shortcut + * @param modifiers + * the (optional) modifiers for invoking the shortcut. Can be set + * to null to be explicit about not having modifiers. + */ + public void removeCloseShortcut(int keyCode, int... modifiers) { + for (CloseShortcut shortcut : closeShortcuts) { + if (shortcut.equals(keyCode, modifiers)) { + removeAction(shortcut); + closeShortcuts.remove(shortcut); + return; + } } } + /** + * Removes all close shortcuts. This includes the default ESCAPE shortcut. + * It is up to the user to add back any and all keyboard close shortcuts + * they may require. For more fine-grained control over shortcuts, use + * {@link #removeCloseShortcut(int, int...)}. + * + * @since + */ + public void removeAllCloseShortcuts() { + for (CloseShortcut shortcut : closeShortcuts) { + removeAction(shortcut); + } + closeShortcuts.clear(); + } + + /** + * Checks if a close window shortcut key has already been registered. + * + * @since + * @param keyCode + * the keycode for invoking the shortcut + * @param modifiers + * the (optional) modifiers for invoking the shortcut. Can be set + * to null to be explicit about not having modifiers. + * @return true, if an exactly matching shortcut has been registered. + */ + public boolean hasCloseShortcut(int keyCode, int... modifiers) { + for (CloseShortcut shortcut : closeShortcuts) { + if (shortcut.equals(keyCode, modifiers)) { + return true; + } + } + return false; + } + + /** + * Returns an unmodifiable collection of {@link CloseShortcut} objects + * currently registered with this {@link Window}. This method is provided + * mainly so that users can implement their own serialization routines. To + * check if a certain combination of keys has been registered as a close + * shortcut, use the {@link #hasCloseShortcut(int, int...)} method instead. + * + * @since + * @return an unmodifiable Collection of CloseShortcut objects. + */ + public Collection getCloseShortcuts() { + return Collections.unmodifiableCollection(closeShortcuts); + } + /** * A {@link ShortcutListener} specifically made to define a keyboard * shortcut that closes the window. @@ -930,6 +1053,25 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, public void handleAction(Object sender, Object target) { window.close(); } + + public boolean equals(int keyCode, int... modifiers) { + if (keyCode != getKeyCode()) { + return false; + } + + if (getModifiers() != null) { + int[] mods = null; + if (modifiers != null) { + // Modifiers provided by the parent ShortcutAction class + // are guaranteed to be sorted. We still need to sort + // the modifiers passed in as argument. + mods = Arrays.copyOf(modifiers, modifiers.length); + Arrays.sort(mods); + } + return Arrays.equals(mods, getModifiers()); + } + return true; + } } /* @@ -1244,11 +1386,26 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, setPositionX(Integer.parseInt(position[0])); setPositionY(Integer.parseInt(position[1])); } + + // Parse shortcuts if defined, otherwise rely on default behavior if (design.hasAttr("close-shortcut")) { - ShortcutAction shortcut = DesignAttributeHandler - .readAttribute("close-shortcut", design.attributes(), - ShortcutAction.class); - setCloseShortcut(shortcut.getKeyCode(), shortcut.getModifiers()); + + // Parse shortcuts + String[] shortcutStrings = DesignAttributeHandler.readAttribute( + "close-shortcut", design.attributes(), String.class).split( + "\\s+"); + + removeAllCloseShortcuts(); + + for (String part : shortcutStrings) { + if (!part.isEmpty()) { + ShortcutAction shortcut = DesignAttributeHandler + .getFormatter().parse(part.trim(), + ShortcutAction.class); + addCloseShortcut(shortcut.getKeyCode(), + shortcut.getModifiers()); + } + } } } @@ -1302,19 +1459,24 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, DesignAttributeHandler.writeAttribute("position", design.attributes(), getPosition(), def.getPosition(), String.class); - CloseShortcut shortcut = getCloseShortcut(); - if (shortcut != null) { - // TODO What if several close shortcuts?? - - CloseShortcut defShortcut = def.getCloseShortcut(); - if (defShortcut == null - || shortcut.getKeyCode() != defShortcut.getKeyCode() - || !Arrays.equals(shortcut.getModifiers(), - defShortcut.getModifiers())) { - DesignAttributeHandler.writeAttribute("close-shortcut", - design.attributes(), shortcut, null, - CloseShortcut.class); + // Process keyboard shortcuts + if (closeShortcuts.size() == 1 && hasCloseShortcut(KeyCode.ESCAPE)) { + // By default, we won't write anything if we're relying on default + // shortcut behavior + } else { + // Dump all close shortcuts to a string... + String attrString = ""; + + // TODO: add canonical support for array data in XML attributes + for (CloseShortcut shortcut : closeShortcuts) { + String shortcutString = DesignAttributeHandler.getFormatter() + .format(shortcut, CloseShortcut.class); + attrString += shortcutString + " "; } + + // Write everything except the last "," to the design + DesignAttributeHandler.writeAttribute("close-shortcut", + design.attributes(), attrString.trim(), null, String.class); } for (Component c : getAssistiveDescription()) { @@ -1328,10 +1490,6 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, return getPositionX() + "," + getPositionY(); } - private CloseShortcut getCloseShortcut() { - return closeShortcut; - } - @Override protected Collection getCustomAttributes() { Collection result = super.getCustomAttributes(); diff --git a/server/tests/src/com/vaadin/tests/design/DesignFormatterTest.java b/server/tests/src/com/vaadin/tests/design/DesignFormatterTest.java index acee3e2ca8..6510d8ad40 100644 --- a/server/tests/src/com/vaadin/tests/design/DesignFormatterTest.java +++ b/server/tests/src/com/vaadin/tests/design/DesignFormatterTest.java @@ -203,7 +203,7 @@ public class DesignFormatterTest { ShortcutAction action = new ShortcutAction("&^d"); String formatted = formatter.format(action); // note the space here - it separates key combination from caption - assertEquals("alt-ctrl-d d", formatted); + assertEquals("ctrl-alt-d d", formatted); ShortcutAction result = formatter .parse(formatted, ShortcutAction.class); @@ -215,7 +215,7 @@ public class DesignFormatterTest { ShortcutAction action = new ShortcutAction(null, KeyCode.D, new int[] { ModifierKey.ALT, ModifierKey.CTRL }); String formatted = formatter.format(action); - assertEquals("alt-ctrl-d", formatted); + assertEquals("ctrl-alt-d", formatted); ShortcutAction result = formatter .parse(formatted, ShortcutAction.class); diff --git a/server/tests/src/com/vaadin/tests/server/component/button/ButtonDeclarativeTest.java b/server/tests/src/com/vaadin/tests/server/component/button/ButtonDeclarativeTest.java index 51abf6be96..b1c10789b4 100644 --- a/server/tests/src/com/vaadin/tests/server/component/button/ButtonDeclarativeTest.java +++ b/server/tests/src/com/vaadin/tests/server/component/button/ButtonDeclarativeTest.java @@ -98,7 +98,7 @@ public class ButtonDeclarativeTest extends DeclarativeTestBase

Known Issues and Limitations

    diff --git a/client/src/com/vaadin/client/connectors/AbstractSelectionModelConnector.java b/client/src/com/vaadin/client/connectors/AbstractSelectionModelConnector.java new file mode 100644 index 0000000000..8ca2292bc5 --- /dev/null +++ b/client/src/com/vaadin/client/connectors/AbstractSelectionModelConnector.java @@ -0,0 +1,82 @@ +/* + * 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.connectors; + +import java.util.Collection; + +import com.vaadin.client.data.DataSource.RowHandle; +import com.vaadin.client.extensions.AbstractExtensionConnector; +import com.vaadin.client.widget.grid.selection.SelectionModel; +import com.vaadin.client.widgets.Grid; +import com.vaadin.shared.ui.grid.GridState; + +import elemental.json.JsonObject; + +/** + * Base class for all selection model connectors. + * + * @since + * @author Vaadin Ltd + */ +public abstract class AbstractSelectionModelConnector> + extends AbstractExtensionConnector { + + @Override + public GridConnector getParent() { + return (GridConnector) super.getParent(); + } + + protected Grid getGrid() { + return getParent().getWidget(); + } + + protected RowHandle getRowHandle(JsonObject row) { + return getGrid().getDataSource().getHandle(row); + } + + protected String getRowKey(JsonObject row) { + return row != null ? getParent().getRowKey(row) : null; + } + + protected abstract T createSelectionModel(); + + public abstract static class AbstractSelectionModel implements + SelectionModel { + + @Override + public boolean isSelected(JsonObject row) { + return row.hasKey(GridState.JSONKEY_SELECTED); + } + + @Override + public void setGrid(Grid grid) { + // NO-OP + } + + @Override + public void reset() { + // Should not need any actions. + } + + @Override + public Collection getSelectedRows() { + throw new UnsupportedOperationException( + "This client-side selection model " + + getClass().getSimpleName() + + " does not know selected rows."); + } + } +} diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index 5f9341c068..1070a46287 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -22,7 +22,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -36,7 +35,6 @@ import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; @@ -48,8 +46,6 @@ import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; import com.vaadin.client.connectors.RpcDataSourceConnector.DetailsListener; import com.vaadin.client.connectors.RpcDataSourceConnector.RpcDataSource; -import com.vaadin.client.data.DataSource.RowHandle; -import com.vaadin.client.renderers.Renderer; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.AbstractHasComponentsConnector; import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; @@ -72,15 +68,6 @@ import com.vaadin.client.widget.grid.events.EditorMoveEvent; import com.vaadin.client.widget.grid.events.EditorOpenEvent; import com.vaadin.client.widget.grid.events.GridClickEvent; import com.vaadin.client.widget.grid.events.GridDoubleClickEvent; -import com.vaadin.client.widget.grid.events.SelectAllEvent; -import com.vaadin.client.widget.grid.events.SelectAllHandler; -import com.vaadin.client.widget.grid.selection.AbstractRowHandleSelectionModel; -import com.vaadin.client.widget.grid.selection.SelectionEvent; -import com.vaadin.client.widget.grid.selection.SelectionHandler; -import com.vaadin.client.widget.grid.selection.SelectionModel; -import com.vaadin.client.widget.grid.selection.SelectionModelMulti; -import com.vaadin.client.widget.grid.selection.SelectionModelNone; -import com.vaadin.client.widget.grid.selection.SelectionModelSingle; import com.vaadin.client.widget.grid.sort.SortEvent; import com.vaadin.client.widget.grid.sort.SortHandler; import com.vaadin.client.widget.grid.sort.SortOrder; @@ -99,7 +86,6 @@ import com.vaadin.shared.ui.grid.GridColumnState; import com.vaadin.shared.ui.grid.GridConstants; import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.shared.ui.grid.GridState.SharedSelectionMode; import com.vaadin.shared.ui.grid.GridStaticSectionState; import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState; import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; @@ -584,19 +570,8 @@ public class GridConnector extends AbstractHasComponentsConnector implements */ private Map columnIdToColumn = new HashMap(); - private AbstractRowHandleSelectionModel selectionModel; - private Set selectedKeys = new LinkedHashSet(); private List columnOrder = new ArrayList(); - /** - * {@link #selectionUpdatedFromState} is set to true when - * {@link #updateSelectionFromState()} makes changes to selection. This flag - * tells the {@code internalSelectionChangeHandler} to not send same data - * straight back to server. Said listener sets it back to false when - * handling that event. - */ - private boolean selectionUpdatedFromState; - /** * {@link #columnsUpdatedFromState} is set to true when * {@link #updateColumnOrderFromState(List)} is updating the column order @@ -608,29 +583,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements private RpcDataSource dataSource; - private SelectionHandler internalSelectionChangeHandler = new SelectionHandler() { - @Override - public void onSelect(SelectionEvent event) { - if (event.isBatchedSelection()) { - return; - } - if (!selectionUpdatedFromState) { - for (JsonObject row : event.getRemoved()) { - selectedKeys.remove(dataSource.getRowKey(row)); - } - - for (JsonObject row : event.getAdded()) { - selectedKeys.add(dataSource.getRowKey(row)); - } - - getRpcProxy(GridServerRpc.class).select( - new ArrayList(selectedKeys)); - } else { - selectionUpdatedFromState = false; - } - } - }; - /* Used to track Grid editor columns with validation errors */ private final Map, String> columnToErrorMessage = new HashMap, String>(); @@ -744,30 +696,8 @@ public class GridConnector extends AbstractHasComponentsConnector implements public void recalculateColumnWidths() { getWidget().recalculateColumnWidths(); } - - @Override - public void setSelectAll(boolean allSelected) { - if (selectionModel instanceof SelectionModel.Multi - && selectionModel.getSelectionColumnRenderer() != null) { - final Widget widget; - try { - HeaderRow defaultHeaderRow = getWidget() - .getDefaultHeaderRow(); - if (defaultHeaderRow != null) { - widget = defaultHeaderRow.getCell( - getWidget().getColumn(0)).getWidget(); - ((CheckBox) widget).setValue(allSelected, false); - } - } catch (Exception e) { - getLogger().warning( - "Problems finding select all checkbox."); - } - } - } }); - getWidget().addSelectionHandler(internalSelectionChangeHandler); - /* Item click events */ getWidget().addBodyClickHandler(itemClickHandler); getWidget().addBodyDoubleClickHandler(itemClickHandler); @@ -800,15 +730,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements } }); - getWidget().addSelectAllHandler(new SelectAllHandler() { - - @Override - public void onSelectAll(SelectAllEvent event) { - getRpcProxy(GridServerRpc.class).selectAll(); - } - - }); - getWidget().setEditorHandler(editorHandler); getWidget().addColumnReorderHandler(columnReorderHandler); getWidget().addColumnVisibilityChangeHandler( @@ -884,19 +805,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements updateFooterFromState(getState().footer); } - // Selection - if (stateChangeEvent.hasPropertyChanged("selectionMode")) { - onSelectionModeChange(); - updateSelectDeselectAllowed(); - } else if (stateChangeEvent - .hasPropertyChanged("singleSelectDeselectAllowed")) { - updateSelectDeselectAllowed(); - } - - if (stateChangeEvent.hasPropertyChanged("selectedKeys")) { - updateSelectionFromState(); - } - // Sorting if (stateChangeEvent.hasPropertyChanged("sortColumns") || stateChangeEvent.hasPropertyChanged("sortDirs")) { @@ -923,14 +831,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements } } - private void updateSelectDeselectAllowed() { - SelectionModel model = getWidget().getSelectionModel(); - if (model instanceof SelectionModel.Single) { - ((SelectionModel.Single) model) - .setDeselectAllowed(getState().singleSelectDeselectAllowed); - } - } - private void updateColumnOrderFromState(List stateColumnOrder) { CustomGridColumn[] columns = new CustomGridColumn[stateColumnOrder .size()]; @@ -1102,20 +1002,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements columnOrder.add(state.id); } - /** - * If we have a selection column renderer, we need to offset the index by - * one when referring to the column index in the widget. - */ - private int getWidgetColumnIndex(final int columnIndex) { - Renderer selectionColumnRenderer = getWidget() - .getSelectionModel().getSelectionColumnRenderer(); - int widgetColumnIndex = columnIndex; - if (selectionColumnRenderer != null) { - widgetColumnIndex++; - } - return widgetColumnIndex; - } - /** * Updates the column values from a state * @@ -1178,63 +1064,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements getWidget().setDataSource(this.dataSource); } - private void onSelectionModeChange() { - SharedSelectionMode mode = getState().selectionMode; - if (mode == null) { - getLogger().fine("ignored mode change"); - return; - } - - AbstractRowHandleSelectionModel model = createSelectionModel(mode); - if (selectionModel == null - || !model.getClass().equals(selectionModel.getClass())) { - selectionModel = model; - getWidget().setSelectionModel(model); - selectedKeys.clear(); - } - } - - private void updateSelectionFromState() { - boolean changed = false; - - List stateKeys = getState().selectedKeys; - - // find new deselections - for (String key : selectedKeys) { - if (!stateKeys.contains(key)) { - changed = true; - deselectByHandle(dataSource.getHandleByKey(key)); - } - } - - // find new selections - for (String key : stateKeys) { - if (!selectedKeys.contains(key)) { - changed = true; - selectByHandle(dataSource.getHandleByKey(key)); - } - } - - /* - * A defensive copy in case the collection in the state is mutated - * instead of re-assigned. - */ - selectedKeys = new LinkedHashSet(stateKeys); - - /* - * We need to fire this event so that Grid is able to re-render the - * selection changes (if applicable). - */ - if (changed) { - // At least for now there's no way to send the selected and/or - // deselected row data. Some data is only stored as keys - selectionUpdatedFromState = true; - getWidget().fireEvent( - new SelectionEvent(getWidget(), - (List) null, null, false)); - } - } - private void onSortStateChange() { List sortOrder = new ArrayList(); @@ -1253,41 +1082,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements return Logger.getLogger(getClass().getName()); } - @SuppressWarnings("static-method") - private AbstractRowHandleSelectionModel createSelectionModel( - SharedSelectionMode mode) { - switch (mode) { - case SINGLE: - return new SelectionModelSingle(); - case MULTI: - return new SelectionModelMulti(); - case NONE: - return new SelectionModelNone(); - default: - throw new IllegalStateException("unexpected mode value: " + mode); - } - } - - /** - * A workaround method for accessing the protected method - * {@code AbstractRowHandleSelectionModel.selectByHandle} - */ - private native void selectByHandle(RowHandle handle) - /*-{ - var model = this.@com.vaadin.client.connectors.GridConnector::selectionModel; - model.@com.vaadin.client.widget.grid.selection.AbstractRowHandleSelectionModel::selectByHandle(*)(handle); - }-*/; - - /** - * A workaround method for accessing the protected method - * {@code AbstractRowHandleSelectionModel.deselectByHandle} - */ - private native void deselectByHandle(RowHandle handle) - /*-{ - var model = this.@com.vaadin.client.connectors.GridConnector::selectionModel; - model.@com.vaadin.client.widget.grid.selection.AbstractRowHandleSelectionModel::deselectByHandle(*)(handle); - }-*/; - /** * Gets the row key for a row object. * @@ -1312,7 +1106,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements @Override public void updateCaption(ComponentConnector connector) { // TODO Auto-generated method stub - } @Override diff --git a/client/src/com/vaadin/client/connectors/MultiSelectionModelConnector.java b/client/src/com/vaadin/client/connectors/MultiSelectionModelConnector.java new file mode 100644 index 0000000000..c1f5d87d68 --- /dev/null +++ b/client/src/com/vaadin/client/connectors/MultiSelectionModelConnector.java @@ -0,0 +1,360 @@ +/* + * 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.connectors; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.ui.CheckBox; +import com.vaadin.client.ServerConnector; +import com.vaadin.client.annotations.OnStateChange; +import com.vaadin.client.data.DataSource; +import com.vaadin.client.data.DataSource.RowHandle; +import com.vaadin.client.renderers.ComplexRenderer; +import com.vaadin.client.renderers.Renderer; +import com.vaadin.client.widget.grid.DataAvailableEvent; +import com.vaadin.client.widget.grid.DataAvailableHandler; +import com.vaadin.client.widget.grid.events.SelectAllEvent; +import com.vaadin.client.widget.grid.events.SelectAllHandler; +import com.vaadin.client.widget.grid.selection.MultiSelectionRenderer; +import com.vaadin.client.widget.grid.selection.SelectionModel; +import com.vaadin.client.widget.grid.selection.SelectionModel.Multi; +import com.vaadin.client.widget.grid.selection.SpaceSelectHandler; +import com.vaadin.client.widgets.Grid; +import com.vaadin.client.widgets.Grid.HeaderCell; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.shared.ui.grid.Range; +import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc; +import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState; +import com.vaadin.ui.Grid.MultiSelectionModel; + +import elemental.json.JsonObject; + +/** + * Connector for server-side {@link MultiSelectionModel}. + * + * @since + * @author Vaadin Ltd + */ +@Connect(MultiSelectionModel.class) +public class MultiSelectionModelConnector extends + AbstractSelectionModelConnector> { + + private Multi selectionModel = createSelectionModel(); + private SpaceSelectHandler spaceHandler; + + @Override + protected void extend(ServerConnector target) { + getGrid().setSelectionModel(selectionModel); + spaceHandler = new SpaceSelectHandler(getGrid()); + } + + @Override + public void onUnregister() { + spaceHandler.removeHandler(); + } + + @Override + protected Multi createSelectionModel() { + return new MultiSelectionModel(); + } + + @Override + public MultiSelectionModelState getState() { + return (MultiSelectionModelState) super.getState(); + } + + @OnStateChange("allSelected") + void updateSelectAllCheckbox() { + if (selectionModel.getSelectionColumnRenderer() != null) { + HeaderCell cell = getGrid().getDefaultHeaderRow().getCell( + getGrid().getColumn(0)); + CheckBox widget = (CheckBox) cell.getWidget(); + widget.setValue(getState().allSelected, false); + } + } + + protected class MultiSelectionModel extends AbstractSelectionModel + implements SelectionModel.Multi.Batched { + + private ComplexRenderer renderer = null; + private Set> selected = new HashSet>(); + private Set> deselected = new HashSet>(); + private HandlerRegistration selectAll; + private HandlerRegistration dataAvailable; + private Range availableRows; + private boolean batchSelect = false; + + @Override + public void setGrid(Grid grid) { + super.setGrid(grid); + if (grid != null) { + renderer = createSelectionColumnRenderer(grid); + selectAll = getGrid().addSelectAllHandler( + new SelectAllHandler() { + + @Override + public void onSelectAll( + SelectAllEvent event) { + selectAll(); + } + }); + dataAvailable = getGrid().addDataAvailableHandler( + new DataAvailableHandler() { + + @Override + public void onDataAvailable(DataAvailableEvent event) { + availableRows = event.getAvailableRows(); + } + }); + } else if (renderer != null) { + renderer.destroy(); + selectAll.removeHandler(); + dataAvailable.removeHandler(); + renderer = null; + } + } + + /** + * Creates a selection column renderer. This method can be overridden to + * use a custom renderer or use {@code null} to disable the selection + * column. + * + * @param grid + * the grid for this selection model + * @return selection column renderer or {@code null} if not needed + */ + protected ComplexRenderer createSelectionColumnRenderer( + Grid grid) { + return new MultiSelectionRenderer(grid); + } + + /** + * Selects all available rows, sends request to server to select + * everything. + */ + public void selectAll() { + assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection."; + + Set> rows = new HashSet>(); + DataSource dataSource = getGrid().getDataSource(); + for (int i = availableRows.getStart(); i < availableRows.getEnd(); ++i) { + final JsonObject row = dataSource.getRow(i); + if (row != null) { + RowHandle handle = dataSource.getHandle(row); + markAsSelected(handle, true); + rows.add(handle); + } + } + + getRpcProxy(MultiSelectionModelServerRpc.class).selectAll(); + cleanRowCache(rows); + } + + @Override + public Renderer getSelectionColumnRenderer() { + return renderer; + } + + /** + * {@inheritDoc} + * + * @return {@code false} if rows is empty, else {@code true} + */ + @Override + public boolean select(JsonObject... rows) { + return select(Arrays.asList(rows)); + } + + /** + * {@inheritDoc} + * + * @return {@code false} if rows is empty, else {@code true} + */ + @Override + public boolean deselect(JsonObject... rows) { + return deselect(Arrays.asList(rows)); + } + + /** + * {@inheritDoc} + * + * @return always {@code true} + */ + @Override + public boolean deselectAll() { + assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection."; + + Set> rows = new HashSet>(); + DataSource dataSource = getGrid().getDataSource(); + for (int i = availableRows.getStart(); i < availableRows.getEnd(); ++i) { + final JsonObject row = dataSource.getRow(i); + if (row != null) { + RowHandle handle = dataSource.getHandle(row); + markAsSelected(handle, false); + rows.add(handle); + } + } + + getRpcProxy(MultiSelectionModelServerRpc.class).deselectAll(); + cleanRowCache(rows); + + return true; + } + + /** + * {@inheritDoc} + * + * @return {@code false} if rows is empty, else {@code true} + */ + @Override + public boolean select(Collection rows) { + if (rows.isEmpty()) { + return false; + } + + for (JsonObject row : rows) { + RowHandle rowHandle = getRowHandle(row); + markAsSelected(rowHandle, true); + selected.add(rowHandle); + } + + if (!isBeingBatchSelected()) { + sendSelected(); + } + return true; + } + + /** + * Marks the JsonObject pointed by RowHandle to have selected + * information equal to given boolean + * + * @param row + * row handle + * @param selected + * should row be selected + */ + protected void markAsSelected(RowHandle row, + boolean selected) { + row.pin(); + if (selected) { + row.getRow().put(GridState.JSONKEY_SELECTED, true); + } else { + row.getRow().remove(GridState.JSONKEY_SELECTED); + } + row.updateRow(); + } + + /** + * {@inheritDoc} + * + * @return {@code false} if rows is empty, else {@code true} + */ + @Override + public boolean deselect(Collection rows) { + if (rows.isEmpty()) { + return false; + } + + for (JsonObject row : rows) { + RowHandle rowHandle = getRowHandle(row); + markAsSelected(rowHandle, false); + deselected.add(rowHandle); + } + + if (!isBeingBatchSelected()) { + sendDeselected(); + } + return true; + } + + private void sendDeselected() { + getRpcProxy(MultiSelectionModelServerRpc.class).deselect( + getRowKeys(deselected)); + cleanRowCache(deselected); + } + + private void sendSelected() { + getRpcProxy(MultiSelectionModelServerRpc.class).select( + getRowKeys(selected)); + cleanRowCache(selected); + } + + private List getRowKeys(Set> handles) { + List keys = new ArrayList(); + for (RowHandle handle : handles) { + keys.add(getRowKey(handle.getRow())); + } + return keys; + } + + private Set getRows(Set> handles) { + Set rows = new HashSet(); + for (RowHandle handle : handles) { + rows.add(handle.getRow()); + } + return rows; + } + + private void cleanRowCache(Set> handles) { + for (RowHandle handle : handles) { + handle.unpin(); + } + handles.clear(); + } + + @Override + public void startBatchSelect() { + assert selected.isEmpty() && deselected.isEmpty() : "Row caches were not clear."; + batchSelect = true; + } + + @Override + public void commitBatchSelect() { + assert batchSelect : "Not batch selecting."; + if (!selected.isEmpty()) { + sendSelected(); + } + + if (!deselected.isEmpty()) { + sendDeselected(); + } + batchSelect = false; + } + + @Override + public boolean isBeingBatchSelected() { + return batchSelect; + } + + @Override + public Collection getSelectedRowsBatch() { + return Collections.unmodifiableSet(getRows(selected)); + } + + @Override + public Collection getDeselectedRowsBatch() { + return Collections.unmodifiableSet(getRows(deselected)); + } + } +} diff --git a/client/src/com/vaadin/client/connectors/NoSelectionModelConnector.java b/client/src/com/vaadin/client/connectors/NoSelectionModelConnector.java new file mode 100644 index 0000000000..b540eed6d5 --- /dev/null +++ b/client/src/com/vaadin/client/connectors/NoSelectionModelConnector.java @@ -0,0 +1,42 @@ +/* + * 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.connectors; + +import com.vaadin.client.ServerConnector; +import com.vaadin.client.widget.grid.selection.SelectionModel; +import com.vaadin.client.widget.grid.selection.SelectionModelNone; +import com.vaadin.shared.ui.Connect; +import com.vaadin.ui.Grid.NoSelectionModel; + +import elemental.json.JsonObject; + +/** + * Connector for server-side {@link NoSelectionModel}. + */ +@Connect(NoSelectionModel.class) +public class NoSelectionModelConnector extends + AbstractSelectionModelConnector> { + + @Override + protected void extend(ServerConnector target) { + getGrid().setSelectionModel(createSelectionModel()); + } + + @Override + protected SelectionModel createSelectionModel() { + return new SelectionModelNone(); + } +} \ No newline at end of file diff --git a/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java b/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java index 78aaebaca1..bcca8816b1 100644 --- a/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java +++ b/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java @@ -99,8 +99,10 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { } @Override - public void updateRowData(JsonObject row) { - RpcDataSource.this.updateRowData(row); + public void updateRowData(JsonArray rowArray) { + for (int i = 0; i < rowArray.length(); ++i) { + RpcDataSource.this.updateRowData(rowArray.getObject(i)); + } } }); } diff --git a/client/src/com/vaadin/client/connectors/SingleSelectionModelConnector.java b/client/src/com/vaadin/client/connectors/SingleSelectionModelConnector.java new file mode 100644 index 0000000000..7c66903c2c --- /dev/null +++ b/client/src/com/vaadin/client/connectors/SingleSelectionModelConnector.java @@ -0,0 +1,148 @@ +/* + * 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.connectors; + +import com.vaadin.client.ServerConnector; +import com.vaadin.client.annotations.OnStateChange; +import com.vaadin.client.data.DataSource.RowHandle; +import com.vaadin.client.renderers.Renderer; +import com.vaadin.client.widget.grid.selection.ClickSelectHandler; +import com.vaadin.client.widget.grid.selection.SelectionModel; +import com.vaadin.client.widget.grid.selection.SelectionModel.Single; +import com.vaadin.client.widget.grid.selection.SpaceSelectHandler; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc; +import com.vaadin.shared.ui.grid.selection.SingleSelectionModelState; +import com.vaadin.ui.Grid.SingleSelectionModel; + +import elemental.json.JsonObject; + +/** + * Connector for server-side {@link SingleSelectionModel}. + * + * @since + * @author Vaadin Ltd + */ +@Connect(SingleSelectionModel.class) +public class SingleSelectionModelConnector extends + AbstractSelectionModelConnector> { + + private SpaceSelectHandler spaceHandler; + private ClickSelectHandler clickHandler; + private Single selectionModel = createSelectionModel(); + + @Override + protected void extend(ServerConnector target) { + getGrid().setSelectionModel(selectionModel); + spaceHandler = new SpaceSelectHandler(getGrid()); + clickHandler = new ClickSelectHandler(getGrid()); + } + + @Override + public SingleSelectionModelState getState() { + return (SingleSelectionModelState) super.getState(); + } + + @Override + public void onUnregister() { + spaceHandler.removeHandler(); + clickHandler.removeHandler(); + + super.onUnregister(); + } + + @Override + protected Single createSelectionModel() { + return new SingleSelectionModel(); + } + + @OnStateChange("deselectAllowed") + void updateDeselectAllowed() { + selectionModel.setDeselectAllowed(getState().deselectAllowed); + } + + /** + * SingleSelectionModel without a selection column renderer. + */ + public class SingleSelectionModel extends AbstractSelectionModel implements + SelectionModel.Single { + + private RowHandle selectedRow; + private boolean deselectAllowed; + + @Override + public Renderer getSelectionColumnRenderer() { + return null; + } + + @Override + public boolean select(JsonObject row) { + boolean changed = false; + if ((row == null && isDeselectAllowed()) + || (row != null && !getRowHandle(row).equals(selectedRow))) { + if (selectedRow != null) { + selectedRow.getRow().remove(GridState.JSONKEY_SELECTED); + selectedRow.updateRow(); + selectedRow.unpin(); + selectedRow = null; + changed = true; + } + + if (row != null) { + selectedRow = getRowHandle(row); + selectedRow.pin(); + selectedRow.getRow().put(GridState.JSONKEY_SELECTED, true); + selectedRow.updateRow(); + changed = true; + } + } + + if (changed) { + getRpcProxy(SingleSelectionModelServerRpc.class).select( + getRowKey(row)); + } + + return changed; + } + + @Override + public boolean deselect(JsonObject row) { + if (getRowHandle(row).equals(selectedRow)) { + select(null); + } + return false; + } + + @Override + public JsonObject getSelectedRow() { + throw new UnsupportedOperationException( + "This client-side selection model " + + getClass().getSimpleName() + + " does not know selected row."); + } + + @Override + public void setDeselectAllowed(boolean deselectAllowed) { + this.deselectAllowed = deselectAllowed; + } + + @Override + public boolean isDeselectAllowed() { + return deselectAllowed; + } + } +} \ No newline at end of file diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index a2eedf4203..91fc87d2c7 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -5287,7 +5287,7 @@ public class Grid extends ResizeComposite implements @Override public void preDetach(Row row, Iterable cellsToDetach) { for (FlyweightCell cell : cellsToDetach) { - Renderer renderer = findRenderer(cell); + Renderer renderer = findRenderer(cell); if (renderer instanceof WidgetRenderer) { try { Widget w = WidgetUtil.findWidget(cell.getElement() @@ -5317,7 +5317,7 @@ public class Grid extends ResizeComposite implements // any more rowReference.set(rowIndex, null, row.getElement()); for (FlyweightCell cell : detachedCells) { - Renderer renderer = findRenderer(cell); + Renderer renderer = findRenderer(cell); if (renderer instanceof ComplexRenderer) { try { Column column = getVisibleColumn(cell.getColumn()); @@ -7233,12 +7233,12 @@ public class Grid extends ResizeComposite implements * if the current selection model is not an instance of * {@link SelectionModel.Single} or {@link SelectionModel.Multi} */ - @SuppressWarnings("unchecked") public boolean select(T row) { if (selectionModel instanceof SelectionModel.Single) { return ((SelectionModel.Single) selectionModel).select(row); } else if (selectionModel instanceof SelectionModel.Multi) { - return ((SelectionModel.Multi) selectionModel).select(row); + return ((SelectionModel.Multi) selectionModel) + .select(Collections.singleton(row)); } else { throw new IllegalStateException("Unsupported selection model"); } @@ -7258,12 +7258,12 @@ public class Grid extends ResizeComposite implements * if the current selection model is not an instance of * {@link SelectionModel.Single} or {@link SelectionModel.Multi} */ - @SuppressWarnings("unchecked") public boolean deselect(T row) { if (selectionModel instanceof SelectionModel.Single) { return ((SelectionModel.Single) selectionModel).deselect(row); } else if (selectionModel instanceof SelectionModel.Multi) { - return ((SelectionModel.Multi) selectionModel).deselect(row); + return ((SelectionModel.Multi) selectionModel) + .deselect(Collections.singleton(row)); } else { throw new IllegalStateException("Unsupported selection model"); } diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index f5d712f6b2..c8f3604fd9 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -744,15 +744,11 @@ public class RpcDataProviderExtension extends AbstractExtension { // Send current rows again if needed. if (refreshCache) { - for (Object itemId : activeItemHandler.getActiveItemIds()) { - internalUpdateRowData(itemId); - } + updatedItemIds.addAll(activeItemHandler.getActiveItemIds()); } } - for (Object itemId : updatedItemIds) { - internalUpdateRowData(itemId); - } + internalUpdateRows(updatedItemIds); // Clear all changes. rowChanges.clear(); @@ -913,11 +909,20 @@ public class RpcDataProviderExtension extends AbstractExtension { updatedItemIds.add(itemId); } - private void internalUpdateRowData(Object itemId) { - if (activeItemHandler.getActiveItemIds().contains(itemId)) { - JsonObject row = getRowData(getGrid().getColumns(), itemId); - rpc.updateRowData(row); + private void internalUpdateRows(Set itemIds) { + if (itemIds.isEmpty()) { + return; + } + + JsonArray rowData = Json.createArray(); + int i = 0; + for (Object itemId : itemIds) { + if (activeItemHandler.getActiveItemIds().contains(itemId)) { + JsonObject row = getRowData(getGrid().getColumns(), itemId); + rowData.set(i++, row); + } } + rpc.updateRowData(rowData); } /** diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index d9c011677b..f58280c6fe 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -82,6 +82,7 @@ import com.vaadin.server.AbstractClientConnector; import com.vaadin.server.AbstractExtension; import com.vaadin.server.EncodeResult; import com.vaadin.server.ErrorMessage; +import com.vaadin.server.Extension; import com.vaadin.server.JsonCodec; import com.vaadin.server.KeyMapper; import com.vaadin.server.VaadinSession; @@ -94,13 +95,16 @@ import com.vaadin.shared.ui.grid.GridColumnState; import com.vaadin.shared.ui.grid.GridConstants; import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.shared.ui.grid.GridState.SharedSelectionMode; import com.vaadin.shared.ui.grid.GridStaticCellType; import com.vaadin.shared.ui.grid.GridStaticSectionState; import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState; import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.ScrollDestination; +import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc; +import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState; +import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc; +import com.vaadin.shared.ui.grid.selection.SingleSelectionModelState; import com.vaadin.shared.util.SharedUtil; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; @@ -111,7 +115,6 @@ import com.vaadin.ui.renderers.TextRenderer; import com.vaadin.util.ReflectTools; import elemental.json.Json; -import elemental.json.JsonArray; import elemental.json.JsonObject; import elemental.json.JsonValue; @@ -710,8 +713,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, /** * The server-side interface that controls Grid's selection state. + * SelectionModel should extend {@link AbstractGridExtension}. */ - public interface SelectionModel extends Serializable { + public interface SelectionModel extends Serializable, Extension { /** * Checks whether an item is selected or not. * @@ -730,6 +734,8 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, /** * Injects the current {@link Grid} instance into the SelectionModel. + * This method should usually call the extend method of + * {@link AbstractExtension}. *

    * Note: This method should not be called manually. * @@ -971,10 +977,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * A base class for SelectionModels that contains some of the logic that is * reusable. */ - public static abstract class AbstractSelectionModel implements - SelectionModel { + public static abstract class AbstractSelectionModel extends + AbstractGridExtension implements SelectionModel, DataGenerator { protected final LinkedHashSet selection = new LinkedHashSet(); - protected Grid grid = null; @Override public boolean isSelected(final Object itemId) { @@ -988,7 +993,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, @Override public void setGrid(final Grid grid) { - this.grid = grid; + if (grid != null) { + extend(grid); + } } /** @@ -1002,7 +1009,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, */ protected void checkItemIdExists(Object itemId) throws IllegalArgumentException { - if (!grid.getContainerDataSource().containsId(itemId)) { + if (!getParentGrid().getContainerDataSource().containsId(itemId)) { throw new IllegalArgumentException("Given item id (" + itemId + ") does not exist in the container"); } @@ -1044,7 +1051,19 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, protected void fireSelectionEvent( final Collection oldSelection, final Collection newSelection) { - grid.fireSelectionEvent(oldSelection, newSelection); + getParentGrid().fireSelectionEvent(oldSelection, newSelection); + } + + @Override + public void generateData(Object itemId, Item item, JsonObject rowData) { + if (isSelected(itemId)) { + rowData.put(GridState.JSONKEY_SELECTED, true); + } + } + + @Override + protected Object getItemId(String rowKey) { + return rowKey != null ? super.getItemId(rowKey) : null; } } @@ -1053,8 +1072,25 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, */ public static class SingleSelectionModel extends AbstractSelectionModel implements SelectionModel.Single { + + @Override + protected void extend(AbstractClientConnector target) { + super.extend(target); + registerRpc(new SingleSelectionModelServerRpc() { + + @Override + public void select(String rowKey) { + SingleSelectionModel.this.select(getItemId(rowKey), false); + } + }); + } + @Override public boolean select(final Object itemId) { + return select(itemId, true); + } + + protected boolean select(final Object itemId, boolean refresh) { if (itemId == null) { return deselect(getSelectedRow()); } @@ -1066,7 +1102,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, if (modified) { final Collection deselected; if (selectedRow != null) { - deselectInternal(selectedRow, false); + deselectInternal(selectedRow, false, true); deselected = Collections.singleton(selectedRow); } else { deselected = Collections.emptySet(); @@ -1075,19 +1111,28 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, fireSelectionEvent(deselected, selection); } + if (refresh) { + refreshRow(itemId); + } + return modified; } private boolean deselect(final Object itemId) { - return deselectInternal(itemId, true); + return deselectInternal(itemId, true, true); } private boolean deselectInternal(final Object itemId, - boolean fireEventIfNeeded) { + boolean fireEventIfNeeded, boolean refresh) { final boolean modified = selection.remove(itemId); - if (fireEventIfNeeded && modified) { - fireSelectionEvent(Collections.singleton(itemId), - Collections.emptySet()); + if (modified) { + if (refresh) { + refreshRow(itemId); + } + if (fireEventIfNeeded) { + fireSelectionEvent(Collections.singleton(itemId), + Collections.emptySet()); + } } return modified; } @@ -1113,23 +1158,25 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, @Override public void setDeselectAllowed(boolean deselectAllowed) { - grid.getState().singleSelectDeselectAllowed = deselectAllowed; + getState().deselectAllowed = deselectAllowed; } @Override public boolean isDeselectAllowed() { - return grid.getState(false).singleSelectDeselectAllowed; + return getState().deselectAllowed; + } + + @Override + protected SingleSelectionModelState getState() { + return (SingleSelectionModelState) super.getState(); } } /** * A default implementation for a {@link SelectionModel.None} */ - public static class NoSelectionModel implements SelectionModel.None { - @Override - public void setGrid(final Grid grid) { - // NOOP, not needed for anything - } + public static class NoSelectionModel extends AbstractSelectionModel + implements SelectionModel.None { @Override public boolean isSelected(final Object itemId) { @@ -1167,7 +1214,40 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, private int selectionLimit = DEFAULT_MAX_SELECTIONS; - private boolean allSelected; + @Override + protected void extend(AbstractClientConnector target) { + super.extend(target); + registerRpc(new MultiSelectionModelServerRpc() { + + @Override + public void select(List rowKeys) { + List items = new ArrayList(); + for (String rowKey : rowKeys) { + items.add(getItemId(rowKey)); + } + MultiSelectionModel.this.select(items, false); + } + + @Override + public void deselect(List rowKeys) { + List items = new ArrayList(); + for (String rowKey : rowKeys) { + items.add(getItemId(rowKey)); + } + MultiSelectionModel.this.deselect(items, false); + } + + @Override + public void selectAll() { + MultiSelectionModel.this.selectAll(false); + } + + @Override + public void deselectAll() { + MultiSelectionModel.this.deselectAll(false); + } + }); + } @Override public boolean select(final Object... itemIds) @@ -1190,6 +1270,10 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, @Override public boolean select(final Collection itemIds) throws IllegalArgumentException { + return select(itemIds, true); + } + + protected boolean select(final Collection itemIds, boolean refresh) { if (itemIds == null) { throw new IllegalArgumentException("itemIds may not be null"); } @@ -1217,6 +1301,12 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, updateAllSelectedState(); + if (refresh) { + for (Object itemId : itemIds) { + refreshRow(itemId); + } + } + return selectionWillChange; } @@ -1270,6 +1360,10 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, @Override public boolean deselect(final Collection itemIds) throws IllegalArgumentException { + return deselect(itemIds, true); + } + + protected boolean deselect(final Collection itemIds, boolean refresh) { if (itemIds == null) { throw new IllegalArgumentException("itemIds may not be null"); } @@ -1285,15 +1379,25 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, updateAllSelectedState(); + if (refresh) { + for (Object itemId : itemIds) { + refreshRow(itemId); + } + } + return hasCommonElements; } @Override public boolean selectAll() { + return selectAll(true); + } + + protected boolean selectAll(boolean refresh) { // select will fire the event - final Indexed container = grid.getContainerDataSource(); + final Indexed container = getParentGrid().getContainerDataSource(); if (container != null) { - return select(container.getItemIds()); + return select(container.getItemIds(), refresh); } else if (selection.isEmpty()) { return false; } else { @@ -1302,14 +1406,18 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * but I guess the only theoretically correct course of * action... */ - return deselectAll(); + return deselectAll(false); } } @Override public boolean deselectAll() { + return deselectAll(true); + } + + protected boolean deselectAll(boolean refresh) { // deselect will fire the event - return deselect(getSelectedRows()); + return deselect(getSelectedRows(), refresh); } /** @@ -1382,11 +1490,15 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, } private void updateAllSelectedState() { - if (allSelected != selection.size() >= selectionLimit) { - allSelected = selection.size() >= selectionLimit; - grid.getRpcProxy(GridClientRpc.class).setSelectAll(allSelected); + if (getState().allSelected != selection.size() >= selectionLimit) { + getState().allSelected = selection.size() >= selectionLimit; } } + + @Override + protected MultiSelectionModelState getState() { + return (MultiSelectionModelState) super.getState(); + } } /** @@ -1571,7 +1683,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * Grid rows. If a description is generated for a row, it is used for all * the cells in the row for which a {@link CellDescriptionGenerator cell * description} is not generated. - * + * * @see Grid#setRowDescriptionGenerator(CellDescriptionGenerator) * * @since 7.6 @@ -3783,6 +3895,17 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, + " instead"); } } + + /** + * Resends the row data for given item id to the client. + * + * @since + * @param itemId + * row to refresh + */ + protected void refreshRow(Object itemId) { + getParentGrid().datasourceExtension.updateRowData(itemId); + } } /** @@ -3982,116 +4105,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, */ private void initGrid() { setSelectionMode(getDefaultSelectionMode()); - addSelectionListener(new SelectionListener() { - @Override - public void select(SelectionEvent event) { - if (applyingSelectionFromClient) { - /* - * Avoid sending changes back to the client if they - * originated from the client. Instead, the RPC handler is - * responsible for keeping track of the resulting selection - * state and notifying the client if it doens't match the - * expectation. - */ - return; - } - - /* - * The rows are pinned here to ensure that the client gets the - * correct key from server when the selected row is first - * loaded. - * - * Once the client has gotten info that it is supposed to select - * a row, it will pin the data from the client side as well and - * it will be unpinned once it gets deselected. Nothing on the - * server side should ever unpin anything from KeyMapper. - * Pinning is mostly a client feature and is only used when - * selecting something from the server side. - */ - for (Object addedItemId : event.getAdded()) { - if (!getKeyMapper().isPinned(addedItemId)) { - getKeyMapper().pin(addedItemId); - } - } - - getState().selectedKeys = getKeyMapper().getKeys( - getSelectedRows()); - } - }); registerRpc(new GridServerRpc() { - @Override - public void select(List selection) { - Collection receivedSelection = getKeyMapper() - .getItemIds(selection); - - applyingSelectionFromClient = true; - try { - SelectionModel selectionModel = getSelectionModel(); - if (selectionModel instanceof SelectionModel.Single - && selection.size() <= 1) { - Object select = null; - if (selection.size() == 1) { - select = getKeyMapper().getItemId(selection.get(0)); - } - ((SelectionModel.Single) selectionModel).select(select); - } else if (selectionModel instanceof SelectionModel.Multi) { - ((SelectionModel.Multi) selectionModel) - .setSelected(receivedSelection); - } else { - throw new IllegalStateException("SelectionModel " - + selectionModel.getClass().getSimpleName() - + " does not support selecting the given " - + selection.size() + " items."); - } - } finally { - applyingSelectionFromClient = false; - } - - Collection actualSelection = getSelectedRows(); - - // Make sure all selected rows are pinned - for (Object itemId : actualSelection) { - if (!getKeyMapper().isPinned(itemId)) { - getKeyMapper().pin(itemId); - } - } - - // Don't mark as dirty since this might be the expected state - getState(false).selectedKeys = getKeyMapper().getKeys( - actualSelection); - - JsonObject diffState = getUI().getConnectorTracker() - .getDiffState(Grid.this); - - final String diffstateKey = "selectedKeys"; - - assert diffState.hasKey(diffstateKey) : "Field name has changed"; - - if (receivedSelection.equals(actualSelection)) { - /* - * We ended up with the same selection state that the client - * sent us. There's nothing to send back to the client, just - * update the diffstate so subsequent changes will be - * detected. - */ - JsonArray diffSelected = Json.createArray(); - for (String rowKey : getState(false).selectedKeys) { - diffSelected.set(diffSelected.length(), rowKey); - } - diffState.put(diffstateKey, diffSelected); - } else { - /* - * Actual selection is not what the client expects. Make - * sure the client gets a state change event by clearing the - * diffstate and marking as dirty - */ - diffState.remove(diffstateKey); - markAsDirty(); - } - } - @Override public void sort(String[] columnIds, SortDirection[] directions, boolean userOriginated) { @@ -4120,13 +4136,6 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, } } - @Override - public void selectAll() { - assert getSelectionModel() instanceof SelectionModel.Multi : "Not a multi selection model!"; - - ((SelectionModel.Multi) getSelectionModel()).selectAll(); - } - @Override public void itemClick(String rowKey, String columnId, MouseEventDetails details) { @@ -5019,25 +5028,11 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, if (this.selectionModel != selectionModel) { // this.selectionModel is null on init if (this.selectionModel != null) { - this.selectionModel.reset(); - this.selectionModel.setGrid(null); + this.selectionModel.remove(); } this.selectionModel = selectionModel; - this.selectionModel.setGrid(this); - this.selectionModel.reset(); - - if (selectionModel.getClass().equals(SingleSelectionModel.class)) { - getState().selectionMode = SharedSelectionMode.SINGLE; - } else if (selectionModel.getClass().equals( - MultiSelectionModel.class)) { - getState().selectionMode = SharedSelectionMode.MULTI; - } else if (selectionModel.getClass().equals(NoSelectionModel.class)) { - getState().selectionMode = SharedSelectionMode.NONE; - } else { - throw new UnsupportedOperationException("Grid currently " - + "supports only its own bundled selection models"); - } + selectionModel.setGrid(this); } } diff --git a/shared/src/com/vaadin/shared/data/DataProviderRpc.java b/shared/src/com/vaadin/shared/data/DataProviderRpc.java index 28e50d9747..05965ea56c 100644 --- a/shared/src/com/vaadin/shared/data/DataProviderRpc.java +++ b/shared/src/com/vaadin/shared/data/DataProviderRpc.java @@ -20,7 +20,6 @@ import com.vaadin.shared.annotations.NoLayout; import com.vaadin.shared.communication.ClientRpc; import elemental.json.JsonArray; -import elemental.json.JsonObject; /** * RPC interface used for pushing container data to the client. @@ -94,13 +93,14 @@ public interface DataProviderRpc extends ClientRpc { public void resetDataAndSize(int size); /** - * Informs the client that a row has updated. The client-side DataSource - * will map the given data to correct index if it should be in the cache. + * Informs the client that rows have been updated. The client-side + * DataSource will map the given data to correct index if it should be in + * the cache. * * @since 7.6 - * @param row - * the updated row data + * @param rowArray + * array of updated rows */ @NoLayout - public void updateRowData(JsonObject row); + public void updateRowData(JsonArray rowArray); } diff --git a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java index 8711a757a1..ac1b1d5a78 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java @@ -55,15 +55,4 @@ public interface GridClientRpc extends ClientRpc { * Command client Grid to recalculate column widths. */ public void recalculateColumnWidths(); - - /** - * Informs the Grid that all items have been selected or not selected on the - * server side. - * - * @since 7.5.3 - * @param allSelected - * true to check the select all checkbox, - * false to uncheck it. - */ - public void setSelectAll(boolean allSelected); } diff --git a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java index a178ff63d0..8d64794b41 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java @@ -29,10 +29,6 @@ import com.vaadin.shared.data.sort.SortDirection; */ public interface GridServerRpc extends ServerRpc { - void select(List newSelection); - - void selectAll(); - void sort(String[] columnIds, SortDirection[] directions, boolean userOriginated); diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index 0d0a5d3e9f..e51b1a78a7 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -128,6 +128,13 @@ public class GridState extends TabIndexState { * */ public static final String JSONKEY_DETAILS_VISIBLE = "dv"; + /** + * The key that tells whether row is selected. + * + * @since + */ + public static final String JSONKEY_SELECTED = "s"; + /** * Columns in grid. */ @@ -153,14 +160,6 @@ public class GridState extends TabIndexState { @DelegateToWidget public HeightMode heightMode = HeightMode.CSS; - // instantiated just to avoid NPEs - public List selectedKeys = new ArrayList(); - - public SharedSelectionMode selectionMode; - - /** Whether single select mode can be cleared through the UI */ - public boolean singleSelectDeselectAllowed = true; - /** Keys of the currently sorted columns */ public String[] sortColumns = new String[0]; diff --git a/shared/src/com/vaadin/shared/ui/grid/selection/MultiSelectionModelServerRpc.java b/shared/src/com/vaadin/shared/ui/grid/selection/MultiSelectionModelServerRpc.java new file mode 100644 index 0000000000..e7324552f4 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/selection/MultiSelectionModelServerRpc.java @@ -0,0 +1,55 @@ +/* + * 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.shared.ui.grid.selection; + +import java.util.List; + +import com.vaadin.shared.communication.ServerRpc; + +/** + * ServerRpc for MultiSelectionModel. + * + * @since + * @author Vaadin Ltd + */ +public interface MultiSelectionModelServerRpc extends ServerRpc { + + /** + * Select a list of rows based on their row keys on the server-side. + * + * @param rowKeys + * list of row keys to select + */ + public void select(List rowKeys); + + /** + * Deselect a list of rows based on their row keys on the server-side. + * + * @param rowKeys + * list of row keys to deselect + */ + public void deselect(List rowKeys); + + /** + * Selects all rows on the server-side. + */ + public void selectAll(); + + /** + * Deselects all rows on the server-side. + */ + public void deselectAll(); +} diff --git a/shared/src/com/vaadin/shared/ui/grid/selection/MultiSelectionModelState.java b/shared/src/com/vaadin/shared/ui/grid/selection/MultiSelectionModelState.java new file mode 100644 index 0000000000..3ffd0d0f93 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/selection/MultiSelectionModelState.java @@ -0,0 +1,31 @@ +/* + * 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.shared.ui.grid.selection; + +import com.vaadin.shared.communication.SharedState; + +/** + * SharedState object for MultiSelectionModel. + * + * @since + * @author Vaadin Ltd + */ +public class MultiSelectionModelState extends SharedState { + + /* Select All -checkbox status */ + public boolean allSelected; + +} diff --git a/shared/src/com/vaadin/shared/ui/grid/selection/SingleSelectionModelServerRpc.java b/shared/src/com/vaadin/shared/ui/grid/selection/SingleSelectionModelServerRpc.java new file mode 100644 index 0000000000..3e2a8ec847 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/selection/SingleSelectionModelServerRpc.java @@ -0,0 +1,35 @@ +/* + * 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.shared.ui.grid.selection; + +import com.vaadin.shared.communication.ServerRpc; + +/** + * ServerRpc for SingleSelectionModel. + * + * @since + * @author Vaadin Ltd + */ +public interface SingleSelectionModelServerRpc extends ServerRpc { + + /** + * Selects a row on server-side. + * + * @param rowKey + * row key of selected row; {@code null} if deselect + */ + public void select(String rowKey); +} diff --git a/shared/src/com/vaadin/shared/ui/grid/selection/SingleSelectionModelState.java b/shared/src/com/vaadin/shared/ui/grid/selection/SingleSelectionModelState.java new file mode 100644 index 0000000000..719b60b2ba --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/selection/SingleSelectionModelState.java @@ -0,0 +1,30 @@ +/* + * 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.shared.ui.grid.selection; + +import com.vaadin.shared.communication.SharedState; + +/** + * SharedState object for SingleSelectionModel. + * + * @since + * @author Vaadin Ltd + */ +public class SingleSelectionModelState extends SharedState { + + /* Allow deselecting rows */ + public boolean deselectAllowed; +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridCustomSelectionModel.java b/uitest/src/com/vaadin/tests/components/grid/GridCustomSelectionModel.java new file mode 100644 index 0000000000..008c24cdd3 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridCustomSelectionModel.java @@ -0,0 +1,37 @@ +/* + * 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.tests.components.grid; + +import com.vaadin.annotations.Widgetset; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.tests.widgetset.TestingWidgetSet; +import com.vaadin.ui.Grid.MultiSelectionModel; + +@Widgetset(TestingWidgetSet.NAME) +public class GridCustomSelectionModel extends AbstractTestUI { + + public static class MySelectionModel extends MultiSelectionModel { + } + + @Override + protected void setup(VaadinRequest request) { + PersonTestGrid grid = new PersonTestGrid(500); + grid.setSelectionModel(new MySelectionModel()); + addComponent(grid); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridCustomSelectionModelTest.java b/uitest/src/com/vaadin/tests/components/grid/GridCustomSelectionModelTest.java new file mode 100644 index 0000000000..976e1e78fe --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridCustomSelectionModelTest.java @@ -0,0 +1,58 @@ +/* + * 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.tests.components.grid; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; + +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.elements.GridElement.GridCellElement; +import com.vaadin.testbench.parallel.TestCategory; +import com.vaadin.tests.tb3.MultiBrowserTest; + +@TestCategory("grid") +public class GridCustomSelectionModelTest extends MultiBrowserTest { + + @Test + public void testCustomSelectionModel() { + setDebug(true); + openTestURL(); + + GridElement grid = $(GridElement.class).first(); + GridCellElement cell = grid.getCell(0, 0); + assertTrue("First column of Grid should not have an input element", + cell.findElements(By.className("input")).isEmpty()); + + assertFalse("Row should not be selected initially", grid.getRow(0) + .isSelected()); + + cell.click(5, 5); + assertTrue("Click should select row", grid.getRow(0).isSelected()); + cell.click(5, 5); + assertFalse("Click should deselect row", grid.getRow(0).isSelected()); + + grid.sendKeys(Keys.SPACE); + assertTrue("Space should select row", grid.getRow(0).isSelected()); + grid.sendKeys(Keys.SPACE); + assertFalse("Space should deselect row", grid.getRow(0).isSelected()); + + assertNoErrorNotifications(); + } +} diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/MySelectionModelConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/MySelectionModelConnector.java new file mode 100644 index 0000000000..81a9ab5bf1 --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/MySelectionModelConnector.java @@ -0,0 +1,61 @@ +/* + * 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.tests.widgetset.client.grid; + +import com.vaadin.client.ServerConnector; +import com.vaadin.client.connectors.MultiSelectionModelConnector; +import com.vaadin.client.renderers.ComplexRenderer; +import com.vaadin.client.widget.grid.selection.ClickSelectHandler; +import com.vaadin.client.widget.grid.selection.SelectionModel.Multi; +import com.vaadin.client.widgets.Grid; +import com.vaadin.shared.ui.Connect; +import com.vaadin.tests.components.grid.GridCustomSelectionModel.MySelectionModel; + +import elemental.json.JsonObject; + +@Connect(MySelectionModel.class) +public class MySelectionModelConnector extends MultiSelectionModelConnector { + + private ClickSelectHandler handler; + + @Override + protected void extend(ServerConnector target) { + super.extend(target); + handler = new ClickSelectHandler(getGrid()); + } + + @Override + public void onUnregister() { + super.onUnregister(); + handler.removeHandler(); + handler = null; + } + + @Override + protected Multi createSelectionModel() { + return new MySelectionModel(); + } + + public class MySelectionModel extends MultiSelectionModel { + + @Override + protected ComplexRenderer createSelectionColumnRenderer( + Grid grid) { + // No Selection Column. + return null; + } + } +} -- cgit v1.2.3 From 67a1ecf6a12b2b749458022ade72beac398a3038 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Thu, 27 Aug 2015 16:55:03 +0300 Subject: Do not open detail row if generator is the NULL one (#18663) Change-Id: Ib853205dae7745ca4af11fb558899e6648e25da8 --- client/src/com/vaadin/client/widgets/Grid.java | 4 ++ server/src/com/vaadin/ui/Grid.java | 3 ++ .../components/grid/GridDetailsLocationTest.java | 19 --------- .../grid/basicfeatures/GridColumnHidingTest.java | 1 + .../client/GridDetailsClientTest.java | 46 ++++++++++++++++------ .../server/GridDetailsServerTest.java | 37 ++++++----------- 6 files changed, 55 insertions(+), 55 deletions(-) (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index dbce8c1bbd..19fd77e75d 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -8226,6 +8226,10 @@ public class Grid extends ResizeComposite implements * @see #isDetailsVisible(int) */ public void setDetailsVisible(int rowIndex, boolean visible) { + if (DetailsGenerator.NULL.equals(detailsGenerator)) { + return; + } + Integer rowIndexInteger = Integer.valueOf(rowIndex); /* diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index f58280c6fe..204f771ee5 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -6597,6 +6597,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * to hide them */ public void setDetailsVisible(Object itemId, boolean visible) { + if (DetailsGenerator.NULL.equals(detailsGenerator)) { + return; + } datasourceExtension.setDetailsVisible(itemId, visible); } diff --git a/uitest/src/com/vaadin/tests/components/grid/GridDetailsLocationTest.java b/uitest/src/com/vaadin/tests/components/grid/GridDetailsLocationTest.java index 33f66d35be..a395d7e721 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridDetailsLocationTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridDetailsLocationTest.java @@ -86,9 +86,7 @@ public class GridDetailsLocationTest extends MultiBrowserTest { for (int rowIndex : params) { - data.add(new Param(rowIndex, false, false)); data.add(new Param(rowIndex, true, false)); - data.add(new Param(rowIndex, false, true)); data.add(new Param(rowIndex, true, true)); } @@ -137,23 +135,6 @@ public class GridDetailsLocationTest extends MultiBrowserTest { } } - @Test - public void testDetailsHeightWithNoGenerator() { - openTestURL(); - toggleAndScroll(5); - - verifyDetailsRowHeight(5, detailsDefaultHeight, 0); - verifyDetailsDecoratorLocation(5, 0, 0); - - toggleAndScroll(0); - - verifyDetailsRowHeight(0, detailsDefaultHeight, 0); - verifyDetailsDecoratorLocation(0, 0, 1); - - verifyDetailsRowHeight(5, detailsDefaultHeight, 1); - verifyDetailsDecoratorLocation(5, 1, 0); - } - @Test public void testDetailsHeightWithGenerator() { openTestURL(); diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridColumnHidingTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridColumnHidingTest.java index 0717cd84d0..a169e701c0 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridColumnHidingTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridColumnHidingTest.java @@ -823,6 +823,7 @@ public class GridColumnHidingTest extends GridBasicClientFeaturesTest { @Test public void testColumnHiding_detailsRowIsOpen_renderedCorrectly() { + selectMenuPath("Component", "Row details", "Set generator"); selectMenuPath("Component", "Row details", "Toggle details for...", "Row 1"); assertColumnHeaderOrder(0, 1, 2, 3, 4); diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridDetailsClientTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridDetailsClientTest.java index 73f9d584a6..01e7e52923 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridDetailsClientTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridDetailsClientTest.java @@ -17,6 +17,7 @@ package com.vaadin.tests.components.grid.basicfeatures.client; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -58,14 +59,10 @@ public class GridDetailsClientTest extends GridBasicClientFeaturesTest { getGridElement().getDetails(1)); } - @Test - public void nullRendererShowsDetailsPlaceholder() { + @Test(expected = NoSuchElementException.class) + public void nullRendererDoesNotShowDetailsPlaceholder() { toggleDetailsFor(1); - TestBenchElement details = getGridElement().getDetails(1); - assertNotNull("details for row 1 should not exist at the start", - details); - assertTrue("details should've been empty for null renderer", details - .getText().isEmpty()); + getGridElement().getDetails(1); } @Test @@ -78,6 +75,14 @@ public class GridDetailsClientTest extends GridBasicClientFeaturesTest { details.getText().startsWith("Row: 1.")); } + @Test(expected = NoSuchElementException.class) + public void openDetailsThenAppyRendererShouldNotShowDetails() { + toggleDetailsFor(1); + selectMenuPath(SET_GENERATOR); + + getGridElement().getDetails(1); + } + @Test public void openHiddenDetailsThenScrollToIt() { try { @@ -122,10 +127,24 @@ public class GridDetailsClientTest extends GridBasicClientFeaturesTest { toggleDetailsFor(1); selectMenuPath(SET_FAULTY_GENERATOR); - getGridElement().getDetails(1); } + @Test + public void settingNewGeneratorStillWorksAfterError() { + selectMenuPath(SET_FAULTY_GENERATOR); + toggleDetailsFor(1); + $(FixedNotificationElement.class).first().close(); + toggleDetailsFor(1); + + selectMenuPath(SET_GENERATOR); + toggleDetailsFor(1); + + assertNotEquals( + "New details should've been generated even after error", "", + getGridElement().getDetails(1).getText()); + } + @Test public void updaterRendersExpectedWidgets() { selectMenuPath(SET_GENERATOR); @@ -171,6 +190,7 @@ public class GridDetailsClientTest extends GridBasicClientFeaturesTest { @Test public void rowElementClassNames() { + selectMenuPath(SET_GENERATOR); toggleDetailsFor(0); toggleDetailsFor(1); @@ -183,25 +203,28 @@ public class GridDetailsClientTest extends GridBasicClientFeaturesTest { @Test public void scrollDownToRowWithDetails() { + selectMenuPath(SET_GENERATOR); toggleDetailsFor(100); scrollToRow(100, ScrollDestination.ANY); - Range validScrollRange = Range.between(1700, 1715); + Range validScrollRange = Range.between(1691, 1706); assertTrue(validScrollRange.contains(getGridVerticalScrollPos())); } @Test public void scrollUpToRowWithDetails() { + selectMenuPath(SET_GENERATOR); toggleDetailsFor(100); scrollGridVerticallyTo(999999); scrollToRow(100, ScrollDestination.ANY); - Range validScrollRange = Range.between(1990, 2010); + Range validScrollRange = Range.between(1981, 2001); assertTrue(validScrollRange.contains(getGridVerticalScrollPos())); } @Test public void cannotScrollBeforeTop() { + selectMenuPath(SET_GENERATOR); toggleDetailsFor(1); scrollToRow(0, ScrollDestination.END); assertEquals(0, getGridVerticalScrollPos()); @@ -209,10 +232,11 @@ public class GridDetailsClientTest extends GridBasicClientFeaturesTest { @Test public void cannotScrollAfterBottom() { + selectMenuPath(SET_GENERATOR); toggleDetailsFor(999); scrollToRow(999, ScrollDestination.START); - Range expectedRange = Range.withLength(19680, 20); + Range expectedRange = Range.withLength(19671, 20); assertTrue(expectedRange.contains(getGridVerticalScrollPos())); } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridDetailsServerTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridDetailsServerTest.java index 326dbcd55f..a9ab7027db 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridDetailsServerTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridDetailsServerTest.java @@ -58,7 +58,7 @@ public class GridDetailsServerTest extends GridBasicFeaturesTest { openTestURL(); } - @Test + @Test(expected = NoSuchElementException.class) public void openVisibleDetails() { try { getGridElement().getDetails(0); @@ -67,8 +67,7 @@ public class GridDetailsServerTest extends GridBasicFeaturesTest { // expected } selectMenuPath(OPEN_FIRST_ITEM_DETAILS); - assertNotNull("details should've opened", getGridElement() - .getDetails(0)); + getGridElement().getDetails(0); } @Test(expected = NoSuchElementException.class) @@ -98,6 +97,7 @@ public class GridDetailsServerTest extends GridBasicFeaturesTest { @Test public void openDetailsOutsideOfActiveRange() throws InterruptedException { getGridElement().scroll(10000); + selectMenuPath(DETAILS_GENERATOR_WATCHING); selectMenuPath(OPEN_FIRST_ITEM_DETAILS); getGridElement().scroll(0); Thread.sleep(50); @@ -215,13 +215,15 @@ public class GridDetailsServerTest extends GridBasicFeaturesTest { @Test public void swappingDetailsGenerators_shownDetails() { + selectMenuPath(DETAILS_GENERATOR_HIERARCHICAL); selectMenuPath(OPEN_FIRST_ITEM_DETAILS); - assertTrue("Details should be empty at the start", getGridElement() - .getDetails(0).getText().isEmpty()); + assertTrue("Details should contain 'One' at first", getGridElement() + .getDetails(0).getText().contains("One")); selectMenuPath(DETAILS_GENERATOR_WATCHING); - assertFalse("Details should not be empty after swapping generator", - getGridElement().getDetails(0).getText().isEmpty()); + assertFalse( + "Details should contain 'Watching' after swapping generator", + getGridElement().getDetails(0).getText().contains("Watching")); } @Test @@ -235,6 +237,7 @@ public class GridDetailsServerTest extends GridBasicFeaturesTest { @Test public void swappingDetailsGenerators_whileDetailsScrolledOut_showAfter() { scrollGridVerticallyTo(1000); + selectMenuPath(DETAILS_GENERATOR_HIERARCHICAL); selectMenuPath(OPEN_FIRST_ITEM_DETAILS); selectMenuPath(DETAILS_GENERATOR_WATCHING); scrollGridVerticallyTo(0); @@ -246,6 +249,7 @@ public class GridDetailsServerTest extends GridBasicFeaturesTest { @Test public void swappingDetailsGenerators_whileDetailsScrolledOut_showBefore() { + selectMenuPath(DETAILS_GENERATOR_HIERARCHICAL); selectMenuPath(OPEN_FIRST_ITEM_DETAILS); selectMenuPath(DETAILS_GENERATOR_WATCHING); scrollGridVerticallyTo(1000); @@ -257,6 +261,7 @@ public class GridDetailsServerTest extends GridBasicFeaturesTest { @Test public void swappingDetailsGenerators_whileDetailsScrolledOut_showBeforeAndAfter() { + selectMenuPath(DETAILS_GENERATOR_HIERARCHICAL); selectMenuPath(OPEN_FIRST_ITEM_DETAILS); selectMenuPath(DETAILS_GENERATOR_WATCHING); scrollGridVerticallyTo(1000); @@ -267,24 +272,6 @@ public class GridDetailsServerTest extends GridBasicFeaturesTest { .getDetails(0)); } - @Test - public void nullDetailComponentToggling() { - selectMenuPath(OPEN_FIRST_ITEM_DETAILS); - selectMenuPath(DETAILS_GENERATOR_WATCHING); - selectMenuPath(DETAILS_GENERATOR_NULL); - - try { - assertTrue("Details should be empty with null component", - getGridElement().getDetails(0).getText().isEmpty()); - } catch (NoSuchElementException e) { - fail("Expected to find a details row with empty content"); - } - - selectMenuPath(DETAILS_GENERATOR_WATCHING); - assertFalse("Details should be not empty with details component", - getGridElement().getDetails(0).getText().isEmpty()); - } - @Test public void noAssertErrorsOnEmptyDetailsAndScrollDown() { selectMenuPath(OPEN_FIRST_ITEM_DETAILS); -- cgit v1.2.3 From ac66a3d17446571c6ebace16cb3930df44381ad7 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Mon, 31 Aug 2015 14:37:30 +0300 Subject: Redesign RpcDataSourceConnector pinning RPC requests (#18692) This patch removes DataProviderKeyMapper which was mostly dead code already. Uses a regular KeyMapper instead. Change-Id: Ic97d1dc827d45fde65bcddc0414bfe711032620c --- .../connectors/MultiSelectionModelConnector.java | 75 ++++--- .../client/connectors/RpcDataSourceConnector.java | 20 +- .../client/data/AbstractRemoteDataSource.java | 10 +- .../com/vaadin/data/RpcDataProviderExtension.java | 249 ++------------------- server/src/com/vaadin/ui/Grid.java | 19 +- .../component/grid/DataProviderExtension.java | 88 -------- .../src/com/vaadin/shared/data/DataRequestRpc.java | 14 -- .../tests/components/grid/CustomRendererTest.java | 4 +- .../components/grid/JavaScriptRenderersTest.java | 2 +- 9 files changed, 91 insertions(+), 390 deletions(-) delete mode 100644 server/tests/src/com/vaadin/tests/server/component/grid/DataProviderExtension.java (limited to 'server/src/com/vaadin/ui') diff --git a/client/src/com/vaadin/client/connectors/MultiSelectionModelConnector.java b/client/src/com/vaadin/client/connectors/MultiSelectionModelConnector.java index c1f5d87d68..e4ad50e7ac 100644 --- a/client/src/com/vaadin/client/connectors/MultiSelectionModelConnector.java +++ b/client/src/com/vaadin/client/connectors/MultiSelectionModelConnector.java @@ -156,19 +156,16 @@ public class MultiSelectionModelConnector extends public void selectAll() { assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection."; - Set> rows = new HashSet>(); DataSource dataSource = getGrid().getDataSource(); for (int i = availableRows.getStart(); i < availableRows.getEnd(); ++i) { final JsonObject row = dataSource.getRow(i); if (row != null) { RowHandle handle = dataSource.getHandle(row); markAsSelected(handle, true); - rows.add(handle); } } getRpcProxy(MultiSelectionModelServerRpc.class).selectAll(); - cleanRowCache(rows); } @Override @@ -205,19 +202,16 @@ public class MultiSelectionModelConnector extends public boolean deselectAll() { assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection."; - Set> rows = new HashSet>(); DataSource dataSource = getGrid().getDataSource(); for (int i = availableRows.getStart(); i < availableRows.getEnd(); ++i) { final JsonObject row = dataSource.getRow(i); if (row != null) { RowHandle handle = dataSource.getHandle(row); markAsSelected(handle, false); - rows.add(handle); } } getRpcProxy(MultiSelectionModelServerRpc.class).deselectAll(); - cleanRowCache(rows); return true; } @@ -235,8 +229,9 @@ public class MultiSelectionModelConnector extends for (JsonObject row : rows) { RowHandle rowHandle = getRowHandle(row); - markAsSelected(rowHandle, true); - selected.add(rowHandle); + if (markAsSelected(rowHandle, true)) { + selected.add(rowHandle); + } } if (!isBeingBatchSelected()) { @@ -246,23 +241,35 @@ public class MultiSelectionModelConnector extends } /** - * Marks the JsonObject pointed by RowHandle to have selected - * information equal to given boolean + * Marks the given row to be selected or deselected. Returns true if the + * value actually changed. + *

    + * Note: If selection model is in batch select state, the row will be + * pinned on select. * * @param row * row handle * @param selected - * should row be selected + * {@code true} if row should be selected; {@code false} if + * not + * @return {@code true} if selected status changed; {@code false} if not */ - protected void markAsSelected(RowHandle row, + protected boolean markAsSelected(RowHandle row, boolean selected) { - row.pin(); - if (selected) { + if (selected && !isSelected(row.getRow())) { row.getRow().put(GridState.JSONKEY_SELECTED, true); - } else { + } else if (!selected && isSelected(row.getRow())) { row.getRow().remove(GridState.JSONKEY_SELECTED); + } else { + return false; } + row.updateRow(); + + if (isBeingBatchSelected()) { + row.pin(); + } + return true; } /** @@ -278,8 +285,9 @@ public class MultiSelectionModelConnector extends for (JsonObject row : rows) { RowHandle rowHandle = getRowHandle(row); - markAsSelected(rowHandle, false); - deselected.add(rowHandle); + if (markAsSelected(rowHandle, false)) { + deselected.add(rowHandle); + } } if (!isBeingBatchSelected()) { @@ -288,16 +296,38 @@ public class MultiSelectionModelConnector extends return true; } + /** + * Sends a deselect RPC call to server-side containing all deselected + * rows. Unpins any pinned rows. + */ private void sendDeselected() { getRpcProxy(MultiSelectionModelServerRpc.class).deselect( getRowKeys(deselected)); - cleanRowCache(deselected); + + if (isBeingBatchSelected()) { + for (RowHandle row : deselected) { + row.unpin(); + } + } + + deselected.clear(); } + /** + * Sends a select RPC call to server-side containing all selected rows. + * Unpins any pinned rows. + */ private void sendSelected() { getRpcProxy(MultiSelectionModelServerRpc.class).select( getRowKeys(selected)); - cleanRowCache(selected); + + if (isBeingBatchSelected()) { + for (RowHandle row : selected) { + row.unpin(); + } + } + + selected.clear(); } private List getRowKeys(Set> handles) { @@ -316,13 +346,6 @@ public class MultiSelectionModelConnector extends return rows; } - private void cleanRowCache(Set> handles) { - for (RowHandle handle : handles) { - handle.unpin(); - } - handles.clear(); - } - @Override public void startBatchSelect() { assert selected.isEmpty() && deselected.isEmpty() : "Row caches were not clear."; diff --git a/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java b/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java index bcca8816b1..5daa02c3bf 100644 --- a/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java +++ b/client/src/com/vaadin/client/connectors/RpcDataSourceConnector.java @@ -189,24 +189,16 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { return new RowHandleImpl(row, key); } - @Override - protected void pinHandle(RowHandleImpl handle) { - // Server only knows if something is pinned or not. No need to pin - // multiple times. - boolean pinnedBefore = handle.isPinned(); - super.pinHandle(handle); - if (!pinnedBefore) { - rpcProxy.setPinned(getRowKey(handle.getRow()), true); - } - } - @Override protected void unpinHandle(RowHandleImpl handle) { // Row data is no longer available after it has been unpinned. String key = getRowKey(handle.getRow()); super.unpinHandle(handle); if (!handle.isPinned()) { - rpcProxy.setPinned(key, false); + if (indexOfKey(key) == -1) { + // Row out of view has been unpinned. drop it + droppedRowKeys.set(droppedRowKeys.length(), key); + } } } @@ -244,7 +236,9 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { @Override protected void onDropFromCache(int rowIndex, JsonObject row) { - droppedRowKeys.set(droppedRowKeys.length(), getRowKey(row)); + if (!((RowHandleImpl) getHandle(row)).isPinned()) { + droppedRowKeys.set(droppedRowKeys.length(), getRowKey(row)); + } } } diff --git a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java index 2438bec8df..58cd5c5f19 100644 --- a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java +++ b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java @@ -16,8 +16,6 @@ package com.vaadin.client.data; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -120,12 +118,7 @@ public abstract class AbstractRemoteDataSource implements DataSource { @Override public T getRow() throws IllegalStateException { - if (isPinned()) { - return row; - } else { - throw new IllegalStateException("The row handle for key " + key - + " was not pinned"); - } + return row; } public boolean isPinned() { @@ -197,7 +190,6 @@ public abstract class AbstractRemoteDataSource implements DataSource { private Map pinnedCounts = new HashMap(); private Map pinnedRows = new HashMap(); - protected Collection temporarilyPinnedRows = Collections.emptySet(); // Size not yet known private int size = -1; diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index c8f3604fd9..78c87ab23d 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -21,14 +21,11 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; -import com.google.gwt.thirdparty.guava.common.collect.BiMap; -import com.google.gwt.thirdparty.guava.common.collect.HashBiMap; import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet; import com.google.gwt.thirdparty.guava.common.collect.Maps; import com.google.gwt.thirdparty.guava.common.collect.Sets; @@ -43,6 +40,7 @@ import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.data.Property.ValueChangeNotifier; import com.vaadin.server.AbstractExtension; import com.vaadin.server.ClientConnector; +import com.vaadin.server.KeyMapper; import com.vaadin.shared.data.DataProviderRpc; import com.vaadin.shared.data.DataRequestRpc; import com.vaadin.shared.ui.grid.GridClientRpc; @@ -70,215 +68,22 @@ import elemental.json.JsonObject; */ public class RpcDataProviderExtension extends AbstractExtension { - /** - * ItemId to Key to ItemId mapper. - *

    - * This class is used when transmitting information about items in container - * related to Grid. It introduces a consistent way of mapping ItemIds and - * its container to a String that can be mapped back to ItemId. - *

    - * Technical note: This class also keeps tabs on which indices are - * being shown/selected, and is able to clean up after itself once the - * itemId ⇆ key mapping is not needed anymore. In other words, this - * doesn't leak memory. - */ - public class DataProviderKeyMapper implements Serializable, DataGenerator { - private final BiMap itemIdToKey = HashBiMap.create(); - private Set pinnedItemIds = new HashSet(); - private long rollingIndex = 0; - - private DataProviderKeyMapper() { - // private implementation - } - - private String nextKey() { - return String.valueOf(rollingIndex++); - } - - /** - * Gets the key for a given item id. Creates a new key mapping if no - * existing mapping was found for the given item id. - * - * @since 7.5.0 - * @param itemId - * the item id to get the key for - * @return the key for the given item id - */ - public String getKey(Object itemId) { - String key = itemIdToKey.get(itemId); - if (key == null) { - key = nextKey(); - itemIdToKey.put(itemId, key); - } - return key; - } - - /** - * Gets keys for a collection of item ids. - *

    - * If the itemIds are currently cached, the existing keys will be used. - * Otherwise new ones will be created. - * - * @param itemIds - * the item ids for which to get keys - * @return keys for the {@code itemIds} - */ - public List getKeys(Collection itemIds) { - if (itemIds == null) { - throw new IllegalArgumentException("itemIds can't be null"); - } - - ArrayList keys = new ArrayList(itemIds.size()); - for (Object itemId : itemIds) { - keys.add(getKey(itemId)); - } - return keys; - } - - /** - * Gets the registered item id based on its key. - *

    - * A key is used to identify a particular row on both a server and a - * client. This method can be used to get the item id for the row key - * that the client has sent. - * - * @param key - * the row key for which to retrieve an item id - * @return the item id corresponding to {@code key} - * @throws IllegalStateException - * if the key mapper does not have a record of {@code key} . - */ - public Object getItemId(String key) throws IllegalStateException { - Object itemId = itemIdToKey.inverse().get(key); - if (itemId != null) { - return itemId; - } else { - throw new IllegalStateException("No item id for key " + key - + " found."); - } - } - - /** - * Gets corresponding item ids for each of the keys in a collection. - * - * @param keys - * the keys for which to retrieve item ids - * @return a collection of item ids for the {@code keys} - * @throws IllegalStateException - * if one or more of keys don't have a corresponding item id - * in the cache - */ - public Collection getItemIds(Collection keys) - throws IllegalStateException { - if (keys == null) { - throw new IllegalArgumentException("keys may not be null"); - } - - ArrayList itemIds = new ArrayList(keys.size()); - for (String key : keys) { - itemIds.add(getItemId(key)); - } - return itemIds; - } - - /** - * Pin an item id to be cached indefinitely. - *

    - * Normally when an itemId is not an active row, it is discarded from - * the cache. Pinning an item id will make sure that it is kept in the - * cache. - *

    - * In effect, while an item id is pinned, it always has the same key. - * - * @param itemId - * the item id to pin - * @throws IllegalStateException - * if {@code itemId} was already pinned - * @see #unpin(Object) - * @see #isPinned(Object) - * @see #getItemIds(Collection) - */ - public void pin(Object itemId) throws IllegalStateException { - if (isPinned(itemId)) { - throw new IllegalStateException("Item id " + itemId - + " was pinned already"); - } - pinnedItemIds.add(itemId); - } - - /** - * Unpin an item id. - *

    - * This cancels the effect of pinning an item id. If the item id is - * currently inactive, it will be immediately removed from the cache. - * - * @param itemId - * the item id to unpin - * @throws IllegalStateException - * if {@code itemId} was not pinned - * @see #pin(Object) - * @see #isPinned(Object) - * @see #getItemIds(Collection) - */ - public void unpin(Object itemId) throws IllegalStateException { - if (!isPinned(itemId)) { - throw new IllegalStateException("Item id " + itemId - + " was not pinned"); - } - - pinnedItemIds.remove(itemId); - } - - /** - * Checks whether an item id is pinned or not. - * - * @param itemId - * the item id to check for pin status - * @return {@code true} iff the item id is currently pinned - */ - public boolean isPinned(Object itemId) { - return pinnedItemIds.contains(itemId); - } - - /** - * {@inheritDoc} - * - * @since 7.6 - */ - @Override - public void generateData(Object itemId, Item item, JsonObject rowData) { - rowData.put(GridState.JSONKEY_ROWKEY, getKey(itemId)); - } - - /** - * Removes all inactive item id to key mapping from the key mapper. - * - * @since 7.6 - */ - public void dropInactiveItems() { - Collection active = activeItemHandler.getActiveItemIds(); - Iterator itemIter = itemIdToKey.keySet().iterator(); - while (itemIter.hasNext()) { - Object itemId = itemIter.next(); - if (!active.contains(itemId) && !isPinned(itemId)) { - itemIter.remove(); - } - } - } - } - /** * Class for keeping track of current items and ValueChangeListeners. * * @since 7.6 */ - private class ActiveItemHandler implements Serializable { + private class ActiveItemHandler implements Serializable, DataGenerator { private final Map activeItemMap = new HashMap(); + private final KeyMapper keyMapper = new KeyMapper(); private final Set droppedItems = new HashSet(); /** - * Registers ValueChangeListeners for given items ids. + * Registers ValueChangeListeners for given item ids. + *

    + * Note: This method will clean up any unneeded listeners and key + * mappings * * @param itemIds * collection of new active item ids @@ -293,7 +98,7 @@ public class RpcDataProviderExtension extends AbstractExtension { // Remove still active rows that were "dropped" droppedItems.removeAll(itemIds); - dropListeners(droppedItems); + internalDropActiveItems(droppedItems); droppedItems.clear(); } @@ -310,11 +115,12 @@ public class RpcDataProviderExtension extends AbstractExtension { } } - private void dropListeners(Collection itemIds) { + private void internalDropActiveItems(Collection itemIds) { for (Object itemId : droppedItems) { assert activeItemMap.containsKey(itemId) : "Item ID should exist in the activeItemMap"; activeItemMap.remove(itemId).removeListener(); + keyMapper.remove(itemId); } } @@ -335,6 +141,12 @@ public class RpcDataProviderExtension extends AbstractExtension { public Collection getValueChangeListeners() { return new HashSet(activeItemMap.values()); } + + @Override + public void generateData(Object itemId, Item item, JsonObject rowData) { + rowData.put(GridState.JSONKEY_ROWKEY, keyMapper.key(itemId)); + } + } /** @@ -635,8 +447,6 @@ public class RpcDataProviderExtension extends AbstractExtension { } }; - private final DataProviderKeyMapper keyMapper = new DataProviderKeyMapper(); - /** RpcDataProvider should send the current cache again. */ private boolean refreshCache = false; @@ -683,25 +493,11 @@ public class RpcDataProviderExtension extends AbstractExtension { cacheSize); } - @Override - public void setPinned(String key, boolean isPinned) { - Object itemId = keyMapper.getItemId(key); - if (isPinned) { - // Row might already be pinned if it was selected from the - // server - if (!keyMapper.isPinned(itemId)) { - keyMapper.pin(itemId); - } - } else { - keyMapper.unpin(itemId); - } - } - @Override public void dropRows(JsonArray rowKeys) { for (int i = 0; i < rowKeys.length(); ++i) { - activeItemHandler.dropActiveItem(keyMapper - .getItemId(rowKeys.getString(i))); + activeItemHandler.dropActiveItem(getKeyMapper().get( + rowKeys.getString(i))); } } }); @@ -711,7 +507,7 @@ public class RpcDataProviderExtension extends AbstractExtension { .addItemSetChangeListener(itemListener); } - addDataGenerator(keyMapper); + addDataGenerator(activeItemHandler); addDataGenerator(detailComponentManager); } @@ -787,7 +583,6 @@ public class RpcDataProviderExtension extends AbstractExtension { rpc.setRowData(firstRowToPush, rows); activeItemHandler.addActiveItems(itemIds); - keyMapper.dropInactiveItems(); } private JsonObject getRowData(Collection columns, Object itemId) { @@ -939,7 +734,7 @@ public class RpcDataProviderExtension extends AbstractExtension { public void setParent(ClientConnector parent) { if (parent == null) { // We're being detached, release various listeners - activeItemHandler.dropListeners(activeItemHandler + activeItemHandler.internalDropActiveItems(activeItemHandler .getActiveItemIds()); if (container instanceof ItemSetChangeNotifier) { @@ -987,8 +782,8 @@ public class RpcDataProviderExtension extends AbstractExtension { refreshCache(); } - public DataProviderKeyMapper getKeyMapper() { - return keyMapper; + public KeyMapper getKeyMapper() { + return activeItemHandler.keyMapper; } protected Grid getGrid() { diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index 204f771ee5..215df3a6a3 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -57,7 +57,6 @@ import com.vaadin.data.DataGenerator; import com.vaadin.data.Item; import com.vaadin.data.Property; import com.vaadin.data.RpcDataProviderExtension; -import com.vaadin.data.RpcDataProviderExtension.DataProviderKeyMapper; import com.vaadin.data.RpcDataProviderExtension.DetailComponentManager; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.fieldgroup.DefaultFieldGroupFieldFactory; @@ -3856,7 +3855,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * @return the item id corresponding to {@code key} */ protected Object getItemId(String rowKey) { - return getParentGrid().getKeyMapper().getItemId(rowKey); + return getParentGrid().getKeyMapper().get(rowKey); } /** @@ -4139,7 +4138,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, @Override public void itemClick(String rowKey, String columnId, MouseEventDetails details) { - Object itemId = getKeyMapper().getItemId(rowKey); + Object itemId = getKeyMapper().get(rowKey); Item item = datasource.getItem(itemId); Object propertyId = getPropertyIdByColumnId(columnId); fireEvent(new ItemClickEvent(Grid.this, item, itemId, @@ -4222,20 +4221,20 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, @Override public void editorOpen(String rowKey) { - fireEvent(new EditorOpenEvent(Grid.this, getKeyMapper() - .getItemId(rowKey))); + fireEvent(new EditorOpenEvent(Grid.this, getKeyMapper().get( + rowKey))); } @Override public void editorMove(String rowKey) { - fireEvent(new EditorMoveEvent(Grid.this, getKeyMapper() - .getItemId(rowKey))); + fireEvent(new EditorMoveEvent(Grid.this, getKeyMapper().get( + rowKey))); } @Override public void editorClose(String rowKey) { - fireEvent(new EditorCloseEvent(Grid.this, getKeyMapper() - .getItemId(rowKey))); + fireEvent(new EditorCloseEvent(Grid.this, getKeyMapper().get( + rowKey))); } }); @@ -5299,7 +5298,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, * * @return the key mapper being used by the data source */ - DataProviderKeyMapper getKeyMapper() { + KeyMapper getKeyMapper() { return datasourceExtension.getKeyMapper(); } diff --git a/server/tests/src/com/vaadin/tests/server/component/grid/DataProviderExtension.java b/server/tests/src/com/vaadin/tests/server/component/grid/DataProviderExtension.java deleted file mode 100644 index 9ecf131c5b..0000000000 --- a/server/tests/src/com/vaadin/tests/server/component/grid/DataProviderExtension.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; - -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.Container.Indexed; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.RpcDataProviderExtension; -import com.vaadin.data.RpcDataProviderExtension.DataProviderKeyMapper; -import com.vaadin.data.util.IndexedContainer; - -public class DataProviderExtension { - private RpcDataProviderExtension dataProvider; - private DataProviderKeyMapper keyMapper; - private Container.Indexed container; - - private static final Object ITEM_ID1 = "itemid1"; - private static final Object ITEM_ID2 = "itemid2"; - private static final Object ITEM_ID3 = "itemid3"; - - private static final Object PROPERTY_ID1_STRING = "property1"; - - @Before - public void setup() { - container = new IndexedContainer(); - populate(container); - - dataProvider = new RpcDataProviderExtension(container); - keyMapper = dataProvider.getKeyMapper(); - } - - private static void populate(Indexed container) { - container.addContainerProperty(PROPERTY_ID1_STRING, String.class, ""); - for (Object itemId : Arrays.asList(ITEM_ID1, ITEM_ID2, ITEM_ID3)) { - final Item item = container.addItem(itemId); - @SuppressWarnings("unchecked") - final Property stringProperty = item - .getItemProperty(PROPERTY_ID1_STRING); - stringProperty.setValue(itemId.toString()); - } - } - - @Test - public void pinBasics() { - assertFalse("itemId1 should not start as pinned", - keyMapper.isPinned(ITEM_ID2)); - - keyMapper.pin(ITEM_ID1); - assertTrue("itemId1 should now be pinned", keyMapper.isPinned(ITEM_ID1)); - - keyMapper.unpin(ITEM_ID1); - assertFalse("itemId1 should not be pinned anymore", - keyMapper.isPinned(ITEM_ID2)); - } - - @Test(expected = IllegalStateException.class) - public void doublePinning() { - keyMapper.pin(ITEM_ID1); - keyMapper.pin(ITEM_ID1); - } - - @Test(expected = IllegalStateException.class) - public void nonexistentUnpin() { - keyMapper.unpin(ITEM_ID1); - } -} diff --git a/shared/src/com/vaadin/shared/data/DataRequestRpc.java b/shared/src/com/vaadin/shared/data/DataRequestRpc.java index b66965fae9..4b553dda68 100644 --- a/shared/src/com/vaadin/shared/data/DataRequestRpc.java +++ b/shared/src/com/vaadin/shared/data/DataRequestRpc.java @@ -46,20 +46,6 @@ public interface DataRequestRpc extends ServerRpc { public void requestRows(int firstRowIndex, int numberOfRows, int firstCachedRowIndex, int cacheSize); - /** - * Informs the server that an item referenced with a key pinned status has - * changed. This is a delayed call that happens along with next rpc call to - * server. - * - * @param key - * key mapping to item - * @param isPinned - * pinned status of referenced item - */ - @Delayed - @NoLoadingIndicator - public void setPinned(String key, boolean isPinned); - /** * Informs the server that items have been dropped from the client cache. * diff --git a/uitest/src/com/vaadin/tests/components/grid/CustomRendererTest.java b/uitest/src/com/vaadin/tests/components/grid/CustomRendererTest.java index 7d706ecd30..d5e01a9de8 100644 --- a/uitest/src/com/vaadin/tests/components/grid/CustomRendererTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/CustomRendererTest.java @@ -47,8 +47,8 @@ public class CustomRendererTest extends MultiBrowserTest { .getText()); grid.getCell(0, 1).click(); - assertEquals("row: 0, key: 0", grid.getCell(0, 1).getText()); - assertEquals("key: 0, itemId: " + CustomRenderer.ITEM_ID, + assertEquals("row: 0, key: 1", grid.getCell(0, 1).getText()); + assertEquals("key: 1, itemId: " + CustomRenderer.ITEM_ID, findDebugLabel().getText()); } diff --git a/uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderersTest.java b/uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderersTest.java index b38178b156..2e86053ef3 100644 --- a/uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderersTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderersTest.java @@ -43,6 +43,6 @@ public class JavaScriptRenderersTest extends MultiBrowserTest { // Verify onbrowserevent cell_1_1.click(); Assert.assertTrue(cell_1_1.getText().startsWith( - "Clicked 1 with key 1 at")); + "Clicked 1 with key 2 at")); } } -- cgit v1.2.3