Inhaltsverzeichnis
Control-Entwicklung
Workflow zur Erstellung neuer UI-Controls für die WvdS FPC RAD Suite.
Übersicht
Ein Control besteht aus mehreren Schichten:
| Schicht | Zweck | Pfad |
|---|---|---|
| Runtime Model | Target-neutrale Logik | ~/sources/common/ui/controls/{category}/ |
| Target Renderer | Target-spezifische Darstellung | ~/sources/common/ui/targets/{target}/renderers/ |
| Pack Manifest | PXAML-Tag → Klasse Mapping | ~/packs/controls/{control_id}/ |
| Designer Metadata | Toolbox, Properties, Events | ~/sources/extensions/wvds.vscode.ui.meta/ |
Variablen
Beim Erstellen eines Controls diese Variablen festlegen:
| Variable | Beispiel | Beschreibung |
|---|---|---|
{CONTROL_ID} | dateedit | Eindeutiger Identifier (lowercase) |
{CONTROL_CLASS} | TWvdSDateEdit | Pascal-Klassenname |
{PXAML_TAG} | DateEdit | Tag-Name im PXAML |
{PXAML_NS} | wvds | Namespace (wvds für interne Controls) |
{TARGETS} | tui,desktop,web | Unterstützte Targets |
Workflow (7 Schritte)
Schritt 1: Contract Freeze
Definiere die öffentliche API des Controls:
Properties:
(* Minimaler Satz öffentlicher Properties *) 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:
(* Erforderliche Events *) property OnValueChanged: TWvdSNotifyEvent read FOnValueChanged write FOnValueChanged; property OnValidating: TWvdSValidateEvent read FOnValidating write FOnValidating;
TargetCaps:
| Capability | TUI | Desktop | Web |
|---|---|---|---|
| Keyboard | Required | Required | Required |
| Mouse | Optional | Required | Required |
| Touch | N/A | Optional | Optional |
| Popup | Optional | Required | Required |
Schritt 2: Runtime Model Unit
Erstelle die target-neutrale Model-Unit:
Pfad: ~/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(Datums-Eingabefeld mit Kalender-Popup) Target-neutrales Model für DateEdit. Enthält KEINE Rendering- oder Host-Abhängigkeiten. *) 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 (* Implementierung ohne Target-spezifischen Code *) end.
Schritt 3: Target Renderer Units
Für jedes Target in {TARGETS} einen Renderer erstellen:
TUI Renderer
Pfad: ~/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 (* Keyboard-Handling für Datumsänderung *) end; end.
Desktop Renderer
Pfad: ~/sources/common/ui/targets/gui/renderers/WvdS.UI.Desktop.Renderers.DateEdit.pas
Web Renderer
Pfad: ~/sources/common/ui/targets/web/renderers/WvdS.UI.Web.Renderers.DateEdit.pas
Schritt 4: Pack Manifest
Erstelle das Pack-Manifest für den PXAML-Resolver:
Pfad: ~/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"] } }
Schritt 5: Designer Metadata
Erstelle Designer-Metadaten für Toolbox und Properties:
Descriptor hinzufügen zu: ~/sources/common/core/meta/WvdS.Meta.Controls.Editors.pas
procedure RegisterDateEditMeta(ARegistry: TMetaRegistry); var TypeMeta: TTypeMeta; begin TypeMeta := CreateTypeMeta('DateEdit', 'TWvdSDateEdit', 'Editors'); TypeMeta.Description := 'Datums-Eingabefeld mit Kalender-Popup'; TypeMeta.Icon := 'calendar'; (* Properties *) TypeMeta.AddProperty(CreatePropertyMeta('Value', pkDateTime, 'Aktueller Datumswert')); TypeMeta.AddProperty(CreatePropertyMeta('Format', pkString, 'Anzeigeformat (z.B. yyyy-mm-dd)')); TypeMeta.AddProperty(CreatePropertyMeta('MinDate', pkDateTime, 'Frühestes erlaubtes Datum')); TypeMeta.AddProperty(CreatePropertyMeta('MaxDate', pkDateTime, 'Spätestes erlaubtes Datum')); TypeMeta.AddProperty(CreatePropertyMeta('ReadOnly', pkBoolean, 'Nur-Lesen Modus')); (* Events *) TypeMeta.AddEvent(CreateEventMeta('OnValueChanged', 'Wird ausgelöst wenn Value sich ändert')); TypeMeta.AddEvent(CreateEventMeta('OnValidating', 'Wird vor Wertänderung ausgelöst')); ARegistry.RegisterType(TypeMeta); end;
Schritt 6: Registry-Registrierung
Registriere das Control in den Runtime-Registries:
PXAML Resolver
Der pxamlc Compiler liest pack.wvds.json automatisch aus ~/packs/.
Target Runtime Registry
Für jedes Target die Renderer-Registry aktualisieren:
(* In WvdS.UI.TUI.Render.Registry.pas *) procedure RegisterTUIRenderers; begin (* ... andere Renderer ... *) RegisterRenderer(TWvdSDateEdit, TWvdSTUIDateEditRenderer); end;
Schritt 7: Dokumentation
API-Dokumentation für das Control erstellen:
Pfad: ~/docs/api/controls/dateedit.md
# TWvdSDateEdit
Datums-Eingabefeld mit optionalem Kalender-Popup.
## Wann verwenden
- Benutzer soll ein Datum eingeben
- Datumsbereich einschränken (MinDate/MaxDate)
- Formatierte Datumsanzeige erforderlich
## Properties
| Property | Typ | Beschreibung |
|----------|-----|--------------|
| Value | TDateTime | Aktueller Datumswert |
| Format | string | Anzeigeformat |
| MinDate | TDateTime | Frühestes erlaubtes Datum |
| MaxDate | TDateTime | Spätestes erlaubtes Datum |
| ReadOnly | Boolean | Nur-Lesen Modus |
## Events
| Event | Beschreibung |
|-------|--------------|
| OnValueChanged | Wird ausgelöst wenn Value sich ändert |
| OnValidating | Erlaubt Validierung vor Wertänderung |
## Beispiel
```xml
<DateEdit Value="{Binding BirthDate}"
Format="dd.MM.yyyy"
MinDate="1900-01-01"
MaxDate="2100-12-31" />
```
Completion Criteria
Ein Control ist fertig wenn:
[ ] VS Code Designer kann {PXAML_NS}:{PXAML_TAG} platzieren
[ ] Properties im Designer editierbar
[ ] Preview im Designer funktioniert
[ ] Target Build (tui/desktop/web) kompiliert erfolgreich
[ ] Pack Manifest vorhanden und vom Resolver erkannt
[ ] API-Dokumentation geschrieben