====== Sviluppo controlli ======
Workflow per la creazione di nuovi controlli UI per la suite WvdS FPC RAD.
I controlli devono funzionare sia in **design-time** (VS Code Designer) che in **runtime** (Desktop/TUI/Web).
===== Panoramica =====
Un controllo e composto da diversi strati:
^ Strato ^ Scopo ^ Percorso ^
| Runtime Model | Logica target-neutrale | ''~/sources/common/ui/controls/{category}/'' |
| Target Renderer | Rappresentazione target-specifica | ''~/sources/common/ui/targets/{target}/renderers/'' |
| Pack Manifest | Mapping tag PXAML → classe | ''~/packs/controls/{control_id}/'' |
| Designer Metadata | Toolbox, properties, eventi | ''~/sources/extensions/wvds.vscode.ui.meta/'' |
===== Variabili =====
Definire queste variabili durante la creazione di un controllo:
^ Variabile ^ Esempio ^ Descrizione ^
| ''{CONTROL_ID}'' | ''dateedit'' | Identificatore univoco (minuscolo) |
| ''{CONTROL_CLASS}'' | ''TWvdSDateEdit'' | Nome classe Pascal |
| ''{PXAML_TAG}'' | ''DateEdit'' | Nome tag nel PXAML |
| ''{PXAML_NS}'' | ''wvds'' | Namespace (wvds per controlli interni) |
| ''{TARGETS}'' | ''tui,desktop,web'' | Target supportati |
===== Workflow (7 passaggi) =====
==== Passaggio 1: Contract Freeze ====
Definire l'API pubblica del controllo:
**Properties:**
(* Set minimo di properties pubbliche *)
property Value: TDateTime read FValue write SetValue;
property Format: string read FFormat write SetFormat;
property MinDate: TDateTime read FMinDate write FMinDate;
property MaxDate: TDateTime read FMaxDate write FMaxDate;
property ReadOnly: Boolean read FReadOnly write FReadOnly;
**Events:**
(* Eventi richiesti *)
property OnValueChanged: TWvdSNotifyEvent read FOnValueChanged write FOnValueChanged;
property OnValidating: TWvdSValidateEvent read FOnValidating write FOnValidating;
**TargetCaps:**
^ Capability ^ TUI ^ Desktop ^ Web ^
| Keyboard | Richiesto | Richiesto | Richiesto |
| Mouse | Opzionale | Richiesto | Richiesto |
| Touch | N/A | Opzionale | Opzionale |
| Popup | Opzionale | Richiesto | Richiesto |
==== Passaggio 2: Unit Runtime Model ====
Creare la unit model target-neutrale:
**Percorso:** ''~/sources/common/ui/controls/{category}/WvdS.UI.Controls.{Category}.{ControlId}.pas''
unit WvdS.UI.Controls.Editors.DateEdit;
{$mode objfpc}{$H+}
interface
uses
WvdS.UI.Core.Base,
WvdS.UI.Core.Events;
type
(*
@abstract(Campo di input data con popup calendario)
Model target-neutrale per DateEdit.
NON contiene dipendenze di rendering o host.
*)
TWvdSDateEdit = class(TWvdSControl)
private
FValue: TDateTime;
FFormat: string;
FOnValueChanged: TWvdSNotifyEvent;
procedure SetValue(AValue: TDateTime);
protected
procedure DoValueChanged; virtual;
public
constructor Create(AOwner: TWvdSComponent); override;
property Value: TDateTime read FValue write SetValue;
property Format: string read FFormat write FFormat;
property OnValueChanged: TWvdSNotifyEvent read FOnValueChanged write FOnValueChanged;
end;
implementation
(* Implementazione senza codice target-specifico *)
end.
**Nessuna dipendenza target-specifica!** No DOM, no Terminal I/O, no unit LCL.
==== Passaggio 3: Unit Target Renderer ====
Creare un renderer per ogni target in ''{TARGETS}'':
=== Renderer TUI ===
**Percorso:** ''~/sources/common/ui/targets/tui/renderers/WvdS.UI.TUI.Renderers.DateEdit.pas''
unit WvdS.UI.TUI.Renderers.DateEdit;
{$mode objfpc}{$H+}
interface
uses
WvdS.UI.TUI.Renderer.Base,
WvdS.UI.Controls.Editors.DateEdit;
type
TWvdSTUIDateEditRenderer = class(TWvdSTUIRenderer)
public
procedure Measure(AControl: TWvdSControl; out AWidth, AHeight: Integer); override;
procedure Paint(AControl: TWvdSControl; ACanvas: TWvdSTUICanvas); override;
procedure HandleInput(AControl: TWvdSControl; AEvent: TWvdSInputEvent); override;
end;
implementation
procedure TWvdSTUIDateEditRenderer.Measure(AControl: TWvdSControl; out AWidth, AHeight: Integer);
begin
AWidth := 12; (* [YYYY-MM-DD] *)
AHeight := 1;
end;
procedure TWvdSTUIDateEditRenderer.Paint(AControl: TWvdSControl; ACanvas: TWvdSTUICanvas);
var
DateEdit: TWvdSDateEdit;
begin
DateEdit := AControl as TWvdSDateEdit;
ACanvas.DrawText(0, 0, FormatDateTime(DateEdit.Format, DateEdit.Value));
end;
procedure TWvdSTUIDateEditRenderer.HandleInput(AControl: TWvdSControl; AEvent: TWvdSInputEvent);
begin
(* Gestione tastiera per modifica data *)
end;
end.
=== Renderer Desktop ===
**Percorso:** ''~/sources/common/ui/targets/gui/renderers/WvdS.UI.Desktop.Renderers.DateEdit.pas''
=== Renderer Web ===
**Percorso:** ''~/sources/common/ui/targets/web/renderers/WvdS.UI.Web.Renderers.DateEdit.pas''
==== Passaggio 4: Pack Manifest ====
Creare il manifesto pack per il resolver PXAML:
**Percorso:** ''~/packs/controls/{control_id}/pack.wvds.json''
{
"id": "dateedit",
"vendor": "wvds",
"version": "0.1.0",
"xml": {
"namespace": "wvds",
"tag": "DateEdit"
},
"model": {
"unit": "WvdS.UI.Controls.Editors.DateEdit",
"class": "TWvdSDateEdit"
},
"renderers": {
"tui": {
"unit": "WvdS.UI.TUI.Renderers.DateEdit",
"class": "TWvdSTUIDateEditRenderer"
},
"desktop": {
"unit": "WvdS.UI.Desktop.Renderers.DateEdit",
"class": "TWvdSDesktopDateEditRenderer"
},
"web": {
"unit": "WvdS.UI.Web.Renderers.DateEdit",
"class": "TWvdSWebDateEditRenderer"
}
},
"targetCaps": {
"requires": ["keyboard"],
"optional": ["mouse", "popup"]
}
}
==== Passaggio 5: Designer Metadata ====
Creare metadati designer per toolbox e properties:
**Aggiungere descriptor a:** ''~/sources/common/core/meta/WvdS.Meta.Controls.Editors.pas''
procedure RegisterDateEditMeta(ARegistry: TMetaRegistry);
var
TypeMeta: TTypeMeta;
begin
TypeMeta := CreateTypeMeta('DateEdit', 'TWvdSDateEdit', 'Editors');
TypeMeta.Description := 'Campo di input data con popup calendario';
TypeMeta.Icon := 'calendar';
(* Properties *)
TypeMeta.AddProperty(CreatePropertyMeta('Value', pkDateTime, 'Valore data corrente'));
TypeMeta.AddProperty(CreatePropertyMeta('Format', pkString, 'Formato visualizzazione (es. yyyy-mm-dd)'));
TypeMeta.AddProperty(CreatePropertyMeta('MinDate', pkDateTime, 'Data minima consentita'));
TypeMeta.AddProperty(CreatePropertyMeta('MaxDate', pkDateTime, 'Data massima consentita'));
TypeMeta.AddProperty(CreatePropertyMeta('ReadOnly', pkBoolean, 'Modalita sola lettura'));
(* Events *)
TypeMeta.AddEvent(CreateEventMeta('OnValueChanged', 'Attivato quando Value cambia'));
TypeMeta.AddEvent(CreateEventMeta('OnValidating', 'Attivato prima del cambio valore'));
ARegistry.RegisterType(TypeMeta);
end;
==== Passaggio 6: Registrazione Registry ====
Registrare il controllo nelle registry runtime:
=== PXAML Resolver ===
Il compilatore ''pxamlc'' legge automaticamente ''pack.wvds.json'' da ''~/packs/''.
=== Target Runtime Registry ===
Aggiornare la registry renderer per ogni target:
(* In WvdS.UI.TUI.Render.Registry.pas *)
procedure RegisterTUIRenderers;
begin
(* ... altri renderer ... *)
RegisterRenderer(TWvdSDateEdit, TWvdSTUIDateEditRenderer);
end;
==== Passaggio 7: Documentazione ====
Creare documentazione API per il controllo:
**Percorso:** ''~/docs/api/controls/dateedit.md''
# TWvdSDateEdit
Campo di input data con popup calendario opzionale.
## Quando usare
- L'utente deve inserire una data
- Limitare l'intervallo date (MinDate/MaxDate)
- Richiesta visualizzazione data formattata
## Properties
| Property | Tipo | Descrizione |
|----------|------|-------------|
| Value | TDateTime | Valore data corrente |
| Format | string | Formato visualizzazione |
| MinDate | TDateTime | Data minima consentita |
| MaxDate | TDateTime | Data massima consentita |
| ReadOnly | Boolean | Modalita sola lettura |
## Events
| Evento | Descrizione |
|--------|-------------|
| OnValueChanged | Attivato quando Value cambia |
| OnValidating | Permette validazione prima del cambio valore |
## Esempio
```xml
```
===== Criteri di completamento =====
Un controllo e completo quando:
[ ] VS Code Designer puo posizionare {PXAML_NS}:{PXAML_TAG}
[ ] Properties modificabili nel designer
[ ] Preview nel designer funzionante
[ ] Build target (tui/desktop/web) compila con successo
[ ] Pack manifest presente e riconosciuto dal resolver
[ ] Documentazione API scritta
===== Vedi anche =====
* [[.:pxaml-pipeline|Pipeline PXAML]]
* [[.:pack-struktur|Struttura pack]]
* [[.:targets|Target di build]]
* [[.:meta-api|Meta API]]