Skip to content

ToggleSwitch

Header: winui/ui/input/ToggleSwitch.hpp. Spec: winui/ui/input/ToggleSwitch.hpp.

A boolean on/off switch: a pill-shaped track with a sliding thumb, an optional header above it and a label beside it that follows the state.

Use it for a setting that takes effect immediately (a CheckBox is for a choice confirmed later). A click released over the control, or Space on key-up while focused, flips checked() and fires on_checked_changed(). The control reserves the width of the wider of its two labels, so flipping it never reflows its row. With animations running, the thumb slides and grows on hover. Otherwise it snaps.

Example:

ui::Property<bool> wifi{true};

auto  stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto* sw    = stack->add(std::make_unique<ui::ToggleSwitchControl>(true));
sw->set_header("Wi-Fi");
sw->on_checked_changed([&](bool on) { wifi = on; });

// Or declaratively, kept in step with the Property:
stack->add(ui::ToggleSwitch{
    .header             = "Airplane mode",
    .checked            = ui::bind(wifi, [](bool w) { return !w; }),
    .checked_text       = "Flying",
    .unchecked_text     = "Grounded",
    .on_checked_changed = [&](bool on) { wifi = !on; },
}.build());
window.set_content(std::move(stack));

ui::ToggleSwitch

A boolean on/off switch, with a header above it and a label beside it that follows the state. Builds a ui::ToggleSwitchControl.

Example:

ui::Property<bool> wifi{true};

window.set_content(ui::ToggleSwitch{
    .header            = "Wi-Fi",
    .checked           = wifi,
    .checked_text      = "Connected",
    .unchecked_text    = "Off",
});

Every field, in the order a designated initializer names them:

ui::ToggleSwitch{
    .header, .checked, .checked_text, .unchecked_text, .checked_icon, .unchecked_icon, .style,
    .margin, .width, .height, .min_width, .min_height, .max_width, .max_height,
    .horizontal_alignment, .vertical_alignment, .visible, .tool_tip, .enabled, .accessibility,
    .on_checked_changed, .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
header ui::Binding<std::string> The label above the control. Empty draws none.
checked ui::TwoWayField<bool> {false} On or off. Seeding it raises no .on_checked_changed.
checked_text ui::Binding<std::optional<std::string>> The label beside the track while on; unset uses strings().toggle_switch.on ("On"). The same text in both checked_text and unchecked_text makes a label that does not change with the state. Both empty shows no label.
unchecked_text ui::Binding<std::optional<std::string>> The label while off; unset uses strings().toggle_switch.off ("Off").
checked_icon ui::Binding<ui::IconSource> An icon in place of the on state's text, which then only names that state for UI Automation.
unchecked_icon ui::Binding<ui::IconSource> The off state's icon, as checked_icon.
on_checked_changed ui::Callback<void(bool)> The new state, whenever it changes.
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::ToggleSwitch> 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

std::unique_ptr<Element> build()&&

Consumes the spec and returns the built ui::ToggleSwitchControl; the caller owns it.

ui::ToggleSwitchControl

Derives from ui::Element, ui::ToggleProvider.

A boolean on/off switch: a pill-shaped track with a sliding thumb, an optional header above it and a label beside it that follows the state.

Use it for a setting that takes effect immediately (a CheckBox is for a choice confirmed later). A click released over the control, or Space on key-up while focused, flips checked() and fires on_checked_changed(). The control reserves the width of the wider of its two labels, so flipping it never reflows its row. With animations running, the thumb slides and grows on hover. Otherwise it snaps.

Example:

ui::Property<bool> wifi{true};

auto  stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto* sw    = stack->add(std::make_unique<ui::ToggleSwitchControl>(true));
sw->set_header("Wi-Fi");
sw->on_checked_changed([&](bool on) { wifi = on; });

// Or declaratively, kept in step with the Property:
stack->add(ui::ToggleSwitch{
    .header             = "Airplane mode",
    .checked            = ui::bind(wifi, [](bool w) { return !w; }),
    .checked_text       = "Flying",
    .unchecked_text     = "Grounded",
    .on_checked_changed = [&](bool on) { wifi = !on; },
}.build());
window.set_content(std::move(stack));

ToggleSwitchControl

ToggleSwitchControl()

An enabled switch, off, with the "On"/"Off" labels and no header.

ToggleSwitchControl

ToggleSwitchControl(bool on)

A switch starting in on; the initial state never fires on_checked_changed() or animates.

const std::string& header() const

The header text; empty by default. See set_header().

set_header

void set_header(std::string header)

The label above the switch. Empty (the default) draws none. A change updates the layout.

checked_text

const std::string& checked_text() const

The label beside the track while on: the control's own, else strings().toggle_switch.on ("On").

set_checked_text

void set_checked_text(std::optional<std::string> text)

Replaces the on label, or with nullopt goes back to strings().toggle_switch.on. Updates the layout, since the control is as wide as its wider label.

unchecked_text

const std::string& unchecked_text() const

The label beside the track while off: the control's own, else strings().toggle_switch.off ("Off").

set_unchecked_text

void set_unchecked_text(std::optional<std::string> text)

Replaces the off label, or with nullopt goes back to strings().toggle_switch.off. Updates the layout, as set_checked_text() does.

checked_icon

const IconSource& checked_icon() const

An icon shown beside the track while on, in place of checked_text(). Unset (the default) shows the text; with an icon, checked_text() still names the state for UI Automation.

set_checked_icon

void set_checked_icon(IconSource icon)

Sets the on icon, or clears it with an empty source. Updates the layout.

unchecked_icon

const IconSource& unchecked_icon() const

An icon shown beside the track while off, in place of unchecked_text().

set_unchecked_icon

void set_unchecked_icon(IconSource icon)

Sets the off icon, or clears it with an empty source. Updates the layout.

checked

bool checked() const

Whether the switch is on. Off by default.

set_checked

void set_checked(bool on)

Changes the state and redraws. Does nothing when unchanged. Otherwise fires on_checked_changed(), also when called from code.

on_checked_changed

void on_checked_changed(Callback<void(bool)> handler)

Invoked with the new state whenever checked() changes (click, Space, UI Automation or set_checked()). Replaces any previous handler.

parallel_measure_safe

bool parallel_measure_safe() const override

True: measuring reads only the switch's own state, the measurer and the theme.

focused

bool focused() const

Whether the switch holds keyboard focus (and so draws its focus ring around the track).

focusable

bool focusable() const override

Only an enabled switch joins the tab ring.

is_pressed

bool is_pressed() const

True while the pointer is held down over the switch or Space is held; never while disabled.

automation_role

AutomationRole automation_role() const override

Role Button; UI Automation has no dedicated toggle switch role.

automation_name_fallback

std::string automation_name_fallback() const override

The header when there is one, since it names the switch; otherwise the state's text.

visual_state

ButtonState visual_state() const

Disabled, Pressed, Hover or Normal, in that priority; picks the track and thumb colors.

measure_override

Size measure_override(Size available, const MeasureContext& context) const override

Implement these instead of measure() and arrange(). The base class has already applied margin, size limits and alignment by the time these run. measure_override() is pure (every concrete Element must size itself); arrange_override() defaults to a no-op since most leaves need nothing beyond having bounds() set, which the base already did.

arrange_override

void arrange_override(const Rect& final_rect, const ArrangeContext& context) override

Position children within the rect arrange() settled on (also in bounds(), window DIPs). Default: nothing, for a leaf.

pointer_override

bool pointer_override(const PointerEvent& e) override

A pointer event for this element: the element under the pointer, one inside it that did not handle the event, or the element holding the capture. The position is in layout coordinates (handle_pointer()). Return true when the event did something here, and it goes no further up; a handled press also captures the pointer until its release. A control that acts on a press (a click, a toggle, a caret placed, a drag started) returns true for the press and its release, so a list row or a card around it does not act on the same click. Default: false.

render_override

void render_override(Renderer& renderer, const RenderContext& context) const override

What the control draws, in layout coordinates; render() has already applied the visual layer. A container draws its children through their render(), never their render_override().

tick_override

void tick_override(Duration delta) override

Advances the knob's slide and resize, and the Aero theme's sliding knob, which is redrawn every frame because its fill is a gradient.

enabled_changed

void enabled_changed() override

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

void focus_changed(bool focused) override

Keyboard focus arrived (true) or left (false); is_focused() already says so. The element is redrawn either way. No-op by default.

key_override

bool key_override(const KeyEvent& e) override

A key for this element: the focused one, or one inside it that did not handle it. Return true when the key did something here, and it goes no further; false lets it bubble on to the parent and then to the shortcuts, a dialog's default button and arrow-key focus moves. Default: false.

focus_ring_rect

Rect focus_ring_rect(const Theme& theme) const override

Around switch and label, or the switch alone in Metro's era.

focus_ring_corner_radius

float focus_ring_corner_radius(const Theme& theme) const override

The ring's corner radius: the theme's control radius by default.

Tests

  • tests/test_toggle_switch.cpp