AS3 flash CC I want to set the width of the parent Movieclip of a Textfield - actionscript-3

I have dynamically created a bunch of textfields, the number of which depends on xml input. The textfields need to be button-enabled, so I thought the easiest solution is to put them in a movieclip and buttonMode that movieclip.
The hierarchy looks like this: main (extends MovieClip) -> klankoefening (a MovieClip) -> Textfieldparent (a MovieClip) -> Textfield
I have a variable maxw which contains the biggest textfield's width and I want all my Textfieldparents to have this width.
I'm trying to do it like this:
//generate textfields
keuzes = new XMLList(lijstOpties.categorie.(#cat == catname));
var nrs:int = keuzes.keuze.length(); //aantal keuzemogelijkheden
var maxw:int = 0; //grootste breedte tf, nodig voor bepalen breedte knoppen
for (var nr:int = 0; nr < nrs; nr++) {
var tf:TextField = createTextfield(werkfmt);
tf.text = keuzes.keuze[nr].toString();
tf.autoSize = "center";
tf.background = true;
tf.backgroundColor = 0xffcc33;
tf.border = true;
tf.borderColor = 0x000000;
tf.name = "keuzetf";
tf.selectable = false;
tf.x = 0;
tf.y = 0;
if (tf.width > maxw) {maxw = tf.width;}
var tfcontainer:MovieClip = new MovieClip();
tfcontainer.buttonMode = true;
tfcontainer.name = keuzes.keuze[nr].toString();
tfcontainer.addChild(tf);
klankoefening.addChildAt(tfcontainer,nr);
}
for (var an:int = 0; an < 3; an++) {
var mcs:MovieClip = klankoefening.getChildAt(an) as MovieClip;
mcs.width = maxw;
trace ("mcs " + mcs.width);
var mtf:TextField = mcs.getChildAt(0) as TextField;
trace ("mtf " + mtf.text);
}
the trace results are
mcs 32.05
mcs 33
mcs 21.25
Which I find odd, because I stated in my second loop that the width for all Textfieldparents should be set to maxw. What is happening?

Consider drawing an underlaying Rectangle of desired width and height. You can use mcs.graphics to do that. The trick is, width of a DisplayObjectContainer is a calculated property, and changing it will likely result in changing scale. Say, a MC has two shapes as chilren, one at (1000,0) and another at (0,0), and both have width of 10 - the resultant width of that MC will be 1010. So, instead of changing width, simulate its change by drawing a rectangle on the MC.
mcs.graphics.clear();
mcs.graphics.lineStyle(0,0,0);
mcs.graphics.beginFill(0x808080,0); // bogus filled rect, to capture events
mcs.graphics.drawRect(0,0,maxw,maxh); // height is needed too
mcs.graphics.endFill();

After thinking a bit more about your problem, I think your issue is that the buttons you're adding the text to have a width entirely defined by the TextField, and because you're set the text to autoSize, it's snapping back to it's original width.
What you will need to do to make the width more indicative of what you need, and allow for easy clicking on the buttons is to create a transparent rectangle in the tfContainer mc, set the width of the tfContainer, and then add the text to the button.

Related

Prevent ColorTransform from changing other filter colors (AS3)

I am trying to change the color of a movieclip when clicked, however I have a shadow filter on it that I want to stay black.
var cty:ColorTransform = new ColorTransform();
cty.color = 0xFFFF00; //color transform yellow
var shdw:DropShadowFilter = new DropShadowFilter();
shdw.color = 0x000000; <----clearly set to black
shdw.distance = 3;
shdw.angle = 45;
shdw.strength = 1;
shdw.blurX = 3;
shdw.blurY = 3;
thisclip.filters=[shdw];
thisclip.addEventListener(MouseEvent.CLICK,myevent);
function myevent(e:MouseEvent):void
{
thisclip.transform.colorTransform = cty;
thisclip.filters=[shdw]; <------ tried adding a refresher but doesnt work
}
The problem is that after the color change the shadow is changed to the same color as the object, is there a way to change the color WITHOUT changing the shadow filter color???
The problem is that after the color change the shadow is changed to
the same color as the object, is there a way to change the color
WITHOUT changing the shadow filter color???
You need to separate the item you want to be drop-shadowed and the item you want to colour transformed. One way is to create a "container" for thisclip and apply the shadow to the container itself, then only transform the colour of this clip. I've modified your code to show what I mean...
var cty:ColorTransform = new ColorTransform();
cty.color = 0xFFFF00; //# yellow
var shdw:DropShadowFilter = new DropShadowFilter();
shdw.color = 0x000000; //# black
shdw.strength = 1;
shdw.distance = 3; shdw.angle = 45;
shdw.blurX = shdw.blurY = 3; //# linked since same value for both
var contMC : MovieClip = new MovieClip;
addChild( contMC ); //# add to stage
contMC.addChild( thisclip ); //# add "thisclip" into container MC
contMC.x = 0; contMC.y = 0; //# set your own position
//thisclip.filters=[shdw];
contMC.filters=[shdw];
thisclip.addEventListener(MouseEvent.CLICK, myevent);
function myevent(e:MouseEvent):void
{
thisclip.transform.colorTransform = cty;
//e.currentTarget.transform.colorTransform = cty; //# use this for ANY listening object
}
Just remember if you create the container MC by code (like I showed) you can reference any added children by their usual instance names like so : thisclip.transform.colorTransform = cty;
But if you create the container MC on stage (with instance name contMC and cut/paste thisclip MC into it, then you now reference it by code as : contMC.thisclip.transform.colorTransform = cty;
tip #1 : To avoid adding a container, if your thisclip itself has an MC (or Sprite) holding some "to be coloured" content you can instead target that content by its instance name :
thisclip.someContent.transform.colorTransform = cty;
(here thisclip becomes the container with shadow, whilst someContent is the inner MC to be colour transformed).
tip #2 : I've added the line e.currentTarget.transform.colorTransform = cty; (but is commented out since not used) to show how you could have any clicked MC respond to the colour transform. Just make sure they are listening to the event like so :
clipInstanceName.addEventListener(MouseEvent.CLICK, myevent);
(now you can have multiple IF checks and adjust colour accordingly per clicked item using cty.color = some new Value; within if statements, before finally setting that .colorTransform = cty; ...

Flash: get absolute position of e.currentTarget

Okay, wasn't sure about the title.
But here's what I am trying to achieve:
Basically i am trying to do something like a quiz, where you can drag and drop the answers into a field. And if they are correkt it should snap the answer field position.
It should be something like
if(myobject.hitTestObject(targetField) && isCorrectAnswer()) {
myobject.x = targetField.x;
myobject.y = targetField.y;
}
But it's not really working.
So here is what I have:
/**
* Generating dragable answer fields based on an array.
**/
function generateAnswer():void {
// creating text format
var myFormat:TextFormat = new TextFormat();
myFormat.color = 0x0066FF;
myFormat.size = 24;
myFormat.align = TextFormatAlign.CENTER
// reference array to store all textfields
var referenceArray:Array = new Array();
// iterate through all answers in vocabListItems and generate textfields
var i:int;
for (i = 0; i < vocabListItems.length; i++) {
var answerField:TextField = new TextField();
// Setting text to current answer
answerField.text = vocabListItems[i];
answerField.width = 140;
answerField.height = 40;
answerField.x = 60+ i*150;
answerField.y = 410;
answerField.background = true;
answerField.backgroundColor = 0xffffff;
answerField.setTextFormat(myFormat);
answerField.selectable = false;
answerField.type = TextFieldType.DYNAMIC
// store the textfield in a container so drag and drop
// will work
var textContainer:Sprite = new Sprite();
textContainer.addChild(answerField);
addChild(textContainer);
referenceArray.push(textContainer);
}
for each (var item in referenceArray) {
item.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
item.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
}
}
And then I start my drag
var start_x:Number;
var start_y:Number;
function startDragging(e: MouseEvent):void {
var object = e.currentTarget;
start_x = e.currentTarget.x;
start_y = e.currentTarget.y;
object.startDrag();
}
And my stop dragging
function stopDragging(e:MouseEvent):void {
e.currentTarget.stopDrag();
if (e.currentTarget.hitTestObject(targetField)) {
e.currentTarget.x = targetField.x;
e.currentTarget.y = targetField.y;
} else {
e.currentTarget.x = start_x;
e.currentTarget.y = start_y;
}
}
So the problem however is, that e.currentTarget.x is starting from 0. And not from the absolute position on the screen. Its always a relative value, so if i drag it to the targetField its x and y is something like -100, -40
If I set it to the targetField x and y it disappears somewhere in the nirvana of the screen.
targetField is in this case just a rectangle drawn on the stage with a x and y of 160
How can I position it to the absolute x and y?
Here's a screenshot
So the top field is the targetField which is only a rectangle with x 161 and y 191.
The field on the bottom are the dragable fields which are the e.currentTarget. But currentTarget.x is always 0.
EDIT
Your e.currentTarget is going to be the textContainer, which you haven't set an x/y on so it will naturally be 0.
It would seem to make more sense to move the container, and not the actual text field when you create it, like so:
for (i = 0; i < vocabListItems.length; i++) {
var answerField:TextField = new TextField();
// Setting text to current answer
answerField.text = vocabListItems[i];
answerField.width = 140;
answerField.height = 40;
//answerField.x = 60+ i*150; //don't move the text field, move the container later
//answerField.y = 410;
answerField.background = true;
answerField.backgroundColor = 0xffffff;
answerField.setTextFormat(myFormat);
answerField.selectable = false;
answerField.type = TextFieldType.DYNAMIC
// store the textfield in a container so drag and drop
// will work
var textContainer:Sprite = new Sprite();
textContainer.x = 60+ i*150;
textContainer.y = 410;
textContainer.addChild(answerField);
addChild(textContainer);
referenceArray.push(textContainer);
//Also, as an aside, there is no reason to loop through the array after this, just add the listeners here
textContainer.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
textContainer.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
}
End Edit
To translate the coordinates from one object to another, you can use the localToGlobal and globalToLocal methods of a display object.
Something like this:
var globalPoint:Point = targetField.localToGlobal(new Point());
var destinationLocalPoint:Point = e.currentTarget.parent.globalToLocal(globalPoint);
e.currentTarget.x = destinationLocalPoint.x;
e.currentTarget.y = destinationLocalPoint.y;
What I'm doing here, is first, getting the global coordinates of the targetField. So it's taking a point (at 0,0) relative to targetField and translating that relative to the stage.
Then I'm making a new Point object that takes that global coordnate set, and translate that to the parent of e.currentTarget.

Actionscript 3 - centring text inside dynamically created MovieClip

I first added a movieClip to my library, then I right clicked the MC in the library and clicked Properties and then named it 'blueBox' and created an Actionscript linkage with the class name as 'blueBox' and base as 'flash.display.MovieClip'. I added this movieclip to the stage like so:
var myText:TextField = new TextField();
var myFormat:TextFormat = new TextFormat();
myFormat.size = 15;
myFormat.color = 0xFFFFFF;
myFormat.align = TextFormatAlign.CENTER;
myText.defaultTextFormat = myFormat;
myText.wordWrap = true;
var blueBoxOne:blueBox = new blueBox();
blueBoxOne.width = 400;
blueBoxOne.addChild(myText);
blueBoxOne.x = 400;
blueBoxOne.y = 40;
myText.x = 0;
addChild(blueBoxOne);
Now, what is myText's x position relative to? It appears as if it is a few pixels to the right of the middle of blueBoxOne. How do I make the center of the text be in the center of the blueBoxOne MovieClip?
The position of a child display object is always relative to its parent. Your TextField shows the text centered but its width is too small - to center the text inside the parent, set its width equal to the parent:
myText.width = blueBoxOne.width;
This is because you never set the width of the TextField and the default width (100px) is just too small for your box.

Remove randomly generated sprites with Mouse.Event

I am making a game for my class where different chemical elements are generated as sprites at the top of the screen and then fall down. Different types are made and I want students to mouse over specific types depending on where they are in the game.
My question is how to write the function to remove them when they are correctly selected? I've tried a lot of different ways but am having a lot of trouble. An example of the code that I wrote to make each element is below and then I have a separate function to move down all of the sprites created.
var spriteArray:Array = new Array();
var halogenArray:Array = new Array("F", "Cl", "Br", "I");
var rndnum:Number = Math.random();
//Halogens
if (rndnum < 0.05)
{
var halo:Sprite = new Sprite();
halo.graphics.beginFill(0x00FF00, 1);
halo.graphics.drawCircle(7.5, 7.5, 15);
halo.graphics.endFill();
halo.addEventListener(MouseEvent.MOUSE_OVER, removeElement);
halo.x = Math.random()*500 + 50;
halo.y = -18;
var textField = new TextField();
textField.text = halogenArray[int(Math.random()*4)];
textField.width = 30;
textField.height = 30;
textField.x = (15 - textField.textWidth)/2; // center it horizontally
textField.y = (15 - textField.textHeight)/2; // center it vertically
halo.addChild(textField);
spriteArray.push(halo);
addChild(halo);
}
At what point are you struggling?
I am assuming it is in determining the types of the halogens.
In your remove function I assume you have the desired type already figured out, you would then compare it to
element.getChildAt(0).text
and you would get the element by either looping across every element in the spriteArray, or using the mouseEvent's target
My suggestion is to use a halogen Class to contain the grapics & textfield, and a vector to hold the objects. It would then be easier to get the type rather than searching the anonymous children of the sprite.
I believe you are looking for something like this:
//give your textfields a name, it isn't totally necessary as we can do getChildAt(0)
//but it's more readable, and if you decide to add more children before you
//add the text field, then this will still work
var textField = new TextField();
textField.text = halogenArray[int(Math.random()*4)];
textField.width = 30;
...
textField.name = "haloTx"; //for tracking later
//assuming you have some variable set to the correct answer
var correctAnswer:String = "F";
function removeElement( e:MouseEvent ):void {
var element:TextField = ( e.target as Sprite ).getChildByName( "haloTx" );
//if we have the correct element, remove from parent and list
if ( element && element.text == correctAnswer ) {
var index:int = spriteArray.indexOf( e.target as Sprite );
removeChild( spriteArray.splice( index, 1 )[0] );
}
}
Although #VBCPP is right, doing that in a separate class is definitely the best way organizationally. Which might look something like:
class ElementSprite extends Sprite {
public var textField:TextField;
//pass shapeArgs as string, so say a circle at x=7.5, y=7.5, and radius=15 -- shapeArgs = "7.5, 7.5, 15"
public function ElementSprite( element:String, drawShape:String="Circle", shapeArgs:String="7.5, 7.5, 15", fillColor:uint=0x00FF00 ) {
//set textfield properties etc. or textFormat
textField = new TextField();
textField.text = element;
addChild( textField );
//if you passed some arguments to draw our shape
if ( shapeArgs != "" ) {
graphics.beginFill( fillColor );
graphics[ "draw" + drawShape ].apply( this, shapeArgs.split( "," ) );
}
}
public function get currentElement():String { return textField.text }
}
Then you would use it like so in your if statement if (rndnum < 0.05):
var elementSprite:ElementSprite = new ElementSprite( "A" );
//elementSprite.x = set your x;
//elementSprite.y = set your y;
addChild(elementSprite);
That would be replacing all your current code in that if statement. This is all a working example, if you have an questions feel free to comment.

AS3: How to print a full page Movieclip?

I am trying to print a movieclip on a button click. The movieclip originally is 300 x 400. I want to scale the movieclip to the highest size possible within the page and then print it.
Currently, I can print the movieclip using the following :
on(release)
{
var pj:PrintJob = new PrintJob();
pj.start();
pj.addPage(my_mc);
pj.send();
}
Thanks
You can first scale the movieclip so its size matches the maximum width and height of the page:
my_mc.width = pj.pageWidth;
my_mc.height = pj.pageHeight;
This will stretch the movie clip however, so to fix that we set the scale of both x and y to the smallest scale:
my_mc.scaleX = my_mc.scaleY = Math.min(my_mc.scaleX, my_mc.scaleY);
Final code:
var pj:PrintJob = new PrintJob();
pj.start();
var printArea : Rectangle = new Rectangle(0, 0, my_mc.width, my_mc.height);
my_mc.width = pj.pageWidth;
my_mc.height = pj.pageHeight;
my_mc.scaleX = my_mc.scaleY = Math.min(my_mc.scaleX, my_mc.scaleY);
pj.addPage(my_mc, printArea );
pj.send();