====== Vega — Layout in der Praxis ====== Vega kennt keinen Layoutmanager mit Zwängen und Gewichten. Es gibt zwei Mittel, und beide sind einfach genug, um sie im Kopf zu behalten: ''Align'' verteilt **Fläche**, ''Anchors'' verteilt **Zuwachs**. Alles Weitere ergibt sich aus der Reihenfolge, in der Kinder angelegt werden. → [[lyx_-_programmiersprache:vega:guides|Vega-Guides]] · [[lyx_-_programmiersprache:vega:guides:eigenschaften|Eigenschaften]] · [[lyx_-_programmiersprache:vega:praxis|Anwendungen aufbauen]] ---- ===== Die Reihenfolge ist das Layout ===== ''ApplyLayout'' geht die Kinder **der Reihe nach** durch. Jedes ausgerichtete Kind schneidet sich seinen Streifen vom verbleibenden Rest ab; was übrig bleibt, bekommt das ''AL_CLIENT''-Kind. +--------------------------------+ | kopf AL_TOP | +--------+-+---------------------+ | links |t| mitte | | AL_LEFT|e| AL_CLIENT | | |i| | | |l| | +--------+-+---------------------+ | fuss AL_BOTTOM | +--------------------------------+ Daraus folgen drei Regeln: * **Ränder zuerst, ''AL_CLIENT'' zuletzt.** Wer den Editor als erstes Kind anlegt, gibt ihm die ganze Fläche — die Leisten danach bekommen nichts mehr. * **Zwei ''AL_CLIENT''-Kinder sind einer zu viel.** Das zweite bekommt, was das erste übrig lässt: nichts. * **Ein Streifen braucht seine Dicke.** Bei ''AL_TOP''/''AL_BOTTOM'' zählt ''Height'', bei ''AL_LEFT''/''AL_RIGHT'' zählt ''Width''; die jeweils andere Angabe wird überschrieben. ---- ===== ApplyLayout wirkt eine Ebene tief ===== ''ApplyLayout'' setzt ''Left''/''Top''/''Width''/''Height'' der **eigenen** Kinder direkt — nicht über ''SetBounds''. Ein Container erfährt davon also nichts und richtet seine eigenen Kinder nicht aus. Wer verschachtelt, zieht das im ''OnResize'' des Fensters nach: fn OnResize(sender: TControl): void { if (g_mitte != null) { g_mitte.ApplyLayout(); } g_form.Invalidate(); } Und einmal von Hand nach ''ShowForm'', damit schon das erste Bild stimmt. ---- ===== Anker ===== ''Anchors'' entscheidet, was beim Vergrößern mitwandert. Es ist eine Bitmaske: ^ Konstante ^ Wert ^ Wirkung ^ | ''ANCHOR_LEFT'' | 1 | Abstand zum linken Rand bleibt | | ''ANCHOR_TOP'' | 2 | Abstand nach oben bleibt | | ''ANCHOR_RIGHT'' | 4 | Abstand zum rechten Rand bleibt | | ''ANCHOR_BOTTOM'' | 8 | Abstand nach unten bleibt | Vorgabe ist ''ANCHOR_LEFT | ANCHOR_TOP'' — das Element klebt oben links. Zwei gegenüberliegende Anker bedeuten **mitwachsen**: ''ANCHOR_LEFT | ANCHOR_RIGHT'' zieht die Breite mit dem Fenster. Anker und ''Align'' schließen sich aus: Ein ausgerichtetes Kind bekommt seine Geometrie ohnehin vom Layout. ---- ===== Splitter und ScrollBox ===== * ''NewSplitter(AL_LEFT)'' legt einen Ziehbalken an, der die **davor** angelegte Spalte greifbar macht. ''MinSize'' verhindert, dass sie ganz zugezogen wird. Der Splitter selbst ist ein ausgerichtetes Kind und gehört unmittelbar hinter das, was er greifen soll. * ''NewScrollBox()'' mit ''AutoScroll := true'' zeigt Bildlaufleisten, sobald der Inhalt größer ist als die Fläche. Kinder darin behalten ihre Koordinaten; gescrollt wird der Ausschnitt. ---- ===== Vollständiges Beispiel ===== Kopfzeile, Fußzeile mit rechts geankertem Knopf, linke Spalte mit Splitter, verschachtelter Container mit ScrollBox: unit Main; import std.io; import Vega.Types; import Vega.Events; import Vega.Canvas; import Vega.Theme; import Vega.Control; import Vega.Font; import Vega.Strings; import Vega.Lists; import Vega.Style; import Vega.StdCtrls; import Vega.Layout; import Vega.Backend; import Vega.BackendAuto; import Vega.Forms; var g_app: TApplication; var g_form: TForm; var g_mitte: TPanel; fn OnBeenden(sender: TControl): void { g_app.Terminate(); } // ApplyLayout wirkt nur eine Ebene tief: die Kinder des Fensters werden // ausgerichtet, deren eigene Kinder nicht. Verschachtelte Container ziehen // ihr Layout deshalb hier nach. fn OnResize(sender: TControl): void { if (g_mitte != null) { g_mitte.ApplyLayout(); } g_form.Invalidate(); } fn main(): int64 { g_app := new TApplication(NewPlatformBackend()); if (!g_app.Ready) { PrintLn("Kein Fenstersystem erreichbar."); return 1; } g_form := new TForm(560, 360, "Layout"); g_form.OnResize := OnResize; // 1. Kopfzeile var kopf: TPanel := new TPanel(); kopf.Height := 32; kopf.Align := AL_TOP; kopf.SetParent(g_form); // 2. Fusszeile mit einem Knopf, der rechts unten klebt var fuss: TPanel := new TPanel(); fuss.Height := 40; fuss.Align := AL_BOTTOM; fuss.SetParent(g_form); var ende: TButton := new TButton("Beenden"); ende.SetBounds(430, 6, 120, 28); ende.Anchors := ANCHOR_RIGHT | ANCHOR_BOTTOM; ende.OnClick := OnBeenden; ende.SetParent(fuss); // 3. Linke Spalte, danach der Splitter, der sie greifbar macht var links: TPanel := new TPanel(); links.Width := 160; links.Align := AL_LEFT; links.SetParent(g_form); var teiler: TSplitter := NewSplitter(AL_LEFT); teiler.MinSize := 80; teiler.SetParent(g_form); // 4. Rest: ein Container, der seine eigenen Kinder ausrichtet g_mitte := new TPanel(); g_mitte.Align := AL_CLIENT; g_mitte.SetParent(g_form); var oben: TLabel := new TLabel("obere Haelfte"); oben.Height := 24; oben.Align := AL_TOP; oben.SetParent(g_mitte); var rest: TScrollBox := NewScrollBox(); rest.Align := AL_CLIENT; rest.AutoScroll := true; rest.SetParent(g_mitte); // Inhalt, der breiter ist als die Flaeche — dafuer die ScrollBox var breit: TLabel := new TLabel("ein sehr breiter Inhalt, der scrollen muss"); breit.SetBounds(8, 8, 900, 20); breit.SetParent(rest); if (!g_app.ShowForm(g_form)) { return 2; } g_form.ApplyLayout(); OnResize(g_form); g_app.Run(); g_app.Shutdown(); return 0; } Übersetzen: ''lyxc layout.lyx -I . -I platform/linux -o layout'' aus der Wurzel von ''lyx-vcl''. Nachgemessen mit ''lyxc 1.1.3I'', übersetzt fehlerfrei. ---- ===== Wenn das Layout nicht stimmt ===== ^ Symptom ^ Ursache ^ | Ein Streifen ist unsichtbar | Er wurde **nach** dem ''AL_CLIENT''-Kind angelegt | | Der Streifen hat die falsche Dicke | Bei ''AL_TOP''/''AL_BOTTOM'' zählt ''Height'', nicht ''Width'' (und umgekehrt) | | Kinder eines Containers liegen übereinander | Sein ''ApplyLayout'' wird nie gerufen | | Beim ersten Bild sitzt alles falsch, nach dem ersten Ziehen richtig | ''ApplyLayout'' nach ''ShowForm'' fehlt | | Ein Knopf wandert beim Vergrößern nicht mit | ''Anchors'' steht auf der Vorgabe (links/oben) | Letzte Aktualisierung: 2026-08-19 — Beispiel mit ''lyxc 1.1.3I'' übersetzt; Splitter/ScrollBox gegen ''Vega/Layout.lyx'' abgeglichen.