Drag and drop grid

For a current project I am working on I have needed to be able to load a group of thumbnails and then allow simple drag and drop actions to re-order the thumbnails and save the new order as well as being able to delete a thumbnail.

I’ve spent some time working on this prototype and I am now pretty happy with the results. You can drag and drop a box from any position and it will re-order all the boxes in their new, correct positions. There where some bugs and problems to overcome in the beginning, such as finding the correct position of the dragged box. Eventually I figured out a method that could simply calculate this new position based on the number of columns I had set for the grid and the items x and y position. The basic equation is:

position = ( insertRow * columns) + insertColumn;

I then replace insertRow and insertColumn with functions to calculate based on the box’s x and y. This gave me a number between 0 and the total number of boxes, I could then splice out the box from an array of boxes and then re-insert it in the correct position. After I had a newly ordered array of boxes I can loop through the array and re-set the position of each box. I have included the final code underneath the example:

This movie requires Flash Player 9 or above

var boxNum:int = 18;

var cols:int = 7;

var padding:int = 24;

var boxes:Array = [];

var saved:Array;

 

createChildren();

 

function createChildren():void

{

 for (var i:int = 0; i < boxNum; i++)

 {

 var box:Box = new Box();

 box.redTxt.text = i.toString();

 addChild(box);

 

 // stop the textfields from dispatching events

 box.redTxt.mouseEnabled = false;

 box.greenTxt.mouseEnabled = false;

 box.invisibleBtn.useHandCursor = false;

 

 // store a reference to the Box

 boxes[i] = box;

 

 // set initial positions

 arrange();

 

 // set dragger

 box.invisibleBtn.addEventListener(MouseEvent.MOUSE_DOWN, _mouseDown);

 box.deleteBtn.addEventListener(MouseEvent.CLICK, _mouseDelete, false, 0, true);

 }

}

 

function arrange():void

{

 saved = [];

 

 for (var i:int = 0; i < boxes.length; i++)

 {

 boxes[i].x = 0 + (boxes[i].width + padding) * (i % cols);

 boxes[i].y = 0 + (boxes[i].height + padding) * int(i / cols);

 boxes[i].arrayIndex = i;

 boxes[i].greenTxt.text = i.toString();

 

 // update the saved boxes too

 saved[i] = boxes[i];

 }

}

 

function remove($mc:Box):void

{

 // remove from array

 boxes.splice($mc.arrayIndex, 1);

 

 // remove event listeners

 $mc.invisibleBtn.addEventListener(MouseEvent.MOUSE_DOWN, _mouseDown);

 $mc.deleteBtn.addEventListener(MouseEvent.CLICK, _mouseDelete, false, 0, true);

 

 // remove child

 removeChild($mc);

 

 // remove for garbage collection

 $mc = null;

 

 // check the current grid

 checkGrid();

 

 // re-position boxes

 arrange();

}

 

function checkGrid():void

{

 var hasChanged:Boolean = false;

 

 // if a box has been deleted

 if(saved.length != boxes.length)

 {

 hasChanged = true;

 }

 

 // if a box has been repositioned

 for(var i:String in boxes)

 {

 if(boxes[i].arrayIndex != i)

 {

 hasChanged = true;

 }

 }

 

 if(hasChanged)

 {

 // tell the world

 trace(”something has changed! update”)

 }

}

 

function _mouseDelete(evt:MouseEvent):void

{

 remove(evt.target.parent);

}

 

function _mouseDown(evt:MouseEvent):void

{

 stage.addEventListener(MouseEvent.MOUSE_UP, _mouseUp);

 

 // swap the Box’s depth to the highest possible

 var tmp:Sprite = evt.target.parent as Sprite;

 setChildIndex(tmp, numChildren - 1);

 

 tmp.startDrag();

}

 

function _mouseUp(evt:MouseEvent):void

{

 stage.removeEventListener(MouseEvent.MOUSE_UP, _mouseUp);

 stopDrag();

 

 // calculate new array positions

 var insertRow:int = getInsertRow(evt.target.parent as Sprite);

 var insertCol:int = getInsertColumn(evt.target.parent as Sprite);

 var insertPoint:int = (insertRow * cols) + insertCol;

 

 // remove box from array

 boxes.splice(evt.target.parent.arrayIndex, 1);

 

 // insert box to array (using insertPoint as the index)

 boxes.splice(insertPoint, 0, evt.target.parent);

 

 // check the current layout

 checkGrid();

 

 // reposition

 arrange();

}

 

function getInsertRow(mc:Sprite):int

{

 var rows:int = Math.ceil(boxNum / cols) - 1;

 var tmp:int = Math.floor((mc.y + (mc.height * 0.5)) / (mc.height + padding));

 

 // normalize value

 if (tmp < 0)

 {

 tmp = 0;

 }

 if (tmp > rows)

 {

 tmp = rows;

 }

 return tmp;

}

 

function getInsertColumn(mc:Sprite):int

{

 var tmp:int = Math.floor((mc.x + (mc.width * 0.5)) / (mc.width + padding));

 

 // normalize value

 if (tmp < 0)

 {

 tmp = 0;

 }

 if (tmp >= cols)

 {

 tmp = cols - 1;

 }

 return tmp;

}