Java: Swing: LookAndFeel: MenuItems
Claim: There is no need for JCheckBoxMenuItem nor JRadioButtonMenuItem nor separate UI delegate class IDs (and implementations) (nor even separate UIDefault keys in BasicLookAndFeel and descendants for most properties (except icons)). Things could have easily been done without.
JCheckBoxMenuItem is just a checkable ("selectable") menu items - whose ButtonModel is selectable (such a notion clearly belongs to ButtonModel)
JRadioButtonMenuItem is just a checkable menu item whose ButtonModel is in a ButtonGroup
Hints:
Neither JCheckBoxMenuItem (which just adds set/getState which could also have been in AbstractButton) and JRadioButtonMenuItem (which does not even that) have any new features and there are no obvious features which one could think of.
In the standard classes, they are never used as a type (argument, result, instance variable), not even in private code.
The LAF implementations are almost completely identical (see below), often code has to be (or is) duplicated.
The LAF properties are identical except for the icon.
One can turn a JMenuItem into checkable (or radio-able) just by installing a custom icon (although: a) the icon placing/spacing looks bad, b) the painting code expects the icon not to change the graphics color (#4812431) )) and button model (and button group), for example
JMenuItem m = new JMenuItem("Item");
m.setIcon(UIManager.getIcon("CheckBoxMenuItem.checkIcon"));
m.setModel(new JToggleButton.ToggleButtonModel());
only has bad look, but is functional.
BasicCheckBox/RadioButtonMenuItem do not have anything but dead code (processMouseEvent). (In fact they needn't as the ButtonModel/ButtonGroup alone will handle the selection).
Metal uses just the BasicXXXMenuItemUIs.
Motif adds subclasses to all three with almost exactly the same code (the plain MenuItemUI misses one isSelected() but it should never become selected anyway), and have the main work delegated to one method in MotifGraphicsUtils (which is not used otherwise)
Windows has unused classes for CheckBox/RadioButton, but changes the MenuItemUI, compare (#4685843)
(This can be seen by enabling mnemonic hiding and opening the menu: the mnemonics for JRadioButton/JCheckBoxMenuItems remain visible. Probably there are other differences in painting as well (or Windows wouldn't have rewritten the method completely). This looks like an oversight.
Synth has the MenuItemUIs copied from Basic (including the dead code)
Inspection of some custom LAFs show that they either miss installing their UI for RadioButton/CheckBoxMenuItems or reproduce essentially the same code in all three cases.
(The actual values are actually rather unimportant)
Basic | Metal | Motif | Windows | |
|---|---|---|---|---|
font | dialogPlain12 | menuTextValue | dialogPlain12 | MenuFont |
acceleratorFont only "MenuItem." used | dialogPlain12 | subTextValue | dialogPlain12 | MenuFont / missing / missing |
background | "menu" | "menu" | MenuBackgroundColor | |
foreground | "menuText" | "menuText" | MenuTextColor | |
selectionBackground | "textHighlightText" | menuSelectedBackground | menuItemPressedBackground | SelectionBackgroundColor |
selectionForeground | "textHighlight" | menuSelectedForeground | menuItemPressedForeground | SelectionTextColor |
disabledForeground only MenuItem. used | null | menuDisabledForeground | InactiveTextColor / missing? / InactiveTextColor | |
acceleratorForeground | "menuText" | acceleratorForeground | MenuTextColor | |
acceleratorSelectionForeground | "textHighlightText" | acceleratorSelectedForeground | SelectionTextColor | |
acceleratorDelimiter only "MenuItem." used | menuItemAcceleratorDelimiter | menuItemAcceleratorDelimiter / missing / missing | ||
border | marginBorder | menuItemBorder | menuMarginBorder | marginBorder / missing/ missing |
borderPainted | false | true | (updated by ChangeListener) | |
margin | (2,2,2,2) | |||
checkIcon | menuItemCheckIcon / radioButtonMenuItemIcon / checkBoxMenuItemIcon | getMenuItemCheckIcon=null / CheckBoxMenuItemIcon / RadioButtonMenuIcon | menuItemCheckIcon / checkBoxIcon / radioButtonIcon | menuItemCheckIcon / missing? / missing? |
arrowIcon | menuItemArrowIcon | getMenuItemArrowIcon | menuItemArrowIcon | menuItemArrowIcon / missing? / missing? |
commandSound | null | MenuItemCommand | win.sound.menuCommand |
"missing?" does not mean that it is not known, but that is highly questionable that this is intended.
Observations: Except for the checkIcon, none of the properties are set to any different value.
Even if some properties are only used in the "MenuItem." version, sometimes all three keys are set.
The purpose of checkIcon (menuItem) and arrowIcon (all) seems to be to fill space to align items better (This already fails in BasicLookAndFeel where the radioButton icon is smaller and is unused in Metal).
(Somewhen, the layout was extended to align separately on text and accelerator (#4113639), probably this should have been cleared up then).
The layout places the checkIcon first, then icon/text; arrowIcon on the right (trailing) side (#4199382).
Which Icon will be chosen when (Implementation details)?
Basic/Metal | Motif | |
|---|---|---|
getPreferredSize | icon | not overridden |
layout for painting | icon | same strategy |
painting | icon != null: !enabled => disabledIcon (note: possibly auto-created by AbstractButton) pressed && armed =>pressedIcon (or icon) else icon | same strategy |
At least I would expect that selected => selectedIcon (or icon), disabled && selected => selectedDisabledIcon (or disabledIcon), and possibly use rollover(Selected)Icon instead of pressedIcon.
BasicCheckBoxMenuItemUI/BasicRadioButtonMenuItemUI did set the "checkIcon" as the menu item's selected icon (uselessly, the selected icon was unused by the painting code). This code has been removed with JDK1.4.0beta2.
Windows did not have a MenuItemUI (It was introduced in JDK1.4.0beta1 (or somewhen in 1.3.1?))
Do not use different UI for the three types of MenuItems. It only complicates everything. As it is the button model that handles the selection anyway, there is no need for explicit LAF code.
(C) 2001-2009 Christian Kaufhold (swing@chka.de)