8165234: Provide a way to not close toggle menu items on mouse click on component level
Reviewed-by: serb, ssadetsky
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java Fri Sep 30 22:57:41 2016 +0400
@@ -33,6 +33,7 @@
import java.awt.*;
import java.awt.event.*;
+import sun.swing.SwingUtilities2;
/**
@@ -89,7 +90,8 @@
Point p = e.getPoint();
if(p.x >= 0 && p.x < menuItem.getWidth() &&
p.y >= 0 && p.y < menuItem.getHeight()) {
- if (UIManager.getBoolean("CheckBoxMenuItem.closeOnMouseClick")) {
+ String property = "CheckBoxMenuItem.doNotCloseOnMouseClick";
+ if (!SwingUtilities2.getBoolean(menuItem, property)) {
manager.clearSelectedPath();
}
menuItem.doClick(0);
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java Fri Sep 30 22:57:41 2016 +0400
@@ -33,6 +33,7 @@
import java.awt.*;
import java.awt.event.*;
import java.io.Serializable;
+import sun.swing.SwingUtilities2;
/**
@@ -97,8 +98,8 @@
Point p = e.getPoint();
if(p.x >= 0 && p.x < menuItem.getWidth() &&
p.y >= 0 && p.y < menuItem.getHeight()) {
- String property = "RadioButtonMenuItem.closeOnMouseClick";
- if (UIManager.getBoolean(property)) {
+ String property = "RadioButtonMenuItem.doNotCloseOnMouseClick";
+ if (!SwingUtilities2.getBoolean(menuItem, property)) {
manager.clearSelectedPath();
}
menuItem.doClick(0);
--- a/jdk/src/java.desktop/share/classes/javax/swing/JCheckBoxMenuItem.java Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/JCheckBoxMenuItem.java Fri Sep 30 22:57:41 2016 +0400
@@ -58,10 +58,13 @@
* <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.
+ * close the menu. Such behavior can be controlled either by client
+ * {@link JComponent#putClientProperty} or the Look and Feel
+ * {@link UIManager#put} property named
+ * {@code "CheckBoxMenuItem.doNotCloseOnMouseClick"}. The default value is
+ * {@code false}. Setting the property to {@code true} prevents the menu from
+ * closing when it is clicked by the mouse. If the client property is set its
+ * value is always used; otherwise the {@literal L&F} property is queried.
* Note: some {@code L&F}s may ignore this property. All built-in {@code L&F}s
* inherit this behaviour.
* <p>
--- a/jdk/src/java.desktop/share/classes/javax/swing/JRadioButtonMenuItem.java Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/JRadioButtonMenuItem.java Fri Sep 30 22:57:41 2016 +0400
@@ -52,10 +52,13 @@
* <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.
+ * close the menu. Such behavior can be controlled either by client
+ * {@link JComponent#putClientProperty} or the Look and Feel
+ * {@link UIManager#put} property named
+ * {@code "RadioButtonMenuItem.doNotCloseOnMouseClick"}. The default value is
+ * {@code false}. Setting the property to {@code true} prevents the menu from
+ * closing when it is clicked by the mouse. If the client property is set its
+ * value is always used; otherwise the {@literal L&F} property is queried.
* Note: some {@code L&F}s may ignore this property. All built-in {@code L&F}s
* inherit this behaviour.
* <p>
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java Fri Sep 30 22:57:41 2016 +0400
@@ -1055,7 +1055,6 @@
"RadioButtonMenuItem.checkIcon", radioButtonMenuItemIcon,
"RadioButtonMenuItem.arrowIcon", menuItemArrowIcon,
"RadioButtonMenuItem.commandSound", null,
- "RadioButtonMenuItem.closeOnMouseClick", Boolean.TRUE,
"CheckBoxMenuItem.font", dialogPlain12,
"CheckBoxMenuItem.acceleratorFont", dialogPlain12,
@@ -1072,7 +1071,6 @@
"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 Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java Fri Sep 30 22:57:41 2016 +0400
@@ -936,13 +936,15 @@
}
}
- boolean closeOnMouseClick() {
+ boolean doNotCloseOnMouseClick() {
if (menuItem instanceof JCheckBoxMenuItem) {
- return UIManager.getBoolean("CheckBoxMenuItem.closeOnMouseClick");
+ String property = "CheckBoxMenuItem.doNotCloseOnMouseClick";
+ return SwingUtilities2.getBoolean(menuItem, property);
} else if (menuItem instanceof JRadioButtonMenuItem) {
- return UIManager.getBoolean("RadioButtonMenuItem.closeOnMouseClick");
+ String property = "RadioButtonMenuItem.doNotCloseOnMouseClick";
+ return SwingUtilities2.getBoolean(menuItem, property);
}
- return true;
+ return false;
}
/**
@@ -967,7 +969,7 @@
BasicLookAndFeel.playSound(menuItem, getPropertyPrefix() +
".commandSound");
}
- if (closeOnMouseClick()) {
+ if (!doNotCloseOnMouseClick()) {
// Visual feedback
if (msm == null) {
msm = MenuSelectionManager.defaultManager();
--- a/jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java Fri Sep 30 22:57:41 2016 +0400
@@ -2047,6 +2047,25 @@
}
/**
+ * Returns the client property for the given key if it is set; otherwise
+ * returns the {@L&F} property.
+ *
+ * @param component the component
+ * @param key an {@code String} specifying the key for the desired boolean value
+ * @return the boolean value of the client property if it is set or the {@L&F}
+ * property in other case.
+ */
+ public static boolean getBoolean(JComponent component, String key) {
+ Object clientProperty = component.getClientProperty(key);
+
+ if (clientProperty instanceof Boolean) {
+ return Boolean.TRUE.equals(clientProperty);
+ }
+
+ return UIManager.getBoolean(key);
+ }
+
+ /**
* Used to listen to "blit" repaints in RepaintManager.
*/
public interface RepaintListener {
--- a/jdk/test/javax/swing/JMenuItem/8158566/CloseOnMouseClickPropertyTest.java Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/test/javax/swing/JMenuItem/8158566/CloseOnMouseClickPropertyTest.java Fri Sep 30 22:57:41 2016 +0400
@@ -37,45 +37,67 @@
/*
* @test
- * @bug 8158566 8160879 8160977
+ * @bug 8158566 8160879 8160977 8158566
* @summary Provide a Swing property which modifies MenuItemUI behaviour
*/
public class CloseOnMouseClickPropertyTest {
+ private static final String CHECK_BOX_PROP = "CheckBoxMenuItem."
+ + "doNotCloseOnMouseClick";
+ private static final String RADIO_BUTTON_PROP = "RadioButtonMenuItem"
+ + ".doNotCloseOnMouseClick";
+
private static JFrame frame;
private static JMenu menu;
+ private static TestItem[] TEST_ITEMS = {
+ new TestItem(TestType.CHECK_BOX_MENU_ITEM, true, true),
+ new TestItem(TestType.CHECK_BOX_MENU_ITEM, true, false),
+ new TestItem(TestType.CHECK_BOX_MENU_ITEM, false, true),
+ new TestItem(TestType.CHECK_BOX_MENU_ITEM, false, false),
+
+ new TestItem(TestType.CHECK_BOX_MENU_ITEM, null, true),
+ new TestItem(TestType.CHECK_BOX_MENU_ITEM, null, false),
+ new TestItem(TestType.CHECK_BOX_MENU_ITEM, true, null),
+ new TestItem(TestType.CHECK_BOX_MENU_ITEM, false, null),
+
+
+ new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, true, true),
+ new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, true, false),
+ new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, false, true),
+ new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, false, false),
+
+ new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, true, null),
+ new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, false, null),
+ new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, null, true),
+ new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, null, false),
+
+ new TestItem(TestType.MENU_ITEM, true, true),
+ new TestItem(TestType.MENU_ITEM, true, false),
+ new TestItem(TestType.MENU_ITEM, false, true),
+ new TestItem(TestType.MENU_ITEM, false, false),
+
+ new TestItem(TestType.MENU_ITEM, true, null),
+ new TestItem(TestType.MENU_ITEM, false, null),
+ new TestItem(TestType.MENU_ITEM, null, true),
+ new TestItem(TestType.MENU_ITEM, null, false),
+ };
+
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);
+ for (TestItem testItem : TEST_ITEMS) {
+ test(testItem);
+ }
}
}
- 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 {
+ private static void test(TestItem item) throws Exception {
Robot robot = new Robot();
robot.setAutoDelay(50);
- SwingUtilities.invokeAndWait(() -> createAndShowGUI(testType));
+ SwingUtilities.invokeAndWait(() -> createAndShowGUI(item));
robot.waitForIdle();
Point point = getClickPoint(true);
@@ -94,21 +116,13 @@
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");
- }
+ if (isShowing ^ item.doNotCloseOnMouseClick()) {
+ throw new RuntimeException("Property is not taken into account!");
}
});
}
- private static void createAndShowGUI(TestType testType) {
+ private static void createAndShowGUI(TestItem testItem) {
frame = new JFrame();
frame.setSize(300, 300);
@@ -116,23 +130,15 @@
JMenuBar menuBar = new JMenuBar();
menu = new JMenu("Menu");
- menu.add(getMenuItem(testType));
+ JMenuItem menuItem = testItem.getMenuItem();
+ testItem.setProperties(menuItem);
+ menu.add(menuItem);
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];
@@ -157,4 +163,60 @@
CHECK_BOX_MENU_ITEM,
RADIO_BUTTON_MENU_ITEM
}
-}
+
+ static class TestItem {
+
+ TestType type;
+ Boolean compDoNotCloseOnMouseClick;
+ Boolean lafDoNotCloseOnMouseClick;
+
+ public TestItem(TestType type,
+ Boolean compDoNotCloseOnMouseClick,
+ Boolean lafDoNotCloseOnMouseClick)
+ {
+ this.type = type;
+ this.compDoNotCloseOnMouseClick = compDoNotCloseOnMouseClick;
+ this.lafDoNotCloseOnMouseClick = lafDoNotCloseOnMouseClick;
+ }
+
+ boolean doNotCloseOnMouseClick() {
+ switch (type) {
+ case MENU_ITEM:
+ return false;
+ default:
+ return compDoNotCloseOnMouseClick != null
+ ? compDoNotCloseOnMouseClick
+ : lafDoNotCloseOnMouseClick;
+ }
+ }
+
+ void setProperties(JMenuItem menuItem) {
+ switch (type) {
+ case CHECK_BOX_MENU_ITEM:
+ menuItem.putClientProperty(CHECK_BOX_PROP, compDoNotCloseOnMouseClick);
+ UIManager.put(CHECK_BOX_PROP, lafDoNotCloseOnMouseClick);
+ break;
+ case RADIO_BUTTON_MENU_ITEM:
+ menuItem.putClientProperty(RADIO_BUTTON_PROP, compDoNotCloseOnMouseClick);
+ UIManager.put(RADIO_BUTTON_PROP, lafDoNotCloseOnMouseClick);
+ break;
+ default:
+ menuItem.putClientProperty(CHECK_BOX_PROP, compDoNotCloseOnMouseClick);
+ menuItem.putClientProperty(RADIO_BUTTON_PROP, compDoNotCloseOnMouseClick);
+ UIManager.put(CHECK_BOX_PROP, lafDoNotCloseOnMouseClick);
+ UIManager.put(RADIO_BUTTON_PROP, lafDoNotCloseOnMouseClick);
+ }
+ }
+
+ JMenuItem getMenuItem() {
+ switch (type) {
+ 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");
+ }
+ }
+ }
+}
\ No newline at end of file