]> source.dussan.org Git - vaadin-framework.git/commitdiff
Implement tree item context click
authorIlia Motornyi <elmot@vaadin.com>
Mon, 10 Jul 2017 12:04:22 +0000 (15:04 +0300)
committerGitHub <noreply@github.com>
Mon, 10 Jul 2017 12:04:22 +0000 (15:04 +0300)
Fixes #9606
Fixes #9645
Fixes #9647

client/src/main/java/com/vaadin/client/ui/AbstractComponentConnector.java
client/src/main/java/com/vaadin/client/ui/composite/CompositeConnector.java
documentation/components/components-tree.asciidoc
server/src/main/java/com/vaadin/event/MouseEvents.java
server/src/main/java/com/vaadin/server/communication/ServerRpcHandler.java
server/src/main/java/com/vaadin/ui/Tree.java
uitest/src/main/java/com/vaadin/tests/contextclick/TreeV8ContextClick.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/contextclick/TreeV8ContextClickTest.java [new file with mode: 0644]

index 8ba94f9ba70aa1eb7873a3712a2cb8a356f89a53..1eb99083354589429cf4d91ab3fa612e4795c4d2 100644 (file)
@@ -330,7 +330,8 @@ public abstract class AbstractComponentConnector extends AbstractConnector
      * interface.
      *
      * @since 7.6
-     * @param event
+     * @param details
+     * @param eventTarget
      */
     protected void sendContextClickEvent(MouseEventDetails details,
             EventTarget eventTarget) {
index 93e42fab1f34c3653dbb4c4fb6cafc5a4b82a090..916363cf231b5fad7e1d47bbc4d60cb67d049f0d 100644 (file)
@@ -15,6 +15,7 @@
  */
 package com.vaadin.client.ui.composite;
 
+import com.google.gwt.dom.client.EventTarget;
 import com.google.gwt.user.client.ui.Label;
 import com.google.gwt.user.client.ui.Widget;
 import com.vaadin.client.ComponentConnector;
@@ -22,6 +23,7 @@ import com.vaadin.client.ConnectorHierarchyChangeEvent;
 import com.vaadin.client.HasComponentsConnector;
 import com.vaadin.client.ui.AbstractHasComponentsConnector;
 import com.vaadin.shared.AbstractComponentState;
+import com.vaadin.shared.MouseEventDetails;
 import com.vaadin.shared.ui.Connect;
 import com.vaadin.shared.ui.Connect.LoadStyle;
 import com.vaadin.ui.Composite;
@@ -93,4 +95,10 @@ public class CompositeConnector extends AbstractHasComponentsConnector {
             ConnectorHierarchyChangeEvent event) {
         // Handled in getChildConnector
     }
+
+    @Override
+    protected void sendContextClickEvent(MouseEventDetails details, EventTarget eventTarget) {
+        //Do nothing, because Composite is not an actual component, and the event
+        //must be handled in inner components.
+    }
 }
index 03ea963170482863f41c33e98f8b27bf17c17cf8..cc44bec6ea0ef35a5371cb439d0a4a82640f1f8e 100644 (file)
@@ -98,6 +98,17 @@ tree.addItemClickListener(event ->
 );
 ----
 
+[[components.tree.right.clicks]]
+=== Right-clicks
+Right-clicks are supported similar way via `addContextClickListener()` method
+
+[source, java]
+----
+  tree.addContextClickListener(event -> Notification.show(
+        ((TreeContextClickEvent<Person>)event).getItem() + " Clicked")
+  );
+----
+
 [[components.tree.expandcollapse]]
 == Expanding and Collapsing Nodes
 
index 76f14d08406a5622d0b7cd398442be7954ca2221..34456f2498a213122a28ac9d4a274a96d287afb7 100644 (file)
@@ -182,6 +182,17 @@ public interface MouseEvents {
         public String getButtonName() {
             return details.getButtonName();
         }
+
+        /**
+         * Returns an information about mouse event like position, buttons
+         * pressed etc.
+         *
+         * @since 8.1
+         * @return An information about mouse event
+         */
+        public MouseEventDetails getMouseEventDetails() {
+            return details;
+        }
     }
 
     /**
index e4783cbf6ea2023a06ff8e9cf943647d1c6d28a0..f2caf5631c419c64b9182095cab9c545c1bbdc6d 100644 (file)
@@ -591,10 +591,12 @@ public class ServerRpcHandler implements Serializable {
              * corresponding to the received method invocation has been
              * registered.
              */
-            getLogger().warning("Ignoring RPC call to " + interfaceName + "."
+            String message = "Ignoring RPC call to " + interfaceName + "."
                     + methodName + " in connector "
                     + connector.getClass().getName() + "(" + connectorId
-                    + ") as no RPC implementation is registered");
+                    + ") as no RPC implementation is registered";
+            assert rpcManager != null : message;
+            getLogger().warning(message);
             return null;
         }
 
index 6ae86efe3711ebd3883933526301f7c920112942..be0041737433d1cff637b886e7d52c763dad6b2f 100644 (file)
@@ -34,12 +34,14 @@ import com.vaadin.data.provider.TreeDataProvider;
 import com.vaadin.event.CollapseEvent;
 import com.vaadin.event.CollapseEvent.CollapseListener;
 import com.vaadin.event.ConnectorEvent;
+import com.vaadin.event.ContextClickEvent;
 import com.vaadin.event.ExpandEvent;
 import com.vaadin.event.ExpandEvent.ExpandListener;
 import com.vaadin.event.SerializableEventListener;
 import com.vaadin.event.selection.SelectionListener;
 import com.vaadin.server.ErrorMessage;
 import com.vaadin.server.Resource;
+import com.vaadin.shared.EventId;
 import com.vaadin.shared.MouseEventDetails;
 import com.vaadin.shared.Registration;
 import com.vaadin.shared.ui.ContentMode;
@@ -69,6 +71,7 @@ public class Tree<T> extends Composite
     @Deprecated
     private static final Method ITEM_CLICK_METHOD = ReflectTools
             .findMethod(ItemClickListener.class, "itemClick", ItemClick.class);
+    private Registration contextClickRegistration = null;
 
     /**
      * A listener for item click events.
@@ -483,7 +486,7 @@ public class Tree<T> extends Composite
      * Adds a selection listener to the current selection model.
      * <p>
      * <strong>NOTE:</strong> If selection mode is switched with
-     * {@link setSelectionMode(SelectionMode)}, then this listener is not
+     * {@link #setSelectionMode(SelectionMode)}, then this listener is not
      * triggered anymore when selection changes!
      *
      * @param listener
@@ -492,7 +495,7 @@ public class Tree<T> extends Composite
      *
      * @throws UnsupportedOperationException
      *             if selection has been disabled with
-     *             {@link SelectionMode.NONE}
+     *             {@link SelectionMode#NONE}
      */
     public Registration addSelectionListener(SelectionListener<T> listener) {
         return treeGrid.addSelectionListener(listener);
@@ -630,6 +633,7 @@ public class Tree<T> extends Composite
      * @param listener
      *            the item click listener, not null
      * @return a registration for the listener
+     * @see #addContextClickListener
      */
     public Registration addItemClickListener(ItemClickListener<T> listener) {
         return addListener(ItemClick.class, listener, ITEM_CLICK_METHOD);
@@ -655,12 +659,12 @@ public class Tree<T> extends Composite
         Objects.requireNonNull(selectionMode,
                 "Can not set selection mode to null");
         switch (selectionMode) {
-        case MULTI:
-            TreeMultiSelectionModel<T> model = new TreeMultiSelectionModel<>();
-            treeGrid.setSelectionModel(model);
-            return model;
-        default:
-            return treeGrid.setSelectionMode(selectionMode);
+            case MULTI:
+                TreeMultiSelectionModel<T> model = new TreeMultiSelectionModel<>();
+                treeGrid.setSelectionModel(model);
+                return model;
+            default:
+                return treeGrid.setSelectionMode(selectionMode);
         }
     }
 
@@ -811,4 +815,102 @@ public class Tree<T> extends Composite
     public void setContentMode(ContentMode contentMode) {
         renderer.getState().mode = contentMode;
     }
+
+    /**
+     * Adds a context click listener that gets notified when a context click
+     * happens.
+     *
+     * @param listener
+     *            the context click listener to add, not null
+     *            actual event provided to the listener is {@link TreeContextClickEvent}
+     * @return a registration object for removing the listener
+     *
+     * @since 8.1
+     * @see #addItemClickListener
+     * @see Registration
+     */
+    @Override
+    public Registration addContextClickListener(ContextClickEvent.ContextClickListener listener) {
+        Registration registration =
+                addListener(EventId.CONTEXT_CLICK, ContextClickEvent.class,
+                        listener, ContextClickEvent.CONTEXT_CLICK_METHOD);
+        setupContextClickListener();
+        return () -> {
+            registration.remove();
+            setupContextClickListener();
+        };
+    }
+
+    @Override
+    @Deprecated
+    public void removeContextClickListener(ContextClickEvent.ContextClickListener listener) {
+        super.removeContextClickListener(listener);
+        setupContextClickListener();
+    }
+
+    private void setupContextClickListener() {
+        if (hasListeners(ContextClickEvent.class)) {
+            if (contextClickRegistration == null) {
+                contextClickRegistration = treeGrid.addContextClickListener(
+                        event -> {
+                            T item = null;
+                            if (event instanceof Grid.GridContextClickEvent) {
+                                item = ((Grid.GridContextClickEvent<T>) event).getItem();
+                            }
+                            fireEvent(new TreeContextClickEvent<>(this, event.getMouseEventDetails(), item));
+                        }
+                );
+            }
+        } else if (contextClickRegistration != null) {
+            contextClickRegistration.remove();
+            contextClickRegistration = null;
+        }
+    }
+
+    /**
+     * ContextClickEvent for the Tree Component.
+     * <p>
+     * Usage:
+     * <pre>
+     * tree.addContextClickListener(event -&gt; Notification.show(
+     *       ((TreeContextClickEvent&lt;Person&gt;)event).getItem() + " Clicked")
+     * );
+     * </pre>
+     *
+     * @param <T> the tree bean type
+     */
+    public static class TreeContextClickEvent<T> extends ContextClickEvent {
+
+        private final T item;
+
+        /**
+         * Creates a new context click event.
+         *
+         * @param source            the tree where the context click occurred
+         * @param mouseEventDetails details about mouse position
+         * @param item              the item which was clicked or {@code null}
+         *                          if the click happened outside any item
+         */
+        public TreeContextClickEvent(Tree<T> source,
+                                     MouseEventDetails mouseEventDetails,
+                                     T item) {
+            super(source, mouseEventDetails);
+            this.item = item;
+        }
+
+        /**
+         * Returns the item of context clicked row.
+         *
+         * @return clicked item; {@code null}
+         *          the click happened outside any item
+         */
+        public T getItem() {
+            return item;
+        }
+
+        @Override
+        public Tree<T> getComponent() {
+            return (Tree<T>) super.getComponent();
+        }
+    }
 }
diff --git a/uitest/src/main/java/com/vaadin/tests/contextclick/TreeV8ContextClick.java b/uitest/src/main/java/com/vaadin/tests/contextclick/TreeV8ContextClick.java
new file mode 100644 (file)
index 0000000..ed30eb4
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2000-2016 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.contextclick;
+
+import com.vaadin.data.TreeData;
+import com.vaadin.shared.Registration;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Tree;
+import com.vaadin.ui.Tree.TreeContextClickEvent;
+
+import java.util.Collections;
+
+public class TreeV8ContextClick extends
+        AbstractContextClickUI<Tree<String>, TreeContextClickEvent<String>> {
+
+    @Override
+    protected Tree<String> createTestComponent() {
+        TreeData<String> treeData = new TreeData<>();
+        for (int i = 0; i < 3; i++) {
+            String grandDad = "Granddad " + i;
+            treeData.addItems(null, grandDad);
+            for (int j = 0; j < 4; j++) {
+                String dad = "Dad " + i + "/" + j;
+                treeData.addItems(grandDad, dad);
+                for (int k = 0; k < 5; k++) {
+                    treeData.addItems(dad, "Son " + i + "/" + j + "/" + k);
+                }
+            }
+        }
+        Tree<String> tree = new Tree<>("Clane", treeData);
+        tree.setWidth("100%");
+        return tree;
+    }
+
+    @Override
+    protected void handleContextClickEvent(
+            TreeContextClickEvent<String> event) {
+        String value = event.getItem();
+        log("ContextClickEvent value: " + value);
+    }
+
+    @Override
+    protected HorizontalLayout createContextClickControls() {
+        HorizontalLayout controls = super.createContextClickControls();
+        controls.addComponent(
+                new Button("Remove all content", new Button.ClickListener() {
+
+                    @Override
+                    public void buttonClick(ClickEvent event) {
+                        testComponent.setItems(Collections.emptyList());
+                        testComponent.setHeight("200px");
+                    }
+                }));
+        return controls;
+    }
+
+}
diff --git a/uitest/src/test/java/com/vaadin/tests/contextclick/TreeV8ContextClickTest.java b/uitest/src/test/java/com/vaadin/tests/contextclick/TreeV8ContextClickTest.java
new file mode 100644 (file)
index 0000000..3272cce
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2000-2016 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.contextclick;
+
+import com.vaadin.testbench.elements.ButtonElement;
+import com.vaadin.testbench.elements.TreeElement;
+import org.junit.Test;
+import org.openqa.selenium.WebElement;
+
+import static org.junit.Assert.assertEquals;
+
+public class TreeV8ContextClickTest extends AbstractContextClickTest {
+
+    @Test
+    public void testBodyContextClickWithTypedListener() {
+        addOrRemoveTypedListener();
+
+        TreeElement tree = $(TreeElement.class).first();
+        contextClick(tree.getItem(0));
+
+        assertEquals(
+                "1. ContextClickEvent value: Granddad 0",
+                getLogRow(0));
+
+        tree.expand(0);
+        tree.expand(2);
+        contextClick(tree.getItem(6));
+
+        assertEquals(
+                "2. ContextClickEvent value: Son 0/1/3",
+                getLogRow(0));
+    }
+
+    /**
+     * Performs a context click on given element at coordinates 20, 10 followed
+     * by a regular click. This prevents browser context menu from blocking
+     * future operations.
+     *
+     * A smaller X offset might hit the resize handle of the previous cell that
+     * overlaps with the next header cell.
+     *
+     * @param e
+     *            web element
+     */
+    @Override
+    protected void contextClick(WebElement e) {
+        contextClick(e, 20, 10);
+    }
+
+}