Linee guida di implementazione per controlli/widget nel target TUI.
Model (Target-neutral) Renderer (Target-specifico) ───────────────────── ───────────────────────────── Classe Pascal TWvdS* TUI: CellBuffer via RenderSurface - Properties, stato GUI: Canvas/GDI - Eventi, comandi Web: DOM/HTML - Nessuna dipendenza I/O
| Componente | Contenuto |
| Unit Model | Properties, eventi, stato (target-neutral) |
|---|---|
| Unit TUI Renderer | Paint, focus, gestione input |
| Pack Manifest | Mapping tag PXAML → unit |
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;
OBBLIGATORIO per ogni TUI Renderer: - Rendering placeholder deterministico (prima della completezza feature) - Visualizzazione indicatore focus - Visualizzazione stato disabled - Navigazione Tab funzionante end-to-end - Opzionale: Gestione mouse (se TerminalCaps.SupportsMouse)
Stati obbligatori: ├─ Base (* Visualizzazione default *) ├─ Focus (* Ha il focus tastiera *) ├─ Disabled (* Non interattivo *) └─ Error/Invalid (* Errore validazione *) Stati opzionali: ├─ Hover (* Mouse sopra - se mouse supportato *) └─ Pressed (* Premuto attivamente *)
procedure TWvdSButtonTUIRenderer.Paint(AControl: TWvdSControl; ASurface: TRenderSurface); var Btn: TWvdSButton; Style: TStyle; begin Btn := TWvdSButton(AControl); (* Selezione stile basata su stato *) if not Btn.Enabled then Style := Theme.ButtonDisabled else if Btn.Focused then Style := Theme.ButtonFocused else Style := Theme.ButtonBase; (* Renderizzare bordo + caption *) ASurface.DrawBorder(Btn.Bounds, bsSingle); ASurface.DrawText(Btn.Bounds.Left + 1, Btn.Bounds.Top, Btn.Caption, Style); end;
OBBLIGATORIO: - Focus Ring / Highlight (* Chiaramente riconoscibile *) - Hint Hotkey/Acceleratore (* Opzionale: [A]nnulla *) - Indicatore stato errore (* Errori validazione visibili *)
OBBLIGATORIO per ogni controllo focalizzabile: - Consumare Tab (FocusManager decide il movimento) - Elaborare correttamente Enter/Space - Input testo via eventi normalizzati (non raw keycode)
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; (* Evento consumato *) 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); (* Dimensione minima ragionevole *) Result.Width := Length(Btn.Caption) + 4; (* Padding + bordo *) Result.Height := 3; (* Bordo sopra/sotto + testo *) (* Rispettare constraint dal parent *) if Result.Width > AAvailable.Width then Result.Width := AAvailable.Width; end;
RIGOROSO: - Disegnare SOLO all'interno del rect assegnato - Clipping DEVE essere attivo - Nessuna operazione di rendering fuori bounds
Per controlli con grandi quantità di dati (tabelle, liste, alberi, log):
OBBLIGATORIO: - Renderizzare solo righe/elementi visibili - Nessuna allocazione buffer per riga per frame - Gestire correttamente posizione scroll
procedure TWvdSListTUIRenderer.Paint(AControl: TWvdSControl; ASurface: TRenderSurface); var List: TWvdSList; VisibleStart, VisibleEnd: Integer; I, Y: Integer; begin List := TWvdSList(AControl); (* Calcolare solo area visibile *) VisibleStart := List.ScrollOffset; VisibleEnd := VisibleStart + List.VisibleRowCount; if VisibleEnd > List.ItemCount then VisibleEnd := List.ItemCount; (* Renderizzare solo elementi visibili *) Y := 0; for I := VisibleStart to VisibleEnd - 1 do begin ASurface.DrawText(0, Y, List.Items[I], GetItemStyle(I)); Inc(Y); end; end;
[ ] Stato base renderizza correttamente [ ] Stato focus chiaramente riconoscibile [ ] Stato disabled distinguibile [ ] Stato errore/invalid visibile
[ ] Navigazione Tab funziona [ ] Enter/Space attiva azione (per button) [ ] Input testo normalizzato (per TextEdit) [ ] Mouse supportato opzionalmente
[ ] Measure restituisce dimensione sensata [ ] Paint rispetta rect assegnato [ ] Nessun rendering fuori bounds [ ] Resize gestito correttamente
[ ] Liste grandi virtualizzate [ ] Nessun repaint O(N) per offscreen [ ] Nessuna allocazione inutile per frame
Documentare ogni controllo: WHY: Perché esiste questo controllo (casi d'uso) WHEN: Quando usarlo vs alternative più semplici Note TUI: - Requisiti capability (mouse, immagini, TrueColor) - Comportamento resize (anchoring/responsive) - Limitazioni note