ui::Element¶
Header: winui/ui/Element.hpp.
The base of every node in the UI tree: a control, a panel, a window's content.
Implement it: derive a control from it and override the hooks it needs. You call a verb (measure(), arrange(), render(), tick(), handle_key()) and override its <verb>_override().
Every node measures, arranges, renders, takes input and may take focus. The public verbs apply what is common to all elements (margin, explicit and min/max size, alignment, visibility, requested theme, the visual transform and opacity, animations, the focus ring) and then call the protected *_override() hook a control implements. measure_override() and render_override() are required; the rest default to doing nothing. Pointer and key input is routed: the window offers an event to the element under the pointer, or the focused one, and then to each parent until a hook returns true. Containers own their children through std::unique_ptr and list them through for_each_child(); every tree-wide walk (tick(), clear_dirty(), the focus ring, popups, automation) reaches the children through that, so a container overrides it once instead of forwarding each walk. Setters invalidate through mark_dirty(), which bubbles to the root so the window knows to lay out or redraw.
Elements are rendered as a platform-independent command stream (Renderer), so a control can be measured, arranged, drawn and driven with input without a window or a GPU.
Example:
// A minimal custom control: a color swatch that is clicked with the mouse or Space.
class SwatchControl final : public ui::Element
{
public:
explicit SwatchControl(ui::Color color) : color_(color) {}
void set_color(ui::Color color)
{
color_ = color;
mark_dirty(ui::DirtyFlags::Visual); // redraw, no relayout
}
void on_click(ui::Callback<void()> handler) { on_click_ = std::move(handler); }
bool focusable() const override { return true; }
protected:
bool key_override(const ui::KeyEvent& e) override
{
if (e.key != ui::Key::Space)
{
return false; // to the parents, the shortcuts and focus moves
}
if (e.action == ui::KeyAction::Up && on_click_)
{
on_click_();
}
return true;
}
ui::Size measure_override(ui::Size, const ui::MeasureContext&) const override
{
return {32.0f, 32.0f};
}
void render_override(ui::Renderer& r, const ui::RenderContext&) const override
{
r.fill_rounded_rect(bounds(), 4.0f, color_);
}
bool pointer_override(const ui::PointerEvent& e) override
{
if (e.action == ui::PointerAction::Down)
{
pressed_ = true; // the press holds the pointer until its release
return true;
}
if (e.action == ui::PointerAction::Up)
{
// A click is a press and a release, both on the swatch.
const bool was_pressed = std::exchange(pressed_, false);
if (was_pressed && bounds().contains(e.position) && on_click_)
{
on_click_();
}
return true;
}
return false;
}
private:
ui::Color color_;
bool pressed_ = false;
ui::Callback<void()> on_click_;
};
// In a tree: the panel owns it, the caller keeps an observer.
auto* panel = window.set_content(
std::make_unique<ui::StackControl>(ui::Orientation::Vertical));
SwatchControl* swatch = panel->add(std::make_unique<SwatchControl>(ui::Color::from_argb(0xFF0078D4)));
swatch->set_margin(ui::Thickness{4.0f});
swatch->set_horizontal_alignment(ui::Alignment::Center);
swatch->set_tool_tip("Accent blue");
swatch->on_click([] { /* ... */ });
~Element¶
Leaves keyboard focus bookkeeping consistent (has_focus_within()) when the focused element goes away with its subtree.
Element¶
Not copyable: a control has a parent link, a stable VisualId and its subscriptions, none of which can be duplicated. A derived control is movable until it is parented (the move operations are protected); in a tree it is owned through unique_ptr and never moved.
operator=¶
visual_id¶
Assigned once at construction and never reused; the stable key other systems (VisualTree) use instead of this object's address.
margin¶
DIPs of empty space kept outside the element on each side, taken from the rect the parent offers before measuring and arranging. Zero by default.
set_margin¶
Changes the margin; invalidates layout.
width¶
The explicit width, nullopt for Auto. Measure and arrange both fold it into the min/max range. The size wins over the content and min_width() wins over the size. A Stretch-aligned element that ends up narrower than its slot is centered in it.
set_width¶
Sets the explicit width in DIPs (negative clamps to 0), or nullopt for Auto; invalidates layout.
height¶
The explicit height, as width() is the explicit width; nullopt for Auto.
set_height¶
Sets the explicit height in DIPs (negative clamps to 0), or nullopt for Auto; invalidates layout.
max_width¶
The maximum width in DIPs; infinity (the default) is no limit. min_width() wins over it.
set_max_width¶
Sets the maximum width in DIPs (negative clamps to 0), or nullopt for no limit; invalidates layout.
max_height¶
The maximum height, as max_width() is the maximum width; infinity for no limit.
set_max_height¶
Sets the maximum height in DIPs (negative clamps to 0), or nullopt for no limit; invalidates layout.
min_width¶
The minimum width in DIPs; 0 (the default) is none. Wins over width() and max_width().
set_min_width¶
Sets the minimum width in DIPs (negative clamps to 0), or nullopt for none; invalidates layout.
min_height¶
The minimum height in DIPs; 0 (the default) is none. Wins over height() and max_height().
set_min_height¶
Sets the minimum height in DIPs (negative clamps to 0), or nullopt for none; invalidates layout.
horizontal_alignment¶
Where the element sits across its slot's width; Alignment::Stretch by default.
set_horizontal_alignment¶
Changes the horizontal alignment; invalidates layout.
vertical_alignment¶
Where the element sits down its slot's height; Alignment::Stretch by default.
set_vertical_alignment¶
Changes the vertical alignment; invalidates layout.
visibility¶
Whether the element is shown. Collapsing marks layout dirty; see Visibility.
set_visibility¶
Shows or collapses the element and its subtree; invalidates layout when it changes.
is_effectively_visible¶
Whether this element and every ancestor is Visible. The tab ring and the popup layer check this, since a Collapsed container's descendants still answer collect_focusables().
enabled¶
This element's own enabled state, as set_enabled() left it; true by default. Whether it takes input also depends on its ancestors: see is_enabled().
set_enabled¶
Enables or disables this element and everything inside it. A disabled element draws its disabled look, takes no pointer or keyboard input and leaves the tab ring; so does every element inside a disabled one, whatever its own enabled(). Redraws.
is_enabled¶
Whether the element takes input right now: its own enabled(), a control's own condition (a Button's command), and every ancestor's. What a control reads to draw its disabled look.
focus¶
Move keyboard focus here, or, when this element takes no focus itself (a panel, a view's host), to the first focus stop inside it. False, and focus stays where it was, when it is in no window or neither is a stop right now (hidden, disabled). Window::push_focus() also remembers what had focus, to give it back.
Example:
ui::Control<ui::TextBox> name;
// ...
name->focus(); // after showing a form, so typing goes into the first field
window¶
The window whose tree this element is in, or nullptr while it is in none (built but not yet placed, or taken out).
requested_theme¶
Measure and draw this subtree in the light or dark version of the theme it is handed (Theme::with_dark_mode), and so with every color the subtree reads from the theme (background resources, text, control fills). No effect where the theme has no other mode (the fixed-palette eras, high contrast). Tokens the app edited on the handed theme that are not colors (corner radii, type, motion, focus-ring widths) stay as edited; colors are the other mode's. A popup opened from inside is drawn by the window in the window's theme.
set_requested_theme¶
Changes the requested theme; invalidates layout and redraws the subtree.
is_hit_test_visible¶
Whether the element takes pointer input. False keeps pointer input from reaching this element and everything under it. It still draws and still takes keyboard focus. Frame turns it off on pages while a navigation transition plays.
set_hit_test_visible¶
Turns pointer input to this subtree on or off; takes effect with the next event.
opacity¶
The opacity, 0..1. The value set, not the one drawn mid-animation (current_opacity()).
set_opacity¶
Sets the opacity (1 by default), animating when an opacity transition is set. Only redraws; the layout stays as it is.
translation¶
An offset from the layout position that does not affect layout.
set_translation¶
Sets the translation in DIPs, animating when a translation transition is set.
rotation¶
The rotation in clockwise degrees about center_point().
set_rotation¶
Sets the rotation in degrees, animating when a rotation transition is set.
scale¶
The scale factors, about center_point().
set_scale¶
Sets the scale factors ({1, 1, 1} by default), animating when a scale transition is set.
center_point¶
The pivot for rotation and scale, relative to the element's top-left corner.
set_center_point¶
Sets the pivot for rotation() and scale(), DIPs from the top-left corner.
current_opacity¶
The values as drawn this frame: the property, or a running animation or transition's value. An ExpressionAnimation that reads another element should read these.
current_translation¶
As current_opacity(), for translation().
current_rotation¶
As current_opacity(), for rotation().
current_scale¶
As current_opacity(), for scale().
current_center_point¶
As current_opacity(), for center_point().
set_opacity_transition¶
An implicit transition for set_opacity(). While one is set, every later change to that property animates from the value on screen (see ScalarTransition and Vector3Transition in Animation.hpp). nullopt (the default) makes changes immediate.
set_rotation_transition¶
As set_opacity_transition(), for set_rotation().
set_translation_transition¶
As set_opacity_transition(), for set_translation().
set_scale_transition¶
As set_opacity_transition(), for set_scale().
opacity_transition¶
The transition set_opacity() animates through, or nullopt for immediate changes.
rotation_transition¶
The transition set_rotation() animates through, or nullopt.
translation_transition¶
The transition set_translation() animates through, or nullopt.
scale_transition¶
The transition set_scale() animates through, or nullopt.
start_animation¶
Run animation on one of this element's visual properties. It replaces whatever runs on any of the same components. A finished DoubleAnimation or KeyFrameAnimation that holds its end (the default) writes its last value into the property, and so does a finished spring. An ExpressionAnimation runs until stop_animation().
stop_animation¶
Stop the animation on property: the components go back to the property's own value.
is_animating¶
Whether an animation or implicit transition is running on any component of property.
transitions¶
The transitions this element runs itself (Repositioning when layout moves it, Entrance when it is first laid out, ...).
set_transitions¶
Replaces this element's own transitions.
children_transitions¶
The transitions every child of this element runs, as if each had them itself. Only meaningful on a container.
set_children_transitions¶
Replaces the transitions every child runs.
transition_target¶
The transition target (Animation.hpp's TransitionTargetState) as drawn this frame.
set_transition_target¶
Drive the transition target directly, for an owner that runs its own item transitions (ListView's) and needs no storyboard on the element.
run_transition¶
Play storyboard on the transition target, replacing one already running; on_completed fires once it ends (not when it is replaced), after the target is back at identity. An empty storyboard has nothing to play: it resets the target and calls on_completed at once, inside this call.
transition_running¶
Whether a transition storyboard is running.
actual_size¶
The size from the last arrange pass.
actual_offset¶
The layout position relative to the parent's (bounds() are window-absolute here).
visual_transform¶
The transform this element is drawn through, in window coordinates: scale then rotation about center_point(), then translation, then the transition target, about the layout position. Identity for an element whose visual layer was never touched.
add_subscription¶
Take ownership of a Subscription so that its lifetime is exactly this node's lifetime. The tree is single-owner and destroyed hierarchically, so a node's subscriptions die with it and a Property can never dispatch into freed memory. A Property dying first is handled inside Subscription itself (core/Property.hpp).
Called by the spec layer's build() for every reactive field; an app writing imperatively may call it directly to tie a hand-made observer to a node's lifetime.
subscription_count¶
How many bindings this node holds: a spec that binds N fields produces N subscriptions. Nothing in the framework branches on it.
clear_subscriptions¶
Drop every binding on this node, leaving whatever values were last written in place. Call it to stop tracking the bound properties and drive the control by hand from then on.
add_shortcut¶
Add a shortcut scoped to this element. Window offers every key press to the shortcuts of the focused element and then of each of its ancestors, innermost first, before the focused element's own handle_key() sees it, so a Tabs control's Ctrl+T works wherever focus is inside it. A shortcut added here only fires while focus is in its element.
handle_shortcut¶
Offers e to this element's shortcuts; true when one handled it. Controls with keys of their own that must work from anywhere inside them (Tabs control's Ctrl+Tab) override this, calling the base first.
handle_global_shortcut¶
The shortcuts that fire wherever focus is. The ones a command surface carries are of this kind (a MenuBar's Ctrl+S, a MenuFlyout item's Delete, an AppBarButton's Ctrl+N), so those controls answer here. Window offers a key that no scoped shortcut took to every element in the window, in tree order, when the focused element and its parents did not handle the key themselves (key_override()). Default: none.
set_tool_tip¶
A plain-text tool tip on any element. Window shows it beside the pointer once the pointer has rested on the element for 800 ms, and hides it on a click, a key or the pointer moving off. ToolTip (ui/dialogs/ToolTip.hpp) stays the way to give a tip arbitrary content or its own placement.
tool_tip_at¶
The tip for window point p, which Window asks for as the pointer moves: tool_tip() unless the control shows a different tip per part (a TableView cell). A change of text while the pointer stays on the control closes the open tip and starts the wait again.
tool_tip¶
The text Window shows; a control whose tip is derived (an AppBarButton's "Save (Ctrl+S)") overrides this.
cursor¶
The cursor shown while the pointer is over this element (or over a descendant that asks for none). nullopt, the default, asks for nothing, so an ancestor's cursor or the control's own cursor_at() decides, and failing everything the pointer is an arrow.
set_cursor¶
Sets or clears (nullopt) the cursor; Window picks it up the next time the pointer moves.
cursor_at¶
The control's own cursor at p (window-client DIPs, inside bounds()) when set_cursor() has set none: IBeam over a TextBox's text, Hand over a hyperlink, a resize arrow over a splitter. nullopt (the default) leaves it to the ancestors. Window asks the innermost element under the pointer first, then each parent in turn, and the first answer wins.
accessibility¶
The caller-supplied accessible name, description and decorative flag (a spec's .accessibility). automation_name() prefers its name over the control's own text; empty by default, which is right for any control with visible text.
set_accessibility¶
Replaces the accessibility information; read by the next automation query.
automation_role¶
What screen readers and other assistive tools see of this node: the role, name, help text and children here, and what a client can do with the node through the providers automation_providers() lists (ui/Automation.hpp).
automation_role() is the role this node reports; Group by default.
automation_name¶
The accessible name: accessibility().name when the caller set one, otherwise the control's own visible text. Not virtual, like measure(). A control overrides automation_name_fallback() instead.
automation_help_text¶
The accessible help text a screen reader offers after the name: accessibility().description when the caller set one, otherwise automation_help_text_fallback().
automation_valid¶
Whether screen readers hear the value as valid: false while automation_invalid() says the control holds input it reports as wrong.
automation_children¶
The children an automation client sees under this node, immediate children only. A client walks the tree one level at a time, and the parent comes from parent(). Defaults to every child for_each_child() lists. Override it only to show a client something else: the children in a different order, or without parts that are only decoration. A composite that is one automation node lists nothing.
automation_focus¶
While this element has keyboard focus, the node inside it that has it as automation sees it: the row, tab or menu item a control moves its own cursor over with the arrow keys. Screen readers announce that node. nullptr (the default) is this element itself.
automation_is_native_host¶
True only for NativeControl (ui/hosting/NativeControl.hpp). Its child HWND answers automation clients itself, so the automation tree (ui/AutomationTree.hpp) leaves it out and treats it as childless, which keeps it from being described twice.
automation_parent_of¶
The automation node between this element and its child child, for a container whose automation tree has a level its element tree lacks (a table's cell around a template cell the table owns). nullptr, the default, makes this element the child's automation parent.
measure¶
Measure the desired size within available. Unbounded (+infinity) on an axis means no limit; 0 is no room. The result is always finite. Applies the margin and the size limits, then calls measure_override() for the control-specific work. Subclasses implement that hook and don't override measure(). Asked again with the same size, measurer and theme while nothing at or below the element was marked DirtyFlags::Measure, it returns what it returned last time without calling measure_override(): a measure_override() must depend on nothing else, or invalidate_measure() whenever that something changes. The context's measurer and theme must outlive the next measure(): a virtualizing container re-measures from arrange() and render() with the ones it was last measured with.
measure¶
Size measure(Size available, const TextMeasurer& measurer, const Theme& theme = Theme::default_light()) const
measure() with a context of measurer and theme, for measuring a control on its own. The theme defaults to Theme::default_light(), as render()'s does.
parallel_measure_safe¶
Whether this element's measure() may run on a layout worker thread while its siblings are measured on others (ParallelLayout). Default false, so an element nobody checked is always measured on the UI thread.
Answer true only when measure_override() touches nothing but this element's own state, the children it lists in for_each_child() (each of which answers for itself), the measurer and the theme: it builds no children, reads or writes no Property, subscribes to nothing, invalidates nothing, and uses no cache or static shared with other elements. A part it measures without listing it in for_each_child() makes the answer false. The answer is inherited, so a subclass whose measure_override() does more overrides it back.
arrange¶
Place the element at its final position and size within final_rect. Applies margin and alignment, then calls arrange_override() to position any children. The default arrange_override() does nothing, since most leaves need only bounds(). measure() is always called before arrange(). Given the rect it was last given, with nothing at or below it measured or marked DirtyFlags::Arrange since, it does nothing.
bounds¶
The rect the last arrange() gave this element, in window-client DIPs (not relative to the parent), after margin and alignment; excludes the margin. Empty before the first layout, and zero-sized while Collapsed.
hit_region¶
Classify p (window-client DIPs, inside bounds()) for Window::hit_region_at(). Each element answers for its whole subtree. The default passes the question to the topmost child under the point, of those for_each_child() lists, and answers Client where there is none, so an ordinary tree is Client everywhere and only a subtree that opts in drags the window. TitleBarControl answers Caption for its own background. DragAreaControl answers Caption for everything but the focusable controls inside it.
title_bar_drag_region¶
Whether a press on this element, inside a TitleBar's content, drags the window. nullopt (the default) lets the TitleBar decide: an enabled focusable control takes the press, anything else drags. true drags even over a control (and, on a container, lets its controls drag too unless they say false). false never drags, over this element or anything inside it. Read on every hit test, so a change takes effect at once.
set_title_bar_drag_region¶
Sets or clears (nullopt) the drag-region override; read by the next hit test.
render¶
Draw this element: nothing while Collapsed or fully transparent, else render_override() through the visual transform and opacity (visual_transform(), current_opacity() times the transition target's). Emits no extra command for an element whose visual layer is at rest, so a still tree renders exactly what render_override() alone would. A container draws each child through this, handing on the context it got.
render¶
Draw this element under theme (Theme::default_light() by default): a control drawn on its own, or the root of a tree.
handle_pointer¶
Deliver a pointer event to this element and everything inside it. It is hit-tested once: the topmost Visible, hit-test visible (is_hit_test_visible()) and enabled element under the pointer gets it in pointer_override(), with its position mapped into that element's layout coordinates (through every visual_transform() and map_to_children() on the way), then each parent up to this one, until one returns true. A press that something handled captures the pointer for that element and its parents until the release, so they get the moves and the release wherever the pointer goes. An element the pointer moves off gets a Leave (once the capture is over, if it holds it), and on_pointer_entered()/on_pointer_exited() are raised. Window calls this on its content; a test can call it on any element, which then routes within its own subtree. Returns whether an element handled the event.
on_pointer_entered¶
The handler for the pointer moving onto or off this element (a press holds neither back; the pointer leaving the window exits). Hit where the element is drawn, as its input is.
on_pointer_exited¶
The handler for the pointer leaving the element; see on_pointer_entered().
on_got_focus¶
The handler for keyboard focus arriving on this element, or on anything inside it, from outside it. Moving focus between two elements inside it raises nothing here. Window raises it once the focused element has been told (set_focused()).
on_lost_focus¶
The handler for keyboard focus leaving this element and everything inside it; see on_got_focus().
handle_wheel¶
A wheel turn, which Window routes through this instead of handle_pointer(): hit-tested like a pointer event, offered to the element under the pointer in wheel_override() and then to each parent until one returns true, so the innermost scrollable region under the pointer scrolls and the page behind it does not. Returns whether an element reacted to it.
handle_key¶
A key press or release. Window delivers it to the focused element and then to each parent in turn until one handles it; what nothing handles goes on to the shortcuts, a dialog's default button and arrow-key focus moves. Offers a press to on_key_down() first, then calls key_override(); true when either took it.
on_key_down¶
The handler for a key pressed (or repeating) while focus is on this element or inside it, for app code: an Escape that closes a view, arrow keys that page through it. It sees the key before this element's own handling and after that of the elements inside it that have focus. ui::Handled stops the key there; ui::NotHandled, or returning nothing, lets it go on to this element and then to the ones around it. One handler per element; setting another replaces it.
Example:
ui::Property<bool> details_open{true};
auto view = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
view->on_key_down([&](const ui::KeyEvent& e) {
if (e.key != ui::Key::Escape) return ui::NotHandled;
details_open = false;
return ui::Handled;
});
on_wheel¶
The handler for a wheel turn over this element or one inside it, for app code: Ctrl+wheel to zoom. It sees the turn before this element scrolls and after the elements inside it under the pointer had theirs; e.position is in this element's coordinates, and e.wheel_delta is positive when the wheel turns toward the user (scroll down). ui::Handled stops the turn there; ui::NotHandled, or returning nothing, lets it go on. One handler per element; setting another replaces it.
Example:
ui::Property<float> zoom{1.0f};
auto tiles = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
tiles->on_wheel([&](const ui::PointerEvent& e) {
if (!e.modifiers.ctrl) return ui::NotHandled;
zoom = std::clamp(zoom.get() + (e.wheel_delta < 0 ? 0.1f : -0.1f), 0.5f, 3.0f); // up zooms in
return ui::Handled;
});
raise_wheel¶
Offers a wheel turn to on_wheel(), then to wheel_override(); true when either took it. What the wheel dispatch calls for each element under the pointer.
handle_text¶
Typed text, routed like a key (focused element first, then its parents). Calls text_override() and returns its answer.
handle_text¶
handle_text() with the text alone.
focusable¶
Whether this element can take keyboard focus (a leaf control opts in).
takes_tab_key¶
Whether Tab and Shift+Tab reach this element while it has focus, instead of moving focus (a text box that indents). Ctrl+Tab never moves focus either way. Default: false.
take_menu_access¶
Alt or F10 alone (letter 0), or Alt with a letter nothing else used, asks for the menus. The window offers it to each visible, enabled element in tree order until one takes it: a menu bar takes keyboard focus (and opens the menu whose header starts with letter) and returns true. Default: false.
is_focused¶
Whether this element has keyboard focus: the window's focused element, or a part a composite control draws as focused on its behalf (a ComboBox's text field). Kept by the window.
takes_focus_at¶
Whether a press at p (window DIPs) gives this focusable element focus. Window asks it of every stop in the ring and focuses the innermost that says yes, so a stop inside another wins. The default is the whole of bounds(); a control that is one stop for only part of what it covers answers for that part, or a press on non-focusable space inside the rest would focus it (NavigationShell: its menu, not the page it hosts).
focus_visible¶
Whether a focused element draws its focus ring: true when the keyboard moved focus, false after a click gave it. Window writes it; a control checks it next to its own focused state when it renders. True by default, so a control focused without a Window draws its ring.
has_focus_within¶
Whether keyboard focus is on this element or on one inside it. A repeater checks it before clearing an element (a focused element stays realized). Kept by Window through note_keyboard_focus(); an element destroyed while focused takes its part of the count with it.
note_keyboard_focus¶
Window's side of has_focus_within(): this element gained or lost keyboard focus.
collect_focusables¶
Append focusable elements in tab order, so the Window can build a flat focus ring over the whole tree. The default pushes this element when focusable(), then recurses into every child for_each_child() lists. Override it when the ring differs from the children: a composite that is one tab stop pushes only itself, a focus scope stops recursing (see collect_scope_focusables()), a pager leaves out its hidden pages.
selection_group¶
The set of choices this control is one of, or nullptr (the default). Focusable controls that return the same pointer act as one group for the keyboard: Tab stops once on the group, on the member whose SelectionItemProvider says it is selected, or else the first. The arrow keys move between the members in tree order, wrapping, and select the one they reach (SelectionItemProvider::select_item()). Ctrl+arrow only moves. Automation clients see each member's position in the set. A RadioButton bound with ui::selects() returns the property it was bound to.
default_focus¶
The element Window focuses when this becomes the open modal, or nullptr for the first stop in its tab ring. DialogControl picks something in its content first, else its default button.
focus_scope¶
Whether this element is a focus scope. By default a container adds its focusable descendants to the window's one flat tab ring (collect_focusables()), so Tab walks the whole tree leaf by leaf. A focus scope is one tab stop instead. Enter steps into it, Tab then cycles only its own inner ring, and Escape steps back out onto the container (Window::enter_focus_scope() and exit_focus_scope()). A navigation pane of fifteen collapsible groups that are scopes is fifteen Tab stops wide instead of eighty.
Off by default, and set per instance: the same Expander can be a grouping box in one layout and a navigable group in another. A control can serve as a scope only if it overrides collect_scope_focusables() (and scope_step() for scopes that navigate items). Setting the flag on a control that overrides neither has no effect, since Window doesn't enter a scope with an empty inner ring.
set_focus_scope¶
Makes this element a focus scope (or an ordinary, transparent container again); redraws.
bring_into_view¶
Ask every scrolling ancestor to reveal this element, innermost first. Window calls it whenever focus lands somewhere, because a tab ring that walks into a scrolled-off region would otherwise park focus on something the user cannot see.
start_bring_into_view¶
Ask every scrolling ancestor to reveal this element (or options.target_rect of it), placed as the options say. Unlike bring_into_view() the whole element is revealed, focus ring or not, and whatever bring_into_view_area() says.
effective_viewport¶
What the scrolling ancestors (and the window itself, the root's bounds) leave in view around this element, in arranged coordinates, not clipped to the element's own bounds. Empty when an ancestor shows nothing; a huge rect when nothing above the element clips it at all (an element with no parent).
caret_bounds¶
Where text typed into this element goes, while it has focus: the caret, in arranged coordinates, or nullopt when it takes no text. The platform puts an input method's candidate window and the emoji panel there (Window::caret_bounds()). A control that hands its typing to an inner text box answers with that box's.
bring_into_view_area¶
What bring_into_view() reveals when focus lands here (arranged coordinates), or nullopt to leave every scrolling ancestor where it is. The element decides, the scroller follows.
Override it when the whole element is not what should come into view. A grid that draws all its cards itself has bounds as tall as its content, so revealing them jumps the viewport to the top on every focus gain; it answers with the current card's rect instead, or nullopt when there is none.
reveal_child¶
Reveal area (arranged coordinates) and return where it sits after scrolling, so the next ancestor up works with the new position. That makes a scroller nested in a scroller work without a layout pass in between. options places it (start_bring_into_view()); bring_into_view() passes the defaults, the smallest scroll. Default: nothing to reveal, geometry unchanged. Scroll is the only override.
scroll_info¶
This element's scrolling, or nullopt for one that does not scroll (the default). Scroll, VirtualList and ListView answer.
parent¶
The container this element sits in (non-owning), or nullptr for a root or an element not yet added anywhere.
for_each_child¶
Call visit once for each child this element owns, in order (back to front where they overlap). The one override a container needs beyond measure, arrange and render. The default of every tree-wide walk recurses through it (collect_focusables(), the popup walk, clear_dirty(), build_visual_tree(), sync_composition(), sync_native(), tick() and automation_children()), so a custom container that lists its children here gets focus, popups, dirty clearing, ticks, composition, native controls and automation right without forwarding any of them. Default: no children, a leaf.
List the children the walks should reach, normally every one this element owns. A walk that must see a different set overrides that walk alone: a composite that is one tab stop and one automation node, a split view leaving its closed pane out of focus and popups. A control that parks parts (an unselected page, a hidden button) may leave them out here instead and clear them in clear_dirty_override() (and tick them in tick_override()), so a parked part never stays dirty.
Const so the const walks can use it, though it hands out mutable children: they are owned objects of their own, not part of this element's state. A tick can run a handler that adds or removes children, so a container that allows that walks its list by index.
Example:
class Row : public ui::Element
{
void for_each_child(ui::ChildVisitor visit) const override
{
for (const auto& child : children_)
{
visit(*child);
}
}
std::vector<std::unique_ptr<ui::Element>> children_;
};
is_dirty¶
Whether any of flags is set on this node, for this node or, since flags bubble up, for something below it. A new element starts fully dirty.
dirty_flags¶
Every dirty flag currently set on this node.
on_screen¶
Whether the last frame the Window measured drew some of this element inside the client area: false once it is scrolled out of view, clipped away, collapsed, fully transparent or no longer drawn at all. Only an element that called set_screen_tracking() is told; for any other element, and for one no frame has measured yet, this is true.
on_screen_changed¶
Called after a frame in which on_screen() changed, for an element that called set_screen_tracking(). The frame that brings an element back into view calls it with true, so a control that paused its clock off screen restarts it here (request_tick_in() works from it). The default does nothing.
tick¶
What Window::tick() calls on the root with the time elapsed since the last tick. Elements never read a clock themselves. Advances this element's own animations and transitions (its visual layer), runs tick_override(), then ticks every child for_each_child() lists. Not virtual, so the children are always ticked. A handler run by the tick may remove elements. They are kept alive until the tick returns, and a child removed before its turn is skipped.
sync_native¶
Called by Window after layout/input changes, with sync_composition()'s cadence. The default only recurses into the children (for_each_child()); NativeControl (winui/ui/hosting/NativeControl.hpp) overrides it to place its child window.
sync_spell_checker¶
Hands the focused element the spell checker. This is not called tree-wide. Window calls it from set_focus(), right before set_focused(true), and before every key or character, so a field is checked the moment it gains focus. A field keeps the pointer after losing focus (its squiggles stay, and it still re-checks when the checker's generation() moves on). A field that was never focused is never checked. TextBox overrides it. Composites with an internal TextBox (ComboBox/NumberBox/AutoSuggestBox) do not forward it, since none of them hold prose.
handle_context_menu¶
Window::handle_context_menu() calls this on the focused element when the user right-clicks or presses Shift+F10/the Menu key, after focusing whatever focusable element sits under the pointer, as a pointer Down would. position is the right-click point (window-client DIPs), or nullopt for a keyboard request, where the element picks its own anchor (TextBox: its caret). host is only valid for this call, so show a menu through it here or not at all. Returns whether a menu was shown; default: none.
on_context_requested¶
The handler for a right-click, Shift+F10 or the Menu key on this element or one inside it. For a right-click Window asks the innermost element under the pointer and then each ancestor, focusable or not; then, and for the keys, the focused element and each ancestor, innermost first, ahead of handle_context_menu(). The handler is the target asked to act: unless it returns ui::NotHandled it has shown something (a MenuFlyout's show_at(), a CommandBarFlyout) and the request goes no further. position is as handle_context_menu()'s. One handler per element; setting another replaces it.
raise_context_requested¶
Runs the handler set above, if any; whether it took the request. A wrapper that stands in for the element it wraps in the tab ring (a flyout around its anchor) asks that element too.
allow_drop¶
Whether this element is a drop target, getting drag_enter()/drag_over()/drag_leave()/drop() while a drag (DragDrop::start()) is over it.
set_allow_drop¶
Makes the element a drop target (or not); takes effect for the next drag movement.
drag_enter¶
The drop events. drag_over() is asked first on the innermost drop target under the pointer, then on its drop targets up the tree until one accepts; every element asked has heard drag_enter() first and hears drag_leave() once the drag is no longer its business. The one that accepted gets drop(). A control overrides them; the defaults run the handlers set with on_drag_enter()/on_drag_over()/on_drag_leave()/on_drop().
drag_enter(): the drag has come over this element; drag_over() follows at once.
drag_over¶
The drag is over this element: answer what a drop here would do. Asked when it arrives and at every move. The default runs the on_drag_over() handler, and refuses without one.
drag_leave¶
The drag left this element, was canceled over it, or was dropped on another target.
drop¶
The pointer was released over this element while its drag_over() accepted. What the drop did is the operation it accepted.
on_drag_enter¶
The handler the default drag_enter() runs; one per element, setting another replaces it.
on_drag_over¶
The handler the default drag_over() runs, which answers for the element.
on_drag_leave¶
The handler the default drag_leave() runs.
on_drop¶
The handler the default drop() runs.
drag_completed¶
A drag this element started has ended. result is what the target did (None when the drag was canceled or dropped nowhere).
transition_opacity¶
How far this element's own open or close fade has come, 0 (hidden) to 1 (fully shown), as for a Flyout's open and close fade or a DialogControl's open fade. The control advances it in tick_override(). The window draws everything the element shows at this opacity: a popup owner's floating content, or a modal's scrim together with its content, so one fade covers both. Default 1: fully visible, no fade running.
build_visual_tree¶
Add this node, every child for_each_child() lists under it, and then what build_visual_tree_override() adds, into tree under parent_id (InvalidVisualId for the root). Called by Window::layout() after arrange(), so bounds() reflects the just-computed layout. Not virtual, so the children are always reached.
raise_context_requested_in¶
A context request inside a subtree that is not in the tab ring (an item's content, drawn by an items control that is the one tab stop): raised on the innermost visual child of root under position (or on root for a keyboard request) and bubbled to root. Returns whether a handler took it.
Element¶
Constructs an element with a visual_id() of its own, fully dirty and in no tree.
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_;
};
open_popup¶
Floats request.content above the tree, hung from request.anchor (this element by default), or updates the popup this element has open. Each element has at most one popup; a control that shows several uses several elements. The window places it after every layout, draws it with its shadow over everything else, routes input inside it to it alone, and on a press outside it or Escape calls request.on_dismiss. It closes with close_popup() or when this element is destroyed. Calling it again while open, from arrange_override() for an anchor that moves, lays the popup out again only when something other than the callbacks changed.
close_popup¶
Closes the popup open_popup() opened. Nothing when none is open.
popup_open¶
Whether this element has a popup open.
Element¶
Moves a not-yet-parented element (a factory returning a control by value); see the copy operations' comment. Never move an element that is already in a tree (asserted). The new element gets a visual_id() of its own and starts fully dirty; the ElementWatch objects on the source follow it here. What the moved-from element is left with is only fit to be destroyed.
operator=¶
Deleted: an element has an identity (its visual id, the watches on it, the handlers that captured it), so assigning one over another has no sensible meaning.
measure_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¶
Position children within the rect arrange() settled on (also in bounds(), window DIPs). Default: nothing, for a leaf.
render_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().
pointer_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.
handled_pointer_override¶
A pointer event that an element inside this one handled (its pointer_override() returned true), so it never reached this element's pointer_override(). Called on each element above the one that handled it, innermost first, with the position in this element's layout coordinates. A container that has to see every press inside it, handled or not, overrides it: a swipe that may turn a press on a button into a drag, a table that commits an open edit when a check box in another row is pressed. Default: nothing.
wheel_override¶
A wheel turn over this element (handle_wheel()). Return true when it scrolled something here. Default: an element with no children hands it to pointer_override() and returns false; one with children returns false.
hit_test_children¶
The children p (in this element's layout coordinates) is tested against, topmost first. Default: what for_each_child() lists, last first. A container that draws in another order (Panel's z-index), a control with parts for_each_child() does not list, or one whose parts cover others in places (a pane drawn over the content) lists them here.
map_to_children¶
A point in this element's layout coordinates, mapped into its children's: a control that scrolls, zooms or scales its content maps it the way the content is drawn. Default: the point unchanged.
children_hit_clip¶
Where the children can be hit, in this element's layout coordinates, or nullopt for anywhere (the default): a scrolling control returns its viewport, so content scrolled out of view is not hit.
hit_test_transparent¶
Whether the pointer passes through this element and everything in it to what is behind, as if it were not there: a wrapper with nothing visible in it (ContentHost while its child is missing or collapsed). Default: false, so an element is hit anywhere in its bounds.
covers_children_at¶
Whether a part of this element drawn over its children is at p (a scroll bar over the content), so the element is hit there instead of a child. Default: false.
capture_pointer¶
Capture the pointer of the event being handled: keep receiving its events, wherever it goes, until release_pointer() or the release of the button. A handled press does this by itself. Only while handling a pointer event.
release_pointer¶
End a capture_pointer() or the capture a press took.
tick_override¶
This element's own part of tick(): a clock of its own (ToolTip's hover delay, an animation's phase, a caret blink). Runs after the visual layer has advanced and before the children are ticked, which tick() does by itself. Tick only parts that for_each_child() does not list here. Default: nothing.
clear_dirty_override¶
This element's own part of clear_dirty(): clear any part it owns but leaves out of for_each_child() (a parked page, a hidden button), through that part's clear_dirty(). Default: nothing.
build_visual_tree_override¶
This element's own part of build_visual_tree(): add any part it owns but leaves out of for_each_child() through that part's build_visual_tree(tree, visual_id()). Default: nothing.
collect_scope_focusables¶
The inner tab ring, built when this scope is entered: a scope's counterpart to collect_focusables(). A container that becomes a scope must stop recursing in collect_focusables() and recurse here instead (Expander is the worked example), since a child in both rings would be reachable without ever entering.
Three meaningful answers:
- several elements: an ordinary container scope. Tab cycles them, Escape leaves.
- this alone: an item-navigating scope. The control's items are not all elements (ListView creates its rows on demand and destroys them as they scroll out), so focus stays on the control and Tab is routed to scope_step() below.
- nothing: there is nothing inside worth entering. Window doesn't enter, and the key that would have entered goes to this element's own handle_key(). That is how Enter expands a collapsed Expander that is marked as a scope.
The one walk that does not default to recursing through for_each_child(): being a scope is something a control opts into, and a default inner ring would make every container enterable while its children still sat in the outer ring too.
scope_step¶
Move an item-navigating scope's own inner cursor one step: delta is +1 for Tab and -1 for Shift+Tab. Wrap and return true to keep focus inside the scope (Escape is then the way out, as for an ordinary container scope). Return false to let the step fall out, popping the scope and leaving focus on this control. Never called for a scope whose inner ring is real elements, since Window walks that ring itself.
set_scope_entered¶
Called by Window as this scope is entered (before its inner ring takes focus) and again as it is left, so a control can show and hide whatever inner cursor it draws. A container scope whose inner ring is real Elements usually needs nothing here, since the focused child draws its own focus ring, as it would anywhere else.
take_focus_request¶
For a part of the main tree that takes focus when it opens, as SidePane's light-dismissable pane does. It holds the tab ring while open (by answering collect_focusables() with the pane alone) and hands focus back when it closes. Returning a request consumes it; the default never asks. Escape reaches it like any key the focused element inside it leaves alone (key_override()).
focus_request_root¶
The subtree an Enter request focuses into; this by default.
draws_focus_ring¶
Whether the window draws this element's focus ring while it is focusable(), is_focused() and focus_visible(). True by default, so a focusable control gets the ring by doing nothing. The window draws it after everything else in the window, or in the popup the element is in, clipped only by the scrolling viewports around the element, and layout never makes room for it. The theme decides where it goes: the Fluent eras draw their two rings just outside focus_ring_rect(), the classic eras their dotted rectangle just inside. Return false only for a control that shows focus in a shape of its own (a text field's accent border) or not at all.
focus_ring_rect¶
What the ring goes around, in window DIPs: bounds() by default. A control whose ring goes around one part of it (a slider's thumb, a list's current row) returns that part. theme is the one the element is drawn with, for a ring placed differently in each era.
focus_ring_inside¶
Whether the ring goes inside focus_ring_rect() in every era, its outer edge on the rect. False by default, which leaves it to the theme. An item inside a viewport (a list row, a tab, a menu item) returns true, as does a control in a tight spot, so a neighbor or the viewport edge never cuts its ring.
focus_ring_corner_radius¶
The ring's corner radius: the theme's control radius by default.
key_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.
text_override¶
Typed text for this element, routed like key_override(). Default: false.
focus_changed¶
Keyboard focus arrived (true) or left (false); is_focused() already says so. The element is redrawn either way. No-op by default.
focus_visible_changed¶
focus_visible() changed; the element is redrawn either way. No-op by default.
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.
set_enabled_gate¶
A condition of the control's own that is ANDed into is_enabled() (a Button whose command may not run). True by default.
attached¶
The element joined a window's tree: window() is now that window. Called for the root a window takes and for everything inside it, and for a subtree added to a tree already in a window. No-op by default.
detached¶
The element left a window's tree (moved out of it). No-op by default.
automation_name_fallback¶
automation_name()'s fallback when accessibility().name is empty. Default: no inherent name, correct for controls with no visible text of their own (Slider, ProgressBar), which need an explicit accessibility().name from the caller. A control with its own visible text (Button, CheckBox, TextControl, ...) overrides this to return it.
automation_help_text_fallback¶
automation_help_text()'s fallback when accessibility().description is empty. Default: none. An input with a description under it returns that text, or its error while set.
automation_invalid¶
True while the control holds input it reports as wrong (an input's error is set); screen readers say so. Default: false.
mark_dirty¶
Mark this node dirty and bubble the newly set bits up the parent chain, so an ancestor's flags show that something below it changed without walking the subtree. Bubbling stops at the first ancestor that already has the bit, since every ancestor of a dirty node is dirty in the same way.
Every setter goes through here, so this is where a build with thread checks catches a setter called on an element under a Window from another thread than the window's. Leave where defaulted: it names the setter that called this.
invalidate_measure¶
This element's size has to be measured again (and it arranged again): mark_dirty() with DirtyFlags::Layout. For a setter whose value changes the element's desired size.
invalidate_arrange¶
This element has to be arranged again, its size has not changed: mark_dirty() with DirtyFlags::Arrange. For a setter that only moves the element or its children within the space they already have (an alignment, a scroll position that is laid out).
request_tick¶
Keep the window ticking: for an element that waits for time to pass without changing anything in the meantime (a caret between blinks, a delay before something opens). A window stops ticking a few seconds after its last tick that changed something, so a longer quiet wait calls this from tick_override() for as long as it lasts. A wait of known length is cheaper with request_tick_in().
request_tick_in¶
Have the window tick again delay from now (0 or less: at once), for an element whose next change has a known time (the next frame of an animated image). Unlike request_tick() it does not keep the window busy: an otherwise idle window sleeps until then and wakes only for it (Window::next_wake()). Call it from tick_override() or on_screen_changed(). It holds for one tick, so an element that still waits asks again from its next tick_override(), which may come sooner. A hidden or minimized window does not wake for it.
What the same tick_override() changes with mark_dirty() is that step's change (a caret blink, an image's next frame): it is drawn, and it does not make the window busy either. A tick_override() that also animates something calls request_tick() as well, or the window waits for the requested time before its next tick.
set_screen_tracking¶
Have the Window report on_screen() for this element and call on_screen_changed() when it changes, from the frames it measures from now on. Off by default, and free while off. A control turns it on while its drawing moves on its own (an animated image), to pause while nobody can see it. Turning it off makes on_screen() true again.
check_thread¶
mark_dirty()'s thread check without the dirtying, for a setter that changes nothing drawn (an event handler, a tip): reports a call from another thread than the one of the Window this element is under. Leave where defaulted: it names the setter that called this.
note_child_deleted¶
For a container whose child just started an AddRemoveTransition exit. In the next arrange the children it moves close the gap only once that exit has played, and a child added in the same pass fades in after them, so neither slides over the one still leaving. A RepositionTransition on those children gives way to it for that pass.
desired_content_size¶
What the last measure asked for, without the margin: how large the content is, where bounds() may be the larger slot a stretching parent gave it.
set_parent_of¶
Record parent as the container part sits in (nullptr: none), when a container adds a child or a part of its own. Dirty flags bubble up this link, and the new parent chain takes on the part's dirty flags and keyboard focus (has_focus_within()) while the old one lets go of the focus, so a container that adopts a dirty part is laid out again without marking itself. Not an ownership transfer: the container already holds the part's unique_ptr.
clear_dirty_of¶
Clear flags on part and its subtree, for a clear_dirty_override() clearing a part this element leaves out of for_each_child().
set_focused_of¶
Show part focused or not, for a composite that draws one of its parts as focused while it holds the focus itself (a ComboBox's text field). Hand focus_visible() on with set_focus_visible_of() too, or the part keeps drawing its ring after a click.
set_focus_visible_of¶
Hand the focus-visible state on to a part; see set_focused_of().
key_handled¶
The usual answer for a focused control's key_override(): an arrow when arrow (the control uses that arrow), Enter when enter, a shortcut-like key (Ctrl, Alt, Delete, Insert, a function key) when shortcut, never Tab or Escape, and any other key (typing, Space, Home, PageDown) always, since a focused control owns its plain keys.
struct ScrollInfo¶
What a scrolling element tells something that follows its scrolling, such as a Parallax.
| Field | Type | Default | |
|---|---|---|---|
offset |
Point |
how far the view is scrolled into the content (zoomed DIPs) | |
viewport |
Size |
the view's size | |
extent |
Size |
the content's size, unzoomed | |
zoom |
float |
1.0f |
the zoom factor; 1 unzoomed |
out_of_bounds_pan |
Size |
How far past either end the content can be pulled: a tenth of the viewport for a plain scroller, a whole viewport for a zoomable one. | |
content |
const Element* |
nullptr |
The element that is the content, whose top-left is the content's origin; nullptr when the content is not one element (a list's realized rows). |