ListViewBase¶
Header: winui/ui/collections/ListViewBase.hpp.
The shared base of ListView and TileView: a scrolling, virtualized, selectable collection of templated items. Each item sits in an item container that draws the hover, press and selection chrome.
The items are either plain strings (set_items(), shown as text by the default template) or an index range that an item template turns into content (set_item_template() and set_item_count()). Only the items in and near the viewport are realized, through the ForEachControl inside the Scroll (repeater(), scroll_view()). Report changes to the collection with insert_item() and remove_item() for strings, or the notify_*() calls for a template's collection. The selection then follows its items and the item container transitions play. The selection mode decides which gestures select. Groups, a header and footer, item click, drag reordering and saving the scroll position are all optional. The class cannot be constructed directly. Create a ListView or a TileView.
The list is one tab stop. The arrow keys move the focused item. In Extended mode, and in Single mode with single_selection_follows_focus(), the selection moves with it. Ctrl+A selects everything in Multiple and Extended mode, or clears the selection when everything is already selected.
Example:
auto list = std::make_unique<ui::ListViewControl>();
list->set_items({"Apple", "Banana", "Cherry"});
list->set_selection_mode(ui::SelectionMode::Extended);
list->set_height(240.0f);
list->on_selection_changed([](std::optional<std::size_t> index)
{ std::println("first selected: {}", index.value_or(0)); });
// A template's collection instead of strings:
struct Contact
{
std::string name;
};
std::vector<Contact> contacts{{"Ada"}, {"Grace"}, {"Linus"}};
list->set_item_template([&contacts](std::size_t i) {
return std::make_unique<ui::TextControl>(contacts[i].name);
});
list->set_item_count(contacts.size());
window.set_content(std::move(list));
ui::ListViewBase¶
Derives from ui::Element, ui::SelectionProvider, ui::ValueProvider.
The shared base of ListView and TileView: a scrolling, virtualized, selectable collection of templated items. Each item sits in an item container that draws the hover, press and selection chrome.
The items are either plain strings (set_items(), shown as text by the default template) or an index range that an item template turns into content (set_item_template() and set_item_count()). Only the items in and near the viewport are realized, through the ForEachControl inside the Scroll (repeater(), scroll_view()). Report changes to the collection with insert_item() and remove_item() for strings, or the notify_*() calls for a template's collection. The selection then follows its items and the item container transitions play. The selection mode decides which gestures select. Groups, a header and footer, item click, drag reordering and saving the scroll position are all optional. The class cannot be constructed directly. Create a ListView or a TileView.
The list is one tab stop. The arrow keys move the focused item. In Extended mode, and in Single mode with single_selection_follows_focus(), the selection moves with it. Ctrl+A selects everything in Multiple and Extended mode, or clears the selection when everything is already selected.
Example:
auto list = std::make_unique<ui::ListViewControl>();
list->set_items({"Apple", "Banana", "Cherry"});
list->set_selection_mode(ui::SelectionMode::Extended);
list->set_height(240.0f);
list->on_selection_changed([](std::optional<std::size_t> index)
{ std::println("first selected: {}", index.value_or(0)); });
// A template's collection instead of strings:
struct Contact
{
std::string name;
};
std::vector<Contact> contacts{{"Ada"}, {"Grace"}, {"Linus"}};
list->set_item_template([&contacts](std::size_t i) {
return std::make_unique<ui::TextControl>(contacts[i].name);
});
list->set_item_count(contacts.size());
window.set_content(std::move(list));
ItemTemplate¶
Makes the content for item index and may choose a different look per item. Called when an item is realized. The list owns what it returns.
GroupHeaderTemplate¶
Makes a group header's content, for group index group (group_key() names it).
~ListViewBase¶
ListViewBase¶
operator=¶
ListViewBase¶
operator=¶
items¶
The string items. Empty when the items come from an item template.
set_items¶
Replaces the items with a new list of strings. Nothing is selected afterwards. on_selection_changed() fires when something was selected before.
insert_item¶
Inserts item before index. The selection stays on its items, and an AddRemoveTransition among the item container transitions plays.
add_item¶
Appends item at the end.
remove_item¶
Removes the item at index, dropping it from the selection.
move_item¶
Moves the item at from to to, keeping its selection state.
set_item_template¶
Sets the content template and rebuilds the realized items. nullptr, the default, shows each string of items() as text.
item_count¶
How many items the list shows: items().size() for strings, else the set_item_count() value.
set_item_count¶
Sets a collection of count items that only the template knows. Counts as a reset and clears items().
notify_inserted¶
Reports that count items were inserted at index in the template's collection.
notify_removed¶
Reports that count items were removed at index. They leave the selection.
notify_replaced¶
Reports that count items at index changed in place. The template builds their content again.
notify_moved¶
Reports that the item at from moved to to. The selection moves with it.
notify_reset¶
Reports that the whole collection changed. Realized items are rebuilt and a ContentTransition among the item container transitions plays. Clears the selection, and fires on_selection_changed when there was one.
index_from_element¶
The item that inside, any element inside an item, belongs to. A handler inside a template, such as a context menu's Delete, uses it to find its item after inserts and removals moved it. Unset when inside is not in a realized item.
set_group_keys¶
Files the items under group headers, one key per item in item order. The groups appear in the order their keys first appear, and the items keep their order within a group, so items with the same key need not be next to each other. An empty vector ungroups. An item past the end of keys goes under an empty key. Indices stay positions in the collection. Only the order on screen, which the arrow keys and Shift ranges follow, comes from the groups. In a TileView with a tile or stack layout, each header takes a line of its own.
group_keys¶
The keys set_group_keys() was given.
group_count¶
How many groups the keys make; 0 when ungrouped.
group_key¶
Group group's key, as its header shows it. Empty past group_count().
set_group_header_template¶
Sets the group header's content. nullptr shows the group's key as 20px text over a 1px divider.
sticky_group_headers¶
Whether group headers are sticky; on by default. The current group's header stays pinned to the top of the viewport until the next header pushes it up.
set_sticky_group_headers¶
Turns sticky group headers on or off.
set_header¶
Sets content above the first item that scrolls with the items. Takes ownership, replaces any previous header, header_text() included, and returns a non-owning observer of the new one.
set_header_text¶
Sets a plain-text header above the first item, in the group headers' text style, that scrolls with the items. Replaces any previous header. An empty text removes a text header.
header_text¶
The text set_header_text() shows. Empty while there is none or the header is content.
header¶
The current header, non-owning, or nullptr.
set_footer¶
Sets content after the last item that scrolls with the items. Takes ownership and returns a non-owning observer.
footer¶
The current footer, non-owning, or nullptr.
selection_mode¶
The selection mode; Single by default.
set_selection_mode¶
Sets the selection mode. None clears the selection, Single keeps one at most, and Multiple shows a check box in each item.
selected_index¶
The first selected item. Unset when nothing is selected.
set_selected_index¶
Selects index alone, clamped to the items, and scrolls it into view. Unset deselects all. No callback fires when nothing changes.
selected_items¶
The selected items, in index order.
set_selected_items¶
Replaces the selection with the items at indices, in any order. Indices past the end are dropped, Single mode keeps the lowest one, and None selects nothing. Fires on_selection_changed() once, and only when the selection changed.
is_selected¶
Whether the item at index is selected.
select¶
Adds an item to the selection, in Multiple and Extended mode.
deselect¶
Removes the item at index from the selection.
select_all¶
Selects every item in Multiple and Extended mode, as Ctrl+A does.
deselect_all¶
Clears the selection.
single_selection_follows_focus¶
Whether moving keyboard focus with the arrows also selects in Single mode; on by default.
set_single_selection_follows_focus¶
Turns selection-follows-focus on or off.
on_selection_changed¶
Called with selected_index() after the change. Fires for user gestures and programmatic changes, and only when the selection changed. Replaces any previous handler.
on_selected_index_shifted¶
Called with the new index when the selected item stayed selected but an insert, removal or move before it changed selected_index(). It is not a selection change, so on_selection_changed does not fire. A two-way .selected_index uses it to follow the item. Replaces any previous handler.
is_item_click_enabled¶
Whether a click raises on_item_click(); off by default.
set_item_click_enabled¶
Turns on_item_click() on or off.
on_item_click¶
Called with the clicked item, before the click selects, while is_item_click_enabled(). Replaces any previous handler.
is_item_invoked_enabled¶
Whether on_item_invoked() is raised; off by default.
set_item_invoked_enabled¶
Turns on_item_invoked() on or off.
on_item_invoked¶
Called with the item on Enter or a double-click, and under SelectionMode::None also a click or Space, while is_item_invoked_enabled(). Raised after the gesture selects. Replaces any previous handler.
set_item_context_menu¶
Sets the context menu of item index. It is asked for on a right-click on the item, or on Shift+F10 or the Menu key while the item is the current one. The item is selected first, unless it already is or the selection mode is None. An empty list opens no menu. Replaces any previous one.
can_drag_items¶
Whether an item dragged past the threshold starts a drag; off by default.
set_can_drag_items¶
Turns item dragging on or off.
can_reorder_items¶
Whether a drag inside the list moves the item; off by default. Needs can_drag_items() and Element::allow_drop().
set_can_reorder_items¶
Turns reordering on or off.
on_drag_items_starting¶
A drag of items starts. Fill in the package's text or data, or cancel.
on_drag_items_completed¶
A drag of items ended, with the items dragged and what the drop did (None when it was refused or canceled).
on_items_reordered¶
A drop inside the list moved item from to to, both positions in the collection after the move. A string list has already moved it. For a template's collection, move the data here. The list has already been told through notify_moved().
insertion_index_at¶
Where a drop at position, in window coordinates, would insert: the index of the item it would go before. Past the middle of an item counts as after it.
scroll_into_view¶
void scroll_into_view(std::size_t index, ScrollIntoViewAlignment alignment = ScrollIntoViewAlignment::Default)
Scrolls so item index shows, by the least amount (Default) or to the top (Leading).
scroll_offset¶
The vertical scroll offset, DIPs. Horizontal for a vertical TileView.
scroll_view¶
The inner Scroll, owned by the list.
repeater¶
The inner ForEachControl that realizes the item containers, owned by the list. Its try_get_element() reaches a realized container by its index, counting group headers.
relative_scroll_position¶
The key of the first item in view, from key_of, and how far it is scrolled past the top, as "key|offset".
set_relative_scroll_position¶
bool set_relative_scroll_position(const std::string& position, Callback<std::optional<std::size_t>(const std::string&)> index_of)
Scrolls back to what relative_scroll_position() described. index_of finds the item for a key and returns unset when there is none. Returns whether the item was found.
items_updating_scroll_mode¶
How the scroll position reacts to inserts and removes; KeepItemsInView by default.
set_items_updating_scroll_mode¶
Sets the scroll mode. KeepLastItemInView makes a chat-style log follow its end.
set_panel_vertical_alignment¶
Where the items sit vertically. Trailing puts a list shorter than its viewport at the bottom, as in a message log.
set_item_container_margin¶
Sets the margin around every item container, DIPs, and lays the items out again.
item_container_margin¶
The margin around each item container.
set_item_horizontal_content_alignment¶
How content sits horizontally in its container. Stretch in a ListView, Center in a TileView by default.
item_height¶
A fixed container height, DIPs. Unset (the default) sizes each container to its content, at least 40 DIPs in a ListView.
set_item_height¶
Sets the fixed container height, or clears it to size containers to their content. Lays the items out again.
set_background¶
Background behind the items, as a fixed color. nullopt is transparent.
set_background¶
Background as a theme color, read again when the theme changes.
set_foreground¶
The default template's text color. nullopt uses the theme's text color.
set_foreground¶
Foreground as a theme color.
set_border_brush¶
The border color. nullopt draws no border.
set_border_brush¶
The border color as a theme color.
set_border_thickness¶
The border width on each side, DIPs. 0, the default, draws none.
set_padding¶
Padding inside the border, around the scrolling area.
item_transitions¶
The item container transitions; empty by default. AddRemoveTransition plays on insert and removal, ContentTransition for a new collection, EntranceTransition when items first appear and RepositionTransition when they move.
set_item_transitions¶
Sets the item container transitions. They apply to changes reported after the call.
focused¶
Whether the list has keyboard focus.
current_item¶
The item keyboard focus is on. Unset when there is none.
set_current_item¶
Moves the keyboard focus cue to item index and scrolls it into view, leaving the selection as it is. An index past the end does nothing. The arrow keys go on from there.
focus_index¶
The item Tab steps through while the list is entered as a focus scope. Unset otherwise.
focusable¶
Whether this element can take keyboard focus (a leaf control opts in).
automation_focus¶
The item the keyboard is on, so a screen reader reads it as the arrow keys move.
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.
for_each_child¶
Lists the scroll view that holds the header, the items and the footer, and an open item context menu. The pinned group header is a drawn copy of a header already in the list and is not listed.
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.
scroll_info¶
This element's scrolling, or nullopt for one that does not scroll (the default). Scroll, VirtualList and ListView answer.
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.
drag_completed¶
A drag this element started has ended. result is what the target did (None when the drag was canceled or dropped nowhere).
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.
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.
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.
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.
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.
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.
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¶
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_changed¶
Keyboard focus arrived (true) or left (false); is_focused() already says so. The element is redrawn either way. No-op by default.
focus_ring_rect¶
Inside the item the keyboard is on, cut to the part of it in view. Around the whole list when that item is out of view.
focus_ring_corner_radius¶
The ring's corner radius: the theme's control radius by default.
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.
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_;
};
struct ListViewDragPayload¶
What a ListView drag carries in DataPackage::data: the list it came from and the items dragged.
A drop target reads it back with std::any_cast<ListViewDragPayload>(&e.data.data).
Example:
void accept_drops(ui::ListViewControl* left, ui::ListViewControl* right)
{
right->on_drop([=](const ui::DragEventArgs& e) {
if (const auto* payload = std::any_cast<ui::ListViewDragPayload>(&e.data.data);
payload != nullptr && payload->source == left)
{
right->insert_item(right->insertion_index_at(e.position), left->items()[payload->items.front()]);
left->remove_item(payload->items.front());
}
});
}
| Field | Type | Default | |
|---|---|---|---|
source |
const Element* |
nullptr |
The list the drag started from (non-owning). |
items |
std::vector<std::size_t> |
The indices of the dragged items, in index order. |
struct DragItemsStartingArgs¶
The arguments of ListViewBase::on_drag_items_starting().
The package already carries a ListViewDragPayload in data.data. A handler may add text for other drop targets, replace the payload, or return ui::Cancel to stop the drag.
Example:
auto list = std::make_unique<ui::ListViewControl>();
list->set_items({"Apple", "Banana", "Cherry"});
list->on_drag_items_starting([&](const ui::DragItemsStartingArgs& e) {
if (e.items.size() > 1)
return ui::Cancel; // single-item drags only
e.data.text = list->items()[e.items.front()];
return ui::Proceed;
});
| Field | Type | Default | |
|---|---|---|---|
items |
std::vector<std::size_t> |
The indices of the items about to be dragged. | |
data |
DataPackage& |
The package the drag will carry. Fill in its text or data. |
struct DragItemsCompletedArgs¶
What on_drag_items_completed gets once a drag out of a ListView or TileView ends.
| Field | Type | Default | |
|---|---|---|---|
items |
std::vector<std::size_t> |
The indices of the dragged items. | |
result |
DataPackageOperation |
DataPackageOperation::None |
What the drop did; None when refused. |
struct ItemsReorderedArgs¶
What on_items_reordered gets when a drop inside the list moved item from to to. Both are positions in the collection after the move.
| Field | Type | Default | |
|---|---|---|---|
from |
std::size_t |
0 |
Where the item was. |
to |
std::size_t |
0 |
Where it is now. |
Enumerations¶
ScrollIntoViewAlignment¶
Where ListViewBase::scroll_into_view() puts the item.
| Value | |
|---|---|
Default |
The least scroll that shows the item. |
Leading |
The item at the top of the viewport. |
ItemsUpdatingScrollMode¶
How a list keeps its scroll position while items are inserted and removed.
| Value | |
|---|---|
KeepItemsInView |
The first item in view stays in view (the default). |
KeepScrollOffset |
The scroll offset stays put and the items move under it. |
KeepLastItemInView |
A list scrolled to its end stays at its end as items arrive. |
Tests¶
tests/test_list_view_base.cpp