# Escape, popping and departure

> How a screen leaves when the path is derived rather than pushed, and how it says goodbye.

If the path is derived from your state, a screen cannot simply remove itself. Leaving has to change the state the walk reads. Carte gives you one route for that, and uses it for Escape, Back buttons and anything else that closes a screen.

## Escape goes to the deepest screen first

```rust
let outcome = router.escape(&model, &mut outbox);
```

`Router::escape` offers Escape to the deepest node on the path, then to each node above it in turn. Each node answers with an `Escape`:

| Answer | Meaning |
| --- | --- |
| `Escape::Pop` | Leave this screen. The ordinary case |
| `Escape::Prompt` | This screen has unsaved work and raises its own prompt instead |
| `Escape::Ignore` | The key means something here (a text field clearing itself) and must go no further |
| `Escape::Fall` | Not this screen's key. Offer it to the parent |

Nothing above the deepest screen intercepts. The first answer other than `Fall` ends the search, and `escape` tells you what happened with an `EscapeOutcome`:

| Outcome | When |
| --- | --- |
| `Popped(id)` | `id` answered `Pop` |
| `Prompted(id)` | `id` answered `Prompt`. The path is unchanged this frame |
| `Consumed(id)` | `id` answered `Ignore` |
| `Unclaimed` | Every node answered `Fall` |

`Unclaimed` is the only case where anything outside the router hears about Escape. Your app may do what it likes with it, such as asking to quit.

## How a pop shortens the path

A pop does not edit the path. When a node answers `Pop`, the router calls `child_popped(out, child)` on its **parent**, the node that was claiming it. The parent pushes an action that stops the claim:

```rust
fn child_popped(&mut self, out: &mut Outbox<Action>, _: RouteId) {
    out.push(Action::Closed { parent: "home" });
}
```

Then the frame carries on as usual. The tick applies `Closed`, which sets `home_child` to `None` in your model, and the next `resolve` derives a shorter path. The child cannot shorten the path itself, because it does not derive it. Only the parent's claim does.

All of this lands in the same frame, because the tick runs after Escape is handled and `resolve` runs after the tick. See [The frame](/docs/the-frame).

A root that answers `Pop` has nobody to ask. `escape` still reports `Popped`, but nothing changes: your root function put it there, and only your root function can take it away. Have roots answer `Fall`.

## Prompting instead of leaving

`Prompt` is for "you have unsaved changes". The router does not raise the prompt. The node does, by pushing the action that makes it claim a prompt child:

```rust
fn escape(&mut self, model: &Model, out: &mut Outbox<Action>) -> Escape {
    match model.dirty() {
        true => {
            out.push(Action::AskDiscard); // sets note_child = Some("discard")
            Escape::Prompt
        }
        false => Escape::Pop,
    }
}
```

The prompt is an ordinary child route, so the walk finds it after the tick. Because the editor stays on the path, its `leave` does not run and its draft survives. Escape in the prompt pops the prompt, which returns you to the editor. Declare the prompt `Occlusion::None` to keep the editor visible under it. See [Occlusion and stacked views](/docs/routing/occlusion).

## Back buttons

A Back or Close button can reuse Escape. The notes example sets a flag when the button is clicked and calls `router.escape` as if the key had been pressed. The deepest screen decides what leaving means, whatever started it.

## Leaving for other reasons

A node may also want to leave in response to an event that is not Escape. It records that in its own state and answers `true` from `take_leave_request`, which must clear the request as it reads it. Carte does not dispatch your UI's events, so your binding asks after delivering one, and reports a yes with `pop_at`:

```rust
let path = router.path().to_vec();
for (depth, id) in path.iter().copied().enumerate() {
    if router.get_mut(id).is_some_and(|node| node.take_leave_request()) {
        router.pop_at(&mut outbox, depth);
    }
}
```

`pop_at` does exactly what Escape's `Pop` does: it tells the parent to stop claiming. Depth 0 is ignored.

## Departure

When the path changes, the deepest node that left is asked how it leaves the frame:

```rust
fn depart(&mut self, to: Option<RouteId>) -> Departure {
    Departure::Bleed { seconds: 0.3 }
}
```

`to` is the new deepest id. The answer is a `Departure`:

- `Departure::Cut`: gone this frame. The default, and it costs nothing.
- `Departure::Bleed { seconds }`: ask the binding to hold the outgoing screen's last frame for that long, composited under the incoming one.

Carte only records the answer. Read it after `resolve`:

```rust
if router.resolve(&mut model, &mut store) {
    if let Some((id, Departure::Bleed { seconds })) = router.last_departure() {
        // keep `id`'s last frame and fade it out over `seconds`
    }
}
```

Capturing and compositing the frame is your UI layer's job. A page turn, a cross-fade, a blur: Carte has no opinion. `last_departure` is `None` on frames where the path did not change.

## Next

[Occlusion and stacked views](/docs/routing/occlusion): what the deeper screens hide.
