8075063: Context menu closes on mouse scroll
authorpsadhukhan
Sat, 01 Jul 2017 09:56:02 +0530
changeset 47144 20f16e21003f
parent 47143 cb2688cc1ca5
child 47145 392def3f8452
8075063: Context menu closes on mouse scroll Reviewed-by: ssadetsky
jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java
jdk/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java
jdk/test/javax/swing/JPopupMenu/8075063/ContextMenuScrollTest.java
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java	Fri Jun 30 11:03:44 2017 +0530
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java	Sat Jul 01 09:56:02 2017 +0530
@@ -917,9 +917,14 @@
                     processMouseEvent(me);
                 break;
             case MouseEvent.MOUSE_WHEEL:
+                // If the scroll is done inside a combobox, menuitem,
+                // or inside a Popup#HeavyWeightWindow or inside a frame
+                // popup should not close which is the standard behaviour
                 if (isInPopup(src)
-                    || ((src instanceof JComboBox) && ((JComboBox) src).isPopupVisible())) {
-
+                    || ((src instanceof JComboBox) && ((JComboBox) src).isPopupVisible())
+                    || ((src instanceof JWindow) && ((JWindow)src).isVisible())
+                    || ((src instanceof JMenuItem) && ((JMenuItem)src).isVisible())
+                    || (src instanceof JFrame)) {
                     return;
                 }
                 cancelPopupMenu();
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java	Fri Jun 30 11:03:44 2017 +0530
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java	Sat Jul 01 09:56:02 2017 +0530
@@ -2306,7 +2306,11 @@
                             if (grabLog.isLoggable(PlatformLogger.Level.FINE)) {
                                 grabLog.fine("Generating UngrabEvent on {0} because not inside of shell", this);
                             }
-                            postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource()));
+                            // Do not post Ungrab Event for mouse scroll
+                            if ((xbe.get_button() != XConstants.buttons[3])
+                                && (xbe.get_button() != XConstants.buttons[4])) {
+                                postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource()));
+                            }
                             return;
                         }
                     }
@@ -2327,14 +2331,26 @@
                             if (grabLog.isLoggable(PlatformLogger.Level.FINE)) {
                                 grabLog.fine("Generating UngrabEvent on {0} because hierarchy ended", this);
                             }
-                            postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource()));
+                            // For mouse wheel event, do not send UngrabEvent
+                            if (xbe.get_type() != XConstants.ButtonPress) {
+                                postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource()));
+                            } else if ((xbe.get_button() != XConstants.buttons[3])
+                                   && (xbe.get_button() != XConstants.buttons[4])) {
+                                postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource()));
+                            }
                         }
                     } else {
                         // toplevel is null - outside of hierarchy
                         if (grabLog.isLoggable(PlatformLogger.Level.FINE)) {
                             grabLog.fine("Generating UngrabEvent on {0} because toplevel is null", this);
                         }
-                        postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource()));
+                        // For mouse wheel event, do not send UngrabEvent
+                        if (xbe.get_type() != XConstants.ButtonPress) {
+                            postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource()));
+                        } else if ((xbe.get_button() != XConstants.buttons[3])
+                               && (xbe.get_button() != XConstants.buttons[4])) {
+                            postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource()));
+                        }
                         return;
                     }
                 } else {
@@ -2342,7 +2358,13 @@
                     if (grabLog.isLoggable(PlatformLogger.Level.FINE)) {
                         grabLog.fine("Generating UngrabEvent on because target is null {0}", this);
                     }
-                    postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource()));
+                    // For mouse wheel event, do not send UngrabEvent
+                    if (xbe.get_type() != XConstants.ButtonPress) {
+                        postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource()));
+                    } else if ((xbe.get_button() != XConstants.buttons[3])
+                            && (xbe.get_button() != XConstants.buttons[4])) {
+                        postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource()));
+                    }
                     return;
                 }
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JPopupMenu/8075063/ContextMenuScrollTest.java	Sat Jul 01 09:56:02 2017 +0530
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @key headful
+ * @bug 8075063
+ * @summary  Verifies if Context menu closes on mouse scroll
+ * @run main ContextMenuScrollTest
+ */
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Robot;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JSeparator;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+
+public class ContextMenuScrollTest extends JPopupMenu
+{
+    private JMenuItem undo;
+    private JMenuItem redo;
+    private JMenuItem cut;
+    private JMenuItem copy;
+    private JMenuItem paste;
+    private JMenuItem delete;
+    private JMenuItem selectAll;
+    private final Robot robot;
+    private JFrame frame;
+    private JMenuBar menuBar;
+    private JMenu menu;
+    private volatile Point p = null;
+    private volatile Dimension d = null;
+
+    public static void main(String[] args) throws Exception {
+        new ContextMenuScrollTest();
+    }
+    void blockTillDisplayed(JComponent comp) throws Exception {
+        while (p == null) {
+            try {
+                SwingUtilities.invokeAndWait(() -> {
+                    p = comp.getLocationOnScreen();
+                    d = menu.getSize();
+                });
+            } catch (IllegalStateException e) {
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException ie) {
+                }
+            }
+        }
+    }
+
+    public ContextMenuScrollTest() throws Exception
+    {
+        robot = new Robot();
+        robot.setAutoDelay(200);
+        try {
+            SwingUtilities.invokeAndWait(()->createGUI());
+            blockTillDisplayed(menu);
+            robot.waitForIdle();
+
+            robot.mouseMove(p.x + d.width/2, p.y + d.height/2);
+            robot.mousePress(InputEvent.BUTTON1_MASK);
+            robot.mouseRelease(InputEvent.BUTTON1_MASK);
+            robot.waitForIdle();
+
+            System.out.println("popmenu visible " + menu.isPopupMenuVisible());
+            robot.mouseWheel(1);
+            robot.waitForIdle();
+            System.out.println("popmenu visible " + menu.isPopupMenuVisible());
+            if (!menu.isPopupMenuVisible()) {
+                throw new RuntimeException("Popup closes on mouse scroll");
+            }
+        } finally {
+            SwingUtilities.invokeAndWait(()->frame.dispose());
+        }
+    }
+
+    public void createGUI() {
+        frame = new JFrame();
+        menuBar = new JMenuBar();
+        menu = new JMenu("Menu");
+        menuBar.add(menu);
+
+        undo = new JMenuItem("Undo");
+        undo.setEnabled(false);
+        undo.setAccelerator(KeyStroke.getKeyStroke("control Z"));
+        undo.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent event) {
+            }
+        });
+
+        menu.add(undo);
+
+        redo = new JMenuItem("Redo");
+        redo.setEnabled(false);
+        redo.setAccelerator(KeyStroke.getKeyStroke("control Y"));
+        redo.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent event) {
+            }
+        });
+        menu.add(redo);
+
+        menu.add(new JSeparator());
+
+        cut = new JMenuItem("Cut");
+        cut.setEnabled(false);
+        cut.setAccelerator(KeyStroke.getKeyStroke("control X"));
+        cut.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent event) {
+            }
+        });
+
+        menu.add(cut);
+
+        copy = new JMenuItem("Copy");
+        copy.setEnabled(false);
+        copy.setAccelerator(KeyStroke.getKeyStroke("control C"));
+        copy.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent event) {
+            }
+        });
+
+        menu.add(copy);
+
+        paste = new JMenuItem("Paste");
+        paste.setEnabled(false);
+        paste.setAccelerator(KeyStroke.getKeyStroke("control V"));
+        paste.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent event) {
+            }
+        });
+
+        menu.add(paste);
+
+        delete = new JMenuItem("Delete");
+        delete.setEnabled(false);
+        delete.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
+        delete.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent event) {
+            }
+        });
+
+        menu.add(delete);
+
+        menu.add(new JSeparator());
+
+        selectAll = new JMenuItem("Select All");
+        selectAll.setEnabled(false);
+        selectAll.setAccelerator(KeyStroke.getKeyStroke("control A"));
+        selectAll.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent event) {
+            }
+        });
+        menu.add(selectAll);
+        frame.setJMenuBar(menuBar);
+
+        frame.pack();
+        frame.setVisible(true);
+    }
+}