Skip to content

TilePanel

Header: winui/ui/panels/TilePanel.hpp. Spec: winui/ui/panels/TilePanel.hpp.

A grid of equal cells that children fill in order, each spanning as many rows and columns of cells as it asks for, wrapping to a new line when one is full.

The cell is item_width() x item_height(), each taken from the first child when unset. Children go first-fit into the free cells, so a small child fills a hole a larger one left behind. A child bigger than the lines is clipped to them. Children that don't fit anywhere are not placed and get an empty rect. The panel asks for the cells it used, plus any alignment offsets. Use it for tile layouts such as a start screen or a photo mosaic. For many items, prefer a virtualizing TileLayoutEngine.

Example:

auto grid = std::make_unique<ui::TilePanelControl>();
grid->set_orientation(ui::Orientation::Horizontal);
grid->set_item_width(80.0f);
grid->set_item_height(80.0f);
grid->set_maximum_rows_or_columns(4);
grid->add(std::make_unique<ui::ButtonControl>("Mail"), 2, 2);   // a 2x2 tile
grid->add(std::make_unique<ui::ButtonControl>("Photos"), 1, 2); // one row, two columns
grid->add(std::make_unique<ui::ButtonControl>("Maps"));

// Or declaratively:
window.set_content(ui::TilePanel{
    .orientation             = ui::Orientation::Horizontal,
    .item_width              = 80.0f,
    .item_height             = 80.0f,
    .maximum_rows_or_columns = 4,
    .content                 = {
        ui::TilePanelItem{.row_span = 2, .column_span = 2, .content = ui::Button{.text = "Mail"}},
        ui::TilePanelItem{.column_span = 2, .content = ui::Button{.text = "Photos"}},
        ui::TilePanelItem{.content = ui::Button{.text = "Maps"}},
    },
});

ui::TilePanel

Equal cells filled in order, children spanning several. Builds a ui::TilePanelControl.

Example:

ui::Spec tile(ui::Color c) { return ui::Border{.background = c}; }

window.set_content(ui::TilePanel{
    .orientation             = ui::Orientation::Horizontal,
    .item_width              = 44.0f,
    .item_height             = 44.0f,
    .maximum_rows_or_columns = 3,
    .content                 = {
        ui::TilePanelItem{.content = tile(ui::colors::Black)},
        ui::TilePanelItem{.row_span = 2, .content = tile(ui::colors::White)},
        ui::TilePanelItem{.column_span = 2, .content = tile(ui::Color::from_rgba(0, 120, 215))},
    },
});

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

ui::TilePanel{
    .orientation, .item_width, .item_height, .maximum_rows_or_columns,
    .horizontal_children_alignment, .vertical_children_alignment, .background, .style, .margin,
    .width, .height, .min_width, .min_height, .max_width, .max_height, .horizontal_alignment,
    .vertical_alignment, .visible, .tool_tip, .enabled, .accessibility, .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
orientation ui::Binding<ui::Orientation> {ui::Orientation::Vertical} Vertical (the default) fills columns top to bottom, then wraps right; Horizontal fills rows.
item_width ui::Binding<std::optional<float>> The cell width, DIPs; unset takes the first child's width.
item_height ui::Binding<std::optional<float>> The cell height, DIPs; unset takes the first child's height.
maximum_rows_or_columns ui::Binding<std::size_t> {0} Cells per line before wrapping; 0 is no cap.
horizontal_children_alignment ui::Binding<ui::Alignment> {ui::Alignment::Leading} Where the block of cells sits horizontally; see TilePanelControl::horizontal_children_alignment().
vertical_children_alignment ui::Binding<ui::Alignment> {ui::Alignment::Leading} Where the block of cells sits vertically.
background ui::Binding<ui::Paint> Background: a Color, or a ThemeColor read from the theme; empty (the default) draws none.
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::TilePanel> Out: the handle that receives the built control.
content ui::TilePanelItemList The children with their spans; see ui::TilePanelItem.

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::TilePanelControl; the caller owns it.

ui::TilePanelItem

One child of a TilePanel with the cell rows and columns it spans. Written inline in the grid's .content; see ui::TilePanel for an example.

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

ui::TilePanelItem{
    .row_span, .column_span, .content
}

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

Field Type Default
row_span int 1 Cells covered along the column axis.
column_span int 1 Cells covered along the row axis.
content ui::Spec The placed child.

ui::TilePanelControl

Derives from ui::Panel.

A grid of equal cells that children fill in order, each spanning as many rows and columns of cells as it asks for, wrapping to a new line when one is full.

The cell is item_width() x item_height(), each taken from the first child when unset. Children go first-fit into the free cells, so a small child fills a hole a larger one left behind. A child bigger than the lines is clipped to them. Children that don't fit anywhere are not placed and get an empty rect. The panel asks for the cells it used, plus any alignment offsets. Use it for tile layouts such as a start screen or a photo mosaic. For many items, prefer a virtualizing TileLayoutEngine.

Example:

auto grid = std::make_unique<ui::TilePanelControl>();
grid->set_orientation(ui::Orientation::Horizontal);
grid->set_item_width(80.0f);
grid->set_item_height(80.0f);
grid->set_maximum_rows_or_columns(4);
grid->add(std::make_unique<ui::ButtonControl>("Mail"), 2, 2);   // a 2x2 tile
grid->add(std::make_unique<ui::ButtonControl>("Photos"), 1, 2); // one row, two columns
grid->add(std::make_unique<ui::ButtonControl>("Maps"));

// Or declaratively:
window.set_content(ui::TilePanel{
    .orientation             = ui::Orientation::Horizontal,
    .item_width              = 80.0f,
    .item_height             = 80.0f,
    .maximum_rows_or_columns = 4,
    .content                 = {
        ui::TilePanelItem{.row_span = 2, .column_span = 2, .content = ui::Button{.text = "Mail"}},
        ui::TilePanelItem{.column_span = 2, .content = ui::Button{.text = "Photos"}},
        ui::TilePanelItem{.content = ui::Button{.text = "Maps"}},
    },
});

TilePanelControl

TilePanelControl() = default

An empty grid: Vertical, cells sized from the first child, no line cap, Leading alignment.

add

template <typename T>
T* add(std::unique_ptr<T> child, int row_span = 1, int column_span = 1)

Takes ownership of a child spanning row_span x column_span cells (0 counts as 1), appends it after the existing children, and returns a non-owning observer to it.

add

template <SpecLike S>
Element* add(S&& spec, int row_span = 1, int column_span = 1)

The declarative overload: builds spec (or takes the node a ui::Spec holds) and adds it as add() does a control. Returns the built node as an Element, or nullptr for an empty ui::Spec, which adds nothing; a spec's .control field gives a typed observer.

set_row_span

void set_row_span(const Element* child, int span)

Changes how many cell rows child covers and updates the layout on a change. Ignored when child is not one of this grid's children.

set_column_span

void set_column_span(const Element* child, int span)

Changes how many cell columns child covers.

row_span

int row_span(const Element* child) const

The row span given for child, or 1 when it is not a child of this grid.

column_span

int column_span(const Element* child) const

The column span given for child, or 1 when it is not a child of this grid.

orientation

Orientation orientation() const

Orientation: Vertical (the default) fills a column top to bottom before moving right.

set_orientation

void set_orientation(Orientation orientation)

Changes the fill direction and updates the layout.

item_width

std::optional<float> item_width() const

The cell width in DIPs; nullopt (the default) takes the first child's desired size on that axis.

set_item_width

void set_item_width(OptionalFloat width)

Sets or clears the cell width and updates the layout.

item_height

std::optional<float> item_height() const

The cell height in DIPs; see item_width().

set_item_height

void set_item_height(OptionalFloat height)

Sets or clears the cell height and updates the layout.

maximum_rows_or_columns

std::size_t maximum_rows_or_columns() const

The most cells in a line; 0 (the default) is no cap.

set_maximum_rows_or_columns

void set_maximum_rows_or_columns(std::size_t maximum)

Caps the cells per line and updates the layout.

horizontal_children_alignment

Alignment horizontal_children_alignment() const

Where the block of cells sits horizontally; Leading (Left/Top) by default. In a panel larger than the used cells, Center and Trailing offset the whole block and Stretch spreads the lines apart.

set_horizontal_children_alignment

void set_horizontal_children_alignment(Alignment alignment)

Changes the horizontal alignment and updates the layout.

vertical_children_alignment

Alignment vertical_children_alignment() const

The vertical alignment of the cell block; see horizontal_children_alignment().

set_vertical_children_alignment

void set_vertical_children_alignment(Alignment alignment)

Changes the vertical alignment and updates the layout.

cell_size

Size cell_size() const

The size of one cell from the last measure.

items_per_line

std::size_t items_per_line() const

Cells per line from the last measure.

line_count

std::size_t line_count() const

Lines used by the last measure.

parallel_measure_safe

bool parallel_measure_safe() const override

True: measuring touches only the panel and its children.

measure_override

Size measure_override(Size available, const MeasureContext& context) const 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

void arrange_override(const Rect& final_rect, const ArrangeContext& context) override

Position children within the rect arrange() settled on (also in bounds(), window DIPs). Default: nothing, for a leaf.

class TilePanelItemList

A TilePanel's children, built, in the same shape as CellList. Callers write it as a braced list of ui::TilePanelItem{...}.

items_

std::vector<Placed> items_

The placed children, in fill order.

TilePanelItemList

TilePanelItemList() = default

An empty list.

TilePanelItemList

template <typename... S>
TilePanelItemList(S&& ... items)
    requires (sizeof...(S) > 0 && (std::same_as<S, TilePanelItem> && ...))

Builds every item's content and takes ownership of it.

TilePanelItemList

TilePanelItemList(const TilePanelItemList&) = delete

operator=

TilePanelItemList& operator=(const TilePanelItemList&) = delete

TilePanelItemList

TilePanelItemList(TilePanelItemList&&) = default

operator=

TilePanelItemList& operator=(TilePanelItemList&&) = default

add

void add(TilePanelItem item)

Builds item's content and appends it, as one more entry of the braced list would.

struct Placed

One built child with its spans; the node is already owned by the list.

Field Type Default
row_span int Cell rows covered.
column_span int Cell columns covered.
node std::unique_ptr<Element> The built child.

Tests

  • tests/test_layout_panels.cpp