From e7201fd30803dbbd231c9a26745b21d5b807abc0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leif=20=C3=85strand?= Date: Thu, 6 Jun 2013 12:01:37 +0300 Subject: [PATCH] Allow using element resize listeners from javascript connectors (#11996) Change-Id: I97b280c2e260752be87ce85d2eda81ec4f14d4fe --- .../client/JavaScriptConnectorHelper.java | 81 +++++++++++++++- .../ui/AbstractJavaScriptComponent.java | 10 ++ .../JavaScriptResizeListener.html | 81 ++++++++++++++++ .../JavaScriptResizeListener.java | 96 +++++++++++++++++++ .../javascriptcomponent/ResizeJsConnector.js | 23 +++++ 5 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptResizeListener.html create mode 100644 uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptResizeListener.java create mode 100644 uitest/src/com/vaadin/tests/components/javascriptcomponent/ResizeJsConnector.js diff --git a/client/src/com/vaadin/client/JavaScriptConnectorHelper.java b/client/src/com/vaadin/client/JavaScriptConnectorHelper.java index b144603be0..52d2eeb6dc 100644 --- a/client/src/com/vaadin/client/JavaScriptConnectorHelper.java +++ b/client/src/com/vaadin/client/JavaScriptConnectorHelper.java @@ -25,11 +25,13 @@ import java.util.Set; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArray; +import com.google.gwt.dom.client.Element; import com.google.gwt.json.client.JSONArray; -import com.google.gwt.user.client.Element; import com.vaadin.client.communication.JavaScriptMethodInvocation; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; +import com.vaadin.client.ui.layout.ElementResizeEvent; +import com.vaadin.client.ui.layout.ElementResizeListener; import com.vaadin.shared.JavaScriptConnectorState; import com.vaadin.shared.communication.MethodInvocation; @@ -42,6 +44,7 @@ public class JavaScriptConnectorHelper { private final Map rpcObjects = new HashMap(); private final Map> rpcMethods = new HashMap>(); + private final Map> resizeListeners = new HashMap>(); private JavaScriptObject connectorWrapper; private int tag; @@ -204,9 +207,71 @@ public class JavaScriptConnectorHelper { 'translateVaadinUri': $entry(function(uri) { return c.@com.vaadin.client.ApplicationConnection::translateVaadinUri(Ljava/lang/String;)(uri); }), + 'addResizeListener': function(element, resizeListener) { + if (!element || element.nodeType != 1) throw "element must be defined"; + if (typeof resizeListener != "function") throw "resizeListener must be defined"; + $entry(h.@com.vaadin.client.JavaScriptConnectorHelper::addResizeListener(*)).call(h, element, resizeListener); + }, + 'removeResizeListener': function(element, resizeListener) { + if (!element || element.nodeType != 1) throw "element must be defined"; + if (typeof resizeListener != "function") throw "resizeListener must be defined"; + $entry(h.@com.vaadin.client.JavaScriptConnectorHelper::removeResizeListener(*)).call(h, element, resizeListener); + } }; }-*/; + // Called from JSNI to add a listener + private void addResizeListener(Element element, + final JavaScriptObject callbackFunction) { + Map elementListeners = resizeListeners + .get(element); + if (elementListeners == null) { + elementListeners = new HashMap(); + resizeListeners.put(element, elementListeners); + } + + ElementResizeListener listener = elementListeners.get(callbackFunction); + if (listener == null) { + LayoutManager layoutManager = LayoutManager.get(connector + .getConnection()); + listener = new ElementResizeListener() { + @Override + public void onElementResize(ElementResizeEvent e) { + invokeElementResizeCallback(e.getElement(), + callbackFunction); + } + }; + layoutManager.addElementResizeListener(element, listener); + elementListeners.put(callbackFunction, listener); + } + } + + private static native void invokeElementResizeCallback(Element element, + JavaScriptObject callbackFunction) + /*-{ + // Call with a simple event object and 'this' pointing to the global scope + callbackFunction.call($wnd, {'element': element}); + }-*/; + + // Called from JSNI to remove a listener + private void removeResizeListener(Element element, + JavaScriptObject callbackFunction) { + Map listenerMap = resizeListeners + .get(element); + if (listenerMap == null) { + return; + } + + ElementResizeListener listener = listenerMap.remove(callbackFunction); + if (listener != null) { + LayoutManager.get(connector.getConnection()) + .removeElementResizeListener(element, listener); + if (listenerMap.isEmpty()) { + resizeListeners.remove(element); + } + } + } + private native void attachRpcMethod(JavaScriptObject rpc, String iface, String method) /*-{ @@ -377,6 +442,20 @@ public class JavaScriptConnectorHelper { public void onUnregister() { invokeIfPresent(connectorWrapper, "onUnregister"); + + if (!resizeListeners.isEmpty()) { + LayoutManager layoutManager = LayoutManager.get(connector + .getConnection()); + for (Entry> entry : resizeListeners + .entrySet()) { + Element element = entry.getKey(); + for (ElementResizeListener listener : entry.getValue().values()) { + layoutManager + .removeElementResizeListener(element, listener); + } + } + resizeListeners.clear(); + } } private static native void invokeIfPresent( diff --git a/server/src/com/vaadin/ui/AbstractJavaScriptComponent.java b/server/src/com/vaadin/ui/AbstractJavaScriptComponent.java index 0b3da768c9..6769b6e513 100644 --- a/server/src/com/vaadin/ui/AbstractJavaScriptComponent.java +++ b/server/src/com/vaadin/ui/AbstractJavaScriptComponent.java @@ -82,6 +82,16 @@ import com.vaadin.shared.ui.JavaScriptComponentState; *
  • translateVaadinUri(uri) - Translates a Vaadin URI to a URL * that can be used in the browser. This is just way of accessing * {@link com.vaadin.client.ApplicationConnection#translateVaadinUri(String)}
  • + *
  • addResizeListener(element, callbackFunction) - Registers a + * listener that gets notified whenever the size of the provided element + * changes. The listener is called with one parameter: an event object with the + * element property pointing to the element that has been resized. + *
  • removeResizeListener(element, callbackFunction) - + * Unregisters a combination of an element and a listener that has previously + * been registered using addResizeListener. All registered + * listeners are automatically unregistered when this connector is unregistered, + * but this method can be use to to unregister a listener at an earlier point in + * time. * * The connector wrapper also supports these special functions: *
      diff --git a/uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptResizeListener.html b/uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptResizeListener.html new file mode 100644 index 0000000000..53af1d4ceb --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptResizeListener.html @@ -0,0 +1,81 @@ + + + + + + +New Test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      New Test
      open/run/com.vaadin.tests.components.javascriptcomponent.JavaScriptResizeListener?restartApplication&debug
      assertTextvaadin=runcomvaadintestscomponentsjavascriptcomponentJavaScriptResizeListener::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VCssLayout[0]/JavaScriptWidget[0]Initial state
      clickvaadin=runcomvaadintestscomponentsjavascriptcomponentJavaScriptResizeListener::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]
      assertTextvaadin=runcomvaadintestscomponentsjavascriptcomponentJavaScriptResizeListener::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VCssLayout[0]/JavaScriptWidget[0]Initial state
      mouseClickvaadin=runcomvaadintestscomponentsjavascriptcomponentJavaScriptResizeListener::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VCheckBox[0]/domChild[0]52,4
      assertTextvaadin=runcomvaadintestscomponentsjavascriptcomponentJavaScriptResizeListener::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VCssLayout[0]/JavaScriptWidget[0]Current size is 200 x 50
      clickvaadin=runcomvaadintestscomponentsjavascriptcomponentJavaScriptResizeListener::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]
      assertTextvaadin=runcomvaadintestscomponentsjavascriptcomponentJavaScriptResizeListener::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VCssLayout[0]/JavaScriptWidget[0]Current size is 100 x 100
      mouseClickvaadin=runcomvaadintestscomponentsjavascriptcomponentJavaScriptResizeListener::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VCheckBox[0]/domChild[0]69,2
      assertTextvaadin=runcomvaadintestscomponentsjavascriptcomponentJavaScriptResizeListener::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VCssLayout[0]/JavaScriptWidget[0]Listener disabled
      clickvaadin=runcomvaadintestscomponentsjavascriptcomponentJavaScriptResizeListener::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]
      assertTextvaadin=runcomvaadintestscomponentsjavascriptcomponentJavaScriptResizeListener::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VCssLayout[0]/JavaScriptWidget[0]Listener disabled
      + + diff --git a/uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptResizeListener.java b/uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptResizeListener.java new file mode 100644 index 0000000000..9d0094b833 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptResizeListener.java @@ -0,0 +1,96 @@ +/* + * Copyright 2000-2013 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.javascriptcomponent; + +import com.vaadin.annotations.JavaScript; +import com.vaadin.data.Property; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.AbstractJavaScriptComponent; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.CssLayout; + +public class JavaScriptResizeListener extends AbstractTestUI { + + @JavaScript("ResizeJsConnector.js") + public class ResizeJsComponent extends AbstractJavaScriptComponent { + public void setListenerEnabled(boolean enabled) { + callFunction("setListenerEnabled", Boolean.valueOf(enabled)); + } + } + + private final ResizeJsComponent resizeJsComponent = new ResizeJsComponent(); + + private final CssLayout holder = new CssLayout(); + + @Override + protected void setup(VaadinRequest request) { + + addComponent(new Button("Change holder size", + new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + updateHolderSize(); + } + })); + addComponent(new CheckBox("Listener active") { + { + setImmediate(true); + addValueChangeListener(new ValueChangeListener() { + @Override + public void valueChange(Property.ValueChangeEvent event) { + resizeJsComponent.setListenerEnabled(event + .getProperty().getValue() == Boolean.TRUE); + } + }); + } + }); + + updateHolderSize(); + addComponent(holder); + + resizeJsComponent.setSizeFull(); + holder.addComponent(resizeJsComponent); + } + + private void updateHolderSize() { + if (holder.getHeight() == 100) { + holder.setHeight("50px"); + } else { + holder.setHeight("100px"); + } + + if (holder.getWidth() == 100) { + holder.setWidth("200px"); + } else { + holder.setWidth("100px"); + } + } + + @Override + protected String getTestDescription() { + return "Test for getting resize events for javascript components"; + } + + @Override + protected Integer getTicketNumber() { + return Integer.valueOf(11996); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/javascriptcomponent/ResizeJsConnector.js b/uitest/src/com/vaadin/tests/components/javascriptcomponent/ResizeJsConnector.js new file mode 100644 index 0000000000..4e871aa1b7 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/javascriptcomponent/ResizeJsConnector.js @@ -0,0 +1,23 @@ +function com_vaadin_tests_components_javascriptcomponent_JavaScriptResizeListener_ResizeJsComponent() { + var self = this; + var e = this.getElement(); + + var setText = function(text) { + e.innerHTML = text; + } + setText('Initial state'); + + var resizeListener = function(event) { + setText('Current size is ' + event.element.offsetWidth + " x " + event.element.offsetHeight); + }; + + this.setListenerEnabled = function(enabled) { + if (enabled) { + setText("Listener enabled"); + self.addResizeListener(e, resizeListener); + } else { + setText("Listener disabled"); + self.removeResizeListener(e, resizeListener); + } + } +} \ No newline at end of file -- 2.39.5