Menus and toolbars
Menus and toolbars are how desktop apps expose commands — actions the user can trigger regardless of what they are doing in the main window. A well-designed menu bar is predictable: users know where to look for File, Edit, and Help without thinking about it. A toolbar puts the most frequent commands within a single click.
This lesson builds both from scratch on a fresh app. By the end you will have a frame with a complete menu bar, keyboard shortcuts, and a toolbar — the kind of chrome that makes an app feel finished.
Start fresh
Create a new file called menu_app.rb:
|
|
Run it. A window with a read-only text area — we will use log to record each action as the user triggers it, so you can see every menu item and toolbar button working without building a full application around them.
Step 1 — A basic menu bar
A menu bar is built from three classes working together:
Wx::MenuBar— the bar itself, attached to the frameWx::Menu— a single drop-down menu (File, Edit, Help, etc.)Wx::MenuItem— an individual item within a menu
Build a File menu and attach it to the frame. Add a build_menu method and call it from initialize before build_ui:
|
|
|
|
Run it. On Windows and Linux, Exit and the separator appear exactly where you placed them. On macOS you will see New, Open, and Save in the File menu — but not the separator or Exit. This is correct platform behaviour, not a bug.
macOS and stock menu items. wxRuby3 automatically moves items with certain stock IDs to the locations macOS expects.
Wx::ID_EXITbecomes “Quit” in the application menu (the menu named after your app, to the left of File).Wx::ID_ABOUTis similarly relocated. When an item is moved, any separator immediately before it in the original menu is also removed — which is why the separator disappears. This is wxRuby3 doing the right thing: your menu code is platform-neutral, and wxRuby3 adapts it to each platform’s conventions.
The items do nothing yet — we will wire them up shortly.
Three things to notice in the code:
Stock IDs. Wx::ID_NEW, Wx::ID_OPEN, Wx::ID_SAVE, and Wx::ID_EXIT are stock identifiers. wxRuby3 knows about these and handles platform-specific placement automatically, as you just saw. Stock IDs give you correct platform behaviour for free.
Ampersands for keyboard access. The & before a letter underlines it (on Windows and Linux) and makes it the keyboard access key when the menu is open. "&New" means pressing N when the File menu is open activates New. The & is invisible on macOS.
Tab-separated shortcuts. The \t separates the menu label from the keyboard shortcut displayed on the right. "&New\tCtrl+N" shows “Ctrl+N” on the right side of the menu item, and wxRuby3 registers that key combination automatically. On macOS, Ctrl is displayed and handled as Cmd.
Step 2 — Handling menu events
Wire up the menu items in bind_events:
|
|
Run it. Click each menu item — the text area logs each action. Wx::ID_EXIT calls close on the frame, which triggers the close event and exits. Try the keyboard shortcuts too — Ctrl+N, Ctrl+O, Ctrl+S, Ctrl+Q all work without any extra wiring.
evt_menu is the event handler for menu items. It takes the menu item’s ID, which for stock items is the stock constant. For custom items you will use the ID returned by append.
Step 3 — A second menu
Add an Edit menu alongside File:
|
|
Standard editing commands. Cut, Copy, Paste, andSelect All work in the text area without any evt_menu handlers because wxRuby3’s text control handles these stock IDs natively. The focused widget processes them first and they never reach your handler. In a real app this is exactly what you want — standard editing just works. Try working with some text in the text pane now - you’ll find they all work with no code!
You only need evt_menu handlers for these IDs if you are doing something beyond the default behaviour, such as updating an undo history or enabling/disabling toolbar buttons in response to selection changes.
Run it. Two menus, all items working.
Step 4 — Custom menu items
Not every item has a stock ID. For custom items, append returns an ID you can use for event binding:
|
|
Wx::ID_ANY tells wxRuby3 to assign a unique ID automatically. The append method returns the Wx::MenuItem object — call .id on it to get the integer ID for event binding.
Add the handlers:
|
|
Run it. Three menus, all custom items working.
Step 5 — Checked and radio menu items
Some menu items have state — they can be checked or unchecked. wxRuby3 supports two variants:
Check items toggle independently — like checkboxes:
|
|
Radio items are mutually exclusive within their group — like radio buttons:
|
|
Add these to build_menu inside the view menu, before menu_bar.append(view_menu, "&View"). Then handle them:
|
|
To read or set a check item’s state programmatically:
|
|
Run it. The ruler item toggles its checkmark. The icon size items behave as a radio group — selecting one deselects the others.
Step 6 — The Help menu and About dialog
A Help menu with an About item is standard on every platform.
|
|
|
|
Run it.
On macOS the About item appears in the application menu rather than a Help menu — that is correct platform behaviour, provided by the stock
Wx::ID_ABOUTID at no extra cost.
Step 7 — Toolbar
A toolbar sits below the menu bar and provides quick access to the most common actions. Add a build_toolbar method and call it from initialize after build_menu:
|
|
|
|
Toolbar buttons fire evt_tool, not evt_menu. Add handlers in bind_events:
|
|
Run it. The toolbar buttons all fire and log their actions. In a real app, the menu item and toolbar button for the same action would call the same handler method:
|
|
toolbar.realize must be called after all tools are added — it finalises the toolbar layout. Forgetting it produces a blank toolbar.
Note that @zoom_in_id and @zoom_out_id are instance variables set in build_menu, so build_menu must be called before build_toolbar — which is already the case in initialize. Update the finished app at the bottom to match.
The finished app
|
|
Previous: Layout with sizers | Next: Dialogs