Vega — Ziehen und Ablegen
Eine Ziehgeste läuft über vier Ereignisse, verteilt auf zwei Beteiligte: das Control, an dem sie beginnt (die Quelle), und das, über dem sie endet (das Ziel).
| Ereignis | Wer bekommt es | Signatur | Aufgabe |
|---|---|---|---|
OnStartDrag | Quelle | fn(sender): void | Merken, was gemeint ist |
OnDragOver | mögliches Ziel | fn(sender, source, x, y): bool | true = „ich nehme das an„ |
OnDragDrop | Ziel | fn(sender, source, x, y): void | Übernehmen |
OnEndDrag | Quelle | fn(sender, target, x, y): void | Aufräumen; target ist 0 bei Abbruch |
→ Vega-Guides · Ereignisse · Listen
Die Geste beginnt
DragMode entscheidet, wie:
| Wert | Bedeutung |
|---|---|
DM_MANUAL (0, Vorgabe) | Nur, wenn die Anwendung BeginDrag ruft |
DM_AUTOMATIC (1) | Von selbst, sobald der Zeiger mit gedrückter Taste weit genug wandert |
Die Vorgabe ist bewusst manuell — wie in Delphi. Sonst zöge man beim Markieren in einem Eingabefeld versehentlich den Text heraus.
Die Geste trägt keine Nutzlast
Vega reicht bei OnDragDrop nur source und die Koordinaten weiter, keinen Gegenstand. Was unterwegs ist, merkt sich die Anwendung selbst — im einfachsten Fall in einer Modulvariablen, die OnStartDrag füllt und OnEndDrag wieder leert.
Das ist ehrlicher als eine typlose Nutzlast: Quelle und Ziel wissen ohnehin, worum es geht, und der Typ bleibt der der Anwendung.
OnDragOver liefert seine Antwort als Rückgabewert — in Delphi ist das ein var-Parameter. Ein Handler, der nichts zurückgibt, lehnt damit ab, statt eine Vorbelegung stehen zu lassen, die niemand gesetzt hat.
Beispiel
Zwei Listen, zwischen denen Einträge wandern:
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.ListCtrls;
import Vega.Backend;
import Vega.BackendAuto;
import Vega.Forms;
var g_app: TApplication;
var g_links: TListBox;
var g_rechts: TListBox;
var g_status: TLabel;
// Was gerade unterwegs ist. Die Geste selbst traegt keine Nutzlast —
// wer etwas mitgeben will, merkt es sich hier.
var g_unterwegs: TTextBuffer;
fn OnBeenden(sender: TControl): void { g_app.Terminate(); }
// 1. Beginn: merken, was gemeint ist
fn OnStart(sender: TControl): void {
var lb: TListBox := sender as TListBox;
if (lb.ItemIndex < 0) { return; }
var t: pchar := lb.Items.Get(lb.ItemIndex);
g_unterwegs.SetText(t);
g_status.SetCaption(t);
}
// 2. Ueberfahren: nimmt DIESES Control den Gegenstand an?
fn OnOver(sender: TControl, source: TControl, x: int64, y: int64): bool {
if (source == sender) { return false; } // nicht auf sich selbst
return g_unterwegs.Length > 0;
}
// 3. Ablegen: uebernehmen und beim Absender entfernen
fn OnDrop(sender: TControl, source: TControl, x: int64, y: int64): void {
var ziel: TListBox := sender as TListBox;
var von: TListBox := source as TListBox;
if (g_unterwegs.Length == 0) { return; }
var t: pchar := g_unterwegs.AsPChar();
ziel.Add(t);
if (von.ItemIndex >= 0) { von.Delete(von.ItemIndex); }
g_status.SetCaption("verschoben");
g_unterwegs.Clear();
}
// 4. Ende: target ist 0, wenn nirgends abgelegt wurde
fn OnEnd(sender: TControl, target: TControl, x: int64, y: int64): void {
if (target == 0) { g_status.SetCaption("abgebrochen"); }
g_unterwegs.Clear();
}
fn Verbinde(lb: TListBox): void {
lb.DragMode := DM_AUTOMATIC; // Geste beginnt von selbst
lb.OnStartDrag := OnStart;
lb.OnDragOver := OnOver;
lb.OnDragDrop := OnDrop;
lb.OnEndDrag := OnEnd;
}
fn main(): int64 {
g_app := new TApplication(NewPlatformBackend());
if (!g_app.Ready) { PrintLn("Kein Fenstersystem erreichbar."); return 1; }
g_unterwegs := new TTextBuffer();
var f: TForm := new TForm(480, 300, "Ziehen und Ablegen");
g_status := new TLabel("bereit");
g_status.SetBounds(16, 12, 440, 18);
g_status.SetParent(f);
g_links := new TListBox();
g_links.SetBounds(16, 40, 200, 200);
g_links.Add("Bass");
g_links.Add("Snare");
g_links.Add("Hihat");
Verbinde(g_links);
g_links.SetParent(f);
g_rechts := new TListBox();
g_rechts.SetBounds(256, 40, 200, 200);
Verbinde(g_rechts);
g_rechts.SetParent(f);
var b: TButton := new TButton("Beenden");
b.SetBounds(336, 252, 120, 28);
b.OnClick := OnBeenden;
b.SetParent(f);
if (!g_app.ShowForm(f)) { return 2; }
g_app.Run();
g_app.Shutdown();
return 0;
}
Übersetzen: lyxc dragdrop.lyx -I . -I platform/linux -o dragdrop. Nachgemessen mit lyxc 1.1.3I, übersetzt fehlerfrei.
Muster
- Ein Handlersatz für viele Controls.
OnDragOverundOnDragDropunterscheiden übersenderundsource— im Beispiel bekommen beide Listen denselben Satz, undsource == senderverhindert das Ablegen auf sich selbst. - Erst im Ziel entfernen, dann beim Absender. So bleibt bei einem Fehler in der Mitte nichts verloren.
- Aufräumen gehört in
OnEndDrag, nicht inOnDragDrop— nurOnEndDragläuft auch beim Abbruch. - Anzeige während der Geste — wer den Zeiger ändern will, setzt
CursorimOnDragOverdes Ziels.
Fallstricke
DragModevergessen. BeiDM_MANUALpassiert ohneBeginDragschlicht nichts.OnDragOvergibt nichts zurück. Dann nimmt niemand etwas an, undOnDragDropfeuert nie.- Zustand nur in
OnDragDropaufgeräumt. Bricht der Benutzer ab, bleibt der gemerkte Eintrag stehen und stört die nächste Geste. targetnicht geprüft. InOnEndDragist0der Normalfall „daneben losgelassen“.
Letzte Aktualisierung: 2026-08-19 — Ereignisse und DragMode gegen Vega/Control.lyx und Vega/Types.lyx erhoben; Beispiel mit lyxc 1.1.3I übersetzt.
