Flex/Accordion: Conditionally hide a child - apache-flex

How do I hide a child in a accordion? Using visible doesn't seem to work and enabled isn't what I'm after.
<mx:Accordion>
<mx:VBox width="100%" height="100%" label="Foo" id="viewOverview" visible="false">
...
</mx:VBox>
...
</mx:Accordion>

I think you can't hide it. Strange that the visible property doesn't work... Anyway, I would control the children through code and remove and insert them as needed by the app. Hiding:
function hideFoo():void {
this.theAccordion.removeChild(this.vboxFoo);
}
You'll probably want to keep a reference to the "hidden" child so that you can add it later again.

This isn't an answer, just some curious things I found out while trying to find another solution to this problem:
Accordion headers have a visible property and a setActualSize method. The latter takes a height and width, and setting each to zero...
acc.getHeaderAt(0).setActualSize(0,0);
...accomplishes the same thing as setting visible = false, that is it hides the contents of the header, but does not remove its area from the accordion. I was hoping to trick the accordion into hiding the child, but no such luck...nonetheless, it might be a path to continue to try. If I get more time I will continue to explore but I'm out of bandwidth at the moment...

You can also create a descendant of accordion with methods like showHeader, hideHeader, isHeaderHidden that contains hash table to keep track of hidden elements similar to the one below:
public class AccordionHideHeader extends Accordion
{
private var _hiddenHeader:Dictionary=new Dictionary();
public function AccordionHideHeader()
{
super();
}
public function hideHeader(header:DisplayObject):void
{
if (contains(header))
{
_hiddenHeader[header]=getChildIndex(header);
removeChild(header);
}
}
public function showHeader(header:DisplayObject):void
{
if (!contains(header))
{
addChildAt(header, _hiddenHeader[header]);
delete _hiddenHeader[header]
}
}
public function isHeaderHidden(header:DisplayObject):Boolean
{
for (var key:Object in _hiddenHeader)
{
if (key==header)
return true;
}
return false;
}
}

Sorry I'm not agree with removing child, because you will having problem when adding it back to its position in exact order.
Example: If you have 5 page in accordion, you remove child 1 and 3, now in any condition you want number 3 back to acordion how do you put it back? because the index is not 3 anymore (rember that 1 is removed too).
I found a good solution here. In short you make your own acordion with enalbe and disable ability where enable and disable define on the child container.
here i paste the acordion code:
/**
* http://blog.flexexamples.com/2008/05/30/preventing-users-from-clicking-on-an-accordion-containers-header-in-flex/
*/
package comps {
import mx.containers.accordionClasses.AccordionHeader;
import mx.events.FlexEvent;
public class MyAccHeader extends AccordionHeader {
public function MyAccHeader() {
super();
addEventListener(FlexEvent.INITIALIZE, accordionHeader_initialize);
}
private function accordionHeader_initialize(evt:FlexEvent):void {
enabled = data.enabled;
}
}
}
Maybe my answer not relevant anymore for you, but i hope can help someone else who face the same problem.

You can override Accordion logic and user includeInLayout property to control visibility of children.
This will work if you set all children in MXML.
import flash.events.Event;
import mx.containers.Accordion;
import mx.core.UIComponent;
public class DynamicAccordion extends Accordion
{
public function DynamicAccordion()
{
}
private var allChildern:Array;
override protected function childrenCreated():void
{
allChildern = new Array();
for (var i:int = numChildren - 1; i >= 0 ; i--)
{
var child:UIComponent = getChildAt(i) as UIComponent;
if (child)
{
child.addEventListener("includeInLayoutChanged", childIncludeLayoutChangedHandler);
if (!child.includeInLayout)
{
removeChild(child);
}
allChildern.push(child);
}
}
allChildern = allChildern.reverse();
super.childrenCreated();
}
private function childIncludeLayoutChangedHandler(event:Event):void
{
var child:UIComponent = event.currentTarget as UIComponent;
if (child.includeInLayout)
{
var index:int = allChildern.indexOf(child);
addChildAt(child, index);
}
else
{
removeChild(child);
}
}
}

I think you might have to actually remove the accordion child itself (e.g. using the State removeChild() mechanism). If you need to preserve the object itself, just keep a reference to it in a global variable.
Cheers

Accordion controls always have 1 child open. By opening another child, the current one will close.
If you want to have more than 1 child open at a time or have all children closed, you can use the VStack component available at: http://weblogs.macromedia.com/pent/archives/2007/04/the_stack_compo.html

<mx:Script>
<![CDATA[
private function hideFn():void
{
acc.removeChildAt(0);
}
private function showFn():void
{
acc.addChildAt(helloBox , 0);
}
]]>
</mx:Script>
<mx:VBox>
<mx:Accordion id="acc" width="200" height="200">
<mx:VBox id="helloBox" label="Test">
<mx:Label text="hello"/>
</mx:VBox>
<mx:VBox label="Test2">
<mx:Label text="hello again"/>
</mx:VBox>
</mx:Accordion>
<mx:Button label="hide" click="hideFn()"/>
<mx:Button label="show" click="showFn()"/>
</mx:VBox>

Here my solution :
http://weflex.wordpress.com/2011/01/25/flex-accordion-hideshow-headers/
I copied and modified the code of the Accordion, so if a child has its "includeInLayout" property to false, it won't be displayed.

Try this one
accrod.getHeaderAt(0).enabled=false;
accrod.getHeaderAt(0).visible=false;

Here is the solution, how to collapse the accordion on click of header.
<mx:Script>
<![CDATA[
import mx.events.IndexChangedEvent;
private var isAccordionClosed:Boolean;
private function myAccordion_clickHandler(event:MouseEvent):void
{
trace(event.currentTarget.label);
var selIdx:int = myAccordion.selectedIndex;
isAccordionClosed = (isAccordionClosed) ? false : true;
if (isAccordionClosed)
{
collapseAccordion(selIdx, !isAccordionClosed);
}
else
{
collapseAccordion(selIdx, !isAccordionClosed);
}
}
private function collapseAccordion(idx:int, showHide:Boolean):void
{
switch(idx)
{
case 0:
vb1.scaleX = vb1.scaleY = int(showHide);
break;
case 1:
vb2.scaleX = vb2.scaleY = int(showHide);
break;
case 2:
vb3.scaleX = vb3.scaleY = int(showHide);
break;
case 3:
vb4.scaleX = vb4.scaleY = int(showHide);
break;
case 4:
vb5.scaleX = vb5.scaleY = int(showHide);
break;
}
}
private function myAccordion_changeHandler(event:IndexChangedEvent):void
{
isAccordionClosed = true;
}
]]>
</mx:Script>
<mx:Accordion id="myAccordion" x="200" y="200" click="myAccordion_clickHandler(event)" resizeToContent="true"
width="399" verticalGap="0" change="myAccordion_changeHandler(event)">
<mx:VBox id="vb1" label="Chapter 1">
<mx:Label text="Accordion 1" width="397" textAlign="center" height="38"/>
</mx:VBox>
<mx:VBox id="vb2" label="Chapter 2">
<mx:Label text="Accordion 2" width="397" textAlign="center" height="43"/>
</mx:VBox>
<mx:VBox id="vb3" label="Chapter 3">
<mx:Label text="Accordion 3" width="397" textAlign="center" height="43"/>
</mx:VBox>
<mx:VBox id="vb4" label="Chapter 4">
<mx:Label text="Accordion 4" width="397" textAlign="center" height="43"/>
</mx:VBox>
<mx:VBox id="vb5" label="Chapter 5">
<mx:Label text="Accordion 5" width="397" textAlign="center" height="43"/>
</mx:VBox>
</mx:Accordion>

Related

Flex Scroller on swfloader dying on zoom

I have a swfloader object on to which i want to zoom into (with respect to a point).... this i achieved with some help on the internet.
But now i notice that when i zoom into a point the scroller on the swf loader doesnt work anymore....
Code i am using below... any ideas on how i could correct this problem???
<s:Scroller id="scrollme" width="100%" height="100%" >
<s:HGroup id="mapView" width="100%" height="100%" clipAndEnableScrolling="true" >
<s:SWFLoader id="img" autoLoad="true" addedToStage="img_addedToStageHandler(event)" click="img_clicked(event)" maintainAspectRatio="true" includeIn="normal" />
</s:HGroup>
</s:Scroller>
and the actionscript bit
protected function onZoom(event:TransformGestureEvent):void
{
event.stopImmediatePropagation();
scaleAt(event.scaleX,event.localX,event.localY)
}
public function scaleAt( scale : Number, originX : Number, originY : Number ) : void
{
// get the transformation matrix of this object
affineTransform = img.content.transform.matrix;
//transform.matrix
trace("zooming to " + scale)
// move the object to (0/0) relative to the origin
affineTransform.translate( -originX, -originY )
// scale
affineTransform.scale( scale, scale )
// move the object back to its original position
affineTransform.translate( originX, originY )
// apply the new transformation to the object
img.content.transform.matrix = affineTransform;
//checkscroller();
}
protected function img_addedToStageHandler(event:Event):void
{
Multitouch.inputMode = MultitouchInputMode.GESTURE;
if (!Multitouch.supportsGestureEvents)
currentState = "normal";
else
{
currentState = "normal";
for each (var item:String in Multitouch.supportedGestures)
{
if (item == TransformGestureEvent.GESTURE_PAN)
img.addEventListener(TransformGestureEvent.GESTURE_PAN, onPan);
/* else if (item == TransformGestureEvent.GESTURE_ROTATE)
img.addEventListener(TransformGestureEvent.GESTURE_ROTATE, onRotate); */
else if (item == TransformGestureEvent.GESTURE_SWIPE)
img.addEventListener(TransformGestureEvent.GESTURE_SWIPE, onSwipe);
else if (item == TransformGestureEvent.GESTURE_ZOOM)
img.addEventListener(TransformGestureEvent.GESTURE_ZOOM, onZoom);
}
}
}
Not sure I understand what you're doing. You're using SWFLoader to load an image? Why not just sure the Image component with a source of the url to the image.
Either way, you can't have your HGroup wrapping your component and have clipAndEnableScrolling set to true. Remove that property and you should be good.
<s:Scroller id="scrollme" width="100%" height="100%" >
<s:HGroup id="mapView">
<s:SWFLoader id="img" autoLoad="true" addedToStage="img_addedToStageHandler(event)" click="img_clicked(event)" maintainAspectRatio="true" includeIn="normal" />
</s:HGroup>
</s:Scroller>

Passing a state to the child button of a Flex4 ButtonBar

I have a Spark ButtonBar that has a custom skin, which defines a custom skin for the "middleButton" requirement. My CustomButtonBarSkin has a custom state, minimized, which I want to pass into my middleButton skin so it can modify its design.
Is it possible to do this? I can see that my button skin could use parentDocument.currentState to get the minimized state, but that's really ugly. Any way to pass a skin from the bar to the child button(s)?
I think you should extend default ButtonBar. Something like this:
package
{
import mx.core.IFactory;
import spark.components.ButtonBar;
[SkinState("minimized")]
[SkinState("minimizedDisabled")]
public class MinimizableButtonBar extends ButtonBar
{
public function MinimizableButtonBar()
{
super();
itemRendererFunction = defaultButtonBarItemRendererFunction;
}
[SkinPart(required="true", type="mx.core.IVisualElement")]
public var middleButtonMinimized:IFactory;
private var _minimized:Boolean;
[Bindable]
public function get minimized():Boolean
{
return _minimized;
}
public function set minimized(value:Boolean):void
{
if (_minimized == value)
return;
_minimized = value;
invalidateSkinState();
itemRendererFunction = defaultButtonBarItemRendererFunction;
}
override protected function getCurrentSkinState():String
{
if (_minimized)
return enabled ? "minimized" : "minimizedDisabled";
return super.getCurrentSkinState();
}
private function defaultButtonBarItemRendererFunction(data:Object):IFactory
{
var i:int = dataProvider.getItemIndex(data);
if (i == 0)
return firstButton ? firstButton : (_minimized ? middleButtonMinimized : middleButton);
var n:int = dataProvider.length - 1;
if (i == n)
return lastButton ? lastButton : (_minimized ? middleButtonMinimized : middleButton);
return (_minimized ? middleButtonMinimized : middleButton);
}
}
}
So using this code you can declare your custom skin with the following way:
<?xml version="1.0" encoding="utf-8"?>
<s:Skin alpha.disabledGroup="0.5" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Metadata>[HostComponent("MinimizableButtonBar")]</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" stateGroups="disabledGroup" />
<s:State name="minimized" stateGroups="minimizedGroup" />
<s:State name="minimizedDisabled" stateGroups="disabledGroup,minimizedGroup" />
</s:states>
<fx:Declarations>
<fx:Component id="firstButton">
<s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarFirstButtonSkin" />
</fx:Component>
<fx:Component id="middleButton">
<s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarMiddleButtonSkin" />
</fx:Component>
<fx:Component id="middleButtonMinimized">
<s:ButtonBarButton skinClass="MinimazedButtonBarMiddleButtonSkin" />
</fx:Component>
<fx:Component id="lastButton">
<s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarLastButtonSkin" />
</fx:Component>
</fx:Declarations>
<s:DataGroup height="100%" id="dataGroup" width="100%">
<s:layout>
<s:ButtonBarHorizontalLayout gap="-1" />
</s:layout>
<s:layout.minimizedGroup>
<s:VerticalLayout />
</s:layout.minimizedGroup>
</s:DataGroup>
</s:Skin>
Hope this solves your problem.
And if your minimized state is only about changing middle button skin you can remove all states related code both from custom component and from skin.
I was working with skinning the button bar recently and wanted to expand on/remove some of the default behavior. Rather than extend & overwrite or copy/paste the ButtonBar code I just rolled my own minimalistic component:.
public class HButtonBarGroup extends HGroup {
public function HButtonBarGroup() {
addEventListener(ElementExistenceEvent.ELEMENT_ADD, refreshSkins);
super();
gap = -1;
}
private function refreshSkins(event : * = null) : void {
var buttonCount : int = numElements;
for (var i : int = 0; i < buttonCount; i++) {
var button : Button = getElementAt(i) as Button;
var skinClass : Class
if ((buttonCount == 0) || (buttonCount > 2 && (i != 0 && i != buttonCount)))
skinClass = GreyButtonBarMiddleButtonSkin;
else if (i == 0)
skinClass = GreyButtonBarFirstButtonSkin;
else
skinClass = GreyButtonBarLastButtonSkin;
Button(getElementAt(i)).setStyle("skinClass", skinClass);
}
}
}
This would give you the ability to do most anything you want without having to tiptoe around ButtonBar, ButtonBarBase, and ButtonBarSkin - all unnecessary unless you want togglebutton/selectedIndex. IMO it is a pain to create buttons based on a dataProvider instead of just declaring buttons in MXML and assigning handlers and other properties there.
I recently needed to change skin on a component based on its parents state. I used the same solution I would have used in HTML, using CSS. In your case, something like:
s|ButtonBar:minimized s|ButtonBarButton {
skinClass: ClassReference("CustomButtonBarSkin");
}
s|ButtonBarButton {
skinClass: ClassReference("spark.skins.spark.ButtonBarMiddleButtonSkin");
}
:minimized is the Pseudo Selector (for States).
Unfortunately, this didn't seem to get picked up by child (bug?) unless I changed styleName on parent element on state change:
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"
styleName.normal="foo" styleName.minimized="foo"
>
Maybe there is some invalidate-method I should have called on parents state change instead to make child pick up the change, but merely change the styleName to something bogus did the trick.
This is maybe not a widely used technique in Flex due to the fact that Flex 3 only supported basic CSS selectors.
Maybe I'm not getting what you're trying to do exactly, but it seems fairly obvious and easy to me. Just have your custome button bar skin set the state of your middle button when the minimized state is active:
<s:Skin>
<s:states>
<s:State name="minimized" />
</s:states>
<s:ButtonBarButton currentState.minimized="someState" />
</s:Skin>
Get it?

PopUpButton with TileList and custom renderer

I have prepared a simple test case for a PopUpButton opening a TileList with black and red entries and it mostly works, but has 2 annoyances.
I've searched a lot, tried several variants (added [Bindable] members in my renderer; added color member to the bids array; created my public override set data() method; ...) and has been getting some answers too, but they are way too general.
I would appreciate if someone can suggest code to fix the 2 issues in my code:
1) Scrolling "tl2" right-left doesn't work well: the entries are displayed in a mix of red and black. I know the TileList reuses itemRenderer, but how do I fix the problem?
2) In debug-mode I get numerous warnings:
warning: unable to bind to property 'label' on class 'Object' (class is not an IEventDispatcher)
Thank you,
Alex
MyRenderer.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
width="100%" height="100%">
<mx:Script>
<![CDATA[
public static function findColor(str:String):uint {
return (str.indexOf('♥') != -1 ||
str.indexOf('♦') != -1) ? 0xFF0000 : 0x000000;
}
]]>
</mx:Script>
<mx:Label truncateToFit="true" width="60"
text="{data.label}" color="{findColor(data.label)}"/>
</mx:Canvas>
MyTest.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationPolicy="all" applicationComplete="init(event);">
<mx:Style>
#font-face {
src:url("C:\\WINDOWS\\Fonts\\arial.ttf");
fontFamily: myFont;
unicodeRange:
U+0020-U+0040, /* Punctuation, Numbers */
U+0041-U+005A, /* Upper-Case A-Z */
U+005B-U+0060, /* Punctuation and Symbols */
U+0061-U+007A, /* Lower-Case a-z */
U+007B-U+007E, /* Punctuation and Symbols */
U+0410-U+0451, /* cyrillic */
U+2660-U+266B; /* card suits */
}
List, CheckBox, Label, Button, PopUpButton, TileList {
fontFamily: myFont;
fontSize: 24;
}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.controls.*;
import mx.events.*;
[Bindable]
private var bids:Array;
private var tl:TileList;
private function init(event:FlexEvent):void {
bids = createBids();
pub.popUp = createList(bids);
}
private function createBids():Array {
var arr:Array = [{label: 'Pass'}];
for (var i:uint = 6; i <= 10; i++)
for (var j:uint = 0; j < 5; j++)
arr.unshift({label: i+'♠♣♦♥ '.charAt(j%5)});
return arr;
}
private function createList(arr:Array):TileList {
tl = new TileList();
tl.maxColumns = 5;
tl.width = 350;
tl.height = 250;
tl.dataProvider = arr;
tl.itemRenderer = new ClassFactory(MyRenderer);
tl.addEventListener('itemClick', itemClickHandler);
if (arr.length > 0) {
tl.selectedIndex = arr.length - 1;
pub.label = arr[tl.selectedIndex].label;
}
return tl;
}
private function itemClickHandler(event:ListEvent):void {
var index:uint = tl.columnCount * event.rowIndex + event.columnIndex;
var label:String = bids[index].label;
pub.label = label;
pub.setStyle('color', MyRenderer.findColor(label));
pub.close();
tl.selectedIndex = index;
}
]]>
</mx:Script>
<mx:Panel title="TileList scrolling problem" height="100%" width="100%"
paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10">
<mx:Label width="100%" color="blue" text="Select your bid:"/>
<mx:TileList id="tl2" height="200" width="200"
maxColumns="5" rowHeight="30" columnWidth="60"
dataProvider="{bids}" itemRenderer="MyRenderer"/>
</mx:Panel>
<mx:ApplicationControlBar width="100%">
<mx:Spacer width="100%"/>
<mx:CheckBox id="auto" label="Auto:"/>
<mx:Button id="left" label="<<"/>
<mx:PopUpButton id="pub" width="90"/>
<mx:Button id="right" label=">>"/>
</mx:ApplicationControlBar>
</mx:Application>
Update:
Thank you Wade, the warning is gone now (I guess it was not ok to use {data.label} in my label), but the "tl2" still has scrolling issues.
New MyRenderer.mxml (still has scrolling issues):
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
width="100%" height="100%">
<mx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
var str:String = String(value.label);
myLabel.text = str;
myLabel.setStyle('color', findColor(str));
}
public static function findColor(str:String):uint {
return (str.indexOf('♥') != -1 ||
str.indexOf('♦') != -1) ? 0xFF0000 : 0x000000;
}
]]>
</mx:Script>
<mx:Label id="myLabel" truncateToFit="true" width="60"/>
</mx:Canvas>
You can take care of both of your issues by overriding the set data method on your item renderer:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
width="100%" height="100%">
<mx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
var str:String = value.label;
this.myLabel.text = str;
this.myLabel.setStyle("color", (str.indexOf('♥') != -1 ||
str.indexOf('♦') != -1) ? 0xFF0000 : 0x000000);
}
]]>
</mx:Script>
<mx:Label id="myLabel" truncateToFit="true" width="60"/>
</mx:Canvas>
Since the renderers are re-used, the best way to ensure they are correctly updated is to use the set data method since it always gets called when a renderer gets re-used. This also gets rid of your binding warning since you are no longer binding to data.label. Note: I haven't tested this code, it may need some tweaking :) Hope that helps.
EDIT: Your "tl2" issue looks like it's caused by horizontally scrolling your tile list, whereas the TileList appears to be optimized for vertical scrolling. Since your data set is finite and relatively small, I would make the tile list full size to show all of the elements (eliminating item renderer re-use) and wrap it in a canvas set to the desired dimensions and let the canvas handle the scrolling. Probably not the answer you are looking for, sorry.

Flex spark Percent height not working

First of all I know there is a spark VolumeBar component but, for design requirements I can't use it..
I'm trying to create a custom component but heights are not responding as should
[Update]
This is were I call the class
<components:VolumeSlider steps="4" height="100" />
The problem is that the volume slider is adapting perfectly, but My custom items component doesn't.
<s:HGroup width="100%" height="100%" maxHeight="{height}" >
<s:VGroup width="100%" height="100%" paddingBottom="20" paddingTop="20">
<s:VSlider id="slider" width="100%" height="100%" maximum="{_steps-1}" />
</s:VGroup>
<s:VGroup id="items" width="100%" height="100%" />
</s:HGroup>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
private var _steps:uint = 10;
public function set steps( value:uint ):void
{
_steps = value;
if ( items != null && items.numChildren != 0 )
{
items.removeAllElements();
}
create();
}
private function create():void
{
for ( var i:uint = 0; i < _steps; ++i )
{
var item:VolumeSliderItem = new VolumeSliderItem();
item.percentHeight = item.percentWidth = 100;
if ( items != null )items.addElement(item );
}
}
]]>
</fx:Script>
where VolumeSliderItem is a spark button
I don't see any call to create(). I added 'creationComplete="create()"' on the Application tag, and then it created 10 sliders to the VGroup with id 'items'. Is that what you're looking for?

Not include component size in measure

I have a custom component that is basically a VBox with a Label and a TextField.
<mx:VBox width="50%">
<mx:Label width="100%"/>
<mx:TextField width="100%"/>
</mx:VBox>
What I want, basically, is to have two of these VBoxes layed out on a HBox and each would take exactly 50% - their children Label and TextField should just obey that.
If I set both Label and TextField's width to 100%, and the Label text doesn't fit, the default behaviour is to expands the VBox width - I don't want that to happen. The TextField should always take 100% of the width, and I'd want the Label to be explicitly set to the width of the TextField, so the text would be truncated and not expand the VBox width.
Is there a way to tell the Label to just obey the VBox (or TextField) width and not be included in the measurement of the VBox width?
Not sure if I was clear. :)
Thanks in advance.
it wasn't that easy as I thought. At the beginning I wanted to suggest maxWidth but it doesn't work correctly with label. However I just tested this:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.controls.TextInput;
import mx.controls.Label;
private function onTextChanged(event:Event):void {
var currentText:String = TextInput(event.target).text;
var shortened:Boolean = false;
// adding 30 to leave some space for ...
while ( lbl.measureText(currentText).width > (lbl.width-30) ) {
currentText = currentText.substr(0,currentText.length-1);
shortened = true;
}
lbl.text = currentText + ((shortened) ? "..." : "" );
}
]]>
</mx:Script>
<mx:VBox width="50%">
<mx:Label id="lbl" width="100%" />
<mx:TextInput width="100%" change="onTextChanged(event);" />
</mx:VBox>
</mx:WindowedApplication>
It probably isn't written in a way (just attributed) you expected but it does what you need.
If this isn't a solution you could think of extending the Label in the following manner.
Crete custom Label:
radekg/MyLabel.as
package radekg {
import mx.controls.Label;
public class MyLabel extends Label {
public function MyLabel() {
super();
}
private var _myText:String;
override public function get text():String {
return _myText;
}
override public function set text(value:String):void {
if ( value != null && initialized ) {
// store the copy so the getter returns original text
_myText = value;
// shorten:
var shortened:Boolean = false;
while ( measureText(value).width > (width-30) ) {
value = value.substr(0,value.length-1);
shortened = true;
}
super.text = value + ((shortened) ? "..." : "");
}
}
}
}
And use it like that:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:controls="radekg.*">
<mx:VBox width="50%">
<controls:MyLabel width="100%" id="lbl" text="{ti.text}" />
<mx:TextInput width="100%" id="ti" />
<mx:Button label="Display label text" click="mx.controls.Alert.show(lbl.text)" />
</mx:VBox>
</mx:WindowedApplication>
This can be used simply with binding. Once you type very long text into the text input and the Label displays ... click on the button. You'll notice that text() getter returns original text.
Hope this helps.

Resources