cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Check out the JMP® Marketplace featured Capability Explorer add-in
MouseBox ListBox Drag and Drop
Craige_Hales
Super User

question that prompted this script:

Trouble with mousebox behavior?

9774_DragDrop.png

Comments

Hi Craige, I'm trying to make your listboxes more general because I love these but I'd like to make any number of them and put them anywhere.  I've got it working but it seems really finicky with the selection.  Is there anything you can think of to change the sensitivity or anything of the mousebox?  I don't want to have to rely on child/parent relations. 

Names Default to here(1);

linked = Function({list}, {},

       Mousebox(

              <<SetDragEnable(1),

              //<<SetTrackEnable(1),

              <<SetDropEnable(1),

              <<Set Drag Begin(

                     Function({this, clickPt},

                           this<<Set Drop Enable(0);

                     )

              ),

              /*<<SetTrack(

                     Function({this, clickPt},

                           this << Set DropEnable(0);

                           sel = (this << child)<<Get Selected;

                           this << SetUserData(sel)

                     )

              ),

              <<SetDropTrack(

                     Function({this, clickPt},

                           {destbox, sourcebox, lb, items, i, lb_source, lb_dest, index},

                           destbox = this << GetDestBox;

                           sourcebox = this << GetSourceBox;

                           show(sourcebox);

                           /*lb_source = sourcebox << child;

                           sel = lb_source << Get Selected;

                           lb_dest = destbox << child;

                           items = lb_source << Get Items;

                           for(i = 1, i<=nitems(sel), i++,

                                  index = Contains(lb_source<<Get Items, sel);

                                  lb_source << Remove Item(index);

                                  lb_dest << Append(sel);

                           );

                           show(destbox, sel, text, lb, index, items);

                           sourcebox << Set Drop Enable(1);

                           1;*/

                     )                   

              ),*/

              <<Set Drag End(

                     Function({this, clickPt, how},

                           this<<Set Drop Enable(1);

                     )

              ),

              <<SetDropCommit(

                     Function({this, clickPt, text},

                           {destbox, sourcebox, lb, items, i, lb_source, lb_dest, index},

                           destbox = this << GetDestBox;

                           sourcebox = this << GetSourceBox;

                           show(sourcebox);

                           lb_source = sourcebox << child;

                           sel = lb_source << Get Selected;

                           lb_dest = destbox << child;

                           items = lb_source << Get Items;

                           for(i = 1, i<=nitems(sel), i++,

                                  index = Contains(lb_source<<Get Items, sel);

                                  lb_source << Remove Item(index);

                                  lb_dest << Append(sel)

                           );

                           show(destbox, sel, text, lb, index, items);

                           sourcebox << Set Drop Enable(1);

                           1;

                     )

              ),

              listbox(list)

       )

);

new window("Test",

       Hlistbox(

              linked({"1", "2", "3"}),

              linked({"4", "5", "6"}),

              linked({"8", "9", "14"})

       )

);

new window("Other Window", //not sure if this is a good or bad thing yet

       linked({"test", "other these", "stuff"})

);

The original JSL does not scale beyond two boxes, and it would be very hard to get it to work, generic or not, for more than two because of the assumption that "the other box" sourced the drag.  There is a comment at the beginning of the code explaining why MouseBox uses the track (mouse moves but button not pressed) method rather than the click or drag methods...both click and drag would prevent the ListBox from making a selection (and then sourcing the drag).   The original JSL is a work-around for something the ListBox doesn't currently do.

Without changes to the internal ListBox support, I don't think you'll find a completely satisfactory way to drag'n'drop between three or more listboxes.  You should be able to make multiple pairs of boxes, I think, using the original code; there is a cross-talk test (checking the other box's selection matches the dragged text) that will detect most abuse attempts.

If you don't need to remove the selected item from the source ListBox, then the problem is a lot simpler.  Use the dropped text to make a clone and don't even worry about finding the other box since you don't need to remove the item.

----

You can build some pretty cool drag'n'drop GUIs with the <<sourceBox and <<destBox messages.  One of the keys to using them is to make dragBegin supply a string for its return code.  That's the string that dropCommit will receive.  The scripting index for MouseBox setDropTrack has a 15-puzzle example with this JSL:

dragBegin = Function( {this, clickPt},

    {x, y} = this << getUserData;

    If( nextToBlank( x, y ),

       ((this << child) << child) << getText/* the message is the textbox content */

    , /* else */

       0 /* suppress the drag */

    );

);/* function to remove the character from the source cell, but only if the drop was successful */

The bold lines show two ways to use the return value: ignoring the drag attempt when the 15-puzzle piece can't be moved, or returning the letter if it can be moved.  The example has some other features too, like the userData that helps figure out which squares are adjacent.

Craige

Thanks Craig.  I'm starting to figure it out I think. 

Is the Key in Get/SetKey another way to store information or does it have some other use?

setKey is a keystroke handler similar to the click/track/drag handlers.  Key strokes .  GetUserData is what you want; use an associative array or list or matrix if you need more than one value.

Thank you. 

Drum_Ty

@Craige_Hales Would this be the ideal set up for adding click/drag functionality to only 1 list box? Ie. A list box containing a list of items in which the user can then select and then drag around to adjust the ordering of the list. If not I think my work around will be up & down arrow buttons... effective but slower to interact with. 

I think the up/down buttons to manipulate the selected item is the right answer; I don't think trying to line up mousebox pixel-level coordinates with listbox item numbers is going to be a robust solution.

Drum_Ty

I think I got it lined out... Not sure if this will be of use to anyone, but posting here just in case...

Keeps track of the selected indices and then reselects after they have been repositioned. 

 

 

/*
 * Changes position of selected list items, moving them up in rank
*/
On Move Up = function({this},
	selectedList = listBoxBoxVariable << Get Selected;	
	selectedIndices = listBoxBoxVariable << Get Selected Indices;
	myNewIndices = List();
	listBoxBoxVariable << Remove Selected;
	
For Each({index, i}, selectedIndices, tempName = selectedList[i]; newInsertIndex = Max(i-1,index-2); listBoxBoxVariable << Insert(tempName, newInsertIndex); Insert Into(myNewIndices, newInsertIndex+1); ); For Each({index, i}, myNewIndices, listBoxBoxVariable << Set Selected(index); ); ); /* * Changes position of selected list items, moving them down in rank */ On Move Down = function({this}, selectedList = listBoxBoxVariable << Get Selected; selectedIndices = listBoxBoxVariable << Get Selected Indices; myNewIndices = List(); listBoxBoxVariable << Remove Selected; For Each({index, i}, selectedIndices, tempName = selectedList[i]; newInsertIndex = Min(Length(TagListBox << Get Items),index); listBoxBoxVariable << Insert(tempName, newInsertIndex); Insert Into(myNewIndices, newInsertIndex+1); ); For Each({index, i}, myNewIndices, listBoxBoxVariable << Set Selected(index); ); );

Recommended Articles