gfx/menu_bar.h
A menu bar, and the same menus as a list for a narrow editor.
#include <sonore/gfx/menu_bar.h>- 3types
- Guides: The native editor
On this page
Design notes
Driven by a model, not built from components
The menus are asked for when they open. A MenuBarModel names the top-level menus and builds each PopupMenu on demand, so a menu's ticks and greyed items are worked out from the plugin's state at the moment somebody looks -- not cached when the editor was built and wrong by the time anyone opens it. The same model drives a MenuBarComponent and a BurgerMenuComponent, so an editor that swaps one for the other when it gets narrow has one place where its menus are defined.
The keyboard is the hard part
F10 enters the bar with the first name lit; Left and Right walk the names; Down, Return or a letter opens one. With a menu open, Left and Right move to the neighbouring menu and Escape steps back to the bar rather than out of it -- a second Escape leaves. Alt with a menu's initial (or the letter after an '&' in its name) opens it from anywhere. That is the desktop's contract and every step of it is a test here.
One thing the peers cannot give: a tap of Alt ON ITS OWN. A bare modifier press is not a key event on any of the three platforms this SDK targets, so the route in from nothing is F10 or Alt+letter. Neither reaches a bar that is not an ancestor of the focused component by itself, either -- keys walk UP from the focus -- so the owner offers unhandled keys with handleShortcutKey(), from MouseRouter::onUnhandledKey, beside its commands.
Inside the editor
The menus open in a PopupMenuOverlay (popup_overlay.h), which is why moving the mouse along the bar with a menu open switches menus and why the bar hears Left and Right while a menu has the keyboard.
Types 3
The menus: their names, and what is in each.
Abstract, and named as the reference names it, because porting a menu means porting these four functions and nothing else. The model must outlive nothing: destroying it tells every component showing it, which then shows nothing rather than calling into freed memory.
class MenuBarComponent : public Widget, private detail::MenuModelListener
The bar along the top.
Takes the keyboard only while it is being driven from it -- entered with F10 or Alt -- and gives it back afterwards. A bar that was always focusable would be a stop in every Tab cycle through the editor for a control the mouse reaches in one move.
class BurgerMenuComponent : public Widget, private detail::MenuModelListener
The same menus as one scrolling list: each top-level name a heading, its items under it.
For an editor too narrow for a bar -- usually inside a SidePanel opened by a burger button, which is the arrangement the name comes from. Re-reads the model whenever it says so and whenever refresh() is called, because a list that is always visible cannot build its contents on open the way a drop-down does.