Flex 2 AS 3 onReleaseOutside
I just ran across an interesting issue with Flex 2/AS 3, the onReleaseOutside event handler no longer exists for MovieClips (or any Interactive Object). According the Adobe AS 2 - AS 3 migration documentation onReleaseOutside has been replaced in the new event model by a mouseUp event after a call to flash.display.InteractiveObject.setCapture(). Upon further investigation the InteractiveObject.setCapture() no longer exists either.
So effectively there is no easy way to catch when a user presses the mouse button, drags the cursor off the pressed object and then releases the mouse button. This causes a serious problem, especially for items that are dragged around since the mouse cursor often comes off the dragged item. Without a proper onReleaseOutside handler if the user releases the mouse button while outside a dragged item, the item will continue to be dragged around until the user mouses back over the item and then clicks and releases. Thankfully there is a relatively clean solution.
In the code below I simply added an extra statement in the start drag function that adds a MouseUp listener to the Application stage. Now if the user clicks and drags off an item and then releases the mouse button, the Application stage will catch the MouseUp and stop the item drag. In the stop drag function I added a statement to remove the listener, in order to cut down on an unnecessary event handling. Not as nice as having a proper onReleaseOutside event but at least it works.
<?xml version="1.0" ?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute">
<mx:Script>
<![CDATA[
import flash.events.MouseEvent;
private function handleRedStartDrag(event:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_UP, handleRedStopDrag);
var bounds:Rectangle = new Rectangle(bounds_cv.x, bounds_cv.y, bounds_cv.width - red_cv.width, bounds_cv.height-red_cv.height);
red_cv.startDrag(false, bounds);
}
private function handleRedStopDrag(event:MouseEvent):void
{
red_cv.stopDrag();
stage.removeEventListener(MouseEvent.MOUSE_UP, handleRedStopDrag);
}
private function handleGreenStartDrag(event:MouseEvent):void
{
var bounds:Rectangle = new Rectangle(bounds_cv.x, bounds_cv.y, bounds_cv.width - green_cv.width, bounds_cv.height-green_cv.height);
green_cv.startDrag(false, bounds);
}
private function handleGreenStopDrag(event:MouseEvent):void
{
green_cv.stopDrag();
}
]]>
</mx:Script>
<!-- bounding rectangle -->
<mx:Canvas
id="bounds_cv"
x="50"
y="50"
width="300"
height="200"
backgroundColor="#CCCCCC"
borderStyle="solid"/>
<!-- The red box being dragged -->
<mx:Canvas
id="red_cv"
backgroundColor="#ff0000"
height="50"
width="50"
x="50"
y="50"
mouseDown="handleRedStartDrag(event);"
mouseUp="handleRedStopDrag(event);" />
<!-- green box being dragged -->
<mx:Canvas
id="green_cv"
backgroundColor="#00ff00"
height="50"
width="50"
x="50"
y="200"
mouseDown="handleGreenStartDrag(event);"
mouseUp="handleGreenStopDrag(event);" />
</mx:Application>
You can test it here, click and drag the red or green box around in the rectangle and then release the mouse button outside of the rectangle in the grey area. The green box uses only the normal MouseUp handler to stop the drag, while the red box has the added statement for the listening to the mouseUp on the Application. Note that if you drag and release outside of the swf the release event is not captured, this is the same issue with the AS 2 onReleaseOutside, no way around it that I am aware of. Thanks Senocular for the tip on using the stage.