====== 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 | → [[lyx_-_programmiersprache:vega:guides|Vega-Guides]] · [[lyx_-_programmiersprache:vega:guides:events|Ereignisse]] · [[lyx_-_programmiersprache:vega:guides:listen|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.** ''OnDragOver'' und ''OnDragDrop'' unterscheiden über ''sender'' und ''source'' — im Beispiel bekommen beide Listen denselben Satz, und ''source == sender'' verhindert 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 in ''OnDragDrop'' — nur ''OnEndDrag'' läuft auch beim Abbruch. * **Anzeige während der Geste** — wer den Zeiger ändern will, setzt ''Cursor'' im ''OnDragOver'' des Ziels. ---- ===== Fallstricke ===== * **''DragMode'' vergessen.** Bei ''DM_MANUAL'' passiert ohne ''BeginDrag'' schlicht nichts. * **''OnDragOver'' gibt nichts zurück.** Dann nimmt niemand etwas an, und ''OnDragDrop'' feuert nie. * **Zustand nur in ''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.