Selkie--EffectiveBounds

NAME

Selkie::EffectiveBounds - The on-screen rectangle a widget may safely paint into

SYNOPSIS

use Selkie::EffectiveBounds;

# Compute via Widget.effective-bounds — apps don't usually construct
# these directly:
my $eb = $some-widget.effective-bounds;

if $eb.is-empty {
    # Widget is entirely outside the visible region — don't paint
} else {
    # The visible rectangle is at ($eb.abs-y, $eb.abs-x), sized
    # $eb.rows by $eb.cols. The widget's own plane has $eb.clip-top
    # rows chopped off the top and $eb.clip-left cols off the left.
}

DESCRIPTION

Notcurses does not clip a child plane's painted content to its parent plane's bounds. A child plane sized larger than its parent paints past the parent's edge into siblings or grandparents. Pixel sprixels (the protocol-agnostic name covering Sixel, Kitty graphics, and iTerm2 inline images) are even worse — they paint at absolute terminal pixel coordinates regardless of any plane hierarchy.

Selkie::EffectiveBounds is the value class returned by Selkie::Widget's effective-bounds method, which walks the parent chain and computes the rectangular intersection of the widget's plane with every ancestor's plane and the terminal viewport. The result is the on-screen rectangle into which the widget may safely paint pixels — anything outside this rectangle would bleed past an ancestor's visible region.

Selkie::Widget::Image uses this to size its blit-plane to the visible intersection, ensuring sprixel pixels never overflow into territory occupied by other widgets. Custom widgets that allocate their own blit plane (following the Selkie::Widget::Image pattern) should do the same.

The clip-top / clip-left fields

When a widget is partially clipped on its top or left edge (typical for a CardList card scrolled past the top of the viewport, or a horizontal scroll), the visible rectangle's top-left does not coincide with the widget's plane's top-left. clip-top and clip-left tell the renderer how many rows / columns of its own plane fall outside the visible region at the leading edges, so a sub-plane (like an Image's blit-plane) can be positioned to land inside the visible intersection rather than at the widget's own (0, 0).

SEE ALSO

  • Selkie::Widget — owns effective-bounds and clip-to-ancestors

  • Selkie::Widget::Image — sizes its blit-plane to these bounds and drives the surrounding sprixel destroy / re-blit lifecycle

has Int $.abs-y

Top edge of the visible intersection in absolute screen coordinates.

has Int $.abs-x

Left edge of the visible intersection in absolute screen coordinates.

has UInt $.rows

Height of the visible intersection in cells. Zero when the widget is entirely outside its ancestors or the terminal.

has UInt $.cols

Width of the visible intersection in cells. Zero when the widget is entirely outside its ancestors or the terminal.

has UInt $.clip-top

Number of rows of the widget's own plane that fall above the visible intersection (chopped off the top by an ancestor's edge).

has UInt $.clip-left

Number of columns of the widget's own plane that fall left of the visible intersection (chopped off the left by an ancestor's edge).

method is-empty

method is-empty() returns Bool

True when the widget has no on-screen visible area — entirely outside an ancestor or the terminal viewport. Renderers should early-return on is-empty rather than emit any pixels.

sub intersect-rect

sub intersect-rect(
    Int :$ay!,
    Int :$ax!,
    Int :$ah! where { ... },
    Int :$aw! where { ... },
    Int :$by!,
    Int :$bx!,
    Int :$bh! where { ... },
    Int :$bw! where { ... },
    Int :$clip-top where { ... } = 0,
    Int :$clip-left where { ... } = 0
) returns Selkie::EffectiveBounds

Compute the rectangular intersection of two cell rectangles given as (abs-y, abs-x, rows, cols) tuples. Returns a new Selkie::EffectiveBounds with clip-top and clip-left reflecting how much of the first rectangle was chopped off its leading edges. is-empty when the rectangles don't overlap.

sub set-terminal-viewport-provider

sub set-terminal-viewport-provider(
    &p
) returns Nil

Set the terminal viewport provider — a closure returning (rows, cols) for the active terminal. Selkie::App calls this on init so Selkie::Widget.effective-bounds can intersect every widget against the terminal's visible area. Tests can pass a fixed-size closure to simulate a small terminal; pass { (1_000, 1_000) } to reset to the generous default.

sub terminal-viewport

sub terminal-viewport() returns List

Current terminal viewport dimensions as (rows, cols), queried through the provider closure (set by Selkie::App at init).

Selkie v0.10.0

High-level TUI framework built on Notcurses

Authors

  • Matt Doughty

License

Artistic-2.0

Dependencies

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

Test Dependencies

Provides

  • Selkie
  • Selkie::App
  • 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::Container
  • Selkie::EffectiveBounds
  • Selkie::Event
  • 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::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::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.