Flex: removedEffect causes error when changing parent - apache-flex

ok I have a subclass of TitleWindow with this method:
public function launchInNewWindow(e:Event):void
{
this.parent.removeChild(this);
ownWindow = new Window();
ownWindow.systemChrome = 'none';
ownWindow.type = NativeWindowType.LIGHTWEIGHT;
ownWindow.transparent = true;
ownWindow.setStyle('showFlexChrome', false);
ownWindow.width = this.width > 750 ? 750 : this.width;
ownWindow.height = this.height > 550 ? 550 : this.height;
edit.enabled = false;
ownWindow.addChild(this);
ownWindow.width += 5; //add to show dropshadow
ownWindow.height += 10; //add to show dropshadow
ownWindow.open();
_inOwnWindow = true;
ownWindow.nativeWindow.x = Application.application.nativeWindow.x + this.x + 5; //keep in same spot add 5 for systemChrom border
ownWindow.nativeWindow.y = Application.application.nativeWindow.y + this.y + 30;//keep in same spot add 30 for systemChrom title
}
What this does is make the title window its own Window (NativeWindow) by creating a new Window object and adding itself to the new Window's displayList.
It works really well, however if I have a removedEffect set on the instance of this class it produces an error when trying to add itself to the Window's displayList.
I tried adding:
this.setStyle('removedEffect',null);
and
this.setStyle('removedEffect',new TitleWindow().getStyle('removedEffect'));
to the method as an attempt to remove any removedEffect set on itself before hand, but with no luck.
but it works fine if there is no removedEffect on the component. There has got to be a way to fix this.
Any ideas?
Thanks!!

For the removedEffect to work the window needs to be on the original parent, but you're immediately trying to add it to the new parent and it can't belong to two parents at once. There are a few options I can think of.
Get a bitmap of the window to be removed, show that in the same place, run the effect you want on this bitmap copy, and then you can run re-parent the original without interference from the effect.
Separate the code that removes and re-parents into two steps. Remove the window first. Then when the removedEffect is done, add it to the new window's display list.

Related

flex: Drag and drop- object centering

In a drag+drop situation using Flex, I am trying to get the object center aligned to the point of drop- somehow, irrespective of the adjustments to height and width, it is always positioning drop point to left top.
here is the code..
imageX = SkinnableContainer(event.currentTarget).mouseX;
imageY = SkinnableContainer(event.currentTarget).mouseY;
// Error checks if imageX/imageY dont satisfy certain conditions- move to a default position
// img.width and img.height are both defined and traced to be 10- idea to center image to drop point
Image(event.dragInitiator).x = imageX-(img.width)/2;
Image(event.dragInitiator).y = imageY-(img.height)/2
The last 2 lines don't seem to have any effect. Any ideas why-must be something straightforward, that I am missing...
You can use the following snippet:
private function on_drag_start(event:MouseEvent):void
{
var drag_source:DragSource = new DragSource();
var drag_initiator:UIComponent = event.currentTarget as UIComponent;
var thumbnail:Image = new Image();
// Thumbnail initialization code goes here
var offset:Point = this.localToGlobal(new Point(0, 0));
offset.x -= event.stageX;
offset.y -= event.stageY;
DragManager.doDrag(drag_initiator, drag_source, event, thumbnail, offset.x + thumbnail.width / 2, offset.y + thumbnail.height / 2, 1.0);
}
Here is one important detail. The snippet uses stage coordinate system.
If you use event.localX and event.localY, this approach will fail in some cases. For example, you click-and-drag a movie clip. If you use localX and localY instead of stage coordinates, localX and localY will define coordinates in currently clicked part of the movie clip, not in the whole movie clip.
Use the xOffset and yOffset properties in the doDrag method of DragManager.
Look here for an example.

Canvas' verticalScrollPosition cannot be changed in Flex

I have a weird problem with verticalScrollPosition in Flex.
I have a content Canvas and a wrapper Canvas. The content is large (5000px X 5000px), the wrapper is 800px X 800px.
public var wrapper:Canvas = new Canvas();
public var content:Canvas = new Canvas();
wrapper.addChild(content);
application.addChild(wrapper);
I would like to set the wrapper's scrollbar position dynamically anytime. I can do it by calling its properties:
wrapper.verticalScrollPosition = A;
wrapper.horizontalScrollPosition = B;
This is working fine. But! If I set a default scrollbar position when the Canvas is complete:
wrapper.addEventListener(FlexEvent.CREATION_COMPLETE, function(e:FlexEvent):void{
wrapper.verticalScrollPosition = DEFAULT_A;
wrapper.horizontalScrollPosition = DEFAULT_B;
});
I can't set the verticalScrollPosition anymore:
wrapper.verticalScrollPosition = C;
trace(wrapper.verticalScrollPosition); // Outputs: DEFAULT_A
So the problem only exist if I set a default position using 'FlexEvent.CREATION_COMPLETE'.
What am I doing wrong here?
Thanks in advance.
I've found a horrible workaround - I wait 1 microsecond to set the default state:
wrapper.addEventListener(FlexEvent.CREATION_COMPLETE, function(e:FlexEvent):void{
setTimeout(
function():void{wrapper.verticalScrollPosition = DEFAULT_A;},
1
);
});
I think we can agree it's really ugly. How can I make it better?
I've found the problem. I attached the event listener to the child of the wrapper. When I added the listener to the wrapper itself it works.
So lesson I've learnt: always track the topmost ui element's events you have to work with.

How do I Print a dynamically created Flex component/chart that is not being displayed on the screen?

I have a several chart components that I have created in Flex. Basically I have set up a special UI that allows the user to select which of these charts they want to print. When they press the print button each of the selected charts is created dynamically then added to a container. Then I send this container off to FlexPrintJob.
i.e.
private function prePrint():void
{
var printSelection:Box = new Box();
printSelection.percentHeight = 100;
printSelection.percentWidth = 100;
printSelection.visible = true;
if (this.chkMyChart1.selected)
{
var rptMyChart1:Chart1Panel = new Chart1Panel();
rptMyChart1.percentHeight = 100;
rptMyChart1.percentWidth = 100;
rptMyChart1.visible = true;
printSelection.addChild(rptMyChart1);
}
print(printSelection);
}
private function print(container:Box):void
{
var job:FlexPrintJob;
job = new FlexPrintJob();
if (job.start()) {
job.addObject(container, FlexPrintJobScaleType.MATCH_WIDTH);
job.send();
}
}
This code works fine if the chart is actually displayed somewhere on the page but adding it dynamically as shown above does not. The print dialog will appear but nothing happens when I press OK.
So I really have two questions:
Is it possible to print flex components/charts when they are not visible on the screen?
If so, how do I do it / what am I doing wrong?
UPDATE:
Well, at least one thing wrong is my use of the percentages in the width and height. Using percentages doesn't really make sense when the Box is not contained in another object. Changing the height and width to fixed values actually allows the printing to progress and solves my initial problem.
printSelection.height = 100;
printSelection.width = 100;
But a new problem arises in that instead of seeing my chart, I see a black box instead. I have previously resolved this issue by setting the background colour of the chart to #FFFFFF but this doesn't seem to be working this time.
UPDATE 2:
I have seen some examples on the adobe site that add the container to the application but don't include it in the layout. This looks like the way to go.
i.e.
printSelection.includeInLayout = false;
addChild(printSelection);
Your component has to be on the stage in order to draw its contents, so you should try something like this:
printSelection.visible = false;
application.addChild(printSelection);
printSelection.width = ..;
printSelection.height = ...;
...
then do the printing
i'm not completely sure, but in one of my application I have to print out a complete Tab Navigator and the only method i have found to print it is to automatically scroll the tabnavigator tab in order to show the component on screen when i add them to the printjob.
In this way they are all printed. Before i created the tabnaviagotr scrolling the print (to PDF) result was a file with all the pages of the tab but only the one visible on screen really printed.

Flex: How to resize DisplayObject to fit panel in Flex application

I am trying to attach some of my actionscript class, inherited from Sprite, to my Flex application by attaching it to a UIComponent, then add UIComponent to a panel. However, the size of my Sprite class appears to be larger than the panel. So, I try to resize it using DisplayObject.scale property, but I need to know the size of my container. Since I try to make my code reusable, I figure that I should let my Sprite class handle the resize by getting it via its parent property (let's say, I can resize it by set
this.scaleX = this.parent.Width/this.width or something like this)
However, my Sprite class's parent, which is the UIComponent, has width/height of 0.
So, my question is:
1) Is there any better way to resize DisplayObject class to fit its container when attached to Flex object?
2) If you know, why my UIComponent's size remain 0 and my DisplayObject still appears at its full size?
Here's my code, using Flash Builder 4:
private var loader:Loader = new Loader();
private function testLoader():void {
var urlRequest:URLRequest = new URLRequest("http://localhost/welcome.png");
loader.load(urlRequest);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoaderLoadComplete);
}
private function onLoaderLoadComplete(e:Event):void {
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE,onLoaderLoadComplete);
var ui : UIComponent = new UIComponent();
ui.addChild(loader);
panelTest.addElement(ui);
trace("ui.width = " + ui.width);
trace("ui.height = " + ui.height);
trace("loader.width = " + loader.width);
trace("loader.height = " + loader.height);
trace("panelTest.width = " + panelTest.width);
trace("panelTest.height = " + panelTest.height);
}
And this is the result when run:
ui.width = 0
ui.height = 0
loader.width = 571
loader.height = 411
panelTest.width = 480
panelTest.height = 320
I think it's the other way around: the parent should set the child element size while rendering. You can override the updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) method to set the size of your loader child. See Advanced Visual Components in ActionScript.
I guess that the size of your ui is not really 0. The size is updated later after the layout and rendering process. So you have to wait for the layout to complete after adding a component to stage.
About the remaining 0, unfortunately is a known (grrr) problem: http://www.mail-archive.com/flexcoders#yahoogroups.com/msg39998.html
You can try to access it in a hacked way (using the mx_internal namespace): get width of dynamically created custom uicomponent
Adding the sprite directly to the panel may also be helpful. This can be done by adding the loader to the panel's rawChildren: http://www.actionscript.org/forums/showthread.php3?p=728034
Enjoy.

Styling UITextField

I've been playing around with different methods of determining at runtime the width of a "label" so that I can resize the "label" because I don't want it to truncate. I've finally found an easy solution through UITextField which allows me to set the .autoSize which is great! However, now I'm trying to "style" (simply adjust font and font size) of the UITextField but it seems that I have to do it manually with '.htmlText' (which I'll gladly accept if that is the ONLY way).
I'm using the .text to set the value of the label.
My test case involves a HBox (I'm actually using a Grid but they should be the same and I've done testing on both):
I style the HBox and the style carries through to the UITextField. I don't believe this will work for me because I have other components inside that I need to style differently.
I've tried: UITextFormat and TextFormat (I see that the .htmlText being updated accordingly but the output doesn't update. Then I noticed that whenever I called hbox.addChild(myUITextField) it would override the .htmlText
I've tried setting the style with myUITextField.setStyle("fontSize", 20) before and/or after the call to addChild neither of which made an impact on the display as per what I noted above.
Changes are being made but they seem to be overrided when I add it to the display.
So what do I need to do in order to style the UITextField aside from manually setting it along with my contents in .htmlText? Solutions not using UITextField is fine as long as there is some easy way of not truncating the text.
EDIT: I want to just do textField.setStyle('fontSize', 20) and expect that every time I change the text, I wouldn't need to use HTML to go with it (so I can just do textField.text = 'something else' and expect that it will still have a font size of 20). This is what I meant by not using .htmlText (sorry if I wasn't clear before).
2nd EDIT: I guess I should present the whole issue and maybe that'll clarify what I did wrong or couldn't achieve.
My intent is to have a Grid and add text into it. I do not want it to wrap or scroll so I add it to the next row in the Grid when the current row's children total width exceeds some number. In order to add it to the next row, I need to be able to calculate the width of the text. I would like to be able to style that text individually based on cases and there might be other components (like a TextInput). Essentially what I'm trying to accomplish is "Fill in the Blank".
I've included code to show what I'm currently doing and it works somewhat. It might be un-related to the original issue of styling but I can't figure out how to adjust the distance between each UITextField but aside from that this fits what I would like to accomplish. Relevant to the question is: I would like to change the way I style each UITextField (currently setting .htmlText) into something a bit straightforward though like I previously mentioned I'll gladly accept using .htmlText if that's the only solution.
So I have a Grid with x Rows in it and in each row, I have exactly one GridItem. Based on the input, I add UITextField and TextInput into the GridItem going on to the next GridItem when necessary. If you have a better way of doing so then that would be better but I guess what I really want is to find a different way of styling.
Also another problem, I'm not sure of the exact way to add a TextField into the display. I tried:
var t : TextField = new TextField();
t.text = "I'm a TextField";
hBox.addChild(t); // doesn't work
//this.addChild(t); // doesn't work either
But I get the following error:
TypeError: Error #1034: Type Coercion failed: cannot convert flash.text::TextField#172c8f9 to mx.core.IUIComponent.
Here's what I have that's working.
private function styleQuestionString(str : String) : String {
return '<FONT leading="1" face="verdana" size="20">' + str + '</FONT>';
}
private function loadQuestion(str : String) : void {
/* Split the string */
var tmp : Array = str.split("_");
/* Track the current width of the GridItem */
var curWidth : int = 0;
/* Display components that we will add */
var txtField : UITextField = null;
var txtInput : TextInput = null;
/* Track the current GridItem */
var curGridItem : GridItem = null;
/* Track the GridItem we can use */
var gridItemAC : ArrayCollection = new ArrayCollection();
var i : int = 0;
/* Grab the first GridItem from each GridRow of Grid */
var tmpChildArray : Array = questionGrid.getChildren();
for (i = 0; i < tmpChildArray.length; i++) {
gridItemAC.addItem((tmpChildArray[i] as GridRow).getChildAt(0));
}
curGridItem = gridItemAC[0];
gridItemAC.removeItemAt(0);
/* Used to set the tab index of the TextInput */
var txtInputCounter : int = 1;
var txtFieldFormat : UITextFormat = new UITextFormat(this.systemManager);
txtFieldFormat.leading = "1";
//var txtFieldFormat : TextFormat = new TextFormat();
//txtFieldFormat.size = 20;
/* Proper Order
txtField = new UITextField();
txtField.text = tmp[curItem];
txtField.autoSize = TextFieldAutoSize.LEFT;
txtField.setTextFormat(txtFieldFormat);
*/
var txtLineMetrics : TextLineMetrics = null;
var tmpArray : Array = null;
curGridItem.setStyle("leading", "1");
var displayObj : DisplayObject = null;
for (var curItem : int= 0; curItem < tmp.length; curItem++) {
/* Using UITextField because it can be auto-sized! */
/** CORRECT BLOCK (ver 1)
txtField = new UITextField();
txtField.text = tmp[curItem];
txtField.autoSize = TextFieldAutoSize.LEFT;
txtField.setTextFormat(txtFieldFormat);
***/
tmpArray = (tmp[curItem] as String).split(" ");
for (i = 0; i < tmpArray.length; i++) {
if (tmpArray[i] as String != "") {
txtField = new UITextField();
txtField.htmlText = styleQuestionString(tmpArray[i] as String);
//txtField.setTextFormat(txtFieldFormat); // No impact on output
txtLineMetrics = curGridItem.measureHTMLText(txtField.htmlText);
curWidth += txtLineMetrics.width + 2;
if (curWidth >= 670) {
curGridItem = gridItemAC[0];
curGridItem.setStyle("leading", "1");
if (gridItemAC.length != 1) {
gridItemAC.removeItemAt(0);
}
// TODO Configure the proper gap distance
curWidth = txtLineMetrics.width + 2;
}
displayObj = curGridItem.addChild(txtField);
}
}
//txtField.setColor(0xFF0000); // WORKS
if (curItem != tmp.length - 1) {
txtInput = new TextInput();
txtInput.tabIndex = txtInputCounter;
txtInput.setStyle("fontSize", 12);
txtInputCounter++;
txtInput.setStyle("textAlign", "center");
txtInput.width = TEXT_INPUT_WIDTH;
curWidth += TEXT_INPUT_WIDTH;
if (curWidth >= 670) {
curGridItem = gridItemAC[0];
if (gridItemAC.length != 1) {
gridItemAC.removeItemAt(0);
}
// TODO Decide if we need to add a buffer
curWidth = TEXT_INPUT_WIDTH + 2;
}
curGridItem.addChild(txtInput);
txtInputAC.addItem(txtInput);
/* Adds event listener so that we can perform dragging into the TextInput */
txtInput.addEventListener(DragEvent.DRAG_ENTER, dragEnterHandler);
txtInput.addEventListener(DragEvent.DRAG_DROP, dragDropHandler);
txtInput.addEventListener(DragEvent.DRAG_EXIT, dragExitHandler);
}
/* Add event so that this label can be dragged */
//txtField.addEventListener(MouseEvent.MOUSE_MOVE, dragThisLabel(event, txtField.text));
}
}
After about 8 hours of searching for a solution to what would seem to be such a simple issue I FINALLY stumbled on your posts here... Thankyou!!!
I have been stumbling around trying to get TextField to work and had no Joy, Label was fine, but limited formatting, and I need to be able to use embedded fonts and rotate. After reading the above this finally worked for me:
var myFormat:TextFormat = new TextFormat();
myFormat.align = "center";
myFormat.font = "myFont";
myFormat.size = 14;
myFormat.color = 0xFFFFFF;
var newTxt:UITextField = new UITextField();
newTxt.text = "HELLO";
addChild(newTxt);
newTxt.validateNow();
newTxt.setTextFormat(myFormat);
The order of addChild before the final 2 steps was critical! (myFont is an embedded font I am using).
One again... a thousand thankyou's...
John
EDIT BASED ON THE ASKERS FEEDBACK:
I didn't realize you wanted to just apply one style to the whole textfield, I thought you wanted to style individual parts. This is even simpler for you, won't give you any trouble at all :)
var textFormat: TextFormat = new TextFormat("Arial", 12, 0xFF0000);
myText.setTextFormat(textFormat);
Be aware that this sets the style to the text that is in the TextField, not necessarily future text you put in. So have your text in the field before you call setTextFormat, and set it again every time you change it just to be sure it stays.
It's probably best if you use a normal TextField as opposed to the component. If you still want the component you may need to call textArea.validateNow() to get it to update with the new style (not 100% sure on that one though) Adobe components are notoriously bad, and should be avoided. :(
To see all available options on the TextFormat object see here
END EDIT ---------
This is easy enough to just do with CSS in a normal old TextField.
var myCSS: String = "Have some CSS here, probably from a loaded file";
var myHTML: String = "Have your HTML text here, and have it use the CSS styles";
// assuming your textfield's name is myText
var styleSheet: StyleSheet = new StyleSheet();
styleSheet.parseCSS(myCSS);
myText.autoSize = TextFieldAutoSize.LEFT;
myText.styleSheet = styleSheet;
myText.htmlText = myHTML;
Supported HTML tags can be found here
Supported CSS can be found here
The reason you have a problem adding Textfield to containers is that it doesn't implement the IUIComponent interface. You need to use UITextField if you want to add it. However, that's presenting me with my own styling issues that brought me to this question.
A few things I know:
TextField is styled using the TextFormat definition, and applying it to the textfield. As Bryan said, order matters.
setStyle does nothing on IUITextField, and the TextFormat method doesn't seem to work the same as in normal TextFields. (Edit #2: Ahah. You need to override the "validateNow" function on UITextFields to use the setTextFormat function)
To autosize a TextArea, you need to do something like this (inheriting from TextArea):
import mx.core.mx_internal;
use namespace mx_internal;
...
super.mx_internal::getTextField().autoSize = TextFieldAutoSize.LEFT;
this.height = super.mx_internal::getTextField().height;
Found this code on, I think, on StackOverflow a while back. Apologies to the original author. But the idea is that you need to access the "mx_internal" raw textfield.
Text and TextArea have wrapping options. (Label does not). So if you set the explicit width of a Text object, you might be able to size using the measuredHeight option and avoid truncation.
(edit: That was #4, but stackoverflow parsed it into a 1...)

Resources