From 52b602ed2e8ca7cbad266b59c45e916cb8394b56 Mon Sep 17 00:00:00 2001 From: Johannes Dahlström Date: Tue, 12 May 2015 12:03:40 +0300 Subject: Catch all Exceptions when trying to open a gzipped resource (#13653) URLConnection.getInputStream may throw any IOException, not just FileNotFoundException. Additionally, catch and log unexpected non-IOExceptions just in case to keep the app from failing hard. Change-Id: Id7ce7ddee3de38ccd10d9e02e6c587a86b9cac96 --- server/src/com/vaadin/server/VaadinServlet.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index 053349a635..b30f6acf16 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -877,8 +877,13 @@ public class VaadinServlet extends HttpServlet implements Constants { is = connection.getInputStream(); // set gzip headers response.setHeader("Content-Encoding", "gzip"); - } catch (FileNotFoundException e) { + } catch (IOException e) { // NOP: will be still tried with non gzipped version + } catch (Exception e) { + getLogger().log( + Level.FINE, + "Unexpected exception looking for gzipped version of resource " + + urlStr, e); } } if (is == null) { -- cgit v1.2.3 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 --- client/src/com/vaadin/client/ui/VButton.java | 3 - .../src/com/vaadin/client/ui/ui/UIConnector.java | 18 ++- server/src/com/vaadin/ui/AbstractFocusable.java | 134 +++++++++++++++++++++ server/src/com/vaadin/ui/Button.java | 105 +--------------- .../tests/components/AbstractComponentTest.java | 41 ++++++- .../abstractfield/AbstractFieldTest.java | 11 +- .../vaadin/tests/components/button/Buttons2.java | 3 - 7 files changed, 186 insertions(+), 129 deletions(-) create mode 100644 server/src/com/vaadin/ui/AbstractFocusable.java diff --git a/client/src/com/vaadin/client/ui/VButton.java b/client/src/com/vaadin/client/ui/VButton.java index bf321f7f00..2eb967c4fa 100644 --- a/client/src/com/vaadin/client/ui/VButton.java +++ b/client/src/com/vaadin/client/ui/VButton.java @@ -23,7 +23,6 @@ import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; -import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.FocusWidget; @@ -94,8 +93,6 @@ public class VButton extends FocusWidget implements ClickHandler { /** For internal use only. May be removed or replaced in the future. */ public int clickShortcut = 0; - private HandlerRegistration focusHandlerRegistration; - private HandlerRegistration blurHandlerRegistration; private long lastClickTime = 0; public VButton() { diff --git a/client/src/com/vaadin/client/ui/ui/UIConnector.java b/client/src/com/vaadin/client/ui/ui/UIConnector.java index e7c252f26a..da23ec4a5a 100644 --- a/client/src/com/vaadin/client/ui/ui/UIConnector.java +++ b/client/src/com/vaadin/client/ui/ui/UIConnector.java @@ -59,6 +59,7 @@ import com.vaadin.client.ResourceLoader.ResourceLoadEvent; import com.vaadin.client.ResourceLoader.ResourceLoadListener; import com.vaadin.client.ServerConnector; import com.vaadin.client.UIDL; +import com.vaadin.client.Util; import com.vaadin.client.VConsole; import com.vaadin.client.ValueMap; import com.vaadin.client.annotations.OnStateChange; @@ -319,19 +320,19 @@ public class UIConnector extends AbstractSingleComponentContainerConnector Scheduler.get().scheduleDeferred(new Command() { @Override public void execute() { - ComponentConnector paintable = (ComponentConnector) uidl + ComponentConnector connector = (ComponentConnector) uidl .getPaintableAttribute("focused", getConnection()); - if (paintable == null) { + if (connector == null) { // Do not try to focus invisible components which not // present in UIDL return; } - final Widget toBeFocused = paintable.getWidget(); + final Widget toBeFocused = connector.getWidget(); /* * Two types of Widgets can be focused, either implementing - * GWT HasFocus of a thinner Vaadin specific Focusable + * GWT Focusable of a thinner Vaadin specific Focusable * interface. */ if (toBeFocused instanceof com.google.gwt.user.client.ui.Focusable) { @@ -340,7 +341,14 @@ public class UIConnector extends AbstractSingleComponentContainerConnector } else if (toBeFocused instanceof Focusable) { ((Focusable) toBeFocused).focus(); } else { - VConsole.log("Could not focus component"); + getLogger() + .severe("Server is trying to set focus to the widget of connector " + + Util.getConnectorString(connector) + + " but it is not focusable. The widget should implement either " + + com.google.gwt.user.client.ui.Focusable.class + .getName() + + " or " + + Focusable.class.getName()); } } }); 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(); diff --git a/uitest/src/com/vaadin/tests/components/AbstractComponentTest.java b/uitest/src/com/vaadin/tests/components/AbstractComponentTest.java index b289279b86..aca617aa5a 100644 --- a/uitest/src/com/vaadin/tests/components/AbstractComponentTest.java +++ b/uitest/src/com/vaadin/tests/components/AbstractComponentTest.java @@ -20,6 +20,7 @@ import com.vaadin.server.ThemeResource; import com.vaadin.tests.util.Log; import com.vaadin.tests.util.LoremIpsum; import com.vaadin.ui.AbstractComponent; +import com.vaadin.ui.Component.Focusable; import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar.MenuItem; import com.vaadin.ui.themes.BaseTheme; @@ -242,16 +243,18 @@ public abstract class AbstractComponentTest createStyleNameSelect(CATEGORY_DECORATIONS); + createFocusActions(); } protected Command focusListenerCommand = new Command() { @Override public void execute(T c, Boolean value, Object data) { + FocusNotifier fn = (FocusNotifier) c; if (value) { - ((FocusNotifier) c).addListener(AbstractComponentTest.this); + fn.addFocusListener(AbstractComponentTest.this); } else { - ((FocusNotifier) c).removeListener(AbstractComponentTest.this); + fn.removeFocusListener(AbstractComponentTest.this); } } }; @@ -259,10 +262,11 @@ public abstract class AbstractComponentTest @Override public void execute(T c, Boolean value, Object data) { + BlurNotifier bn = (BlurNotifier) c; if (value) { - ((BlurNotifier) c).addListener(AbstractComponentTest.this); + bn.addBlurListener(AbstractComponentTest.this); } else { - ((BlurNotifier) c).removeListener(AbstractComponentTest.this); + bn.removeBlurListener(AbstractComponentTest.this); } } }; @@ -279,6 +283,35 @@ public abstract class AbstractComponentTest } + private void createFocusActions() { + if (FocusNotifier.class.isAssignableFrom(getTestClass())) { + createFocusListener(CATEGORY_LISTENERS); + } + if (BlurNotifier.class.isAssignableFrom(getTestClass())) { + createBlurListener(CATEGORY_LISTENERS); + } + if (Focusable.class.isAssignableFrom(getTestClass())) { + LinkedHashMap tabIndexes = new LinkedHashMap(); + tabIndexes.put("0", 0); + tabIndexes.put("-1", -1); + tabIndexes.put("10", 10); + createSelectAction("Tab index", "State", tabIndexes, "0", + new Command() { + @Override + public void execute(T c, Integer tabIndex, Object data) { + ((Focusable) c).setTabIndex(tabIndex); + } + }); + + createClickAction("Set focus", "State", new Command() { + @Override + public void execute(T c, Void value, Object data) { + ((Focusable) c).focus(); + } + }, null); + } + } + private void createStyleNameSelect(String category) { LinkedHashMap options = new LinkedHashMap(); options.put("-", null); diff --git a/uitest/src/com/vaadin/tests/components/abstractfield/AbstractFieldTest.java b/uitest/src/com/vaadin/tests/components/abstractfield/AbstractFieldTest.java index 692ca25b07..496a44a6c1 100644 --- a/uitest/src/com/vaadin/tests/components/abstractfield/AbstractFieldTest.java +++ b/uitest/src/com/vaadin/tests/components/abstractfield/AbstractFieldTest.java @@ -13,8 +13,6 @@ import com.vaadin.data.Property; import com.vaadin.data.Property.ReadOnlyStatusChangeEvent; import com.vaadin.data.Property.ReadOnlyStatusChangeListener; import com.vaadin.data.Property.ValueChangeListener; -import com.vaadin.event.FieldEvents.BlurNotifier; -import com.vaadin.event.FieldEvents.FocusNotifier; import com.vaadin.tests.components.AbstractComponentTest; import com.vaadin.ui.AbstractField; import com.vaadin.ui.MenuBar; @@ -29,15 +27,9 @@ public abstract class AbstractFieldTest extends @Override protected void createActions() { super.createActions(); + createBooleanAction("Required", CATEGORY_STATE, false, requiredCommand); createRequiredErrorSelect(CATEGORY_DECORATIONS); - if (FocusNotifier.class.isAssignableFrom(getTestClass())) { - createFocusListener(CATEGORY_LISTENERS); - } - - if (BlurNotifier.class.isAssignableFrom(getTestClass())) { - createBlurListener(CATEGORY_LISTENERS); - } createValueChangeListener(CATEGORY_LISTENERS); createReadOnlyStatusChangeListener(CATEGORY_LISTENERS); @@ -52,7 +44,6 @@ public abstract class AbstractFieldTest extends // * invalidallowed // * error indicator // - // * tabindex // * validation visible // * ShortcutListener diff --git a/uitest/src/com/vaadin/tests/components/button/Buttons2.java b/uitest/src/com/vaadin/tests/components/button/Buttons2.java index 7526e7dbc3..4f75dfbfef 100644 --- a/uitest/src/com/vaadin/tests/components/button/Buttons2.java +++ b/uitest/src/com/vaadin/tests/components/button/Buttons2.java @@ -42,9 +42,6 @@ public class Buttons2 extends AbstractComponentTest protected void createActions() { super.createActions(); - createFocusListener(CATEGORY_LISTENERS); - createBlurListener(CATEGORY_LISTENERS); - createBooleanAction("Disable on click", CATEGORY_FEATURES, false, disableOnClickCommand); addClickListener(CATEGORY_LISTENERS); -- 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 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 8ba56b9c442ee05845e5b0519c2f457c64352bd2 Mon Sep 17 00:00:00 2001 From: Johannes Dahlström Date: Tue, 19 May 2015 19:30:06 +0300 Subject: Refactor handling of focus and blur RPC in Connectors (#17917) Change-Id: I2e4fb3fb941fda2aa4cbc7154fa9a3f7e8e9ce02 --- .../com/vaadin/client/ApplicationConnection.java | 11 ++- client/src/com/vaadin/client/EventHelper.java | 17 ++--- .../vaadin/client/connectors/GridConnector.java | 36 ++------- .../client/ui/ConnectorFocusAndBlurHandler.java | 87 ++++++++++++++++++++++ .../vaadin/client/ui/button/ButtonConnector.java | 38 +--------- .../client/ui/checkbox/CheckBoxConnector.java | 34 +-------- .../ui/nativebutton/NativeButtonConnector.java | 35 +-------- .../ui/nativeselect/NativeSelectConnector.java | 44 ++--------- 8 files changed, 121 insertions(+), 181 deletions(-) create mode 100644 client/src/com/vaadin/client/ui/ConnectorFocusAndBlurHandler.java diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index af86818fb3..a8266ad451 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -63,6 +63,7 @@ import com.google.gwt.user.client.Window.ClosingHandler; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConfiguration.ErrorMessage; +import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent; import com.vaadin.client.ResourceLoader.ResourceLoadEvent; import com.vaadin.client.ResourceLoader.ResourceLoadListener; import com.vaadin.client.communication.HasJavaScriptConnectorHelper; @@ -94,7 +95,6 @@ import com.vaadin.client.ui.VOverlay; import com.vaadin.client.ui.dd.VDragAndDropManager; import com.vaadin.client.ui.ui.UIConnector; import com.vaadin.client.ui.window.WindowConnector; -import com.vaadin.shared.AbstractComponentState; import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.JsonConstants; import com.vaadin.shared.VaadinUriResolver; @@ -3414,20 +3414,19 @@ public class ApplicationConnection implements HasHandlers { * before the component is updated so the value is correct if called from * updatedFromUIDL. * - * @param paintable + * @param connector * The connector to register event listeners for * @param eventIdentifier * The identifier for the event * @return true if at least one listener has been registered on server side * for the event identified by eventIdentifier. * @deprecated As of 7.0. Use - * {@link AbstractComponentState#hasEventListener(String)} - * instead + * {@link AbstractConnector#hasEventListener(String)} instead */ @Deprecated - public boolean hasEventListeners(ComponentConnector paintable, + public boolean hasEventListeners(ComponentConnector connector, String eventIdentifier) { - return paintable.hasEventListener(eventIdentifier); + return connector.hasEventListener(eventIdentifier); } /** diff --git a/client/src/com/vaadin/client/EventHelper.java b/client/src/com/vaadin/client/EventHelper.java index f251215d41..1ee252af0f 100644 --- a/client/src/com/vaadin/client/EventHelper.java +++ b/client/src/com/vaadin/client/EventHelper.java @@ -51,7 +51,6 @@ import com.google.gwt.user.client.ui.Widget; * * * - * */ public class EventHelper { @@ -69,7 +68,7 @@ public class EventHelper { */ public static HandlerRegistration updateFocusHandler( T connector, HandlerRegistration handlerRegistration) { - return updateHandler(connector, FOCUS, handlerRegistration, + return updateHandler(connector, connector, FOCUS, handlerRegistration, FocusEvent.getType(), connector.getWidget()); } @@ -89,7 +88,7 @@ public class EventHelper { */ public static HandlerRegistration updateFocusHandler( T connector, HandlerRegistration handlerRegistration, Widget widget) { - return updateHandler(connector, FOCUS, handlerRegistration, + return updateHandler(connector, connector, FOCUS, handlerRegistration, FocusEvent.getType(), widget); } @@ -107,7 +106,7 @@ public class EventHelper { */ public static HandlerRegistration updateBlurHandler( T connector, HandlerRegistration handlerRegistration) { - return updateHandler(connector, BLUR, handlerRegistration, + return updateHandler(connector, connector, BLUR, handlerRegistration, BlurEvent.getType(), connector.getWidget()); } @@ -128,23 +127,21 @@ public class EventHelper { */ public static HandlerRegistration updateBlurHandler( T connector, HandlerRegistration handlerRegistration, Widget widget) { - return updateHandler(connector, BLUR, handlerRegistration, + return updateHandler(connector, connector, BLUR, handlerRegistration, BlurEvent.getType(), widget); } - private static HandlerRegistration updateHandler( - ComponentConnector connector, String eventIdentifier, + public static HandlerRegistration updateHandler( + ComponentConnector connector, H handler, String eventIdentifier, HandlerRegistration handlerRegistration, Type type, Widget widget) { if (connector.hasEventListener(eventIdentifier)) { if (handlerRegistration == null) { - handlerRegistration = widget.addDomHandler((H) connector, type); + handlerRegistration = widget.addDomHandler(handler, type); } } else if (handlerRegistration != null) { handlerRegistration.removeHandler(); handlerRegistration = null; } return handlerRegistration; - } - } diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index 3eb92ab94c..47b21a3429 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -32,17 +32,11 @@ 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; @@ -52,6 +46,7 @@ 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; import com.vaadin.client.ui.SimpleManagedLayout; import com.vaadin.client.widget.grid.CellReference; import com.vaadin.client.widget.grid.CellStyleGenerator; @@ -86,7 +81,6 @@ 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; @@ -119,7 +113,7 @@ import elemental.json.JsonValue; */ @Connect(com.vaadin.ui.Grid.class) public class GridConnector extends AbstractHasComponentsConnector implements - SimpleManagedLayout, DeferredWorker, FocusHandler, BlurHandler { + SimpleManagedLayout, DeferredWorker { private static final class CustomCellStyleGenerator implements CellStyleGenerator { @@ -764,9 +758,6 @@ 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() { @@ -915,6 +906,9 @@ public class GridConnector extends AbstractHasComponentsConnector implements getWidget().addColumnReorderHandler(columnReorderHandler); getWidget().addColumnVisibilityChangeHandler( columnVisibilityChangeHandler); + + ConnectorFocusAndBlurHandler.addHandlers(this); + getWidget().setDetailsGenerator(customDetailsGenerator); getLayoutManager().registerDependency(this, getWidget().getElement()); @@ -992,12 +986,6 @@ 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() { @@ -1434,18 +1422,4 @@ 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/ui/ConnectorFocusAndBlurHandler.java b/client/src/com/vaadin/client/ui/ConnectorFocusAndBlurHandler.java new file mode 100644 index 0000000000..817d070a9f --- /dev/null +++ b/client/src/com/vaadin/client/ui/ConnectorFocusAndBlurHandler.java @@ -0,0 +1,87 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui; + +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.ui.Widget; +import com.vaadin.client.EventHelper; +import com.vaadin.client.communication.StateChangeEvent; +import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; +import com.vaadin.shared.EventId; +import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc; + +/** + * A handler for focus and blur events which uses {@link FocusAndBlurServerRpc} + * to transmit received events to the server. Events are only handled if there + * is a corresponding listener on the server side. + * + * @since + * @author Vaadin Ltd + */ +public class ConnectorFocusAndBlurHandler implements StateChangeHandler, + FocusHandler, BlurHandler { + + private final AbstractComponentConnector connector; + private final Widget widget; + private HandlerRegistration focusRegistration = null; + private HandlerRegistration blurRegistration = null; + + public static void addHandlers(AbstractComponentConnector connector) { + addHandlers(connector, connector.getWidget()); + } + + public static void addHandlers(AbstractComponentConnector connector, + Widget widget) { + connector.addStateChangeHandler("registeredEventListeners", + new ConnectorFocusAndBlurHandler(connector, widget)); + } + + private ConnectorFocusAndBlurHandler(AbstractComponentConnector connector, + Widget widget) { + this.connector = connector; + this.widget = widget; + } + + @Override + public void onStateChanged(StateChangeEvent stateChangeEvent) { + focusRegistration = EventHelper.updateHandler(connector, this, + EventId.FOCUS, focusRegistration, FocusEvent.getType(), widget); + blurRegistration = EventHelper.updateHandler(connector, this, + EventId.BLUR, blurRegistration, BlurEvent.getType(), widget); + } + + @Override + public void onFocus(FocusEvent event) { + // updateHandler ensures that this is called only when + // there is a listener on the server side + getRpc().focus(); + } + + @Override + public void onBlur(BlurEvent event) { + // updateHandler ensures that this is called only when + // there is a listener on the server side + getRpc().blur(); + } + + private FocusAndBlurServerRpc getRpc() { + return connector.getRpcProxy(FocusAndBlurServerRpc.class); + } +} diff --git a/client/src/com/vaadin/client/ui/button/ButtonConnector.java b/client/src/com/vaadin/client/ui/button/ButtonConnector.java index 2d13d62a91..2c2006e19b 100644 --- a/client/src/com/vaadin/client/ui/button/ButtonConnector.java +++ b/client/src/com/vaadin/client/ui/button/ButtonConnector.java @@ -16,24 +16,17 @@ package com.vaadin.client.ui.button; -import com.google.gwt.event.dom.client.BlurEvent; -import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; -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.DOM; -import com.vaadin.client.EventHelper; import com.vaadin.client.MouseEventDetailsBuilder; import com.vaadin.client.VCaption; import com.vaadin.client.annotations.OnStateChange; -import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; import com.vaadin.client.ui.Icon; import com.vaadin.client.ui.VButton; import com.vaadin.shared.MouseEventDetails; -import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.Connect.LoadStyle; import com.vaadin.shared.ui.button.ButtonServerRpc; @@ -42,10 +35,7 @@ import com.vaadin.ui.Button; @Connect(value = Button.class, loadStyle = LoadStyle.EAGER) public class ButtonConnector extends AbstractComponentConnector implements - BlurHandler, FocusHandler, ClickHandler { - - private HandlerRegistration focusHandlerRegistration = null; - private HandlerRegistration blurHandlerRegistration = null; + ClickHandler { @Override public boolean delegateCaptionHandling() { @@ -57,6 +47,7 @@ public class ButtonConnector extends AbstractComponentConnector implements super.init(); getWidget().addClickHandler(this); getWidget().client = getConnection(); + ConnectorFocusAndBlurHandler.addHandlers(this); } @OnStateChange("errorMessage") @@ -90,15 +81,6 @@ public class ButtonConnector extends AbstractComponentConnector implements } } - @Override - public void onStateChanged(StateChangeEvent stateChangeEvent) { - super.onStateChanged(stateChangeEvent); - focusHandlerRegistration = EventHelper.updateFocusHandler(this, - focusHandlerRegistration); - blurHandlerRegistration = EventHelper.updateBlurHandler(this, - blurHandlerRegistration); - } - @OnStateChange({ "caption", "captionAsHtml" }) void setCaption() { VCaption.setCaptionText(getWidget().captionElement, getState()); @@ -126,20 +108,6 @@ public class ButtonConnector extends AbstractComponentConnector implements return (ButtonState) super.getState(); } - @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(); - } - @Override public void onClick(ClickEvent event) { if (getState().disableOnClick) { diff --git a/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java b/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java index 63984ff225..3daac849d0 100644 --- a/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java +++ b/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java @@ -16,25 +16,19 @@ package com.vaadin.client.ui.checkbox; import com.google.gwt.dom.client.Style.Display; -import com.google.gwt.event.dom.client.BlurEvent; -import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; -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.DOM; import com.google.gwt.user.client.Event; -import com.vaadin.client.EventHelper; import com.vaadin.client.MouseEventDetailsBuilder; import com.vaadin.client.VCaption; import com.vaadin.client.VTooltip; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractFieldConnector; +import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; import com.vaadin.client.ui.Icon; import com.vaadin.client.ui.VCheckBox; import com.vaadin.shared.MouseEventDetails; -import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.checkbox.CheckBoxServerRpc; import com.vaadin.shared.ui.checkbox.CheckBoxState; @@ -42,10 +36,7 @@ import com.vaadin.ui.CheckBox; @Connect(CheckBox.class) public class CheckBoxConnector extends AbstractFieldConnector implements - FocusHandler, BlurHandler, ClickHandler { - - private HandlerRegistration focusHandlerRegistration; - private HandlerRegistration blurHandlerRegistration; + ClickHandler { @Override public boolean delegateCaptionHandling() { @@ -55,21 +46,18 @@ public class CheckBoxConnector extends AbstractFieldConnector implements @Override protected void init() { super.init(); + getWidget().addClickHandler(this); getWidget().client = getConnection(); getWidget().id = getConnectorId(); + ConnectorFocusAndBlurHandler.addHandlers(this); } @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); - focusHandlerRegistration = EventHelper.updateFocusHandler(this, - focusHandlerRegistration); - blurHandlerRegistration = EventHelper.updateBlurHandler(this, - blurHandlerRegistration); - if (null != getState().errorMessage) { getWidget().setAriaInvalid(true); @@ -126,20 +114,6 @@ public class CheckBoxConnector extends AbstractFieldConnector implements return (VCheckBox) super.getWidget(); } - @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(); - } - @Override public void onClick(ClickEvent event) { if (!isEnabled()) { diff --git a/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java b/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java index 2aae9beae6..65d4a1eb9b 100644 --- a/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java +++ b/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java @@ -15,30 +15,20 @@ */ package com.vaadin.client.ui.nativebutton; -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.DOM; -import com.vaadin.client.EventHelper; import com.vaadin.client.VCaption; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; import com.vaadin.client.ui.Icon; import com.vaadin.client.ui.VNativeButton; -import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.button.ButtonServerRpc; import com.vaadin.shared.ui.button.NativeButtonState; import com.vaadin.ui.NativeButton; @Connect(NativeButton.class) -public class NativeButtonConnector extends AbstractComponentConnector implements - BlurHandler, FocusHandler { - - private HandlerRegistration focusHandlerRegistration; - private HandlerRegistration blurHandlerRegistration; +public class NativeButtonConnector extends AbstractComponentConnector { @Override public void init() { @@ -47,6 +37,8 @@ public class NativeButtonConnector extends AbstractComponentConnector implements getWidget().buttonRpcProxy = getRpcProxy(ButtonServerRpc.class); getWidget().client = getConnection(); getWidget().paintableId = getConnectorId(); + + ConnectorFocusAndBlurHandler.addHandlers(this); } @Override @@ -59,10 +51,6 @@ public class NativeButtonConnector extends AbstractComponentConnector implements super.onStateChanged(stateChangeEvent); getWidget().disableOnClick = getState().disableOnClick; - focusHandlerRegistration = EventHelper.updateFocusHandler(this, - focusHandlerRegistration); - blurHandlerRegistration = EventHelper.updateBlurHandler(this, - blurHandlerRegistration); // Set text VCaption.setCaptionText(getWidget(), getState()); @@ -107,19 +95,4 @@ public class NativeButtonConnector extends AbstractComponentConnector implements public NativeButtonState getState() { return (NativeButtonState) super.getState(); } - - @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/ui/nativeselect/NativeSelectConnector.java b/client/src/com/vaadin/client/ui/nativeselect/NativeSelectConnector.java index 938903da9a..d6ff2015b4 100644 --- a/client/src/com/vaadin/client/ui/nativeselect/NativeSelectConnector.java +++ b/client/src/com/vaadin/client/ui/nativeselect/NativeSelectConnector.java @@ -16,55 +16,23 @@ package com.vaadin.client.ui.nativeselect; -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.vaadin.client.EventHelper; -import com.vaadin.client.annotations.OnStateChange; +import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; import com.vaadin.client.ui.VNativeSelect; import com.vaadin.client.ui.optiongroup.OptionGroupBaseConnector; -import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc; import com.vaadin.shared.ui.Connect; import com.vaadin.ui.NativeSelect; @Connect(NativeSelect.class) -public class NativeSelectConnector extends OptionGroupBaseConnector implements - BlurHandler, FocusHandler { +public class NativeSelectConnector extends OptionGroupBaseConnector { - private HandlerRegistration focusHandlerRegistration = null; - private HandlerRegistration blurHandlerRegistration = null; - - public NativeSelectConnector() { - super(); - } - - @OnStateChange("registeredEventListeners") - private void onServerEventListenerChanged() { - focusHandlerRegistration = EventHelper.updateFocusHandler(this, - focusHandlerRegistration, getWidget().getSelect()); - blurHandlerRegistration = EventHelper.updateBlurHandler(this, - blurHandlerRegistration, getWidget().getSelect()); + @Override + protected void init() { + super.init(); + ConnectorFocusAndBlurHandler.addHandlers(this, getWidget().getSelect()); } @Override public VNativeSelect getWidget() { return (VNativeSelect) super.getWidget(); } - - @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(); - } - } -- 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 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(-) 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 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 11d95f2a97f4ffc4ded3421952ab2f57683679b4 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Wed, 27 May 2015 15:33:56 +0300 Subject: Allow vertical scroll in unbuffered edit mode. Change-Id: Ia1e3dde57fdfcbc4fef6b91d3686dc9121e8ad56 --- client/src/com/vaadin/client/widgets/Grid.java | 44 +++++++++++++++++- .../server/GridEditorBufferedTest.java | 52 +++++++++++++++++++++- .../grid/basicfeatures/server/GridEditorTest.java | 44 ------------------ .../server/GridEditorUnbufferedTest.java | 45 ++++++++++++++++++- 4 files changed, 137 insertions(+), 48 deletions(-) diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 4616c43902..037131bec8 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -1227,6 +1227,11 @@ public class Grid extends ResizeComposite implements private final Set> columnErrors = new HashSet>(); private boolean buffered = true; + /** Original position of editor */ + private double originalTop; + /** Original scroll position of grid when editor was opened */ + private double originalScrollTop; + public Editor() { saveButton = new Button(); saveButton.setText(GridConstants.DEFAULT_SAVE_CAPTION); @@ -1420,7 +1425,8 @@ public class Grid extends ResizeComposite implements EditorRequest request = new EditorRequestImpl(grid, rowIndex, bindRequestCallback); handler.bind(request); - grid.getEscalator().setScrollLocked(Direction.VERTICAL, true); + grid.getEscalator().setScrollLocked(Direction.VERTICAL, + isBuffered()); updateSelectionCheckboxesAsNeeded(false); } } @@ -1484,6 +1490,9 @@ public class Grid extends ResizeComposite implements @Override public void onScroll(ScrollEvent event) { updateHorizontalScrollPosition(); + if (!isBuffered()) { + updateVerticalScrollPosition(); + } } }); @@ -1527,9 +1536,11 @@ public class Grid extends ResizeComposite implements int gridTop = gridElement.getAbsoluteTop(); double overlayTop = rowTop + bodyTop - gridTop; - if (buttonsShouldBeRenderedBelow(tr)) { + originalScrollTop = grid.getScrollTop(); + if (!isBuffered() || buttonsShouldBeRenderedBelow(tr)) { // Default case, editor buttons are below the edited row editorOverlay.getStyle().setTop(overlayTop, Unit.PX); + originalTop = overlayTop; editorOverlay.getStyle().clearBottom(); } else { // Move message and buttons wrapper on top of cell wrapper if @@ -1649,6 +1660,35 @@ public class Grid extends ResizeComposite implements cellWrapper.getStyle().setLeft(-scrollLeft, Unit.PX); } + /** + * Moves the editor overlay on scroll so that it stays on top of the + * edited row. This will also snap the editor to top or bottom of the + * row container if the edited row is scrolled out of the visible area. + */ + private void updateVerticalScrollPosition() { + double newScrollTop = grid.getScrollTop(); + + int gridTop = grid.getElement().getAbsoluteTop(); + int editorHeight = editorOverlay.getOffsetHeight(); + + Escalator escalator = grid.getEscalator(); + TableSectionElement header = escalator.getHeader().getElement(); + int footerTop = escalator.getFooter().getElement().getAbsoluteTop(); + int headerBottom = header.getAbsoluteBottom(); + + double newTop = originalTop - (newScrollTop - originalScrollTop); + + if (newTop + gridTop < headerBottom) { + // Snap editor to top of the row container + newTop = header.getOffsetHeight(); + } else if (newTop + gridTop > footerTop - editorHeight) { + // Snap editor to the bottom of the row container + newTop = footerTop - editorHeight - gridTop; + } + + editorOverlay.getStyle().setTop(newTop, Unit.PX); + } + protected void setGridEnabled(boolean enabled) { // TODO: This should be informed to handler as well so possible // fields can be disabled. 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 49a5174f8d..cc4b894e14 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 @@ -23,9 +23,13 @@ import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +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; @@ -121,7 +125,8 @@ public class GridEditorBufferedTest extends GridEditorTest { selectMenuPath("Component", "State", "Enabled"); scrollGridVerticallyTo(100); - assertEquals("Grid shouldn't scroll vertically while editing", + assertEquals( + "Grid shouldn't scroll vertically while editing in buffered mode", originalScrollPos, getGridVerticalScrollPos()); } @@ -150,4 +155,49 @@ public class GridEditorBufferedTest extends GridEditorTest { GridConstants.DEFAULT_CANCEL_CAPTION, getCancelButton() .getText()); } + + @Test(expected = NoSuchElementException.class) + public void testVerticalScrollLocking() { + selectMenuPath(EDIT_ITEM_5); + getGridElement().getCell(200, 0); + } + + @Test + public void testNoScrollAfterEditByAPI() { + int originalScrollPos = getGridVerticalScrollPos(); + + selectMenuPath(EDIT_ITEM_5); + + scrollGridVerticallyTo(100); + assertEquals( + "Grid shouldn't scroll vertically while editing in buffered mode", + originalScrollPos, getGridVerticalScrollPos()); + } + + @Test + public void testNoScrollAfterEditByMouse() { + int originalScrollPos = getGridVerticalScrollPos(); + + GridCellElement cell_5_0 = getGridElement().getCell(5, 0); + new Actions(getDriver()).doubleClick(cell_5_0).perform(); + + scrollGridVerticallyTo(100); + assertEquals( + "Grid shouldn't scroll vertically while editing in buffered mode", + originalScrollPos, getGridVerticalScrollPos()); + } + + @Test + public void testNoScrollAfterEditByKeyboard() { + int originalScrollPos = getGridVerticalScrollPos(); + + GridCellElement cell_5_0 = getGridElement().getCell(5, 0); + cell_5_0.click(); + new Actions(getDriver()).sendKeys(Keys.ENTER).perform(); + + scrollGridVerticallyTo(100); + assertEquals( + "Grid shouldn't scroll vertically while editing in buffered mode", + originalScrollPos, getGridVerticalScrollPos()); + } } 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 b32d50024d..9f743c93a1 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 @@ -27,11 +27,9 @@ import org.junit.Before; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.Keys; -import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; -import com.vaadin.testbench.elements.GridElement.GridCellElement; import com.vaadin.tests.components.grid.basicfeatures.GridBasicFeatures; import com.vaadin.tests.components.grid.basicfeatures.GridBasicFeaturesTest; @@ -84,12 +82,6 @@ public abstract class GridEditorTest extends GridBasicFeaturesTest { assertEditorOpen(); } - @Test(expected = NoSuchElementException.class) - public void testVerticalScrollLocking() { - selectMenuPath(EDIT_ITEM_5); - getGridElement().getCell(200, 0); - } - @Test public void testKeyboardOpeningClosing() { @@ -139,42 +131,6 @@ public abstract class GridEditorTest extends GridBasicFeaturesTest { } - @Test - public void testNoScrollAfterEditByAPI() { - int originalScrollPos = getGridVerticalScrollPos(); - - selectMenuPath(EDIT_ITEM_5); - - scrollGridVerticallyTo(100); - assertEquals("Grid shouldn't scroll vertically while editing", - originalScrollPos, getGridVerticalScrollPos()); - } - - @Test - public void testNoScrollAfterEditByMouse() { - int originalScrollPos = getGridVerticalScrollPos(); - - GridCellElement cell_5_0 = getGridElement().getCell(5, 0); - new Actions(getDriver()).doubleClick(cell_5_0).perform(); - - scrollGridVerticallyTo(100); - assertEquals("Grid shouldn't scroll vertically while editing", - originalScrollPos, getGridVerticalScrollPos()); - } - - @Test - public void testNoScrollAfterEditByKeyboard() { - int originalScrollPos = getGridVerticalScrollPos(); - - GridCellElement cell_5_0 = getGridElement().getCell(5, 0); - cell_5_0.click(); - new Actions(getDriver()).sendKeys(Keys.ENTER).perform(); - - scrollGridVerticallyTo(100); - assertEquals("Grid shouldn't scroll vertically while editing", - originalScrollPos, getGridVerticalScrollPos()); - } - @Test public void testUneditableColumn() { selectMenuPath(EDIT_ITEM_5); 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 821239f1dc..59e9a2972a 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 @@ -20,7 +20,11 @@ import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; +import org.openqa.selenium.Keys; import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.elements.GridElement.GridCellElement; public class GridEditorUnbufferedTest extends GridEditorTest { @@ -85,4 +89,43 @@ public class GridEditorUnbufferedTest extends GridEditorTest { assertTrue("Editor is not at correct row index (10)", "(10, 0)".equals(firstFieldValue)); } -} + + @Test + public void testNoScrollAfterEditByAPI() { + int originalScrollPos = getGridVerticalScrollPos(); + + selectMenuPath(EDIT_ITEM_5); + + scrollGridVerticallyTo(100); + assertGreater( + "Grid should scroll vertically while editing in unbuffered mode", + getGridVerticalScrollPos(), originalScrollPos); + } + + @Test + public void testNoScrollAfterEditByMouse() { + int originalScrollPos = getGridVerticalScrollPos(); + + GridCellElement cell_5_0 = getGridElement().getCell(5, 0); + new Actions(getDriver()).doubleClick(cell_5_0).perform(); + + scrollGridVerticallyTo(100); + assertGreater( + "Grid should scroll vertically while editing in unbuffered mode", + getGridVerticalScrollPos(), originalScrollPos); + } + + @Test + public void testNoScrollAfterEditByKeyboard() { + int originalScrollPos = getGridVerticalScrollPos(); + + GridCellElement cell_5_0 = getGridElement().getCell(5, 0); + cell_5_0.click(); + new Actions(getDriver()).sendKeys(Keys.ENTER).perform(); + + scrollGridVerticallyTo(100); + assertGreater( + "Grid should scroll vertically while editing in unbuffered mode", + getGridVerticalScrollPos(), originalScrollPos); + } +} \ No newline at end of file -- cgit v1.2.3 From cf563c053fdf0d6b2991eba98e1cc118a8e74e54 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Fri, 29 May 2015 13:53:56 +0300 Subject: Unbuffered editor hides notification area if no error msg present. Change-Id: I2441d684baeb9dc48ffad845a097c6cce51a9436 --- client/src/com/vaadin/client/widgets/Grid.java | 16 ++++++++++++++++ .../basicfeatures/server/GridEditorUnbufferedTest.java | 15 +++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 037131bec8..cc37074259 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -41,6 +41,7 @@ import com.google.gwt.dom.client.EventTarget; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Display; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.dom.client.TableCellElement; import com.google.gwt.dom.client.TableRowElement; @@ -1261,6 +1262,10 @@ public class Grid extends ResizeComposite implements messageWrapper.appendChild(message); } } + // In unbuffered mode only show message wrapper if there is an error + if (!isBuffered()) { + setMessageAndButtonsWrapperVisible(errorMessage != null); + } } public int getRow() { @@ -1526,6 +1531,8 @@ public class Grid extends ResizeComposite implements attachWidget(cancelButton, buttonsWrapper); } + setMessageAndButtonsWrapperVisible(isBuffered()); + updateHorizontalScrollPosition(); AbstractRowContainer body = (AbstractRowContainer) grid @@ -1768,11 +1775,20 @@ public class Grid extends ResizeComposite implements public void setBuffered(boolean buffered) { this.buffered = buffered; + setMessageAndButtonsWrapperVisible(buffered); } public boolean isBuffered() { return buffered; } + + private void setMessageAndButtonsWrapperVisible(boolean visible) { + if (visible) { + messageAndButtonsWrapper.getStyle().clearDisplay(); + } else { + messageAndButtonsWrapper.getStyle().setDisplay(Display.NONE); + } + } } public static abstract class AbstractGridKeyEvent 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 59e9a2972a..60fe154f85 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 @@ -20,8 +20,10 @@ import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; +import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; import com.vaadin.testbench.elements.GridElement.GridCellElement; @@ -90,6 +92,19 @@ public class GridEditorUnbufferedTest extends GridEditorTest { "(10, 0)".equals(firstFieldValue)); } + @Test + public void testErrorMessageWrapperHidden() { + selectMenuPath(EDIT_ITEM_5); + + assertEditorOpen(); + + WebElement editorFooter = getEditor().findElement( + By.className("v-grid-editor-footer")); + + assertTrue("Editor footer should not be visible when there's no error", + editorFooter.getCssValue("display").equalsIgnoreCase("none")); + } + @Test public void testNoScrollAfterEditByAPI() { int originalScrollPos = getGridVerticalScrollPos(); -- cgit v1.2.3 From 6008e42ce8d1e796e402dfd8d3f462c502d22269 Mon Sep 17 00:00:00 2001 From: Johannes Dahlström Date: Tue, 2 Jun 2015 12:19:11 +0300 Subject: Don't open Grid editor from header or footer Change-Id: Ide40c6331a41bbdd4b5eb7baddbec07fcccb2a5d --- client/src/com/vaadin/client/widgets/Grid.java | 8 ++++---- .../grid/basicfeatures/server/GridEditorTest.java | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 9cd42199c7..cb3735c141 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -6462,10 +6462,10 @@ public class Grid extends ResizeComposite implements final int key = event.getKeyCode(); final boolean editorIsActive = editor.getState() != Editor.State.INACTIVE; - final boolean openEvent = type == Event.ONDBLCLICK - || (type == Event.ONKEYDOWN && key == Editor.KEYCODE_SHOW); + final boolean openEvent = eventCell.isBody() + && (type == Event.ONDBLCLICK || (type == Event.ONKEYDOWN && key == Editor.KEYCODE_SHOW)); - final boolean moveEvent = type == Event.ONCLICK; + final boolean moveEvent = eventCell.isBody() && type == Event.ONCLICK; final boolean closeEvent = type == Event.ONKEYDOWN && key == Editor.KEYCODE_HIDE; @@ -6477,7 +6477,7 @@ public class Grid extends ResizeComposite implements return true; - } else if (editorIsActive && eventCell.isBody() && moveEvent) { + } else if (editorIsActive && moveEvent) { editor.hide(); cellFocusHandler.setCellFocus(eventCell); 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 b724ce4aa4..4c4b1c6f8b 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 @@ -188,6 +188,23 @@ public abstract class GridEditorTest extends GridBasicFeaturesTest { } + @Test + public void testNoOpenFromHeaderOrFooter() { + selectMenuPath("Component", "Footer", "Visible"); + + getGridElement().getHeaderCell(0, 0).doubleClick(); + assertEditorClosed(); + + new Actions(getDriver()).sendKeys(Keys.ENTER).perform(); + assertEditorClosed(); + + getGridElement().getFooterCell(0, 0).doubleClick(); + assertEditorClosed(); + + new Actions(getDriver()).sendKeys(Keys.ENTER).perform(); + assertEditorClosed(); + } + protected WebElement getSaveButton() { return getDriver().findElement(By.className("v-grid-editor-save")); } -- cgit v1.2.3 From ec54a601dc882333687bcef2b4fa59c02e6c4ede Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Tue, 2 Jun 2015 12:39:05 +0300 Subject: Update editor overlay position on Grid resize Change-Id: I980d6fa95939273c493e8ed726aa436d9d961003 --- client/src/com/vaadin/client/widgets/Grid.java | 5 +++ .../grid/basicfeatures/server/GridEditorTest.java | 46 ++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index cb3735c141..2717dc3580 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -7692,6 +7692,11 @@ public class Grid extends ResizeComposite implements @Override public void execute() { recalculateColumnWidths(); + // Vertical resizing could make editor positioning invalid so it + // needs to be recalculated on resize + if (isEditorActive()) { + editor.updateVerticalScrollPosition(); + } } }); } 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 4c4b1c6f8b..0a6d884251 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 @@ -30,6 +30,7 @@ import org.openqa.selenium.Keys; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; +import com.vaadin.testbench.TestBenchElement; import com.vaadin.testbench.elements.GridElement.GridCellElement; import com.vaadin.testbench.elements.GridElement.GridEditorElement; import com.vaadin.tests.components.grid.basicfeatures.GridBasicFeatures; @@ -205,6 +206,51 @@ public abstract class GridEditorTest extends GridBasicFeaturesTest { assertEditorClosed(); } + public void testEditorMoveOnResize() { + selectMenuPath("Component", "Size", "Height", "500px"); + getGridElement().getCell(22, 0).doubleClick(); + assertEditorOpen(); + + GridEditorElement editor = getGridElement().getEditor(); + TestBenchElement tableWrapper = getGridElement().getTableWrapper(); + + int tableWrapperBottom = tableWrapper.getLocation().getY() + + tableWrapper.getSize().getHeight(); + int editorBottom = editor.getLocation().getY() + + editor.getSize().getHeight(); + + assertTrue("Editor should not be initially outside grid", + tableWrapperBottom - editorBottom <= 2); + + selectMenuPath("Component", "Size", "Height", "300px"); + assertEditorOpen(); + + tableWrapperBottom = tableWrapper.getLocation().getY() + + tableWrapper.getSize().getHeight(); + editorBottom = editor.getLocation().getY() + + editor.getSize().getHeight(); + + assertTrue("Editor should not be outside grid after resize", + tableWrapperBottom - editorBottom <= 2); + } + + public void testEditorDoesNotMoveOnResizeIfNotNeeded() { + selectMenuPath("Component", "Size", "Height", "500px"); + + selectMenuPath(EDIT_ITEM_5); + assertEditorOpen(); + + GridEditorElement editor = getGridElement().getEditor(); + + int editorPos = editor.getLocation().getY(); + + selectMenuPath("Component", "Size", "Height", "300px"); + assertEditorOpen(); + + assertTrue("Editor should not have moved due to resize", + editorPos == editor.getLocation().getY()); + } + protected WebElement getSaveButton() { return getDriver().findElement(By.className("v-grid-editor-save")); } -- cgit v1.2.3 From eed4059ca05483f7874f45bfbd876b6c2dfe4bb0 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Tue, 2 Jun 2015 16:29:57 +0300 Subject: On progr. editor open, focus target cell if Grid had focus prior Change-Id: I8ad9100356a309309e1f8964d6bc293981b2a827 --- client/src/com/vaadin/client/widgets/Grid.java | 18 ++++++++++++++++-- .../grid/basicfeatures/GridBasicFeatures.java | 21 +++++++++++++++++++++ .../grid/basicfeatures/server/GridEditorTest.java | 14 ++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 2717dc3580..db3cea58dc 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -1280,12 +1280,26 @@ public class Grid extends ResizeComposite implements } /** - * Equivalent to {@code editRow(rowIndex, -1)}. + * If a cell of this Grid had focus once this editRow call was + * triggered, the editor component at the previously focused column + * index will be focused. + * + * If a Grid cell was not focused prior to calling this method, it will + * be equivalent to {@code editRow(rowIndex, -1)}. * * @see #editRow(int, int) */ public void editRow(int rowIndex) { - editRow(rowIndex, -1); + // Focus the last focused column in the editor iff grid or its child + // was focused before the edit request + Cell focusedCell = grid.cellFocusHandler.getFocusedCell(); + if (focusedCell != null + && grid.getElement().isOrHasChild( + WidgetUtil.getFocusedElement())) { + editRow(rowIndex, focusedCell.getColumn()); + } else { + editRow(rowIndex, -1); + } } /** 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 c98ee5c53b..a9cc528ac0 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -127,6 +127,14 @@ public class GridBasicFeatures extends AbstractComponentTest { } }; + private ItemClickListener editorOpeningItemClickListener = new ItemClickListener() { + + @Override + public void itemClick(ItemClickEvent event) { + grid.editItem(event.getItemId()); + } + }; + private ColumnReorderListener columnReorderListener = new ColumnReorderListener() { @Override @@ -642,6 +650,19 @@ public class GridBasicFeatures extends AbstractComponentTest { } } + }); + createBooleanAction("EditorOpeningItemClickListener", "State", false, + new Command() { + + @Override + public void execute(Grid c, Boolean value, Object data) { + if (!value) { + c.removeItemClickListener(editorOpeningItemClickListener); + } else { + c.addItemClickListener(editorOpeningItemClickListener); + } + } + }); createBooleanAction("ColumnReorderListener", "State", false, new Command() { 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 0a6d884251..ba6c29522b 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 @@ -161,6 +161,20 @@ public abstract class GridEditorTest extends GridBasicFeaturesTest { assertEquals("", cell.getText(), focused.getAttribute("value")); } + @Test + public void testFocusOnProgrammaticOpenOnItemClick() { + selectMenuPath("Component", "State", "EditorOpeningItemClickListener"); + + GridCellElement cell = getGridElement().getCell(4, 2); + + cell.click(); + + WebElement focused = getFocusedElement(); + + assertEquals("", "input", focused.getTagName()); + assertEquals("", cell.getText(), focused.getAttribute("value")); + } + @Test public void testNoFocusOnProgrammaticOpen() { -- cgit v1.2.3 From a4b2dc6caf82487903e583edfa30eec837bbb5b1 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Wed, 3 Jun 2015 15:19:48 +0300 Subject: Fix broken Grid editor tests Change-Id: Iaef796694f09983d208b63af5e211e74512ff7be --- .../basicfeatures/server/GridEditorBufferedTest.java | 2 +- .../grid/basicfeatures/server/GridEditorTest.java | 8 ++++++-- .../server/GridEditorUnbufferedTest.java | 19 +++---------------- 3 files changed, 10 insertions(+), 19 deletions(-) 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 9f55d2adcb..606fe783d4 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 @@ -102,7 +102,7 @@ public class GridEditorBufferedTest extends GridEditorTest { assertFalse("Exception should not exist", isElementPresent(NotificationElement.class)); assertEquals("There should be no editor error message", null, - editor.getErrorMessage()); + getGridElement().getEditor().getErrorMessage()); } @Test 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 ba6c29522b..dbb903ff98 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 @@ -38,6 +38,10 @@ import com.vaadin.tests.components.grid.basicfeatures.GridBasicFeaturesTest; public abstract class GridEditorTest extends GridBasicFeaturesTest { + protected static final By BY_EDITOR_CANCEL = By + .className("v-grid-editor-cancel"); + protected static final By BY_EDITOR_SAVE = By + .className("v-grid-editor-save"); protected static final String[] EDIT_ITEM_5 = new String[] { "Component", "Editor", "Edit item 5" }; protected static final String[] EDIT_ITEM_100 = new String[] { "Component", @@ -266,10 +270,10 @@ public abstract class GridEditorTest extends GridBasicFeaturesTest { } protected WebElement getSaveButton() { - return getDriver().findElement(By.className("v-grid-editor-save")); + return getDriver().findElement(BY_EDITOR_SAVE); } protected WebElement getCancelButton() { - return getDriver().findElement(By.className("v-grid-editor-cancel")); + return getDriver().findElement(BY_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 index 80e273ff1d..90a4c8b0b1 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 @@ -22,7 +22,6 @@ import org.junit.Before; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.Keys; -import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; @@ -48,23 +47,11 @@ public class GridEditorUnbufferedTest extends GridEditorTest { 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; - } + isElementPresent(BY_EDITOR_SAVE)); + assertFalse("Cancel button should not be visible in unbuffered mode.", - cancelButtonFound); + isElementPresent(BY_EDITOR_CANCEL)); } @Test -- 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(-) 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 97bb024d847e3eaddd537dacdfe5eaff9b319334 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Tue, 30 Jun 2015 12:05:11 +0300 Subject: Use the type and key vars since they are there already Change-Id: I76fca3f2260dadbaf65496ce02b25e8d0f6c9a2f --- client/src/com/vaadin/client/widgets/Grid.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 57c8a503f5..2cc1fbad2b 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -6560,23 +6560,22 @@ public class Grid extends ResizeComposite implements int currentX = WidgetUtil.getTouchOrMouseClientX(event); int currentY = WidgetUtil.getTouchOrMouseClientY(event); - final boolean validTouchOpenEvent = event.getTypeInt() == Event.ONTOUCHEND + final boolean validTouchOpenEvent = type == Event.ONTOUCHEND && now - lastTouchEventTime < 500 && lastTouchEventRow == eventCell.getRowIndex() && Math.abs(lastTouchEventX - currentX) < 20 && Math.abs(lastTouchEventY - currentY) < 20; final boolean openEvent = eventCell.isBody() - && (event.getTypeInt() == Event.ONDBLCLICK - || (event.getTypeInt() == Event.ONKEYDOWN && event - .getKeyCode() == Editor.KEYCODE_SHOW) || validTouchOpenEvent); + && (type == Event.ONDBLCLICK + || (type == Event.ONKEYDOWN && key == Editor.KEYCODE_SHOW) || validTouchOpenEvent); - if (event.getTypeInt() == Event.ONTOUCHSTART) { + if (type == Event.ONTOUCHSTART) { lastTouchEventX = currentX; lastTouchEventY = currentY; } - if (event.getTypeInt() == Event.ONTOUCHEND) { + if (type == Event.ONTOUCHEND) { lastTouchEventTime = now; lastTouchEventRow = eventCell.getRowIndex(); } -- cgit v1.2.3 From db9929d8fc79d37bb299bad2ee644428f3bddc94 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Thu, 25 Jun 2015 15:01:06 +0300 Subject: Added test to confirm that value change affects the editor field Change-Id: I06e4ba377d89393f2ca912ecb5f845f3992a864a --- .../grid/basicfeatures/GridBasicFeatures.java | 32 ++++++++++++++++++++++ .../server/GridEditorUnbufferedTest.java | 18 ++++++++++++ 2 files changed, 50 insertions(+) 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 44a6da224e..35e39c9986 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -29,6 +29,8 @@ import java.util.Random; import com.vaadin.data.Container.Filter; import com.vaadin.data.Item; import com.vaadin.data.Property; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.data.fieldgroup.FieldGroup.CommitException; import com.vaadin.data.sort.Sort; import com.vaadin.data.sort.SortOrder; @@ -48,6 +50,7 @@ import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; 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.CellReference; import com.vaadin.ui.Grid.CellStyleGenerator; @@ -135,6 +138,16 @@ public class GridBasicFeatures extends AbstractComponentTest { } }; + private ValueChangeListener reactiveValueChanger = new ValueChangeListener() { + @Override + @SuppressWarnings("unchecked") + public void valueChange(ValueChangeEvent event) { + Object id = grid.getEditedItemId(); + grid.getContainerDataSource().getContainerProperty(id, "Column 2") + .setValue("Modified"); + } + }; + private ColumnReorderListener columnReorderListener = new ColumnReorderListener() { @Override @@ -663,6 +676,25 @@ public class GridBasicFeatures extends AbstractComponentTest { } } + }); + createBooleanAction("ReactiveValueChanger", "State", false, + new Command() { + + @Override + public void execute(Grid c, Boolean value, Object data) { + Field targetField = grid.getEditorFieldGroup() + .getField("Column 0"); + if (targetField != null) { + if (!value) { + targetField + .removeValueChangeListener(reactiveValueChanger); + } else { + targetField + .addValueChangeListener(reactiveValueChanger); + } + } + } + }); createBooleanAction("ColumnReorderListener", "State", false, new Command() { 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 4725d24903..87c6cc805d 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 @@ -176,4 +176,22 @@ public class GridEditorUnbufferedTest extends GridEditorTest { assertEquals("Editor should edit row 100", "(100, 0)", getEditorWidgets().get(0).getAttribute("value")); } + + @Test + public void testExternalValueChangePassesToEditor() { + selectMenuPath(EDIT_ITEM_5); + assertEditorOpen(); + + selectMenuPath("Component", "State", "ReactiveValueChanger"); + + getEditorWidgets().get(0).click(); + getEditorWidgets().get(0).sendKeys("changing value"); + + // Focus another field to cause the value to be sent to the server + getEditorWidgets().get(2).click(); + + assertEquals("Value of Column 2 in the editor was not changed", + "Modified", getEditorWidgets().get(2).getAttribute("value")); + } + } \ No newline at end of file -- cgit v1.2.3 From 5460f5ec736f479795d3301acb73d62040c99311 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Thu, 25 Jun 2015 16:32:32 +0300 Subject: Fixes non-editable column and selection visibility in editor Change-Id: Ic336ac744ce66b1fd182aaae3cff6d1006b8238f --- WebContent/VAADIN/themes/base/grid/grid.scss | 12 ++++++++++++ client/src/com/vaadin/client/widgets/Grid.java | 7 ++++++- .../grid/basicfeatures/server/GridEditorTest.java | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/WebContent/VAADIN/themes/base/grid/grid.scss b/WebContent/VAADIN/themes/base/grid/grid.scss index 1653032703..3fa5ddeb19 100644 --- a/WebContent/VAADIN/themes/base/grid/grid.scss +++ b/WebContent/VAADIN/themes/base/grid/grid.scss @@ -189,6 +189,14 @@ $v-grid-details-border-bottom-stripe: 1px solid darken($v-grid-row-background-co } // Rows + .#{$primaryStyleName}-row.editing { + > td { + background: $v-grid-editor-background-color; + } + &.#{$primaryStyleName}-row-selected > .v-grid-cell { + border-left: $v-grid-border; + } + } .#{$primaryStyleName}-row > td, .#{$primaryStyleName}-editor-cells > div { @@ -349,6 +357,10 @@ $v-grid-details-border-bottom-stripe: 1px solid darken($v-grid-row-background-co vertical-align: middle; background: $v-grid-editor-background-color; + &.not-editable { + visibility: hidden; + } + &:first-child { border-left: none; } diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 1dd9a9f06b..c46e8d03e8 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -1115,6 +1115,7 @@ public class Grid extends ResizeComposite implements private static final String ERROR_CLASS_NAME = "error"; private static final String NOT_EDITABLE_CLASS_NAME = "not-editable"; + private static final String CURRENTLY_EDITING_CLASS_NAME = "editing"; protected enum State { INACTIVE, ACTIVATING, BINDING, ACTIVE, SAVING @@ -1542,6 +1543,7 @@ public class Grid extends ResizeComposite implements TableRowElement tr = grid.getEscalator().getBody() .getRowElement(rowIndex); + tr.addClassName(CURRENTLY_EDITING_CLASS_NAME); scrollHandler = grid.addScrollHandler(new ScrollHandler() { @Override @@ -1647,7 +1649,6 @@ public class Grid extends ResizeComposite implements if (editorOverlay.getParentElement() == null) { return; } - for (Widget w : columnToWidget.values()) { setParent(w, null); } @@ -1656,6 +1657,10 @@ public class Grid extends ResizeComposite implements detachWidget(saveButton); detachWidget(cancelButton); + TableRowElement tr = grid.getEscalator().getBody() + .getRowElement(rowIndex); + tr.removeClassName(CURRENTLY_EDITING_CLASS_NAME); + editorOverlay.removeAllChildren(); cellWrapper.removeAllChildren(); editorOverlay.removeFromParent(); 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..2cb40e1df8 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 @@ -56,6 +56,20 @@ public abstract class GridEditorTest extends GridBasicFeaturesTest { selectMenuPath(TOGGLE_EDIT_ENABLED); } + @Test + public void testNonEditableCellDoesNotBlockContents() { + selectMenuPath(EDIT_ITEM_5); + assertEditorOpen(); + + WebElement editorCellContainer = getEditor().findElement( + By.className("v-grid-editor-cells")); + WebElement nonEditableDiv = editorCellContainer.findElements( + By.tagName("div")).get(3); + + assertEquals("Non editable div was not hidden", "hidden", + nonEditableDiv.getCssValue("visibility")); + } + @Test public void testProgrammaticOpeningClosing() { selectMenuPath(EDIT_ITEM_5); -- 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(-) 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 2b255c73094fe4d785a225ca6334790b645b4b64 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Thu, 2 Jul 2015 13:10:24 +0300 Subject: Fix Grid regression in editor opening with no focused element Change-Id: I94d9765cb8177b6cc83158486b609efb255e4beb --- client/src/com/vaadin/client/widgets/Grid.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 1665a76c7a..eabe133fd1 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -1294,9 +1294,9 @@ public class Grid extends ResizeComposite implements // Focus the last focused column in the editor iff grid or its child // was focused before the edit request Cell focusedCell = grid.cellFocusHandler.getFocusedCell(); - if (focusedCell != null - && grid.getElement().isOrHasChild( - WidgetUtil.getFocusedElement())) { + Element focusedElement = WidgetUtil.getFocusedElement(); + if (focusedCell != null && focusedElement != null + && grid.getElement().isOrHasChild(focusedElement)) { editRow(rowIndex, focusedCell.getColumn()); } else { editRow(rowIndex, -1); -- cgit v1.2.3 From aecc8f5a42faa6ee461e879f552ced37d1a47d0d Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Fri, 3 Jul 2015 11:42:35 +0000 Subject: Revert "Fixes non-editable column and selection visibility in editor" This reverts commit 5460f5ec736f479795d3301acb73d62040c99311. Change-Id: I043e3db57b55694c6ef87751e487472a778c1b8a --- WebContent/VAADIN/themes/base/grid/grid.scss | 12 ------------ client/src/com/vaadin/client/widgets/Grid.java | 7 +------ .../grid/basicfeatures/server/GridEditorTest.java | 14 -------------- 3 files changed, 1 insertion(+), 32 deletions(-) diff --git a/WebContent/VAADIN/themes/base/grid/grid.scss b/WebContent/VAADIN/themes/base/grid/grid.scss index 3fa5ddeb19..1653032703 100644 --- a/WebContent/VAADIN/themes/base/grid/grid.scss +++ b/WebContent/VAADIN/themes/base/grid/grid.scss @@ -189,14 +189,6 @@ $v-grid-details-border-bottom-stripe: 1px solid darken($v-grid-row-background-co } // Rows - .#{$primaryStyleName}-row.editing { - > td { - background: $v-grid-editor-background-color; - } - &.#{$primaryStyleName}-row-selected > .v-grid-cell { - border-left: $v-grid-border; - } - } .#{$primaryStyleName}-row > td, .#{$primaryStyleName}-editor-cells > div { @@ -357,10 +349,6 @@ $v-grid-details-border-bottom-stripe: 1px solid darken($v-grid-row-background-co vertical-align: middle; background: $v-grid-editor-background-color; - &.not-editable { - visibility: hidden; - } - &:first-child { border-left: none; } diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index eabe133fd1..5cbfd5ea75 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -1115,7 +1115,6 @@ public class Grid extends ResizeComposite implements private static final String ERROR_CLASS_NAME = "error"; private static final String NOT_EDITABLE_CLASS_NAME = "not-editable"; - private static final String CURRENTLY_EDITING_CLASS_NAME = "editing"; protected enum State { INACTIVE, ACTIVATING, BINDING, ACTIVE, SAVING @@ -1543,7 +1542,6 @@ public class Grid extends ResizeComposite implements TableRowElement tr = grid.getEscalator().getBody() .getRowElement(rowIndex); - tr.addClassName(CURRENTLY_EDITING_CLASS_NAME); scrollHandler = grid.addScrollHandler(new ScrollHandler() { @Override @@ -1649,6 +1647,7 @@ public class Grid extends ResizeComposite implements if (editorOverlay.getParentElement() == null) { return; } + for (Widget w : columnToWidget.values()) { setParent(w, null); } @@ -1657,10 +1656,6 @@ public class Grid extends ResizeComposite implements detachWidget(saveButton); detachWidget(cancelButton); - TableRowElement tr = grid.getEscalator().getBody() - .getRowElement(rowIndex); - tr.removeClassName(CURRENTLY_EDITING_CLASS_NAME); - editorOverlay.removeAllChildren(); cellWrapper.removeAllChildren(); editorOverlay.removeFromParent(); 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 2cb40e1df8..dbb903ff98 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 @@ -56,20 +56,6 @@ public abstract class GridEditorTest extends GridBasicFeaturesTest { selectMenuPath(TOGGLE_EDIT_ENABLED); } - @Test - public void testNonEditableCellDoesNotBlockContents() { - selectMenuPath(EDIT_ITEM_5); - assertEditorOpen(); - - WebElement editorCellContainer = getEditor().findElement( - By.className("v-grid-editor-cells")); - WebElement nonEditableDiv = editorCellContainer.findElements( - By.tagName("div")).get(3); - - assertEquals("Non editable div was not hidden", "hidden", - nonEditableDiv.getCssValue("visibility")); - } - @Test public void testProgrammaticOpeningClosing() { selectMenuPath(EDIT_ITEM_5); -- cgit v1.2.3 From 7a5b61f19838dec76715026e5232c565082190a7 Mon Sep 17 00:00:00 2001 From: Teppo Kurki Date: Fri, 3 Jul 2015 13:52:51 +0300 Subject: Added missing "grid" TestCategory for grid tests Change-Id: I9d78fba84fca2eef72c751b76d7748cbd40210b3 --- .../tests/components/grid/GridCellFocusOnResetSizeTest.java | 2 ++ .../vaadin/tests/components/grid/GridDetailsWidthTest.java | 2 ++ .../components/grid/GridInitiallyHiddenColumnsTest.java | 2 ++ .../tests/components/grid/GridMultiSelectionOnInitTest.java | 2 ++ .../vaadin/tests/components/grid/GridSortIndicatorTest.java | 2 ++ .../tests/components/grid/GridSpacerDecoClipTest.java | 13 ++++++++----- .../components/grid/GridSubPixelProblemWrappingTest.java | 2 ++ .../tests/components/grid/GridSwitchRenderersTest.java | 2 ++ .../vaadin/tests/components/grid/GridThemeChangeTest.java | 2 ++ .../vaadin/tests/components/grid/GridWidthIncreaseTest.java | 2 ++ .../tests/components/grid/JavaScriptRenderersTest.java | 2 ++ .../components/grid/ProgrammaticEditorControlTest.java | 2 ++ .../vaadin/tests/components/grid/SelectDuringInitTest.java | 2 ++ .../grid/basicfeatures/server/GridClearContainerTest.java | 2 ++ 14 files changed, 34 insertions(+), 5 deletions(-) diff --git a/uitest/src/com/vaadin/tests/components/grid/GridCellFocusOnResetSizeTest.java b/uitest/src/com/vaadin/tests/components/grid/GridCellFocusOnResetSizeTest.java index 31ccb4eaf2..cccb306369 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridCellFocusOnResetSizeTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridCellFocusOnResetSizeTest.java @@ -24,8 +24,10 @@ import org.openqa.selenium.By; import com.vaadin.testbench.elements.GridElement; import com.vaadin.testbench.elementsbase.ServerClass; +import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.MultiBrowserTest; +@TestCategory("grid") public class GridCellFocusOnResetSizeTest extends MultiBrowserTest { @ServerClass("com.vaadin.tests.widgetset.server.TestWidgetComponent") diff --git a/uitest/src/com/vaadin/tests/components/grid/GridDetailsWidthTest.java b/uitest/src/com/vaadin/tests/components/grid/GridDetailsWidthTest.java index 0a6f53820e..41838b427b 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridDetailsWidthTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridDetailsWidthTest.java @@ -23,8 +23,10 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.SingleBrowserTest; +@TestCategory("grid") public class GridDetailsWidthTest extends SingleBrowserTest { @Test diff --git a/uitest/src/com/vaadin/tests/components/grid/GridInitiallyHiddenColumnsTest.java b/uitest/src/com/vaadin/tests/components/grid/GridInitiallyHiddenColumnsTest.java index a3e941053a..f495d7f2aa 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridInitiallyHiddenColumnsTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridInitiallyHiddenColumnsTest.java @@ -23,8 +23,10 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.SingleBrowserTest; +@TestCategory("grid") public class GridInitiallyHiddenColumnsTest extends SingleBrowserTest { @Test diff --git a/uitest/src/com/vaadin/tests/components/grid/GridMultiSelectionOnInitTest.java b/uitest/src/com/vaadin/tests/components/grid/GridMultiSelectionOnInitTest.java index 5f5b4df8de..d5eedae824 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridMultiSelectionOnInitTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridMultiSelectionOnInitTest.java @@ -21,8 +21,10 @@ import org.junit.Test; import org.openqa.selenium.By; import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.MultiBrowserTest; +@TestCategory("grid") public class GridMultiSelectionOnInitTest extends MultiBrowserTest { @Test diff --git a/uitest/src/com/vaadin/tests/components/grid/GridSortIndicatorTest.java b/uitest/src/com/vaadin/tests/components/grid/GridSortIndicatorTest.java index 30ce7b76f9..2874ec32a6 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridSortIndicatorTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridSortIndicatorTest.java @@ -20,8 +20,10 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.MultiBrowserTest; +@TestCategory("grid") public class GridSortIndicatorTest extends MultiBrowserTest { @Test diff --git a/uitest/src/com/vaadin/tests/components/grid/GridSpacerDecoClipTest.java b/uitest/src/com/vaadin/tests/components/grid/GridSpacerDecoClipTest.java index c2cfe3cd7d..f08ab7a8a4 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridSpacerDecoClipTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridSpacerDecoClipTest.java @@ -15,20 +15,23 @@ */ package com.vaadin.tests.components.grid; -import com.vaadin.testbench.TestBenchElement; -import com.vaadin.testbench.elements.GridElement; -import com.vaadin.tests.minitutorials.v7_5.ShowingExtraDataForRows; -import com.vaadin.tests.tb3.MultiBrowserTest; import org.junit.Assert; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.SearchContext; +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; +import com.vaadin.tests.minitutorials.v7_5.ShowingExtraDataForRows; +import com.vaadin.tests.tb3.MultiBrowserTest; + /** * Test for "Grid detail row outline overflows" (#17826) * * @author Vaadin Ltd */ +@TestCategory("grid") public class GridSpacerDecoClipTest extends MultiBrowserTest { private static final String SPACER_CSS_CLASS_DECO = "v-grid-spacer-deco"; @@ -88,7 +91,7 @@ public class GridSpacerDecoClipTest extends MultiBrowserTest { } private TestBenchElement getSpacerDeco(int index) { - SearchContext context = this.getContext(); + SearchContext context = getContext(); return (TestBenchElement) context.findElements( By.className(SPACER_CSS_CLASS_DECO)).get(index); } diff --git a/uitest/src/com/vaadin/tests/components/grid/GridSubPixelProblemWrappingTest.java b/uitest/src/com/vaadin/tests/components/grid/GridSubPixelProblemWrappingTest.java index 7aacfa8548..319cf3b8b8 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridSubPixelProblemWrappingTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridSubPixelProblemWrappingTest.java @@ -25,8 +25,10 @@ import com.vaadin.testbench.elements.ButtonElement; import com.vaadin.testbench.elements.GridElement; import com.vaadin.testbench.elements.GridElement.GridRowElement; import com.vaadin.testbench.parallel.Browser; +import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.MultiBrowserTest; +@TestCategory("grid") public class GridSubPixelProblemWrappingTest extends MultiBrowserTest { @Override diff --git a/uitest/src/com/vaadin/tests/components/grid/GridSwitchRenderersTest.java b/uitest/src/com/vaadin/tests/components/grid/GridSwitchRenderersTest.java index e3bab865c0..5989ba607f 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridSwitchRenderersTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridSwitchRenderersTest.java @@ -20,8 +20,10 @@ import org.junit.Test; import com.vaadin.testbench.elements.CheckBoxElement; import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.MultiBrowserTest; +@TestCategory("grid") public class GridSwitchRenderersTest extends MultiBrowserTest { @Test diff --git a/uitest/src/com/vaadin/tests/components/grid/GridThemeChangeTest.java b/uitest/src/com/vaadin/tests/components/grid/GridThemeChangeTest.java index 5a21705b55..182bec04c9 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridThemeChangeTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridThemeChangeTest.java @@ -22,8 +22,10 @@ import org.junit.Test; import org.openqa.selenium.remote.DesiredCapabilities; import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.MultiBrowserTest; +@TestCategory("grid") public class GridThemeChangeTest extends MultiBrowserTest { @Override public List getBrowsersToTest() { diff --git a/uitest/src/com/vaadin/tests/components/grid/GridWidthIncreaseTest.java b/uitest/src/com/vaadin/tests/components/grid/GridWidthIncreaseTest.java index 96b1a56fba..6f39253fb0 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridWidthIncreaseTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridWidthIncreaseTest.java @@ -25,8 +25,10 @@ import org.openqa.selenium.remote.DesiredCapabilities; import com.vaadin.testbench.elements.ButtonElement; import com.vaadin.testbench.elements.GridElement; import com.vaadin.testbench.parallel.BrowserUtil; +import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.MultiBrowserTest; +@TestCategory("grid") public class GridWidthIncreaseTest extends MultiBrowserTest { private static int INCREASE_COUNT = 3; diff --git a/uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderersTest.java b/uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderersTest.java index 96fd672ab1..b38178b156 100644 --- a/uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderersTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderersTest.java @@ -20,8 +20,10 @@ import org.junit.Test; 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 JavaScriptRenderersTest extends MultiBrowserTest { @Test diff --git a/uitest/src/com/vaadin/tests/components/grid/ProgrammaticEditorControlTest.java b/uitest/src/com/vaadin/tests/components/grid/ProgrammaticEditorControlTest.java index 811bd03a64..efbc164e15 100644 --- a/uitest/src/com/vaadin/tests/components/grid/ProgrammaticEditorControlTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/ProgrammaticEditorControlTest.java @@ -21,8 +21,10 @@ import org.junit.Test; import com.vaadin.testbench.elements.ButtonElement; import com.vaadin.testbench.elements.GridElement; import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.SingleBrowserTest; +@TestCategory("grid") public class ProgrammaticEditorControlTest extends SingleBrowserTest { @Test diff --git a/uitest/src/com/vaadin/tests/components/grid/SelectDuringInitTest.java b/uitest/src/com/vaadin/tests/components/grid/SelectDuringInitTest.java index edfc8031a8..1ba223d71c 100644 --- a/uitest/src/com/vaadin/tests/components/grid/SelectDuringInitTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/SelectDuringInitTest.java @@ -19,8 +19,10 @@ import org.junit.Assert; import org.junit.Test; import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.SingleBrowserTest; +@TestCategory("grid") public class SelectDuringInitTest extends SingleBrowserTest { @Test diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridClearContainerTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridClearContainerTest.java index fd35dac6fc..91b5d04ab6 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridClearContainerTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridClearContainerTest.java @@ -20,6 +20,7 @@ import org.junit.Test; import com.vaadin.testbench.elements.ButtonElement; import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.MultiBrowserTest; /** @@ -28,6 +29,7 @@ import com.vaadin.tests.tb3.MultiBrowserTest; * * @author Vaadin Ltd */ +@TestCategory("grid") public class GridClearContainerTest extends MultiBrowserTest { private final String ERRORNOTE = "Unexpected cell contents."; -- 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(-) 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 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(-) 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 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 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(-) 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