How to make the inline graphic image interactive in a TextFlow - actionscript-3

I'm trying to make an image clickable in a TLF TextFlow and nothing seems to be working. I've added a event listener for a click event, set buttonMode to true and even set useHandCursor.
Here is my code so far:
<?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"
xmlns:utils="com.flexcapacitor.utils.*"
width="1000" height="550"
applicationComplete="init()" >
<fx:Script>
<![CDATA[
import spark.utils.TextFlowUtil;
import flashx.textLayout.edit.IEditManager;
import flashx.textLayout.edit.SelectionState;
import flashx.textLayout.elements.InlineGraphicElement;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.formats.TextLayoutFormat;
static private const simpleText:String = "<TextFlow xmlns='http://ns.adobe.com/textLayout/2008'>"
+ "<p styleName='center'><span typeName='a'>There are many </span><span styleName='italic'>such</span><span> lime-kilns </span><a href='http://www.google.com' typeName='a'><span>links</span></a><span> in that tract of country, for the purpose of burning the white marble which composes a large part of the substance of the hills. Some of them, built years ago, and long deserted, with weeds growing in the vacant round of the interior, which is open to the sky, and grass and wild-flowers rooting themselves into the chinks of the stones, look already like relics of antiquity, and may yet be overspread with the lichens of centuries to come. Others, where the lime-burner still feeds his daily and nightlong fire, afford points of interest to the wanderer among the hills, who seats himself on a log of wood or a fragment of marble, to hold a chat with the solitary man. It is a lonesome, and, when the character is inclined to thought, may be an intensely thoughtful occupation; as it proved in the case of Ethan Brand, who had mused to such strange purpose, in days gone by, while the fire in this very kiln was burning.</span></p>"
+ "<br/><p><span>The man who now watched the </span><span id='bold'>fire</span><span> was of a </span><span typeName='foo'>different</span><span> order, and troubled himself with no thoughts save the very few that were requisite to his business. At frequent intervals, he flung back the clashing weight of the iron door, and, turning his face from the insufferable glare, thrust in huge logs of oak, or stirred the immense brands with a long pole. Within the furnace were seen the curling and riotous flames, and the burning marble, almost molten with the intensity of heat; while without, the reflection of the fire quivered on the dark intricacy of the surrounding forest, and showed in the foreground a bright and ruddy little picture of the hut, the spring beside its door, the athletic and coal-begrimed figure of the lime-burner, and the half-frightened child, shrinking into the protection of his father's shadow. And when again the iron door was closed, then reappeared the tender light of the half-full moon, which vainly strove to trace out the indistinct shapes of the neighboring mountains; and, in the upper sky, there was a flitting congregation of clouds, still faintly tinged with the rosy sunset, though thus far down into the valley the sunshine had vanished long and long ago.</span></p>"
+ "</TextFlow>";
private function init():void {
var textFlow:TextFlow;
TextFlow.defaultConfiguration.unfocusedSelectionFormat = TextFlow.defaultConfiguration.focusedSelectionFormat;
TextFlow.defaultConfiguration.inactiveSelectionFormat = TextFlow.defaultConfiguration.focusedSelectionFormat;
textFlow = TextFlowUtil.importFromString(simpleText);
// set it into the editor
editor.textFlow = textFlow;
}
public var imageElementsDictionary:Dictionary = new Dictionary(true);
/**
* Insert an image
*
* source is either
* a String interpreted as a URI,
* a Class interpreted as the class of an Embed DisplayObject,
* a DisplayObject instance or
* a URLRequest.
* width, height is a number or percent
* options - none
*/
public function insertImage(source:Object, width:Object = null, height:Object = null, operationState:SelectionState = null):InlineGraphicElement {
var inlineGraphicElement:InlineGraphicElement;
var currentFormat:TextLayoutFormat;
var selectionStart:int;
var selectionEnd:int;
var loader:Loader;
var displayObject:DisplayObject;
var textFlow:TextFlow;
if (editor && editor.textFlow && editor.textFlow.interactionManager is IEditManager) {
textFlow = editor.textFlow;
if (editor.selectionActivePosition==-1) {
textFlow.interactionManager.selectFirstPosition();
}
inlineGraphicElement = IEditManager(textFlow.interactionManager).insertInlineGraphic(source, width, height, null, operationState);
//inlineGraphicElement.status = InlineGraphicElementStatus.LOADING;
loader = inlineGraphicElement.graphic as Loader;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, inlineGraphicElementLoader_complete);
loader.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorHandler);
displayObject = loader.parent;
imageElementsDictionary[displayObject] = inlineGraphicElement;
IEditManager(editor.textFlow.interactionManager).updateAllControllers();
}
return inlineGraphicElement;
}
/**
* Handle inline graphics loaded
* */
public function inlineGraphicElementLoader_complete(event:Event):void {
var loader:Loader = event.currentTarget.loader as Loader;
var displayObject:DisplayObject = loader.parent;
var sprite:Sprite = displayObject as Sprite;
var inlineGraphicElement:InlineGraphicElement;
inlineGraphicElement = imageElementsDictionary[displayObject];
if (sprite) {
sprite.buttonMode = true;
}
if (displayObject) {
displayObject.addEventListener(MouseEvent.CLICK, inlineGraphicElementClickHandler, false, 0, true);
}
}
protected function inlineGraphicElementClickHandler(event:Event):void {
trace ("Clicked");
}
private function uncaughtErrorHandler(event:UncaughtErrorEvent):void {
trace ("Error");
if (event.error is Error) {
var error:Error = event.error as Error;
// do something with the error
}
else if (event.error is ErrorEvent) {
var errorEvent:ErrorEvent = event.error as ErrorEvent;
// do something with the error
}
else {
// a non-Error, non-ErrorEvent type was thrown and uncaught
}
}
]]>
</fx:Script>
<s:Button label="Insert Image" top="0" horizontalCenter="0"
click="insertImage('http://www.radii8.com/r8m/wp-content/uploads/2015/10/radiate-logo-2.png')"/>
<s:RichEditableText id="editor" text="Some rich text"
top="100" left="100"
width="600"/>
</s:WindowedApplication>

The following code uses the cursor manager to show a hand cursor when over inline graphic elements. There are some a few lines of references to local classes you will need to remove to enable this example code to run.
NOTE: I pasted the example code here but hit the limit on characters so here's the pastebin. I may create a smaller example.

Related

Compositing the stage's last frame

I've created a series of classes that can be used to generate and render images. I want to store a copy of the last frame displayed so I can mix it with the current frame to create a video sustain effect. A brief overview of the classes involved in this example:
MasterContainer: a subclass of Sprite used as the main display object. Generative classes are placed in the MasterContainer, and redrawn when the container is told to render
CustomWave: a subclass of Shape used to contain, draw, and manipulate a GraphicsPath object. One of the aforementioned 'generative classes'
My current attempt involves the use of two MasterContainer objects - one for the current frame, and one for the last frame. If I'm not mistaken, the current appearance of one MasterContainer (and its children) can be copied to the other with a command like lastMaster.graphics.copyFrom(master.graphics);. Consider the following code:
var time:Number;
var master:MasterContainer = new MasterContainer(); //current frame
var lastMaster:MasterContainer = new MasterContainer(); // last frame
var wave:CustomWave = new CustomWave(new <Number>[0,0,0,0],0xffffff,5); //generator for current frame
master.RegisterComponent(wave); //adds CustomWave and registers with the rendering loop
addChild(lastMaster); //add last frame to stage
addChild(master); //add current frame to stage
addEventListener(Event.ENTER_FRAME, perFrame);
function perFrame(event:Event):void{
time = 0.001 * getTimer();
lastMaster.graphics.copyFrom(master.graphics); //copy previous frame's graphics
UpdatePoints(); //update the path of the CustomWave
UpdateColor(); //update the color of the CustomWave
master.fireRenderCannon(); //redraw objects registered to master
}
This seems to work in theory, but as far as I can tell lastMaster ends up with no visible graphics content even though master renders as expected. I've tried several times to test whether this is the case, and am pretty convinced that that it is, but am newish to AS3 and am concerned I am overlooking something - the code looks like it should work. Does anyone have suggestions on how to test this properly? Are there obvious defects within this code that would cause lastMaster to be visually blank? Is there an better way of accomplishing my goal?
I think I'm in over my head on this... I would love any input. Thanks!
After you copied graphics, what do you try to do with it?
Method copyFrom works as clocks, without any problems. Isn't here logic bug in your code?
function perFrame(event:Event):void{
time = 0.001 * getTimer();
lastMaster.graphics.copyFrom(master.graphics); //Here
//master.graphics.copyFrom(lastMaster.graphics);
UpdatePoints();
UpdateColor();
master.fireRenderCannon();
}
Example of copyFrom, it works fine with any complexity of graphics:
var complex: Shape = new Shape();
adobeExample(complex.graphics);
var test2: Shape = new Shape();
test2.graphics.copyFrom(complex.graphics);
addChild(test2);
private function adobeExample(graphics: Graphics):void{
// define the line style
graphics.lineStyle(2,0x000000);
// define the fill
graphics.beginFill(0x666699);//set the color
// establish a new Vector object for the commands parameter
var star_commands:Vector.<int> = new Vector.<int>();
// use the Vector array push() method to add moveTo() and lineTo() values
// 1 moveTo command followed by 3 lineTo commands
star_commands.push(1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2);
// establish a new Vector object for the data parameter
var star_coord:Vector.<Number> = new Vector.<Number>();
// use the Vector array push() method to add a set of coordinate pairs
star_coord.push(0,0, 75,50, 100,0, 125,50, 200,0, 150,75, 200,100, 150,125, 200,200, 125,150, 100,200, 75,150, 0,200, 50,125, 0,100, 50,75, 0,0);
graphics.drawPath(star_commands, star_coord);
}
After the comments made by Bennet and Nicolas, it became obvious that my requirements were (nearly) impossible without a fair amount of redesign. The changes made are as follows:
Generators are no longer DisplayObjects. They are only used to calculate vectors containing the IGraphicsData objects necessary to draw the generated graphic with the drawGraphicsData method.
MasterContainer is now a shape subclass that retrieves the Vector.<IGraphicsData> from each registered generator in order to draw the output.
A bitmap subclass is used to render the contents of the MasterContainer, combining it with a color-dampened version of the previous frame.
An abridged version of the bitmap subclass:
private var constantSustain:Number;
private var linearSustain:Number;
private var sustain:ColorTransform;
private var lastFrame:BitmapData;
public function BitmapManipulator(constantSustain:Number = 0.998, linearSustain:Number = 0.98) {
this.constantSustain = Math.min(Math.max(constantSustain, 0), 1);
this.linearSustain = Math.min(Math.max(linearSustain, 0), 1);
this.UpdateSustain();
this.addEventListener(Event.ADDED_TO_STAGE, OnAddedToStage)
}
private function UpdateSustain():void {
var constantRelease:Number = 255 * (this.constantSustain - 1);
this.sustain = new ColorTransform(this.linearSustain, this.linearSustain, this.linearSustain, 1,
constantRelease, constantRelease, constantRelease, 0);
}
private function OnAddedToStage(event:Event) {
this.lastFrame = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0);
}
public function DrawFrame(container:MasterContainer):void {
this.lastFrame.draw(container);
this.bitmapData = lastFrame;
this.lastFrame = this.bitmapData
this.lastFrame.colorTransform(getBounds(this), this.sustain);
}
...and finally the results #60fps when using an indigo sine wave of shifting phase as the input for the CustomWave:

AS3 best way to mass crop tiles from a tilesheet?

Okay so I'm trying to create a program that reads a map from a csv file and then draw each tile using a tilesheet. Reading in the map works fine and I could draw certain tiles depending on the value read in but only if I embedded the images. Obviously this is impractical when it comes to having >20 different tiles; embedding them all just wouldn't be smart.
This is my code for drawing the tiles from the tilesheet.
package
{
import flash.display.Graphics;
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.geom.Point;
public class Tile extends Sprite
{
[Embed(source="../bin/lib/TileSheet.png")]
private var BitmapClass:Class;
private var tileBitmap:Bitmap = new BitmapClass();
var tileSize = 25;
var tileSheetLength = 20;
var sheetColumns:int = tileBitmap.bitmapData.width / tileSize;
var pt:Point = new Point(0, 0);
var bmp:Bitmap = new Bitmap(new BitmapData(tileSize, tileSize, true, 0));
public function Tile(collide:Boolean, id:int)
{
Draw(id);
}
private function Draw(id:int):void
{
var col:int = id % sheetColumns;
var row:int = Math.floor(id / sheetColumns);
var rect:Rectangle = new Rectangle(col * tileSize, row * tileSize, tileSize, tileSize);
bmp.bitmapData.copyPixels (tileBitmap.bitmapData, rect, pt, null, null, true);
this.addChild(bmp);
}
public function Update():void
{
}
}
}
'
So what I need help with is optimising this code so that I can run it around 1,900 times rather than the 910-911 times it can handle right now before just closing without errors. If there is a better way of doing this please let me know and any constructive criticism is always appreciated!
You have a Tile class which has a BitmapClass instance. Perhaps that should be a static property (belonging to the class, not every instance) to begin with. My guess is you're using the memory for the whole tile sheet every since time you instantiate a single tile which you probably don't want to do.
Another thing I'm noticing is you're creating a new BitmapData for each tile, when in fact you probably just need the tile data (it's id/coordinates) so you can copy pixels into the final BitmapData which gets displayed on stage. Perhaps you need to a class to manage resources(embedded bitmaps) and another to manage the different Tile instance(which should hold render data and references to pixels, but shouldn't store the actual data) and copying to the main buffer.
Also, it's a good idea to use BitmapData's lock() and unlock() functions for performance when doing multiple pixel operations on an image.
Have a look at Lee Brimelow's Sprite Sheet tutorials (part 1,2 and especially 3). They're really easy to follow and useful.
Also, it might be worth having a look at the have a look at the GPU accelerated IsoHill
library.
I've used IsoHill for a project before and it's quite fast, but it's best to get comfortable with the basics first, otherwise this might seem a bit much.

AS3 Blitting is Slower than a Movieclip. Why?

I tried following a combination of Lee Brimlow's blitting tutorial series and and the technique in Rex Van der spuy's "advanced game design with flash"
I am a developer working on a web online virutal world made in flash. I made a phone application (works similar to the phone in grand theft auto games). Anyway, when a message is sent we want to play this crazy animation of an envelope flying around and transforming with sparkles around it. It was laggy (especially on older computers) so I thought it would be a great chance to use blitting. However, the blitting animation actually plays slower than a regular movieclip!! What the heck is going on here? Is blitting only better for mobile devices and actually slower on computers? Maybe I am doing something wrong. Here is my code:
// THIS PART HAPPENS WHEN PHONE IT INITIALIZED
//**
//---------------- Blitting stuff ----------------------------------
// add this bitmap stage to the display list so we can see it
_bitmapStage = new BitmapData(550, 400, true, 0xD6D6D6);
_phoneItself.addChild(new Bitmap(_bitmapStage));
var _spritesheetClass:Class = getDefinitionByName("ESpritesheet_1") as Class;
_spritesheet = new _spritesheetClass() as BitmapData;
_envelopeBlit = new BlitSprite(_spritesheet, BlitConfig.envelopeAnimAry , _bitmapStage);
_envelopeBlit.x = -100;
_envelopeBlit.y = 0;
_envelopePlayTimer = new Timer(5, 0);
_envelopePlayTimer.addEventListener(TimerEvent.TIMER, onEnterTimerFrame);
_envelopeBlit.addEventListener("ENV_ANIM_DONE", onEnvAnimFinished);
// a "BlitSprite" is a class that I made. It looks like this:
package com.fs.util_j.blit_utils
{
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Point;
import flash.geom.Rectangle;
public class BlitSprite extends EventDispatcher
{
private var _fullSpriteSheet:BitmapData;
private var _rects:Array;
private var _bitmapStage:BitmapData;
private var pos:Point = new Point ();
public var x:Number = 0;
public var y:Number = 0;
public var _animIndex:
int = 0;
private var _count:int = 0;
public var animate:Boolean = true;
private var _whiteTransparent:BitmapData;
private var _envelopeAnimAry:Array;
private var _model:Object;
public function BlitSprite(fullSpriteSheet:BitmapData, envelopeAnimAry:Array, bitmapStage:BitmapData, model:Object = null)
{
_fullSpriteSheet = fullSpriteSheet;
_envelopeAnimAry = envelopeAnimAry;
_bitmapStage = bitmapStage;
_model= model;
init();
}
private function init():void
{
// _whiteTransparent = new BitmapData(100, 100, true, 0x80FFffFF);
this.addEventListener("ENV_ANIM_DONE", onEvnAnimDone);
}
protected function onEvnAnimDone(event:Event):void
{
}
public function render():void
{
// pos.x = x - _rects[_animIndex].width*.5;
// pos.y = y - _rects[_animIndex].width*.5;
// if (_count % 1 == 0 && animate == true)
// {
// trace("rendering");
if (_animIndex == (_envelopeAnimAry.length - 1) )
{
// _animIndex = 0;
dispatchEvent(new Event("ENV_ANIM_DONE", true));
animate = false;
// trace("!!!!animate over " + _model.animOver);
// if (_model != null)
// {
// _model.animOver = true;
// }
// trace("!!!!animate over " + _model.animOver);
}
else
{
_animIndex++;
}
pos.x = x + _envelopeAnimAry[_animIndex][1];
pos.y = y + _envelopeAnimAry[_animIndex][2];
_bitmapStage.copyPixels(_fullSpriteSheet, _envelopeAnimAry[_animIndex][0], pos, null, null, true);
}
}
}
// THIS PART HAPPENS WHEN PHONE'S SEND BUTTON IS CLICKED
_envelopeBlit.animate = true;
_envelopeBlit._animIndex = 0;
_darkSquare.visible = true;
_envelopePlayTimer.addEventListener(TimerEvent.TIMER, onEnterTimerFrame);
_envelopePlayTimer.start();
it also uses BlitConfig which stores the info about the spritesheet spit out by TexturePacker
package com.fs.pack.phone.configuration
{
import flash.geom.Rectangle;
public final class BlitConfig
{
public static var _sending_message_real_20001:Rectangle = new Rectangle(300,1020,144,102);
public static var _sending_message_real_20002:Rectangle = new Rectangle(452,1012,144,102);
public static var _sending_message_real_20003:Rectangle = new Rectangle(852,852,146,102);
public static var _sending_message_real_20004:Rectangle = new Rectangle(2,1018,146,102);
public static var _sending_message_real_20005:Rectangle = new Rectangle(702,822,148,102);
.
.
.
public static var _sending_message_real_20139:Rectangle = new Rectangle(932,144,1,1);
public static var envelopeAnimAry:Array = [
// rectangle, x offset, y offset
[ _sending_message_real_20001, 184,155],
[ _sending_message_real_20002, 184,155],
[ _sending_message_real_20003, 183,155],
[ _sending_message_real_20004, 183,155],
.
.
.
[ _sending_message_real_20139, 0,0]
]
public function BlitConfig()
{
}
}
}
EDIT:
Knowing that this is not mobile, my answer below is irrelevant. I will leave it there, though, in case someone is having trouble with blitting on mobile in the future.
With regards to this specific question, you are running your timer every 5ms. First off, the lowest range that a Timer is accurate is >15ms so that will never be a viable solution. For any Timer relating to displaying soemthing on the stage, you should never do it less than a single frame. (1000/stage.framerate. ~40ms for a 30fps app)
For blitting, the goal is to reduce calculations and rendering. The way you have this set up right now, it looks like you are blitting every 5ms. That is actually more than 8 times as often as the MovieClip is rendering. You should reduce how often you blit. Only do it when a change has actually been made beyond translation. Doing it any more often than that is overkill and the reason it is so slow (again, creating bitmaps is slow)
In general, you do not want to blit in an AIR for Mobile application (which I assume you are doing since you mentioned the phone being initialized). I'm not sure if it is okay to do it using other/native SDKs, but avoid it in AIR.
Essentially, it comes down to how blitting works. Blitting takes a screen capture and displays that on the stage rather than the actual object. In general, this is great. It means that your display objects, particularly vectors which are slow to render, have to render far less often. It is especially good when animating because an object tends to re-render every time it is translated in any way, but not a bitmap.
On mobile platforms, however, creating that bitmap is incredibly slow. I've never looked into how the SDK creates the Bitmaps, but it doesn't do it efficiently (it often makes me wonder if it does it pixel-by-pixel). On desktops, this is generally fine. There is plenty of CPU and plenty of RAM to make this happen quickly. On mobile, however, that luxury is not there at the moment. So when you blit and create that bitmap, it takes a while to run that process.
The problem is exacerbated on high-resolution screens. An app I developed from January to May of this year selectively used blitting to use filters in a GPU accelerated environment. On an iPad 2, the blitting took my app from 30fps to ~24fps. Not a big deal, not anything the user would notice. On an iPad 3 with retina display, however, it dropped down to 10fps. It makes sense when you think about it, as retina iPads have 4x as many pixels as non-retina iPads do.
If you do want to use blitting on mobile, I recommend a few things:
Use GPU rendering mode. Without it, you stand no chance. Be aware that, at least with pre-AIR 3.7, filters were not supported in GPU mode. I am unsure if that is still the case. You should avoid using filters on mobile regardless, though, as they are very slow to render
Make sure to test a release-mode application. Depending on build settings, the difference between debug mode and a release mode app can be substantial, especially on iOS. An app I just developed went from taking 2-3 seconds to create a new Flex View in debug mode to less than a frame (~40ms) to do it in release mode on an iPhone 4
Use blitting sparingly. Only do it where absolutely necessary
Look for ways to simplify your display list. It is easy to have an object with 40 children to create a button. Instead, look for ways to simplify that into fewer objects and fewer filters (even if removing a filter requires you add another object). I don't believe this will help with the actual blitting process, but it should help with rendering the objects in the first place.
So in general, use blitting sparingly on mobile because bitmap creation is slow.

Flash local Shared Object not detecting changes by other SWF's

I'm using a local shared object as means to transfer data between two swf's running at the same time.
so = getLocal("mySO");
// set the value from a user input, and set it to 'connected'
so.setProperty(txtName.text, "connected")
// every few seconds check if the other SWF started and modified value
onTimer
{
var data:Object = so.data
// breakpoint and inspect the data with debugger
}
The last commented line is where the problem is. In debugger, no changes to data are detected, but when inspecting .sol file with .minerva, I see the changes from both SWF's. So, although shared object is modified by SWF 2, the SWF 1 does not see these changes. Is this how's it supposed to be?
ps: I know I could use LocalConnection for communicating between two running SWF's but still want to know about the SO if there's some kind of a limitation.
UPDATE:
Run the compiled Flex app from disk twice, put the "swf2" into textfield of the second one. Press start on both. The first one that is started never detects that the second one was connected.
<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"
creationComplete="application1_creationCompleteHandler(event)"
>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
private var timer:Timer;
private var so:SharedObject;
protected function application1_creationCompleteHandler(event:FlexEvent):void
{
so = SharedObject.getLocal("mySO");
timer = new Timer(1000);
timer.addEventListener(TimerEvent.TIMER, onTimer);
}
protected function button1_clickHandler(event:MouseEvent):void
{
so.setProperty(txtSwf.text, 'connected');
so.flush();
timer.start();
}
protected function onTimer(event:TimerEvent):void
{
so = SharedObject.getLocal("mySO");
txtLog.text += so.data["swf1"] + "\n";
txtLog.text += so.data["swf2"] + "\n";
txtLog.text += "------------ \n";
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:TextInput id="txtSwf" x="10" y="50" text="swf1"/>
<s:Button x="170" y="50" label="Start" click="button1_clickHandler(event)"/>
<s:TextArea id="txtLog" x="10" y="154" width="279" height="112" />
</s:Application>
You need to force the reading of the file, but not of the data which client already has :/
so = getLocal("mySO");
// set the value from a user input, and set it to 'connected'
so.setProperty(txtName.text, "connected")
// every few seconds check if the other SWF started and modified value
onTimer
{
so = getLocal("mySO"); // read the so file.
var data:Object = so.data
}
Phew, found an answer. Jevgeenij gave me a lead to forcing the re-read of the shared object, but his code didn't work exactly. what I needed to add was this.
so = getLocal("mySO");
// set the value from a user input, and set it to 'connected'
so.setProperty(txtName.text, "connected")
// every few seconds check if the other SWF started and modified value
onTimer
{
so = getLocal("mySO"); // read the so file.
var data:Object = so.data;
so = null // MAKE IT NULL AND HOPE IT'S GC'D BEFORE NEXT TIMER
}
The trick was to set it to null after use, only then it is forcefully re-read and values written from other SWF instance picked up.

How to run a function after the canvas has changed in ActionScript 3.0?

Please understand: I am a total beginner at Actionscript and anything like Actionscript, and though I generally have some idea of what I'm doing as a programmer, I'm having some setbacks in self-learning this language and Flex Builder. Right now I'm trying to make a very simple implementation where a label effectively has its text added to multiple times over the first few seconds that the program is run. The problem I'm running into is that I can't just put all that into one function + one call on that function, as changes to the label's text are apparently not seen until that function runs its full course.
So I tried using a viewstack and cloning the canvas a few times, giving each canvas's version of that label a different bit of text. Then I set the initialize function for the viewstack to change the canvases on regular intervals over the first few seconds. But that didn't work either, as the difference isn't seen until the function runs its full course.
So then I tried putting function calls on the individual canvases' initialize attributes, and those functions aren't being called at all apparently.
What the heck? I know this probably isn't even the way that you're supposed to animate something in ActionScript or Flex, but I still would like to know how to approach the problem this way for future reference. What am I missing? Thanks!
As you've noticed, changes to the displayed output of your program can't happen in the middle of executing a function that you've written. ActionScript is single-threaded, which means that none of the framework code that updates the screen can run while your function is running.
If you're interested in learning exactly what happens in order to update the screen, do a search for "flex component lifecycle" and read some of the stuff you find. It's a bit advanced, but it was the thing that really helped me understand how the Flex framework works.
Anyway, on to your real question - how to animate something. There are many ways, but for your case of progressively adding text to a label, I'd probably use the Timer class.
Here is an example:
<?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">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
private var timer:Timer;
private var words:Array = ["The", "quick", "brown", "fox"];
private function startTimer(event:FlexEvent):void
{
timer = new Timer(1000, 1);
timer.addEventListener(TimerEvent.TIMER, updateText);
timer.start();
}
private privatefunction updateText(event:TimerEvent):void
{
theLabel.text += words.shift() + " ";
if (words.length > 0)
{
timer.reset();
timer.start();
}
}
]]>
</fx:Script>
<s:Label id="theLabel" text="" creationComplete="startTimer(event)"/>
</s:Application>
A basic approach would be to use data binding for the label value, and setTimeout() or setInterval() to cause the delay between updates.
Here is an example:
<?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"
initialize="init()"
minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
[Bindable]
private var displayString:String = "Detonation in ";
private var count:int = 5;
private var timer:uint;
private function init():void {
timer = setInterval(updateDisplay, 1000);
}
private function updateDisplay():void {
displayString += count + ".. ";
if (--count == 0) {
clearInterval(timer);
}
}
]]>
</fx:Script>
<s:Label text="{displayString}"/>
</s:Application>