Files
Jander_Semester2/Semesterprojekt/docs/superpowers/specs/2026-05-31-presentation-layer-design.md
Jean-Luc Makiola 705d5f9db1 docs: spec for presentation/output layer (sub-project 1)
Semantic output model (Style/Span/StyledText) + RoomView/Hud views,
GameIO extended additively (write(String) stays as default), ConsoleIO
(ANSI + box-drawing, tiered glyph modes) and SwingIO (region layout,
JTextPane, bundled font) renderers, command + TestIO migration. Map,
menu, save, music explicitly deferred to later sub-projects.

Also: capture "don't overwhelm / distribute info across regions" and
"B baseline + ASCII for big moments" decisions in the backlog.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:06:36 +02:00

223 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Spec: Präsentations-/Ausgabe-Schicht (Teilprojekt 1)
Stand: 2026-05-31. Erstes Teilprojekt aus [enhancement-ideas.md](../../enhancement-ideas.md).
Baut auf der v1.0-Baseline (Tag `v1.0`) auf.
## 1. Kontext & Ziel
`GameIO` bietet aktuell nur `readLine()` + `write(String)`; beide Frontends
(`ConsoleIO`, `SwingIO`) schieben rohen Text durch. Ziel: **reichere, strukturierte
Ausgabe** in beiden Modi, ohne den für beide Frontends **identischen Game-Loop**
aufzugeben (Kernwert der Architektur).
Zielbild (bestätigt im Brainstorming):
- Konsole: **Stufe B** Boxed Heading, gelabelte Sektionen, persistente HUD-Zeile;
ASCII-Art nur für große Momente.
- GUI ist der **Fokus** (mehr Freiheit), Konsole wird weiter unterstützt.
- Semantische Farb-Rollen für Items / NPCs / Exits / Headings / Gefahr.
- **Progressive Disclosure** (nicht überladen) und **Info auf Regionen verteilen**
(Karte ersetzt später die Exit-Zeile).
## 2. Scope
**In Scope:**
- Semantisches Ausgabe-Modell (`Style`, `Span`, `StyledText` + Builder).
- High-Level-Views: `RoomView`, `Hud`.
- Erweiterung der `GameIO`-Schnittstelle (additiv, `write(String)` bleibt als Default).
- `ConsoleIO` als Renderer: ANSI-Farben + Box-Drawing (Stufe B), HUD-Zeile,
ASCII-Art-Banner-Mechanismus, gestufte Glyph-Fähigkeit.
- `SwingIO` als Renderer: Region-Layout (HUD oben, Output mittig, Seitenpanel
rechts reserviert, Input unten), `JTextPane`/`StyledDocument`, **gebündelter Font**.
- Migration der bestehenden Commands auf die neue API; `TestIO` angepasst.
**Out of Scope (jeweils eigene spätere Specs):**
- Tatsächliches Zeichnen der **Karte** (Teilprojekt 2) Seitenpanel ist nur Slot,
zeigt bis dahin die Exit-Liste.
- **Hauptmenü/Settings** (6), **Speichern/Laden** (7), **Musik** (8).
- Neue **Inhalte/Mechaniken** (Quests, Licht, Item-Kombination).
Die Schnittstellen lassen bewusst Slots (Seitenpanel, HUD-Felder) für diese
späteren Teile.
## 3. Architektur (Approach 2)
**Commands erzeugen Bedeutung, kein Format.** Ein Command baut ein semantisches
Objekt (`StyledText` oder eine View) und übergibt es an `GameIO`. Jedes Frontend
**rendert** es selbst: `ConsoleIO` → ANSI/Box-Drawing, `SwingIO` → Swing-Komponenten
und **eigenes Layout**. Der Game-Loop bleibt unverändert.
```mermaid
flowchart LR
CMD["Command<br/>(z.B. LookCommand)"] -- "baut" --> VIEW["RoomView / StyledText / Hud<br/>(semantisch, kein Format)"]
VIEW -- "GameIO.showRoom() / print() / setHud()" --> IO{{"GameIO"}}
IO --> CON["ConsoleIO<br/>ANSI + Box-Drawing"]
IO --> GUI["SwingIO<br/>JTextPane + Panels"]
```
## 4. Komponenten & API
Neues Subpackage `io.text` für das Modell **und** die Views (`Style`, `Span`,
`StyledText`, `RoomView`, `Hud`); die Renderer (`ConsoleIO`, `SwingIO`) bleiben in `io`.
### 4.1 Style (enum)
```java
public enum Style { PLAIN, HEADING, ITEM, NPC, EXIT, DANGER, DIM }
```
Kleines, bewusst begrenztes Rollen-Set. Mapping auf konkrete Farben/Attribute
liegt im jeweiligen Renderer, nicht am Style.
### 4.2 Span & StyledText
```java
public record Span(String text, Style style) {}
public final class StyledText {
private final List<Span> spans; // immutable
public List<Span> spans() { ... }
public String plainText() { ... } // Spans konkateniert, ohne Format
public static StyledText of(String s) { ... } // ein PLAIN-Span
public static Builder builder() { ... }
public static final class Builder {
Builder plain(String s); Builder heading(String s);
Builder item(String s); Builder npc(String s);
Builder exit(String s); Builder danger(String s);
Builder dim(String s); StyledText build();
}
}
```
Beispiel:
```java
io.print(StyledText.builder()
.plain("You pick up ").item("the brass lamp").plain(".").build());
```
Zeilenumbrüche stehen als `\n` im Text der Spans.
### 4.3 Views
```java
public record RoomView(String name, String description,
List<String> items, List<String> npcs,
List<String> exits) {}
public record Hud(String location, int gold, int turn, boolean lightOn) {}
```
Views tragen bereits **anzeigefertige Strings** (Namen aufgelöst), aber **kein
Layout**. `RoomView.exits` versorgt vorerst das GUI-Seitenpanel; sobald Teilprojekt 2
die Karte zeichnet, blendet der GUI-Renderer die Exit-Sektion aus.
### 4.4 GameIO (erweitert, additiv)
```java
public interface GameIO {
String readLine();
void print(StyledText text); // gestylter Ausgabe-Block
void showRoom(RoomView room); // High-Level-Raumansicht
void setHud(Hud hud); // HUD-Region aktualisieren
default void write(String s) { // Komfort + Rückwärtskompatibilität
print(StyledText.of(s));
}
}
```
`write(String)` bleibt als Default → bestehende Aufrufstellen kompilieren weiter;
Migration kann schrittweise erfolgen.
### 4.5 ConsoleIO (Renderer)
- `print`: Spans → ANSI-Sequenzen (Rollen→Farbe), dann `println`.
- `showRoom`: Stufe-B-Layout mit Box-Drawing-Rahmen (Heading + Sektionen
„You see / Here is / Exits"), Rollen-Farben.
- `setHud`: dim gefärbte Statuszeile (`[ Kitchen · 0 gold · turn 7 · light: off ]`).
- **ASCII-Art-Banner**: Methode/Helfer für große Momente (Start, Finale); Kunst aus
Ressourcen geladen. Mechanismus in Scope, konkrete Kunstwerke folgen mit Inhalten.
- **Konfiguration** (Defaults sicher, Verdrahtung mit Settings-Menü später):
- `ColorMode`: an/aus; Auto-Erkennung (`System.console() == null` → keine ANSI).
- `GlyphMode`: `ASCII | UNICODE | GLYPH`, Default `UNICODE` (Box-Drawing breit
unterstützt; Nerd-Glyphen nur bei explizitem Opt-in). Bei `ASCII` degradieren
die Rahmen auf `+`, `-`, `|` statt der Unicode-Box-Drawing-Zeichen.
### 4.6 SwingIO (Renderer) + Layout
Region-Layout (bestätigt, Variante A):
- **NORTH**: HUD-Leiste (`JPanel` mit `JLabel`s) Ort · Gold · Zug · Licht.
- **CENTER**: `JTextPane` mit `StyledDocument` (Farben/Stile via `AttributeSet` pro
`Style`), scrollbar.
- **EAST**: Seitenpanel, reserviert für Karte; zeigt bis dahin `RoomView.exits` als Liste.
- **SOUTH**: Eingabefeld (bestehende `LinkedBlockingQueue`-Brücke bleibt).
**Gebündelter Font:** `.ttf` unter `src/main/resources/fonts/`, via
`Font.createFont(Font.TRUETYPE_FONT, stream)` geladen und registriert → Farben und
**Glyphen funktionieren ohne Installation auf dem Spieler-System**. Fallback auf
`Font.MONOSPACED` bei Ladefehler (geloggt).
`print`/`showRoom` schreiben gestylte Spans in das `JTextPane`; `setHud` aktualisiert
die Labels. Alle Swing-Mutationen via `SwingUtilities.invokeLater` (wie bisher).
## 5. Datenfluss
```
Command → baut RoomView/StyledText/Hud → GameIO.{showRoom|print|setHud}
→ Frontend-Renderer → Konsole (ANSI) bzw. GUI (Swing)
```
Game-Loop, Parser, Registry, Domain: **unverändert**.
## 6. Nerd-Font / Glyph-Strategie (zusammengefasst)
- **GUI**: Font mitliefern (bundled `.ttf`) → Problem gelöst, keine Installation nötig.
- **Konsole**: Font nicht kontrollierbar → **gestufte Fallbacks** (`ASCII`
`UNICODE`-Box-Drawing → `GLYPH`). Default `UNICODE`. Keine harte Abhängigkeit von
installierten Nerd Fonts.
## 7. Migration der Commands & Tests
- `LookCommand`: statt `StringBuilder` eine `RoomView` bauen → `io.showRoom(view)`.
- Übrige Commands (`Take`, `Drop`, `Use`, `Read`, `Examine`, `Talk`, `Give`,
`Inventory`, `Help`): `io.print(StyledText…)` mit Rollen, wo es Mehrwert bringt;
sonst genügt das Default-`write(String)`. Schrittweise, nicht Big-Bang.
- `TestIO`: implementiert `print`/`showRoom`/`setHud` durch **Flattening auf
Klartext** (`StyledText.plainText()` bzw. RoomView→Textblock), abgelegt in
`outputs`. `lastOutput()`/`allOutput()` liefern weiterhin Klartext → die
bestehenden `.contains(...)`-Assertions der 67 Tests bleiben gültig.
## 8. Fehlerbehandlung
| Fall | Verhalten |
|---|---|
| Gebündelter Font nicht ladbar | Log-Warnung, Fallback `Font.MONOSPACED` |
| Terminal ohne ANSI / Umleitung | Auto-Erkennung → Farben aus, reiner Text |
| `GlyphMode=GLYPH` ohne passende Terminal-Font | Nutzer-Opt-in; kein Auto-Fallback nötig, Default bleibt `UNICODE` |
| `null`-Text an `StyledText.of` | `IllegalArgumentException` (wie bestehende Konventionen) |
## 9. Testing-Strategie
- **Unit**: `StyledText`-Builder & `plainText()`, `Span`, Immutabilität von
`RoomView`/`Hud`.
- **ConsoleIO**: Rendern einer bekannten `RoomView`/`Hud` mit Farbe AUS gegen einen
`StringWriter`/`PrintStream` (Test-Konstruktor existiert bereits) → strukturelle
Assertions (Heading, Labels, Exit-Namen).
- **Command-Tests**: laufen unverändert über das angepasste `TestIO` (Klartext).
- **SwingIO**: nicht unit-getestet (EDT/Swing); Logik dünn halten, manueller
Smoke-Test. Verifikation per `mvn exec:java@gui`.
## 10. Handoffs an spätere Teilprojekte
- **Karte (2)**: GUI-Seitenpanel + künftige `showMap(MapView)`-Methode;
`RoomView.exits` ist bis dahin Platzhalter-Quelle.
- **Menü/Settings (6)**: verdrahtet später `ColorMode`/`GlyphMode`/Lautstärke.
- **Save (7)**, **Musik (8)**: nutzen die HUD/Region-Slots, hier nicht implementiert.
## 11. Offene Detailfragen (in Implementierung entscheidbar)
- Konkrete Wahl des gebündelten Fonts (freier Monospace bzw. Nerd-Font-Variante,
Lizenz prüfen).
- Exakte ANSI-Farbpalette pro Rolle.
- Ob `Hud` schon ein `lightOn` führt, obwohl die Licht-Mechanik erst mit den Inhalten
kommt → Feld vorsehen, vorerst konstant `false`/ausgeblendet.