Pinning Drag-and-drop to the mat: a primer

Having just wrestled with drag-and-drop for the last few hours, I figured I'd better document what I've been through in the hope of saving someone (maybe myself) some time the next time this comes up. What I wanted to do seemed like a simple task: drag the gradient array from one graphic image to another. This ended up with several hours of experimenting and seemingly unexplainable failures, finally ending up with a bunch of "put into msg" statements to figure out what was going on.

To start with, the documentation is a bit misleading, so let's take a look at the available commands:

To start with, the documentation is a bit misleading, so let's take a look at the available commands:

dragStart : sent to source control

dragEnter : sent to possible destination

dragLeave : sent to possible destination (not used here)

dragDrop : sent to destination control

dragEnd : sent to source control (not used here)

 

and the engine properties of interest - these can be queried any time drag-and-drop is in effect:

the dragSource

the control the drag started from (not used here)

the dragDestination

the control the data was dropped onto

the allowableDragActions

one or more of {none or move or copy or link}

the dragAction

one of {none or move or copy or link}

the dragData

the data being dragged

dragStart

dragStart

In chronological order, the dragStart message is sent to the source control when you start dragging from it. If you want to be able to drag it or its contents or something else from a control then you must have a dragStart handler either in the control or in its path. This could possibly mean in the owning group or in a behavior control.

In the dragStart handler you need to specify what will be dragged and in what format it will be supplied. Note in particular that you can *not* use arrays as draggable data - if you need to drag array data then you need to combine it into a string representation before setting it as the dragData. See dragData below.

So here's what happens when I start dragging from one of my gradient source controls:

on dragStart

   local tGradient

   

   -- start dragging the gradient to the strokeGradient box

   set the allowableDragActions to "copy"

   set the dragAction to "copy"

   put the fillgradient of the target into tGradient

   -- convert the gradient array to string data

   combine tGradient using tab and ":"

   -- say what type of gradient this is

   set the dragData["private"] to "fill" & cr & tGradient

   pass dragStart

end dragStart

the dragData

The documentation for the dragData property says "the dragData is an array with one or more of the following elements:" (text, html, rtf, Unicode, image, files, styles, private), but don't construe that to mean that you can do something like

on dragStart

set the dragData["text"] to "hello, world"

set the dragData["private"] to "top secret"

because the second incarnation ("private") will overwrite the first and you will lose the "text" part. For this reason (and after much experimenting) I ended up using "private" for all my dragData. That way I could put "fill" or "stroke" into line one of the dragData and fill the rest with with string gradient values.

dragEnter

dragEnter

The dragEnter message is sent to a control when the mouse moves over it while dragging data. This is essential - if you don't have a dragEnter handler in the destination control or somewhere in its path then it will not be a candidate for dropping the data (the cursor will not change to a drop cursor and you won't be able to drop data onto it).

Another way of saying this is that if you want to enable a control as a destination for dropped objects you need to add a dragEnter handler to its script.

on dragEnter

set the dragAction to "copy"

pass dragEnter

end dragEnter

dragLeave

dragLeave

The dragLeave message is sent to a control when the mouse leaves it. I didn't need to use the dragLeave message, but you could use it if you wanted to control the visual display of the image, for example.

 

the dragSource

In my case I wanted to make sure that the data being dropped came from one of the gradient controls, so I wanted to check the dragSource. Here's another case where the documentation failed me. The documentation says the dragSource contains the long id of the control where the drag started from, and it does. However, the example in the documentation

if the short name of the dragSource is empty then beep

doesn't work because you can't get the short name of the dragSource (you'll get a runtime error if you try). What does work, however, is

put the dragSource into tSource

if the short name of tSource is empty then beep

In the end I opted not to use the dragSource and instead to place the source type as the first line of my private data. Using the dragSource would have worked as well, but for my needs here it would have limited dragging from only those objects, and I wanted a more generic solution.

the dragDestination

The dragDestination is also a long id, this time of the control that is the recipient of the dropped data. See the dragDrop description below to see how I'm using this.

dragDrop

dragDrop

The dragDrop message is sent when the mouse is released over a valid drop candidate control. Here's where you want to handle getting the dragData information and doing something with it. As noted above, I opted not to use the dragSource to determine the type of gradient and so I'm extracting that data from the first line of the private data. Then I delete the first line since I'm done with it, and what's left over is the contents of the gradient array. I copy the gradient array I extracted from the dragData over to the dragDestination control.

My dragDrop handler looks like this:

on dragDrop

local tFillArray

local tDestination

local tType

-- see where we're dropping the data

put the dragdestination into tDestination

put the dragData["private"] into tFillArray

-- extract the data type

put line 1 of tFillArray into tType

    -- we're done with the data type

    -- what's left over is the gradient data as a set of strings

delete line 1 of tFillArray

-- turn gradient data back into an array

split tFillArray with tab and ":"

if the keys of tFillArray is not empty then

    -- handle the different types of gradients

if tType is "stroke" then

set the uStrokeGradient of tDestination to tFillArray

else

set the uFillGradient of tDestination to tFillArray

end if

end if

-- play well with others

pass dragDrop

end dragDrop

 

dragEnd

dragEnd

The dragEnd message is sent to the source control after the drop has occurred. I didn't need to do any postprocessing, so I'm not using this message. But here's where you might check the dragDestination to see where the data went, especially if you're moving data rather than copying it.

 

So here's the destination group arrangement I ended up with...

So here's the destination group arrangement I ended up with...

The script of my group of destination controls has these handlers:

dragStart

dragEnter

dragDrop

control 1

control 2

control 3

control 4

-- can drag from sources to these controls (dragEnter and dragStop)

-- or from one control to another (dragStart)

...and the source control group arrangement

...and the source control group arrangement

group of source controls

dragEnter (enable the controls as destination points)

gradient source 1

dragStart (these enable the drag source functionality)

dragDrop

gradient source 2

dragStart

dragDrop

-- can drag from source 1 to source 2 and vice versa

0 Comments

Add your comment

E-Mail me when someone replies to this comment