Drag and Drop
Definition
Unter Drag and Drop versteht man das Verschieben von Informationen per Maus. Von welcher Art die Information ist, das ist erst einmal zweitrangig.
Das Programm muss eine sogenannte Drop-Zone definieren (bereitstellen), und auf die entsprechenden Events reagieren.
Definition einer Drop-Zone
Fast jedes Xbase-Part kennt die Instanz-Variable :dropZone. Sie steht standardmässig auf FALSE, d.h. das jeweilige Element ignoriert eine Drag and Drop Operation:
oXbp := XbpPushButton():new(oDlg:drawingArea, oDlg:drawingArea, aPos, aSize) oXbp:dropZone := .T.
Darüber hinaus ist es (meistens) erforderlich, zumindest einen Callback-Slot zu definieren, der im Fall des Drop ausgeführt wird:
oXbp:dragDrop := {|aState, oDragData, self| HandleDropData(oDragData, aState)}
Für Text-Controls wie XbpSLE() und XbpMLE() kann auf diese Zuweisung normalerweise verzichtet werden, denn vom Format kompatible Informationen werden bei diesen Xbase-Parts direkt eingetragen. Wenn also z.B. eine Datei aus dem Explorer auf ein XbpSLE() gezogen wird, wird im XbpSLE() der Dateiname mit dem kompletten Pfad eingetragen.
Es gibt insgesamt vier Callback-Slots, die im Zusammenhang mit Drag and Drop stehen:
:dragEnter :dragMotion :dragLeave :dragDrop
:dragEnter
Der :dragEnter Callback-Slot wird ausgeführt, wenn die Maus während einer Drag-Operation auf das entsprechende Xbase-Part gezogen wird.
Auf diese Weise kann geprüft werden, ob die Information für das Program "akzeptabel" ist:
oXbp:dragEnter := {|aState, oDragData| CheckDropData(aState, oDragData)} ... FUNCTION CheckDropData(aState, oDrag) IF oDrag:QueryGetFormat(XBPCLPBRD_FILELIST) == .T. RETURN XBP_DROPMODE_COPY ENDIF RETURN(XBP_DROPMODE_NONE)
In diesem Beispiel wird nur ein Liste von Dateien (auch eine einzelne Datei stellt eine Liste mit einem Eintrag dar) akzeptiert. Wenn etwas anderes "herübergezogen" wird, gibt die Funktion CheckDropData() eine Information zurück, die dazu führt, dass sich das Maussymbol so ändert, dass der Anwender erkennen kann, dass eine Drop-Operation HIER nicht zulässig ist.
Return-Wert | Bedeutung | Auswirkung | Hinweis |
---|---|---|---|
XBP_DROPMODE_NONE | Anwendung kann mit dem Datentyp nicht umgehen | Cursor zeigt "no drop" Symbol | - |
XBP_DROPMODE_COPY | Anwendung nimmt die Daten als Kopie entgegen | Cursor zeigt "drop possible" Symbol | Standard-Verhalten |
XBP_DROPMODE_MOVE | Die Datenquelle soll die Daten nach dem Drop entfernen | Cursor zeigt "drop possible" Symbol | - |
XBP_DROPMODE_LINK | Die Datenquelle soll einen Link zu den Daten bereitstellen | Cursor zeigt "drop possible" Symbol | - |
:dragMotion
Der :dragMotion Callback-Slot wird ausgeführt, wenn die Maus innerhalb der DropZone bewegt wird, oder eine Taste gedrückt wird.
aState => {nKeyState, aPos} aPos => {X-Pos Maus, Y-Pos Maus)
Dann kann das Programm wiederum prüfen, ob die Drag and Drop Operation zulässig ist oder nicht. Es gelten die gleichen Rückgabewerte wie bei :dragEnter.
:dragLeave
Der :dragLeave Callback-Slot wird ausgeführt, wenn die Maus aus dem Bereich der DropZone herausgeführt wird. Falls das Xbase-Programm irgendwelche Vorbereitungen für den Drop getroffen hat, können diese nun freigegeben werden.
Abweichend von den anderen Callback-Slots sind hier die beiden Parameter NIL.
:dragDrop
Der :dragDrop Callback-Slot wird ausgeführt, wenn die Daten im Bereich der DropZone durch Loslassen der Maustaste "gedropt" werden.
Analog zu :dragMotion enthält der Parameter aState Informationen zur gedrückten Taste und der Position des Maus-Cursors zum Zeitpunkt des Drop:
FUNCTION HandleDropData(oData, aState) Local aData aData := oData:getData(XBPCLPBRD_FILELIST) IF aData == NIL ConfirmBox(, "Falsches Format.", "Fehler", XBPMB_OK, XBPMB_CRITICAL) RETU(.F.) ENDIF
Ein Xbase-Programm kann nicht jedes Datenformat entgegennehmen, das unter Windows möglich ist. Wenn die Daten nicht im angeforderten Format vorliegen (in diesem Beispiel als XBPCLPBRD_FILELIST), liefert die Methode :getData() NIL als Ergebnis zurück. NIL signaliert also nicht "keine Daten", sondern "keine Daten in diesem Format".
Neben dem obigen Beispie, die Daten direkt entgegenzunehmen, besteht auch die Möglichkeit, das Format abzufragen: oData ist ein Objekt der DragDataObject() Klasse, und über die Methode :QueryGetFormat() kann geprüft werden, ob die Daten in einem bestimmten Format vorliegen:
lFormat := oData:QueryGetFormat(XBPCLPBRD_FILELIST)
Folgende Abfragen sind möglich:
Format-Typ | Bedeutung |
---|---|
XBPCLPBRD_TEXT | Daten liegen im ASCII-Format als Text vor |
XBPCLPBRD_BITMAP | graphische Daten liegen im Bitmap-Format vor |
XBPCLPBRD_METAFILE | graphische Daten liegen im Metafile-Format vor |
XBPCLPBRD_FILELIST | Die Daten werden als Liste von Dateinamen vom Windows-Explorer übergeben |
nLen := Len(aData) cTitle := RootWindow():getTitle() RootWindow():setTitle(cTitle + " - processing input") nColor := RootWindow():drawingArea:setColorBG(GRA_CLR_CYAN) FOR nI := 1 TO nLen PushData(aData[nI]) NEXT RootWindow():drawingArea:setColorBG(nColor) RootWindow():setTitle(cTitle)
In diesem Beispiel ist die :drawingArea des Programmfenster als DropZone definiert. Wenn ein passender Wert (hier eine Datei-Liste) per Drag and Drop übergeben wird, wird der Titel des Programms geändert, und die Hintergrundfarbe der :drawingArea verändert, um anzuzeigen, dass ein besonderer Prozess abläuft. Dann werden die übergebenen Dateien der Reihe nach in der Funktion PushData() verarbeitet. Zum Abschluss wird der ursprüngliche Titel des Programms wiederhergestellt, und auch die Farbe der :drawingArea zurückgeändert.
Besonderheiten
Wenn z.B. ein Link aus dem Browser auf eine DropZone gezogen wird, bleibt der Browser so lange "gesperrt" (d.h. reagiert nicht auf Eingaben), wie das Xbase-Programm im Rahmen der Drop-Operation tätig ist. Erst mit Aufnahme des Event-Loops kann wieder mit dem Browser gearbeitet werden.
Um auf das vorhergehende Beispiel zurückzukommen: wenn während der Verarbeitung der Drag and Drop-Operation das Programm weiterhin genutzt werden soll, besteht die Möglichkeit, die Drag and Drop-Verarbeitung über einen Thread zu starten und dann wieder in den Event-Loop zurückzukehren.