MenuFlyout¶
Headers: winui/ui/menus/MenuFlyout.hpp, winui/ui/menus/MenuItem.hpp. Spec: winui/ui/menus/MenuFlyout.hpp.
A list of commands in a popup: hung from an anchor element (a click on a Button anchor toggles it) or opened at a point with show_at() for a context menu.
The flyout sits in the tree in its anchor's place and owns the anchor (set_anchor()); the menu floats in the Window's overlay layer, cascades into submenus, and closes on invoke, light dismiss or Escape. Placement and show()/hide() come from FlyoutBase. The declarative form is ui::MenuFlyout.
Example:
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto menu = std::make_unique<ui::MenuFlyoutControl>();
ui::ButtonControl* options = menu->set_anchor(std::make_unique<ui::ButtonControl>("Options"));
menu->set_items({
{.text = "Open", .on_click = [&] { open_file(); }},
ui::MenuItem::sub_item("Send to", {{.text = "Bluetooth"}, {.text = "Desktop"}}),
ui::MenuItem::separator(),
ui::MenuItem::toggle("Repeat", /*checked=*/true),
});
ui::MenuFlyoutControl* flyout = stack->add(std::move(menu)); // the stack owns flyout and anchor
// Also a context menu: a right-click on the anchor opens it at the pointer.
options->on_context_requested([flyout, options](std::optional<ui::Point> at) {
flyout->show_at(at.value_or(options->bounds().center()));
});
// Or declaratively:
stack->add(ui::MenuFlyout{
.items = {{.text = "Reset"}, ui::MenuItem::separator(),
ui::MenuItem::toggle("Repeat", true)},
.anchor = ui::Button{.text = "Options"},
}.build());
ui::MenuFlyout¶
A list of commands in a popup, hung from an anchor. Builds a ui::MenuFlyoutControl. A Button anchor opens it on click. To use it as a context menu, call show_at(point) on its .control handle from an on_context_requested handler. The built element takes the anchor's place in the tree, and the menu floats in the window's popup layer.
Example:
bool repeat = true;
window.set_content(ui::MenuFlyout{
.items = { { .text = "Reset", .on_click = [&] { repeat = true; } },
ui::MenuItem::separator(),
ui::MenuItem::toggle("Repeat", true, [&](bool on) { repeat = on; }),
ui::MenuItem::sub_item("Sort by", {
ui::MenuItem::radio("Name", true),
ui::MenuItem::radio("Date") }) },
.anchor = ui::Button{ .text = "Options" },
});
Every field, in the order a designated initializer names them:
ui::MenuFlyout{
.items, .is_open, .placement, .placement_alignment, .light_dismiss, .style, .margin,
.width, .height, .min_width, .min_height, .max_width, .max_height, .horizontal_alignment,
.vertical_alignment, .visible, .tool_tip, .accessibility, .on_opened, .on_closing,
.on_closed, .on_pointer_entered, .on_pointer_exited, .on_got_focus, .on_lost_focus,
.on_key_down, .on_wheel, .control, .anchor
}
Fields, in declaration order (designators must follow it):
| Field | Type | Default | |
|---|---|---|---|
items |
ui::Binding<std::vector<ui::MenuItem>> |
The menu's entries: items, toggles, radio items, separators and submenus. | |
is_open |
ui::TwoWayField<bool> |
{false} |
Whether the flyout is showing. Two-way with ui::bind(): closing it writes false, and a false that on_closing cancels is put back to true. |
placement |
ui::Binding<ui::PopupPlacement> |
{ui::PopupPlacement::Below} |
Which side of the anchor it opens on, centered on the anchor; PopupPlacement::Below by default. |
placement_alignment |
ui::Binding<ui::PopupPlacementAlignment> |
{ui::PopupPlacementAlignment::Center} |
How the menu lines up with the anchor along that side. |
light_dismiss |
ui::Binding<bool> |
{true} |
Whether an outside click or Escape closes it; true by default. Off, it stays open until is_open turns false, hide() runs or a command in it runs. |
on_opened |
ui::Action |
The flyout is showing. A two-way is_open has been written by then. |
|
on_closing |
ui::Callback<ui::Decision(const ui::PopupClosingArgs&)> |
Gets why it is about to close; return ui::Cancel to keep it open. | |
on_closed |
ui::Callback<void(ui::PopupCloseReason)> |
Gets why it closed, once the close fade has finished. A two-way is_open has been written by then. |
|
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::MenuFlyout> |
Out: the handle that receives the built control. | |
anchor |
ui::Spec |
The in-tree element the menu hangs from. |
Plus the common fields style, margin, width, height, min_width, min_height, max_width, max_height, horizontal_alignment, vertical_alignment, visible, tool_tip, accessibility every spec carries (conventions).
build¶
Consumes the spec and returns the built ui::MenuFlyoutControl; the caller owns it.
ui::MenuFlyoutControl¶
Derives from ui::FlyoutBase.
A list of commands in a popup: hung from an anchor element (a click on a Button anchor toggles it) or opened at a point with show_at() for a context menu.
The flyout sits in the tree in its anchor's place and owns the anchor (set_anchor()); the menu floats in the Window's overlay layer, cascades into submenus, and closes on invoke, light dismiss or Escape. Placement and show()/hide() come from FlyoutBase. The declarative form is ui::MenuFlyout.
Example:
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto menu = std::make_unique<ui::MenuFlyoutControl>();
ui::ButtonControl* options = menu->set_anchor(std::make_unique<ui::ButtonControl>("Options"));
menu->set_items({
{.text = "Open", .on_click = [&] { open_file(); }},
ui::MenuItem::sub_item("Send to", {{.text = "Bluetooth"}, {.text = "Desktop"}}),
ui::MenuItem::separator(),
ui::MenuItem::toggle("Repeat", /*checked=*/true),
});
ui::MenuFlyoutControl* flyout = stack->add(std::move(menu)); // the stack owns flyout and anchor
// Also a context menu: a right-click on the anchor opens it at the pointer.
options->on_context_requested([flyout, options](std::optional<ui::Point> at) {
flyout->show_at(at.value_or(options->bounds().center()));
});
// Or declaratively:
stack->add(ui::MenuFlyout{
.items = {{.text = "Reset"}, ui::MenuItem::separator(),
ui::MenuItem::toggle("Repeat", true)},
.anchor = ui::Button{.text = "Options"},
}.build());
MenuFlyoutControl¶
An empty menu with no anchor.
~MenuFlyoutControl¶
items¶
The items. Toggle and radio items' is_checked() track clicks made in the menu and the properties their checked is bound to.
set_items¶
Replaces the items; an open menu closes first. The menu redraws when the command of any item changes, submenus included, and follows every bound checked while it holds the items.
handle_global_shortcut¶
The shortcut of an item, in the menu or in a submenu, invokes that item whether the menu is open or not.
transition_opacity¶
The menu's opacity during its open and close transitions. A menu that slides in (Theme::popup_motion) opens fully opaque, and one that fades in blends in. Closing fades it out over the theme's close duration.
tick_override¶
Picks up the theme's menu open and close durations, then advances the flyout's transition.
struct MenuItem¶
One entry of a menu. The plain case is {.text = "Open", .on_click = [&] { open(); }}; the factories below spell out the other kinds. The fields follow the spec order: what the item shows, then the command and the handlers, then the submenu.
on_click is a copyable std::function, since items live in a std::vector built from a brace list, which only copies. A handler must not capture a move-only value.
Items are plain values: the menu that holds them (MenuFlyout, MenuBar, a context menu, the window's system menu) keeps its own copy, so a toggle's or radio item's current state is read back from that owner's items() with is_checked(), or followed through on_checked_changed. text, icon, shortcut, enabled and checked take a value or a property: bound to a property, every menu built from the item follows it, and a click writes a two-way checked.
Radio items form a group in one of two ways. Bound with .checked = ui::selects(choice, value), the items bound to the same property are the group, wherever they sit in the menu. Unbound, a run of radio items next to each other in one list is a group, so a separator or any other item between two runs starts a new one.
Example:
enum class Size { Small, Large };
ui::Property<std::string> note;
ui::Property<bool> word_wrap{true};
ui::Property<Size> size{Size::Small};
std::vector<ui::MenuItem> items{
{.text = "Copy", .icon = ui::Symbol::Copy, .shortcut = ui::Ctrl + ui::Key::C,
.on_click = [&] { ui::clipboard().set_text(note.get()); }},
ui::MenuItem::separator(),
ui::MenuItem::toggle("Word wrap", ui::bind(word_wrap)),
ui::MenuItem::radio("Small", ui::selects(size, Size::Small)),
ui::MenuItem::radio("Large", ui::selects(size, Size::Large)),
ui::MenuItem::sub_item("Send to", {{.text = "Desktop"}, {.text = "Mail recipient"}}),
ui::MenuItem::split("Paste", [&] { note = ui::clipboard().text().value_or(""); }, {{.text = "Paste as text"}}),
};
auto flyout = std::make_unique<ui::MenuFlyoutControl>();
flyout->set_items(std::move(items)); // or a ui::MenuBarItem's items
text¶
The text. Falls back to the command's text.
kind¶
Which kind of menu item this entry is; the factories below set it. Defaults to a plain Item.
icon¶
The icon, drawn 16x16. Falls back to the command's icon.
shortcut¶
The keyboard shortcut. It fires app-wide and invokes the item, and its text is shown in the shortcut column. Falls back to the command's.
shortcut_text¶
Shown instead of the shortcut's own text.
enabled¶
A disabled item is drawn dimmed, skipped by the keyboard, and its shortcut does not fire; a disabled command disables it too (is_enabled()).
checked¶
The checked state of a Toggle or Radio item. Two-way from a ui::Property<bool> or ui::selects(): the item follows the property in every menu built from it, and a click writes the new state back before on_checked_changed runs.
command¶
The command, run after on_click with command_parameter. The command also supplies the text, icon and shortcut the item does not set, and disables the item while it is disabled.
command_parameter¶
Passed to the command's execute.
on_click¶
Runs on a click, after the menu has closed, and after a toggle or radio item has changed.
on_checked_changed¶
Called when a Toggle or Radio item's checked changes through the menu (a click, a key, a shortcut, or a group-mate being checked), after a two-way checked has been written. A change of the bound property does not call it.
items¶
The submenu of a SubItem or Split item.
toggle¶
static MenuItem toggle(std::string text, Binding<bool> checked = false, std::function<void(bool)> on_checked_changed = {})
A toggle item: a click flips checked (drawn as a check mark) and reports the new state to on_checked_changed before on_click runs.
radio¶
static MenuItem radio(std::string text, Binding<bool> checked = false, std::function<void(bool)> on_checked_changed = {})
A radio item: a click checks it and unchecks the rest of its group (see above); clicking a checked one leaves it checked. Each item unchecked this way writes false into its own two-way checked.
separator¶
A 1px line between groups of items; never focused or invoked.
sub_item¶
A submenu item: invoking it (click, Right, Enter, Space, or a 400 ms hover) opens items as a cascading submenu beside it. It has no on_click of its own.
split¶
static MenuItem split(std::string text, std::function<void()> on_click, std::vector<MenuItem> items)
A split item: the item runs on_click, and the chevron button at its right edge opens items as a submenu.
from_command¶
An item built entirely from command: its text, icon, shortcut and action.
display_text¶
text, or the command's text when text is empty.
display_icon¶
icon, or the command's icon when icon is empty (an empty IconSource when neither is set).
display_shortcut¶
shortcut, or the command's shortcut when it is empty.
display_shortcut_text¶
What the shortcut column shows: shortcut_text if set, else display_shortcut()'s "Ctrl+S" text (empty when there is no shortcut).
is_checked¶
The check state: checked's value. In a menu's items() it is the current state, whether a click or the bound property set it last.
is_enabled¶
Enabled, and its command (if any) is too.
focusable¶
Can hold focus in a menu: not a separator, and enabled.
has_submenu¶
Opens a submenu (a SubItem, or a Split item with items).
Enumerations¶
MenuItemKind¶
Which kind of menu item a MenuItem stands for.
| Value | |
|---|---|
Item |
a plain command |
Toggle |
a check item |
Radio |
one of a group of exclusive items |
Separator |
a line between groups |
SubItem |
items open as a submenu |
Split |
invokes like an Item, and items open from its chevron |
Tests¶
tests/test_menu_flyout.cpp