From 00dc7ab85e8fd533854ccc50b788012cf1450342 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Mon, 28 Sep 2015 11:40:57 +0300 Subject: [PATCH] Add support for ContextClickEvent in AbstractComponent (#19015) Change-Id: I93d5d20925b8ac9b4b108ccc6deec2090e68aa0d --- .../client/ui/AbstractComponentConnector.java | 45 ++++++++++ .../AbsoluteLayoutConnector.java | 10 --- .../browserframe/BrowserFrameConnector.java | 5 -- .../ui/csslayout/CssLayoutConnector.java | 10 --- .../client/ui/embedded/EmbeddedConnector.java | 5 -- .../client/ui/flash/FlashConnector.java | 5 -- .../vaadin/client/ui/link/LinkConnector.java | 5 -- .../com/vaadin/event/ContextClickEvent.java | 82 +++++++++++++++++++ .../src/com/vaadin/ui/AbstractComponent.java | 32 +++++++- .../com/vaadin/shared/ContextClickRpc.java | 29 +++++++ shared/src/com/vaadin/shared/EventId.java | 2 +- .../abstractcomponent/ContextClickUI.java | 47 +++++++++++ .../abstractcomponent/ContextClickUITest.java | 67 +++++++++++++++ 13 files changed, 302 insertions(+), 42 deletions(-) create mode 100644 server/src/com/vaadin/event/ContextClickEvent.java create mode 100644 shared/src/com/vaadin/shared/ContextClickRpc.java create mode 100644 uitest/src/com/vaadin/tests/components/abstractcomponent/ContextClickUI.java create mode 100644 uitest/src/com/vaadin/tests/components/abstractcomponent/ContextClickUITest.java diff --git a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java index 1f09c14fb0..643adffe12 100644 --- a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java +++ b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java @@ -18,12 +18,16 @@ package com.vaadin.client.ui; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.dom.client.Element; +import com.google.gwt.event.dom.client.ContextMenuEvent; +import com.google.gwt.event.dom.client.ContextMenuHandler; +import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.ui.Focusable; import com.google.gwt.user.client.ui.HasEnabled; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ComponentConnector; import com.vaadin.client.HasComponentsConnector; import com.vaadin.client.LayoutManager; +import com.vaadin.client.MouseEventDetailsBuilder; import com.vaadin.client.Profiler; import com.vaadin.client.ServerConnector; import com.vaadin.client.StyleConstants; @@ -31,6 +35,7 @@ import com.vaadin.client.TooltipInfo; import com.vaadin.client.UIDL; import com.vaadin.client.Util; import com.vaadin.client.VConsole; +import com.vaadin.client.annotations.OnStateChange; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.metadata.NoDataException; import com.vaadin.client.metadata.Type; @@ -39,12 +44,16 @@ import com.vaadin.client.ui.ui.UIConnector; import com.vaadin.shared.AbstractComponentState; import com.vaadin.shared.ComponentConstants; import com.vaadin.shared.Connector; +import com.vaadin.shared.ContextClickRpc; +import com.vaadin.shared.EventId; import com.vaadin.shared.ui.ComponentStateUtil; import com.vaadin.shared.ui.TabIndexState; public abstract class AbstractComponentConnector extends AbstractConnector implements ComponentConnector { + private HandlerRegistration contextHandler = null; + private Widget widget; private String lastKnownWidth = ""; @@ -64,6 +73,42 @@ public abstract class AbstractComponentConnector extends AbstractConnector public AbstractComponentConnector() { } + @OnStateChange("registeredEventListeners") + void handleContextClickListenerChange() { + if (contextHandler == null && hasEventListener(EventId.CONTEXT_CLICK)) { + contextHandler = getWidget().addDomHandler( + new ContextMenuHandler() { + @Override + public void onContextMenu(ContextMenuEvent event) { + sendContextClickEvent(event); + } + }, ContextMenuEvent.getType()); + } else if (contextHandler != null + && !hasEventListener(EventId.CONTEXT_CLICK)) { + contextHandler.removeHandler(); + contextHandler = null; + } + } + + /** + * This method sends the context menu event to the server-side. Can be + * overridden to provide extra information through an alternative RPC + * interface. + * + * @since + * @param event + */ + protected void sendContextClickEvent(ContextMenuEvent event) { + event.preventDefault(); + event.stopPropagation(); + + // The default context click implementation only provides the mouse + // coordinates relative to root element of widget. + getRpcProxy(ContextClickRpc.class).contextClick( + MouseEventDetailsBuilder.buildMouseEventDetails( + event.getNativeEvent(), getWidget().getElement())); + } + /** * Creates and returns the widget for this VPaintableWidget. This method * should only be called once when initializing the paintable. diff --git a/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java b/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java index e1234d436a..19b66fb72f 100644 --- a/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java @@ -74,16 +74,6 @@ public class AbsoluteLayoutConnector extends } }; - /* - * (non-Javadoc) - * - * @see com.vaadin.client.ui.AbstractComponentConnector#init() - */ - @Override - protected void init() { - super.init(); - } - /** * Returns the deepest nested child component which contains "element". The * child component is also returned if "element" is part of its caption. diff --git a/client/src/com/vaadin/client/ui/browserframe/BrowserFrameConnector.java b/client/src/com/vaadin/client/ui/browserframe/BrowserFrameConnector.java index 8ff8a0b72d..95d64b61d0 100644 --- a/client/src/com/vaadin/client/ui/browserframe/BrowserFrameConnector.java +++ b/client/src/com/vaadin/client/ui/browserframe/BrowserFrameConnector.java @@ -25,11 +25,6 @@ import com.vaadin.shared.ui.browserframe.BrowserFrameState; @Connect(com.vaadin.ui.BrowserFrame.class) public class BrowserFrameConnector extends AbstractComponentConnector { - @Override - protected void init() { - super.init(); - } - @Override public VBrowserFrame getWidget() { return (VBrowserFrame) super.getWidget(); diff --git a/client/src/com/vaadin/client/ui/csslayout/CssLayoutConnector.java b/client/src/com/vaadin/client/ui/csslayout/CssLayoutConnector.java index bef506b492..1ec395280d 100644 --- a/client/src/com/vaadin/client/ui/csslayout/CssLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/csslayout/CssLayoutConnector.java @@ -61,16 +61,6 @@ public class CssLayoutConnector extends AbstractLayoutConnector { private final FastStringMap childIdToCaption = FastStringMap .create(); - /* - * (non-Javadoc) - * - * @see com.vaadin.client.ui.AbstractComponentConnector#init() - */ - @Override - protected void init() { - super.init(); - } - /* * (non-Javadoc) * diff --git a/client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java b/client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java index 38995cf800..7b837726a7 100644 --- a/client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java +++ b/client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java @@ -51,11 +51,6 @@ public class EmbeddedConnector extends AbstractComponentConnector implements private ObjectElement objectElement; private String resourceUrl; - @Override - protected void init() { - super.init(); - } - @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); diff --git a/client/src/com/vaadin/client/ui/flash/FlashConnector.java b/client/src/com/vaadin/client/ui/flash/FlashConnector.java index 7d01f6f560..e859e9cbf1 100644 --- a/client/src/com/vaadin/client/ui/flash/FlashConnector.java +++ b/client/src/com/vaadin/client/ui/flash/FlashConnector.java @@ -25,11 +25,6 @@ import com.vaadin.shared.ui.flash.FlashState; @Connect(com.vaadin.ui.Flash.class) public class FlashConnector extends AbstractComponentConnector { - @Override - protected void init() { - super.init(); - } - @Override public VFlash getWidget() { return (VFlash) super.getWidget(); diff --git a/client/src/com/vaadin/client/ui/link/LinkConnector.java b/client/src/com/vaadin/client/ui/link/LinkConnector.java index 5a12445655..1e77fb51b4 100644 --- a/client/src/com/vaadin/client/ui/link/LinkConnector.java +++ b/client/src/com/vaadin/client/ui/link/LinkConnector.java @@ -31,11 +31,6 @@ import com.vaadin.ui.Link; @Connect(Link.class) public class LinkConnector extends AbstractComponentConnector { - @Override - protected void init() { - super.init(); - } - @Override public LinkState getState() { return (LinkState) super.getState(); diff --git a/server/src/com/vaadin/event/ContextClickEvent.java b/server/src/com/vaadin/event/ContextClickEvent.java new file mode 100644 index 0000000000..88b46d938b --- /dev/null +++ b/server/src/com/vaadin/event/ContextClickEvent.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.event; + +import java.io.Serializable; +import java.lang.reflect.Method; + +import com.vaadin.event.MouseEvents.ClickEvent; +import com.vaadin.shared.MouseEventDetails; +import com.vaadin.ui.Component; +import com.vaadin.util.ReflectTools; + +/** + * Context click event fired by a {@link Component}. ContextClickEvent happens + * when context click happens on the client-side inside the Component. + * + * @since + * @author Vaadin Ltd + */ +public class ContextClickEvent extends ClickEvent { + + public static final Method CONTEXT_CLICK_METHOD = ReflectTools + .findMethod(ContextClickListener.class, "contextClick", + ContextClickEvent.class); + + public ContextClickEvent(Component source, + MouseEventDetails mouseEventDetails) { + super(source, mouseEventDetails); + } + + /** + * Listener for {@link ContextClickEvent ContextClickEvents}. + */ + public interface ContextClickListener extends Serializable { + + /** + * Called when the context click happens. + * + * @param event + * the context click event + */ + public void contextClick(ContextClickEvent event); + } + + /** + * The interface for adding and removing listeners for + * {@link ContextClickEvent ContextClickEvents}. + */ + public interface ContextClickNotifier extends Serializable { + /** + * Adds a context click listener that gets notified when a context click + * happens. + * + * @param listener + * the context click listener to add + */ + public void addContextClickListener(ContextClickListener listener); + + /** + * Removes a context click listener that was previously added with + * {@link #addContextClickListener(ContextClickListener)}. + * + * @param listener + * the context click listener to remove + */ + public void removeContextClickListener(ContextClickListener listener); + } + +} diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java index f499cb1fec..42b36b0f21 100644 --- a/server/src/com/vaadin/ui/AbstractComponent.java +++ b/server/src/com/vaadin/ui/AbstractComponent.java @@ -34,6 +34,9 @@ import org.jsoup.nodes.Element; import com.vaadin.event.ActionManager; import com.vaadin.event.ConnectorActionManager; +import com.vaadin.event.ContextClickEvent; +import com.vaadin.event.ContextClickEvent.ContextClickListener; +import com.vaadin.event.ContextClickEvent.ContextClickNotifier; import com.vaadin.event.ShortcutListener; import com.vaadin.server.AbstractClientConnector; import com.vaadin.server.AbstractErrorMessage.ContentMode; @@ -49,6 +52,9 @@ import com.vaadin.server.UserError; import com.vaadin.server.VaadinSession; import com.vaadin.shared.AbstractComponentState; import com.vaadin.shared.ComponentConstants; +import com.vaadin.shared.ContextClickRpc; +import com.vaadin.shared.EventId; +import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.ui.ComponentStateUtil; import com.vaadin.shared.util.SharedUtil; import com.vaadin.ui.Field.ValueChangeEvent; @@ -67,7 +73,7 @@ import com.vaadin.util.ReflectTools; */ @SuppressWarnings("serial") public abstract class AbstractComponent extends AbstractClientConnector - implements Component { + implements Component, ContextClickNotifier { /* Private members */ @@ -1391,6 +1397,30 @@ public abstract class AbstractComponent extends AbstractClientConnector return false; } + @Override + public void addContextClickListener(ContextClickListener listener) { + // Register default Context Click RPC if needed. This RPC won't be + // called if there are no listeners on the server-side. A client-side + // connector can override this and use a different RPC channel. + if (getRpcManager(ContextClickRpc.class.getName()) == null) { + registerRpc(new ContextClickRpc() { + @Override + public void contextClick(MouseEventDetails details) { + fireEvent(new ContextClickEvent(AbstractComponent.this, + details)); + } + }); + } + + addListener(EventId.CONTEXT_CLICK, ContextClickEvent.class, listener, + ContextClickEvent.CONTEXT_CLICK_METHOD); + } + + @Override + public void removeContextClickListener(ContextClickListener listener) { + removeListener(EventId.CONTEXT_CLICK, ContextClickEvent.class, listener); + } + private static final Logger getLogger() { return Logger.getLogger(AbstractComponent.class.getName()); } diff --git a/shared/src/com/vaadin/shared/ContextClickRpc.java b/shared/src/com/vaadin/shared/ContextClickRpc.java new file mode 100644 index 0000000000..2dd045f426 --- /dev/null +++ b/shared/src/com/vaadin/shared/ContextClickRpc.java @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared; + +import com.vaadin.shared.communication.ServerRpc; + +/** + * Client-to-server RPC interface for context click events + * + * @since + * @author Vaadin Ltd + */ +public interface ContextClickRpc extends ServerRpc { + + public void contextClick(MouseEventDetails details); +} diff --git a/shared/src/com/vaadin/shared/EventId.java b/shared/src/com/vaadin/shared/EventId.java index 1c2ada1d87..ad840d1e5d 100644 --- a/shared/src/com/vaadin/shared/EventId.java +++ b/shared/src/com/vaadin/shared/EventId.java @@ -24,5 +24,5 @@ public interface EventId extends Serializable { public static final String LAYOUT_CLICK_EVENT_IDENTIFIER = "lClick"; public static final String POLL = "poll"; public static final String CHANGE = "change"; - + public static final String CONTEXT_CLICK = "cClick"; } diff --git a/uitest/src/com/vaadin/tests/components/abstractcomponent/ContextClickUI.java b/uitest/src/com/vaadin/tests/components/abstractcomponent/ContextClickUI.java new file mode 100644 index 0000000000..f412be726a --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/abstractcomponent/ContextClickUI.java @@ -0,0 +1,47 @@ +/* + * 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.abstractcomponent; + +import com.vaadin.event.ContextClickEvent; +import com.vaadin.event.ContextClickEvent.ContextClickListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; + +public class ContextClickUI extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + final ContextClickListener listener = new ContextClickListener() { + + @Override + public void contextClick(ContextClickEvent event) { + log("Received context click at (" + event.getClientX() + ", " + + event.getClientY() + ")"); + } + }; + getUI().addContextClickListener(listener); + + addComponent(new Button("Remove listener", new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + getUI().removeContextClickListener(listener); + } + })); + } +} diff --git a/uitest/src/com/vaadin/tests/components/abstractcomponent/ContextClickUITest.java b/uitest/src/com/vaadin/tests/components/abstractcomponent/ContextClickUITest.java new file mode 100644 index 0000000000..4b975dbecc --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/abstractcomponent/ContextClickUITest.java @@ -0,0 +1,67 @@ +/* + * 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.abstractcomponent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; +import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.remote.DesiredCapabilities; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.UIElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class ContextClickUITest extends MultiBrowserTest { + + @Override + public List getBrowsersToTest() { + return getBrowsersSupportingContextMenu(); + } + + @Test + public void testContextClick() { + openTestURL(); + + new Actions(getDriver()) + .moveToElement($(UIElement.class).first(), 10, 10) + .contextClick().perform(); + + assertEquals("Context click not received correctly", + "1. Received context click at (10, 10)", getLogRow(0)); + } + + @Test + public void testRemoveListener() { + openTestURL(); + + $(ButtonElement.class).first().click(); + + new Actions(getDriver()) + .moveToElement($(UIElement.class).first(), 50, 50) + .contextClick().perform(); + + new Actions(getDriver()) + .moveToElement($(UIElement.class).first(), 10, 10).click() + .perform(); + + assertTrue("Context click should not be handled.", getLogRow(0).trim() + .isEmpty()); + } +} -- 2.39.5