A pilha de janelas
Entender o modelo evita a maioria dos bugs de navegação.
O modelo (LIFO)
O EventsProcessor mantém uma pilha de Window. A janela do topo é a que recebe eventos e é
pintada por cima. As de baixo continuam vivas (não são destruídas), apenas cobertas.
┌─────────────┐ ← topo: recebe toque/teclado, é pintada
│ TelaDetalhe │
├─────────────┤
│ TelaLista │ ← coberta, viva
├─────────────┤
│ TelaHome │ ← base
└─────────────┘
show()→ push: registra noEventsProcessor, chamaonShow().hide()→ pop: desregistra, chamaonRemove(); a janela de baixo volta ao topo.
Ciclo de vida por janela
| Método | Quando | Uso |
|---|---|---|
onShow() |
ao entrar na pilha | montar a UI (sempre super.onShow()) |
onReposition() |
ao redimensionar/rotacionar | reorganizar/recalcular layout |
onRemove() |
ao sair da pilha | liberar timers, cursores, listeners |
Fullscreen vs popup
A mesma classe Window pode ser tela cheia ou popup — depende do WindowStyle
(widthRatio/heightRatio < 1 ⇒ popup; ver Diálogos e modais). Popups
ficam sobre a tela anterior (que continua visível atrás).
Botão Voltar (Android) e tocar fora
- O botão físico Voltar é capturado pela aplicação e, por padrão, faz
hide()da janela do topo. - Em um popup com
canHide = true, tocar fora fecha o popup. - Para impedir a saída (ex.: confirmação de descarte), trate a intenção antes de deixar
hide()ocorrer.
Exemplo completo (copiar, colar e rodar)
Demonstra push/pop na pilha e uma transição de entrada configurada no WindowStyle da tela empilhada.
package com.exemplo;
import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;
/** Tela principal executável. Aponte o launcher (classname) e rode: 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 no topo da pilha
add(LAY_AUTO, empilhar);
}
}
/** Tela que entra no topo com uma transição e volta com hide() (pop). */
class TelaEmpilhada extends Window
{
@Override
public void onShow() throws Exception
{
super.onShow();
// transição de entrada/saída desta janela (a de baixo continua viva atrá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: sai da pilha, a de baixo volta ao topo
add(LAY_AUTO, voltar);
}
}
Os trechos abaixo mostram só o miolo (o que vai dentro de
onShow()). Para rodar, coloque-os no esqueletoMainWindowacima.
Transições
A animação de entrada/saída vem de WindowStyle.transition (UIConstants.Transition): NONE
(padrão), FADE, SLIDE_LEFT/RIGHT/UP/DOWN, SCALE, SCALE_DOWN. Pickers usam FADE por padrão.
getStyle().transition = UIConstants.Transition.SLIDE_LEFT; // desliza ao abrir
Boas práticas
- Não segure referências às janelas de baixo além do necessário — deixe-as serem coletadas ao
hide(). - Todo
show()deve ter umhide()correspondente (evite empilhar telas sem retorno).
Erros comuns
- Reconstruir a UI em
onReposition()sem preservar estado (rolagem, seleção). - Assumir que a janela de baixo foi destruída ao abrir outra — ela continua viva.