Selkie.git | docs/api/ | Selkie--ScreenManager.md


NAME
====

Selkie::ScreenManager - Named multi-screen management

SYNOPSIS
========

You rarely use `ScreenManager` directly — [Selkie::App](Selkie--App.md)'s `add-screen` and `switch-screen` methods forward to its `add-screen` and `switch-to` methods (the App-level name is `switch-screen`; the ScreenManager-level name is `switch-to`). When you do need the underlying object directly (e.g. to enumerate screen names), reach it via `$app.screen-manager`:

```raku
my $sm = $app.screen-manager;

say $sm.active-screen;           # 'main'
say $sm.screen-names;            # ('login', 'main', 'settings')

# Route a keybind based on the active screen
$app.on-key('ctrl+n', -> $ {
    given $app.screen-manager.active-screen {
        when 'tasks' { create-task }
        when 'notes' { create-note }
    }
});
```

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

A tiny registry mapping screen names to root containers, with one marked as active at a time. `Selkie::App` uses it to park inactive screens off-screen while preserving their state.

Switching screens is fast — the inactive roots remain fully built, just repositioned off-screen. Their widgets keep their state (text input buffers, scroll positions, cursor positions) until the screen is reactivated.

EXAMPLES
========

Checking before registering
---------------------------

```raku
unless $app.screen-manager.has-screen('settings') {
    $app.add-screen('settings', build-settings-screen());
}
```

Cleanup
-------

Remove a screen you no longer need (e.g. after logout). Attempting to remove the active screen throws:

```raku
$app.switch-screen('login');
$app.screen-manager.remove-screen('main');
```

`remove-screen` unsubscribes the screen's entire widget tree from the store before destroying it, so a build-screen / remove-screen cycle leaves the store's subscription count exactly where it started. Check it with `$app.store.subscription-count` either side of the cycle if you are chasing a leak; a rising count means something in your tree holds children somewhere neither `children` nor `content` reaches.

SEE ALSO
========

  * [Selkie::App](Selkie--App.md) — wraps `ScreenManager` with higher-level conveniences

### method add-screen

```raku
method add-screen(
    Str:D $name,
    Selkie::Container $root
) returns Mu
```

Register a screen under a name. If this is the first screen added, it automatically becomes active. Subsequent screens join the registry but the active screen is unchanged. Idempotent by name: re-adding the same name overwrites the previous root.

### method remove-screen

```raku
method remove-screen(
    Str:D $name
) returns Mu
```

Remove a registered screen by name. Fails if the screen is currently active — switch to another screen first. Unsubscribes the screen's entire widget tree from the store, then destroys it. The explicit `unsubscribe-subtree` is load-bearing, not belt-and- braces. `destroy` cascades through whatever each container's own `destroy` reaches, and the widgets that hold children outside `@!children` — `CardList`'s cards, `Border`/`Modal`'s content — are exactly the ones a naive cascade misses, so their subscriptions used to outlive the screen (measured at ~7 leaked subscriptions per editor open/close cycle in a consumer app, each one paying pull-check cost on every subsequent tick, forever). `unsubscribe-subtree` walks `children` **and** `content` from the root, so it reaches them regardless of how each container tears itself down.

### method switch-to

```raku
method switch-to(
    Str:D $name
) returns Mu
```

Make the named screen active. Marks its root dirty so it re-renders. Fails if no screen with that name exists.

### method active-screen

```raku
method active-screen() returns Str
```

The name of the currently active screen, or `Nil` if no screens are registered.

### method active-root

```raku
method active-root() returns Selkie::Container
```

The root container of the currently active screen, or the type object `Selkie::Container` if no screen is active.

### method screen-names

```raku
method screen-names() returns List
```

Sorted list of registered screen names.

### method screen

```raku
method screen(
    Str:D $name
) returns Selkie::Container
```

Look up a registered screen's root widget by name. Returns the Container type object if no screen with that name exists.

### method has-screen

```raku
method has-screen(
    Str:D $name
) returns Bool
```

True if a screen with the given name is registered.

### method focusable-descendants

```raku
method focusable-descendants() returns Seq
```

Focusable descendants of the active screen's root. Used by `Selkie::App` to build the Tab cycle.

### method handle-resize

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

Propagate a terminal resize to every registered screen, not just the active one. Without this, switching to an inactive screen after a resize would render at stale dimensions until a re-layout happens to fire. Each screen's root is a Container, so its handle-resize cascades through its subtree synchronously.

### method destroy

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

Destroy every registered screen and clear the active screen reference. Called automatically by `Selkie::App.shutdown`. Unsubscribes each tree first, for the same reason `remove-screen` does — a subscription that outlives its widget's plane can be fired by a store tick racing shutdown.