TUI Engine

Architettura e requisiti di qualità del TUI Rendering Engine.

Questi requisiti sono non negoziabili - definiscono lo standard minimo per qualità TUI simile alla GUI.

Requisiti di qualità

1. Rendering flicker-free

OBBLIGATORIO:
- Double buffering con flush basato su diff
- Ottimizzazione dirty-rect o run-length
- Nessun loop di redraw full-screen

Perché: Il flicker distrugge l'esperienza utente e rende la TUI inutilizzabile per applicazioni professionali.

2. Layout deterministico + clipping

OBBLIGATORIO:
- Implementare ciclo di vita Measure/Arrange/Paint
- Clippare il disegno ai rettangoli assegnati
- Nessuna operazione di scrittura out-of-bounds

3. Correttezza Unicode

OBBLIGATORIO:
- Gestire correttamente wide character (East Asian Width)
- Gestire correttamente combining mark
- Calcolare correttamente larghezza emoji
- Posizionamento cursore deve essere grapheme-aware

Problema: „Drifting Caret“ - il cursore deriva con i wide char.

Soluzione: Usare grapheme cluster per il calcolo del cursore.

4. Theme + State Layering

OBBLIGATORIO:
- Supportare token di stile
- Implementare stati stratificati:
  Base → Hover (se mouse) → Focus → Pressed → Disabled → Error/Validation
- Indicatori focus consistenti su tutti i controlli

5. Input + Focus come first-class

OBBLIGATORIO:
- Navigazione focus (Tab/Shift+Tab)
- Ordinamento focus deterministico
- Modello eventi input normalizzato:
  Tasti, input testo, resize, mouse opzionale

6. Performance e scalabilita

OBBLIGATORIO:
- Virtualizzazione per surface grandi (tabelle/log/alberi)
- Evitare repaint O(N) di righe/colonne offscreen
- Uso memoria efficiente

7. Supporto immagini (SCADA/MES)

OBBLIGATORIO:
- Implementare rilevamento capability
- Catena fallback:
  1. Kitty Graphics (preferito)
  2. Sixel
  3. Placeholder (frame + caption + "image unavailable")

Componenti engine

A) Layer Terminal Capability

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) Modello render: CellBuffer + DiffEngine

TCell = record
  Grapheme: string;      (* Unicode Grapheme Cluster *)
  Fg: TColor;            (* Foreground Color *)
  Bg: TColor;            (* Background Color *)
  Attrs: TAttributes;    (* Bold, Italic, Underline, ecc. *)
  Link: string;          (* Opzionale: Hyperlink *)
  ImageRef: Integer;     (* Opzionale: Riferimento immagine *)
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;   (* Frame target *)
  FFrontBuffer: TCellBuffer;  (* Ultimo frame flushato *)
public
  procedure ComputeDiff(out ADirtyRects: TRectArray);
  procedure Flush(ATerminal: TTerminalIO);
end;

Requisiti per DiffEngine:

  • Movimenti cursore minimi
  • Cambi stile minimi
  • Stabile con resize veloce

C) Render Surface (API disegno)

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;

Importante: Surface scrive solo in BackBuffer, non direttamente in TerminalIO.

D) Layout Engine

I controlli vengono disposti in rect.

Container minimi:
- Stack (verticale/orizzontale)
- Dock (Left, Top, Right, Bottom, Fill)
- Grid (successivo)
- Overlay (per popup/modali)

E) Contratto rendering Widget/Control

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

Test snapshot

OBBLIGATORIO:
- Dump BackBuffer in rappresentazione testuale stabile
- Confronto con output atteso

Fixture Unicode

OBBLIGATORIO:
- Test con wide character (cinese, giapponese)
- Test con combining mark (e = e + accento)
- Test con emoji

Test fallback immagini

OBBLIGATORIO:
- Testare fallback Kitty → Sixel → Placeholder
- Verificare rilevamento capability

Checklist verifica

Verifica TUI Engine:

Rendering:
- [ ] Nessun flicker al repaint ripetuto
- [ ] Resize produce layout stabile
- [ ] Nessun cursore bloccato
- [ ] Nessun clipping rotto

Unicode:
- [ ] Wide character posizionati correttamente
- [ ] Combining mark visualizzati correttamente
- [ ] Larghezza emoji corretta

Performance:
- [ ] Table/List renderizza 10k righe con scroll
- [ ] Nessun repaint O(N) per offscreen

Immagini:
- [ ] Kitty Graphics funziona (se supportato)
- [ ] Fallback Sixel funziona
- [ ] Placeholder se non supportato

Vedi anche

Zuletzt geändert: il 30/01/2026 alle 01:31