From dace5ab66322c226b2cce79848a9c0518740f020 Mon Sep 17 00:00:00 2001 From: Anna Koskinen Date: Wed, 25 Feb 2015 13:43:39 +0200 Subject: [PATCH] Fix mouse wheel scrolling of ComboBox in IE11 (#16918) IE11 is broken so that the simple implementation always moves up on any mouse wheel event. Therefore, this change borrows the approach taken by Escalator for low-level handling of mouse wheel events in a way compatible with all supported browsers. Change-Id: I98c89d3ccfeea38d6a454ef708f0eb7d1d3f480c --- .../client/ui/JsniMousewheelHandler.java | 80 ++++++++++++++++ .../com/vaadin/client/ui/VFilterSelect.java | 92 +++++++++++++++---- .../combobox/ComboBoxMousewheel.java | 59 ++++++++++++ 3 files changed, 213 insertions(+), 18 deletions(-) create mode 100644 client/src/com/vaadin/client/ui/JsniMousewheelHandler.java create mode 100644 uitest/src/com/vaadin/tests/components/combobox/ComboBoxMousewheel.java diff --git a/client/src/com/vaadin/client/ui/JsniMousewheelHandler.java b/client/src/com/vaadin/client/ui/JsniMousewheelHandler.java new file mode 100644 index 0000000000..82ea768871 --- /dev/null +++ b/client/src/com/vaadin/client/ui/JsniMousewheelHandler.java @@ -0,0 +1,80 @@ +/* + * Copyright 2000-2014 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.ui; + +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.dom.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.widgets.Escalator; + +/** + * A mousewheel handling class to get around the limits of + * {@link Event#ONMOUSEWHEEL}. + * + * For internal use only. May be removed or replaced in the future. + * + * @see Escalator.JsniWorkaround + */ +abstract class JsniMousewheelHandler { + + /** + * A JavaScript function that handles the mousewheel DOM event, and passes + * it on to Java code. + * + * @see #createMousewheelListenerFunction(Widget) + */ + protected final JavaScriptObject mousewheelListenerFunction; + + protected JsniMousewheelHandler(final Widget widget) { + mousewheelListenerFunction = createMousewheelListenerFunction(widget); + } + + /** + * A method that constructs the JavaScript function that will be stored into + * {@link #mousewheelListenerFunction}. + * + * @param widget + * a reference to the current instance of {@link Widget} + */ + protected abstract JavaScriptObject createMousewheelListenerFunction( + Widget widget); + + public native void attachMousewheelListener(Element element) + /*-{ + if (element.addEventListener) { + // FireFox likes "wheel", while others use "mousewheel" + var eventName = 'onmousewheel' in element ? 'mousewheel' : 'wheel'; + element.addEventListener(eventName, this.@com.vaadin.client.ui.JsniMousewheelHandler::mousewheelListenerFunction); + } else { + // IE8 + element.attachEvent("onmousewheel", this.@com.vaadin.client.ui.JsniMousewheelHandler::mousewheelListenerFunction); + } + }-*/; + + public native void detachMousewheelListener(Element element) + /*-{ + if (element.addEventListener) { + // FireFox likes "wheel", while others use "mousewheel" + var eventName = element.onwheel===undefined?"mousewheel":"wheel"; + element.removeEventListener(eventName, this.@com.vaadin.client.ui.JsniMousewheelHandler::mousewheelListenerFunction); + } else { + // IE8 + element.detachEvent("onmousewheel", this.@com.vaadin.client.ui.JsniMousewheelHandler::mousewheelListenerFunction); + } + }-*/; + +} diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java index 6ddce3d93c..99ebde1910 100644 --- a/client/src/com/vaadin/client/ui/VFilterSelect.java +++ b/client/src/com/vaadin/client/ui/VFilterSelect.java @@ -25,9 +25,11 @@ import java.util.List; import java.util.Set; import com.google.gwt.aria.client.Roles; +import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.dom.client.Style.Unit; @@ -219,6 +221,50 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, } } + /** An inner class that handles all logic related to mouse wheel. */ + private class MouseWheeler extends JsniMousewheelHandler { + + public MouseWheeler() { + super(VFilterSelect.this); + } + + @Override + protected native JavaScriptObject createMousewheelListenerFunction( + Widget widget) + /*-{ + return $entry(function(e) { + var deltaX = e.deltaX ? e.deltaX : -0.5*e.wheelDeltaX; + var deltaY = e.deltaY ? e.deltaY : -0.5*e.wheelDeltaY; + + // IE8 has only delta y + if (isNaN(deltaY)) { + deltaY = -0.5*e.wheelDelta; + } + + @com.vaadin.client.ui.VFilterSelect.JsniUtil::moveScrollFromEvent(*)(widget, deltaX, deltaY, e); + }); + }-*/; + + } + + /** + * A utility class that contains utility methods that are usually called + * from JSNI. + *

+ * The methods are moved in this class to minimize the amount of JSNI code + * as much as feasible. + */ + static class JsniUtil { + public static void moveScrollFromEvent(final Widget widget, + final double deltaX, final double deltaY, + final NativeEvent event) { + + if (!Double.isNaN(deltaY)) { + ((VFilterSelect) widget).suggestionPopup.scroll(deltaY); + } + } + } + /** * Represents the popup box with the selection options. Wraps a suggestion * menu. @@ -243,6 +289,8 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, private int topPosition; + private final MouseWheeler mouseWheeler = new MouseWheeler(); + /** * Default constructor */ @@ -275,6 +323,18 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, setPreviewingAllNativeEvents(true); } + @Override + protected void onLoad() { + super.onLoad(); + mouseWheeler.attachMousewheelListener(getElement()); + } + + @Override + protected void onUnload() { + mouseWheeler.detachMousewheelListener(getElement()); + super.onUnload(); + } + /** * Shows the popup where the user can see the filtered options * @@ -531,6 +591,20 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, } } + private void scroll(double deltaY) { + boolean scrollActive = menu.isScrollActive(); + + debug("VFS.SP: scroll() scrollActive: " + scrollActive); + + if (!scrollActive) { + if (deltaY > 0d) { + lazyPageScroller.scrollDown(); + } else { + lazyPageScroller.scrollUp(); + } + } + } + @Override public void onBrowserEvent(Event event) { debug("VFS.SP: onBrowserEvent()"); @@ -543,24 +617,6 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, lazyPageScroller.scrollDown(); } - } else if (event.getTypeInt() == Event.ONMOUSEWHEEL) { - - boolean scrollNotActive = !menu.isScrollActive(); - - debug("VFS.SP: onBrowserEvent() scrollNotActive: " - + scrollNotActive); - - if (scrollNotActive) { - int velocity = event.getMouseWheelVelocityY(); - - debug("VFS.SP: onBrowserEvent() velocity: " + velocity); - - if (velocity > 0) { - lazyPageScroller.scrollDown(); - } else { - lazyPageScroller.scrollUp(); - } - } } /* diff --git a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxMousewheel.java b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxMousewheel.java new file mode 100644 index 0000000000..802e7480f5 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxMousewheel.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2014 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.combobox; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.ComboBox; + +/** + * Tests mousewheel handling in ComboBox. + * + * @author Vaadin Ltd + */ +public class ComboBoxMousewheel extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + addComponent(createComboBox("Paged")); + + ComboBox cb = createComboBox("Unpaged"); + cb.setPageLength(0); + addComponent(cb); + } + + private ComboBox createComboBox(String caption) { + ComboBox cb = new ComboBox(caption); + cb.setId(caption); + cb.setImmediate(true); + for (int i = 1; i < 100; i++) { + cb.addItem("Item " + i); + } + return cb; + } + + @Override + protected String getTestDescription() { + return "ComboBox scrolling should be possible to both directions on Paged + IE as well.
" + + "IE should not move paging up when scrolled down."; + } + + @Override + protected Integer getTicketNumber() { + return 16918; + } + +} -- 2.39.5