Files
rustkeep/.scratch/rustkeep-1.0/issues/06-research-ratatui-architektur.md

4.0 KiB

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 (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.