Implementierungsrichtlinien für Controls/Widgets im TUI-Target.
Model (Target-neutral) Renderer (Target-spezifisch) ───────────────────── ───────────────────────────── TWvdS* Pascal-Klasse TUI: CellBuffer via RenderSurface - Properties, State GUI: Canvas/GDI - Events, Commands Web: DOM/HTML - Keine I/O-Abhängigkeit
| Komponente | Inhalt |
| Model Unit | Properties, Events, State (target-neutral) |
|---|---|
| TUI Renderer Unit | Paint, Focus, Input Handling |
| Pack Manifest | PXAML Tag → Units Mapping |
TWvdSButton = class(TWvdSControl) private FCaption: string; FEnabled: Boolean; FFocused: Boolean; FPressed: Boolean; FOnClick: TNotifyEvent; public property Caption: string read FCaption write SetCaption; property Enabled: Boolean read FEnabled write SetEnabled; property Focused: Boolean read FFocused write SetFocused; property OnClick: TNotifyEvent read FOnClick write FOnClick; end;
IWvdSTUIRenderer = interface (* Layout *) function Measure(AControl: TWvdSControl; AAvailable: TSize): TSize; procedure Arrange(AControl: TWvdSControl; ARect: TRect); (* Rendering *) procedure Paint(AControl: TWvdSControl; ASurface: TRenderSurface); (* Input *) function HandleInput(AControl: TWvdSControl; AEvent: TInputEvent): Boolean; end;
PFLICHT für jeden TUI Renderer: - Deterministisches Placeholder-Rendering (vor Feature-Vollständigkeit) - Focus-Indikator Darstellung - Disabled-State Darstellung - Tab-Navigation funktioniert End-to-End - Optional: Mouse Handling (wenn TerminalCaps.SupportsMouse)
Pflicht-States: ├─ Base (* Default-Darstellung *) ├─ Focus (* Hat Tastatur-Fokus *) ├─ Disabled (* Nicht interaktiv *) └─ Error/Invalid (* Validierungsfehler *) Optional-States: ├─ Hover (* Maus darüber - wenn Mouse supported *) └─ Pressed (* Aktiv gedrückt *)
procedure TWvdSButtonTUIRenderer.Paint(AControl: TWvdSControl; ASurface: TRenderSurface); var Btn: TWvdSButton; Style: TStyle; begin Btn := TWvdSButton(AControl); (* State-basierte Style-Auswahl *) if not Btn.Enabled then Style := Theme.ButtonDisabled else if Btn.Focused then Style := Theme.ButtonFocused else Style := Theme.ButtonBase; (* Border + Caption rendern *) ASurface.DrawBorder(Btn.Bounds, bsSingle); ASurface.DrawText(Btn.Bounds.Left + 1, Btn.Bounds.Top, Btn.Caption, Style); end;
PFLICHT: - Focus Ring / Highlight (* Klar erkennbar *) - Hotkey/Accelerator Hint (* Optional: [A]bbrechen *) - Error State Indicator (* Validierungsfehler sichtbar *)
PFLICHT für jedes fokussierbare Control: - Tab konsumieren (FocusManager entscheidet Bewegung) - Enter/Space korrekt verarbeiten - Text Input über normalisierte Events (nicht Raw Keycodes)
function TWvdSButtonTUIRenderer.HandleInput(AControl: TWvdSControl; AEvent: TInputEvent): Boolean; var Btn: TWvdSButton; begin Result := False; Btn := TWvdSButton(AControl); if not Btn.Enabled then Exit; if not Btn.Focused then Exit; case AEvent.Kind of iekKey: if AEvent.Key in [vkReturn, vkSpace] then begin if Assigned(Btn.OnClick) then Btn.OnClick(Btn); Result := True; (* Event konsumiert *) end; iekMouse: if (AEvent.MouseButton = mbLeft) and (AEvent.MouseAction = maClick) then begin if Assigned(Btn.OnClick) then Btn.OnClick(Btn); Result := True; end; end; end;
function TWvdSButtonTUIRenderer.Measure(AControl: TWvdSControl; AAvailable: TSize): TSize; var Btn: TWvdSButton; begin Btn := TWvdSButton(AControl); (* Minimale vernünftige Größe *) Result.Width := Length(Btn.Caption) + 4; (* Padding + Border *) Result.Height := 3; (* Border oben/unten + Text *) (* Constraints vom Parent respektieren *) if Result.Width > AAvailable.Width then Result.Width := AAvailable.Width; end;
STRENG: - NUR innerhalb des zugewiesenen Rect zeichnen - Clipping MUSS aktiv sein - Keine Out-of-Bounds Schreiboperationen
Für Controls mit großen Datenmengen (Tables, Lists, Trees, Logs):
PFLICHT: - Nur sichtbare Rows/Items rendern - Keine Per-Row Buffer-Allokation pro Frame - Scroll-Position korrekt verwalten
procedure TWvdSListTUIRenderer.Paint(AControl: TWvdSControl; ASurface: TRenderSurface); var List: TWvdSList; VisibleStart, VisibleEnd: Integer; I, Y: Integer; begin List := TWvdSList(AControl); (* Nur sichtbaren Bereich berechnen *) VisibleStart := List.ScrollOffset; VisibleEnd := VisibleStart + List.VisibleRowCount; if VisibleEnd > List.ItemCount then VisibleEnd := List.ItemCount; (* Nur sichtbare Items rendern *) Y := 0; for I := VisibleStart to VisibleEnd - 1 do begin ASurface.DrawText(0, Y, List.Items[I], GetItemStyle(I)); Inc(Y); end; end;
[ ] Base State rendert korrekt [ ] Focus State klar erkennbar [ ] Disabled State unterscheidbar [ ] Error/Invalid State sichtbar
[ ] Tab-Navigation funktioniert [ ] Enter/Space löst Aktion aus (bei Buttons) [ ] Text Input normalisiert (bei TextEdit) [ ] Mouse optional unterstützt
[ ] Measure gibt sinnvolle Größe zurück [ ] Paint respektiert zugewiesenes Rect [ ] Kein Rendering außerhalb Bounds [ ] Resize wird korrekt behandelt
[ ] Große Listen virtualisiert [ ] Kein O(N) Repaint für Offscreen [ ] Keine unnötigen Allokationen pro Frame
Jedes Control dokumentieren: WHY: Warum existiert dieses Control (Use Cases) WHEN: Wann verwenden vs. einfachere Alternativen TUI Notes: - Capability Requirements (Mouse, Images, TrueColor) - Resize Behavior (Anchoring/Responsive) - Known Limitations