Lade...
 

Erläuterungen zu Drag und Drop

Erläuterungen zu Drag und Drop

Drag und Drop bietet die Möglichkeit, Einträge und vor allem Relationen der Windowobjekte auf der Oberfläche interaktiv zu verändern, ohne explizit InstantView®-Code auszuführen.
In der Wirkung gleicht eine Drag und Drop Aktion der Ausführung von OboxFill, wobei die Selektion automatisch erfolgt und beim loslassen (Drop) gleich dem Objekt unter dem Maus-Cursor ist.

Eine Drag und Drop Aktion wird gestartet (Drag), indem auf einem Objekt, d.h. auf einem Widget, das ein (-en Teil eines)  Objekt (-es) darstellt oder auf einem Element zu einem Objekt innerhalb einer ObjectBox, die linke Maustaste gedrückt wird und gedrückt gehalten wird. Danach kann man bei gedrückt gehaltener linken Maustaste den Maus-Cursor über den Bildschirm bewegen, was durch eine geänderte Form des Cursors visualisiert wird. Beendet wird die Aktion (Drop), indem irgendwo auf dem Bildschirm die linke Maustaste losgelassen wird. Hatte der Maus-Cursor zu diesem Zeitpunkt nicht die Form Cancel (s.u.), war das unter dem Cursor befindliche Objekt für die Aktion zulässig und die an das Drag und Drop geknüpfte Aktion erfolgt.

Diese 3 beschriebenen Stadien von Drag und Drop: Beginn, Bewegung, Ende, sind essentiell für den ganzen Vorgang. Sie können im einzelnen beeinflusst werden, d.h. es gibt bestimmte Nachrichten (Events), die vom System während eines Stadiums ausgesandt werden. Je nachdem, wie auf diese durch eine InstantView®-Implementation reagiert wird, erfolgt eine bestimmtes Verhalten des Systems:

Beginn(Drag):
Hier erfolgt zunächst nur eine Visualisierung des Drag und Drop durch das System: Der Maus-Cursor nimmt die Form eines Drag und Drop Cursors an, wie sie auch im Bewegungs-Stadium gesetzt werden (siehe dort). Außerdem wird bei einigen ObjectBoxen ein Bild erzeugt, das das gedraggte Objekt darstellt und sich mit dem Maus-Cursor mitbewegt.

Bewegung:
In diesem Stadium muss das System entscheiden, ob ein sofortiges Loslassen (Drop) zu einem zulässigen Ergebnis führt. Dies geschieht auf zweierlei Weise: Zunächst ergeben sich aus logischen Gründen Konstellationen, die sich für ein erfolgreiches Drop verbieten. So muss sich natürlich unterhalb des Maus-Cursors überhaupt ein Widget befinden, das das gedraggte Objekt darstellen kann. Ein Drop auf ein leeres Window ist also immer unzulässig. Außerdem gibt es strukturelle Gründe, die in einzelnen Fällen bei einem erfolgreichen Drop zu absurden Ergebnissen führen würde. So verbietet der ObjectTree automatisch, dass ein Objekt in eines seiner untergeordneten Objekte gedroppt wird. So eine Aktion hätte ansonsten eine Endlosrekursion an Einfüllungen zur Folge.

Als zweite Möglichkeit kann man im InstantView®-Code entscheiden, ob die Drop-Aktionen, die aus rein logischen Gründen korrekt scheinen, auch aus semantischen Gründen korrekt oder auch nur erwünscht sind. Hierzu sendet das System an einige Widgets genau dann die folgende Nachricht, wenn die beteiligten Partner (Quell-(Drag-)Objekt, Ziel-(Drop-)Objekt, Einfügemodus) sich geändert haben. Der Syntax lautet:

 

IS_DROPPABLE:

Stack
Stack Position Beschreibung
Stack(In) Top ]
  Top-1 Quell-Objekt On (Drag)
  ... ...
  Top-n Quell-Objekt O1 (Drag)
  Top-n-1 [
  Top-n-2 Ziel-Objekt (Drop) oder NULL
  Top-n-3 Einfügemodus (Integer, s.u.)
Stack(Out)   0 oder 1

Nur bei Widgets mit Mehrfachselektionsmöglichkeit (ObjectList,ObjectListView) als Quell-Widget können mehrere Quell-Objekte übergeben werden. Es wird jedoch grundsätzlich eine Liste (d.h. die Marken '[' und ']') übergeben. Als Zielobjekt kommt immer nur ein Objekt in Frage. (Objekt unter dem Cursor wird gewählt, nicht etwa die selektierten!) Der Einfügemodus ist eine Zahl deren Bedeutung unten erläutert wird.

Will man das Drag und Drop Verhalten beeinflussen, definiert man also in gleicher Weise wie bei allen anderen System-Events einen Label IS_DROPPABLE mit folgendem InstantView®-Code, der Aufgrund der auf dem Stack vorgefundenen Parametern entscheidet, ob, z.B., das Quell-Objekt in eine Collection des Ziel-Objektes passt, also ein geeignetes untergeordnetes Objekt wäre. Ist dies der Fall, sollte der InstantView®-Code, der IS_DROPPABLE implementiert nach Ausführung den Wert 1 auf dem Stack (als Rückgabewert) zurücklassen, andernfalls entsprechend den Wert 0.

Den bis hierher schon mehrfach erwähnten Einfügemodi in Zahlenwerten und Cursor-Formen (können sich bei in Windows individuell geänderten Cursorn ändern) lauten wie folgt:

Cursor Name Modus Beschreibung
DragCopM.gif (950 Byte) Cancel 1 Mit diesem Wert würde IS_DROPPABLE erst gar nicht aufgerufen. (Drop bereits logisch nicht möglich.)
DragCopM.gif (950 Byte) Move 0 Ein Objekt wird verschoben, d.h. an der Quelle die Referenz entfernt
DragCopy.gif (933 Byte) Copy 2 Ein Objekt wird kopiert, d.h. am Ziel eine weitere Referenz eingefügt
DragCopM.gif (950 Byte) Move+Sibling 4 Ein Objekt wird verschoben und gleichzeitig am Ziel an einer bestimmten Stelle einsortiert.
DragCopM.gif (950 Byte) Copy+Sibling 6 Ein Objekt wird kopiert und gleichzeitig am Ziel an einer bestimmten Stelle einsortiert.
DragCopM.gif (950 Byte) Multiple Move 8 Mehrere Objekte werden verschoben
dragcopm.gif (950 Byte) Multiple Copy 10 Mehrere Objekte werden kopiert

Die Einfügemodi werden durch verschiedene Faktoren bestimmt. Mit der - und -Taste kann zwischen Copy und Move gewählt werden. In manchen Fällen ist nur Move logisch nicht möglich (Objekt kann nicht entfernt werden),  dann erscheint bei durch -Taste gewähltem Move Modus der Cancel-Cursor, während bei -Taste und Copy der Copy-Cursor erscheint. Ohne Tasten gibt es Default-Verhalten derart, das innerhalb eines Widgets (z.B. Verschieben in einer Liste) automatisch Move, zwischen 2 verschiedenen Widgets automatisch Copy gewählt wird.
Der Sibling-Modus wird, ohne gedrückte Taste bzw. bei nur - und/oder -Taste gedrückt, durch die Position des Cursors relativ zu Objekten bestimmt. Zwischen 2  Objekten (bzw. ganz nah am Rand einer Objektdarstellung) wird automatisch zum Sibling-Modus übergegangen. Wird (zusätzlich) die -Taste gedrückt gehalten, wird immer der Sibling-Modus versucht. Als Position wird der Rand einer Objektdarstellung gewählt, der der Maus-Cursor-Position am nahesten ist. In der unteren Hälfte eines nur halb gefüllten ObjectTree ist dies z.B. immer der untere Rand des letzten dargestellten Objektes. Wird in diesem Beispiel der Drop ausgeführt, wird das neue Objekte das neue unterste Objekt mit dem selben übergeordneten Objekt wie das zuvor unterste Objekt.
Auch ein Sibling-Modus kann, unabhängig von der Zulässigkeit eines normalen Drops, unzulässig sein. (Z.B.: Vermischung von Elementen aus 2 verschiedenen Collections im ObjectTree.) In diesem Fall erscheint entweder der Cancel-Cursor oder es wird automatisch der normale Modus gewählt. (Letzteres erfolgt nie bei gedrückter -Taste.)
Multiple-Modi werden immer dann gewählt, wenn im Quell-Widget mehrere Objekts markiert waren (ObjectList,ObjectListView). In diesem Fall werden alle diese markierten Objekte verschoben. Im Multiplen Modus kann nicht Sibling eingefügt werden.

Ende(Drop):
Bevor die folgenden Aktionen erfolgen, ist sichergestellt, dass auf jeden Fall noch einmal IS_DROPPABLE ausgeführt wird. Vor dem Einfügen wird ein Kandidat auf jeden Fall also noch einmal überprüft.

Als direkte Reaktion auf das Loslassen der Maus-Taste sendet das System den Event

DROP:

Stack
Stack Position Beschreibung
Stack(In) Top ]
  Top-1 Quell-Objekt On (Drag)
  ... ...
  Top-n Quell-Objekt O1 (Drag)
  Top-n-1 [
  Top-n-2 Ziel-Objekt (Drop) oder NULL
  Top-n-3 Einfügemodus
Stack(Out)   -

an das Ziel-Widget.

Es ist dem Schreiber des auf dieses Event reagierenden InstantView®-Codes überlassen, ob und in welcher Form auf ein Drop reagiert werden soll.
"Ob" heißt hierbei, dass die gesamte Reaktion auch mit cancel abgebrochen werden kann. Auch die Möglichkeit eines Fehlerabbruchs, z.B. durch die Fehlermeldung des Systems, ist vorgesehen. In diesem Fall gleicht das Ergebnis eines gewollten Abbruchs mit cancel.
Ein Abbruch bei ungewollter Aktion ist im Allgemeinen erforderlich, da mit dem erfolgreichen Abschließen des Drag und Drops weitere automatische Reaktionen verbunden sind. So wird bei den oben beschriebenen Einfügemodi mit Move die Quell-Referenz gelöscht.
Auch wird der Einfügemodus zur detaillierteren Reaktion übergeben, ist jedoch nicht zu überschreiben. So wird beim Sibling-(sortierten)Einfügen intern die gewünschte Position gemerkt und bei folgenden Aktionen wie OboxFill mit dem übergebenen Quell-Objekt ausgewertet. Zwar ist es durch gezielte Manipulationen möglich eine Reaktion zu implementieren, die nicht dem Einfügemodus entspricht, dieses sollte aber vermieden werden, da dies den Anwender auf jeden Fall verwirrt und zu falschen Annahmen führt.

Da Einfügemodi mit Copy und generell Drops aus anderen Widgets für eine ObjectBox als Ziel-Widgets wie FillObox zu einem Zuwachs der enthaltenen Elemente führt, gelten wie bei diesen Befehlen die Einschränkungen, die durch SetLimit und SetLevels definiert worden. Speziell beim Copy im ObjectTree gibt es, auch wenn ein direktes Verschieben eines übergeordneten Objekts (Parent) in ein untergeordnetes Objekt (Child) nicht möglich ist, die Möglichkeit, dass durch eine solche Aktion eine zyklische Struktur entsteht. In diesem Fall wird, wie beim ObjectTree beschrieben, ein entsprechendes virtuelles Element generiert, mit dem der Zyklus durchbrochen wird.

Sind die untergeordneten Objekte des Ziel-Objekts noch nicht eingelesen (durch SetLevels auf einen Wert kleiner gleich der Stufe des Ziel-Objekts; kann bei Sibling nicht vorkommen), so werden diese bei erfolgreichem Drop automatisch eingelesen. Es ist also (genauso wie bei OboxFill) sichergestellt, dass untergeordnete Objekte immer komplett oder gar nicht eingelesen werden.