# The window stack

> The model behind navigation — the EventsProcessor stack, popup vs fullscreen, the Back button and transitions.

Source: https://crossfly.dev/docs/ui/navigation/the-window-stack — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# The window stack

Understanding the model avoids most navigation bugs.

## The model (LIFO)
The `EventsProcessor` keeps a **stack** of `Window`. The top window is the one that **receives events and is
painted on top**. The ones below stay alive (they are not destroyed), just covered.

```
┌─────────────┐  ← topo: recebe toque/teclado, é pintada
│ TelaDetalhe │
├─────────────┤
│ TelaLista   │  ← coberta, viva
├─────────────┤
│ TelaHome    │  ← base
└─────────────┘
```

- `show()` → **push**: registers with the `EventsProcessor`, calls `onShow()`.
- `hide()` → **pop**: unregisters, calls `onRemove()`; the window below returns to the top.

## Per-window lifecycle
| Method | When | Use |
|---|---|---|
| `onShow()` | on entering the stack | build the UI (always `super.onShow()`) |
| `onReposition()` | on resize/rotation | reorganize/recompute layout |
| `onRemove()` | on leaving the stack | release timers, cursors, listeners |

## Fullscreen vs popup
The **same** `Window` class can be fullscreen or popup — it depends on the `WindowStyle`
(`widthRatio`/`heightRatio` < 1 ⇒ popup; see [Dialogs and modals](/docs/ui/navigation/dialogs-and-modals)). Popups
sit over the previous screen (which stays visible behind).

## Back button (Android) and tapping outside
- The **physical Back button** is captured by the application and, by default, does `hide()` on the top window.
- In a popup with `canHide = true`, **tapping outside** closes the popup.
- To **prevent** leaving (e.g. a discard confirmation), handle the intent before letting `hide()` happen.

## Full example (copy, paste and run)
Demonstrates push/pop on the stack and an entry transition configured in the stacked screen's `WindowStyle`.
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;

/** Runnable main screen. Point the launcher (classname) and run: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      add(LAY_AUTO, new Label("Base da pilha (MainWindow)"));

      Button empilhar = new Button("Empilhar tela (push)");
      empilhar.onAction(c -> new TelaEmpilhada().show());   // push: goes to the top of the stack
      add(LAY_AUTO, empilhar);
   }
}

/** Screen that enters at the top with a transition and returns with hide() (pop). */
class TelaEmpilhada extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      // entry/exit transition of this window (the one below stays alive behind)
      getStyle().transition(UIConstants.Transition.SLIDE_LEFT);
      setLayout(new VerticalLayout());

      add(LAY_AUTO, new Label("Topo da pilha — a tela de baixo continua viva"));

      Button voltar = new Button("Voltar (pop)");
      voltar.onAction(c -> hide());   // pop: leaves the stack, the one below returns to the top
      add(LAY_AUTO, voltar);
   }
}
```
> The snippets below show **only the core** (what goes inside `onShow()`). To run them, place them in the `MainWindow` skeleton above.

## Transitions
The entry/exit animation comes from `WindowStyle.transition` (`UIConstants.Transition`): `NONE`
(default), `FADE`, `SLIDE_LEFT/RIGHT/UP/DOWN`, `SCALE`, `SCALE_DOWN`. Pickers use `FADE` by default.
```java
getStyle().transition = UIConstants.Transition.SLIDE_LEFT;   // slides in on open
```

## Best practices
- Don't hold references to the windows below longer than necessary — let them be collected on `hide()`.
- Every `show()` should have a corresponding `hide()` (avoid stacking screens with no way back).

## Common mistakes
- Rebuilding the UI in `onReposition()` without preserving state (scroll, selection).
- Assuming the window below was destroyed when another opened — it stays alive.

## See also
- [Navigate between screens](/docs/ui/navigation/navigate-between-screens) · [Dialogs and modals](/docs/ui/navigation/dialogs-and-modals)
