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

  1. Die Reihenfolge der Kinder ist das Layout. ApplyLayout arbeitet 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 dem AL_CLIENT-Kind angelegt werden — sonst bekommt der Rand nichts mehr ab.
  2. ApplyLayout wirkt nur eine Ebene tief. Es setzt Left/Top/Width/Height der Kinder direkt, nicht über SetBounds — ein verschachtelter Container erfährt davon nichts. Wer Container in Containern hat, ruft deren ApplyLayout im OnResize des Fensters selbst nach.
  3. Einmal von Hand ausrichten. Nach ShowForm folgt ein ApplyLayout, damit schon das erste Bild stimmt und nicht erst das nach der ersten Größenänderung.
  4. 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.
  5. Das Backend kommt aus dem Include-Pfad, nicht aus einer Fallunterscheidung. Vega.BackendAuto liegt je einmal unter platform/linux und platform/win64; extern-Deklarationen binden hart, auch ungenutzt. Deshalb entscheidet der Übersetzungsaufruf, nicht der Quelltext.
  6. Der Fokus wird gesetzt, nicht geerbt. Ohne Dispatcher.SetFocus laufen 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) und TFont (DrawText, TextWidth, TextHeight).
  • self.Hovered und self.Pressed stehen für Zustandsdarstellung bereit.
  • self.CanFocus entscheidet, 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


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.
  • SetMenu setzt die Breite einmalig aus der damaligen Fensterbreite; im OnResize gehört f.Menu.Width := f.Width nachgezogen.

Letzte Aktualisierung: 2026-08-19 — Seite neu geschrieben; Muster aus einer bestehenden Vega-Anwendung abgeleitet, Beispielprogramm mit lyxc 1.1.3I übersetzt.