TeachingTip¶
Header: winui/ui/dialogs/TeachingTip.hpp. Spec: winui/ui/dialogs/TeachingTip.hpp.
A card that points at a control (or sits against the window) to teach something about it.
The element itself takes no room where it is placed in the tree. While open, its card floats above the window, placed against target() by placement() and placement_alignment(). It falls back to the other sides when there is no room. The card holds an optional icon, the title and subtitle, optional content, optional hero content, and up to two buttons. The tip owns its content and hero content. The target is a non-owning pointer that must outlive the tip or be cleared first. The target usually exists only once the tree is built, so set it right before opening, or give the spec a .target handle, which links in either build order.
Example:
ui::Control<ui::Button> show;
ui::Control<ui::TeachingTip> tip;
window.set_content(ui::VerticalStack{
.content = {
ui::Button{.text = "Show TeachingTip", .on_click = [&] { tip->set_open(true); }, .control = show},
ui::TeachingTip{.title = "Save automatically",
.subtitle = "Your work is saved to the cloud as you go.",
.action_button_text = "Got it",
.close_button_text = "Close",
.target = show,
.placement = ui::PopupPlacement::Below,
.on_closed = [](ui::PopupCloseReason) { /* remember it */ },
.control = tip,
.content = ui::Text{.text = "Turn it off in Settings.",
.wrapping = ui::TextWrapping::WrapWholeWords}},
},
});
// Or imperatively, next to the target in the same window:
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto* target = stack->add(std::make_unique<ui::ButtonControl>("Show TeachingTip"));
auto* t = stack->add(std::make_unique<ui::TeachingTipControl>());
t->set_title("This is the title");
t->set_subtitle("And this is the subtitle");
t->set_light_dismiss(true);
target->on_click([t, target] {
t->set_target(target);
t->set_open(true);
});
ui::TeachingTip¶
A card pointing at a control, or at the window, to teach something. Builds a ui::TeachingTipControl, which takes no room in layout. Set its target with .target, or through .control once the target exists. Without a target it sits against the window's edge.
Example:
ui::Control<ui::Button> save_button;
ui::Control<ui::TeachingTip> tip;
window.set_content(ui::VerticalStack{ .content = {
ui::Button{ .text = "Save", .control = save_button },
ui::TeachingTip{ .title = "Saving automatically",
.subtitle = "We save your changes as you go.",
.close_button_text = "Got it",
.placement = ui::PopupPlacement::Below,
.control = tip },
}});
tip->set_target(save_button);
tip->set_open(true);
Every field, in the order a designated initializer names them:
ui::TeachingTip{
.title, .subtitle, .is_open, .icon, .action_button_text, .close_button_text,
.light_dismiss, .target, .placement, .placement_alignment, .placement_margin,
.hero_content_placement, .tail_visibility, .style, .margin, .width, .height, .min_width,
.min_height, .max_width, .max_height, .horizontal_alignment, .vertical_alignment, .visible,
.tool_tip, .enabled, .accessibility, .on_action_button_click, .on_close_button_click,
.on_opened, .on_closing, .on_closed, .on_pointer_entered, .on_pointer_exited,
.on_got_focus, .on_lost_focus, .on_key_down, .on_wheel, .control, .content, .hero_content
}
Fields, in declaration order (designators must follow it):
| Field | Type | Default | |
|---|---|---|---|
title |
ui::Binding<std::string> |
The semibold first line. | |
subtitle |
ui::Binding<std::string> |
The line under the title. | |
is_open |
ui::TwoWayField<bool> |
{false} |
Whether the tip is open. ui::bind(prop) is two-way. Closing the tip writes false into the property, and a false that on_closing cancels is put back to true. |
icon |
ui::Binding<ui::IconSource> |
The icon left of the title: a Symbol, a glyph, a path or a picture; empty draws none. | |
action_button_text |
ui::Binding<std::string> |
The accent button's caption; empty shows no action button. | |
close_button_text |
ui::Binding<std::string> |
A text close button; empty (with light dismiss off) shows a corner "X" instead. | |
light_dismiss |
ui::Binding<bool> |
{false} |
Whether an outside click or Escape closes it. |
target |
ui::ElementLink |
The element the tail points at, by its handle (.target = save with a ui::Control<ui::Button>), built before or after the tip. Unset for an untargeted tip. |
|
placement |
ui::Binding<std::optional<ui::PopupPlacement>> |
The side of the target tried first; the tip moves to another side when there is no room. Unset tries above first, and puts an untargeted tip at the bottom of the window. | |
placement_alignment |
ui::Binding<ui::PopupPlacementAlignment> |
{ui::PopupPlacementAlignment::Center} |
How the card lines up with the tail: Leading runs it right (or down) from the tail, Trailing left (or up). |
placement_margin |
ui::Binding<ui::Thickness> |
The gap in DIPs to the target on the side the card is on, or for an untargeted tip the gap from the window's edges (ui::TeachingTipControl::placement_margin()). | |
hero_content_placement |
ui::Binding<ui::TeachingTipHeroPlacement> |
{ui::TeachingTipHeroPlacement::Auto} |
Whether .hero_content goes above or below the text. |
tail_visibility |
ui::Binding<ui::AutoVisibility> |
{ui::AutoVisibility::Auto} |
When the tail pointing at the target is drawn. |
on_action_button_click |
ui::Action |
Fires when the action button is clicked. Does not close the tip by itself. | |
on_close_button_click |
ui::Action |
Fires when the close button is clicked, before the tip closes. | |
on_opened |
ui::Action |
The tip has opened. A two-way is_open has been written by then. |
|
on_closing |
ui::Callback<ui::Decision(const ui::PopupClosingArgs&)> |
Gets why the tip is about to close; return ui::Cancel to keep it open. | |
on_closed |
ui::Callback<void(ui::PopupCloseReason)> |
Raised however it closed, which is where a bound is_open learns about it. |
|
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::TeachingTip> |
Out: the handle that receives the built control, through which to set_target(). | |
content |
ui::Spec |
Extra content under the subtitle. | |
hero_content |
ui::Spec |
An edge-to-edge picture at the top or bottom of the card. |
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::TeachingTipControl; the caller owns it.
ui::TeachingTipControl¶
Derives from ui::Element.
A card that points at a control (or sits against the window) to teach something about it.
The element itself takes no room where it is placed in the tree. While open, its card floats above the window, placed against target() by placement() and placement_alignment(). It falls back to the other sides when there is no room. The card holds an optional icon, the title and subtitle, optional content, optional hero content, and up to two buttons. The tip owns its content and hero content. The target is a non-owning pointer that must outlive the tip or be cleared first. The target usually exists only once the tree is built, so set it right before opening, or give the spec a .target handle, which links in either build order.
Example:
ui::Control<ui::Button> show;
ui::Control<ui::TeachingTip> tip;
window.set_content(ui::VerticalStack{
.content = {
ui::Button{.text = "Show TeachingTip", .on_click = [&] { tip->set_open(true); }, .control = show},
ui::TeachingTip{.title = "Save automatically",
.subtitle = "Your work is saved to the cloud as you go.",
.action_button_text = "Got it",
.close_button_text = "Close",
.target = show,
.placement = ui::PopupPlacement::Below,
.on_closed = [](ui::PopupCloseReason) { /* remember it */ },
.control = tip,
.content = ui::Text{.text = "Turn it off in Settings.",
.wrapping = ui::TextWrapping::WrapWholeWords}},
},
});
// Or imperatively, next to the target in the same window:
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto* target = stack->add(std::make_unique<ui::ButtonControl>("Show TeachingTip"));
auto* t = stack->add(std::make_unique<ui::TeachingTipControl>());
t->set_title("This is the title");
t->set_subtitle("And this is the subtitle");
t->set_light_dismiss(true);
target->on_click([t, target] {
t->set_target(target);
t->set_open(true);
});
TeachingTipControl¶
A closed, untargeted tip with no text, no content and no buttons (only the corner "X"). Not hit-test visible: it draws nothing where it sits, so a press there goes to what is under it.
~TeachingTipControl¶
set_title¶
Sets the title.
title¶
The semibold first line of the card, wrapping at whole words; hidden when empty. Screen readers also use it as the name when none is set.
set_subtitle¶
Sets the subtitle.
subtitle¶
The line under the title, wrapping at whole words; hidden when empty.
set_icon¶
Sets the icon, in its own column left of the titles. A glyph shows at its own size, which is 20 DIPs for a Symbol. Anything else fills a 20 DIP square.
icon¶
The icon left of the titles; empty (the default) drops the icon column.
set_content¶
Takes ownership of the content under the titles and returns a non-owning observer. Replaces and destroys any previous content; nullptr clears it.
content¶
The content under the titles, owned by the tip; nullptr when none.
set_hero_content¶
Takes ownership of the hero content, typically an image, drawn edge to edge across the card's top or bottom, and returns a non-owning observer. Replaces any previous hero.
hero_content¶
The hero content, owned by the tip; nullptr when none.
set_hero_content_placement¶
Sets which edge the hero sits at.
hero_content_placement¶
Which edge the hero sits at; Auto (the default) puts the hero away from the tail.
set_action_button_text¶
Sets the action button's text; empty hides the button. The same goes for the close button. The action button only raises on_action_button_click; the close button also closes the tip.
action_button_text¶
The action button's text; empty (the default) hides it.
set_close_button_text¶
Sets the close button's text; a non-empty one also hides the corner "X".
close_button_text¶
The close button's text; empty (the default) hides it.
action_button¶
The action button, owned by the tip (for styling); shown only when action_button_text() is non-empty.
close_button¶
The close button, owned by the tip; shown only when close_button_text() is non-empty.
alternate_close_button¶
The corner "X". It shows only when there is no close button text and light dismiss is off (alternate_close_visible()).
alternate_close_visible¶
Whether the corner "X" is on screen: no close button text and light dismiss off.
enabled¶
A disabled tip grays out its action, close and corner buttons, which then take no input. Its content keeps its own state, and light dismiss and Escape still close it.
set_target¶
The element the tail points at; nullptr for an untargeted tip. Non-owning: a target that is destroyed while the tip points at it leaves the tip untargeted. Setting it makes the next layout re-decide the placement.
target¶
The current target (non-owning); nullptr (the default) for an untargeted tip.
set_placement¶
Sets the side tried first; nullopt (the default) tries above the target first, and puts an untargeted tip at the bottom of the window. The next layout re-decides the placement.
placement¶
The side tried first, before the other sides; nullopt by default. For an untargeted tip, the window edge the card sits against.
set_placement_alignment¶
Sets how the card lines up with the tail; the next layout re-decides the placement.
placement_alignment¶
How the card lines up with the tail on its side; Center by default. Leading runs the card right (or down) from the tail, Trailing left (or up). For an untargeted tip, which end of the window edge the card sits at.
set_placement_margin¶
Sets the placement margin; takes effect at the next layout.
placement_margin¶
The placement margin, in DIPs; zero by default. For a targeted tip, it is the gap between the target and the card. Only the side the card is on counts, such as top for a card above the target or left for one to its left. For an untargeted tip, it is the gap from the window's edges, added to the fixed edge inset. A card centered on an axis moves by left - right or top - bottom.
set_tail_visibility¶
Sets when the tail is drawn.
tail_visibility¶
When the tail is drawn; Auto (the default) draws the tail only for a targeted tip.
effective_placement¶
The side the open tip settled on, Below for an untargeted one. Only meaningful after a layout pass with the tip open. When no side fits, the card is centered over the target and this reports Above.
effective_placement_alignment¶
How the open tip lines up with its tail on that side.
tail_shown¶
Whether the tail is drawn for the current placement.
set_light_dismiss¶
Turns light dismiss on or off; false by default. With it on, a click outside or Escape closes the tip with LightDismiss, and the card fills with in-app acrylic instead of its solid background. The tip also takes keyboard focus while open, and the corner "X" is hidden.
light_dismiss¶
Whether light dismiss is on; see set_light_dismiss().
is_open¶
Whether the tip is open; false by default. Stays true when an on_closing() handler cancels.
set_open¶
Opens or closes the tip. Opening fires on_opened() at once and places the card at the next layout. A tip that fits nowhere then closes with Programmatic. Closing this way reports Programmatic, and on_closing() can cancel it. Setting the current state does nothing.
on_action_button_click¶
Fires when the action button is clicked; the tip stays open.
on_close_button_click¶
Fires when the close button or the corner "X" is clicked, before the tip closes with CloseButton.
on_closing¶
Fires with the reason before an open tip closes; return ui::Cancel to keep the tip open.
on_closed¶
Fires with the reason once the tip has closed (not when canceled).
on_opened¶
Fires from set_open(true) as the tip opens. There is no opening animation to wait for.
automation_role¶
Always AutomationRole::Pane. Screen readers reach the floating card the way they reach any popup's content.
automation_name_fallback¶
The title, when no accessibility name is set.
measure_override¶
Always Size: the tip takes no room where it sits in the tree.
arrange_override¶
Nothing to arrange in place; the card is placed by the popup.
render_override¶
Draws nothing in place. The card draws above the window.
clear_dirty_override¶
Also clears the content, hero and buttons, which are not ordinary children.
tick_override¶
Forwards the tick to the card's children (button animations) while open.
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.
Enumerations¶
TeachingTipHeroPlacement¶
Which edge of the card the hero content sits at: Auto puts it at the edge away from the tail.
| Value | |
|---|---|
Auto |
The edge away from the tail; the top when there is no tail at the top. |
Top |
Always across the top of the card. |
Bottom |
Always across the bottom of the card. |
Tests¶
tests/test_teaching_tip.cpp