Button¶
Header: winui/ui/input/Button.hpp. Spec: winui/ui/input/Button.hpp.
A push button, clicked by mouse (press and release inside), Enter/Space while focused, or UI Automation's Invoke.
Shows either a plain text() label, whose color tracks hover/press/disabled automatically, or arbitrary set_content() (an icon plus text, say). A click runs, in order: the attached flyout() toggles open or closed, the protected on_activated() hook, the on_click() handler, the command (set_command()), then navigate_uri() opens if set. The look comes from variant() and the theme, with per-instance background / foreground overrides. Not final: ToggleButton and DropdownButton derive from it through the protected hooks.
Example:
ui::Property<int> clicks{0};
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto* label = stack->add(std::make_unique<ui::TextControl>("Clicks: 0"));
ui::ButtonControl* go = stack->add(std::make_unique<ui::ButtonControl>("Click me"));
go->set_variant(ui::ButtonVariant::Accent);
go->on_click([label, n = 0]() mutable { label->set_text(std::format("Clicks: {}", ++n)); });
window.set_content(std::move(stack));
// Declaratively:
window.set_content(ui::VerticalStack{
.spacing = 12,
.content = {
ui::Text{.text = ui::bind(clicks, [](int n) { return std::format("Clicks: {}", n); })},
ui::Button{.text = "Click me", .variant = ui::ButtonVariant::Accent,
.on_click = [&] { ++clicks; }},
ui::Button{.text = "Docs", .navigate_uri = "https://example.com/docs",
.variant = ui::ButtonVariant::Hyperlink},
},
});
ui::Button¶
A push button. Builds a ui::ButtonControl.
.text is the label; .content replaces it with any subtree (an icon beside text, say). A click runs .on_click, then .command, then opens .navigate_uri. With a .command the button is enabled only while both .enabled and the command's enabled allow it, and an unset .tool_tip falls back to the command's description.
Example:
ui::Property<int> clicks{0};
ui::Property<bool> dirty{false};
ui::Command save{.enabled = dirty, .execute = [&] { save_document(); }};
window.set_content(ui::VerticalStack{
.spacing = 8,
.content = {
ui::Button{.text = "Click me", .on_click = [&] { ++clicks; }},
ui::Button{.text = "Save", .command = save}, // enablement comes with the command
ui::Button{.variant = ui::ButtonVariant::Accent,
.content = ui::HorizontalStack{
.spacing = 8,
.content = {ui::Icon{.source = ui::glyphs::More}, ui::Text{.text = "More"}},
}},
ui::Button{.text = "Docs",
.navigate_uri = "https://example.com/docs",
.variant = ui::ButtonVariant::Hyperlink},
},
});
Every field, in the order a designated initializer names them:
ui::Button{
.text, .navigate_uri, .variant, .padding, .background, .foreground, .style, .margin,
.width, .height, .min_width, .min_height, .max_width, .max_height, .horizontal_alignment,
.vertical_alignment, .visible, .tool_tip, .enabled, .accessibility, .command,
.command_parameter, .on_click, .on_context_requested, .on_pointer_entered,
.on_pointer_exited, .on_got_focus, .on_lost_focus, .on_key_down, .on_wheel, .control,
.content
}
Fields, in declaration order (designators must follow it):
| Field | Type | Default | |
|---|---|---|---|
text |
ui::Binding<std::string> |
The label, ignored while .content is set. |
|
navigate_uri |
ui::Binding<std::string> |
A URI (ui::ButtonControl::set_navigate_uri()), opened with its default handler on every click, after .on_click and .command. |
|
variant |
ui::Binding<ui::ButtonVariant> |
{ui::ButtonVariant::Neutral} |
Neutral, Accent, Subtle or Hyperlink (ui::ButtonControl::set_variant()). |
padding |
ui::Binding<std::optional<ui::Thickness>> |
The padding around the text or content; nullopt keeps the theme's button padding. | |
background |
ui::Binding<ui::Paint> |
The fill at rest (ui::ButtonControl::set_background()): a fixed color or a theme resource, which follows theme switches. Unset keeps the style's. | |
foreground |
ui::Binding<ui::Paint> |
The label color at rest: a fixed color or a theme resource, which follows theme switches. Unset keeps the style's. | |
command |
ui::CommandRef |
A shared, named action (ui/Command.hpp), run after .on_click with .command_parameter. The button is enabled only while both the command and .enabled allow it, and follows the command as it changes. Written as the command itself: .command = save. |
|
command_parameter |
std::string |
Handed to .command when it runs. |
|
on_click |
ui::Action |
A completed press and release inside, or Enter/Space while focused. Runs before .command. |
|
on_context_requested |
ui::Callback<ui::Handling(std::optional<ui::Point>)> |
A context menu was requested (Element::on_context_requested()). Returning nothing, or ui::Handled, means a menu or flyout was shown; ui::NotHandled lets the request go on. | |
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::Button> |
Out: the handle that receives the built control. | |
content |
ui::Spec |
Arbitrary content instead of a plain label, such as an icon plus text. When set it replaces .text for layout and rendering entirely. |
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::ButtonControl; the caller owns it.
ui::ButtonControl¶
Derives from ui::Element, ui::InvokeProvider.
A push button, clicked by mouse (press and release inside), Enter/Space while focused, or UI Automation's Invoke.
Shows either a plain text() label, whose color tracks hover/press/disabled automatically, or arbitrary set_content() (an icon plus text, say). A click runs, in order: the attached flyout() toggles open or closed, the protected on_activated() hook, the on_click() handler, the command (set_command()), then navigate_uri() opens if set. The look comes from variant() and the theme, with per-instance background / foreground overrides. Not final: ToggleButton and DropdownButton derive from it through the protected hooks.
Example:
ui::Property<int> clicks{0};
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto* label = stack->add(std::make_unique<ui::TextControl>("Clicks: 0"));
ui::ButtonControl* go = stack->add(std::make_unique<ui::ButtonControl>("Click me"));
go->set_variant(ui::ButtonVariant::Accent);
go->on_click([label, n = 0]() mutable { label->set_text(std::format("Clicks: {}", ++n)); });
window.set_content(std::move(stack));
// Declaratively:
window.set_content(ui::VerticalStack{
.spacing = 12,
.content = {
ui::Text{.text = ui::bind(clicks, [](int n) { return std::format("Clicks: {}", n); })},
ui::Button{.text = "Click me", .variant = ui::ButtonVariant::Accent,
.on_click = [&] { ++clicks; }},
ui::Button{.text = "Docs", .navigate_uri = "https://example.com/docs",
.variant = ui::ButtonVariant::Hyperlink},
},
});
ButtonControl¶
A button with an empty label.
ButtonControl¶
A button labeled text (UTF-8).
text¶
The plain-text label (UTF-8), drawn centered in the current state's text color, so it tracks hover/press/disabled automatically. Also the automation name fallback. Ignored once content() is set.
set_text¶
Sets the label (UTF-8). The button is measured again and redrawn.
set_content¶
Any Element as content in place of the text label, e.g. a StackControl of an icon and a TextControl. Takes ownership, destroys any previous content, and returns a non-owning observer, as StackControl::add() does. When set, content replaces text() for layout and rendering. The caller picks the content's colors, because the state's foreground color only applies to text(). content must not be null. The button is measured again and redrawn.
content¶
The content set with set_content() as a non-owning observer, or nullptr for a text button.
variant¶
The token set the button draws with; ButtonVariant::Neutral by default.
set_variant¶
Redraws only; every style has the same metrics.
set_padding¶
The padding around the text or content, DIPs; nullopt (the default) keeps the theme's button padding (11,5,11,6 in Fluent). The theme padding clips the icon of a small fixed-size icon button, such as a 40x40 title-bar back button, so pass Thickness there. The button is measured again and redrawn.
on_click¶
Invoked on a completed click (mouse press and release inside, Enter/Space while focused, or UI Automation's Invoke) while enabled, before the command runs. The handler may destroy the button. Replaces any previous handler.
set_flyout¶
A click opens flyout (and a second one closes it). Set for you when a Button becomes a Flyout's anchor; the Flyout owns the Button, so the pointer never outlives what it points at.
flyout¶
The flyout a click opens, or nullptr; owned by that flyout, not the button.
set_command¶
Runs command with parameter on every click, after the click handler. The button is enabled only while the command is, and follows it as it changes; without a tooltip of its own it shows the command's description. Either may go first: a destroyed command leaves the button as if it had none. An empty ref clears it. A plain Button takes no label or icon from the command.
command¶
The command set with set_command(), or nullptr (none, or destroyed); not owned.
command_parameter¶
The parameter passed to the command's execute.
set_navigate_uri¶
Every click opens uri in its default handler, after the click handler and the command. Meant for ButtonVariant::Hyperlink, but works with any variant. Empty (the default) opens nothing.
navigate_uri¶
The URI a click opens, or empty.
parallel_measure_safe¶
True for a text button. A button with content() answers false, because it measures the content without listing it in for_each_child() (the two share one tab stop).
focused¶
Whether the button holds keyboard focus.
focusable¶
A disabled button is skipped in the tab order.
set_background¶
The button's fill at rest. Hover, press and the disabled state still use the theme's fills. nullopt (the default) keeps the style's. Clears a theme-resource background. Redraws.
set_background¶
The button's fill at rest as a theme resource, re-read under every theme. Clears a fixed background. Redraws.
set_foreground¶
The label color in the Normal state; nullopt (the default) keeps the style's. Clears a theme-resource foreground. Redraws.
set_foreground¶
The label color in the Normal state as a theme resource, re-read under every theme. It wins over a fixed foreground color. Redraws.
handle_global_shortcut¶
The command's shortcut, app-wide: clicks the button while it is enabled. Nothing without a command or with an empty key combination.
tool_tip¶
The tooltip set on the button, else its command's description.
is_pressed¶
True while pressed by the pointer (inside) or by a held Enter/Space.
cursor_at¶
The Hand cursor for ButtonVariant::Hyperlink; no opinion otherwise.
automation_role¶
Hyperlink for ButtonVariant::Hyperlink, Button otherwise.
automation_name_fallback¶
text(), used when Accessibility::name is not set. Empty for a content button: name it through set_accessibility().
style_visual¶
variant's colors in state: the Neutral/Accent/Subtle/Hyperlink table every Button draws from, and every button-shaped part of another control (SplitButton's two halves).
visual_state¶
The state the button currently draws: Disabled, Pressed, Hover or Normal, derived from the enabled flag and the pointer/key input.
measure_override¶
Desired size = measured text (or content()'s own measure()) + padding, clamped to available; the padding is cached for arrange_override().
arrange_override¶
Places content() inside the padding measure_override() last used, centered.
pointer_override¶
Tracks hover and press; a release inside after a press inside is a click.
render_override¶
Emits: fill_rounded_rect (background) + draw_text (or the content), plus a stroke_rounded_rect_gradient elevation border for Neutral/Accent's Normal/Hover states in Fluent. Every other state/style/theme has no border command at all. A focus ring stroke is added only while focused and enabled.
tick_override¶
Forwards the tick to content(), so animated content (an AnimatedIcon, a ProgressRing) keeps moving inside a button.
on_activated¶
Runs on every completed click (pointer, Enter/Space, UI Automation's Invoke) before the click handler. ToggleButton flips its state here.
current_visual¶
The colors the current state draws with; variant()'s set by default.
activate¶
Performs a click as if the user completed one, when enabled: the flyout toggle, on_activated(), on_click, the command, then navigate_uri().
set_trailing_reserve¶
Width kept free at the right of the content, inside the padding, for a trailing part the derived control draws itself: DropdownButton's chevron column. The content (or the label) is laid out and centered in what is left.
trailing_reserve¶
The width set by set_trailing_reserve(), DIPs; 0 by default.
padding¶
The padding measure_override() last used (padding override or the theme's).
focus_ring_corner_radius¶
The ring follows the button's own corner radius.
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 Window. Losing focus cancels an Enter/Space press in progress. Redraws.
key_override¶
Enter or Space down presses the button, and up (while still focused) clicks it.
automation_providers¶
Lists what a client can do with this node: set out's field for each pattern this control implements now to this, through the provider interface it inherits privately (ui/Automation.hpp). A pattern that only applies in some states is an if. out arrives empty, and nothing is listed by default.
Example:
class Chip final : public ui::Element, private ui::InvokeProvider
{
protected:
void automation_providers(ui::AutomationProviders& out) override
{
out.invoke = this;
}
private:
bool invoke() override
{
on_click_();
return true;
}
ui::Callback<void()> on_click_;
};
Enumerations¶
ButtonVariant¶
Which of Theme's per-control token sets a Button draws with.
Example:
auto save = std::make_unique<ui::ButtonControl>("Save");
save->set_variant(ui::ButtonVariant::Accent); // the one high-emphasis action in the view
| Value | |
|---|---|
Neutral |
The default look: a subtle translucent fill with an elevation border. |
Accent |
The filled, high-emphasis look, in the accent color. Use at most one per view. |
Subtle |
Borderless, showing only a wash on hover/press: toolbar, command-bar and menu buttons. |
Hyperlink |
A hyperlink button: Subtle's backgrounds under link-colored text, a Hand cursor, reported to UI Automation as a hyperlink, and opening ButtonControl::navigate_uri() when clicked. |
Example¶
window.set_content(ui::VerticalStack{
.spacing = 12,
.horizontal_alignment = ui::Alignment::Center,
.vertical_alignment = ui::Alignment::Center,
.content =
{
ui::Text{.text = ui::bind(clicks, [](int n) { return std::format("Clicks: {}", n); })},
ui::Button{.text = "Click me", .on_click = [&] { ++clicks; }},
ui::Button{.text = "Reset", .on_click = [&] { clicks = 0; }},
},
});
Tests¶
tests/test_button.cpp