Carousel¶
Header: winui/ui/navigation/Carousel.hpp. Spec: winui/ui/navigation/Carousel.hpp.
Shows one item at a time, filling the whole view, and flips between them with previous/next buttons, the mouse wheel or the keyboard.
Items are any elements, owned by the view (add_item()). They sit side by side along the flip axis, each the size of the view. Only the items in view are drawn or get input, and Tab reaches only the selected item's focusable content. The previous/next buttons appear while the mouse moves over the view and for 3 s after, or once keyboard focus arrives. A button never shows on a side with no item beyond it. One wheel turn flips one item. Along the flip axis the arrow keys and PageUp/PageDown step one item, and Home/End jump to the ends. A step to a neighbor slides over 300 ms, and longer jumps land at once. For a page indicator, pair it with a PipsPager bound to the same index.
Example:
// Declarative: the pages are children, each filling the view.
ui::Property<std::size_t> page{0};
window.set_content(ui::VerticalStack{
.spacing = 8,
.content = {
ui::Carousel{
.selected_index = page,
.height = 270.0f,
.max_width = 400.0f,
.content = {
ui::Image{.source = ui::Bitmap::read("cliff.jpg")},
ui::Image{.source = ui::Bitmap::read("grapes.jpg")},
ui::Text{.text = "The end", .horizontal_alignment = ui::Alignment::Center},
},
},
ui::PipsPager{.selected_index = page, .number_of_pages = 3}, // two-way
},
});
// Imperative.
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto* flip = stack->add(std::make_unique<ui::CarouselControl>());
flip->set_height(200.0f);
flip->add_item(std::make_unique<ui::TextControl>("First")); // becomes the selection
flip->add_item(std::make_unique<ui::TextControl>("Second"));
flip->on_selection_changed([&page](std::size_t i) { page = i; });
flip->set_selected_index(1); // slides once laid out; fires the handler
ui::Carousel¶
One item at a time, flipped with arrows, the wheel or the keys. Builds a ui::CarouselControl. Its items are child elements, and each fills the whole view.
Example:
ui::Property<std::size_t> page{0};
window.set_content(ui::Carousel{
.selected_index = page,
.height = 270.0f,
.max_width = 400.0f,
.content = {
ui::Image{.source = ui::Bitmap::read("cliff.jpg")},
ui::Image{.source = ui::Bitmap::read("grapes.jpg")},
ui::Text{.text = "The end", .horizontal_alignment = ui::Alignment::Center},
},
});
Every field, in the order a designated initializer names them:
ui::Carousel{
.selected_index, .orientation, .use_animations, .background, .border_brush,
.border_thickness, .style, .margin, .width, .height, .min_width, .min_height, .max_width,
.max_height, .horizontal_alignment, .vertical_alignment, .visible, .tool_tip, .enabled,
.accessibility, .on_selection_changed, .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 | |
|---|---|---|---|
selected_index |
ui::TwoWayField<std::size_t> |
{0} |
The index of the item in view. |
orientation |
ui::Binding<ui::Orientation> |
{ui::Orientation::Horizontal} |
Horizontal (the default) flips left/right; Vertical flips up/down. |
use_animations |
ui::Binding<bool> |
{true} |
One-item flips slide. |
background |
ui::Binding<ui::Paint> |
The fill behind the items: a fixed color or a theme resource; unset is the window's background color. | |
border_brush |
ui::Binding<ui::Paint> |
A border drawn over the items' edges: a fixed color or a theme resource; unset draws none. | |
border_thickness |
ui::Binding<ui::Thickness> |
See border_brush. |
|
on_selection_changed |
ui::Callback<void(std::size_t)> |
Called with the index of the item now in view. | |
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::Carousel> |
Out: the handle that receives the built control. | |
content |
ui::SpecList |
The pages, in flip order; each fills the whole view. |
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::CarouselControl; the caller owns it.
ui::CarouselControl¶
Derives from ui::Element, ui::SelectionProvider.
Shows one item at a time, filling the whole view, and flips between them with previous/next buttons, the mouse wheel or the keyboard.
Items are any elements, owned by the view (add_item()). They sit side by side along the flip axis, each the size of the view. Only the items in view are drawn or get input, and Tab reaches only the selected item's focusable content. The previous/next buttons appear while the mouse moves over the view and for 3 s after, or once keyboard focus arrives. A button never shows on a side with no item beyond it. One wheel turn flips one item. Along the flip axis the arrow keys and PageUp/PageDown step one item, and Home/End jump to the ends. A step to a neighbor slides over 300 ms, and longer jumps land at once. For a page indicator, pair it with a PipsPager bound to the same index.
Example:
// Declarative: the pages are children, each filling the view.
ui::Property<std::size_t> page{0};
window.set_content(ui::VerticalStack{
.spacing = 8,
.content = {
ui::Carousel{
.selected_index = page,
.height = 270.0f,
.max_width = 400.0f,
.content = {
ui::Image{.source = ui::Bitmap::read("cliff.jpg")},
ui::Image{.source = ui::Bitmap::read("grapes.jpg")},
ui::Text{.text = "The end", .horizontal_alignment = ui::Alignment::Center},
},
},
ui::PipsPager{.selected_index = page, .number_of_pages = 3}, // two-way
},
});
// Imperative.
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto* flip = stack->add(std::make_unique<ui::CarouselControl>());
flip->set_height(200.0f);
flip->add_item(std::make_unique<ui::TextControl>("First")); // becomes the selection
flip->add_item(std::make_unique<ui::TextControl>("Second"));
flip->on_selection_changed([&page](std::size_t i) { page = i; });
flip->set_selected_index(1); // slides once laid out; fires the handler
CarouselControl¶
An empty view: no items, nothing selected, not focusable.
add_item¶
Append an item, taking ownership; returns a non-owning observer. The first item added becomes the selection (without firing on_selection_changed()).
item_count¶
How many items the view holds.
item¶
The item at index (owned by the view), or nullptr past the end.
selected_index¶
The index of the item in view; unset with no items.
set_selected_index¶
Selects the item at index, clamped to the items there are. A change fires on_selection_changed. A step to a neighbor slides (use_animations()), and anything else lands at once. Ignored with no items, since a carousel with items always shows one.
orientation¶
The flip axis; Horizontal by default.
set_orientation¶
Changes the flip axis and which arrow keys flip; updates the layout.
use_animations¶
Whether a one-item flip slides. On by default.
set_use_animations¶
Turns the one-item slide on or off for later flips.
set_background¶
The fill behind the items: a fixed color or a theme resource. Empty, the default, is the window's background color.
set_border_brush¶
The border color around the items (none by default), drawn over their edges.
set_border_thickness¶
The border's thickness per side, DIPs (0 by default). Drawn over the items, so it does not shrink them; redraws only.
on_selection_changed¶
Called with the new index whenever the selected item changes, by a button, the wheel, a key or set_selected_index(). One handler; setting another replaces it.
previous_button_visible¶
Whether the previous/next buttons are showing right now (see the class comment).
next_button_visible¶
As previous_button_visible(), for the next button.
previous_button_bounds¶
Where the previous button sits (window DIPs): 16x38 centered 1 DIP inside the left edge, or 38x16 at the top when vertical. Valid after arrange(), shown or not.
next_button_bounds¶
As previous_button_bounds(), at the right (or bottom) edge.
focusable¶
Focusable while enabled and holding at least one item.
focused¶
Whether the view itself holds keyboard focus.
for_each_child¶
Every item, in order.
collect_focusables¶
The view itself, then the selected item's focusable descendants. Tab cannot reach the other items.
automation_role¶
A List.
measure_override¶
Takes the whole available size on a bounded axis; on an unbounded one, the largest item's.
arrange_override¶
Gives every item the view's size and places them along the flip axis at the current position.
render_override¶
Draws the background, the items in view (clipped), the border, the buttons while shown and the focus ring, which sits just inside the bounds.
pointer_override¶
Mouse movement shows the buttons; a click on a button flips. Other input reaches the items in view.
wheel_override¶
An item in view that scrolls gets the turn first. Otherwise each turn flips one item, and a repeat in the same direction within 200 ms is ignored. Returns false (not handled) at the first or last item and for Ctrl+wheel, so an outer scroller can react.
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.
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.
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.
tick_override¶
Advances the slide and the buttons' show timer, then ticks the items.
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.
key_override¶
Arrows along the flip axis step, Home/End jump to the ends, PageUp/PageDown step one item.
focus_changed¶
Called by Window; gaining keyboard focus (focus_visible()) shows the buttons. Redraws.
focus_ring_inside¶
Inside the view, which fills its slot edge to edge.
Tests¶
tests/test_carousel.cpp