TUI Engine

Architektur und Qualitätsanforderungen der TUI Rendering Engine.

Diese Anforderungen sind nicht verhandelbar - sie definieren den Mindeststandard für GUI-ähnliche TUI-Qualität.

Qualitätsanforderungen

1. Flicker-free Rendering

PFLICHT:
- Double-Buffering mit Diff-based Flush
- Dirty-Rect oder Run-Length Optimierung
- Keine Full-Screen Redraw Loops

Warum: Flicker zerstört die User Experience und macht TUI unbrauchbar für professionelle Anwendungen.

2. Deterministisches Layout + Clipping

PFLICHT:
- Measure/Arrange/Paint Lifecycle implementieren
- Drawing auf zugewiesene Rectangles clippen
- Keine Out-of-Bounds Schreiboperationen

3. Unicode-Korrektheit

PFLICHT:
- Wide Characters (East Asian Width) korrekt behandeln
- Combining Marks korrekt behandeln
- Emoji-Breite korrekt berechnen
- Cursor-Positionierung muss Grapheme-aware sein

Problem: „Drifting Caret“ - Cursor wandert bei Wide Chars.

Lösung: Grapheme-Cluster für Cursor-Berechnung verwenden.

4. Theme + State Layering

PFLICHT:
- Style Tokens unterstützen
- Layered States implementieren:
  Base → Hover (falls Mouse) → Focus → Pressed → Disabled → Error/Validation
- Focus-Indikatoren konsistent über alle Controls

5. Input + Focus als First-Class

PFLICHT:
- Focus Navigation (Tab/Shift+Tab)
- Deterministisches Focus Ordering
- Normalisiertes Input Event Model:
  Keys, Text Input, Resize, optionale Mouse

6. Performance und Skalierung

PFLICHT:
- Virtualisierung für große Surfaces (Tables/Logs/Trees)
- O(N) Repaint von Offscreen Rows/Columns vermeiden
- Effiziente Speichernutzung

7. Image-Support (SCADA/MES)

PFLICHT:
- Capability Detection implementieren
- Fallback-Kette:
  1. Kitty Graphics (bevorzugt)
  2. Sixel
  3. Placeholder (Frame + Caption + "image unavailable")

Engine-Bausteine

A) Terminal Capability Layer

TTerminalCaps = record
  SupportsAnsi: Boolean;
  SupportsAltScreen: Boolean;
  SupportsTrueColor: Boolean;
  Supports256Color: Boolean;
  SupportsMouse: Boolean;
  SupportsKittyGraphics: Boolean;
  SupportsSixel: Boolean;
  SupportsUnicodeWidth: Boolean;
end;
 
TTerminalIO = class
  procedure EnterAltScreen;
  procedure ExitAltScreen;
  procedure ShowCursor;
  procedure HideCursor;
  procedure WriteEscapeSequence(const ASeq: string);
  procedure WriteTextRun(const AText: string);
  procedure MoveCursor(AX, AY: Integer);
  procedure SetStyle(AStyle: TStyle);
  procedure BatchFlush;
end;

B) Render Model: CellBuffer + DiffEngine

TCell = record
  Grapheme: string;      (* Unicode Grapheme Cluster *)
  Fg: TColor;            (* Foreground Color *)
  Bg: TColor;            (* Background Color *)
  Attrs: TAttributes;    (* Bold, Italic, Underline, etc. *)
  Link: string;          (* Optional: Hyperlink *)
  ImageRef: Integer;     (* Optional: Image Reference *)
end;
 
TCellBuffer = class
private
  FCells: array of array of TCell;
  FWidth, FHeight: Integer;
public
  procedure SetCell(AX, AY: Integer; const ACell: TCell);
  function GetCell(AX, AY: Integer): TCell;
  procedure Clear;
  procedure Resize(AWidth, AHeight: Integer);
end;

DiffEngine:

TDiffEngine = class
private
  FBackBuffer: TCellBuffer;   (* Ziel-Frame *)
  FFrontBuffer: TCellBuffer;  (* Letzter geflushter Frame *)
public
  procedure ComputeDiff(out ADirtyRects: TRectArray);
  procedure Flush(ATerminal: TTerminalIO);
end;

Anforderungen an DiffEngine:

  • Minimale Cursor-Bewegungen
  • Minimale Style-Wechsel
  • Stabil bei schnellem Resize

C) Render Surface (Drawing API)

TRenderSurface = class
private
  FBuffer: TCellBuffer;
  FClipStack: TRectStack;
public
  procedure FillRect(ARect: TRect; AChar: Char; AStyle: TStyle);
  procedure DrawText(AX, AY: Integer; const AText: string; AStyle: TStyle);
  procedure DrawBorder(ARect: TRect; ABorderStyle: TBorderStyle);
  procedure DrawImage(ARect: TRect; AImageRef: Integer);
  procedure ClipPush(ARect: TRect);
  procedure ClipPop;
end;

Wichtig: Surface schreibt nur in BackBuffer, nicht direkt in TerminalIO.

D) Layout Engine

Controls werden in Rects arrangiert.

Minimum Containers:
- Stack (vertikal/horizontal)
- Dock (Left, Top, Right, Bottom, Fill)
- Grid (später)
- Overlay (für Popups/Modals)

E) Widget/Control Rendering Contract

IWvdSTUIRenderer = interface
  function Measure(AControl: TWvdSControl; AAvailable: TSize): TSize;
  procedure Arrange(AControl: TWvdSControl; ARect: TRect);
  procedure Paint(AControl: TWvdSControl; ASurface: TRenderSurface);
  function HandleInput(AControl: TWvdSControl; AEvent: TInputEvent): Boolean;
end;

Testing

Snapshot Tests

PFLICHT:
- BackBuffer in stabile Textdarstellung dumpen
- Vergleich mit erwarteter Ausgabe

Unicode-Fixtures

PFLICHT:
- Tests mit Wide Characters (中文, 日本語)
- Tests mit Combining Marks (é = e + ´)
- Tests mit Emoji (👍, 🎉)

Image-Fallback Tests

PFLICHT:
- Kitty → Sixel → Placeholder Fallback testen
- Capability Detection verifizieren

Verifikations-Checkliste

TUI Engine Verification:

Rendering:
- [ ] Kein Flicker bei wiederholtem Repaint
- [ ] Resize produziert stabiles Layout
- [ ] Kein hängengebliebener Cursor
- [ ] Kein gebrochenes Clipping

Unicode:
- [ ] Wide Characters korrekt positioniert
- [ ] Combining Marks korrekt dargestellt
- [ ] Emoji-Breite korrekt

Performance:
- [ ] Table/List rendert 10k Rows mit Scroll
- [ ] Kein O(N) Repaint für Offscreen

Images:
- [ ] Kitty Graphics funktioniert (wenn supported)
- [ ] Sixel Fallback funktioniert
- [ ] Placeholder bei fehlender Unterstützung

Siehe auch

Zuletzt geändert: den 29.01.2026 um 15:13