8158566: Provide a Swing property to not close toggle menu items on mouse click
Reviewed-by: prr, ssadetsky
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java Thu Jun 30 22:27:28 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java Thu Jun 30 23:49:00 2016 +0300
@@ -89,7 +89,9 @@
Point p = e.getPoint();
if(p.x >= 0 && p.x < menuItem.getWidth() &&
p.y >= 0 && p.y < menuItem.getHeight()) {
- manager.clearSelectedPath();
+ if (UIManager.getBoolean("CheckBoxMenuItem.closeOnMouseClick")) {
+ manager.clearSelectedPath();
+ }
menuItem.doClick(0);
} else {
manager.processMouseEvent(e);
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java Thu Jun 30 22:27:28 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java Thu Jun 30 23:49:00 2016 +0300
@@ -97,7 +97,10 @@
Point p = e.getPoint();
if(p.x >= 0 && p.x < menuItem.getWidth() &&
p.y >= 0 && p.y < menuItem.getHeight()) {
- manager.clearSelectedPath();
+ String property = "RadioButtonMenuItem.closeOnMouseClick";
+ if (UIManager.getBoolean(property)) {
+ manager.clearSelectedPath();
+ }
menuItem.doClick(0);
} else {
manager.processMouseEvent(e);
--- a/jdk/src/java.desktop/share/classes/javax/swing/JCheckBoxMenuItem.java Thu Jun 30 22:27:28 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/javax/swing/JCheckBoxMenuItem.java Thu Jun 30 23:49:00 2016 +0300
@@ -56,6 +56,15 @@
* href="http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html">How
* to Use Actions</a>, a section in <em>The Java Tutorial</em>.
* <p>
+ * Some times it is required to select several check box menu items from a menu.
+ * In this case it is useful that clicking on one check box menu item does not
+ * close the menu. Such behavior can be controlled by the Look and Feel property
+ * named {@code "CheckBoxMenuItem.closeOnMouseClick"}. The default value is
+ * {@code true}. Setting the property to {@code false} prevents the menu from
+ * closing when it is clicked by the mouse.
+ * Note: some {@code L&F}s may ignore this property. All built-in {@code L&F}s
+ * inherit this behaviour.
+ * <p>
* For further information and examples of using check box menu items,
* see <a
href="http://docs.oracle.com/javase/tutorial/uiswing/components/menu.html">How to Use Menus</a>,
--- a/jdk/src/java.desktop/share/classes/javax/swing/JRadioButtonMenuItem.java Thu Jun 30 22:27:28 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/javax/swing/JRadioButtonMenuItem.java Thu Jun 30 23:49:00 2016 +0300
@@ -50,6 +50,15 @@
* href="http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html">How
* to Use Actions</a>, a section in <em>The Java Tutorial</em>.
* <p>
+ * Some menus can have several button groups with radio button menu items. In
+ * this case it is useful that clicking on one radio button menu item does not
+ * close the menu. Such behavior can be controlled by the Look and Feel property
+ * named {@code "RadioButtonMenuItem.closeOnMouseClick"}. The default value is
+ * {@code true}. Setting the property to {@code false} prevents the menu from
+ * closing when it is clicked by the mouse.
+ * Note: some {@code L&F}s may ignore this property. All built-in {@code L&F}s
+ * inherit this behaviour.
+ * <p>
* For further documentation and examples see
* <a
href="http://docs.oracle.com/javase/tutorial/uiswing/components/menu.html">How to Use Menus</a>,
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java Thu Jun 30 22:27:28 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java Thu Jun 30 23:49:00 2016 +0300
@@ -1055,6 +1055,7 @@
"RadioButtonMenuItem.checkIcon", radioButtonMenuItemIcon,
"RadioButtonMenuItem.arrowIcon", menuItemArrowIcon,
"RadioButtonMenuItem.commandSound", null,
+ "RadioButtonMenuItem.closeOnMouseClick", Boolean.TRUE,
"CheckBoxMenuItem.font", dialogPlain12,
"CheckBoxMenuItem.acceleratorFont", dialogPlain12,
@@ -1071,6 +1072,7 @@
"CheckBoxMenuItem.checkIcon", checkBoxMenuItemIcon,
"CheckBoxMenuItem.arrowIcon", menuItemArrowIcon,
"CheckBoxMenuItem.commandSound", null,
+ "CheckBoxMenuItem.closeOnMouseClick", Boolean.TRUE,
"Menu.font", dialogPlain12,
"Menu.acceleratorFont", dialogPlain12,
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java Thu Jun 30 22:27:28 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java Thu Jun 30 23:49:00 2016 +0300
@@ -936,6 +936,15 @@
}
}
+ boolean closeOnMouseClick() {
+ if (menuItem instanceof JCheckBoxMenuItem) {
+ return UIManager.getBoolean("CheckBoxMenuItem.closeOnMouseClick");
+ } else if (menuItem instanceof JRadioButtonMenuItem) {
+ return UIManager.getBoolean("RadioButtonMenuItem.closeOnMouseClick");
+ }
+ return true;
+ }
+
/**
* Call this method when a menu item is to be activated.
* This method handles some of the details of menu item activation
@@ -958,11 +967,14 @@
BasicLookAndFeel.playSound(menuItem, getPropertyPrefix() +
".commandSound");
}
- // Visual feedback
- if (msm == null) {
- msm = MenuSelectionManager.defaultManager();
+ if (closeOnMouseClick()) {
+ // Visual feedback
+ if (msm == null) {
+ msm = MenuSelectionManager.defaultManager();
+ }
+
+ msm.clearSelectedPath();
}
- msm.clearSelectedPath();
menuItem.doClick(0);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JMenuItem/8158566/CloseOnMouseClickPropertyTest.java Thu Jun 30 23:49:00 2016 +0300
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.event.InputEvent;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+/*
+ * @test
+ * @bug 8158566
+ * @summary Provide a Swing property which modifies MenuItemUI behaviour
+ */
+public class CloseOnMouseClickPropertyTest {
+
+ private static JFrame frame;
+ private static JMenu menu;
+
+ public static void main(String[] args) throws Exception {
+
+ for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
+ UIManager.setLookAndFeel(info.getClassName());
+ test(true);
+
+ setProperty(false);
+ test(false);
+
+ setProperty(true);
+ test(true);
+ }
+ }
+
+ private static void setProperty(boolean closeOnMouseClick) {
+ UIManager.put("CheckBoxMenuItem.closeOnMouseClick", closeOnMouseClick);
+ UIManager.put("RadioButtonMenuItem.closeOnMouseClick", closeOnMouseClick);
+ }
+
+ private static void test(boolean closeOnMouseClick) throws Exception {
+ for (TestType testType : TestType.values()) {
+ test(testType, closeOnMouseClick);
+ }
+ }
+
+ private static void test(TestType testType, boolean closeOnMouseClick)
+ throws Exception {
+
+ Robot robot = new Robot();
+ robot.setAutoDelay(50);
+ SwingUtilities.invokeAndWait(() -> createAndShowGUI(testType));
+ robot.waitForIdle();
+
+ Point point = getClickPoint(true);
+ robot.mouseMove(point.x, point.y);
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+ robot.waitForIdle();
+
+ point = getClickPoint(false);
+ robot.mouseMove(point.x, point.y);
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+ robot.waitForIdle();
+
+ SwingUtilities.invokeAndWait(() -> {
+ JMenuItem menuItem = menu.getItem(0);
+ boolean isShowing = menuItem.isShowing();
+ frame.dispose();
+
+ if (TestType.MENU_ITEM.equals(testType)) {
+ if (isShowing) {
+ throw new RuntimeException("Menu Item is not closed!");
+ }
+ } else {
+ if (isShowing ^ !closeOnMouseClick) {
+ throw new RuntimeException("Property is not taken into account:"
+ + " closeOnMouseClick");
+ }
+ }
+ });
+ }
+
+ private static void createAndShowGUI(TestType testType) {
+
+ frame = new JFrame();
+ frame.setSize(300, 300);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ JMenuBar menuBar = new JMenuBar();
+ menu = new JMenu("Menu");
+ menu.add(getMenuItem(testType));
+ menuBar.add(menu);
+ frame.setJMenuBar(menuBar);
+ frame.setVisible(true);
+ }
+
+ private static JMenuItem getMenuItem(TestType testType) {
+ switch (testType) {
+ case CHECK_BOX_MENU_ITEM:
+ return new JCheckBoxMenuItem("Check Box");
+ case RADIO_BUTTON_MENU_ITEM:
+ return new JRadioButtonMenuItem("Radio Button");
+ default:
+ return new JMenuItem("Menu Item");
+ }
+ }
+
+ private static Point getClickPoint(boolean parent) throws Exception {
+ Point points[] = new Point[1];
+
+ SwingUtilities.invokeAndWait(() -> {
+
+ JComponent comp = parent ? menu : menu.getItem(0);
+
+ Point point = comp.getLocationOnScreen();
+ Rectangle bounds = comp.getBounds();
+ point.x += bounds.getWidth() / 2;
+ point.y += bounds.getHeight() / 2;
+
+ points[0] = point;
+ });
+
+ return points[0];
+ }
+
+ enum TestType {
+
+ MENU_ITEM,
+ CHECK_BOX_MENU_ITEM,
+ RADIO_BUTTON_MENU_ITEM
+ }
+}