This page was machine-translated from Portuguese and may contain errors. The Portuguese version is canonical.
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 theEventsProcessor, callsonShow().hide()→ pop: unregisters, callsonRemove(); 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). 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.
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 theMainWindowskeleton 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.
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 correspondinghide()(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.