summaryrefslogtreecommitdiffstats
path: root/client/src
diff options
context:
space:
mode:
authorArtur <artur@vaadin.com>2018-03-28 15:21:16 +0300
committerIlia Motornyi <elmot@vaadin.com>2018-03-28 15:21:16 +0300
commit761bef8fb790258ac7a527f51a23da0bd1802dbe (patch)
treef7c6174a32affe0dbd9803143f09c5b9ddf959c3 /client/src
parent96efeccce765a2f12db71c9af2ac07fc4283c201 (diff)
downloadvaadin-framework-761bef8fb790258ac7a527f51a23da0bd1802dbe.tar.gz
vaadin-framework-761bef8fb790258ac7a527f51a23da0bd1802dbe.zip
Support starting downloads and opening URLs from a menu item (#10478)
Diffstat (limited to 'client/src')
-rw-r--r--client/src/main/java/com/vaadin/client/extensions/AbstractEventTriggerExtensionConnector.java73
-rw-r--r--client/src/main/java/com/vaadin/client/extensions/BrowserWindowOpenerConnector.java18
-rw-r--r--client/src/main/java/com/vaadin/client/extensions/EventTrigger.java55
-rw-r--r--client/src/main/java/com/vaadin/client/extensions/FileDownloaderConnector.java22
-rw-r--r--client/src/main/java/com/vaadin/client/ui/VMenuBar.java75
-rw-r--r--client/src/main/java/com/vaadin/client/ui/menubar/MenuBarConnector.java1
6 files changed, 207 insertions, 37 deletions
diff --git a/client/src/main/java/com/vaadin/client/extensions/AbstractEventTriggerExtensionConnector.java b/client/src/main/java/com/vaadin/client/extensions/AbstractEventTriggerExtensionConnector.java
new file mode 100644
index 0000000000..29a7da9208
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/extensions/AbstractEventTriggerExtensionConnector.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2000-2018 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.extensions;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.web.bindery.event.shared.HandlerRegistration;
+import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.ServerConnector;
+import com.vaadin.shared.extension.PartInformationState;
+
+/**
+ * An abstract extension connector with trigger support. Implementor's
+ * {@link #trigger} method call may be initiated by another {@code Component}
+ * without server round-trip. The class is used to overcome browser security
+ * limitations. For instance, window may not be open with the round-trip.
+ *
+ * @author Vaadin Ltd.
+ * @since
+ */
+public abstract class AbstractEventTriggerExtensionConnector
+ extends AbstractExtensionConnector {
+
+ private HandlerRegistration eventHandlerRegistration;
+
+ /**
+ * Called whenever a click occurs on the widget (if widget does not
+ * implement {@link EventTrigger}) or when the {@link EventTrigger} fires.
+ *
+ */
+ protected abstract void trigger();
+
+ @Override
+ public PartInformationState getState() {
+ return (PartInformationState) super.getState();
+ }
+
+ @Override
+ protected void extend(ServerConnector target) {
+ Widget targetWidget = ((ComponentConnector) target).getWidget();
+ if (targetWidget instanceof EventTrigger) {
+ String partInformation = getState().partInformation;
+ eventHandlerRegistration = ((EventTrigger) targetWidget)
+ .addTrigger(this::trigger, partInformation);
+ } else {
+ eventHandlerRegistration = targetWidget
+ .addDomHandler(e -> trigger(), ClickEvent.getType());
+ }
+ }
+
+ @Override
+ public void onUnregister() {
+ super.onUnregister();
+
+ if (eventHandlerRegistration != null) {
+ eventHandlerRegistration.removeHandler();
+ eventHandlerRegistration = null;
+ }
+ }
+}
diff --git a/client/src/main/java/com/vaadin/client/extensions/BrowserWindowOpenerConnector.java b/client/src/main/java/com/vaadin/client/extensions/BrowserWindowOpenerConnector.java
index 77844d9084..009af93f1a 100644
--- a/client/src/main/java/com/vaadin/client/extensions/BrowserWindowOpenerConnector.java
+++ b/client/src/main/java/com/vaadin/client/extensions/BrowserWindowOpenerConnector.java
@@ -18,13 +18,8 @@ package com.vaadin.client.extensions;
import java.util.Map.Entry;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.client.ComponentConnector;
-import com.vaadin.client.ServerConnector;
import com.vaadin.server.BrowserWindowOpener;
import com.vaadin.shared.ui.BrowserWindowOpenerState;
import com.vaadin.shared.ui.Connect;
@@ -37,15 +32,8 @@ import com.vaadin.shared.util.SharedUtil;
* @since 7.0.0
*/
@Connect(BrowserWindowOpener.class)
-public class BrowserWindowOpenerConnector extends AbstractExtensionConnector
- implements ClickHandler {
-
- @Override
- protected void extend(ServerConnector target) {
- final Widget targetWidget = ((ComponentConnector) target).getWidget();
-
- targetWidget.addDomHandler(this, ClickEvent.getType());
- }
+public class BrowserWindowOpenerConnector
+ extends AbstractEventTriggerExtensionConnector {
@Override
public BrowserWindowOpenerState getState() {
@@ -53,7 +41,7 @@ public class BrowserWindowOpenerConnector extends AbstractExtensionConnector
}
@Override
- public void onClick(ClickEvent event) {
+ protected void trigger() {
String url = getResourceUrl(BrowserWindowOpenerState.locationResource);
url = addParametersAndFragment(url);
if (url != null) {
diff --git a/client/src/main/java/com/vaadin/client/extensions/EventTrigger.java b/client/src/main/java/com/vaadin/client/extensions/EventTrigger.java
new file mode 100644
index 0000000000..eecba64515
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/extensions/EventTrigger.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2000-2018 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.extensions;
+
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.web.bindery.event.shared.HandlerRegistration;
+
+/**
+ * Provides support for triggering an event from a given parts of a component or
+ * using various events.
+ * <p>
+ * Used by features such as {@link FileDownloaderConnector} and
+ * {@link BrowserWindowOpenerConnector} to listen to a given event on a given
+ * element. The component is the one responsible for deciding the element and
+ * the event to listen to.
+ * <p>
+ * This is the client side interface.
+ * <p>
+ * If the component on the server side implements
+ * {@code com.vaadin.server.EventTrigger} then this interface should be
+ * implemented by the {@link Widget} used by the client side connector.
+ *
+ * @since
+ */
+public interface EventTrigger {
+
+ /**
+ * Adds an appropriate event handler on the correct element inside the
+ * widget and invokes the given file downloader when the event occurs.
+ *
+ * @param command
+ * The command to execute when the event occurs
+ * @param partInformation
+ * Information passed from the server, typically telling which
+ * element to attach the DOM handler to
+ * @return a registration handler which can be used to remove the handler
+ */
+ public HandlerRegistration addTrigger(Command command,
+ String partInformation);
+
+}
diff --git a/client/src/main/java/com/vaadin/client/extensions/FileDownloaderConnector.java b/client/src/main/java/com/vaadin/client/extensions/FileDownloaderConnector.java
index d4023c299c..3edd79bde4 100644
--- a/client/src/main/java/com/vaadin/client/extensions/FileDownloaderConnector.java
+++ b/client/src/main/java/com/vaadin/client/extensions/FileDownloaderConnector.java
@@ -21,33 +21,27 @@ import com.google.gwt.dom.client.IFrameElement;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.Style.Visibility;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.RootPanel;
-import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.BrowserInfo;
-import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ServerConnector;
import com.vaadin.server.FileDownloader;
import com.vaadin.shared.extension.filedownloader.FileDownloaderState;
import com.vaadin.shared.ui.Connect;
@Connect(FileDownloader.class)
-public class FileDownloaderConnector extends AbstractExtensionConnector
- implements ClickHandler {
+public class FileDownloaderConnector
+ extends AbstractEventTriggerExtensionConnector {
private IFrameElement iframe;
+ /**
+ * Called when the download should start.
+ *
+ * @since
+ */
@Override
- protected void extend(ServerConnector target) {
- final Widget downloadWidget = ((ComponentConnector) target).getWidget();
-
- downloadWidget.addDomHandler(this, ClickEvent.getType());
- }
-
- @Override
- public void onClick(ClickEvent event) {
+ protected void trigger() {
final String url = getResourceUrl("dl");
if (url != null && !url.isEmpty()) {
BrowserInfo browser = BrowserInfo.get();
diff --git a/client/src/main/java/com/vaadin/client/ui/VMenuBar.java b/client/src/main/java/com/vaadin/client/ui/VMenuBar.java
index d95954b26d..9dedb78205 100644
--- a/client/src/main/java/com/vaadin/client/ui/VMenuBar.java
+++ b/client/src/main/java/com/vaadin/client/ui/VMenuBar.java
@@ -16,8 +16,10 @@
package com.vaadin.client.ui;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Queue;
import com.google.gwt.core.client.GWT;
@@ -46,6 +48,7 @@ import com.google.gwt.user.client.ui.HasHTML;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
+import com.google.web.bindery.event.shared.HandlerRegistration;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.LayoutManager;
@@ -53,12 +56,13 @@ import com.vaadin.client.TooltipInfo;
import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
import com.vaadin.client.WidgetUtil;
+import com.vaadin.client.extensions.EventTrigger;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.shared.ui.menubar.MenuBarConstants;
public class VMenuBar extends FocusableFlowPanel
-implements CloseHandler<PopupPanel>, KeyPressHandler, KeyDownHandler,
-FocusHandler, SubPartAware, MouseOutHandler, MouseOverHandler {
+ implements CloseHandler<PopupPanel>, KeyPressHandler, KeyDownHandler,
+ FocusHandler, SubPartAware, MouseOutHandler, MouseOverHandler, EventTrigger {
// The hierarchy of VMenuBar is a bit weird as VMenuBar is the Paintable,
// used for the root menu but also used for the sub menus.
@@ -117,6 +121,8 @@ FocusHandler, SubPartAware, MouseOutHandler, MouseOverHandler {
/** For internal use only. May be removed or replaced in the future. */
public boolean htmlContentAllowed;
+ private Map<String, List<Command>> triggers = new HashMap<>();
+
public VMenuBar() {
// Create an empty horizontal menubar
this(false, null);
@@ -414,9 +420,12 @@ FocusHandler, SubPartAware, MouseOutHandler, MouseOverHandler {
* @param item
*/
public void itemClick(CustomMenuItem item) {
- if (item.getCommand() != null) {
+ boolean triggered = triggerEventIfNeeded(item);
+ if (item.getCommand() != null || triggered) {
try {
- item.getCommand().execute();
+ if (item.getCommand() != null) {
+ item.getCommand().execute();
+ }
} finally {
setSelected(null);
if (visibleChildMenu != null) {
@@ -808,6 +817,7 @@ FocusHandler, SubPartAware, MouseOutHandler, MouseOverHandler {
protected ContentMode descriptionContentMode = null;
private String styleName;
+ private String id;
/**
* Default menu item {@link Widget} constructor for GWT.create().
@@ -1166,6 +1176,14 @@ FocusHandler, SubPartAware, MouseOutHandler, MouseOverHandler {
return null;
}
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
}
/**
@@ -1642,6 +1660,7 @@ FocusHandler, SubPartAware, MouseOutHandler, MouseOverHandler {
openMenuAndFocusFirstIfPossible(getSelected());
} else {
try {
+ triggerEventIfNeeded(getSelected());
final Command command = getSelected().getCommand();
if (command != null) {
command.execute();
@@ -1654,10 +1673,7 @@ FocusHandler, SubPartAware, MouseOutHandler, MouseOverHandler {
// not leave menu to visible ("hover open") mode
menuVisible = false;
- VMenuBar root = this;
- while (root.getParentMenu() != null) {
- root = root.getParentMenu();
- }
+ VMenuBar root = getRoot();
root.ignoreFocus = true;
root.getElement().focus();
root.ignoreFocus = false;
@@ -1669,6 +1685,17 @@ FocusHandler, SubPartAware, MouseOutHandler, MouseOverHandler {
return false;
}
+ private boolean triggerEventIfNeeded(CustomMenuItem item) {
+ List<Command> commands = getTriggers().get(item.getId());
+ if (commands != null) {
+ for (Command command : commands) {
+ command.execute();
+ }
+ return true;
+ }
+ return false;
+ }
+
private void selectFirstItem() {
for (int i = 0; i < items.size(); i++) {
CustomMenuItem item = items.get(i);
@@ -1877,4 +1904,36 @@ FocusHandler, SubPartAware, MouseOutHandler, MouseOverHandler {
public void onMouseOut(MouseOutEvent event) {
LazyCloser.schedule();
}
+
+ private VMenuBar getRoot() {
+ VMenuBar root = this;
+
+ while (root.getParentMenu() != null) {
+ root = root.getParentMenu();
+ }
+
+ return root;
+ }
+
+ @Override
+ public HandlerRegistration addTrigger(Command command,
+ String partInformation) {
+ if (partInformation == null || partInformation.isEmpty()) {
+ throw new IllegalArgumentException(
+ "The 'partInformation' parameter must contain the menu item id");
+ }
+
+ getTriggers().computeIfAbsent(partInformation, s-> new ArrayList<>()).add(command);
+ return () -> {
+ List<Command> commands = getTriggers().get(partInformation);
+ if (commands != null) {
+ commands.remove(command);
+ }
+ };
+ }
+
+ private Map<String, List<Command>> getTriggers() {
+ return getRoot().triggers;
+ }
+
}
diff --git a/client/src/main/java/com/vaadin/client/ui/menubar/MenuBarConnector.java b/client/src/main/java/com/vaadin/client/ui/menubar/MenuBarConnector.java
index 6e6869edb2..8697f07d49 100644
--- a/client/src/main/java/com/vaadin/client/ui/menubar/MenuBarConnector.java
+++ b/client/src/main/java/com/vaadin/client/ui/menubar/MenuBarConnector.java
@@ -129,6 +129,7 @@ public class MenuBarConnector extends AbstractComponentConnector
}
currentItem = currentMenu.addItem(itemHTML, cmd);
+ currentItem.setId("" + itemId);
currentItem.updateFromUIDL(item, client);
if (item.getChildCount() > 0) {