Control-Architektur

Architektur und Patterns für WvdS UI Controls.

Übersicht

Alle WvdS Controls folgen dem Properties/ViewInfo/Control Pattern (WPF-inspiriert):

TWvdSDependencyObject
    └── TWvdSFrameworkElement
            └── TWvdSControl
                    └── TWvdS{ControlName}

Klassenhierarchie

TWvdSDependencyObject

Basis für alle Objekte mit Dependency Properties.

TWvdSDependencyObject = class
private
  FPropertyValues: TDictionary<string, TValue>;
public
  function GetValue(AProperty: TWvdSDependencyProperty): TValue;
  procedure SetValue(AProperty: TWvdSDependencyProperty; AValue: TValue);
  procedure ClearValue(AProperty: TWvdSDependencyProperty);
end;

TWvdSFrameworkElement

Erweitert um Layout und visuelle Eigenschaften.

TWvdSFrameworkElement = class(TWvdSDependencyObject)
private
  FWidth, FHeight: Double;
  FMinWidth, FMinHeight: Double;
  FMaxWidth, FMaxHeight: Double;
  FMargin, FPadding: TWvdSThickness;
  FHorizontalAlignment: TWvdSHorizontalAlignment;
  FVerticalAlignment: TWvdSVerticalAlignment;
public
  function MeasureOverride(AAvailableSize: TWvdSSize): TWvdSSize; virtual;
  function ArrangeOverride(AFinalSize: TWvdSSize): TWvdSSize; virtual;
  procedure Render(ASurface: TWvdSRenderSurface); virtual; abstract;
end;

TWvdSControl

Basis für alle interaktiven Controls.

TWvdSControl = class(TWvdSFrameworkElement)
private
  FIsEnabled: Boolean;
  FIsFocused: Boolean;
  FTabIndex: Integer;
  FTemplate: TWvdSControlTemplate;
  FViewInfo: TWvdSViewInfo;
public
  property IsEnabled: Boolean read FIsEnabled write SetIsEnabled;
  property IsFocused: Boolean read FIsFocused;
  property TabIndex: Integer read FTabIndex write FTabIndex;
  property Template: TWvdSControlTemplate read FTemplate write FTemplate;
  property ViewInfo: TWvdSViewInfo read FViewInfo;
 
  procedure OnGotFocus; virtual;
  procedure OnLostFocus; virtual;
  procedure OnKeyDown(AEvent: TWvdSKeyEventArgs); virtual;
  procedure OnMouseDown(AEvent: TWvdSMouseEventArgs); virtual;
end;

Properties/ViewInfo/Control Pattern

Drei-Schichten-Architektur

┌─────────────────────────────────────────────────┐
│  Properties Layer                               │
│  - Öffentliche API (Properties, Events)        │
│  - Dependency Properties                        │
│  - Bindungsfähig, PXAML-kompatibel             │
├─────────────────────────────────────────────────┤
│  ViewInfo Layer                                 │
│  - Berechnete visuelle Informationen           │
│  - Cache für Rendering                          │
│  - Bounds, Rects, berechnete Werte             │
├─────────────────────────────────────────────────┤
│  Control Layer                                  │
│  - Rendering-Logik                              │
│  - Input-Handling                               │
│  - Target-spezifisch (TUI/GUI/Web)             │
└─────────────────────────────────────────────────┘

Beispiel: TWvdSButton

(* Properties Layer *)
TWvdSButtonProperties = class(TWvdSControlProperties)
published
  property Caption: string;
  property ImageSource: TWvdSImageSource;
  property IsDefault: Boolean;
  property IsCancel: Boolean;
  property Command: IWvdSCommand;
end;
 
(* ViewInfo Layer *)
TWvdSButtonViewInfo = class(TWvdSViewInfo)
private
  FCaptionBounds: TRect;
  FImageBounds: TRect;
  FContentBounds: TRect;
public
  procedure Calculate(AAvailableRect: TRect); override;
  property CaptionBounds: TRect read FCaptionBounds;
  property ImageBounds: TRect read FImageBounds;
end;
 
(* Control Layer *)
TWvdSButton = class(TWvdSControl)
private
  FProperties: TWvdSButtonProperties;
  FViewInfo: TWvdSButtonViewInfo;
public
  constructor Create;
  procedure Render(ASurface: TWvdSRenderSurface); override;
  procedure OnClick; virtual;
published
  property Caption: string read GetCaption write SetCaption;
  property OnClick: TNotifyEvent read FOnClick write FOnClick;
end;

Target-Renderer

Jedes Control hat pro Target einen spezialisierten Renderer.

Renderer-Interface

IWvdSControlRenderer = interface
  function Measure(AControl: TWvdSControl; AAvailable: TWvdSSize): TWvdSSize;
  procedure Arrange(AControl: TWvdSControl; ARect: TRect);
  procedure Paint(AControl: TWvdSControl; ASurface: TWvdSRenderSurface);
  function HandleInput(AControl: TWvdSControl; AEvent: TWvdSInputEvent): Boolean;
end;

Target-spezifische Implementierungen

sources/common/ui/targets/
├── tui/
│   ├── WvdS.UI.TUI.ButtonRenderer.pas
│   ├── WvdS.UI.TUI.TextEditRenderer.pas
│   └── ...
├── gui/
│   ├── WvdS.UI.GUI.ButtonRenderer.pas
│   └── ...
└── web/
    ├── WvdS.UI.Web.ButtonRenderer.pas
    └── ...

TUI Renderer Beispiel

TWvdSButtonTUIRenderer = class(TInterfacedObject, IWvdSControlRenderer)
public
  function Measure(AControl: TWvdSControl; AAvailable: TWvdSSize): TWvdSSize;
  procedure Arrange(AControl: TWvdSControl; ARect: TRect);
  procedure Paint(AControl: TWvdSControl; ASurface: TWvdSRenderSurface);
  function HandleInput(AControl: TWvdSControl; AEvent: TWvdSInputEvent): Boolean;
end;
 
procedure TWvdSButtonTUIRenderer.Paint(AControl: TWvdSControl;
  ASurface: TWvdSRenderSurface);
var
  Btn: TWvdSButton;
  Style: TWvdSStyle;
begin
  Btn := TWvdSButton(AControl);
 
  (* State-basierte Style-Auswahl *)
  if not Btn.IsEnabled then
    Style := Theme.ButtonDisabled
  else if Btn.IsFocused then
    Style := Theme.ButtonFocused
  else
    Style := Theme.ButtonDefault;
 
  (* Border zeichnen *)
  ASurface.DrawBorder(Btn.Bounds, bsSingle, Style);
 
  (* Caption zentriert zeichnen *)
  ASurface.DrawTextCentered(Btn.ViewInfo.ContentBounds,
    Btn.Caption, Style);
end;

Dependency Properties

Definition

class var
  CaptionProperty: TWvdSDependencyProperty;
 
class constructor TWvdSButton.Create;
begin
  CaptionProperty := TWvdSDependencyProperty.Register(
    'Caption',                    (* Name *)
    TypeInfo(string),             (* Typ *)
    TWvdSButton,                  (* Owner *)
    '',                           (* Default *)
    @OnCaptionChanged             (* Callback *)
  );
end;
pas2js-Kompatibilität: class var funktioniert nicht in pas2js. Verwende stattdessen Unit-Level Variablen.

pas2js-kompatible Alternative

var
  GButtonCaptionProperty: TWvdSDependencyProperty = nil;
 
function ButtonCaptionProperty: TWvdSDependencyProperty;
begin
  if GButtonCaptionProperty = nil then
    GButtonCaptionProperty := TWvdSDependencyProperty.Register(...);
  Result := GButtonCaptionProperty;
end;

Visual States

Pflicht-States:
├─ Normal       (* Default-Zustand *)
├─ Focused      (* Hat Tastatur-Fokus *)
├─ Disabled     (* IsEnabled = False *)
└─ Invalid      (* Validierungsfehler *)

Optional-States:
├─ MouseOver    (* Maus darüber *)
├─ Pressed      (* Aktiv gedrückt *)
└─ Selected     (* Ausgewählt *)

Input-Handling

Event-Reihenfolge

1. PreviewKeyDown (Tunneling)
2. KeyDown (Bubbling)
3. Control-spezifische Verarbeitung
4. Command-Ausführung (wenn gebunden)

Focus-Management

TWvdSFocusManager = class
public
  class function GetFocusedElement: TWvdSFrameworkElement;
  class procedure SetFocus(AElement: TWvdSFrameworkElement);
  class procedure MoveFocus(ADirection: TWvdSFocusNavigationDirection);
end;
 
TWvdSFocusNavigationDirection = (
  fndNext,      (* Tab *)
  fndPrevious,  (* Shift+Tab *)
  fndUp,
  fndDown,
  fndLeft,
  fndRight
);

Data Binding

Binding-Syntax in PXAML

<TextEdit Text="{Binding CustomerName, Mode=TwoWay}" />
<Button Caption="{Binding SaveCommand.Caption}"
        Command="{Binding SaveCommand}" />

Binding-Modes

Mode Beschreibung
OneTime Einmalig bei Initialisierung
OneWay Source → Target
TwoWay Source ↔ Target
OneWayToSource Target → Source

Control-Lebenszyklus

1. Konstruktor
   └─ Properties initialisieren

2. Template anwenden
   └─ ControlTemplate laden und anwenden

3. Measure
   └─ Gewünschte Größe berechnen

4. Arrange
   └─ Finale Position und Größe

5. Render
   └─ In RenderSurface zeichnen

6. Input-Events
   └─ Tastatur, Maus, Touch

7. Destruktor
   └─ Ressourcen freigeben

Dateistruktur

sources/common/ui/
├── framework/                    # Basis-Framework (42 Units)
│   ├── WvdS.UI.DependencyObject.pas
│   ├── WvdS.UI.FrameworkElement.pas
│   ├── WvdS.UI.Control.pas
│   ├── WvdS.UI.ViewInfo.pas
│   └── ...
│
├── controls/                     # Control-Implementierungen
│   ├── basic/
│   │   ├── WvdS.UI.Controls.Button.pas
│   │   ├── WvdS.UI.Controls.Label.pas
│   │   └── ...
│   ├── editors/
│   ├── data/
│   └── ...
│
├── targets/                      # Target-spezifische Renderer
│   ├── tui/
│   ├── gui/
│   └── web/
│
└── themes/                       # Theme-Definitionen
    ├── WvdS.UI.Themes.Default.pas
    └── WvdS.UI.Themes.Dark.pas

Siehe auch

Zuletzt geändert: den 29.01.2026 um 15:13