Selkie.git | docs/api/ | Selkie--Widget--Toast.md


NAME
====

Selkie::Widget::Toast - Transient overlay notification

SYNOPSIS
========

You normally use `$app.toast(...)` which manages the widget for you:

```raku
$app.toast('Settings saved');
$app.toast('Connection lost', duration => 5e0);
```

Direct construction is rarely needed.

DESCRIPTION
===========

A centered single-line message bar that auto-dismisses. By convention rendered near the bottom of the screen.

Unlike most widgets, Toast does **not** own a backing plane covering its full area — that would obscure the widgets behind it. Instead it manages a small inline plane, created on `show` and destroyed on hide, attached directly to the parent stdplane via `attach`.

The `Selkie::App.toast` wrapper hides these details: it lazily constructs the widget, calls `attach`, and ensures the correct size on each invocation.

Fading
------

Off by default. `Selkie::App.new(:animate-toast)` turns it on, at which point `Selkie::App` hands the toast its tween group via `enable-fade` and every `show` resolves the bar up out of the screen background over `fade-in-seconds` and dissolves it back over `fade-out-seconds` before it disappears.

```raku
my $app = Selkie::App.new(theme => $theme, :animate-toast);
$app.toast('Saved');          # fades in, holds, fades out

# Driving a detached Toast yourself:
$toast.enable-fade($app.tweens);
$toast.show('Saved', duration => 2e0);
```

What ramps is **colour**, not alpha — notcurses alpha is a two-bit enum with no intermediate states ([Selkie::Alpha](Selkie--Alpha.md)), so a fade is a walk between two RGB values. The far end is `theme.base.bg` for both the foreground and the background, i.e. a bar the same colour as the screen behind it. A theme whose `base` carries no `bg` has no colour to fade from, and the toast simply appears and disappears as it always has.

The out-fade is armed from inside `tick`, which `Selkie::App` already calls once per frame: when the remaining lifetime drops below `fade-out-seconds` the toast adds one reversed tween to the group and never looks at the clock again. Both tweens are bounded, and the toast cancels whatever is in flight on the next `show`, on dismissal, and in `destroy` — so a fade can never outlive the plane it paints into.

Timings are `fade-in-seconds` (0.1) and `fade-out-seconds` (0.2), settable at construction. A toast whose whole duration is shorter than `fade-out-seconds` gets an out-fade clamped to the time it actually has left rather than one that would run past its own dismissal.

EXAMPLES
========

Custom styling
--------------

```raku
# Red warning style
$app.store.subscribe-with-callback(
    'errors',
    -> $s { $s.get-in('error') // '' },
    -> $msg {
        if $msg.chars > 0 {
            $app.toast($msg);   # default blue-highlight style
        }
    },
    $some-widget,
);
```

SEE ALSO
========

  * [Selkie::App](Selkie--App.md) — `toast(...)` wrapper is the normal entry point

  * [Selkie::Tween](Selkie--Tween.md) — the interpolation the fade runs on

  * [Selkie::Alpha](Selkie--Alpha.md) — why a fade is a colour ramp and never an alpha ramp

### has Num $.fade-in-seconds

Seconds the toast takes to resolve up out of the screen background. Only consulted once `enable-fade` has been called.

### has Num $.fade-out-seconds

Seconds the toast takes to dissolve back into it, ending as the duration expires. Clamped to the lifetime actually remaining.

### method attach

```raku
method attach(
    Notcurses::Native::Types::NcplaneHandle $parent-plane,
    Int :$rows where { ... },
    Int :$cols where { ... }
) returns Mu
```

Attach to the standard plane. Called once by `Selkie::App` in place of the usual `init-plane` — Toast lives outside the widget tree (so it can paint on top of any screen and any modal) so it doesn't adopt a plane of its own; the toast-plane is created lazily in `render` on first show.

### method handle-resize

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

Toast lives at screen-top, outside the widget tree, so it doesn't receive the normal handle-resize cascade from containers. App calls this directly when the terminal resizes so the toast-plane sits at the correct width.

### method resize-screen

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

Back-compat alias. Deprecated — prefer handle-resize.

### method show

```raku
method show(
    Str:D $message,
    Num :$duration = 2e0,
    Selkie::Style :$style,
    Instant :$at = Code.new
) returns Mu
```

Show a toast message for `:duration` seconds. Re-callable while a toast is already showing — the new message replaces the old and the duration restarts from `:at`. Apps don't usually call this directly; prefer `$app.toast(...)` which routes here. `:at` is the toast's zero point, defaulting to `now`; pass it explicitly to drive the lifetime (and the fades) from a test clock rather than the wall clock.

### method is-visible

```raku
method is-visible() returns Bool
```

True while a toast is currently being shown (between `show` and the next `tick` that observes the duration has expired).

### method enable-fade

```raku
method enable-fade(
    Selkie::Tween::TweenGroup:D $group
) returns Nil
```

Turn fading on and hand the toast the `Selkie::Tween::TweenGroup` its fades run on — normally `Selkie::App.tweens`, wired automatically when the app was built with C«:animate-toast». Idempotent, and takes effect from the next `show`: a toast already on screen keeps whatever it is doing.

### method disable-fade

```raku
method disable-fade() returns Nil
```

Go back to appearing and vanishing. Any fade in flight is cancelled and the toast drops straight to its own style, so a "reduce motion" preference flipped mid-fade lands somewhere sane rather than freezing a half-transparent bar on screen.

### method fade-enabled

```raku
method fade-enabled() returns Bool
```

True once `enable-fade` has been called and `disable-fade` hasn't.

### method fading

```raku
method fading() returns Bool
```

True while a fade-in or fade-out is actually running. A testing hook.

### method render-style

```raku
method render-style() returns Selkie::Style
```

The style `render` paints with right now: the fade's current interpolation while one is in flight, and the toast's own `style` otherwise. Public so a fade can be asserted without a plane.

### method fade-from

```raku
method fade-from(
    Selkie::Style:D $to
) returns Selkie::Style
```

The colour a fade starts from (and ends at): the theme's `base` background on both channels, with every discrete attribute copied from `$to` so nothing snaps at the midpoint. Returns `$to` unchanged when the theme's `base` has no background — there is no colour to fade from, so there is no fade.

### method tick

```raku
method tick(
    Instant $at = Code.new
) returns Bool
```

Advance the toast's lifetime clock. Called once per frame by `Selkie::App`. When the duration has elapsed, the toast flips to invisible and its plane is destroyed. Returns `True` when visibility *just transitioned* from visible to invisible this tick — the caller (`Selkie::App`) treats that as a signal to force one more composite render so the toast is actually erased from the terminal. Returns `False` otherwise (toast is still visible, or was never visible this tick). `$at` defaults to `now`; pass it to drive the lifetime from a test clock. This is also where the out-fade is armed, once, when the remaining lifetime drops under `fade-out-seconds` — a check that costs one subtraction on a toast that isn't fading.

### method destroy

```raku
method destroy() returns Mu
```

Tear down the toast plane and cancel any fade still running against it. Called by `Selkie::App.shutdown`; apps don't usually call this directly.