Bug on Tree : scrolls automatically after a drag/drog
Here is an override to solve that problem :
package ui.controls
{
import mx.collections.ICollectionView;
import mx.collections.IViewCursor;
import mx.collections.ItemResponder;
import mx.collections.errors.ItemPendingError;
import mx.controls.Tree;
import mx.core.mx_internal;
import mx.events.DragEvent;
import mx.managers.DragManager;
use namespace mx_internal;
/**
* This override of the List correct the bug which happen after dragging one time, the List scroll automatically when the mouse is moving over.
*
* */
public class DroppableListItemTree extends Tree
{
public function DroppableListItemTree()
{
super();
}
/**
* Handles DragEvent.DRAG_COMPLETE
events. This method
* removes the item from the data provider.
*
* @param event The DragEvent object.
*/
override protected function dragCompleteHandler(event:DragEvent):void
{
isPressed = false;
resetDragScrolling()
if (event.isDefaultPrevented())
return;
try
{
if (event.dragSource.hasFormat(“treeItems”))
{
// if we’ve moved the elements, then remove them here
if (event.action == DragManager.MOVE && dragMoveEnabled)
{
// if we moved onto ourselves, we already handled this in
// dragDropHandler
if (event.relatedObject != this)
{
var items:Array = event.dragSource.dataForFormat(“treeItems”) as Array;
var parent:*;
var index:int;
var i:int;
var n:int;
//do the remove
n = items.length;
for (i = 0; i < n; i++)
{
parent = getParentItem(items[i]);
index = getChildIndexInParent(parent, items[i]);
removeChildItem(parent, items[i], index);
}
// then add it to the target control (copy operations are
// handled in the target's dragDropHandler), but the MOVE
// operations need to be handled here (see comment in
// dragDropHandler about this)
if (event.relatedObject is Tree)
{
var targetTree:Tree = Tree(event.relatedObject);
if (!targetTree.dataProvider) {
// Create an empty collection to drop items into.
targetTree.dataProvider = [];
targetTree.validateNow();
}
n = items.length;
for (i = 0; i < n; i++)
{
var item:Object = items[i];
targetTree.addChildItem(targetTree._dropData.parent,
item,
targetTree._dropData.index);
}
}
}
clearSelected(false);
}
}
}
catch(e:ItemPendingError)
{
e.addResponder(new ItemResponder(seekPendingDuringDragResultHandler, seekPendingDuringDragFailureHandler,
new TreeSeekPending(event, dragCompleteHandler)));
}
lastDragEvent = null;
}
/**
* Handles DragEvent.DRAG_DROP events
. This method hides
* the drop feedback by calling the hideDropFeedback()
method.
*
*
If the action is a COPY
,
* then this method makes a deep copy of the object
* by calling the ObjectUtil.copy()
method,
* and replaces the copy’s uid
property (if present)
* with a new value by calling the UIDUtil.createUID()
method.
*
* @param event The DragEvent object.
*
* @see mx.utils.ObjectUtil
* @see mx.utils.UIDUtil
*/
override protected function dragDropHandler(event:DragEvent):void
{
resetDragScrolling()
if (event.isDefaultPrevented())
return;
hideDropFeedback(event);
if (event.dragSource.hasFormat(“treeItems”))
{
var items:Array = event.dragSource.dataForFormat(“treeItems”) as Array;
var i:int;
var n:int;
// if we’re moving to ourselves, we need to treat it specially and check for “parent”
// problems where we could recurse forever.
if (event.action == DragManager.MOVE && dragMoveEnabled)
{
if (event.dragInitiator == this)
{
// If we’re dropping onto ourselves or a child of a descendant then dont actually drop
calculateDropIndex(event);
// If we did start this drag op then we need to remove first
var index:int;
var parent:*;
var parentItem:*;
var dropIndex:int = _dropData.index;
//get ancestors of the drop target item
var dropParentStack:Array = getParentStack(_dropData.parent);
dropParentStack.unshift(_dropData.parent);
n = items.length;
for (i = 0; i < n; i++)
{
parent = getParentItem(items[i]);
index = getChildIndexInParent(parent, items[i]);
//check ancestors of the dropTarget if the item matches, we're invalid
for each (parentItem in dropParentStack)
{
//we dont want to drop into one of our own sets of children
if (items[i] === parentItem)
return;
}
//we remove before we add due to the behavior
//of structures with parent pointers like e4x
removeChildItem(parent, items[i], index);
//is the removed item before the drop location?
// then we need to shift the dropIndex accordingly
if (parent == _dropData.parent && index < _dropData.index)
dropIndex–;
addChildItem(_dropData.parent, items[i], dropIndex);
}
return;
}
}
// If not dropping onto ourselves, then add the
// items here if it's a copy operation.
// If it's a move operation (and not on ourselves), then they
// are added in dragCompleteHandler and are removed from
// the source's dragCompleteHandler. We do both in dragCompleteHandler
// because in order to be re-parented, they must be removed from their
// original source FIRST. This means our code isn't coupled fantastically
// as dragCompleteHandler must get the destination tree and
// cast it to a Tree.
if (event.action == DragManager.COPY)
{
if (!dataProvider) {
// Create an empty collection to drop items into.
dataProvider = [];
validateNow();
}
n = items.length;
for (i = 0; i < n; i++)
{
var item:Object = copyItemWithUID(items[i]);
addChildItem(_dropData.parent,
item,
_dropData.index);
}
}
}
lastDragEvent = null;
}
/**
* Handles DragEvent.DRAG_EXIT
events. This method hides
* the UI feeback by calling the hideDropFeedback()
method.
*
* @param event The DragEvent object.
*/
override protected function dragExitHandler(event:DragEvent):void
{
resetDragScrolling();
if (event.isDefaultPrevented())
return;
lastDragEvent = null;
hideDropFeedback(event);
DragManager.showFeedback(DragManager.NONE);
}
/**
* @private
* Returns the stack of parents from a child item.
*/
private function getParentStack(item:Object):Array
{
var stack:Array = [];
if (item == null)
return stack;
var parent:* = getParentItem(item);
while (parent)
{
stack.push(parent);
parent = getParentItem(parent);
}
return stack;
}
/**
* @private
*/
private var haveItemIndices:Boolean;
/**
* @private
*/
private function checkItemIndices(event:DragEvent):void
{
if (haveItemIndices)
return;
// if we’re moving to ourselves, we need to make sure we have
// everything paged below before we allow a drop
if ((event.action == DragManager.MOVE || event.action == DragManager.NONE) && dragMoveEnabled)
{
if (event.dragInitiator == this)
{
var items:Array = event.dragSource.dataForFormat(“treeItems”) as Array;
var n:int = items.length;
for (var i:int = 0; i < n; i++)
{
var parent:Object = getParentItem(items[i]);
getChildIndexInParent(parent, items[i]);
}
haveItemIndices = true;
}
}
}
/**
* @private
* Finds the index distance between a parent and child
*/
private function getChildIndexInParent(parent:Object, child:Object):int
{
var index:int = 0;
if (!parent)
{
var cursor:IViewCursor = ICollectionView(iterator.view).createCursor();
while (!cursor.afterLast)
{
if (child === cursor.current)
break;
index++;
cursor.moveNext();
}
}
else
{
if (parent != null &&
_dataDescriptor.isBranch(parent, iterator.view) &&
_dataDescriptor.hasChildren(parent, iterator.view))
{
var children:ICollectionView = getChildren(parent, iterator.view);
if (children.contains(child))
{
cursor = children.createCursor();
while (!cursor.afterLast)
{
if (child === cursor.current)
break;
cursor.moveNext();
index++;
}
}
else
{
//throw new Error("Parent item does not contain specified child: " + itemToUID(child));
}
}
}
return index;
}
/**
* @private
*/
override protected function dragEnterHandler(event:DragEvent):void
{
if (event.isDefaultPrevented())
return;
lastDragEvent = event;
haveItemIndices = false;
try
{
if (iteratorValid && event.dragSource.hasFormat("treeItems"))
{
//if (collectionThrowsIPE)
//checkItemIndices(event);
DragManager.acceptDragDrop(this);
DragManager.showFeedback(event.ctrlKey ?
DragManager.COPY :
DragManager.MOVE);
showDropFeedback(event);
return;
}
}
catch(e:ItemPendingError)
{
if (!lastTreeSeekPending)
{
lastTreeSeekPending = new TreeSeekPending(event, dragEnterHandler)
e.addResponder(new ItemResponder(seekPendingDuringDragResultHandler, seekPendingDuringDragFailureHandler,
lastTreeSeekPending));
}
}
catch(e1:Error)
{
}
hideDropFeedback(event);
DragManager.showFeedback(DragManager.NONE);
}
/**
* @private
*/
override protected function dragOverHandler(event:DragEvent):void
{
if (event.isDefaultPrevented())
return;
lastDragEvent = event;
try
{
if (iteratorValid && event.dragSource.hasFormat("treeItems"))
{
if (collectionThrowsIPE)
checkItemIndices(event);
DragManager.showFeedback(event.ctrlKey ?
DragManager.COPY :
DragManager.MOVE);
showDropFeedback(event);
return;
}
}
catch(e:ItemPendingError)
{
if (!lastTreeSeekPending)
{
lastTreeSeekPending = new TreeSeekPending(event, dragOverHandler)
e.addResponder(new ItemResponder(seekPendingDuringDragResultHandler, seekPendingDuringDragFailureHandler,
lastTreeSeekPending));
}
}
catch(e1:Error)
{
}
hideDropFeedback(event);
DragManager.showFeedback(DragManager.NONE);
}
/**
* @private
*/
private var lastTreeSeekPending:TreeSeekPending;
/**
* @private
* The default failure handler when a seek fails due to a page fault.
*/
private function seekPendingDuringDragFailureHandler(data:Object,
info:TreeSeekPending):void
{
}
/**
* @private
* The default result handler when a seek fails due to a page fault.
* This method re-attempts setting the drag feedback
*/
private function seekPendingDuringDragResultHandler(data:Object,
info:TreeSeekPending):void
{
lastTreeSeekPending = null;
if (lastDragEvent)
info.retryFunction(info.event);
}
}
}
import mx.events.DragEvent;
class TreeSeekPending
{
public function TreeSeekPending(event:DragEvent, retryFunction:Function)
{
this.event = event;
this.retryFunction = retryFunction;
}
public var event:DragEvent;
public var retryFunction:Function;
}