8165234: Provide a way to not close toggle menu items on mouse click on component level

Reviewed-by: serb, ssadetsky
This commit is contained in:
Alexander Scherbatiy 2016-09-30 22:57:41 +04:00
parent d055089575
commit 63a1a87940
8 changed files with 154 additions and 64 deletions

View File

@ -33,6 +33,7 @@ import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
import java.awt.*;
import java.awt.event.*;
import sun.swing.SwingUtilities2;
/**
@ -89,7 +90,8 @@ public class MotifCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI
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);

View File

@ -33,6 +33,7 @@ import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
import java.awt.*;
import java.awt.event.*;
import java.io.Serializable;
import sun.swing.SwingUtilities2;
/**
@ -97,8 +98,8 @@ public class MotifRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI
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);

View File

@ -58,10 +58,13 @@ import javax.accessibility.*;
* <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>

View File

@ -52,10 +52,13 @@ import javax.accessibility.*;
* <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>

View File

@ -1055,7 +1055,6 @@ public abstract class BasicLookAndFeel extends LookAndFeel implements Serializab
"RadioButtonMenuItem.checkIcon", radioButtonMenuItemIcon,
"RadioButtonMenuItem.arrowIcon", menuItemArrowIcon,
"RadioButtonMenuItem.commandSound", null,
"RadioButtonMenuItem.closeOnMouseClick", Boolean.TRUE,
"CheckBoxMenuItem.font", dialogPlain12,
"CheckBoxMenuItem.acceleratorFont", dialogPlain12,
@ -1072,7 +1071,6 @@ public abstract class BasicLookAndFeel extends LookAndFeel implements Serializab
"CheckBoxMenuItem.checkIcon", checkBoxMenuItemIcon,
"CheckBoxMenuItem.arrowIcon", menuItemArrowIcon,
"CheckBoxMenuItem.commandSound", null,
"CheckBoxMenuItem.closeOnMouseClick", Boolean.TRUE,
"Menu.font", dialogPlain12,
"Menu.acceleratorFont", dialogPlain12,

View File

@ -936,13 +936,15 @@ public class BasicMenuItemUI extends MenuItemUI
}
}
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 @@ public class BasicMenuItemUI extends MenuItemUI
BasicLookAndFeel.playSound(menuItem, getPropertyPrefix() +
".commandSound");
}
if (closeOnMouseClick()) {
if (!doNotCloseOnMouseClick()) {
// Visual feedback
if (msm == null) {
msm = MenuSelectionManager.defaultManager();

View File

@ -2046,6 +2046,25 @@ public class SwingUtilities2 {
return false;
}
/**
* 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.
*/

View File

@ -37,45 +37,67 @@ import javax.swing.UIManager;
/*
* @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 @@ public class CloseOnMouseClickPropertyTest {
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 @@ public class CloseOnMouseClickPropertyTest {
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 @@ public class CloseOnMouseClickPropertyTest {
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");
}
}
}
}