ActionScript3: Changing button text - actionscript-3

I have a button instance named Button that I have in my movie, this instance has a Dynamic Text object in it named myText, how can I change the text? I already tried Button.myText.text = "Stuff";
I get the error "Scene 1, Layer 'Layer 1', Frame 1, Line 7 1119: Access of possibly undefined property myText through a reference with static type flash.display:SimpleButton." When I compile.
AS:
import flash.events.MouseEvent;
TheButton.addEventListener(MouseEvent.CLICK, onClick);
function onClick(Event:MouseEvent):void{
TheButton.myText.text = "Moo";
}

You can't use the dot syntax to access a display object container's child display objects in AS3 as you did in AS2. Normally you would use the display object container's getChildByName() method to get its child display objects, but because your dealing with an instance of SimpleButton which is a subclass of DisplayObject that method doesn't exist. The simple solution is to change you button from a button to a movieclip after which the following should work:
TheButton.addEventListener(MouseEvent.CLICK, onClick);
function onClick(Event:MouseEvent):void
{
TheButton.getChildByName("myText").text = "Moo";
}
Note: the TextField display object in the TheButton display object container must have an instance name of "myText" and obviously the TheButton display object container must have an instance name of "TheButton".
Also if your going with this approach you may want to rewrite the code as follows:
import flash.display.DisplayObjectContainer
import flash.events.MouseEvent;
import flash.text.TextField;
button.addEventListener(MouseEvent.CLICK, onButtonClick);
function onButtonClick(e:MouseEvent):void
{
var button:DisplayObjectContainer = DisplayObjectContainer(e.target);
var textField:TextField = TextField(button.getChildByName("textField"));
textField.text = "Moo";
}// end function
[UPDATE]
Another solution is to create a movieclip/sprite object for the SimpleButton object's upState, overState and downState properties like the following:
import flash.display.DisplayObjectContainer;
import flash.display.SimpleButton;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
var simpleButton:SimpleButton = new SimpleButton();
addChild(simpleButton);
var content:Sprite = new Sprite();
draw(content.graphics, 0xFF0000);
var textField:TextField = new TextField();
textField.name = "textField";
textField.text = "UP";
content.addChild(textField);
simpleButton.upState = content;
simpleButton.overState = content;
simpleButton.downState = content;
simpleButton.hitTestState = content;
simpleButton.addEventListener(MouseEvent.MOUSE_OVER, onSimpleButtonMouseOver);
simpleButton.addEventListener(MouseEvent.MOUSE_DOWN, onSimpleButtonMouseDown);
simpleButton.addEventListener(MouseEvent.MOUSE_OUT, onSimpleButtonMouseOut);
simpleButton.addEventListener(MouseEvent.MOUSE_UP, onSimpleButtonMouseUp);
function onSimpleButtonMouseOver(e:MouseEvent):void
{
var content:DisplayObjectContainer = DisplayObjectContainer(SimpleButton(e.target).overState);
var textField:TextField = TextField(content.getChildByName("textField"));
textField.text = "OVER";
draw(content.graphics, 0xC8C8C8);
}// end function
function onSimpleButtonMouseDown(e:MouseEvent):void
{
var content:DisplayObjectContainer = DisplayObjectContainer(SimpleButton(e.target).downState);
var textField:TextField = TextField(content.getChildByName("textField"));
textField.text = "DOWN";
draw(content.graphics, 0x646464);
}// end function
function onSimpleButtonMouseUp(e:MouseEvent):void
{
var content:DisplayObjectContainer = DisplayObjectContainer(SimpleButton(e.target).overState);
var textField:TextField = TextField(content.getChildByName("textField"));
textField.text = "OVER";
draw(content.graphics, 0xC8C8C8);
}// end function
function onSimpleButtonMouseOut(e:MouseEvent):void
{
var content:DisplayObjectContainer = DisplayObjectContainer(SimpleButton(e.target).upState);
var textField:TextField = TextField(content.getChildByName("textField"));
textField.text = "UP";
draw(content.graphics, 0xFF0000);
}// end function
function draw(graphics:Graphics, color:uint):void
{
graphics.clear();
graphics.beginFill(color)
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
}// end function

It's extremely easy.
Suppose I have a button that was created from the Components menu called myBtn.
myBtn.label = "Awesome text";
You use label instead of creating a textfield over it then setting the texfield's text.
That's it!

In the absence of extra information as to whether you're getting errors and what they are, my first assumption would be that the system is having trouble with the variable name "Button". There is already a class named "Button", so I would think the system is thinking you are trying to call a class-level method/var of class Button instead of accessing a variable named "Button". I would try changing the name of the button var to something unique.

Children of SimpleButton are inaccessible from Actionscript; notice that it does not actually extend DisplayObjectContainer. The SimpleButton class is a bit of a mutant.
You will have to use a container MovieClip and put the SimpleButton and the label TextField inside. Alternatively you can roll your own button out of a MovieClip or use a component library. SimpleButton is not meant for this kind of use.

Related

AS3 error 2025 removechild

I made a game with AS3 where you must click on falling bombs before they explode and destroy a wall. Now, I'm trying to remove the bombs that fell just when the wall got destroyed, so I did a removeChild(blob) in my game over function, since these bombs are added to the stage with an addChild(blob), and I keep getting this error:
ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller. ...line 80
...And by the way, I've already tried things like these:
this.parent.removeChild(this);
or
blob.parent.removeChild(blob);
or
stage.removeChild(blob);
but I still get the same error.
Here's my code:
package cem {
import flash.geom.*;
import flash.display.*;
import flash.events.*;
import flash.display.MovieClip;
import flash.utils.Timer;
public class Jeu extends MovieClip {
//Variables publiques
var decor: MovieClip = new Decor();
var chrono: cem.Chronometre;
var nextObject: Timer = new Timer(800, 0);
var _menu: MovieClip = new Menu();
var _btnJouer: MovieClip = new BoutonJouer();
var blob: cem.Blob;
var score: Score;
public function Jeu() {
// constructor code
//***********************************************Mettre menu***********************************************//
addChild(_menu);
addChild(_btnJouer);
_btnJouer.x = 500;
_btnJouer.y = 500;
_btnJouer.addEventListener(MouseEvent.CLICK, jouer);
}
//*****************************************************Jouer**************************************************//
function jouer(e: MouseEvent) {
removeChild(_menu);
addChild(decor);
decor.gotoAndStop(1);
chrono = new cem.Chronometre();
addChild(chrono);
chrono.demarrer();
score = new Score();
score.x = 600;
nextObject.addEventListener(TimerEvent.TIMER, creerBlobs);
nextObject.start();
}
//**************************************************Créer Bombes***********************************************//
function creerBlobs(e: TimerEvent) {
blob = new cem.Blob();
blob.x = Math.floor(Math.random() * (stage.stageWidth - blob.width));
addChild(blob);
blob.gotoAndStop(1);
blob.addEventListener("explosion", perdreVies);
}
//************************************************Perdre des vies*********************************************//
public function perdreVies(e: Event) {
decor.moinsVie();
decor.addEventListener("gameisover", _gameOver);
}
//************************************************Partie terminée*********************************************//
public function _gameOver(e: Event) {
blob.removeEventListener("explosion", perdreVies);
removeChild(blob);
chrono.arret();
addChild(_menu);
addChild(_btnJouer);
nextObject.stop();
nextObject.removeEventListener(TimerEvent.TIMER, creerBlobs);
nextObject.removeEventListener(TimerEvent.TIMER, creerBlobs);
addChild(score);
score.affichageScore.text = "votre score: " + chrono.secondes * 1000;
}
}
}
The var name blob can only reference one specific Blob object at a time.
Each time you create a new Blob you are reassigning the name blob to the last created one, losing the reference to the previous one.
That error says, at the time you call removeChild the specific Blob assigned to the name "blob" is not a child, it is not on the display list.
So its referencing the wrong Blob or its already removed from the display list.
To avoid errors u can also say something like if (blob.parent) remove child blob

variable in class not returning updated value

I'm relatively new to actionscript 3, and this one has me stumped. Here's my class;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class Clicker extends MovieClip {
public var clicks:uint;
public var string:String;
public function Clicker() {
// constructor code
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
addEventListener(Event.ADDED_TO_STAGE, alignCentre);
string = "clicks: ";
clicks = 5; // I can change it here
}
public function clicked(e:MouseEvent):void{
clicks++;
trace(clicks); // it outputs updated value here
}
public function alignCentre(e:Event):void{
x = stage.stageWidth / 2 - width/2;
y = stage.stageHeight / 2 - height/2;
}
public function addedToStageHandler(e:Event):void{
this.stage.addEventListener(MouseEvent.CLICK, clicked);
}
public function get_clicks():uint{
trace(clicks); // gives me whatever I initialise it to in the constructor
return clicks;
}
}
}
I want to return the value of clicks from my Clicker class, but the value remains whatever I set it to in the constructor within get_clicks(), and I'm not sure why.
The variable has class scope, so why would it return the default value (in this case, 5) of clicks from get_clicks()? my clicked() method traces the correctly updated value. Is it a scope issue? I'm very confused.
This is my first frame where I create the object;
import flash.display.DisplayObject;
import flash.events.MouseEvent;
import flash.text.TextField;
var clicks:TextField = new TextField();
var circle:Clicker = new Clicker();
clicks.text = circle.get_clicks().toString();
trace(circle.get_clicks());
addChild(circle);
addChild(clicks);
As you'd expect from the problem I'm having, trace spits out 5 over and over, and Clicks doesn't change from 5.
Edit:
There was a mistake, but fixing it has caused clicks not to update at all. I had a library version of the movieclip on my first frame rather than adding the object to the frame with addChild. Now that I've added circle, clicks does not update as my clicked() method isn't being triggered when I click my object.
Resolved:
import flash.display.DisplayObject;
import flash.events.MouseEvent;
import flash.text.TextField;
var clicks:TextField = new TextField();
var circle:Clicker = new Clicker();
var myTimer:Timer = new Timer(100,0);
myTimer.addEventListener(TimerEvent.TIMER, timerListener);
function timerListener (e:TimerEvent):void{
trace("Timer is Triggered");
trace(circle.get_clicks());
clicks.text = circle.get_clicks().toString();
addChild(circle);
addChild(clicks);
}
myTimer.start();
Rather than use the timeline and frames, I wrapped the code I wanted to repeat in a timer listener. Works exactly as intended.

Get global coordinate of a dynamically created nested component?

In the code below, I have created 4 sprites (picn) inside a sprite (noteholder). How can I get the absolute values of the picn instances that I create? I know about the localToGlobal function, but I can't figure out how to use that in this case. At this point, I think I need the container because I need to be able to move the sprites after creation. Any help is greatly appreciated. Thanks!
package
{
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import Main;
public class Notes extends Sprite
{
private var speed:int = 14;
[Embed(source="../lib/Dodgethis.jpg")]
private var picn:Class;
private var noteholder:Sprite = new Sprite();
public function appear() {
trace ("appear ran")
var arr1:Array = new Array;
var numnotes:Number = 4;
Main.StageRef.addChild(noteholder);
trace (noteholder.x, noteholder.y);
for (var i = 0; i < numnotes; i++)
{
//trace (i);
var nbm:Bitmap = new picn;
noteholder.addChild(nbm);
nbm.y = i * 50;
arr1.push(nbm);
You need to hold a reference to the display objects you dynamically create — you don't need a "name" which is just an abstraction of instance variables when creating visual assets in the Flash IDE.
I've updated your code to demonstrate the concept:
package
{
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.display.Stage; // added this
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle; // added this (see below)
import Main;
public class Notes extends Sprite
{
private var speed:int = 14;
[Embed(source="../lib/Dodgethis.jpg")]
private var NoteBMP:Class;
private var display:Sprite = new Sprite();
private var notes:Array = []; // make notes an instance variable, so it's accessible throughout the class
// I renamed appear to 'build' because I'm a pedantic moron
public function build():void
{
trace ("build ran");
var noteCount:int = 4;
Main.StageRef.addChild(display);
for (var i:int = 0; i < noteCount; i++)
{
var nbm:Bitmap = new NoteBMP;
display.addChild(nbm);
nbm.y = i * 50;
notes.push(nbm); // by adding the display objects to an array you can reference them later
then at some later point in the Notes class you can reference your bitmaps by looping through the notes array
// initialize variables outside the loop
var localPos:Point
var globalPos:Point;
var bounds:Rectangle;
var n:DisplayObject;
var stage:Stage = Main.StageRef;
for (var i:int = 0; i < notes.length; i++)
{
trace( 'note' + i );
n = notes[i] as DisplayObject; // cast during assignment since array items have ambiguous type
// getBounds() returns a Rectangle representing the bounding box of the display object
bounds = n.getBounds(stage);
trace(
'getBounds:',
bounds.top, bounds.left, bounds.bottom, bounds.right
);
// localToGlobal() returns a Point which is just the position
localPos = new Point( n.x, n.y );
globalPos = display.localToGlobal(localPos)
trace(
'localToGlobal:',
globalPos.x, globalPos.y
);
}
A few points:
I refactored some names to make the intention more clear
I wonder why your Notes class extends Sprite, since it adds display (what you called "noteholder") to the stage. Assuming that's correct just make Notes a generic object i.e. doesn't seem to need to extend another class.
You can use getBounds about getBounds
// call this after added to stage
if (stage) {
var r:Rectangle = nbm.getBounds(stage);// r's x and y is the global pos
}
And I think localToGlobal should work in your case. Move the sprites don't have effect on localToGlobal

How to get all images from HTML control in Flex?

I have a HTML control in Flex that succefully loads a page.
Is there someway that i can extract or get all the images from that HTML control and show them?
You can actually use the HTML document DOM and ask about all img tags. It's a lot cleaner this way.
Once you get the DOM it's all standard so you can check HTML DOM documentation if you're unsure about something.
Here's quick example. I'll just write the onComplete function, as it would be called in Taurayi's example
var _imagesSrc:Array= [];
private function onComplete(e:Event):void
{
var htmlLoader:HTMLLoader = e.target as HTMLLoader;
var doc:* = htmlLoader.window.document; //use void (*) type for DOM objects
var imgTags:* = doc.getElementsByTagName("img");
if(imgTags)
{
for(var i:int=0;i<imgTags.length;i++)
{
var src:* = imgTags[i].getAttribute("src"); //not sure about return type here, could also be a String
if(src)
_imagesSrc.push(src.toString());
}
}
}
Well an idea would be that you load the html page using HTMLLoader and then when it has loaded you get the source. Then using a parser or regex you would extract the values of the src attributes of the <img /> tags. The values of the src attributes will be the urls to the images on the page you loaded. Then using Loader and said urls, you would load them into your flash application.
Here's an example:
Main.as(document class):
package
{
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.html.HTMLLoader;
import flash.net.URLRequest;
public class Main extends Sprite
{
public function Main():void
{
var htmlLoader:HTMLLoader = new HTMLLoader();
htmlLoader.addEventListener(Event.COMPLETE, onComplete);
htmlLoader.width = stage.stageWidth;
htmlLoader.height = stage.stageHeight;
htmlLoader.load(new URLRequest("http://www.wampserver.com/"));
}// end function
private function onComplete(e:Event):void
{
var htmlLoader:HTMLLoader = e.target as HTMLLoader;
var body:String = htmlLoader.window.document.body.outerHTML;
var imgTags:Array = getImgTags(body);
var src:String = getSrc(imgTags[1]);
trace(src); // output: http://www.wampserver.com/wp-content/themes/wampserver/img/home-title.png
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete);
loader.load(new URLRequest(src));
}// end function
private function onLoaderComplete(e:Event):void
{
var loader:Loader = (e.target as LoaderInfo).loader;
addChild(loader);
}// end function
private function getImgTags(source:String):Array
{
return source.match(/(<img.*?>)/g);
}// end function
private function getSrc(imgTag:String):String
{
return imgTag.match(/src="(.*?)"/)[1];
}// end function
}// end class
}// end package
Keep in mind this is just an idea, there are some obvious flaws.

how do I access the main class's stage? + Can I pass functions as arguments like this?

There are two files in my actionscript project named "TestAPP", TestAPP.as and Draggable.as
TestAPP.as:
package {
import flash.display.Sprite;
import flash.display.Stage;
public class TestAPP extends Sprite
{
var _mainStage:Stage;
public function TestAPP()//This is where we test the UI components.
{
var sp:Sprite = new Sprite();
_mainStage = stage;
_mainStage.addChild(sp);
sp.graphics.beginFill(0x00FF00);
sp.graphics.drawCircle(0,0,10);
sp.graphics.endFill();
sp.x = 50;
sp.y = 50;
var draggable1:Draggable = new draggable(sp,_mainStage,limitingfunc);
}
public function limitingfunc(x:Number,y:Number):int{
return 0;
}
}
}
And for the draggable.as:
package
{
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.MouseEvent;
public class Draggable
{
private var _limitingFunc:Function;
private var _which:Sprite;
private var _MouseSavedX:Number;
private var _MouseSavedY:Number;
private var _stage:Stage;
public function Draggable(which:Sprite,stage:Stage,limitingfunc:Function)
{
_limitingFunc = limitingfunc;
_which = which;
_stage = stage;
_which.addEventListener(MouseEvent.MOUSE_DOWN,begin_drag);
}
//limiting func: returns 0 when the object is free to move that place.
//returns -1 when the user wants to block X coordinate changes (but maintain Y free)
//returns -2 when the user wants to block Y ...
//returns -3 or less when the user wants to block both X and Y from changing.
//returns
private function Return_0(x:Number = 0,y:Number = 0):int{
return 0;
}
private function begin_drag(ev:MouseEvent):void{
var xTo:Number = _stage.mouseX - _MouseSavedX + _which.x;
var yTo:Number = _stage.mouseY - _MouseSavedY + _which.y;
var limitingFuncReturnValue:int = _limitingFunc(xTo,yTo);
if(limitingFuncReturnValue == 0){//free to move.
_which.x = xTo;
_which.y = yTo;
}
else if(limitingFuncReturnValue == -1){//free to move Y
_which.y = yTo;
}
else if(limitingFuncReturnValue == -2){
_which.y = yTo;
}
//else:do nothing.
}
}
}
In "my actionscript theory", I'm supposed to see a circle that follows the mouse when I click it. (The draggable is not fully implemented) But the circle doesn't even budge :(
...I've been trying to figure out how to access the main class's stage property. I've googled for it, but still no progress.
Please help this helpless newb!!! I'll really appreciate your help:)
Thank you!
You're implementing your 2nd class as "draggable", when you named it (and it has to be named) "Draggable" with an upper case. Change it and see if that works. You seem to be passing in the parent classes stage correctly though.
If TestAPP is your document class. You can access to the stage thru the stage property (like your are doing in your example).
If TestAPP is not the document class, you should listen first to the ADDED_TO_STAGE event and then access to the stage property.
There is no need to add _mainStage property because you already have the stage property.
In order to move objects around, you need to use the ENTER_FRAME event.
You can access the main class' stage property the same way you do it for any DisplayObject on the stage: Use this.stage.
So just this should be enough:
var sp:Sprite = new Sprite();
stage.addChild(sp);
You could then pass the sprite and the main class' stage as a parameter, the way you did - but I would recommend creating a subclass Draggable extends Sprite and instantiate the whole thing using new Draggable() instead.
The new object will automatically have its own reference to stage as soon as it is added to the display list, and limitingFunc could be a member of Draggable, as well.
Also, you can use the startDrag() and stopDrag() methods, no need to implement your own: You can specify a Rectangle as a parameter to startDrag() to limit the permitted movement.
if you need it so much you may:
private static var _instance: TestAPP;
//...
public function TestAPP(){
_instance = this;
//...
}
public static function get instance():TestAPP{
return _instance;
}
public static function set instance(newVal: TestAPP):void{
_instance = newVal;
}
and you'll be able to reference its stage from any class where your TestAPP will be imported just with TestAPP.instance.stage.
but _instance is a property of the class TestAPP itself, not its instances