Vega: Anwendungen aufbauen
Die Seite beschreibt die Muster, nach denen eine größere Vega-Anwendung entsteht — Layout, eigene Steuerelemente, Zeitgeber, Dialoge. Sie ergänzt Steuerelemente (was es gibt) um die Frage, wie man es zusammensetzt.
→ Vega VCL · Architektur · Lyx-Eigenheiten
Die sechs Regeln
- Die Reihenfolge der Kinder ist das Layout.
ApplyLayoutarbeitet die Kinder der Reihe nach ab; jedes ausgerichtete Kind nimmt sich seinen Rand vom Rest. Alle Ränder (AL_TOP,AL_BOTTOM,AL_LEFT) müssen deshalb vor demAL_CLIENT-Kind angelegt werden — sonst bekommt der Rand nichts mehr ab. ApplyLayoutwirkt nur eine Ebene tief. Es setztLeft/Top/Width/Heightder Kinder direkt, nicht überSetBounds— ein verschachtelter Container erfährt davon nichts. Wer Container in Containern hat, ruft derenApplyLayoutimOnResizedes Fensters selbst nach.- Einmal von Hand ausrichten. Nach
ShowFormfolgt einApplyLayout, damit schon das erste Bild stimmt und nicht erst das nach der ersten Größenänderung. - Modulvariablen stehen alle oben. Bis lyxc 1.1.3D zerfiel eine Modulvariable, die vor ihrer Deklaration benutzt wurde, still in zwei (#1665). Die Gewohnheit bleibt sinnvoll: sie macht den Zustand eines Fensters an einer Stelle sichtbar.
- Das Backend kommt aus dem Include-Pfad, nicht aus einer Fallunterscheidung.
Vega.BackendAutoliegt je einmal unterplatform/linuxundplatform/win64;extern-Deklarationen binden hart, auch ungenutzt. Deshalb entscheidet der Übersetzungsaufruf, nicht der Quelltext. - Der Fokus wird gesetzt, nicht geerbt. Ohne
Dispatcher.SetFocuslaufen Tastendrücke ins Leere, bis jemand hineinklickt.
Gerüst
Das folgende Programm zeigt alle Bausteine auf engstem Raum: eigenes Steuerelement mit Paint, Randleiste plus AL_CLIENT, Knopf mit Handler und ein Zeitgeber, der die Anzeige fortschreibt.
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.Backend;
import Vega.BackendAuto;
import Vega.Forms;
// Modulvariablen stehen ALLE oben — eine Variable, die erst nach ihrer
// ersten Verwendung deklariert wird, zerfiel bis lyxc 1.1.3D still in zwei.
var g_app: TApplication;
var g_form: TForm;
var g_anzeige: TAnzeige;
var g_zaehler: int64;
var g_timer: TTimer;
// Eigenes Steuerelement: von TControl ableiten und Paint ueberschreiben.
pub type TAnzeige = class extends TControl {
Wert: int64;
fn Create(): void {
self.Init();
self.Wert := 0;
self.Height := 40;
}
override fn Paint(canvas: TCanvas): void {
var r: TRect := self.ClientRect();
canvas.FillRect(r, RGB(240, 240, 240));
var f: TFont := DefaultFont();
f.DrawText(canvas, 8, 10, IntToStr(self.Wert), RGB(0, 0, 0));
}
}
fn OnTick(sender: TTimer): void {
g_zaehler := g_zaehler + 1;
g_anzeige.Wert := g_zaehler;
g_anzeige.Invalidate();
}
fn OnEnde(sender: TControl): void {
g_app.Terminate();
}
fn main(): int64 {
g_app := new TApplication(NewPlatformBackend());
if (!g_app.Ready) { PrintLn("Kein Fenstersystem erreichbar."); return 1; }
g_form := NewForm(320, 200, "Praxis");
// Reihenfolge IST das Layout: Raender zuerst, AL_CLIENT zuletzt.
var leiste: TControl := new TControl();
leiste.Height := 32;
leiste.Align := AL_BOTTOM;
g_form.AddChild(leiste);
var b: TButton := new TButton("Schliessen");
b.SetBounds(8, 4, 120, 24);
b.OnClick := OnEnde;
leiste.AddChild(b);
g_anzeige := new TAnzeige();
g_anzeige.Align := AL_CLIENT;
g_form.AddChild(g_anzeige);
if (!g_app.ShowForm(g_form)) { return 2; }
g_form.ApplyLayout();
g_timer := NewTimer(500);
g_timer.OnTimer := OnTick;
g_timer.Start();
g_app.AddTimer(g_timer);
g_app.Run();
g_app.Shutdown();
return 0;
}
Übersetzen aus der Wurzel des lyx-vcl-Repositorys:
lyxc praxis.lyx -I . -I platform/linux -o praxis
Nachgemessen mit lyxc 1.1.3I: übersetzt fehlerfrei, rund 770 KB.
Eigene Steuerelemente
Ein eigenes Element leitet von TControl ab, ruft im Konstruktor self.Init() und überschreibt Paint:
self.ClientRect()liefert die Fläche ohne Rahmen.- Gezeichnet wird über
TCanvas(FillRect) undTFont(DrawText,TextWidth,TextHeight). self.Hoveredundself.Pressedstehen für Zustandsdarstellung bereit.self.CanFocusentscheidet, ob das Element Tastatureingaben bekommt.- Nach einer Zustandsänderung
Invalidate()rufen — Vega zeichnet nicht von selbst neu.
Für Elemente, deren Inhalt an Zeichenpositionen ausgerichtet sein soll (Tabellen, Editoren), ist eine selbst gezeichnete Fläche der ruhigere Weg als ein Container voller Labels: Pixelabstände wandern bei jeder Textänderung, Zeichenpositionen nicht.
Zeitgeber
g_timer := NewTimer(20); // Millisekunden
g_timer.OnTimer := OnTick;
g_timer.Start();
g_app.AddTimer(g_timer); // ohne AddTimer feuert er nie
Ein Zeitgeber läuft durch; ob sein Handler etwas tut, entscheidet der Handler. Das ist einfacher zu überblicken als ein Timer, der ständig an- und abgemeldet wird.
Dialoge
ShowModal braucht die Anwendung. Wer Dialoge in einer eigenen Unit hält, muss ihr die TApplication bekannt machen — sonst steigt jede Dialogaktion still wieder aus:
g_app := app; // vor dem ersten Dialogaufruf
Menüs
Die Menüleiste liegt als Kind auf 0/0 und nimmt am Layout nicht teil. Zwei Folgen:
- Ein Platzhalter in Höhe von
f.MenuHeight()als erstes Kind verhindert, dass sich die erste Zeile darunterlegt. SetMenusetzt die Breite einmalig aus der damaligen Fensterbreite; imOnResizegehörtf.Menu.Width := f.Widthnachgezogen.
Letzte Aktualisierung: 2026-08-19 — Seite neu geschrieben; Muster aus einer bestehenden Vega-Anwendung abgeleitet, Beispielprogramm mit lyxc 1.1.3I übersetzt.
