Selkie--Align

NAME

Selkie::Align - Text alignment and box cross-axis alignment enums

SYNOPSIS

use Selkie::Align;
use Selkie::Widget::Text;
use Selkie::Layout::VBox;
use Selkie::Sizing;

# Centre a banner's text inside its own plane.
my $banner = Selkie::Widget::Text.new(
    text   => 'Selkie',
    align  => TextCenter,
    sizing => Sizing.fixed(1),
);
$banner.set-align(TextRight);

# Centre a fixed-width card inside a full-width column.
my $col = Selkie::Layout::VBox.new(sizing => Sizing.flex, align-items => CrossCenter);
$col.add: Selkie::Widget::Text.new(
    text         => 'a narrow card',
    cross-sizing => Sizing.fixed(20),   # 20 columns, centred in the VBox
    sizing       => Sizing.fixed(3),
);

DESCRIPTION

Two enums, kept in one module so every widget that positions something inside a wider slot speaks the same language.

TextAlign is about glyphs within a widget's own plane: Selkie::Widget::Text offsets each wrapped line by Text.align-column. CrossAlign is about widgets within a container: Selkie::Layout::VBox and Selkie::Layout::HBox place each child along the axis they do not stack on.

The two are orthogonal, and so is gap: gap reserves cells along the main axis (between children), alignment moves a child along the cross axis. A VBox with gap =E<gt> 1 and align-items =E<gt> CrossCenter gets both, independently.

The cross axis

A VBox stacks children top to bottom, so its main axis is rows and its cross axis is columns. An HBox is the mirror image: main axis columns, cross axis rows. "Cross-axis alignment" therefore means horizontal placement in a VBox and vertical placement in an HBox.

Two attributes drive it, and they answer different questions:

  • How big is the child on the cross axis? Widget.cross-sizing β€” a Selkie::Sizing resolved against the container's cross extent. Undefined (the default) means "as big as the container", which is what Selkie has always done.

  • Where does that size sit? CrossAlign β€” on the container as align-items, overridable per child as Widget.align-self.

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

# Both are 1 row tall; the second opts out of the container's rule.
$row.add: $stamp;                     # cross-sizing => Sizing.fixed(1) β€” top
$row.add: $badge;                     # …and align-self => CrossEnd β€” bottom
$badge.set-align-self(CrossEnd);

Defaults are the old behaviour

CrossFill plus an undefined cross-sizing is exactly what VBox and HBox did before alignment existed: every child gets the container's full cross extent at offset 0. TextLeft likewise puts every line at column 0. Nothing in an existing app moves by a cell.

EXAMPLES

A centred, fixed-width login form

my $screen = Selkie::Layout::VBox.new(
    sizing      => Sizing.flex,
    gap         => 1,
    align-items => CrossCenter,
);
$screen.add: Selkie::Widget::Text.new(
    text         => 'Sign in',
    align        => TextCenter,
    cross-sizing => Sizing.fixed(40),
    sizing       => Sizing.fixed(1),
);
$screen.add: $username-row;    # cross-sizing => Sizing.fixed(40)
$screen.add: $password-row;    # cross-sizing => Sizing.fixed(40)

A right-aligned status column

my $col = Selkie::Layout::VBox.new(sizing => Sizing.flex, align-items => CrossEnd);
$col.add: Selkie::Widget::Text.new(
    text         => '3 unread',
    cross-sizing => Sizing.percent(50),   # half the column, flush right
    sizing       => Sizing.fixed(1),
);

Note the difference between the two ways of pushing text to the right: TextRight moves the glyphs inside a full-width widget, while CrossEnd moves the widget inside a wider container. Reach for TextRight when the widget's own background should span the row, and for CrossEnd when it shouldn't.

SEE ALSO

Horizontal placement of a line of text inside its widget's plane. TextLeft is the default and the historical behaviour: every line starts at column 0. TextCenter and TextRight shift each line independently, so a wrapped paragraph comes out ragged-left rather than block-justified. Alignment is an offset, never padding β€” Selkie does not write spaces on either side of the line. That keeps the untouched cells showing the plane's base cell, which is what makes aligned text work over a scrim or a gradient.

Placement of a child along its container's cross axis β€” columns in a VBox, rows in an HBox. CrossFill is the default: the child spans the container's whole cross extent (unless it declares a cross-sizing, which always wins on size; CrossFill then places it at offset 0, like CrossStart). CrossStart is flush left / top, CrossEnd flush right / bottom, and CrossCenter splits the leftover space, rounding the leading side down.

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.