====== Architektur ======
Vega ist in vier Schichten aufgebaut. Jede kennt nur die unter ihr — das
ist die Regel, an der sich entscheidet, ob eine neue Datei richtig liegt.
===== Die vier Schichten =====
^ Schicht ^ Units ^ Aufgabe ^
| 1 — Grundlagen | ''Types'', ''Events'', ''Strings'', ''Lists'' | Farben, Rechtecke, Ereignisarten, Puffer, Listen |
| 2 — Zeichnen | ''Canvas'', ''Font'', ''TrueType'', ''Picture'', ''Theme'', ''Style'' | Pixel, Schrift, Bilder, Aussehen |
| 3 — Steuerelemente | ''Control'', ''StdCtrls'', ''Edits'', ''ListCtrls'', … | Der Baum aus Controls und der Dispatcher |
| 4 — Fenster | ''Backend'', ''Backend/*'', ''Forms'' | Die einzige Stelle mit Kontakt zum Betriebssystem |
Eine Unit aus Schicht 2 darf nichts aus Schicht 3 wissen. Wo das doch nötig
wird, steht ein **Funktionszeiger-Haken** statt eines Imports — siehe unten.
===== Eigenes Zeichnen =====
Jedes Control malt sich selbst in einen Pixelpuffer im Format ''TColor''
(Byte 0 = Blau, 1 = Grün, 2 = Rot, 3 = Alpha). Erst der fertige Puffer geht
über ''Present'' ans Fenstersystem.
Das kostet Arbeit und bringt drei Dinge:
* **Gleiches Aussehen überall.** Ein Knopf sieht unter X11 aus wie unter
Windows, weil es derselbe Code ist.
* **Themes bestimmen alles**, bis hinunter zur Titelleiste — es gibt
keine Systemvorgabe, die dazwischenfunkt.
* **Die HAL bleibt klein.** 21 Methoden, mehr braucht eine neue
Plattform nicht.
===== Die HAL =====
''Vega/Backend.lyx'' beschreibt den Vertrag als Interface ''IVegaBackend'':
Fenster anlegen und zerstören, Titel, Lage und Größe, Rahmen an/aus,
Maximieren, Ziehen am Rand, ''Present'', ''PollEvent'', Zeigerform,
Zeichenflächengröße, Monitore, Bildschirmgröße, Zwischenablage,
Skalierung.
Fensterhandles sind **opake ''int64''**. Was dahintersteckt, weiß nur das
Backend (X11: Window-XID, Win32: HWND). Der geteilte Code reicht sie durch
und deutet sie nie.
Mehr dazu: [[lyx_-_programmiersprache:vega:backends|Backends]].
===== Der Dispatcher =====
''Vega/Dispatch.lyx'' verteilt die Ereignisse eines Fensters auf die
Controls: Treffertest, Fokus, Maus einfangen, Doppelklick, Tastatur samt
Umschaltern, Ziehen und Ablegen, Kontextmenü, Hinweisfenster.
Er ist der Grund, warum ein Control nur ''DoMouseDown'' überschreiben muss
und nichts über Fenster wissen darf.
===== Zwei Muster, die man kennen muss =====
==== Funktionszeiger-Haken gegen Zyklen ====
Lyx hat keine Methodenzeiger und keine zyklischen Imports. Wenn eine untere
Schicht etwas aus einer oberen braucht, wird eine **Funktionsvariable**
gesetzt, statt zu importieren:
// In der unteren Unit:
var _kontextmenueOeffner: TOeffnerFn;
pub fn SetContextMenuOpener(f: TOeffnerFn): void {
_kontextmenueOeffner := f;
}
Die obere Unit trägt sich beim Start ein. Benutzt an drei Stellen:
Backend-Verweis in [[lyx_-_programmiersprache:vega:controls:tcustomedit|TCustomEdit]], ''SetActionRunner'' zwischen Menüs und
Aktionen, ''SetContextMenuOpener'' zwischen Dispatcher und Formularen.
Verwandt: ''ActionPtr'', ''PopupMenuPtr'' und ''ImagesPtr'' liegen als
''int64'' an [[lyx_-_programmiersprache:vega:controls:tcontrol|TControl]] — dieselbe Lösung für dasselbe Problem.
==== Eigenschaften als Getter/Setter ====
Lyx kennt kein ''property''. Was Delphi als
property Left: Integer read FLeft write SetLeft;
schreibt, ist hier ein Paar ''GetLeft'' / ''SetLeft'' — genau das, was der
Pascal-Compiler intern ohnehin daraus macht. Die Felder bleiben öffentlich
und lesbar; für Schleifen und Rechnungen ist der direkte Zugriff schneller
und völlig in Ordnung.
**Der Unterschied:** ''ctl.Left := 20;'' verschiebt das Control, ohne neu
zu zeichnen. ''ctl.SetLeft(20)'' zeichnet neu.
Gebündelt im Interface ''IControlProps''. Das zahlt sich zweimal aus: beim
Formatieren fremder Controls und beim LFM-Format, wo eine einzige Funktion
''SetCommonProp'' **jedes** Control bedient, ohne dessen Klasse zu kennen.
===== Layout =====
* **''Align''** — ''alTop'', ''alBottom'', ''alLeft'', ''alRight'',
''alClient''. Das Control bekommt die ganze Kante.
* **''Anchors''** — Bitmaske aus ''ANCHOR_LEFT/TOP/RIGHT/BOTTOM''.
Bestimmt, was bei einer Größenänderung des Elternteils passiert.
* **Größengrenzen** — ''MinWidth'', ''MaxWidth'', ''MinHeight'',
''MaxHeight''.
Die Ankerrechnung merkt sich die vorige Größe am **Elternteil**
(''_layoutW'', ''_layoutH'') statt vier Werte je Kind — ein Feld pro
Container gegen Delphis vier pro Control.
Ohne Anker gilt eine Regel, die überrascht und trotzdem richtig ist: das
Control **wandert halb mit**. Sonst klebte ein mittig gedachtes Control
beim Vergrößern oben links fest.