slightly weird question about ordering layers - apache-flex

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.

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" />
...

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.

Flex 4: How to skin a spark volumebar to make it work like a HSlider?

Can anyone point me in the direction of skinning a video player volumebar? I want a mute button on the left side and then an HSlider to the right that is always open (no popups).
I've managed to change the skin to use a custom track button and a custom thumb button and it mostly looks how I want.
I can't seem to figure out how to make the thumb slide horizontally across the track and how to hook it into the videoplayer. The thumb just wiggles a little up and down.
I realize I can make a separate HSlider and button and then attach them to the videoplayer controls, I was just hoping since the functionality is already built in that I could override a few skins and be done with it.
I had the same issue today and found that the solution was to reimplement VolumeBar extends VSlider so that HVolumeBar extends HSlider then alter updateSkinDisplayList() so the thumb position works correctly. I tried the rotation approach without success. I played with the skins a bit also.
override protected function updateSkinDisplayList():void
{
if (!thumb || !track)
return;
var thumbRange:Number = track.getLayoutBoundsWidth() - thumb.getLayoutBoundsWidth();
var range:Number = maximum - minimum;
// calculate new thumb position.
var thumbPosTrackX:Number;
// if muted, it's 0. otherwise, calculate it normally
// TODO (rfrishbe): should probably use setValue(0) and listen for CHANGE on the VideoPlayer
// instead of VALUE_COMMIT.
if (!muted)
thumbPosTrackX = (range > 0) ? (((pendingValue - minimum) / range) * thumbRange) : 0;
else
thumbPosTrackX = thumbRange;
// convert to parent's coordinates.
var thumbPos:Point = track.localToGlobal(new Point(thumbPosTrackX,0));
var thumbPosParentX:Number = thumb.parent.globalToLocal(thumbPos).x;
thumb.setLayoutBoundsPosition(Math.round(thumbPosParentX), thumb.getLayoutBoundsY() );
}
Edit: Horizontal Volume Bar Skin
<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fb="http://ns.adobe.com/flashbuilder/2009" minWidth="11" alpha.disabled=".5">
<!-- host component -->
<fx:Metadata>
/**
* #copy spark.skins.spark.ApplicationSkin#hostComponent
*/
[HostComponent("grammit.media.HorizontalVolumeBar")]
</fx:Metadata>
<fx:Script fb:purpose="styling">
/* Define the skin elements that should not be colorized. */
static private const exclusions:Array = ["muteButton", "track", "thumb"];
/**
* #private
*/
override public function get colorizeExclusions():Array {return exclusions;}
/**
* #private
*/
override protected function initializationComplete():void
{
useChromeColor = true;
super.initializationComplete();
}
</fx:Script>
<s:states>
<s:State name="normal" />
<s:State name="open" />
<s:State name="disabled" />
</s:states>
<!--- The PopUpAnchor control that contains the drop-down slider control. -->
<s:PopUpAnchor id="popup" displayPopUp.normal="false" displayPopUp.open="true" includeIn="open"
left="0" right="0" top="0" bottom="0" popUpPosition="left" itemDestructionPolicy="auto">
<!--- #copy spark.components.mediaClasses.VolumeBar#dropDown -->
<s:Group id="dropDown" width="84" height="28" verticalCenter="0">
<!-- dropshadow for the dropdown -->
<s:Rect left="0" top="0" right="0" bottom="0">
<s:filters>
<s:DropShadowFilter knockout="true" blurX="20" blurY="20" alpha="0.32" distance="11" angle="90" />
</s:filters>
<s:fill>
<s:SolidColor color="0x000000" />
</s:fill>
</s:Rect>
<!-- background for the popup -->
<s:Rect left="0" right="0" top="0" bottom="0">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0xFFFFFF"/>
<s:GradientEntry color="0xDCDCDC"/>
</s:LinearGradient>
</s:fill>
<s:stroke>
<s:SolidColorStroke color="0x000000" />
</s:stroke>
</s:Rect>
<!--- The skin pat that defines the drop-down slider track. -->
<s:Button id="track" verticalCenter="0" left="6" right="7" minWidth="33" width="100"
skinClass="grammit.skins.HorizontalVolumeBarTrackSkin" />
<!--- The skin pat that defines the thumb in the drop-down slider track. -->
<s:Button id="thumb" verticalCenter="0" width="11" height="11"
skinClass="spark.skins.spark.mediaClasses.normal.VolumeBarThumbSkin" />
</s:Group>
</s:PopUpAnchor>
<!--- #copy spark.components.mediaClasses.VolumeBar#muteButton -->
<s:MuteButton id="muteButton" left="0" right="0" top="0" bottom="0" focusEnabled="false"
skinClass="spark.skins.spark.mediaClasses.normal.MuteButtonSkin" />
</s:SparkSkin>
I couldn't find a good solution to this either so I just used rotation=90 to volumebar and rotation=-90 to what ever needed to be rotated back inside the volumebarskin.

Flex event propagation

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.

Resources