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.

This lesson does not apply on mobile because the drag messages are Desktop only.

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"
end dragStart

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

5 Comments

Trevix

This doesn't work on mobile.
It should be stated.

Elanor Buchanan

Hi Trevix

Thanks for bringing that to our attention. I have added a desktop tag and a note to the introduction.

Kind regards

Elanor

Robert

Thanks! It is indeed not obvious to get the sequences right, nor which handlers need to be present to get it all to work.

Eric Guerin

Any change you could update or augment this on how this would work for mobile?

Elanor Buchanan

Hi Eric

Most of these drag messages are desktop only. The grab command does work on mobile which you might find useful. We have a lesson on using the grab command here.

https://lessons.livecode.com/m/4071/l/743709-how-do-i-create-and-move-graphics-in-an-app

I hope that helps.

Elanor

Add your comment

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.