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.
Nov 08, 2006 at 9:37 AM
I would suggest using stage over Application.application. a) its less typing and b) it will detect mouseUp events outside of the Flash Player. :)
Nov 08, 2006 at 9:55 AM
Thnaks the tip, I have updated the code and sample, works nice.
Nov 08, 2006 at 10:45 AM
Is using "stage" safe for loaded swfs?
Nov 08, 2006 at 11:39 AM
yeah, i ran into this yesterday too.
i thought it was a total hack at first, but after the implementation i sat back and looked at it, and it actually made more sense than having an as1&2-style onRelease handler. the MOUSE_UP truly is a stage event, if it's not on the button. this method now makes onReleaseOutside now seem a little convoluted.
anyway, hopefullly i'll *continue* to see it that way ;)
Nov 29, 2006 at 2:48 PM
can't make this work by creating the red_cv in actionscript - var red_cv : Canvas = new Canvas(); - and by adding - red_cv.addEventListener(MouseEvent.MOUSE_DOWN, handleRedStartDrag); - somehow the stage.addEventListener is not recognize. any idea?
Apr 30, 2007 at 11:45 AM
I couldn't get stage instance in a normal component so I tried this hack :
test1= new AnyKindDisplayObject(this) //can be a shape or anything that extends displayObject
test1.addEventListener(Event.ADDED_TO_STAGE,setListenerStopDraw)
whiteBoard.addChild(test1)
private function setListenerStopDraw(event:Event):void {
try {
event.target.stage.addEventListener(MouseEvent.MOUSE_UP,stopDraw) as DisplayObject
} catch (e:Error) {
}
}
I hope this help. Remember stage only exist for displayObject
Jun 20, 2007 at 12:54 PM
This will not work in NPRuntime browsers (Firefox et al) as of 9.45 if the wmode is set to transparent or opaque. I know of no workaround (other than just stopping the drag, which is very unelegant)
Sep 12, 2007 at 10:20 PM
I managed to find a hack to get it working in Firefox on all platforms.
http://www.justsuppose.com/2007/06/as3flash-9firefox-mouse-capture-bug.html
Oct 15, 2007 at 5:47 PM
Neat but I can't get it working in firefox