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