flex prevent selection state on spark list - apache-flex

I have an ItemRenderer in which the selected state should be disabled (I'm using the renderer states and I don't have a selected state).
The problem is that the list (spark) resets the item renderer state upon click even though I don't have a "selected" state.
I want to fully prevent this behavior but I don't know how.
My renderer has autoDrawBackground set to false but it has to be enabled (although enabled=false fixes this issue)
Also, the renderer has several children including a list of its own.
Setting mouseEnabled="false" on the renderer fixes the renderer itself but not its children, and I need some of the children to be mouse enabled.
Edit:
Following is an excerpt from my item renderer:
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
width="100%" autoDrawBackground="false">
<s:states>
<s:State name="normal" />
<s:State name="suitable" />
<s:State name="mine" />
<s:State name="deleted" />
</s:states>
<s:Rect id="rect" top="0" right="0" bottom="0" left="0">
<s:fill>
<s:SolidColor id="background"
alpha=".8" alpha.deleted=".4"
color="0xff0000" color.suitable="0x00ff00" color.mine="0x0000ff" />
</s:fill>
</s:Rect>
<s:Label id="name" left="4" top="4" right="40" />
<s:List id="myList" left="4" top="40" right="4"
contentBackgroundAlpha="0" borderVisible="false" horizontalScrollPolicy="off">
<s:layout>
<s:VerticalLayout gap="3" paddingBottom="4" requestedMinRowCount="2" />
</s:layout>
</s:List>
</s:ItemRenderer>
Second Edit:
I had the same problem with the mouse hover state but that seems to have a workaround:
override protected function set hovered(value:Boolean) : void
{
// do nothing (prevent current state from changing to "hovered" state)
}

I'm unclear if you want to prevent an item from being selected; or just prevent the visual properties that go along with an item being selected.
To handle the visual aspects; I'd try to override the getCurrentRendererState() method. Something like this:
override protected function getCurrentRendererState():String{
if (selected && hasState("selected"))){
return "normal"; // or whatever state you want it to be
}
if (selected && down && hasState("downAndSelected")){
return "normal"; // or whatever state you want it to be
}
super.getCurrentRendererState()
}
In theory, this should prevent your renderer from ever going into a selected state.

Related

Double click event not fired/triggered when double clicking on left side of tree node

I have the following Flex tree
<mx:Tree id="Tree" left="0" right="0" top="0" bottom="0"
alternatingItemColors="[#EEEEEE, white]" dataProvider="{lsEspecie}"
dragEnabled="true" dragMoveEnabled="true" dropEnabled="true" labelField="item"
labelFunction="tree_labelFunc" showRoot="false"
doubleClickEnabled="true" doubleClick="Tree_DoubleClick(event)">
</mx:Tree>
When I double click on the ICON and to the right of side on any node, double click event is being fired as expected. But when clicking on any part of the left side of the node double click is not being fired
Is there any way to make the double click event fire when is happening to the left side of the node ?
Looks like a bug with mx:Tree. I added mouse over and mouse out handlers to my test application to trace what was going on. In certain cases double click was working in the red area you indicated and I saw that my mouse was over the Tree's selectionLayer. After I clicked and moved the mouse a bunch it would randomly stop accepting double-click and at that point my mouse was always over some SpriteAsset in the selectionLayer with mouseEnabled=true.
I fixed the problem by subclassing Tree and forcing all the children of the selectionLayer to have mouseEnabled=false:
import flash.display.InteractiveObject;
import flash.events.Event;
import mx.controls.Tree;
import mx.core.mx_internal;
use namespace mx_internal;
public class FixedTree extends Tree {
public function FixedTree() {
super();
}
override protected function createChildren():void {
super.createChildren();
getListContentHolder().selectionLayer.addEventListener(Event.ADDED, selectionLayerChildAddedHandler);
}
private function selectionLayerChildAddedHandler(e:Event):void {
// ListBase:5790 creates a SpriteAsset for the selectionLayer but does not set mouseEnabled to false
InteractiveObject(e.target).mouseEnabled = false;
}
}
I'm not 100% sure if this is the correct way to fix this bug but it got double click working for me and I don't see an side-effects yet.
You can create an itemrenderer for the tree and inside it add a doubleclick listener to its HGroup.
eg-
protected function mxtreeitemrenderer1_creationCompleteHandler(event:FlexEvent):void
{
mygrp.addEventListener(MouseEvent.DOUBLE_CLICK,dblclk);
}
private function dblclk(event:MouseEvent):void
{
Alert.show("doubleclicked");
}
]]>
</fx:Script>
<s:states>
<s:State name="normal" />
<s:State name="hovered" />
<s:State name="selected" />
</s:states>
<s:HGroup id="mygrp" left="0" right="0" top="0" bottom="0" verticalAlign="middle">
<s:Rect id="indentationSpacer" width="{treeListData.indent}" percentHeight="100" alpha="0">
<s:fill>
<s:SolidColor color="0xFFFFFF" />
</s:fill>
</s:Rect>
<s:Group id="disclosureGroup">
<s:BitmapImage source="{treeListData.disclosureIcon}" visible="{treeListData.hasChildren}" />
</s:Group>
<s:BitmapImage source="{treeListData.icon}" />
<s:Label id="labelField" text="{treeListData.label}" paddingTop="2"/>
</s:HGroup>

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

Spark datagrid with checkbox does not update correctly

The checkboxes are updated correctly when I select one or more datagrid rows but when I select a checkbox for the first time the checkbox does not refresh until the pointer moves out of the datagrid row. How can I fix this?
<?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">
<s:DataGrid id="dg" x="344" y="48" selectionMode="multipleRows" requestedRowCount="4">
<s:columns>
<s:ArrayList>
<s:GridColumn>
<s:itemRenderer>
<fx:Component>
<s:GridItemRenderer>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import spark.components.DataGrid;
override public function prepare(hasBeenRecycled:Boolean):void
{
cb.selected = grid.selectionContainsIndex(rowIndex);
}
]]>
</fx:Script>
<s:CheckBox id="cb" label="" horizontalCenter="0"/>
</s:GridItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:GridColumn>
<s:GridColumn dataField="dataField1" headerText="Column 1"></s:GridColumn>
<s:GridColumn dataField="dataField2" headerText="Column 2"></s:GridColumn>
<s:GridColumn dataField="dataField3" headerText="Column 3"></s:GridColumn>
</s:ArrayList>
</s:columns>
<s:typicalItem>
<fx:Object dataField1="Sample Data" dataField2="Sample Data" dataField3="Sample Data"></fx:Object>
</s:typicalItem>
<s:ArrayList>
<fx:Object dataField1="data1" dataField2="data1" dataField3="data1"></fx:Object>
<fx:Object dataField1="data2" dataField2="data2" dataField3="data2"></fx:Object>
<fx:Object dataField1="data3" dataField2="data3" dataField3="data3"></fx:Object>
<fx:Object dataField1="data4" dataField2="data4" dataField3="data4"></fx:Object>
</s:ArrayList>
</s:DataGrid>
</s:Application>
Change this:
<s:CheckBox id="cb" label="" horizontalCenter="0"/>
To:
<s:CheckBox id="cb" label="" horizontalCenter="0" enabled="false"/>
I just recommend you to use enabled property.
I think the dispatched "click event" from both checkbox and gridColumn, then returned function prevented each other.
If enabled property set false, your click event dispathed on only gridColumn then using cb.selected=grid.selectionContainsIndex(rowIndex); correctly occupy if you want to show checkbox enabled, you can use CSS or skinclass
The easiest way is to just use the renders selected state as suggested by RIAStar. However if you are using global skinning doing the custom drawing does not work, use either a skinnable container or as I have just put the checkbox in there but dont make it respond to mouse commands. For the multi selection, as long as your grid is setup to multiple rows or columns you can simply capture the mouse events and force the ctrl key which makes them handle multi select.
<s:GridItemRenderer
mouseDown="mouseDownHandler(event)"
mouseUp="mouseUpHandler(event)"
buttonMode="true"
mouseChildren="false"
useHandCursor="true"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
>
<s:states>
<s:State name="normal"/>
<s:State name="selected"/>
</s:states>
<fx:Script>
<![CDATA[
protected function mouseUpHandler(event:MouseEvent):void {
event.ctrlKey = true;
}
protected function mouseDownHandler(event:MouseEvent):void {
event.ctrlKey = true;
}
]]>
</fx:Script>
<s:CheckBox
id="check"
selected.normal="false"
selected.selected="true"
horizontalCenter="0"
verticalCenter="0"
/>
</s:GridItemRenderer>
I ended up simply doing this:
<s:GridColumn dataField="myBoolean" headerText="Returned" width="55">
<s:itemRenderer>
<fx:Component>
<s:GridItemRenderer>
<s:CheckBox id="cb1" selected="{data.myBoolean}" change="{data.myBoolean=cb1.selected}"/>
</s:GridItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:GridColumn>
You can just fake the CheckBox by drawing a CheckBox shape in the ItemRenderer and use the states to show the tick.
<s:GridItemRenderer>
<s:states>
<s:State name="normal" />
<s:State name="hovered" />
<s:State name="selected" />
</s:states>
<!-- checkbox graphics -->
<s:Group width="16" height="16" horizontalCenter="0" verticalCenter="0">
<s:Rect left="0" right="0" top="0" bottom="0">
<s:fill>
<s:SolidColor color="0xffffff" />
</s:fill>
<s:stroke>
<s:SolidColorStroke color="0xa9aeb2" />
</s:stroke>
</s:Rect>
<!-- tick, only shown when selected -->
<s:Rect includeIn="selected" width="8" height="8" horizontalCenter="0" verticalCenter="0">
<s:fill>
<s:SolidColor color="0x90b40c" />
</s:fill>
</s:Rect>
</s:Group>
</s:GridItemRenderer>
This is a simplified graphic for a checkbox, but you can go grab the code from the spark CheckBoxSkin and copy/paste it in the itemrenderer. Just might have to change some state names.
This will not deselect a single row though when you hit the CheckBox of an already selected row, unless you hold the CTRL key down. That's the default behavior of the DataGrid component. I'm afraid you'll have to create your own subclass of DataGrid if you want to prevent that behavior.
Another important thing to know: setting the selected property on the itemrenderers doesn't change the selectIndices of the DataGrid. Hence on the next commitProperties() cycle the value you set in the renderer will be overridden by the DataGrid.
Old answer: (before edit)
The ItemRenderer class (and thus the GridItemRenderer class too) has a selected property.
So you could bind the checkboxes selected property to the itemrenders, like so:
<s:CheckBox selected="{selected}" horizontalCenter="0" />
You'd have to create a separate ItemRenderer class for that to work though instead of an inline one.
If you absolutely want to go the inline way you can always override the selected setter.
<s:GridItemRenderer>
<fx:Script>
<![CDATA[
override public function set selected(value:Boolean):void {
super.selected = cb.selected = value;
}
]]>
</fx:Script>
<s:CheckBox id="cb" horizontalCenter="0"/>
</s:GridItemRenderer>

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)

mouseEnabledWhereTransparent not working as expected

I just cannot understand why mouseEnabledWhereTransparent does not work on this skin.
The Skin this creates is basically a Button with a transparent background and a little triangle to the left side, like so: >ButtonText But the empty space around the Triangle does not receive mouse events.
I've tried wrapping another group around the triangle path and i've tried wrapping it into a Graphic Object, also without success. I could create a Rect with 0 alpha below everything, but isn't that exactly what mouseEnabledWhereTransparent should be doing for me?
<?xml version="1.0" encoding="utf-8"?>
<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="21" minHeight="21" alpha.disabled="0.5"
mouseEnabledWhereTransparent="true">
<!-- host component -->
<fx:Metadata>
<![CDATA[
[HostComponent("spark.components.Button")]
]]>
</fx:Metadata>
<!-- states -->
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>
<!-- triangle tip -->
<s:Path data="M 0 0 L 0 14 L 10 7 L 0 0" bottom="5">
<s:fill>
<s:SolidColor color="0xFFFFFF" />
</s:fill>
</s:Path>
<!-- text -->
<s:Label id="labelDisplay"
textAlign="center"
verticalAlign="bottom"
maxDisplayedLines="1"
left="14" right="10" top="2" bottom="2" color="0x000000" fontSize="14">
</s:Label>
</s:SparkSkin>
Yo, what you need is to add a rectangle before that path to act as a hit area, this works:
<s:Rect left="0" right="0" top="0" bottom="0" >
<s:fill>
<s:SolidColor alpha="0" />
</s:fill>
</s:Rect>
This guy here has a good example of making custom buttons (minus the label)
mouseEnabledWhereTransparent has no effect if there are no mouse event listeners on the Group it is set on. In your example the Button object has a click event listener, but the Button's skin object (SparkSkin) does not.
From the ASDoc for GroupBase.mouseEnabledWhereTransparent:
"This property only goes in to effect if mouse, touch, or flash player gesture events are added to this instance."
A workaround for you would be to add a mouse event listener to the skin that won't do anything, for example:
<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
mouseEnabledWhereTransparent="true" click="doNothing()">
<fx:Script>
<![CDATA[
protected function doNothing():void {
trace('doNothing');
}
]]>
</fx:Script>
...
</s:SparkSkin>
The main use case for this property has been adding click events directly to a Group, but it is interesting to think about the case you bring up. It might be reasonable to file an enhancement request along the lines of "Setting mouseEnabledWhereTransparent on a skin should have an effect if the hostComponent has mouse listeners attached".

Resources