TUI Engine

Architecture and quality requirements for the TUI rendering engine.

These requirements are non-negotiable - they define the minimum standard for GUI-like TUI quality.

Quality Requirements

1. Flicker-free Rendering

REQUIRED:
- Double-buffering with diff-based flush
- Dirty-rect or run-length optimization
- No full-screen redraw loops

Why: Flicker destroys user experience and makes TUI unusable for professional applications.

2. Deterministic Layout + Clipping

REQUIRED:
- Implement Measure/Arrange/Paint lifecycle
- Clip drawing to assigned rectangles
- No out-of-bounds write operations

3. Unicode Correctness

REQUIRED:
- Handle wide characters (East Asian Width) correctly
- Handle combining marks correctly
- Calculate emoji width correctly
- Cursor positioning must be grapheme-aware

Problem: „Drifting caret“ - cursor wanders with wide chars.

Solution: Use grapheme clusters for cursor calculation.

4. Theme + State Layering

REQUIRED:
- Support style tokens
- Implement layered states:
  Base → Hover (if mouse) → Focus → Pressed → Disabled → Error/Validation
- Consistent focus indicators across all controls

5. Input + Focus as First-Class

REQUIRED:
- Focus navigation (Tab/Shift+Tab)
- Deterministic focus ordering
- Normalized input event model:
  Keys, text input, resize, optional mouse

6. Performance and Scaling

REQUIRED:
- Virtualization for large surfaces (tables/logs/trees)
- Avoid O(N) repaint of offscreen rows/columns
- Efficient memory usage

7. Image Support (SCADA/MES)

REQUIRED:
- Implement capability detection
- Fallback chain:
  1. Kitty Graphics (preferred)
  2. Sixel
  3. Placeholder (frame + caption + "image unavailable")

Engine Building Blocks

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;   (* Target frame *)
  FFrontBuffer: TCellBuffer;  (* Last flushed frame *)
public
  procedure ComputeDiff(out ADirtyRects: TRectArray);
  procedure Flush(ATerminal: TTerminalIO);
end;

DiffEngine Requirements:

  • Minimal cursor movements
  • Minimal style switches
  • Stable during rapid 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;

Important: Surface writes only to BackBuffer, not directly to TerminalIO.

D) Layout Engine

Controls are arranged into rects.

Minimum Containers:
- Stack (vertical/horizontal)
- Dock (Left, Top, Right, Bottom, Fill)
- Grid (later)
- Overlay (for 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

REQUIRED:
- Dump BackBuffer to stable text representation
- Compare with expected output

Unicode Fixtures

REQUIRED:
- Tests with wide characters (Chinese, Japanese)
- Tests with combining marks (e + accent)
- Tests with emoji

Image Fallback Tests

REQUIRED:
- Test Kitty → Sixel → Placeholder fallback
- Verify capability detection

Verification Checklist

TUI Engine Verification:

Rendering:
- [ ] No flicker during repeated repaint
- [ ] Resize produces stable layout
- [ ] No stuck cursor
- [ ] No broken clipping

Unicode:
- [ ] Wide characters positioned correctly
- [ ] Combining marks displayed correctly
- [ ] Emoji width correct

Performance:
- [ ] Table/List renders 10k rows with scroll
- [ ] No O(N) repaint for offscreen

Images:
- [ ] Kitty Graphics works (if supported)
- [ ] Sixel fallback works
- [ ] Placeholder when no support

See also

Zuletzt geändert: on 2026/01/29 at 10:28 PM