Skip to content

TileView

Header: winui/ui/collections/TileView.hpp. Spec: winui/ui/collections/TileView.hpp.

Templated items in wrapping rows of equal cells, virtualized and selectable.

Every cell is the size of the first item's container, or set_item_width() and set_item_height_cell(). A line holds as many cells as fit, or maximum_rows_or_columns(). Each item sits in a container with a backplate for hover and press, a 2 pixel accent frame when selected, and a check box in the top-right corner in Multiple mode. Items, templates, selection, drag reordering and scrolling come from ListViewBase. When grouped (set_group_keys()), each group header takes a line of its own and its cells start on the next.

Example:

// Declarative: a 3-column picker of 96x96 tiles.
std::vector<std::string> names{"Red", "Green", "Blue", "Cyan", "Magenta", "Yellow"};
window.set_content(ui::TileView<std::string>{
    .items                   = names,
    .maximum_rows_or_columns = 3,
    .on_item_click           = [](const std::string& name) { std::println("picked {}", name); },
    .item                    = [](const std::string& name) -> ui::Spec {
        return ui::Text{.text = name, .width = 96.0f, .height = 96.0f};
    },
});

// Imperative:
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto grid  = std::make_unique<ui::TileViewControl>();
grid->set_item_template([](std::size_t i) { return std::make_unique<ui::TextControl>(std::to_string(i)); });
grid->set_item_count(12);
grid->set_item_width(100.0f);
grid->set_item_height_cell(64.0f);
grid->set_selection_mode(ui::SelectionMode::Multiple);
stack->add(std::move(grid));

ui::TileView

Templated items in rows of equal cells. Builds a ui::TileViewControl.

It shares ListViewBase with ui::ListView. .items, .item and .key take the same sources, and selection, click, drag and reorder work the same way. The cells wrap. .layout swaps the equal cells for another arrangement and keeps everything else.

Example:

std::vector<ui::Color>  swatches{ui::Color::from_rgb(0xE81123), ui::Color::from_rgb(0x0078D4),
                                 ui::Color::from_rgb(0x107C10), ui::Color::from_rgb(0xFFB900)};
ui::Property<ui::Color> picked{ui::colors::Black};

window.set_content(ui::TileView<ui::Color>{
    .items                   = swatches,
    .maximum_rows_or_columns = 4,
    .on_item_click           = [&](const ui::Color& color) { picked = color; },
    .item                    = [](const ui::Color& color) -> ui::Spec
    { return ui::Border{.background = color, .width = 40.0f, .height = 40.0f}; },
});

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

ui::TileView{
    .header, .items, .key, .selected_index, .selected_indices, .item_transitions,
    .selection_mode, .item_click_enabled, .item_invoked_enabled, .can_drag_items,
    .can_reorder_items, .allow_drop, .group_by, .sticky_group_headers,
    .maximum_rows_or_columns, .layout, .orientation, .background, .border_brush,
    .border_thickness, .item_container_margin, .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_item_click,
    .on_item_invoked, .on_drag_items_starting, .on_drag_items_completed, .on_items_reordered,
    .on_pointer_entered, .on_pointer_exited, .on_got_focus, .on_lost_focus, .on_key_down,
    .on_wheel, .control, .header_content, .footer_content, .item, .group_header_template,
    .item_context_menu
}

Fields, in declaration order (designators must follow it):

Field Type Default
header ui::Binding<std::string> A plain-text header above the first cell, in the group headers' text style. header_content wins over it.
items ui::Binding<std::vector<T>> The collection: a ui::ObservableVector, a ui::Property<std::vector<T>>, a plain vector or a braced list.
key ui::ItemKey<std::type_identity_t<T>> What identifies an item when .items is bound to a property; see ui::ListView.
selected_index ui::TwoWayField<std::optional<std::size_t>> The selected item's index. Unset, the default, means nothing is selected.
selected_indices ui::Binding<std::vector<std::size_t>> Every selected item, as ascending positions in the collection. Bind a ui::Property<std::vector<std::size_t>> to read and set the selection under Multiple or Extended. Two-way with ui::bind(). Left empty and unbound, .selected_index decides.
item_transitions ui::Binding<ui::TransitionList> Transitions every item container runs (ui::TileViewControl::set_item_transitions()).
selection_mode ui::Binding<ui::SelectionMode> {ui::SelectionMode::Single} None, Single (the default), Multiple (check boxes) or Extended.
item_click_enabled ui::Binding<bool> {false} Whether clicks raise on_item_click, before they select. Setting on_item_click turns it on unless it is bound.
item_invoked_enabled ui::Binding<bool> {false} Whether Enter and double-clicks raise on_item_invoked. Setting on_item_invoked turns it on unless it is bound.
can_drag_items ui::Binding<bool> {false} Whether items can be dragged out; see on_drag_items_starting.
can_reorder_items ui::Binding<bool> {false} Whether dragging within the view moves the item, in the view and in the collection.
allow_drop ui::Binding<bool> {false} Whether the view accepts drops.
group_by ui::Callback<std::string(const std::type_identity_t<T>&)> Files the items under group headers by the key it returns for each item. The groups come in the order their keys first appear, and the items keep their order within a group, without sorting. Each header takes a line of its own, and the group's cells start on the next. Asked again on every collection change. Positions in the collection stay the indices.
sticky_group_headers ui::Binding<bool> {true} Whether the current group's header stays pinned at the top while its cells scroll under it.
maximum_rows_or_columns ui::Binding<std::size_t> {0} The most cells in a line before wrapping; 0 is no limit. With a ui::TileLayout in layout, 0 keeps the layout's own maximum_rows_or_columns and anything else overrides it.
layout ui::LayoutSpec How the cells are placed: equal cells when left out, ui::WrapLayout{} for cells of their own width, ui::LinedFlowLayout{} for a photo wall. With a layout other than a tile layout, maximum_rows_or_columns and orientation have no effect.
orientation ui::Binding<std::optional<ui::Orientation>> The direction cells are laid in before they wrap, which also sets the scroll axis. Unset takes the orientation of a ui::TileLayout in layout, else Horizontal. When set, it overrides the layout's.
background ui::Binding<ui::Paint> Background behind the cells; nullopt draws none.
border_brush ui::Binding<ui::Paint> The border color: a fixed color, or a theme color read again on a theme change.
border_thickness ui::Binding<ui::Thickness> The border thickness, DIPs; zero (the default) draws no border.
item_container_margin ui::Binding<std::optional<ui::Thickness>> {std::nullopt} Every item container's margin. nullopt keeps the default 0,0,4,4.
on_selection_changed ui::Callback<void(ui::SelectedItem<std::type_identity_t<T>>)> Called with a copy of the first selected item, unset when nothing is selected (see ui::SelectedItem). The copy is safe to keep.
on_item_click ui::Callback<void(const std::type_identity_t<T>&)> Called with the clicked item. Turns item_click_enabled on. The reference is valid during the call. An item of a ui::ObservableVector stays at its address while it is in the collection.
on_item_invoked ui::Callback<void(const std::type_identity_t<T>&)> Called with the item on Enter or a double-click. With SelectionMode::None a click or Space also invokes. Turns item_invoked_enabled on. The reference is valid as for on_item_click.
on_drag_items_starting ui::Callback<ui::Decision(const ui::DragItemsStartingArgs&)> A drag of items starts. Fill in the drag package, or cancel. The args name the items by position.
on_drag_items_completed ui::Callback<void(const ui::DragItemsCompletedArgs&)> A drag of items ended, with the positions of the dragged items and what the drop did.
on_items_reordered ui::Callback<void(const ui::ItemsReorderedArgs&)> A reorder moved the item at position from to to. The collection has already been moved.
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<std::type_identity_t<ui::TileView>> Out: the handle that receives the built control. Its type uses std::type_identity_t, so passing a handle does not take part in deducing the type argument.
header_content ui::Spec Content above the first cell (header_content) and after the last (footer_content). Both scroll with the cells.
footer_content ui::Spec See header_content.
item ui::Callback<ui::Spec(std::type_identity_t<T>&)> How one cell looks: a callable taking const T&, or T& to edit an item of a ui::ObservableVector. Left out, an item that is text is shown as that text. Any other item type needs one, and a debug build asserts without it.
group_header_template ui::Callback<ui::Spec(const std::string& key)> A group header's content, given the group's key. Left out, the header shows the key as text. Needs .group_by.
item_context_menu ui::Callback<std::vector<ui::MenuItem>(const std::type_identity_t<T>&)> The context menu of an item: a callable taking the item and returning the menu's entries. Called on a right-click on the item, or on Shift+F10 or the Menu key while it is current. The item is selected first. An empty list opens no menu. An item of a ui::ObservableVector stays where it is while it is in the collection, so an entry's on_click may keep a reference to it. Any other item is valid during the call only, so capture it by value.

Plus the common fields style, margin, width, height, min_width, min_height, max_width, max_height, horizontal_alignment, vertical_alignment, visible, tool_tip, enabled, accessibility every spec carries (conventions).

build

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

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

ui::TileViewControl

Derives from ui::ListViewBase.

Templated items in wrapping rows of equal cells, virtualized and selectable.

Every cell is the size of the first item's container, or set_item_width() and set_item_height_cell(). A line holds as many cells as fit, or maximum_rows_or_columns(). Each item sits in a container with a backplate for hover and press, a 2 pixel accent frame when selected, and a check box in the top-right corner in Multiple mode. Items, templates, selection, drag reordering and scrolling come from ListViewBase. When grouped (set_group_keys()), each group header takes a line of its own and its cells start on the next.

Example:

// Declarative: a 3-column picker of 96x96 tiles.
std::vector<std::string> names{"Red", "Green", "Blue", "Cyan", "Magenta", "Yellow"};
window.set_content(ui::TileView<std::string>{
    .items                   = names,
    .maximum_rows_or_columns = 3,
    .on_item_click           = [](const std::string& name) { std::println("picked {}", name); },
    .item                    = [](const std::string& name) -> ui::Spec {
        return ui::Text{.text = name, .width = 96.0f, .height = 96.0f};
    },
});

// Imperative:
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto grid  = std::make_unique<ui::TileViewControl>();
grid->set_item_template([](std::size_t i) { return std::make_unique<ui::TextControl>(std::to_string(i)); });
grid->set_item_count(12);
grid->set_item_width(100.0f);
grid->set_item_height_cell(64.0f);
grid->set_selection_mode(ui::SelectionMode::Multiple);
stack->add(std::move(grid));

TileViewControl

TileViewControl()

An empty grid in Single selection mode, horizontal orientation.

set_layout

template <typename T>
T* set_layout(std::unique_ptr<T> layout)

Replaces the layout that places the cells and returns an observer. The default is equal cells (a TileLayoutEngine). Use a WrapLayoutEngine for cells of their own width, or a LinedFlowLayoutEngine for a photo wall. nullptr restores the default. With a layout that is not a TileLayoutEngine, the cell setters below have no effect, and the arrow keys find the item above or below by where the items are.

maximum_rows_or_columns

std::size_t maximum_rows_or_columns() const

The most cells per row (per column when Vertical); 0 (the default) is as many as fit.

set_maximum_rows_or_columns

void set_maximum_rows_or_columns(std::size_t max)

Caps the cells per row (per column when Vertical) and lays the view out again.

orientation

Orientation orientation() const

The fill direction; Horizontal by default.

set_orientation

void set_orientation(Orientation orientation)

Changes the fill direction, and the scroll axis with it, and lays the view out again.

set_item_width

void set_item_width(OptionalFloat width)

Sets the cell width in DIPs. nullopt, the default, takes the first item's width.

set_item_height_cell

void set_item_height_cell(OptionalFloat height)

Sets the cell height in DIPs. nullopt, the default, takes the first item's height. It has its own name because ListViewBase::set_item_height() sets the list container height.

Tests

  • tests/test_list_view_base.cpp