Flex event propagation - apache-flex

I try to capture a event and I can't.
Please look here:
http://www.pata.ro/FlexTest/
The blue rectangle is the parent, and the other two are the children.
If I roll over a child (red or green) I am still over the blue one (the RollOut is not fired for the blue one). I made the green rectangle a little transparent so you can see that it is over the red one.
When I put my cursor over the green one in a place where it is over the red one, I get, BlueRollOver, GreenRollOver, RedRollOut.
What I try to do is to get the red one RollOver too, even if it is under the green one. Like the parent captures RollOver even if I am over one of it's children. Or vice versa.
So, how can I propagate the event down to the element under the one I have the mouse over?
Thanks
Bellow you have my code.
The event listeners were declared in the MXML, so I rewrite those for the red rectangle so I could add the useCapture argument. If I set useCapture to TRUE, the red rectangle dosen't capture any event, wherever I have the mouse. If I set it to false it works like before. So, how can I use this argument?
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
applicationComplete="init()">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
private function init():void
{
gRed.addEventListener(MouseEvent.ROLL_OVER,RedRollOver,true);
gRed.addEventListener(MouseEvent.ROLL_OUT,RedRollOut,true);
}
private function BlueRollOver(ev:Event):void
{
idBlue.text="RollOver";
}
private function BlueRollOut(ev:Event):void
{
idBlue.text="RollOut";
}
private function RedRollOver(ev:Event):void
{
idRed.text="RollOver";
}
private function RedRollOut(ev:Event):void
{
idRed.text="RollOut";
}
private function GreenRollOver(ev:Event):void
{
idGreen.text="RollOver";
}
private function GreenRollOut(ev:Event):void
{
idGreen.text="RollOut";
}
]]>
</fx:Script>
<s:Group id="gBlue" x="114" y="94" width="404" height="301" rollOver="BlueRollOver(event)" rollOut="BlueRollOut(event)">
<s:Rect width="100%" height="100%">
<s:fill>
<s:SolidColor color="#0000CC"/>
</s:fill>
</s:Rect>
<s:Group id="gRed" x="140" y="101" width="230" height="114">
<s:Rect width="100%" height="100%">
<s:fill>
<s:SolidColor color="#EE0000"/>
</s:fill>
</s:Rect>
</s:Group>
<s:Group id="gGreen" x="39" y="20" width="200" height="200" rollOver="GreenRollOver(event)" rollOut="GreenRollOut(event)">
<s:Rect width="100%" height="100%" alpha="0.6">
<s:fill>
<s:SolidColor color="#00EE00"/>
</s:fill>
</s:Rect>
</s:Group>
</s:Group>
<s:Label x="535" y="94" text="Blue" color="#0000CC" width="149" id="idBlue"/>
<s:Label x="535" y="114" text="Red" color="#EE0000" width="173" id="idRed"/>
<s:Label x="535" y="137" text="Green" color="#00EE00" width="173" id="idGreen"/>
</s:Application>

Try setting the useCapture argument to true when you add the event listener to the red layer.

It's because the green and the red rectangles are siblings, the events can only bubble up if there is a parent / child hierachy. Use the useCapture flag on the red rectangle.

Related

Custom drop down on mouse over

I'm making a custom component that shows a little drop down area after you hover on the username. I'm using two states, up and hover to toggle my drop down box.
My problem is that it thinks I'm leaving the component level Group after I leave the username. Then the second level group (my drop down) disappears.
I've tried re-attaching an event handler to my component level Group on a callLater in my mouse over function but that didn't work.
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
mouseOver="groupLogIn_mouseOverHandler(event)"
mouseOut="groupLogIn_mouseOutHandler(event)"
>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import spark.skins.SparkSkin;
protected function groupLogIn_mouseOverHandler(event:MouseEvent):void
{
this.currentState = "hover";
}
protected function groupLogIn_mouseOutHandler(event:MouseEvent):void
{
this.currentState = "up"
}
]]>
</fx:Script>
<s:states>
<s:State name="up"/>
<s:State name="hover"/>
</s:states>
<s:Label id="lblUsername" color="0xffffff" fontSize="14" right="18" top="5"/>
<s:Group includeIn="hover" width="160" height="110" top="20" right="0" >
<s:Rect top="0" right="0" bottom="0" left="0">
<s:fill>
<s:SolidColor color="0x1a1a1a"/>
</s:fill>
</s:Rect>
</s:Group>
</s:Group>
You need a "hitzone" for your mouse events. Right now parts of your component are completely transparent. When the mouse goes over a transparent zone, the MOUSE_OUT event is triggered. (When I say 'transparent', µi actually mean there's nothing there at all).
Luckily this can easily be fixed: just add a Rect that covers the entire area below the other components and set its alpha to 0. This makes the Rect invisible to the user, but it can still react to mouse events.
<s:Rect id="hitzone" top="0" right="0" bottom="0" left="0">
<s:fill>
<s:SolidColor alpha="0" />
</s:fill>
</s:Rect>
<s:Label id="lblUsername" />
...

How do I ignore mouse actions on transparent areas of a PNG using Flex image?

I made a simple test Air application to try different approaches to masking or using hitArea to ignore mouse events over transparent areas of a PNG. Can't seem to find the right mix of things to make it work, nor could I find a succinct example on the web.
Clicking on the transparent areas of any of these methods don't result in the click getting handled by the background.
Here's the code I have:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
status="{clicked}">
<fx:Declarations>
<s:Image id="maskX" source="#Embed('mask1.png')" cacheAsBitmap="true"/>
</fx:Declarations>
<fx:Script>
<![CDATA[
[Bindable] public var clicked:String = "none";
protected function onClick(event:MouseEvent):void
{
clicked = event.currentTarget["id"] + "\t" + (new Date()).time;
}
]]>
</fx:Script>
<!-- Some sort of background so I can see transparency working. -->
<s:Group id="background" width="100%" height="100%" click="onClick(event)">
<s:Rect width="100%" height="100%">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="blue"/>
<s:GradientEntry color="white"/>
</s:LinearGradient>
</s:fill>
</s:Rect>
</s:Group>
<s:Group width="100%" height="100%">
<s:layout>
<s:TileLayout/>
</s:layout>
<!-- Simple attempt at having Flex respect the alpha in the PNG itself as transparent
with regard to clicks -->
<s:Group id="image1" click="onClick(event)" mouseEnabledWhereTransparent="false">
<s:Image source="#Embed('image1.png')" cacheAsBitmap="true"/>
</s:Group>
<!-- Use an explicit bitmap mask for the hitArea -->
<s:Group id="image2" click="onClick(event)" hitArea="{mask2}" mouseEnabledWhereTransparent="false">
<s:Image source="#Embed('image1.png')"/>
<s:Image id="mask2" source="#Embed('mask1.png')" cacheAsBitmap="true"/>
</s:Group>
<!-- Try using just the mask bitmap -->
<s:Group id="image3" click="onClick(event)" hitArea="{mask3}" mouseEnabledWhereTransparent="false">
<s:Image id="mask3" source="#Embed('mask1.png')" cacheAsBitmap="true"/>
</s:Group>
<!-- Specify the hitArea with alternate syntax -->
<s:Group id="image4" click="onClick(event)" mouseEnabledWhereTransparent="false">
<s:Image source="#Embed('image1.png')"/>
<s:hitArea>
<s:Image id="mask4" source="#Embed('mask1.png')" cacheAsBitmap="true"/>
</s:hitArea>
</s:Group>
</s:Group>
The image1 and mask1 files I've uploaded here:
image1 - http://img853.imageshack.us/img853/923/image1yj.png
mask1 - http://img715.imageshack.us/img715/3755/mask1.png
Actually, it is possible. Here is a sample:
http://www.webverwirklichung.com/en/blog/programming/flex/creating-hitarea-png-image-transparent-alpha-regions-flex
Flex doesn't respect the alpha channel of a PNG, but you can render out the visible content into a sprite, and use that as a mask on any DisplayObject. Using this approach, only the visible area of the ping will cause mouse events, and it should respect all opacity. If you are layering things, you might hit a few issues.
Just make sure you use the alpha channel for masking content, not a specific color channel.

slightly weird question about ordering layers

I was wondering if there is a way to have an object A (e.g. a Rect) visually appear behind an object B (e.g. another Rect) but in front of it from a mouse rollover standpoint. In other words, if you move the mouse tip over the intersection of A and B, A rolls over instead of B.
If this doesn't exist out of the box I'm curious how you would implement something like this.
thank you
Maybe this sample suits your need?
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
private function putBlueRectInFront():void
{
blueRect.depth = 1;
redRect.depth = 0;
}
private function putRedRectInFront():void
{
redRect.depth = 1;
blueRect.depth = 0;
}
protected function rectContainer_rollOutHandler(event:MouseEvent):void
{
rectContainer.removeEventListener(MouseEvent.MOUSE_MOVE, rectContainer_mouseMoveHandler);
putBlueRectInFront();
}
protected function rectContainer_rollOverHandler(event:MouseEvent):void
{
rectContainer.addEventListener(MouseEvent.MOUSE_MOVE, rectContainer_mouseMoveHandler);
}
private function rectContainer_mouseMoveHandler(event:MouseEvent):void
{
if (event.localX <= redRect.width && event.localY <= redRect.height)
putRedRectInFront();
else
putBlueRectInFront();
}
]]>
</fx:Script>
<s:BorderContainer height="400" horizontalCenter="0" id="rectContainer"
rollOut="rectContainer_rollOutHandler(event)" rollOver="rectContainer_rollOverHandler(event)" verticalCenter="0"
width="400">
<s:borderStroke>
<s:SolidColorStroke color="black" weight="1" />
</s:borderStroke>
<s:Rect height="250" id="redRect" left="0" top="0" width="250">
<s:fill>
<s:SolidColor color="red" />
</s:fill>
</s:Rect>
<s:Rect bottom="0" height="250" id="blueRect" right="0" width="250">
<s:fill>
<s:SolidColor color="blue" />
</s:fill>
</s:Rect>
</s:BorderContainer>
</s:Application>
I don't think this works intrinsically.
To make ObjectA appear behind ObjectB, you just have to add ObjectA to the container before ObjectB. Flex handles the "Z-Order" based on the order items were added to the container.
I'd implement the Z-Order change using using swapChildren and mouse events. Possibly mouseOver to move ObjectA on top and then mouseOut to move ObjectA to the back.
If you have a more complicated structure with more than two classes, you can use swapChildrenAt.

Flex first window width/bound gets ignored

I have a window in which I want to add/remove component and resize the window accordingly based on states. but for some reason it's ignoring the first resize.
here's a sample code
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="450"
minHeight="355"
currentState="{stateBox.selected ? 'one' : 'two'}"
currentStateChange="handleStateChange(event.oldState, event.newState)">
<fx:Script>
<![CDATA[
private function handleStateChange(oldState:String, newState:String):void
{
if (oldState == "two") {
width -= 341;
minWidth = 450;
} else {
width += 341;
minWidth = 791;
}
}
]]>
</fx:Script>
<s:states>
<s:State name="one" />
<s:State name="two" />
</s:states>
<s:Rect id="rect1" top="0" left="0" right="0" right.two="341" bottom="0" >
<s:fill>
<s:SolidColor color="0xDDDDDD" />
</s:fill>
</s:Rect>
<s:Rect id="rect2" top="0" bottom="0" width="341" right="0" includeIn="two" >
<s:fill>
<s:SolidColor color="0x000000" />
</s:fill>
</s:Rect>
<s:CheckBox id="stateBox" label="change state" />
</s:WindowedApplication>
Idea is that when the state change to 'two', I want to add rect2 to display and increase the minWidth and size of the window. And when the state change to 'one', i want to remove rect2 from display and resize the window. This seems to work except the window doesn't shrink on the very first time the state change from "two" to "one" but works as expected afterwards. I'm not sure why it's ignoring first time I reduce the width. I also tried changing nativeWindow.bounds directly but that didn't work either.
Originally I just tried setting minWidth based on state (minWidth.one="450 minWidth.two="791") but that caused window to grow on left and shrink on right which caused the window to move left whenever the state changed. so then I just moved the window to right whenever state changed but that caused some flicker that I didn't want.
does anyone have any idea why this might be happening? or a good solution for my problem?
Try flipping these lines around from:
width -= 341;
minWidth = 450;
to:
minWidth = 450;
width -= 341;
I think the problem is with the way that condition is setup in general.... I'm going to copy your code and modify it how I would go about doing this and repost as an edit, but you could try the above suggestion (I believe you may be setting the width lower than the minimum width then reducing the minWidth so the setting of the width in the first place would have been set to a width that isn't valid according to the current minWidth, think you may only see this the first time around due to timing with validation.
EDIT(ed twice, add locking the window size by setting min and max heights equal to the set height):
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minHeight="355"
maxHeight="355"
minWidth="450"
width.two="791"
width.one="450"
minWidth.one="450"
minWidth.two="791"
maxWidth.one="450"
maxWidth.two="791">
<fx:Script>
<![CDATA[
protected function stateBox_changeHandler(event:Event):void
{
// TODO Auto-generated method stub
currentState=stateBox.selected ? 'two' : 'one';
}
]]>
</fx:Script>
<s:states>
<s:State name="one"/>
<s:State name="two"/>
</s:states>
<s:HGroup width="100%"
height="100%"
gap="0">
<s:Rect width="450"
height="100%">
<s:fill>
<s:SolidColor color="0xff0000"/>
</s:fill>
</s:Rect>
<s:Rect width="341"
height="100%"
includeIn="two">
<s:fill>
<s:SolidColor color="0x000000"/>
</s:fill>
</s:Rect>
</s:HGroup>
<s:CheckBox id="stateBox"
label="change state"
change="stateBox_changeHandler(event)"/>
</s:WindowedApplication>
Does this achieve the desired behavior ^ (sorry I know I changed a lot of code but this is just how I would approach it, maybe this won't work because of other explicit sizing you need to set but seems to achieve the goal to me)

Wrong target when clicking on a <s:Group> with a Mask in Flex 4

I am having trouble handling a MouseEvent.MOUSE_DOWN on an component with a Image for a mask in Flex 4.
I have an application as follows:
<?xml version="1.0"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="init()"
>
<fx:Script>
<![CDATA[
import spark.core.MaskType;
public function init():void {
manager.addEventListener( MouseEvent.MOUSE_DOWN, handleMouseDown );
}
public function handleMouseDown(e:MouseEvent):void {
trace( e.target )
}
]]>
</fx:Script>
<s:Group id="manager" width="500" height="500">
<s:Group id="layer" mouseChildren="false" x="220">
<s:Group id="content" maskType="{MaskType.ALPHA}" cacheAsBitmap="true">
<s:Rect width="338" height="162">
<s:fill>
<s:SolidColor color="0xDDAAAA" />
</s:fill>
</s:Rect>
<s:mask>
<s:Group>
<mx:Image
source="http://www.wpclipart.com/signs_symbol/alphabets_numbers/3D_text_T.png"
cacheAsBitmap="true"
/>
</s:Group>
</s:mask>
</s:Group>
</s:Group>
</s:Group>
</s:Application>
If you run this, you will notice that if you click on the left side of the image (on the "3"), the trace correctly indicates that "layer" got clicked. However, if you click on the right side of the image (on the "D") you'll see that "manager" got clicked instead of "layer", which is wrong. If you click anywhere on the "layer" that is past 338px (the mask size), the mouse event does not seem to make it to the layer object.
If I remove the "x" offset on "layer", the whole layer works as expected, but as soon as I offset, anything past the width of the mask (338px) seems to not be captured properly, like if the mask wasn't offset. The strange thing is that if you click to the left of the image once it has been offset, you do get "manager", which means that the layer (and mask) did get moved.
Here's what I mean, with no offset, clicking on the green area returns "layer", clicking on the blue area returns "manager", which is the expected behaviour. See image
If I offset the layer by 220, the areas behave like this image
Sorry I can't embed the images here, but I don't have enough reputation :(
Figured it out.
Apparently the problem is that the Image that is used as the mask needs to be added to the display list before it gets set as the mask or this erratic behaviour happens. This is what the code looks like now and it's working:
<?xml version="1.0"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="init()"
>
<fx:Script>
<![CDATA[
import spark.core.MaskType;
public function init():void {
manager.addEventListener( MouseEvent.MOUSE_DOWN, handleMouseDown );
}
public function handleMouseDown(e:MouseEvent):void {
trace( e.target.id )
}
]]>
</fx:Script>
<s:Group id="manager" width="500" height="500">
<s:Group id="layer" mouseChildren="false" x="100" mouseEnabledWhereTransparent="false">
<s:Group id="content" mask="{maskImage}" maskType="{MaskType.ALPHA}" cacheAsBitmap="true">
<s:Rect width="338" height="162">
<s:fill>
<s:SolidColor color="0xDDAAAA" />
</s:fill>
</s:Rect>
</s:Group>
<mx:Image id="maskImage"
source="http://www.wpclipart.com/signs_symbol/alphabets_numbers/3D_text_T.png"
cacheAsBitmap="true"
/>
</s:Group>
</s:Group>
</s:Application>
Also of note is that the maskImage is a child of layer and not a child of content. Setting it as a child of the latter results in the same incorrect behaviour as before.

Resources