# La pila de ventanas

> El modelo detrás de la navegación — la pila del EventsProcessor, popup vs fullscreen, botón Atrás y transiciones.

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

# La pila de ventanas

Entender el modelo evita la mayoría de los bugs de navegación.

## El modelo (LIFO)
El `EventsProcessor` mantiene una **pila** de `Window`. La ventana de la cima es la que **recibe eventos y se
pinta por encima**. Las de abajo siguen vivas (no se destruyen), solo cubiertas.

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

- `show()` → **push**: registra en el `EventsProcessor`, llama a `onShow()`.
- `hide()` → **pop**: desregistra, llama a `onRemove()`; la ventana de abajo vuelve a la cima.

## Ciclo de vida por ventana
| Método | Cuándo | Uso |
|---|---|---|
| `onShow()` | al entrar en la pila | montar la UI (siempre `super.onShow()`) |
| `onReposition()` | al redimensionar/rotar | reorganizar/recalcular layout |
| `onRemove()` | al salir de la pila | liberar timers, cursores, listeners |

## Fullscreen vs popup
La **misma** clase `Window` puede ser pantalla completa o popup — depende del `WindowStyle`
(`widthRatio`/`heightRatio` < 1 ⇒ popup; ver [Diálogos y modales](/docs/ui/navigation/dialogs-and-modals)). Los popups
quedan sobre la pantalla anterior (que sigue visible detrás).

## Botón Atrás (Android) y tocar fuera
- El **botón físico Atrás** es capturado por la aplicación y, por defecto, hace `hide()` de la ventana de la cima.
- En un popup con `canHide = true`, **tocar fuera** cierra el popup.
- Para **impedir** la salida (ej.: confirmación de descarte), maneja la intención antes de dejar que ocurra `hide()`.

## Ejemplo completo (copiar, pegar y ejecutar)
Demuestra push/pop en la pila y una transición de entrada configurada en el `WindowStyle` de la pantalla apilada.
```java
package com.exemplo;

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

/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: 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: entra al tope de la pila
      add(LAY_AUTO, empilhar);
   }
}

/** Pantalla que entra al tope con una transición y vuelve con hide() (pop). */
class TelaEmpilhada extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      // transición de entrada/salida de esta ventana (la de abajo sigue viva detrás)
      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: sale de la pila, la de abajo vuelve al tope
      add(LAY_AUTO, voltar);
   }
}
```
> Los fragmentos siguientes muestran **solo el núcleo** (lo que va dentro de `onShow()`). Para ejecutar, colócalos en el esqueleto `MainWindow` anterior.

## Transiciones
La animación de entrada/salida viene de `WindowStyle.transition` (`UIConstants.Transition`): `NONE`
(por defecto), `FADE`, `SLIDE_LEFT/RIGHT/UP/DOWN`, `SCALE`, `SCALE_DOWN`. Los pickers usan `FADE` por defecto.
```java
getStyle().transition = UIConstants.Transition.SLIDE_LEFT;   // desliza al abrir
```

## Buenas prácticas
- No mantengas referencias a las ventanas de abajo más de lo necesario — deja que se recolecten al `hide()`.
- Todo `show()` debe tener un `hide()` correspondiente (evita apilar pantallas sin retorno).

## Errores comunes
- Reconstruir la UI en `onReposition()` sin preservar el estado (scroll, selección).
- Asumir que la ventana de abajo se destruyó al abrir otra — sigue viva.

## Ver también
- [Navegar entre pantallas](/docs/ui/navigation/navigate-between-screens) · [Diálogos y modales](/docs/ui/navigation/dialogs-and-modals)
