Skip to content

TwoPane

Header: winui/ui/panes/TwoPane.hpp. Spec: winui/ui/panes/TwoPane.hpp.

Two content panes that sit side by side when there is width, stacked when there is height, and collapse to one pane when there is neither.

Each measure pass picks the mode from the available size. It is Wide when the width exceeds min_wide_mode_width(), else Tall when the height exceeds min_tall_mode_height(), else a single pane chosen by pane_priority(). The two panes share the split axis by pane1_length() and pane2_length(), like two Grid tracks. The pane not shown takes no part in layout, rendering, focus or accessibility. There is no mode-changed event, so read mode() after layout. Panes are not wrapped in a Scroll, so put one in a pane that needs to scroll. Spanning two screens is not supported.

Example:

auto  view = std::make_unique<ui::TwoPaneControl>();
auto* list = view->set_pane1(std::make_unique<ui::ListViewControl>());
view->set_pane2(std::make_unique<ui::TextControl>("Select an item"));
view->set_pane1_length(ui::GridLength::dip(320.0f));
view->set_min_wide_mode_width(500.0f);
view->set_tall_mode_configuration(ui::TwoPaneTallModeConfiguration::SinglePane);
window.set_content(std::move(view));

// Or declaratively:
window.set_content(ui::TwoPane{
    .pane1_length        = ui::GridLength::dip(320.0f),
    .min_wide_mode_width = 500.0f,
    .pane1               = ui::Text{.text = "Master"},
    .pane2               = ui::Text{.text = "Detail"},
});

ui::TwoPane

Two regions that sit side by side, stacked or alone, depending on the space they get. Builds a ui::TwoPaneControl. No event reports the mode. Read TwoPaneControl::mode() after layout.

Each measure picks the mode from the available size. It is Wide when the width exceeds min_wide_mode_width, else Tall when the height exceeds min_tall_mode_height, else the single pane pane_priority names.

Example:

using ui::operator""_dip;

window.set_content(ui::TwoPane{
    .pane1_length        = 320_dip,
    .min_wide_mode_width = 500.0f,
    .pane1               = ui::ListView<std::string>{.items = {"Inbox", "Sent", "Drafts"}},
    .pane2               = ui::Text{.text = "Select an item"},
});

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

ui::TwoPane{
    .pane1_length, .pane2_length, .pane_priority, .wide_mode_configuration,
    .tall_mode_configuration, .min_wide_mode_width, .min_tall_mode_height, .style, .margin,
    .width, .height, .min_width, .min_height, .max_width, .max_height, .horizontal_alignment,
    .vertical_alignment, .visible, .tool_tip, .accessibility, .on_pointer_entered,
    .on_pointer_exited, .on_got_focus, .on_lost_focus, .on_key_down, .on_wheel, .control,
    .pane1, .pane2
}

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

Field Type Default
pane1_length ui::Binding<ui::GridLength> {ui::GridLength::auto_()} Pane 1's share of the split axis (Auto by default).
pane2_length ui::Binding<ui::GridLength> {ui::GridLength::fraction()} Pane 2's share; one fraction by default, so it takes the rest.
pane_priority ui::Binding<ui::TwoPanePriority> {ui::TwoPanePriority::Pane1} The pane shown alone in SinglePane mode.
wide_mode_configuration ui::Binding<ui::TwoPaneWideModeConfiguration> {ui::TwoPaneWideModeConfiguration::LeftRight} The Wide arrangement: LeftRight (the default), RightLeft, or SinglePane to never split.
tall_mode_configuration ui::Binding<ui::TwoPaneTallModeConfiguration> {ui::TwoPaneTallModeConfiguration::TopBottom} The Tall arrangement: TopBottom (the default), BottomTop, or SinglePane to never stack.
min_wide_mode_width ui::Binding<float> {641.0f} The width threshold in DIPs. Wide needs more available width than this.
min_tall_mode_height ui::Binding<float> {641.0f} The height threshold in DIPs. Tall needs more available height than this.
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::TwoPane> Out: the handle that receives the built control.
pane1 ui::Spec The first region: left or top by default, and the one shown alone unless pane_priority says otherwise.
pane2 ui::Spec The second region.

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

build

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

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

ui::TwoPaneControl

Derives from ui::Element.

Two content panes that sit side by side when there is width, stacked when there is height, and collapse to one pane when there is neither.

Each measure pass picks the mode from the available size. It is Wide when the width exceeds min_wide_mode_width(), else Tall when the height exceeds min_tall_mode_height(), else a single pane chosen by pane_priority(). The two panes share the split axis by pane1_length() and pane2_length(), like two Grid tracks. The pane not shown takes no part in layout, rendering, focus or accessibility. There is no mode-changed event, so read mode() after layout. Panes are not wrapped in a Scroll, so put one in a pane that needs to scroll. Spanning two screens is not supported.

Example:

auto  view = std::make_unique<ui::TwoPaneControl>();
auto* list = view->set_pane1(std::make_unique<ui::ListViewControl>());
view->set_pane2(std::make_unique<ui::TextControl>("Select an item"));
view->set_pane1_length(ui::GridLength::dip(320.0f));
view->set_min_wide_mode_width(500.0f);
view->set_tall_mode_configuration(ui::TwoPaneTallModeConfiguration::SinglePane);
window.set_content(std::move(view));

// Or declaratively:
window.set_content(ui::TwoPane{
    .pane1_length        = ui::GridLength::dip(320.0f),
    .min_wide_mode_width = 500.0f,
    .pane1               = ui::Text{.text = "Master"},
    .pane2               = ui::Text{.text = "Detail"},
});

TwoPaneControl

TwoPaneControl() = default

An empty view: no panes, pane 1 Auto, pane 2 one fraction, both thresholds 641 DIPs.

set_pane1

template <typename T>
T* set_pane1(std::unique_ptr<T> content)

Takes ownership of pane 1 and returns a non-owning observer, like ExpanderControl::set_content(). Destroys any previous pane 1. Null clears it. Lays the view out again.

set_pane2

template <typename T>
T* set_pane2(std::unique_ptr<T> content)

Takes ownership of pane 2 and returns a non-owning observer; see set_pane1().

pane1

Element* pane1() const

Pane 1, or null before set_pane1(). Owned by the view.

pane2

Element* pane2() const

Pane 2, or null before set_pane2(). Owned by the view.

pane1_length

GridLength pane1_length() const

Pane 1's track along the split axis, a GridLength as in a Grid track (DIPs, Auto or a fraction); Auto by default.

set_pane1_length

void set_pane1_length(GridLength length)

Changes pane 1's track and lays the view out again. Only used while both panes are shown.

pane2_length

GridLength pane2_length() const

Pane 2's track along the split axis; one fraction by default, so it takes the rest.

set_pane2_length

void set_pane2_length(GridLength length)

Changes pane 2's track and lays the view out again.

pane_priority

TwoPanePriority pane_priority() const

The pane shown in SinglePane mode; Pane1 by default.

set_pane_priority

void set_pane_priority(TwoPanePriority priority)

Changes the single-pane choice and lays the view out again.

wide_mode_configuration

TwoPaneWideModeConfiguration wide_mode_configuration() const

How the panes are arranged in Wide mode; LeftRight by default.

set_wide_mode_configuration

void set_wide_mode_configuration(TwoPaneWideModeConfiguration configuration)

Changes the Wide arrangement and lays the view out again.

tall_mode_configuration

TwoPaneTallModeConfiguration tall_mode_configuration() const

How the panes are arranged in Tall mode; TopBottom by default.

set_tall_mode_configuration

void set_tall_mode_configuration(TwoPaneTallModeConfiguration configuration)

Changes the Tall arrangement and lays the view out again.

min_wide_mode_width

float min_wide_mode_width() const

The width threshold in DIPs; 641 by default. Wide needs more available width than this. An unbounded width never qualifies.

set_min_wide_mode_width

void set_min_wide_mode_width(float width)

Sets the Wide threshold, clamped to >= 0, and lays the view out again.

min_tall_mode_height

float min_tall_mode_height() const

The height threshold in DIPs; 641 by default. Tall needs more available height than this. Only tested when Wide did not apply.

set_min_tall_mode_height

void set_min_tall_mode_height(float height)

Sets the Tall threshold, clamped to >= 0, and lays the view out again.

mode

TwoPaneMode mode() const

The mode the most recent measure() pass picked; SinglePane before the first. No event reports a change. A caller that reacts to it reads it after layout, for example from its own tick_override().

for_each_child

void for_each_child(ChildVisitor visit) const override

Only the panes the mode shows. A hidden pane takes no part in any walk over the tree, such as layout and rendering.

measure_override

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

The view itself is not focusable. Tab reaches into the shown panes.

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.

render_override

void render_override(Renderer& renderer, const RenderContext& context) const 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().

Enumerations

TwoPanePriority

enum class TwoPanePriority

Which pane wins when the available space fits neither Wide nor Tall.

Value
Pane1 Show pane 1 alone (the default).
Pane2 Show pane 2 alone.

TwoPaneMode

enum class TwoPaneMode

Which axis, if either, the two panes are split along, as mode() reports it. The order of the panes on that axis comes from TwoPaneWideModeConfiguration or TwoPaneTallModeConfiguration, so mode() does not tell LeftRight from RightLeft.

Value
SinglePane Only the pane_priority() pane is shown.
Wide Both panes, side by side.
Tall Both panes, one above the other.

TwoPaneWideModeConfiguration

enum class TwoPaneWideModeConfiguration

How the panes are arranged once the width qualifies for Wide.

Value
SinglePane Never split horizontally; fall through to the Tall test.
LeftRight Pane 1 on the left (the default).
RightLeft Pane 1 on the right.

TwoPaneTallModeConfiguration

enum class TwoPaneTallModeConfiguration

How the panes are arranged once the height qualifies for Tall.

Value
SinglePane Never stack; show a single pane instead.
TopBottom Pane 1 on top (the default).
BottomTop Pane 1 at the bottom.

Tests

  • tests/test_two_pane.cpp