Inhaltsverzeichnis
Control Development
Workflow for creating new UI controls for the WvdS FPC RAD Suite.
Overview
A control consists of multiple layers:
| Layer | Purpose | Path |
|---|---|---|
| Runtime Model | Target-neutral logic | ~/sources/common/ui/controls/{category}/ |
| Target Renderer | Target-specific rendering | ~/sources/common/ui/targets/{target}/renderers/ |
| Pack Manifest | PXAML tag → class mapping | ~/packs/controls/{control_id}/ |
| Designer Metadata | Toolbox, Properties, Events | ~/sources/extensions/wvds.vscode.ui.meta/ |
Variables
When creating a control, define these variables:
| Variable | Example | Description |
|---|---|---|
{CONTROL_ID} | dateedit | Unique identifier (lowercase) |
{CONTROL_CLASS} | TWvdSDateEdit | Pascal class name |
{PXAML_TAG} | DateEdit | Tag name in PXAML |
{PXAML_NS} | wvds | Namespace (wvds for internal controls) |
{TARGETS} | tui,desktop,web | Supported targets |
Workflow (7 Steps)
Step 1: Contract Freeze
Define the public API of the control:
Properties:
(* Minimal set of public 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:
(* Required 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 |
Step 2: Runtime Model Unit
Create the target-neutral model unit:
Path: ~/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(Date input field with calendar popup) Target-neutral model for DateEdit. Contains NO rendering or host dependencies. *) 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 (* Implementation without target-specific code *) end.
Step 3: Target Renderer Units
Create a renderer for each target in {TARGETS}:
TUI Renderer
Path: ~/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 for date changes *) end; end.
Desktop Renderer
Path: ~/sources/common/ui/targets/gui/renderers/WvdS.UI.Desktop.Renderers.DateEdit.pas
Web Renderer
Path: ~/sources/common/ui/targets/web/renderers/WvdS.UI.Web.Renderers.DateEdit.pas
Step 4: Pack Manifest
Create the pack manifest for the PXAML resolver:
Path: ~/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"] } }
Step 5: Designer Metadata
Create designer metadata for Toolbox and Properties:
Add descriptor to: ~/sources/common/core/meta/WvdS.Meta.Controls.Editors.pas
procedure RegisterDateEditMeta(ARegistry: TMetaRegistry); var TypeMeta: TTypeMeta; begin TypeMeta := CreateTypeMeta('DateEdit', 'TWvdSDateEdit', 'Editors'); TypeMeta.Description := 'Date input field with calendar popup'; TypeMeta.Icon := 'calendar'; (* Properties *) TypeMeta.AddProperty(CreatePropertyMeta('Value', pkDateTime, 'Current date value')); TypeMeta.AddProperty(CreatePropertyMeta('Format', pkString, 'Display format (e.g. yyyy-mm-dd)')); TypeMeta.AddProperty(CreatePropertyMeta('MinDate', pkDateTime, 'Earliest allowed date')); TypeMeta.AddProperty(CreatePropertyMeta('MaxDate', pkDateTime, 'Latest allowed date')); TypeMeta.AddProperty(CreatePropertyMeta('ReadOnly', pkBoolean, 'Read-only mode')); (* Events *) TypeMeta.AddEvent(CreateEventMeta('OnValueChanged', 'Fired when Value changes')); TypeMeta.AddEvent(CreateEventMeta('OnValidating', 'Fired before value change')); ARegistry.RegisterType(TypeMeta); end;
Step 6: Registry Registration
Register the control in the runtime registries:
PXAML Resolver
The pxamlc compiler reads pack.wvds.json automatically from ~/packs/.
Target Runtime Registry
Update the renderer registry for each target:
(* In WvdS.UI.TUI.Render.Registry.pas *) procedure RegisterTUIRenderers; begin (* ... other renderers ... *) RegisterRenderer(TWvdSDateEdit, TWvdSTUIDateEditRenderer); end;
Step 7: Documentation
Create API documentation for the control:
Path: ~/docs/api/controls/dateedit.md
# TWvdSDateEdit
Date input field with optional calendar popup.
## When to Use
- User needs to enter a date
- Restrict date range (MinDate/MaxDate)
- Formatted date display required
## Properties
| Property | Type | Description |
|----------|------|-------------|
| Value | TDateTime | Current date value |
| Format | string | Display format |
| MinDate | TDateTime | Earliest allowed date |
| MaxDate | TDateTime | Latest allowed date |
| ReadOnly | Boolean | Read-only mode |
## Events
| Event | Description |
|-------|-------------|
| OnValueChanged | Fired when Value changes |
| OnValidating | Allows validation before value change |
## Example
```xml
<DateEdit Value="{Binding BirthDate}"
Format="dd.MM.yyyy"
MinDate="1900-01-01"
MaxDate="2100-12-31" />
```
Completion Criteria
A control is complete when:
[ ] VS Code Designer can place {PXAML_NS}:{PXAML_TAG}
[ ] Properties editable in Designer
[ ] Preview in Designer works
[ ] Target build (tui/desktop/web) compiles successfully
[ ] Pack manifest present and recognized by resolver
[ ] API documentation written