====== Razvoj kontrolnikov ====== Potek dela za ustvarjanje novih UI kontrolnikov za WvdS FPC RAD Suite. Kontrolniki morajo delovati tako v **načrtovalnem času** (VS Code Designer) kot v **izvajalnem času** (Desktop/TUI/Web). ===== Pregled ===== Kontrolnik sestoji iz več plasti: ^ Plast ^ Namen ^ Pot ^ | Runtime Model | Cilju nevtralna logika | ''~/sources/common/ui/controls/{category}/'' | | Target Renderer | Cilju specifična predstavitev | ''~/sources/common/ui/targets/{target}/renderers/'' | | Pack Manifest | PXAML oznaka → Preslikava razreda | ''~/packs/controls/{control_id}/'' | | Designer Metadata | Orodjarna, lastnosti, dogodki | ''~/sources/extensions/wvds.vscode.ui.meta/'' | ===== Spremenljivke ===== Pri ustvarjanju kontrolnika določite te spremenljivke: ^ Spremenljivka ^ Primer ^ Opis ^ | ''{CONTROL_ID}'' | ''dateedit'' | Enolični identifikator (male črke) | | ''{CONTROL_CLASS}'' | ''TWvdSDateEdit'' | Ime Pascal razreda | | ''{PXAML_TAG}'' | ''DateEdit'' | Ime oznake v PXAML | | ''{PXAML_NS}'' | ''wvds'' | Imenski prostor (wvds za notranje kontrolnike) | | ''{TARGETS}'' | ''tui,desktop,web'' | Podprti cilji | ===== Potek dela (7 korakov) ===== ==== Korak 1: Zamrznitev pogodbe ==== Definirajte javni API kontrolnika: **Lastnosti:** (* Minimalni nabor javnih lastnosti *) 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; **Dogodki:** (* Zahtevani dogodki *) property OnValueChanged: TWvdSNotifyEvent read FOnValueChanged write FOnValueChanged; property OnValidating: TWvdSValidateEvent read FOnValidating write FOnValidating; **TargetCaps:** ^ Zmožnost ^ TUI ^ Desktop ^ Web ^ | Tipkovnica | Zahtevano | Zahtevano | Zahtevano | | Miška | Opcijsko | Zahtevano | Zahtevano | | Dotik | N/A | Opcijsko | Opcijsko | | Pojavno okno | Opcijsko | Zahtevano | Zahtevano | ==== Korak 2: Enota izvajalnega modela ==== Ustvarite cilju nevtralno enoto modela: **Pot:** ''~/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(Polje za vnos datuma s pojavnim koledarjem) Cilju nevtralen model za DateEdit. Ne vsebuje NOBENIH odvisnosti od upodabljanja ali gostitelja. *) 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 (* Implementacija brez cilju specifične kode *) end. **Brez cilju specifičnih odvisnosti!** Brez DOM, brez terminalskih I/O, brez LCL enot. ==== Korak 3: Enote ciljnih upodabljalnikov ==== Za vsak cilj v ''{TARGETS}'' ustvarite upodabljalnik: === TUI upodabljalnik === **Pot:** ''~/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 (* Obravnava tipkovnice za spremembo datuma *) end; end. === Desktop upodabljalnik === **Pot:** ''~/sources/common/ui/targets/gui/renderers/WvdS.UI.Desktop.Renderers.DateEdit.pas'' === Web upodabljalnik === **Pot:** ''~/sources/common/ui/targets/web/renderers/WvdS.UI.Web.Renderers.DateEdit.pas'' ==== Korak 4: Pack manifest ==== Ustvarite Pack manifest za PXAML razreševalnik: **Pot:** ''~/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"] } } ==== Korak 5: Metapodatki načrtovalnika ==== Ustvarite metapodatke načrtovalnika za orodjarno in lastnosti: **Dodajte deskriptor v:** ''~/sources/common/core/meta/WvdS.Meta.Controls.Editors.pas'' procedure RegisterDateEditMeta(ARegistry: TMetaRegistry); var TypeMeta: TTypeMeta; begin TypeMeta := CreateTypeMeta('DateEdit', 'TWvdSDateEdit', 'Editors'); TypeMeta.Description := 'Polje za vnos datuma s pojavnim koledarjem'; TypeMeta.Icon := 'calendar'; (* Lastnosti *) TypeMeta.AddProperty(CreatePropertyMeta('Value', pkDateTime, 'Trenutna vrednost datuma')); TypeMeta.AddProperty(CreatePropertyMeta('Format', pkString, 'Format prikaza (npr. yyyy-mm-dd)')); TypeMeta.AddProperty(CreatePropertyMeta('MinDate', pkDateTime, 'Najzgodnejši dovoljeni datum')); TypeMeta.AddProperty(CreatePropertyMeta('MaxDate', pkDateTime, 'Najpoznejši dovoljeni datum')); TypeMeta.AddProperty(CreatePropertyMeta('ReadOnly', pkBoolean, 'Način samo za branje')); (* Dogodki *) TypeMeta.AddEvent(CreateEventMeta('OnValueChanged', 'Sproži se ob spremembi Value')); TypeMeta.AddEvent(CreateEventMeta('OnValidating', 'Sproži se pred spremembo vrednosti')); ARegistry.RegisterType(TypeMeta); end; ==== Korak 6: Registracija v registru ==== Registrirajte kontrolnik v izvajalnih registrih: === PXAML razreševalnik === ''pxamlc'' prevajalnik samodejno bere ''pack.wvds.json'' iz ''~/packs/''. === Register izvajalnika ciljev === Za vsak cilj posodobite register upodabljalnikov: (* V WvdS.UI.TUI.Render.Registry.pas *) procedure RegisterTUIRenderers; begin (* ... drugi upodabljalniki ... *) RegisterRenderer(TWvdSDateEdit, TWvdSTUIDateEditRenderer); end; ==== Korak 7: Dokumentacija ==== Ustvarite API dokumentacijo za kontrolnik: **Pot:** ''~/docs/api/controls/dateedit.md'' # TWvdSDateEdit Polje za vnos datuma z opcijskim pojavnim koledarjem. ## Kdaj uporabiti - Uporabnik mora vnesti datum - Omejitev datumskega razpona (MinDate/MaxDate) - Potreben formatiran prikaz datuma ## Lastnosti | Lastnost | Tip | Opis | |----------|-----|------| | Value | TDateTime | Trenutna vrednost datuma | | Format | string | Format prikaza | | MinDate | TDateTime | Najzgodnejši dovoljeni datum | | MaxDate | TDateTime | Najpoznejši dovoljeni datum | | ReadOnly | Boolean | Način samo za branje | ## Dogodki | Dogodek | Opis | |---------|------| | OnValueChanged | Sproži se ob spremembi Value | | OnValidating | Omogoča validacijo pred spremembo vrednosti | ## Primer ```xml ``` ===== Merila za dokončanje ===== Kontrolnik je dokončan, ko: [ ] VS Code Designer lahko postavi {PXAML_NS}:{PXAML_TAG} [ ] Lastnosti so urejalne v načrtovalniku [ ] Predogled v načrtovalniku deluje [ ] Ciljna gradnja (tui/desktop/web) se uspešno prevede [ ] Pack manifest je prisoten in razreševalnik ga prepozna [ ] API dokumentacija je napisana ===== Glejte tudi ===== * [[.:pxaml-pipeline|PXAML cevovod]] * [[.:pack-struktur|Struktura paketov]] * [[.:targets|Cilji gradnje]] * [[.:meta-api|Meta API]]