Leepoint Java Reference Notes on the Java language and its standard library.

Menus

A menu bar, menus, and items. Three classes and a couple of conventions.

JMenuBar bar = new JMenuBar();

JMenu file = new JMenu("File");
JMenuItem open = new JMenuItem("Open…");
open.addActionListener(e -> openFile());
file.add(open);
file.addSeparator();
file.add(new JMenuItem("Exit"));

bar.add(file);
frame.setJMenuBar(bar);

Note setJMenuBar on the frame, not add. A menu bar is not laid out with the content.

JMenu extends JMenuItem, so a menu can be added to a menu — that is how submenus work, with no extra class.

Mnemonics and accelerators

Two different things, both worth setting.

A mnemonic underlines a letter for keyboard navigation within an open menu:

file.setMnemonic(KeyEvent.VK_F);

An accelerator is a shortcut that works without opening anything:

open.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
        Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()));

Use getMenuShortcutKeyMaskEx() rather than hard-coding CTRL_DOWN_MASK. It returns Command on macOS and Control elsewhere, so one line of code produces the right shortcut on each platform.

Checkbox and radio items

JCheckBoxMenuItem wrap = new JCheckBoxMenuItem("Word wrap");

ButtonGroup group = new ButtonGroup();
JRadioButtonMenuItem light = new JRadioButtonMenuItem("Light");
JRadioButtonMenuItem dark  = new JRadioButtonMenuItem("Dark");
group.add(light);
group.add(dark);

The ButtonGroup enforces that exactly one is selected. Without it the radio items behave as independent checkboxes — a bug that looks like a rendering problem.

Use Actions

If a command also appears on a toolbar, define it once as an Action and give it to both. Enabling or disabling the action updates every control using it, which is the only maintainable way to keep a menu in step with application state.

Popup menus

JPopupMenu popup = new JPopupMenu();
popup.add(new JMenuItem("Copy"));
component.setComponentPopupMenu(popup);

setComponentPopupMenu handles the platform’s trigger — right-click on most systems, Control-click on macOS — and inherits to child components. Detecting the trigger by hand with a mouse listener is the older approach and gets the platform differences wrong.

Enabling and disabling

A menu item that is irrelevant right now should be greyed, not hidden. Hiding items makes menus change shape between visits, so users cannot learn where anything is.

save.setEnabled(document.isModified());

Update the state when the underlying condition changes, not when the menu opens — a menuSelected listener that recalculates everything is a common shortcut that goes wrong the moment a keyboard accelerator is used, because the menu never opened.

Platform conventions

macOS expects the menu bar at the top of the screen rather than inside the window. One system property, set before any UI is created, moves it:

System.setProperty("apple.laf.useScreenMenuBar", "true");

The conventional ordering — File, Edit, View, then application-specific menus, then Help last — is worth following even when your application would arrange them differently. Users navigate by position long before they read the labels.