73 lines
4.0 KiB
Markdown
73 lines
4.0 KiB
Markdown
# 06 — Research: ratatui-App-Architektur
|
|
|
|
Type: research
|
|
Status: resolved
|
|
Blocked by: —
|
|
|
|
## Question
|
|
|
|
Wie strukturiert man eine ratatui-Anwendung mit mehreren Bildschirmen sauber?
|
|
|
|
rustkeep braucht Grid-Ansicht, Kampfbildschirm, Inventar, Shop, Questlog, Charakterbogen, Hauptmenü.
|
|
Fakten sammeln. Zu klären:
|
|
|
|
- Welches App-Muster ist etabliert — Elm-artig (Message/Update/View), Screen-Stack, State-Machine?
|
|
Was empfiehlt ratatui selbst, was machen echte Projekte?
|
|
- Wie werden Bildschirme gestapelt, wenn ein Popup über dem Kampf liegt?
|
|
- Wie trennt man Spiellogik von Rendering, damit der Kern ohne Terminal testbar bleibt?
|
|
- Event-Loop: Wie läuft Eingabe gegen Rendering, braucht ein rundenbasiertes Spiel überhaupt einen Tick?
|
|
- Welche Widgets bringt ratatui mit (Liste, Tabelle, Gauge, Popup), und was muss man selbst bauen —
|
|
insbesondere ein Dungeon-Grid?
|
|
- Terminal-Aufräumen bei Panic, damit kein kaputtes Terminal zurückbleibt.
|
|
- Stand der Versionen: aktuelle ratatui- und crossterm-Version, bekannte Fallstricke unter Windows.
|
|
|
|
Ergebnis: empfohlenes App-Muster mit Skizze der Modulstruktur, plus Quellenliste.
|
|
|
|
## Answer
|
|
|
|
Vollständiger Bericht: [`research/06-ratatui-architektur.md`](../research/06-ratatui-architektur.md)
|
|
(942 Zeilen, gegen Live-Quellen geprüft am 2026-08-11). Kern:
|
|
|
|
- **App-Muster: Screen-Stack (`Vec<Screen>`) über zentralem `Game`-State.** ratatui empfiehlt offiziell
|
|
nichts — es dokumentiert Elm, Component und Flux neutral und hat zu Multi-Screen gar keine Seite.
|
|
Maintainer joshka rät im Forum zum Enum; ratatuis Flaggschiff-Beispiel `demo2` macht Enum plus
|
|
`impl Widget for &App`; das rundenbasierte chess-tui fährt `current_page` plus `current_popup`.
|
|
Elm scheitert an ratatuis eigenem `StatefulWidget`; das Component-Template schleppt tokio, clap und
|
|
tracing mit.
|
|
- **Modulstruktur:** `game/` (kennt ratatui nicht — mechanisch prüfbar), `data/`,
|
|
`ui/{screen,input,theme,widgets/,screens/}`, genau ein Eintrittspunkt `Game::apply(Action)`.
|
|
- **Popups: kein Widget-Stack.** `Clear` auf ein zentriertes Sub-`Rect` nach dem Screen. Negativbefund:
|
|
kein produktives Projekt nutzt `Vec<Box<dyn Component>>` für Popups, auch gitui nicht.
|
|
- **Event-Loop: blockierendes `event::read()`, kein Tick, kein tokio.** Das async-Rezept auf ratatui.rs
|
|
ist für ein rundenbasiertes Spiel Overkill.
|
|
- **Dungeon-Grid: Eigenbau.** Es gibt kein Grid- oder Tilemap-Widget, weder mitgeliefert noch als
|
|
Drittanbieter-Crate. `impl Widget` mit direktem Buffer-Zugriff. `Canvas` ist eine Vektorfläche und
|
|
taugt nur für Minimap und Sichtlinien.
|
|
- **Panic: `ratatui::run(...)` genügt** — die Init-Funktionen installieren den Hook selbst, die drei
|
|
Rezeptseiten dazu sind veraltet. `Drop` von `Terminal` stellt *nicht* wieder her.
|
|
- **Versionen: ratatui 0.30.2** (2026-06-19, MSRV 1.88, in Teilcrates aufgespalten), **crossterm 0.29.0**.
|
|
crossterm nicht separat in `Cargo.toml` ziehen.
|
|
- **Windows:** Der `KeyEventKind::Press`-Filter ist Pflicht, sonst doppelte Züge. Kitty-Protokoll und
|
|
`Event::Paste` gehen dort gar nicht, conhost verschiebt Spalten bei doppelbreiten Zeichen.
|
|
|
|
Sieben Punkte sind im Bericht ausdrücklich als nicht verifiziert markiert. Sechs Anschlussfragen bleiben
|
|
offen und sind zu Ticket 11 und 12 graduiert.
|
|
|
|
## Nachtrag aus Ticket 12: der Windows-Fallstrick ist gemessen
|
|
|
|
Der `KeyEventKind::Press`-Filter stand hier als Faktum, aber ungeprüft. Der Durchstich zählt
|
|
Tasten-Events ungefiltert **und** gefiltert mit. Unter Windows 11:
|
|
|
|
```
|
|
Tasten 14 → Press 7 → Züge 7
|
|
```
|
|
|
|
Jeder Tastendruck erzeugt zwei `Event::Key` — Press und Release. Ohne den Filter kostete **jeder
|
|
Schritt zwei Züge**, und weil ein Zug befristete Modifikatoren tickt, wäre das kein kosmetischer
|
|
Fehler, sondern doppelte Spielzeit.
|
|
|
|
Ebenfalls im Durchstich bestätigt: `ratatui::run`, der Buffer-Zugriff über `buf[(x, y)].set_symbol(…)`
|
|
für ein eigenes Grid-Widget, `Clear` auf ein Sub-`Rect` für Popups, und dass `ratatui` die Crate
|
|
`crossterm` re-exportiert — ein eigener `Cargo.toml`-Eintrag ist tatsächlich unnötig. Gebaut gegen
|
|
ratatui 0.30.2 mit Rust 1.96.
|