Selkie--Layout--HBox

NAME

Selkie::Layout::HBox - Arrange children left to right

SYNOPSIS

use Selkie::Layout::HBox;
use Selkie::Sizing;

my $row = Selkie::Layout::HBox.new(sizing => Sizing.fixed(1));
$row.add: $label;     # Sizing.fixed(8)
$row.add: $input;     # Sizing.flex
$row.add: $button;    # Sizing.fixed(10)

DESCRIPTION

HBox arranges children horizontally. Allocation follows the same three-pass sizing rule as Selkie::Layout::VBox, but operates on columns instead of rows.

Children get the full parent height unless they say otherwise with cross-sizing — see [/Cross-axis alignment](/Cross-axis alignment).

Gaps

gap reserves columns between children — the least fiddly way to space out a button row or keep a sidebar off its neighbour:

my $buttons = Selkie::Layout::HBox.new(sizing => Sizing.fixed(1), gap => 2);
$buttons.add: Selkie::Widget::Button.new(label => 'Cancel', sizing => Sizing.flex);
$buttons.add: Selkie::Widget::Button.new(label => 'OK',     sizing => Sizing.flex);

$buttons.set-gap(4);      # marks dirty; relayout on the next render

Note that this replaces the old spacer-child idiom (an empty Text with Sizing.fixed(2) between the buttons): a gap is not a widget, so it costs no plane and paints nothing.

Gutters come off the top of the column budget before any child is sized, so Sizing.percent resolves against the content box (columns minus gutters) — see Selkie::Layout::Allocate. The rules match Selkie::Layout::VBox exactly: no leading or trailing gap, no gap beside a child allocated zero columns (it's parked), and gap cells stay unpainted so the HBox's own plane base shows through.

gap defaults to 0, which allocates and positions exactly as HBox always has. Selkie::Layout::Split deliberately has no gap — it separates its panes with a divider it draws and drags itself.

Cross-axis alignment

An HBox lays out columns, so its cross axis is rows: align-items places children vertically, and a child's cross-sizing decides how tall it is. This is the natural way to vertically centre a one-row label beside a taller pane:

my $row = Selkie::Layout::HBox.new(
    sizing      => Sizing.flex,
    gap         => 2,
    align-items => CrossCenter,
);

$row.add: Selkie::Widget::Text.new(
    text         => 'Status:',
    sizing       => Sizing.fixed(10),
    cross-sizing => Sizing.fixed(1),    # one row, vertically centred
);
$row.add: $detail-pane;                 # full height — no cross-sizing

# One child pinned to the bottom regardless of the row's policy.
$row.add: my $stamp = Selkie::Widget::Text.new(
    text         => '12:04',
    sizing       => Sizing.fixed(5),
    cross-sizing => Sizing.fixed(1),
    align-self   => CrossEnd,
);

The rules are Selkie::Layout::VBox's with the axes swapped: CrossFill (the default) plus an undefined cross-sizing is the historical full-height layout at row 0; align-self beats align-items; a child resolving to zero rows is parked but keeps its columns and its gutter, so a cross-axis collapse never reflows the row; and gap, which only ever touches columns here, is entirely orthogonal.

EXAMPLES

Three-column main layout

The classic file-manager pattern: sidebar + main + details.

my $columns = Selkie::Layout::HBox.new(sizing => Sizing.flex);
$columns.add: $sidebar;        # Sizing.fixed(20)
$columns.add: $main-content;   # Sizing.flex
$columns.add: $details;        # Sizing.fixed(30)

A button row

my $buttons = Selkie::Layout::HBox.new(sizing => Sizing.fixed(1));
$buttons.add: Selkie::Widget::Button.new(label => 'Cancel', sizing => Sizing.flex);
$buttons.add: Selkie::Widget::Text.new(text => '', sizing => Sizing.fixed(2));   # spacer
$buttons.add: Selkie::Widget::Button.new(label => 'OK', sizing => Sizing.flex);

SEE ALSO

has UInt $.gap

Columns of empty space reserved between adjacent children. Defaults to 0 — no gutter, and layout identical to a gap-free HBox. The total reservation is gap-reserve from Selkie::Layout::Allocate: children collapsed to Sizing.fixed(0) don't get a gutter.

method set-gap

method set-gap(
    Int:D $g where { ... }
) returns Nil

Change the inter-child gutter and mark the box dirty so the next render re-runs the allocation. 0 restores the gap-free layout.

has CrossAlign $.align-items

Where children sit vertically — the HBox's cross axis. Defaults to CrossFill: every child is given the box's full height at row 0, exactly as HBox has always laid out. A child overrides this for itself with align-self, and controls how tall it is with cross-sizing. See Selkie::Align.

method set-align-items

method set-align-items(
    CrossAlign:D $a
) returns Nil

Change the cross-axis alignment policy for children that haven't set their own align-self, and mark the box dirty so the next render re-runs the layout.

method render

method render() returns Mu

Perform layout and render each child. Called automatically by the render cycle. Columns are allocated using the same three-pass strategy as VBox, applied to the width axis.

method handle-resize

method handle-resize(
    Int $rows where { ... },
    Int $cols where { ... }
) returns Mu

Resize this container's own plane. Children are re-laid-out in render, not here. See VBox for the rationale.

Selkie v0.11.1

High-level TUI framework built on Notcurses

Authors

  • Matt Doughty

License

Artistic-2.0

Dependencies

Notcurses::Native:ver<0.4.1+>:auth<zef:apogee>

Test Dependencies

Provides

  • Selkie
  • Selkie::Align
  • Selkie::Alpha
  • Selkie::App
  • Selkie::App::Internal::Animation
  • Selkie::App::Internal::Dispatch
  • Selkie::App::Internal::ErrorLog
  • Selkie::App::Internal::FocusTree
  • Selkie::App::Internal::HitTest
  • Selkie::App::Internal::IdleBudget
  • Selkie::App::Internal::OverlayTree
  • Selkie::App::Internal::RenderLoop
  • Selkie::App::Internal::ScreenModalLifecycle
  • Selkie::App::Internal::Terminal
  • Selkie::App::Internal::TerminalSequences
  • Selkie::BorderStyle
  • Selkie::Container
  • Selkie::EffectiveBounds
  • Selkie::Event
  • Selkie::Gradient
  • Selkie::Layout::Allocate
  • Selkie::Layout::HBox
  • Selkie::Layout::Split
  • Selkie::Layout::VBox
  • Selkie::Plot::Palette
  • Selkie::Plot::Scaler
  • Selkie::Plot::Ticks
  • Selkie::ScreenManager
  • Selkie::Sizing
  • Selkie::Store
  • Selkie::Style
  • Selkie::Test::Focus
  • Selkie::Test::Keys
  • Selkie::Test::Snapshot
  • Selkie::Test::Snapshot::Harness
  • Selkie::Test::Store
  • Selkie::Test::Supply
  • Selkie::Test::Tree
  • Selkie::Theme
  • Selkie::Trace
  • Selkie::Tree
  • Selkie::Tween
  • Selkie::Widget
  • Selkie::Widget::Axis
  • Selkie::Widget::BarChart
  • Selkie::Widget::Border
  • Selkie::Widget::Button
  • Selkie::Widget::CardList
  • Selkie::Widget::Checkbox
  • Selkie::Widget::CommandPalette
  • Selkie::Widget::ConfirmModal
  • Selkie::Widget::FileBrowser
  • Selkie::Widget::FocusableByDefault
  • Selkie::Widget::GradientFill
  • Selkie::Widget::Heatmap
  • Selkie::Widget::HelpOverlay
  • Selkie::Widget::Histogram
  • Selkie::Widget::Image
  • Selkie::Widget::Legend
  • Selkie::Widget::LineChart
  • Selkie::Widget::ListView
  • Selkie::Widget::Modal
  • Selkie::Widget::MultiLineInput
  • Selkie::Widget::PasswordStrength
  • Selkie::Widget::Plot
  • Selkie::Widget::ProgressBar
  • Selkie::Widget::RadioGroup
  • Selkie::Widget::RichText
  • Selkie::Widget::RichText::Span
  • Selkie::Widget::ScatterPlot
  • Selkie::Widget::ScrollView
  • Selkie::Widget::Select
  • Selkie::Widget::Sparkline
  • Selkie::Widget::Spinner
  • Selkie::Widget::TabBar
  • Selkie::Widget::Table
  • Selkie::Widget::Text
  • Selkie::Widget::TextInput
  • Selkie::Widget::TextInput::HighlightSpan
  • Selkie::Widget::TextStream
  • Selkie::Widget::Toast
  • Selkie::Widget::ViewportedCardList

Documentation

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.

Built with Podlite — the markup and publishing tools behind this site.