diff options
Diffstat (limited to 'client/src')
16 files changed, 798 insertions, 242 deletions
diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 628559dd2a..d9e705426b 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -94,7 +94,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; @@ -3378,20 +3377,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; * * * </pre> - * */ public class EventHelper { @@ -69,7 +68,7 @@ public class EventHelper { */ public static <T extends ComponentConnector & FocusHandler> 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 <T extends ComponentConnector & FocusHandler> 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 <T extends ComponentConnector & BlurHandler> 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 <T extends ComponentConnector & BlurHandler> HandlerRegistration updateBlurHandler( T connector, HandlerRegistration handlerRegistration, Widget widget) { - return updateHandler(connector, BLUR, handlerRegistration, + return updateHandler(connector, connector, BLUR, handlerRegistration, BlurEvent.getType(), widget); } - private static <H extends EventHandler> HandlerRegistration updateHandler( - ComponentConnector connector, String eventIdentifier, + public static <H extends EventHandler> HandlerRegistration updateHandler( + ComponentConnector connector, H handler, String eventIdentifier, HandlerRegistration handlerRegistration, Type<H> 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 4c2e8ab4e1..62326c4b07 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,8 +39,8 @@ 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.ServerConnector; -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; @@ -47,6 +48,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; @@ -60,6 +62,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; @@ -113,8 +119,8 @@ import elemental.json.JsonValue; public class GridConnector extends AbstractHasComponentsConnector implements SimpleManagedLayout, DeferredWorker { - private static final class CustomCellStyleGenerator implements - CellStyleGenerator<JsonObject> { + private static final class CustomStyleGenerator implements + CellStyleGenerator<JsonObject>, RowStyleGenerator<JsonObject> { @Override public String getStyle(CellReference<JsonObject> cellReference) { JsonObject row = cellReference.getRow(); @@ -140,10 +146,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements } } - } - - private static final class CustomRowStyleGenerator implements - RowStyleGenerator<JsonObject> { @Override public String getStyle(RowReference<JsonObject> rowReference) { JsonObject row = rowReference.getRow(); @@ -153,7 +155,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements return null; } } - } /** @@ -580,6 +581,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 DetailsListener detailsListener = new DetailsListener() { @Override @@ -691,6 +693,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<JsonObject>() { @Override public void sort(SortEvent<JsonObject> event) { @@ -728,9 +734,38 @@ public class GridConnector extends AbstractHasComponentsConnector implements getWidget().addColumnReorderHandler(columnReorderHandler); getWidget().addColumnVisibilityChangeHandler( columnVisibilityChangeHandler); + + ConnectorFocusAndBlurHandler.addHandlers(this); + 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(); } @@ -1080,24 +1115,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; @@ -1252,4 +1269,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<JsonObject> cell = getWidget().getCellReference(element); + + if (cell != null) { + JsonObject row = cell.getRow(); + if (row == null) { + return null; + } + + Column<?, JsonObject> 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/ui/ConnectorFocusAndBlurHandler.java b/client/src/com/vaadin/client/ui/ConnectorFocusAndBlurHandler.java new file mode 100644 index 0000000000..8272f99d25 --- /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 7.6 + * @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/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/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()); @@ -127,20 +109,6 @@ public class ButtonConnector extends AbstractComponentConnector implements } @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) { // Simulate getting disabled from the server without waiting for the diff --git a/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java b/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java index 9e689f3314..8cfcf7feb1 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); @@ -127,20 +115,6 @@ public class CheckBoxConnector extends AbstractFieldConnector implements } @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()) { return; 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(); - } - } diff --git a/client/src/com/vaadin/client/ui/ui/UIConnector.java b/client/src/com/vaadin/client/ui/ui/UIConnector.java index cfb444ada6..9d8aa5b877 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/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<EditorEventHandler> { + public static final Type<EditorEventHandler> TYPE = new Type<EditorEventHandler>(); + + private CellReference<?> cell; + + protected EditorEvent(CellReference<?> cell) { + this.cell = cell; + } + + @Override + public Type<EditorEventHandler> getAssociatedType() { + return TYPE; + } + + /** + * Get a reference to the Grid that fired this Event. + * + * @return a Grid reference + */ + @SuppressWarnings("unchecked") + public <T> Grid<T> getGrid() { + return (Grid<T>) cell.getGrid(); + } + + /** + * Get a reference to the cell that was active when this Event was fired. + * NOTE: do <i>NOT</i> rely on this information remaining accurate after + * leaving the event handler. + * + * @return a cell reference + */ + @SuppressWarnings("unchecked") + public <T> CellReference<T> getCell() { + return (CellReference<T>) cell; + } + + /** + * Get a reference to the row that was active when this Event was fired. + * NOTE: do <i>NOT</i> rely on this information remaining accurate after + * leaving the event handler. + * + * @return a row data object + */ + @SuppressWarnings("unchecked") + public <T> T getRow() { + return (T) cell.getRow(); + } + + /** + * Get the index of the row that was active when this Event was fired. NOTE: + * do <i>NOT</i> 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 <i>NOT</i> rely on this information remaining accurate after + * leaving the event handler. + * + * @return a column object + */ + @SuppressWarnings("unchecked") + public <C, T> Column<C, T> getColumn() { + return (Column<C, T>) cell.getColumn(); + } + + /** + * Get the index of the column that was active when this Event was fired. + * NOTE: do <i>NOT</i> 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 377943ed61..a17c59c795 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -42,6 +42,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; @@ -79,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; @@ -121,6 +123,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; @@ -206,8 +213,8 @@ import com.vaadin.shared.util.SharedUtil; * @author Vaadin Ltd */ public class Grid<T> extends ResizeComposite implements - HasSelectionHandlers<T>, SubPartAware, DeferredWorker, HasWidgets, - HasEnabled { + HasSelectionHandlers<T>, SubPartAware, DeferredWorker, Focusable, + com.google.gwt.user.client.ui.Focusable, HasWidgets, HasEnabled { /** * Enum describing different sections of Grid. @@ -1155,8 +1162,17 @@ public class Grid<T> 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; @@ -1211,6 +1227,7 @@ public class Grid<T> extends ResizeComposite implements + " remember to call success() or fail()?"); } }; + private final EditorRequestImpl.RequestCallback<T> bindRequestCallback = new EditorRequestImpl.RequestCallback<T>() { @Override public void onSuccess(EditorRequest<T> request) { @@ -1218,10 +1235,7 @@ public class Grid<T> 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(); } } @@ -1229,22 +1243,30 @@ public class Grid<T> extends ResizeComposite implements @Override public void onError(EditorRequest<T> 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); } } }; /** A set of all the columns that display an error flag. */ private final Set<Column<?, T>> columnErrors = new HashSet<Grid.Column<?, T>>(); + private boolean buffered = true; + + /** Original position of editor */ + private double originalTop; + /** Original scroll position of grid when editor was opened */ + private double originalScrollTop; + private RowHandle<T> pinnedRowHandle; public Editor() { saveButton = new Button(); @@ -1275,6 +1297,10 @@ public class Grid<T> 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() { @@ -1282,12 +1308,26 @@ public class Grid<T> 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(); + Element focusedElement = WidgetUtil.getFocusedElement(); + if (focusedCell != null && focusedElement != null + && grid.getElement().isOrHasChild(focusedElement)) { + editRow(rowIndex, focusedCell.getColumn()); + } else { + editRow(rowIndex, -1); + } } /** @@ -1304,28 +1344,40 @@ public class Grid<T> 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 */ - 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"); } 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; 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); } } @@ -1348,14 +1400,18 @@ public class Grid<T> 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<T>(grid, rowIndex, null)); + doCancel(); + } - EditorRequest<T> request = new EditorRequestImpl<T>(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)); } private void updateSelectionCheckboxesAsNeeded(boolean isEnabled) { @@ -1448,14 +1504,15 @@ public class Grid<T> 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); EditorRequest<T> request = new EditorRequestImpl<T>(grid, rowIndex, bindRequestCallback); handler.bind(request); - grid.getEscalator().setScrollLocked(Direction.VERTICAL, true); + grid.getEscalator().setScrollLocked(Direction.VERTICAL, + isBuffered()); updateSelectionCheckboxesAsNeeded(false); } } @@ -1465,15 +1522,6 @@ public class Grid<T> 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() { @@ -1518,6 +1566,8 @@ public class Grid<T> extends ResizeComposite implements * @since 7.5 */ protected void showOverlay() { + // Ensure overlay is hidden initially + hideOverlay(); DivElement gridElement = DivElement.as(grid.getElement()); @@ -1528,6 +1578,9 @@ public class Grid<T> extends ResizeComposite implements @Override public void onScroll(ScrollEvent event) { updateHorizontalScrollPosition(); + if (!isBuffered()) { + updateVerticalScrollPosition(); + } } }); @@ -1575,6 +1628,50 @@ public class Grid<T> 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()); + } } } @@ -1588,8 +1685,12 @@ public class Grid<T> extends ResizeComposite implements messageAndButtonsWrapper.appendChild(buttonsWrapper); } - attachWidget(saveButton, buttonsWrapper); - attachWidget(cancelButton, buttonsWrapper); + if (isBuffered()) { + attachWidget(saveButton, buttonsWrapper); + attachWidget(cancelButton, buttonsWrapper); + } + + setMessageAndButtonsWrapperVisible(isBuffered()); updateHorizontalScrollPosition(); @@ -1601,9 +1702,11 @@ public class Grid<T> 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 @@ -1636,6 +1739,15 @@ public class Grid<T> extends ResizeComposite implements } protected void hideOverlay() { + if (editorOverlay.getParentElement() == null) { + return; + } + + if (pinnedRowHandle != null) { + pinnedRowHandle.unpin(); + pinnedRowHandle = null; + } + for (Widget w : columnToWidget.values()) { setParent(w, null); } @@ -1727,6 +1839,35 @@ public class Grid<T> extends ResizeComposite implements frozenCellWrapper.getOffsetWidth() - 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. @@ -1803,6 +1944,23 @@ public class Grid<T> extends ResizeComposite implements public boolean isEditorColumnError(Column<?, T> column) { return columnErrors.contains(column); } + + 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<HANDLER extends AbstractGridKeyEventHandler> @@ -5236,7 +5394,7 @@ public class Grid<T> 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() { @@ -5912,6 +6070,19 @@ public class Grid<T> 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; } @@ -6375,6 +6546,14 @@ public class Grid<T> 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))) { @@ -6385,7 +6564,6 @@ public class Grid<T> 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) @@ -6493,50 +6671,68 @@ public class Grid<T> extends ResizeComposite implements } private boolean handleEditorEvent(Event event, RowContainer container) { - - final boolean closeEvent = event.getTypeInt() == Event.ONKEYDOWN - && event.getKeyCode() == Editor.KEYCODE_HIDE; + final int type = event.getTypeInt(); + final int key = event.getKeyCode(); + final boolean editorIsActive = editor.getState() != Editor.State.INACTIVE; double now = Duration.currentTimeMillis(); 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 = event.getTypeInt() == Event.ONDBLCLICK - || (event.getTypeInt() == Event.ONKEYDOWN && event.getKeyCode() == Editor.KEYCODE_SHOW) - || validTouchOpenEvent; + final boolean openEvent = eventCell.isBody() + && (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(); } - if (editor.getState() != Editor.State.INACTIVE) { - if (closeEvent) { - editor.cancel(); - FocusUtil.setFocus(this, true); - } - return true; - } + // TODO: Move on touch events + final boolean moveEvent = eventCell.isBody() && type == Event.ONCLICK; + + final boolean closeEvent = type == Event.ONKEYDOWN + && key == Editor.KEYCODE_HIDE; + + if (!editorIsActive && editor.isEnabled() && openEvent) { - if (container == escalator.getBody() && editor.isEnabled() && openEvent) { editor.editRow(eventCell.getRowIndex(), eventCell.getColumnIndexDOM()); + fireEvent(new EditorOpenEvent(eventCell)); event.preventDefault(); + + return true; + + } else if (editorIsActive && !editor.isBuffered() && moveEvent) { + + 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); + return true; } - return false; + // Swallow events if editor is open and buffered (modal) + return editor.isBuffered() && editorIsActive; } private boolean handleRendererEvent(Event event, RowContainer container) { @@ -7698,6 +7894,12 @@ public class Grid<T> extends ResizeComposite implements if (escalator.getInnerWidth() != autoColumnWidthsRecalculator.lastCalculatedInnerWidth) { recalculateColumnWidths(); } + + // Vertical resizing could make editor positioning invalid so it + // needs to be recalculated on resize + if (isEditorActive()) { + editor.updateVerticalScrollPosition(); + } } }); } @@ -7990,6 +8192,54 @@ public class Grid<T> extends ResizeComposite implements } } + @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); + } + + /** + * Sets the buffered editor mode. + * + * @since 7.6 + * @param editorUnbuffered + * <code>true</code> to enable buffered editor, + * <code>false</code> to disable it + */ + public void setEditorBuffered(boolean editorBuffered) { + editor.setBuffered(editorBuffered); + } + + /** + * Gets the buffered editor mode. + * + * @since 7.6 + * @return <code>true</code> if buffered editor is enabled, + * <code>false</code> otherwise + */ + public boolean isEditorBuffered() { + return editor.isBuffered(); + } + /** * Returns the {@link EventCellReference} for the latest event fired from * this Grid. @@ -8002,4 +8252,26 @@ public class Grid<T> extends ResizeComposite implements public EventCellReference<T> 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 <code>null</code> if cell was not found. + */ + public CellReference<T> getCellReference(Element element) { + RowContainer container = getEscalator().findRowContainer(element); + if (container != null) { + Cell cell = container.getCell(element); + if (cell != null) { + EventCellReference<T> cellRef = new EventCellReference<T>(this); + cellRef.set(cell, getSectionFromContainer(container)); + return cellRef; + } + } + return null; + } } |