@@ -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) { |
@@ -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. | |||
} | |||
} |
@@ -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 | |||
@@ -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; | |||
} | |||
} | |||
/** |
@@ -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; | |||
} | |||
@@ -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 -> Notification.show( | |||
* ((TreeContextClickEvent<Person>)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(); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |