PDA

View Full Version : e.currentTarget question


MadBeat
10-22-2009, 09:06 PM
Hi
Basically I have 16 buttons which I want to change colour when turned on and off kinda thing using colourTransform. I have achieved this by using 16 event listners and 16 functions but know there must be a better approach. I kinda got it working with one function using e.currentTarget but cant figure out to store the on/off state of each button to send to the one function whilst using e.currentTarget. E.G. Turn one button on and will have to click another twice to turn it on.

How could this be solved. Hope this makes sense. Thx.

b1.addEventListener(MouseEvent.CLICK, onPattern, false, 0,
true);
b2.addEventListener(MouseEvent.CLICK, onPattern, false, 0,
true);

function onPattern(e:MouseEvent):void{
on1 = on1+1
if(on1 >= 2){
on1 = 0
colorInfo1.greenOffset = 0;
colorInfo1.blueOffset = 0;
colorInfo1.redOffset = 0;


e.currentTarget.transform.colorTransform = colorInfo1;
}

if(on1 == 1){


colorInfo1.greenOffset = 20;
colorInfo1.blueOffset = -100;
colorInfo1.redOffset = -65;

e.currentTarget.transform.colorTransform = colorInfo1;
}


}

nyghtrunner
10-22-2009, 09:22 PM
Here's a little class that helps out a whole lot with what you are trying to do.


package coms.utils {

public class proxyFunction {

public static function create($function:Function,... $rest):Function {
return function(... $moreRest):void {
$function.apply(this,$moreRest.concat($rest));
}
}

}
}


And then you use it like this:


instance.addEventListener(MouseEvent.MOUSE_DOWN,pr oxyFunction.create(buttonClick,instance));

function buttonClick(e:MouseEvent,instance:*):void {
//do stuff
trace(instance); //will trace the button.
}


As you will notice, you can pass the actual instance (or anything else) to the listener function along with the event. Not just the event.

Tinker around, extend it, do other stuff with it... It's a lot quicker than creating custom event dispatches, though less powerful.

Hope this helps!

RogerClark
10-22-2009, 09:25 PM
There are various solutions to storing the on / off state of the button.

One way is to subclass the class you are using as the button e.g. MovieClip and add an extra property e.g. public var onState:Boolean;

If you already use a custom class, just add the property to the class.

There may be other hacky ways to do it e.g using some other property of the mc that you don't care about that much e.g set very slight differences in alpha for on and off states.
But you could be better off subclassing

MadBeat
10-22-2009, 09:32 PM
Sick! Glad I asked thx. If u dont mind are able to explain the basis of how that bit code works!

Have tested it and works a treat but a bit of a mystery to my coding level, I can visualize how I want my code to work but have trouble implementing it?

Abracadabra! :)

maskedMan
10-22-2009, 09:37 PM
If you use that proxyFunction, be aware that you are setting yourself up such that you can never remove the eventListener. Not a small consideration if cleanup and memory management is an issue, or if your swf will be loaded into a container swf.

MadBeat
10-22-2009, 09:38 PM
what do u mean by a sub class, i knw what a class is. Just getting to grips with classes thx. All help is awesome.

nyghtrunner
10-22-2009, 09:48 PM
Yeah... I think I went beyond the pail with my reply... There's no need to complicate things like that.

I agree with RogerClark. Create a class. Give each button a number, and an open/close Boolean, and just take care of the rest in the mouse listener method.

Then it's pretty much a simple if/else statement to handle stuff.


if (onState == true) {
//do stuff to turn button "off"
onState = false;
} else {
//do stuff to turn button "on"
onState = true;
}


And in the class, you can give each one a number, and each instance will "know" what number it is, so you can swap things up based on that number. Make sense?

lordofduct
10-22-2009, 09:56 PM
no offense to nyghtrunner but I'm with maskedMan about issues with that code.

Not only does it make it nye impossible to remove the event listener (which can cause memory leaks and other memory issues). But it also creates a scope dependency that can prove whacky.

Not the function inside the proxyFunction is scoped to access values defined in proxyFunction... this leaves the two params $function and $params floating somewhere in lala land... with no reference or symbolic link going on. It could randomly get stuck in memory, be lost randomly (causing the function to fail).

Sorry, it just logically doesn't work out in the end. It plays on some of the buggy scoping issues AS3 has, which can further more cause this to break down the road if these scoping bugs get fixed. Consider it, you are relying on the internal function being scoped to proxyFunction at all times.



@OP - you can do several things to store the on/off value. You can create a property on the Button that can be set to true or false, you can create a Dictionary that you store the value of the Button in, you can check the presence of what is done when on (if transform.colorTransform == some crap), etc etc. And all of this can be accessed abstractly as e.currentTarget.

MadBeat
10-22-2009, 10:00 PM
yeah totally, I agree. lol.

nyghtrunner
10-22-2009, 10:03 PM
Here's a little class that helps out a whole lot with what you are trying to do.


package coms.utils {

public class proxyFunction {

public static function create($function:Function,... $rest):Function {
return function(... $moreRest):void {
$function.apply(this,$moreRest.concat($rest));
}
}

}
}



Masked Man is right. This is not an "elegant" solution in many ways, just a quick one.

Here's pretty much how it works. When you call "proxyFunction.create(funcName,...rest)", you call the class, and send it the function name (funcName), and any arguments you would like to send to the function (instance, etc.).

The proxyFunction then essentially returns a function for the named function, along with the extra parameters, which are concatenated from the ...$rest (parameters passed to it). Does that make sense?

It's a nifty little hack, but as the Masked one said... It's static, so it's always there in the memory.

In retrospect, I would recommend classing out the buttons, and not using the proxyFunction, as much fun as it is.

nyghtrunner
10-22-2009, 10:05 PM
no offense to nyghtrunner but I'm with maskedMan about issues with that code.


None taken, because I actually agree. Like I said, it's not "elegant", just a nifty hack for a lazy programmer who doesn't want to create custom event listeners for some things, especially when testing.

And it's easy to use.

MadBeat
10-22-2009, 10:09 PM
thx nyghtrunner makes sense, yes.

If and else statement makes perfect sense. When you say give each button a number do mean within the instance name or do mean make a class that assigns each instance of the button a number that the function will use to target the button pressed?

MadBeat
10-22-2009, 10:12 PM
Is anyone watching Question Time.

nyghtrunner
10-22-2009, 10:29 PM
thx nyghtrunner makes sense, yes.

If and else statement makes perfect sense. When you say give each button a number do mean within the instance name or do mean make a class that assigns each instance of the button a number that the function will use to target the button pressed?

This is essentially what I mean:


package {

import flash.display.MovieClip;
import flash.events.MouseEvent;

public class myButton extends MovieClip {

private var idNum:uint;
private var onState:Boolean = false; //assuming it's not on when you start

public function myButton() {
init();
}

private function init():void {
this.addEventListener(MouseEvent.MOUSE_DOWN,swapSt ate);
}
private function swapState(e:MouseEvent):void {
trace(idNum); //should return the button number from when you created it

if (onState == true) {
//do stuff to turn button "off"
onState = false;
} else {
//do stuff to turn button "on"
onState = true;
}

}

public function set _idNum(num:uint):void {
idNum = num;
}
public function get _idNum():uint {
return idNum;
}

}
}


That's the stripped down class for the button.

Here's how I would attach them, and set each number:

var numButtons:uint = 16;
var buttons:Array = new Array();

for (var i:uint=0;i<numButtons;i++) {
var tempButton:myButton = new myButton();
tempButton._idNum = i;
buttons[i] = tempButton;
tempButton.x = ?;//formula for x
tempButton.y = ?;//formula for y
addChild(tempButton);
}


So, you have created 16 unique instances of the "myButton" class, given them an x/y, passed them into an array for storage (if necessary), and given each one a unique number (0-15).

When you click on each one, it should trace out its number. With that, you can honestly do pretty much anything, it's just a question of how/what.

This also gets around my pet peeves with the e.currentTarget... You need to remember to turn off the mouseChildren of the class, or things like text boxes/other instances/etc will throw the functions, because, while the mouseEvent dispatches, if you are on a textbox (especially dynamic or input), the text box is the target, not the class itself...

That was why I did the proxyFunction a while back in the first place... But it was for something created on the timeline that I didn't want to make a class for.

MadBeat
10-22-2009, 10:36 PM
Well that is exactly the fix I needed. Its for a drum machine Ive been making which currently has about 4000 lines of code to which Im sure I could code much better.

Thx. check out my old version.

http://www.newgrounds.com/portal/view/464751

matbury
10-22-2009, 11:31 PM
Hey guys, not to be nit-picky or anything but*...

Personally, I wouldn't add the event listener inside the button class itself and here's why. On the MOUSE_DOWN event, the button gives a nice clear visual response that it has indeed been clicked and on the MOUSE_UP event it should give another nice clear visual response that is has been released. The problem is that the button can receive the MOUSE_UP and MOUSE_DOWN events in any order, for example when users move the mouse around while they're clicking. What you can end up with is a bunch of buttons that are half-way through their up or down states and it looks messy.

Here's a solution, for simplicity's sake on just one button but you can replicate this anyway you like:

var btn:MyButton = new MyButton();
btn.addEventListener(MouseEvent.MOUSE_DOWN, downHandler);
// position, variables, etc.
var currentBtn:MyButton; // remember the last button hit

function downHandler(event:MouseEvent):void {
currentBtn = event.currentTarget as MyButton;
currentBtn.removeEventListener(MouseEvent.MOUSE_DO WN, downHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, upHandler); // add the up handler to the stage, not the btn
// do stuff to the button, call functions, etc.
}

function upHandler(event:MouseEvent):void {
currentBtn.addEventListener(MouseEvent.MOUSE_DOWN, downHandler);
stage.removeEventListener(MouseEvent.MOUSE_UP, upHandler);
// do stuff to the button, call functions, etc.
}


This way, you also only ever have one event listener on each button making your SWFs more efficient.

Hope this helps! :)

* Yeah, yeah, I know, It's like saying I'm not racist but... !!?

MadBeat
10-23-2009, 05:17 PM
:confused:Cant get the previous post to work, im missing something.
var btn:MyButton = new MyButton();

is mybutton a movieclip or button in my library which has been exported for AS with the class name MyButton that needs to be added to stage, or is MyButton a MC instance?

Please bear with me here, im just getting my head around a few things. Thx.

nyghtrunner
10-23-2009, 05:48 PM
Hey guys, not to be nit-picky or anything but*...

Personally, I wouldn't add the event listener inside the button class itself and here's why. On the MOUSE_DOWN event, the button gives a nice clear visual response that it has indeed been clicked and on the MOUSE_UP event it should give another nice clear visual response that is has been released. The problem is that the button can receive the MOUSE_UP and MOUSE_DOWN events in any order, for example when users move the mouse around while they're clicking. What you can end up with is a bunch of buttons that are half-way through their up or down states and it looks messy.

Here's a solution, for simplicity's sake on just one button but you can replicate this anyway you like:

var btn:MyButton = new MyButton();
btn.addEventListener(MouseEvent.MOUSE_DOWN, downHandler);
// position, variables, etc.
var currentBtn:MyButton; // remember the last button hit

function downHandler(event:MouseEvent):void {
currentBtn = event.currentTarget as MyButton;
currentBtn.removeEventListener(MouseEvent.MOUSE_DO WN, downHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, upHandler); // add the up handler to the stage, not the btn
// do stuff to the button, call functions, etc.
}

function upHandler(event:MouseEvent):void {
currentBtn.addEventListener(MouseEvent.MOUSE_DOWN, downHandler);
stage.removeEventListener(MouseEvent.MOUSE_UP, upHandler);
// do stuff to the button, call functions, etc.
}


This way, you also only ever have one event listener on each button making your SWFs more efficient.

Hope this helps! :)

* Yeah, yeah, I know, It's like saying I'm not racist but... !!?

Hey Mat,

No worries on being racist, err... I mean nit-picky. ;)

Just out of curiosity, though... Could you not use the same logic in the class itself? I simply mean, add the MOUSE_DOWN event, and in that handler, add the MOUSE_UP function to the stage to create essentially the same thing as the old "onReleaseOutside" in AS2? Because that's essentially the problem you're describing, if I'm reading this right...

Sort of like this:

private function startDrawing(e:MouseEvent):void {
container = new MovieClip();
container.x = mouseX;
container.y = mouseY;
target.addChild(container);

myLine = new Sprite();
myLine.graphics.lineStyle(_lineWidth,_color,_alpha );
myLine.graphics.moveTo(0,0);
container.addChild(myLine);

var blur:BlurFilter = new BlurFilter();
blur.blurX = blurX;
blur.blurY = blurY;
blur.quality = BitmapFilterQuality.HIGH;
myLine.filters = [blur];

underlyer = new Sprite();
underlyer.graphics.lineStyle(6,_color,0);
underlyer.graphics.moveTo(0,0);
container.addChild(underlyer);

drawrings.push(underlyer);

stage.addEventListener(MouseEvent.MOUSE_MOVE,isDra wing); //looking specifically at this
stage.addEventListener(MouseEvent.MOUSE_UP,stopDra wing); //and this
}
private function isDrawing(e:MouseEvent):void {
myLine.graphics.lineTo(mouseX-container.x,mouseY-container.y);
underlyer.graphics.lineTo(mouseX-container.x,mouseY-container.y);
}
private function stopDrawing(e:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_MOVE,is Drawing); //and then these
stage.removeEventListener(MouseEvent.MOUSE_UP,stop Drawing);
}


Granted, it's for a pen tool, not a button click, but the idea is the same, I think. Correct me if I'm wrong, of course. It just still seems cleaner to me to try to keep things in the class itself, because it leaves more flexibility later on, I would think.

matbury
10-23-2009, 09:12 PM
If you wanted the listener to be in the button code itself, you'd have to pass in a reference to the main stage:
var btn:MyButton = new MyButton(stage);

Then in the class:
public function MyButton(st:Stage) {
_stage = st;
}

private function aButtonEvent(event:MouseEvent):void {
_stage.addEventListener(MouseEvent.MOUSE_UP, upHandler);
}

That ought to do it. :)