Selkie--Container
NAME
Selkie::Container - Role for widgets that hold child widgets
SYNOPSIS
A minimal custom container that stacks its children vertically with a one-row gap between them:
use Selkie::Widget;
use Selkie::Container;
use Selkie::Sizing;
unit class My::GapBox does Selkie::Container;
method render() {
my $y = 0;
for self.children -> $child {
if $child.plane {
$child.reposition($y, 0);
$child.resize($child.sizing.value, self.cols);
} else {
$child.init-plane(
self.plane,
y => $y, x => 0,
rows => $child.sizing.value,
cols => self.cols,
);
}
$child.render;
$y += $child.sizing.value + 1; # leave a 1-row gap
}
self.clear-dirty;
}DESCRIPTION
Selkie::Container layers on top of Selkie::Widget (also does Selkie::Widget). Compose it for any widget that owns child widgets ā layouts (VBox, HBox, Split), decorators (Border, Modal), scrollers (ScrollView).
The role provides:
A
childrenlist, manipulated viaadd,remove,clearAutomatic store propagation to added children
Recursive destruction and subscription cleanup on
remove/clearA
focusable-descendantswalker soSelkie::Appcan build the Tab cycleA
!render-childrenhelper that cascades dirty flags for correct subtree redraws
Your container's job is to implement render, which positions and sizes each child before rendering it. For typical layouts, lean on VBox/HBox/Split instead of building your own container from scratch.
EXAMPLES
Adding and removing children
my $vbox = Selkie::Layout::VBox.new(sizing => Sizing.flex);
my $header = Selkie::Widget::Text.new(text => 'Hi', sizing => Sizing.fixed(1));
$vbox.add($header);
# Later ā remove cleans up the widget's plane, subscriptions, and children
$vbox.remove($header);Rebuilding from scratch
$vbox.clear; # destroys all children
$vbox.add($new-a);
$vbox.add($new-b);Writing your own container
If the built-in layouts don't fit, compose Selkie::Container directly and implement render. Use !render-children (inherited) to cascade dirty flags and render each child ā this ensures subtree correctness when the container is dirty:
method render() {
self!layout-children; # your own positioning logic
self!render-children; # handles dirty cascade + per-child render
self.clear-dirty;
}SEE ALSO
Selkie::Widget ā the base role
Containerbuilds onSelkie::Layout::VBox, Selkie::Layout::HBox, Selkie::Layout::Split ā the built-in containers
Selkie::Widget::Border, Selkie::Widget::Modal ā decorators that also compose
Container
method children
method children() returns ListThe current list of children, in insertion order. Immutable list ā to modify, use add, remove, or clear.
method add
method add(
Selkie::Widget $child
) returns Selkie::WidgetAdd a child widget. The child's parent is set, the store is propagated to it (and its subtree), and the container is marked dirty. Returns the added child for chaining.
method remove
method remove(
Selkie::Widget $child
) returns MuRemove and destroy a specific child. Unsubscribes the child and its entire subtree from the store before destroying. No-op if the given widget isn't actually a child.
method clear
method clear() returns MuRemove and destroy every child. Useful before rebuilding the container's contents from scratch (e.g. in a subscription callback that regenerates a list).
method render-children
method render-children() returns MuRender each child, cascading dirty to the whole subtree if the container itself is dirty. This is the rendering helper you almost always want in a custom container's render method ā it handles the "parent dirty ā children also need redrawing" rule correctly. Private so composed classes can call it as self!render-children.
method park
method park() returns MuPark self plus every child/content recursively. Container override of Widget.park ā without this, swapping a container off-screen only moves the container's own plane; descendants whose visibility isn't tied to their parent's plane position (e.g. Image's sprixel, Modal's bg-plane) keep showing on terminal.
method focusable-descendants
method focusable-descendants() returns SeqDepth-first sequence of focusable descendants. Used by Selkie::App to build the Tab/Shift-Tab cycle. Walks children recursively, yielding any whose focusable is True. Override if your container needs a non-standard traversal order.
method destroy
method destroy() returns MuDestroy the container and every child recursively. Called automatically when the widget goes out of scope or its parent calls remove.