Repository navigation
Add menus
An application can have a menu bar, a context menu for a right click on the page, and menus that the page opens itself, all drawn by the platform. In this guide, a notes window gets a menu bar with File, Edit, and View, a context menu for its notes, and a Sort by button that opens a menu from the page.

application.menu sets the menu bar of every window. Each entry at the top level is a submenu:
public class Menus {
public static void main(String[] args) {
Application.launch(
WindowParameters.of("Menus", 620, 440),
window -> {
window
.application()
.menu(
MenuItem.submenu(
"File",
MenuItem.of("New note", "CmdOrCtrl+N", () -> window.emit("command", "new")),
MenuItem.of("Export", "CmdOrCtrl+E", () -> window.emit("command", "export")),
MenuItem.separator(),
MenuItem.role(MenuRole.QUIT)),
MenuItem.editMenu(),
MenuItem.submenu(
"View",
MenuItem.checkbox(
"Show done", true, shown -> window.emit("showDone", shown))));
window.contextMenu(
MenuItem.of("Mark as done", () -> window.emit("command", "done")),
MenuItem.of("Delete", () -> window.emit("command", "delete")),
MenuItem.separator(),
MenuItem.role(MenuRole.SELECT_ALL));
window.loadResource("menus/index.html");
});
}
}| Entry | What it does |
|---|---|
MenuItem.of(label, action) |
Runs action when the user picks it. |
MenuItem.of(label, accelerator, action) |
The same, and also on the keys, such as CmdOrCtrl+N, while a window of the application has the keyboard. |
MenuItem.checkbox(label, checked, onToggle) |
A check mark that hears every change. |
MenuItem.submenu(label, items...) |
A menu inside a menu. |
MenuItem.separator() |
A line between groups of entries. |
MenuItem.role(MenuRole.QUIT) |
An entry that the platform knows: UNDO, REDO, CUT, COPY, PASTE, SELECT_ALL, CLOSE_WINDOW, MINIMIZE, FULLSCREEN, or QUIT, with the label and the keys of the platform. |
MenuItem.editMenu() |
An Edit menu with Undo, Redo, Cut, Copy, Paste, and Select All. |
MenuItem.windowMenu() |
A Window menu with Minimize, Full Screen, and Close Window. |
An action runs off the UI thread, so it can block or call back into a window. The keys of an entry come before the page: a page that listens to Ctrl+S itself doesn't hear the Ctrl+S of an entry. The keys of an editing role stay with the page, so text fields keep their copy and paste.
window.menu(...) gives one window a menu bar of its own, and an empty list takes the bar away from
that window. window.useApplicationMenu() goes back to the menu bar of the application.
window.contextMenu replaces the menu that the engine opens on a right click:

The page still decides first. A page that handles contextmenu and calls preventDefault() keeps
the click to itself, so a text editor on the page can open its own menu. A check mark of a context
menu keeps its state from one opening to the next. The accelerators of a context menu are only
shown, because a context menu has no keys.
To open a menu from Java at the pointer, such as for a button of a custom title bar, call
window.showContextMenu(items...). Its future completes when the menu closes.
lwjwae.menu.popup opens a menu that the page describes, and resolves to the id of the entry that
the user picked, or to null:
document.querySelector("#sort").addEventListener("click", async (event) => {
const picked = await lwjwae.menu.popup([
{ id: "title", label: "Title" },
{ id: "date", label: "Date", checked: true },
{ separator: true },
{ id: "reverse", label: "Reverse order", checked: false }
], event);
log(picked === null ? "Sort: nothing picked" : `Sort: ${picked}`);
});
An entry is { id, label, enabled, accelerator }, with checked for a check mark, submenu for a
list of entries, role for a role such as "copy", or separator: true. The second argument
places the menu: a mouse event, { x, y } in the coordinates of the page, or nothing for the
pointer. Pass { signal } with an AbortSignal to close the menu from the page.
The page hears what Java emits for the entries of the menu bar and the context menu:
lwjwae.listen("command", ({ payload }) => log(`Command: ${payload}`));
lwjwae.listen("showDone", ({ payload }) => log(`Show done: ${payload}`));| Platform | The menu bar |
|---|---|
| Windows | Under the title bar of every window. A window without a title bar doesn't show it. |
| macOS | At the top of the screen, for the window in front. The application menu, with About, Hide, and Quit, stays first. Without an Edit menu, a text field of a page takes no Command+C, V, X, A, or Z. |
| Linux, GTK 3 and GTK 4 | Above the page of every window, also in a window without a title bar. |
- A menu is a value. A change, such as a new label, sets the menu again.
- On macOS, a role in a menu of the page goes along the responder chain, as every Mac menu sends it, and the page hears of it as of no pick.
- On Wayland, the compositor opens a menu only near the last click or key press that it saw, which is where the pointer is for a menu opened from a click.
Tutorial
- Set up the project
- Open a window
- Call Java from the page
- Send events from Java to the page
- Draw your own title bar
- Export notes through a native dialog
- Keep the application in the tray
- Show notifications
- Give the application an icon and a name
- Build a native executable
- Package the application
Guides
- Pass objects through the bridge
- Stream bytes between the page and Java
- Open several windows
- Place and control a window
- Make a transparent window
- Add menus
- Register global shortcuts
- Show progress in the taskbar
- Use the clipboard
- Handle dropped files
- Zoom the page
- Set the window icon
- Follow the theme of the desktop
- Use cookies
- Store data in SQLite
- Keep secrets
- Show dialogs
- Run a single instance
- Open your own links and files
- Update the application
- Handle page permissions
- Handle links and page loads
Reference