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
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.
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.
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.
OnDragOver und OnDragDrop unterscheiden über sender und source — im Beispiel bekommen beide Listen denselben Satz, und source == sender verhindert das Ablegen auf sich selbst.OnEndDrag, nicht in OnDragDrop — nur OnEndDrag läuft auch beim Abbruch.Cursor im OnDragOver des Ziels.DragMode vergessen. Bei DM_MANUAL passiert ohne BeginDrag schlicht nichts.OnDragOver gibt nichts zurück. Dann nimmt niemand etwas an, und OnDragDrop feuert nie.OnDragDrop aufgeräumt. Bricht der Benutzer ab, bleibt der gemerkte Eintrag stehen und stört die nächste Geste.target nicht geprüft. In OnEndDrag ist 0 der 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.