Flex TextArea htmlText with stylesheet click bug - apache-flex

This bug is hard to describe, but easily reproduced with the bottom code. Just copy, paste, and compile+run in Flex 3 and you'll see the problem. Anyone know of a work around?
Edit: Here is a link to a running demo: http://shiinaringo.se/hosted/flex/textarea-bug/HtmlTextBug.html
In the demo, the default color of TextArea is set to red.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal" applicationComplete="applicationComplete(event);">
<mx:Script>
<![CDATA[
import mx.events.FlexEvent;
private function applicationComplete(event:Event):void {
var styles:String = "a:hover { color: #6666ff; text-decoration: underline; } a { color: #0000ff; }";
var ss:StyleSheet = new StyleSheet();
ss.parseCSS(styles);
textGreenStylesheet.styleSheet = ss;
}
private function enteredText(event:FlexEvent):void {
textDefault.htmlText = event.currentTarget.text;
textGreen.htmlText = event.currentTarget.text;
textGreenStylesheet.htmlText = event.currentTarget.text;
}
]]>
</mx:Script>
<mx:VBox height="100%" width="400" horizontalAlign="center">
<mx:Panel width="250" height="200" layout="absolute" title="TextArea A. Default colored text">
<mx:TextArea id="textDefault" condenseWhite="true" width="100%" height="100%" x="0" y="0">
<mx:htmlText>
<![CDATA[
This text has the default text color of the TextArea control.
]]>
</mx:htmlText>
</mx:TextArea>
</mx:Panel>
<mx:Panel width="250" height="200" layout="absolute" title="TextArea B. Green text">
<mx:TextArea id="textGreen" condenseWhite="true" width="100%" height="100%" x="0" y="0" color="green">
<mx:htmlText>
<![CDATA[
This text has the text color set to green
]]>
</mx:htmlText>
</mx:TextArea>
</mx:Panel>
<mx:Panel width="250" height="200" layout="absolute" title="TextArea C. Green text + stylesheet">
<mx:TextArea id="textGreenStylesheet" condenseWhite="true" width="100%" height="100%" x="0" y="0" color="green">
<mx:htmlText>
<![CDATA[
This text has the text color set to green, and also uses a stylesheet to make links blue and underlined when hovered.
]]>
</mx:htmlText>
</mx:TextArea>
</mx:Panel>
<mx:TextInput x="69" y="282" width="207" enter="enteredText(event);"/>
</mx:VBox>
<mx:VBox height="100%" width="200">
<mx:Text width="166" text="We have three TextArea controls. The top uses default text color, the middle one uses defined green text color, the bottom one also uses green color, but also uses a stylesheet to define some custom coloring of A tags." height="232"/>
<mx:Text width="166" text="To reproduce the problem, first try to just enter new text in the input field in the bottom, and press enter. The text in the three boxes will update. Notice that the colors and other styles don't change in any of the three boxes. But when you click once inside textarea C, then enter new text in the input field and hit enter, you'll notice that the color and font is lost in textarea C. Bug?" height="232"/>
</mx:VBox>
</mx:Application>

Basically, StyleSheet and TextFormat doesn't go together in a flash textfield.
Following is my guestimate of what might be happening:
The color="green" will become part of the defaultTextFormat of the internal TextField of the TextArea and will be applied to the text well before applicationComplete is fired. You can verify this by tracing trace(textGreenStylesheet.htmlText); in the application complete handler (before you set the stylesheet). Here is what I got:
<TEXTFORMAT LEADING="2"><P ALIGN="LEFT"><FONT FACE="Verdana" SIZE="10" COLOR="#008000" LETTERSPACING="0" KERNING="0">This text has the text color set to green, and also uses a stylesheet to make links blue and underlined when hovered. </FONT></P></TEXTFORMAT>
Now when you apply the stylesheet, the color remains unchanged (green) as the stylesheet do not specify any color for the whole text.
When you click on the TextArea, I believe flex recalculates properties of it (may be click triggers an invalidation - I am not sure what's happening underneath). While doing this, compiler finds that a stylesheet has been applied and ignores the color="green" attribute. Now, these new properties are applied only when the text/htmltext property is changed (later by hitting enter). So unless you click or somehow trigger an invalidation of textarea, it retains the default color specified before applying the stylesheet.
If you add .yellow{color:#ffff00;} to the stylesheet and enclose some text in the third text area with <span class="yellow">some text</span> tags, you can see that the enclosed text retains yellow color whether you click on it or not.

Here's what I'm doing to resolve this. It's a big hack, but it does work.
import flash.events.Event;
import flash.text.TextFormat;
import mx.controls.Text;
import flash.text.StyleSheet;
import mx.core.mx_internal;
import mx.events.FlexEvent;
public class SupText extends Text
{
use namespace mx_internal;
public var linkColor:String = "#355EBF";
private var format:TextFormat;
public function SupText()
{
super();
this.addEventListener(FlexEvent.CREATION_COMPLETE, function(e:Event):void { setStyleSheet(); });
}
override public function set htmlText(value:String):void {
if (format != null) {
//glorious hack for style problem
textField.styleSheet = null;
textField.defaultTextFormat = format;
setStyleSheet();
}
super.htmlText = value;
if (textField.defaultTextFormat.font != "Times New Roman") {
format = textField.defaultTextFormat;
}
}
public function setStyleSheet():void {
var ss:StyleSheet = textField.styleSheet;
if(textField.styleSheet == null){
textField.styleSheet = new StyleSheet();
}
textField.styleSheet.setStyle("sup", { display: "inline", fontFamily: "ArialSup", fontWeight:"normal"});
textField.styleSheet.setStyle("a:link", { textDecoration: "none", color: linkColor });
textField.styleSheet.setStyle("a:hover", { textDecoration: "underline" });
textField.styleSheet.setStyle("a:active", { textDecoration: "underline" });
}
}
}

Can you assign the text directly to the .text property?
private function enteredText(event:FlexEvent):void
{
textDefault.text = event.currentTarget.text;
textGreen.text = event.currentTarget.text;
textGreenStylesheet.text = event.currentTarget.text;
}

Related

Flex: rounded corners for dynamic created images

there a better way as this example
to create round corners for dynamic (addChild or addElement) created Images?
ok, here is a custom class http://santobay.blogspot.com/2010/04/rounded-corner-image-in-flex.html . save this code as com/RoundedImage.as , create new mxml file with this code
<mx:Application name="Image_mask_test"
xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:custom="com.*"
layout="vertical"
verticalAlign="middle"
backgroundColor="white">
<mx:HBox id="hbox" width="100%">
<custom:RoundedImage source="images/test.jpg" width="250" height="250" cornerRadius="15"/>
</mx:HBox></mx:Application>
and compile. For create images dynamic use this code:
<fx:Script>
<![CDATA[
import com.RoundedImage;
public function createImage():void {
var newImage:RoundedImage = new RoundedImage();
newImage.source = "images/test.jpg";
newImage.cornerRadius = 20;
hbox.addChild(newImage);
}
]]>
</fx:Script>
No, you must use a mask, if you add it dynamically.
However, you could add a 'frame' on top of the image, if the background is solid, you can use this trick.

Flex - number validation, wont remove red glow if click focus? (example provided)

Reproduce problem:
run code
click checkbox
click on the first input box
enter: 100
The red glowing error box should have disappeared, but it will disappear when you focus on another component e.g. the second input box.
If you refersh the screen, click the checkbox, and TAB onto the first input box, type 100, the error glow will disappear as I expected. This is what I want for click also. Any ideas?
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="onInit()">
<mx:Script>
<![CDATA[
import mx.validators.NumberValidator;
private var amountValidator:NumberValidator;
private function onInit():void{
amountValidator = new NumberValidator();
amountValidator.property = "text";
amountValidator.trigger = txtAmount;
amountValidator.triggerEvent = "change";
amountValidator.minValue = 10;
amountValidator.domain = "int";
amountValidator.precision = 0;
amountValidator.allowNegative = false;
amountValidator.source = txtAmount;
}
private function onInputChange():void{
amountValidator.validate();
trace("input");
}
private function checkClick():void{
if(myCheckBox.selected){
amountValidator.validate();
}
}
]]>
</mx:Script>
<mx:HBox horizontalGap="5" x="68" y="37">
<mx:CheckBox id="myCheckBox" click="checkClick()"/>
<mx:TextInput id="txtAmount" change="onInputChange()" width="160" height="20" restrict="0-9" maxChars="8" x="75" y="44"/>
<mx:TextInput id="dummyInput" y="72" x="75"/>
</mx:HBox>
</mx:Application>
Lovely stuff, found an answer to this question at last.
It is logged as a bug under Flex SDK 3.5 with a workaround. Add a keyUp event tag to the target TextInput component as follows:
keyUp="txtAmount.drawFocus(true)"
Bingo!

Flex container transform matrix problems

I have a Box container that has a label element inside it. When the box is transformed using a Matrix the label element is not visible anymore. How do I make the elements visible?
<mx:Script>
<![CDATA[
private function onBoxClick(event:MouseEvent):void
{
var transformMatrix:Matrix = this.box.transform.matrix;
transformMatrix.c = Math.PI * 2 * -15 / 360;;
this.box.transform.matrix = transformMatrix;
}
]]>
</mx:Script>
<mx:HBox id="box"
x="100" y="100"
width="100" height="100"
backgroundColor="0x000000"
click="onBoxClick(event)">
<mx:Label id="textLabel" text="This is a test" color="#FFFFFF" visible="true"/>
</mx:HBox>
I'm guessing the TextField inside the Label component doesn't have the font embedded. If you plan to use .rotation or .alpha on a dynamic text you must embed the font.
You can easily test this with a regular TextField:
var t:TextField = new TextField();
t.defaultTextFormat = new TextFormat('Verdana',12,0x000000);
t.embedFonts = true;
t.rotation = 10;
t.text = 'rotated';
addChild(t);
That is assuming you have the Verdana font embedded in this example. If you comment out the 3rd line you will see the text disappear.

Flex: Moving canvases between several ui elements

I need to animate a lable movement between 2 canvases...
The mxml example of the code is:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="main()" frameRate="1">
<mx:Script>
<![CDATA[
import mx.controls.Label;
public function main():void
{
onEnd();
}
private function onEnd():void
{
(canv1.getChildAt(0) as Label).move(canv2.x, canv2.y);
}
]]>
</mx:Script>
<mx:Panel x="208" y="0" width="190" height="200" layout="absolute" title="Panel2" id="d">
</mx:Panel>
<mx:Panel width="200" height="200" id="c" title="Panel 1">
<mx:Canvas width="135" height="56" id="canv1" label="c1" themeColor="#79B4DA" backgroundColor="#65D565">
<mx:Label text="Move me after event" y="10"/>
</mx:Canvas>
<mx:Canvas width="135" height="79" id="canv2" label="C2" backgroundColor="#E4CACA">
</mx:Canvas>
</mx:Panel>
</mx:Application>
Currently the problem is that the label actually do not leave borders of the first canvas (I see scrollbars instead of it).
I think this is related to globalToLocal conversion problems, but do not understand how to use it.
Also another question is how to animate the movement corretly, because move function performs movement without any visible action.
(The movement happens seamlessly).
I don't think the move function will solve this for you since it will only move the label within it's parent, just as Robusto has commented above.
The way to go about this would perhaps be to see to it that you first detach the label from its parent. Then move it, and add it as a child to the other canvas. Manipulating x,y coordinates will not implicitly change the parent for you. That will always have to be done explicitly, which is good...
So for example, to actually switch parent you would need to do something like this (pseudo code):
/**
* This function only switches the parent.
*/
private function moveLabel(label:Label) {
label.parent.removeChild(label);
canv2.addChild(label);
}
If you want this action to be animated you would first have to detach the label from the canvas and see to it that it is added to the parent of the canvas, in your case, the panel with id "c". Then you can tween it into position and add it to the correct canvas.
TweenLite is a great library for tweening. http://www.greensock.com/tweenlite/
But I guess the main lesson here is that manipulating coordinates will never result in a new parent for the component you are moving.
Update: Here's a complete example of how you could solve this, take into account that the code is not very nice looking, but it contains a simple example of animation.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="main()">
<mx:Script>
<![CDATA[
import mx.controls.Label;
import flash.geom.Point;
import gs.TweenLite;
import gs.easing.Expo;
public function main():void
{
onEnd();
}
private function onEnd():void
{
var label:Label = canv1.getChildAt(0) as Label;
var originX:int = label.localToGlobal(new Point(label.x, label.y)).x;
var originY:int = label.localToGlobal(new Point(label.x, label.y)).y;
label.parent.removeChild(label);
stage.addChild(label);
label.x = originX;
label.y = originY;
TweenLite.to(label, 1.5, {y: "80", ease:Expo.easeOut, onComplete:onFinishTween, onCompleteParams:[label]});
}
private function onFinishTween(label:Label):void {
stage.removeChild(label);
label.x = canv2.globalToLocal(new Point(label.x, label.y)).x;
label.y = canv2.globalToLocal(new Point(label.x, label.y)).y;
canv2.addChild(label);
}
]]>
</mx:Script>
<mx:Canvas width="135" height="56" id="canv1" label="c1" themeColor="#79B4DA" backgroundColor="#65D565" y="-1" x="9">
<mx:Label text="Move me after event" y="10"/>
</mx:Canvas>
<mx:Canvas width="135" height="79" id="canv2" label="C2" backgroundColor="#E4CACA" y="90" x="8">
</mx:Canvas>
</mx:Application>

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