MenuBar¶
Header: winui/ui/menus/MenuBar.hpp. Spec: winui/ui/menus/MenuBar.hpp.
A horizontal menu bar: a row of headers, each dropping down a cascading MenuFlyout-style menu that floats in the overlay layer under the header's left edge.
The bar is one tab stop. Left/Right move between headers and wrap around. Down, Enter and Space open the focused header's menu with its first item focused. While a menu is open the bar keeps focus and passes the keys on to the menu. Every item's shortcut fires app-wide, whether its menu is open or not. The declarative form is ui::MenuBar.
Example:
bool show_status_bar = true;
auto menu = std::make_unique<ui::MenuBarControl>();
menu->set_items({
ui::MenuBarItem{.text = "File", .items = {
{.text = "Open", .shortcut = ui::Ctrl + ui::Key::O, .on_click = [&] { open_file(); }},
ui::MenuItem::separator(),
{.text = "Exit", .on_click = [&] { quit(); }},
}},
ui::MenuBarItem{.text = "View", .items = {
ui::MenuItem::toggle("Status bar", /*checked=*/true,
[&](bool on) { show_status_bar = on; }),
}},
});
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
ui::MenuBarControl* bar = stack->add(std::move(menu)); // observer; the stack owns it
window.set_content(std::move(stack));
// Or declaratively:
window.set_content(ui::MenuBar{ .items = {
{ .text = "File", .items = { { .text = "Open", .on_click = [&] { open_file(); } },
ui::MenuItem::separator(),
{ .text = "Exit", .on_click = [&] { quit(); } } } },
}});
ui::MenuBar¶
A menu bar. Builds a ui::MenuBarControl. Each menu is a list of ui::MenuItem, so its items may be toggles, radio items, separators or submenus with icons and shortcuts. Every item's shortcut fires app-wide, whether its menu is open or not.
Menu items live in a std::vector built from a brace list, which only copies, so ui::MenuItem::on_click is a copyable std::function. A handler must not capture a move-only value.
Example:
ui::Property<bool> status_bar{true};
window.set_content(ui::VerticalStack{ .content = {
ui::MenuBar{ .items = {
{ .text = "File",
.items = { { .text = "Open", .shortcut = ui::Ctrl + ui::Key::O,
.on_click = [&] { open_file(); } },
ui::MenuItem::separator(),
{ .text = "Exit", .on_click = [&] { quit(); } } } },
{ .text = "View",
.items = { ui::MenuItem::toggle("Status bar", ui::bind(status_bar)) } },
}},
ui::Text{ .text = "Document" },
}});
Every field, in the order a designated initializer names them:
ui::MenuBar{
.items, .style, .margin, .width, .height, .min_width, .min_height, .max_width, .max_height,
.horizontal_alignment, .vertical_alignment, .visible, .tool_tip, .enabled, .accessibility,
.on_pointer_entered, .on_pointer_exited, .on_got_focus, .on_lost_focus, .on_key_down,
.on_wheel, .control
}
Fields, in declaration order (designators must follow it):
| Field | Type | Default | |
|---|---|---|---|
items |
ui::Binding<std::vector<ui::MenuBarItem>> |
The bar's headers, left to right, each with the menu it drops down. | |
on_pointer_entered |
ui::Action |
The pointer moved onto the control. | |
on_pointer_exited |
ui::Action |
The pointer left the control. | |
on_got_focus |
ui::Action |
Keyboard focus arrived on the control, or on anything inside it, from outside it. A control with nothing focusable in it never gets focus, and never raises this. | |
on_lost_focus |
ui::Action |
Keyboard focus left the control and everything inside it. | |
on_key_down |
ui::Callback<ui::Handling(const ui::KeyEvent&)> |
A key pressed while focus is on the control or inside it, before the control's own handling; ui::Handled stops it there (Element::on_key_down()). | |
on_wheel |
ui::Callback<ui::Handling(const ui::PointerEvent&)> |
A wheel turn over the control, before the control scrolls; ui::Handled stops it there (Element::on_wheel()). | |
control |
ui::ControlOut<ui::MenuBar> |
Out: the handle that receives the built control. |
Plus the common fields style, margin, width, height, min_width, min_height, max_width, max_height, horizontal_alignment, vertical_alignment, visible, tool_tip, enabled, accessibility every spec carries (conventions).
build¶
Consumes the spec and returns the built ui::MenuBarControl; the caller owns it.
ui::MenuBarControl¶
Derives from ui::Element.
A horizontal menu bar: a row of headers, each dropping down a cascading MenuFlyout-style menu that floats in the overlay layer under the header's left edge.
The bar is one tab stop. Left/Right move between headers and wrap around. Down, Enter and Space open the focused header's menu with its first item focused. While a menu is open the bar keeps focus and passes the keys on to the menu. Every item's shortcut fires app-wide, whether its menu is open or not. The declarative form is ui::MenuBar.
Example:
bool show_status_bar = true;
auto menu = std::make_unique<ui::MenuBarControl>();
menu->set_items({
ui::MenuBarItem{.text = "File", .items = {
{.text = "Open", .shortcut = ui::Ctrl + ui::Key::O, .on_click = [&] { open_file(); }},
ui::MenuItem::separator(),
{.text = "Exit", .on_click = [&] { quit(); }},
}},
ui::MenuBarItem{.text = "View", .items = {
ui::MenuItem::toggle("Status bar", /*checked=*/true,
[&](bool on) { show_status_bar = on; }),
}},
});
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
ui::MenuBarControl* bar = stack->add(std::move(menu)); // observer; the stack owns it
window.set_content(std::move(stack));
// Or declaratively:
window.set_content(ui::MenuBar{ .items = {
{ .text = "File", .items = { { .text = "Open", .on_click = [&] { open_file(); } },
ui::MenuItem::separator(),
{ .text = "Exit", .on_click = [&] { quit(); } } } },
}});
MenuBarControl¶
An enabled, empty bar; it is not focusable until it has items.
~MenuBarControl¶
MenuBarControl¶
Moves a bar that has not been parented yet.
operator=¶
items¶
The menus, in header order. Toggle and radio items' is_checked() reflect clicks made in the open menu and the properties their checked is bound to, so read them back from here.
set_items¶
Replaces the menus, closes any that is open and lays out the header row again.
open_index¶
Which header's menu is open; unset when none is.
active_command¶
The item holding focus in the open menu (an index into its top level); unset when there is none.
focused_header¶
The header the keyboard is on (Left/Right move it); 0 for a fresh bar.
open¶
Opens header index's menu, as a click on it would, closing any other. With from_keyboard the menu's first focusable item is focused, as Down/Enter/Space do. Does nothing for an out-of-range index or a disabled bar; a header with an empty menu only takes the keyboard focus.
close¶
Closes the open menu and its submenus. The menu fades out over the theme's close duration.
focused¶
Whether the bar holds keyboard focus; the focus ring is then drawn around focused_header().
focusable¶
One tab stop for the whole bar, while it is enabled and has at least one header.
handle_global_shortcut¶
Every menu's shortcuts, open or not.
take_menu_access¶
Alt or F10 alone moves keyboard focus to the first header. Alt with a letter opens the menu whose header starts with that letter, ignoring case, with its first item focused. Escape and running an item give focus back (Window::end_menu_access()).
transition_opacity¶
The menu's opacity: fully opaque while it slides in, then fading out as it closes.
automation_role¶
AutomationRole::MenuBar.
automation_children¶
One MenuItem node per header, which expands to open its menu.
automation_parent_of¶
The open menu belongs to its header's node.
automation_focus¶
The open menu's item the keyboard is on, else the header it is on.
measure_override¶
The header row. Each header is its title plus padding and margin, and the bar is at least 40 DIPs tall. An open menu floats and never changes this size.
arrange_override¶
Keeps an open menu hanging from its header.
pointer_override¶
Hover highlights a header; a click opens its menu, and a second click on the same header closes it. While a menu is open, moving onto another header opens that one instead.
render_override¶
Draws the header row: subtle header backgrounds, the open menu's header pressed-in, and the focus ring around the focused header. The menu itself is a popup the window draws.
tick_override¶
Advances the menu's open slide, submenu hover delays and the close fade.
clear_dirty_override¶
Clears the bar's dirty flags and those of the floating menu with it.
enabled_changed¶
is_enabled() flipped, through set_enabled() here or on an ancestor, or a move to another parent. Drop any press or hover state. The element is redrawn either way. No-op by default.
focus_changed¶
Called by the Window as focus arrives or leaves; losing focus closes the open menu.
key_override¶
Left/Right move between headers. With a menu open, they move into the neighboring menu when the open menu has no use for the key. Down, Enter and Space open the focused header's menu, and while a menu is open its keys go to it.
focus_ring_rect¶
Around the focused header.
struct MenuBarItem¶
One top-level entry of a Menu bar: the header title and the items of the menu that drops down from it.
Example:
auto menu = std::make_unique<ui::MenuBarControl>();
ui::MenuBarItem file{.text = "File", .items = {
{.text = "Open", .shortcut = ui::Ctrl + ui::Key::O, .on_click = [&] { open_file(); }},
ui::MenuItem::separator(),
{.text = "Exit", .on_click = [&] { quit(); }},
}};
menu->set_items({std::move(file)});
| Field | Type | Default | |
|---|---|---|---|
text |
std::string |
The header title shown in the bar. | |
items |
std::vector<MenuItem> |
The menu's entries; they may be toggles, radio items, separators and submenus. |
Tests¶
tests/test_menu_bar.cpp