Skip to content

WrapPanel

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

A panel that places its children one after another along a line and starts a new line when the next child would cross the far edge. Use it for tag chips, a toolbar of variable-width buttons, or a gallery of cards that reflows with the window.

Along the orientation children sit item_spacing() apart; lines sit line_spacing() apart, each as tall as its tallest child, and every child in a line is arranged at the line's full height. The panel asks for its widest line by the sum of its lines, plus the padding. A first child wider than the whole panel does not push an empty line in front of itself. Built declaratively by ui::WrapPanel.

Example:

// Declarative:
window.set_content(ui::WrapPanel{
    .item_spacing = 8.0f,
    .line_spacing = 8.0f,
    .padding      = ui::Thickness{12.0f},
    .content      = {
        ui::Button{.text = "Alpha"},
        ui::Button{.text = "Beta"},
        ui::Button{.text = "A much longer gamma"},
        ui::Button{.text = "Delta"},
    },
});

// Imperative:
auto panel = std::make_unique<ui::WrapPanelControl>();
panel->set_orientation(ui::Orientation::Horizontal);
panel->set_item_spacing(8.0f);
panel->set_line_spacing(4.0f);
panel->set_items_stretch(ui::WrapPanelItemsStretch::Last);
for (const char* tag : {"red", "green", "blue", "cyan", "magenta"})
{
    panel->add(std::make_unique<ui::ButtonControl>(tag));
}
window.set_content(std::move(panel));

ui::WrapPanel

Children in lines that wrap. Builds a ui::WrapPanelControl.

Children keep their desired size; a line breaks when the next child would not fit.

Example:

ui::Spec chip(const char* text) { return ui::Button{.text = text}; }

window.set_content(ui::WrapPanel{
    .item_spacing = 8,
    .line_spacing = 8,
    .padding      = ui::Thickness{12.0f},
    .content      = {chip("Alpha"), chip("Beta item"), chip("Gamma"), chip("Delta item")},
});

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

ui::WrapPanel{
    .orientation, .item_spacing, .line_spacing, .items_stretch, .padding, .background,
    .border_brush, .border_thickness, .corner_radius, .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::Horizontal} Horizontal (the default) fills rows and wraps downward; Vertical fills columns.
item_spacing ui::Binding<float> {0.0f} Gap between neighbors along a line, DIPs.
line_spacing ui::Binding<float> {0.0f} Gap between lines, DIPs.
items_stretch ui::Binding<ui::WrapPanelItemsStretch> {ui::WrapPanelItemsStretch::None} None keeps every child's length; Last widens the last child to the end of its line.
padding ui::Binding<ui::Thickness> {ui::Thickness{}} Inset between the panel's edge and the children, DIPs.
background ui::Binding<ui::Paint> Background: a Color, or a ThemeColor read from the theme; empty (the default) draws none.
border_brush ui::Binding<ui::Paint> Border color, a Color or a ThemeColor; empty (the default) draws no border.
border_thickness ui::Binding<ui::Thickness> {ui::Thickness{}} Border width per edge, DIPs; it insets the children like padding.
corner_radius ui::Binding<float> {0.0f} Corner radius of the background and the border, DIPs.
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::WrapPanel> Out: the handle that receives the built control.
content ui::SpecList The wrapped children, in reading order.

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

ui::WrapPanelControl

Derives from ui::Panel.

A panel that places its children one after another along a line and starts a new line when the next child would cross the far edge. Use it for tag chips, a toolbar of variable-width buttons, or a gallery of cards that reflows with the window.

Along the orientation children sit item_spacing() apart; lines sit line_spacing() apart, each as tall as its tallest child, and every child in a line is arranged at the line's full height. The panel asks for its widest line by the sum of its lines, plus the padding. A first child wider than the whole panel does not push an empty line in front of itself. Built declaratively by ui::WrapPanel.

Example:

// Declarative:
window.set_content(ui::WrapPanel{
    .item_spacing = 8.0f,
    .line_spacing = 8.0f,
    .padding      = ui::Thickness{12.0f},
    .content      = {
        ui::Button{.text = "Alpha"},
        ui::Button{.text = "Beta"},
        ui::Button{.text = "A much longer gamma"},
        ui::Button{.text = "Delta"},
    },
});

// Imperative:
auto panel = std::make_unique<ui::WrapPanelControl>();
panel->set_orientation(ui::Orientation::Horizontal);
panel->set_item_spacing(8.0f);
panel->set_line_spacing(4.0f);
panel->set_items_stretch(ui::WrapPanelItemsStretch::Last);
for (const char* tag : {"red", "green", "blue", "cyan", "magenta"})
{
    panel->add(std::make_unique<ui::ButtonControl>(tag));
}
window.set_content(std::move(panel));

WrapPanelControl

WrapPanelControl() = default

An empty horizontal panel with no spacing, no padding and no stretch.

add

template <typename T>
T* add(std::unique_ptr<T> child)

Appends child, taking ownership, and returns a non-owning observer to it. Children are placed in the order added.

add

template <SpecLike S>
Element* add(S&& spec)

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.

orientation

Orientation orientation() const

The flow direction: Horizontal (rows, the default) or Vertical (columns).

set_orientation

void set_orientation(Orientation orientation)

Changes the flow direction; invalidates layout.

item_spacing

float item_spacing() const

The gap between two children in a line, in DIPs. Default 0.

set_item_spacing

void set_item_spacing(float spacing)

Sets the gap between children; invalidates layout.

line_spacing

float line_spacing() const

The gap between two lines, in DIPs. Default 0.

set_line_spacing

void set_line_spacing(float spacing)

Sets the gap between lines; invalidates layout.

items_stretch

WrapPanelItemsStretch items_stretch() const

Whether the last child is widened to the end of its line. Default None.

set_items_stretch

void set_items_stretch(WrapPanelItemsStretch stretch)

Sets whether the last child is widened; invalidates layout.

parallel_measure_safe

bool parallel_measure_safe() const override

True: measuring touches only the panel and its children.

border_brush

std::optional<Color> border_brush() const

The outline color; nullopt (the default) draws no border even with a non-zero border_thickness(). Nullopt as well while a ThemeColor is set.

border_brush_resource

ThemeColor border_brush_resource() const

The theme resource the border is drawn from, or null.

border_thickness

const Thickness& border_thickness() const

The border width in DIPs per side (default zero); it insets the children like padding.

corner_radius

float corner_radius() const

The corner radius in DIPs, uniform for all four corners (default 0, square).

padding

const Thickness& padding() const

The padding in DIPs between the border and the children (default zero).

set_border_brush

void set_border_brush(std::optional<Color> color)

A fixed outline color; replaces any resource. Marks the panel for redraw.

set_border_brush

void set_border_brush(ThemeColor resource)

An outline color read from the theme each time the panel is drawn; replaces any fixed color. Marks the panel for redraw.

set_border_thickness

void set_border_thickness(const Thickness& thickness)

Sets the border width; marks the panel for layout and redraw.

set_corner_radius

void set_corner_radius(float radius)

Sets the corner radius; marks the panel for redraw.

set_padding

void set_padding(const Thickness& padding)

Sets the padding; marks the panel for layout.

measure_override

Size measure_override(Size available, const MeasureContext& context) const override

Lays the children out in lines against available (less the padding) and returns the widest line by the sum of the lines, plus the padding.

arrange_override

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

Wraps against final_rect's size and places each child in its slot, every child as tall as its line and the last one stretched when items_stretch() is Last.

Enumerations

WrapPanelItemsStretch

enum class WrapPanelItemsStretch

Whether a WrapPanel stretches its last child.

Value
None every child keeps its desired length along the line (the default)
Last the panel's last child is widened to the end of its line

Tests

  • tests/test_layout_panels.cpp