Skip to content

LayoutPanel

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

A panel whose measure and arrange are delegated to an attachable Layout.

Use it for a fixed set of children with a layout that no built-in panel offers. That is a custom NonVirtualizingLayout, or a StackLayoutEngine, TileLayoutEngine, WrapLayoutEngine or LinedFlowLayoutEngine applied to ordinary children, where every child counts as realized. Border and padding come off the room before the layout sees it. With no layout, every child fills the whole content box, stacked on top of each other.

Example:

// Declarative:
window.set_content(ui::LayoutPanel{
    .layout   = ui::TileLayout{},
    .padding  = ui::Thickness{12.0f},
    .content  = {ui::Text{.text = "A"}, ui::Text{.text = "B"}, ui::Text{.text = "C"}},
});

// Imperative:
auto  panel = std::make_unique<ui::LayoutPanelControl>();
auto* stack = panel->set_layout(std::make_unique<ui::StackLayoutEngine>());
stack->set_spacing(8.0f);
panel->add(std::make_unique<ui::TextControl>("First"));
panel->add(std::make_unique<ui::TextControl>("Second"));
panel->set_padding(ui::Thickness{12.0f});
window.set_content(std::move(panel));

ui::LayoutPanel

Children measured and arranged by a Layout object. Builds a ui::LayoutPanelControl. The panel takes ownership of .layout; reach it again through .control's layout().

The counterpart of ui::ForEach for hand-written children: every child is realized, and any ui::Layout (a stock one or the app's own subclass) places them.

Example:

auto grid = std::make_unique<ui::TileLayoutEngine>();
grid->set_min_row_spacing(8.0f);
grid->set_min_column_spacing(8.0f);

window.set_content(ui::LayoutPanel{
    .layout   = std::move(grid),
    .padding  = ui::Thickness{12.0f},
    .content  = {
        ui::Button{.text = "One"},
        ui::Button{.text = "Two"},
        ui::Button{.text = "Three"},
    },
});

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

ui::LayoutPanel{
    .layout, .background, .border_brush, .border_thickness, .corner_radius, .padding, .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
layout ui::LayoutSpec The layout that measures and arranges the children; the panel takes ownership.
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> Border width per edge, DIPs.
corner_radius ui::Binding<float> {0.0f} Corner radius, DIPs.
padding ui::Binding<ui::Thickness> {ui::Thickness{}} Padding between the border and the laid-out children, 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::LayoutPanel> Out: the handle that receives the built control.
content ui::SpecList The children, in the order the layout sees them.

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

ui::LayoutPanelControl

Derives from ui::Panel, ui::LayoutOwner.

A panel whose measure and arrange are delegated to an attachable Layout.

Use it for a fixed set of children with a layout that no built-in panel offers. That is a custom NonVirtualizingLayout, or a StackLayoutEngine, TileLayoutEngine, WrapLayoutEngine or LinedFlowLayoutEngine applied to ordinary children, where every child counts as realized. Border and padding come off the room before the layout sees it. With no layout, every child fills the whole content box, stacked on top of each other.

Example:

// Declarative:
window.set_content(ui::LayoutPanel{
    .layout   = ui::TileLayout{},
    .padding  = ui::Thickness{12.0f},
    .content  = {ui::Text{.text = "A"}, ui::Text{.text = "B"}, ui::Text{.text = "C"}},
});

// Imperative:
auto  panel = std::make_unique<ui::LayoutPanelControl>();
auto* stack = panel->set_layout(std::make_unique<ui::StackLayoutEngine>());
stack->set_spacing(8.0f);
panel->add(std::make_unique<ui::TextControl>("First"));
panel->add(std::make_unique<ui::TextControl>("Second"));
panel->set_padding(ui::Thickness{12.0f});
window.set_content(std::move(panel));

LayoutPanelControl

LayoutPanelControl() = default

An empty panel with no layout.

~LayoutPanelControl

~LayoutPanelControl() override

add

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

Takes ownership of child, appends it after the existing children, and returns a non-owning observer. Updates the layout.

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.

set_layout

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

Takes ownership of the layout, destroying any previous one, and returns an observer to it. nullptr leaves the panel with none.

layout

Layout* layout() const

The current layout (non-owning), or nullptr when the panel has none.

parallel_measure_safe

bool parallel_measure_safe() const override

True without a layout, otherwise what the layout answers (Layout::parallel_measure_safe()).

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

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.

Tests

  • tests/test_layout_panels.cpp