Files
kst4contest/docs/superpowers/plans/2026-09-28-etappe5-hauptfenster.md
T
Rsclub2_2 cc8c90204e Draw the whole interface in Compose
Etappen 3 to 6a of the migration (129 commits). Every window the operator sees
is Compose; the JavaFX ones still exist beside them so the two could be
compared against a live session, which is the only way the differences came to
light.

The toolchain first, then the settings window with its eleven tabs and 72 real
preferences; then the multi-column table as a component, and with it the rules
it needs -- stable row identity, a selection that survives a refresh, column
order the operator can drag, and a repaint when the rows change rather than
when the list instance does. Then the update and monitor windows, and the main
window: 22 station columns, the filter bar, the selected-station panel, the
message tabs, the priority bar, the sked controls, the status indicators and
the timeline, assembled behind a splitter because Compose has none.

The map stops being a WebView. A Compose canvas projects WebMercator tiles
itself, with the clustering in core and an asynchronous fetcher in place of the
local tile proxy; derive() becomes a pure Kotlin reimplementation instead of a
reflection call into com.sun.javafx.

Most of the volume here is parity the operator would notice rather than new
behaviour: desktop density instead of Material's phone minimums, colours taken
from the JavaFX stylesheets instead of Material's palette, row dividers, band
columns, grid-square labels and their colours, the home marker and antenna
beam, pinch-to-zoom under Wayland, and layouts that survive a window narrower
than the chart they stack.

Two lessons are worth more than the diff. A control bound to a value Compose
cannot observe writes through and never redraws -- that one shipped repeatedly
with a green build, because no test drew anything. And a layout that clips
instead of wrapping is worse than one that is merely too narrow: half a word
is the worst available outcome.
2026-10-05 22:33:28 +02:00

92 lines
4.3 KiB
Markdown

# Umsetzungsplan Etappe 5 — Hauptfenster
Spezifikation: `docs/superpowers/specs/2026-09-25-compose-migration-design.md`, Etappe 5.
## Der Umfang, gemessen
| Teil | Zeilen | Besonderheit |
|---|---|---|
| Stationsliste `initChatMemberTable` | 854 | 22 Spalten, 13 Zellfabriken, 1 Zeilenfabrik, 4 Vergleicher |
| Filterleiste | ~977 | 37 Bedienelemente, davon **25 Umschaltknöpfe** |
| Infobereich `generateFurtherInfoAbtSelectedCallsignBP` | 881 | eigener Teiler mit gespeicherter Position |
| Menüleiste (File, Options, Windows, Help) | ~343 | macOS-Systemmenüleiste |
| `TimelineView` | 376 | eigene Datei, eigene Zeichnung |
| Top-Prioritätenliste | 69 | |
| Statusanzeigen | ~40 | Sked-Warnung, Verbindung, Band-Upgrade |
Über **3500 Zeilen**. Etappe 3 war kleiner und wurde geteilt.
**Der Infobereich ist so groß wie die Stationsliste.** In der Spezifikation steht er
als einer von sieben Aufzählungspunkten; das untertreibt ihn um eine Größenordnung.
## Aufteilung in 5a, 5b und 5c
Nicht aus Ordnungsliebe, sondern weil eine Etappe lieferbar sein soll. Nach jedem
Abschnitt läuft die Anwendung, und du kannst abnehmen, bevor der nächste anfängt.
### 5a — Stationsliste und Filterleiste
Der Kern. Die mehrspaltige Tabelle aus Etappe 4 wurde **für diese Liste** zugeschnitten
und trägt hier ihre erste echte Last: 22 Spalten, Zellfärbung aus Zeile *und* Wert,
Zeilenfärbung nach `getCurrentPriorityScore()` über vier Schwellen, eigene Vergleicher,
23 gespeicherte Spaltenbreiten.
Die 25 Umschaltknöpfe der Filterleiste sind kein Formular, sondern Zustand mit Regeln —
welche Filter einander ausschließen, welche sich stapeln. Die Regeln gehören in einen
testbaren Zustand, bevor ein Knopf gezeichnet wird.
**Abnahme:** Die Liste zeigt dieselben Stationen in derselben Reihenfolge wie JavaFX,
die Filter wirken gleich, Spaltenbreiten überleben den Neustart.
### 5b — Infobereich, Top-Prioritätenliste, Chat-Reiter
Der Infobereich hängt an der Auswahl der Liste aus 5a — deshalb danach und nicht
davor. Sein Teiler wird hier einmal richtig gebaut; im Monitor-Fenster habe ich ihn
bewusst weggelassen, statt ihn zweimal halb zu bauen.
**Abnahme:** Auswahl in der Liste füllt den Infobereich wie bisher, Teilerpositionen
werden entprellt gespeichert.
### 5c — Menüleiste, Statusleiste, TimelineView, Profilwechsel
Die Menüleiste wird eine gemeinsame Composable-Funktion, die jedes Fenster aufruft —
so verlangt es Compose, und so bekommen die Compose-Fenster auf macOS ihre
Systemmenüleiste zurück, die sie seit Etappe 3b nicht haben.
**Der Profilwechsel ändert seine Natur.** Heute reißt er die Laufzeit ab und baut eine
neue `Kst4ContestApplication` auf, weil viele Bedienelemente inline erzeugte
Instanzfelder sind. Dieser Grund entfällt; der Wechsel wird zum Austausch des
Zustandsobjekts. `shutdownRuntime()` bleibt trotzdem verantwortlich für alles, was eine
Verbindung überdauert.
**Abnahme:** Profilwechsel im laufenden Betrieb, ohne doppelt angemeldete Zuhörer und
ohne gehaltene Ressourcen. Auf macOS erscheint die Menüleiste bei jedem Fenster.
## Was aus Etappe 4 mitgenommen wird
Vier Fehler dieser Etappe kamen **ausschließlich** durch Hinsehen ans Licht, keiner
durch einen Test, und der Prüfer fand nur einen Teil davon:
1. Ein Zwischenspeicher, der den Zustand nicht mehr las, und eine Tabelle, die deshalb
nie wieder zeichnete.
2. Eine Kopfzeile, die die ganze Tabellenhöhe nahm.
3. Eine Liste, die bei jeder neuen Zeile vom Kopf wegdriftete.
4. Ein Menüeintrag, der seinen Umschalter verloren hatte.
Daraus drei Regeln für diese Etappe:
- **Was gezeichnet wird, muss beim Lesen Zustand berühren.** Der `Snapshot.observe`-Test
aus Etappe 4 fängt genau das und wird hier für jede neue Ansicht angelegt.
- **Nach jedem sichtbaren Teil wird gestartet und hingesehen**, nicht erst am Ende des
Abschnitts.
- **Kein Verhalten wird weggelassen, um ein Darstellungsproblem zu umgehen.** Der
verlorene Umschalter war genau dieser Tausch.
## Offene Punkte
1. Ob `Platform.setImplicitExit(false)` und `ApplicationRuntimeLauncher.exitApplication()`
in der Compose-Fassung noch eine Entsprechung brauchen — laut Spezifikation nach
Etappe 4 zu klären, also jetzt.
2. Ob die Filterleiste ihre 25 Knöpfe behält oder ob mehrere davon derselbe Zustand
sind. Vor dem Zeichnen zu lesen, nicht danach.