====== 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.