diff options
author | Artur <artur@vaadin.com> | 2018-03-28 15:21:16 +0300 |
---|---|---|
committer | Ilia Motornyi <elmot@vaadin.com> | 2018-03-28 15:21:16 +0300 |
commit | 761bef8fb790258ac7a527f51a23da0bd1802dbe (patch) | |
tree | f7c6174a32affe0dbd9803143f09c5b9ddf959c3 /client/src | |
parent | 96efeccce765a2f12db71c9af2ac07fc4283c201 (diff) | |
download | vaadin-framework-761bef8fb790258ac7a527f51a23da0bd1802dbe.tar.gz vaadin-framework-761bef8fb790258ac7a527f51a23da0bd1802dbe.zip |
Support starting downloads and opening URLs from a menu item (#10478)
Diffstat (limited to 'client/src')
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) { |