Intercepting EventListener ActionScript - actionscript-3

I'm trying to extend the Button class and remove the default EventListener through the following:
removeEventListener(MouseEvent.CLICK, clickHandler);
And then add something like this:
protected function _clickHandler(event:MouseEvent):void{
Alert.show("Are you sure about this operation", "Alert", 3, this, execute);
function execute(e:CloseEvent):void{
if(e.detail == Alert.YES)
super.clickHandler(event);
}
}
This way, I'll have a default component which will trigger a message alert with YES or NO option and prevent me from having to write that on every button that trigger the server. Unfortunatelly, it doesn't work that way.
Tried removing the default function and adding the one I wrote on listener;
Tried overriding clickHandler directly, also doesn't work;
Edit: Here is what I want: When the users click a button that will make a service call in my application, I always pop a window for him to tell me if he's sure of that. What I wanted was to build an automatic component for that, like this:
package screen{
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.ui.Keyboard;
import mx.controls.Alert;
import mx.controls.Button;
import mx.events.CloseEvent;
public class CommonButton extends Button{
public function CommonButton(){
super();
//removeEventListener(MouseEvent.CLICK, clickHandler)
//addEventListener(MouseEvent.CLICK, clickHandler);
addEventListener(KeyboardEvent.KEY_DOWN, function (e:KeyboardEvent):void{
if(e.altKey == Keyboard.ENTER)
dispatchEvent(new MouseEvent(MouseEvent.CLICK));
});
}
private var _saveEvent:MouseEvent;
override protected function clickHandler(event:MouseEvent):void{
_saveEvent = event;
event.stopImmediatePropagation();
Alert.show("Are you sure about this operation", "Alert", 3, this, execute);
}
private function execute(e:CloseEvent):void{
if(e.detail == Alert.YES)
super.clickHandler(_saveEvent);
}
}
}
And then:
<mx:Script>
<![CDATA[
import mx.controls.Alert;
private function test():void{
//if the user clicked No, this method will never be called.
Alert.show("You clicked YES");
}
]]>
</mx:Script>
<screen:CommonButton click="test()" />
Final Edit with Solution:
package screen{
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.ui.Keyboard;
import mx.controls.Alert;
import mx.controls.Button;
import mx.events.CloseEvent;
public class CommonButton extends Button{
public function CommonButton(){
super();
addEventListener(KeyboardEvent.KEY_DOWN, function (e:KeyboardEvent):void{
if(e.altKey == Keyboard.ENTER)
dispatchEvent(new MouseEvent(MouseEvent.CLICK));
});
}
private var _stopProp:Boolean = true;
override protected function clickHandler(event:MouseEvent):void{
if(_stopProp){
event.stopImmediatePropagation()
Alert.show("Are you sure about this operation", "Alert", 3, this, execute);
}else
_stopProp = true;
}
private function execute(e:CloseEvent):void{
if(e.detail == Alert.YES){
_stopProp = false;
dispatchEvent(new MouseEvent(MouseEvent.CLICK));
}
}
}
}

Why even bother with this? Just add your custom event handler to the extended component and allow the normal event handler to fire regardless. You can have multiple handlers registered to the same event. The only reason I could see this being an issue is if you were releasing this as a library and wanted to prevent the user from attach a click handler themselves, but you're not. You don't need to remove or stop the default click handler at all here.
EDIT: Here is an example of what the asker wants to happen here.
public class AButton extends Button
{
private var stopProp:Boolean = true;
public function AButton()
{
super();
this.addEventListener(MouseEvent.CLICK,this.clickHandlers);
}
private function clickHandlers( e:MouseEvent ):void{
if ( stopProp ) {
e.stopImmediatePropagation();
trace( "clicked in button" );
Alert.show( "Continue with event", "Warning!", Alert.YES|Alert.NO, null, this.closeHandler );
}
}
private function closeHandler( e:CloseEvent ):void {
if ( e.detail == Alert.YES ) {
this.stopProp = false;
this.dispatchEvent( new MouseEvent( MouseEvent.CLICK ) );
this.stopProp = true;
}
}
}
That will stop the event from going any further if the stopProp property is true. Then it will prompt the user. If the user selects "Yes", it will set stopProp to false, re-dispatch a click event from the button, and reset stopProp to false for next time. If they select "No", nothing happens.
I made a quick prototype to test this and it definitely worked.

Use stopImmediatePropagation() method of the Event class.
protected function _clickHandler(event:MouseEvent):void{
event.stopImmediatePropagation();
Alert.show("Are you sure about this operation", "Alert", 3, this, execute);
function execute(e:CloseEvent):void{
if(e.detail == Alert.YES)
super.clickHandler(event);
}
}

Related

AS3: Exit full screen event listener

How to add an event listener that listens to the exit full screen event by pressing escape key ??
stage.addEventListener(Event.RESIZE, backtoresize) //doesn't work :(
Thanks :)
I had it like this.
mcVideoControls.btnFullscreen.addEventListener(MouseEvent.CLICK, fullscreenClicked);
function fullscreenClicked(e:MouseEvent):void {
//fullscreen works only with an internet browser
if (stage.displayState == StageDisplayState.NORMAL) {
stage.displayState = StageDisplayState.FULL_SCREEN;
}
else {
stage.displayState = StageDisplayState.NORMAL;
}
}
But you could rewrite it. It would then be something like this.... wait hang on...
package {
import flash.display.Stage;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
public class UserInputHandler{
//escape button var
public static var keyEscape:Boolean;
public function UserInputHandler(stage:Stage){
//this events are sending the value true when specific keyboard button is pressed to the stage.
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
}
//you can provide more key codes in the function
private function keyDownHandler(e:KeyboardEvent):void{
switch(e.keyCode){
case Keyboard.ESCAPE:
UserInputHandler.keyEscape = true;
break;
}
}
//function when key is released from pressing
private function keyUpHandler(e:KeyboardEvent):void{
switch(e.keyCode){
case Keyboard.ESCAPE:
keyEscape = false;
break;
}
}
}
}
stage.addEventListener(FullScreenEvent.FULL_SCREEN, etc ...)
This triggers whether you enter or leave fullscreen.
Try:
stage.nativeWindow.addEventListener(Event.RESIZE, backtoresize);

AS3: int used where a boolean value was expected

I have three different buttons. When you click one of the buttons, it is supposed to activate the stageSelect function, which should then output the button's number.
But when I do that, I get the error in the title. What am I doing wrong here?
package {
import flash.display.MovieClip;
import flash.display.SimpleButton;
import flash.events.MouseEvent;
import flash.ui.Mouse;
public class MenuScreen extends MovieClip {
public function MenuScreen() {
Mouse.show();
selectGrass.addEventListener(MouseEvent.CLICK, stageSelect, 1);
selectDirt.addEventListener(MouseEvent.CLICK, stageSelect, 2);
selectGravel.addEventListener(MouseEvent.CLICK, stageSelect, 3);
}
public function stageSelect(stageID:Number) {
trace(stageID);
}
}
}
this is because the third param for the method addEventListener is useCapture which requires a boolean saying that you wish to grab the event during the capture phase before bubbling. You are calling
selectGrass.addEventListener( MouseEvent.CLICK, StageSelect, 1);
What you need to do instead is
selectGrass.addEventListener( MouseEvent.CLICK, grassSelected);
selectDirt.addEventListener( MouseEvent.CLICK, dirtSelected);
private function grassSelected(event:MouseEvent):void{
// do grass stuff
}
private function dirtSelected(event:MouseEvent):void{
// do dirt stuff
}

Space bar and arrow keys on Radio buttons trigger TypeError: Error #1009

I've just discovered a bug in my app, it looks like a Flash player bug, i would like to know if anyone has found a workaround or something.
I've some radio buttons groups in my app. If I press the arrow keys while holding the spacebar pressed, it ends up triggering
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at fl.controls::LabelButton/drawIcon()
at fl.controls::LabelButton/draw()
at fl.controls::RadioButton/draw()
at fl.core::UIComponent/callLaterDispatcher()
If found this thread that describes my same situation.
Do you think there's any workaround for this? Is it really a flash bug?
So, I've investigated this problem and found, that there is an error in standard fl components. It is not only about RadioButton. If you hold SpaceBar and then switch current UI element by clicking "TAB" button - you will receive similar error.
To solve it, you should fix source of the component: in class LabelButton, method keyUpHandler replace code
setMouseState(oldMouseState);
with
if (oldMouseState!==null) {
setMouseState(oldMouseState);
}
and add override:
override protected function focusOutHandler(event:FocusEvent):void {
if (oldMouseState) {
keyUpHandler(new KeyboardEvent(KeyboardEvent.KEY_UP, true, true, 0, Keyboard.SPACE));
}
super.focusOutHandler(event);
}
If you don't know, how you can do it:
just copy Adobe Flash CS5 install folder\Common\Configuration\Component Source\ActionScript 3.0\User Interface\fl\controls\LabelButton.as to another directory with the same package structure \fl\controls\LabelButton.as
then you need include this duplicated structure to project.
It is not very good way, but in this case it really fixes this error. BTW, I'll send it to Adobe.
actually its not a flash bug its component's bug. if space bar is no need for your radio buttons(i think its useless) you can disable it with creating your own MyRadioButton which extends RadioButton class this will be disable space key input
package {
import flash.events.KeyboardEvent;
import fl.controls.RadioButton;
import flash.ui.Keyboard;
public class MyRadioButton extends RadioButton{
public function MyRadioButton() {
super();
}
override protected function keyUpHandler(event:KeyboardEvent):void {
if(event.keyCode != Keyboard.SPACE){
super.keyUpHandler(event);
}
}
override protected function keyDownHandler(event:KeyboardEvent):void {
if(event.keyCode != Keyboard.SPACE){
super.keyDownHandler(event);
}
}
}
}
just you need to change class property fl.controls.RadioButton to MyRadioButton from your library element:"RadioButton". (i assume MyRadioButton is near fla)
------>edit
here is another solution without disabling spacebar. this time when user makes anykeydown before spacebar keyup code runs spacebar keyup before anykeydown. and focus get removed from radio button also you can add anyother solutions like if spacebar keystate is down donot let any keydown.
package {
import flash.events.KeyboardEvent;
import fl.controls.RadioButton;
import flash.ui.Keyboard;
import flash.events.Event;
public class MyRadioButton extends RadioButton{
private var _isSpaceDown:Boolean = false;
public function MyRadioButton() {
super();
}
override protected function handleChange(event:Event):void {
if (_isSpaceDown) {
keyUpHandler(new KeyboardEvent(KeyboardEvent.KEY_UP, true, true, 0, Keyboard.SPACE));
setMouseState('up');
}
super.handleChange(event);
}
override protected function keyUpHandler(event:KeyboardEvent):void {
if(event.keyCode == Keyboard.SPACE){
if(_isSpaceDown){
_isSpaceDown = false;
}else{
return;
}
}
super.keyUpHandler(event);
}
override protected function keyDownHandler(event:KeyboardEvent):void {
if(event.keyCode == Keyboard.SPACE){
_isSpaceDown = true;
}else{
if(_isSpaceDown){
var e:KeyboardEvent = new KeyboardEvent(KeyboardEvent.KEY_DOWN);
e.keyCode = Keyboard.SPACE;
super.keyUpHandler(e);
_isSpaceDown = false;
}
}
super.keyDownHandler(event);
}
}
}

actionscript 3 track dynamic text change

Is there a way to "listen" to dynamic text? I have this dynamic textfield on the stage and I want to call a function once the the dynamic text has been changed. I tried to add event listener but it seems to work only on INPUT text. Any suggestions?
Thanks
While #annonymously's answer works, attaching enterFrame listeners to wait for text change (or any change, for that matter) is not a good idea - the text might not change at all, then why would you run a piece of code many times per second, if it's avoidable? Not to mention the fact, that you'd have to do it over and over again for each new text field instance you want to monitor.
It is better to react to actual changes, and these are caused by your own setting of properties. You should simply extend the TextField class and override the setters for htmlText and/or text to dispatch a change event:
override public function set text ( text : String ) : void {
super.text = text;
dispatchEvent (new Event (Event.CHANGE);
}
You can write very simple subclass which allows you to listen text change events.
Sublass:
package
{
import flash.events.Event;
import flash.text.TextField;
public class CustomTextField extends TextField
{
public function CustomTextField()
{
super();
}
override public function set text( value:String ):void
{
if( super.text != value )
{
super.text = value;
dispatchEvent(new Event(Event.CHANGE, true));
}
}
}
}
Usage example:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class CustomTextFieldTest extends Sprite
{
private var tf:CustomTextField;
public function CustomTextFieldTest()
{
tf = new CustomTextField();
tf.x = tf.y = 10;
tf.width = tf.height = 200;
tf.addEventListener(Event.CHANGE, onTfChange);
addChild(tf);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
private function onTfChange( e:Event ):void {
trace("text changed, new text: '" + tf.text + "'");
}
private function onMouseDown( e:MouseEvent ):void {
tf.text = "some random text: " + Math.round(100 * Math.random());
}
}
}
Did you try the CHANGE event? It should work for all types of TextFields including non-input ones.
Only input text dispatches CHANGE events, as you said. You'll have to work around it, perhaps like this:
var oldText:String = "";
function changeEnterFrame (e:Event) {
if (oldText != textField.text) {
// Do your stuff here
}
oldText = textField.text;
}
addEventListener(Event.ENTER_FRAME, changeEnterFrame);

Custom event dispatchment location

I've been looking into custom event (listeners) for quite some time, but never succeeded in making one. There are so many different methods, extending the Event class, but also Extending the EventDispatcher class, very confusing!
I want to settle with this once and for all and learn the appropriate technique.
package{
import flash.events.Event;
public class CustomEvent extends Event{
public static const TEST:String = 'test'; //what exac is the purpose of the value in the string?
public var data:Object;
public function CustomEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false, data:Object = null):void
{
this.data = data;
super();
}
}
}
As far as I know a custom class where you set the requirements for the event to be dispatched has to be made:
package
{
import flash.display.MovieClip;
public class TestClass extends MovieClip
{
public function TestClass():void {
if (ConditionForHoldToComplete == true) {
dispatchEvent(new Event(CustomEvent.TEST));
}
}
}
}
I'm not sure if this is correct, but it should be something along the lines of this.
Now What I want is something like a mouseevent, which can be applied to a target and does not require a specific class.
It would have to work something like this:
package com.op_pad._events{
import flash.events.MouseEvent;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.EventDispatcher;
import flash.events.Event;
public class HoldEvent extends Event
{
public static const HOLD_COMPLETE:String = "hold completed";
var timer:Timer;
public function SpriteEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
{
super( type, bubbles, cancelable );
timer = new Timer(1000, 1);
//somehow find the target where is event is placed upon -> target.addEventlistener
target.addEventListener(MouseEvent.MOUSE_DOWN, startTimer);
target.addEventListener(MouseEvent.MOUSE_UP, stopTimer);
}
public override function clone():Event
{
return new SpriteEvent(type, bubbles, cancelable);
}
public override function toString():String
{
return formatToString("MovieEvent", "type", "bubbles", "cancelable", "eventPhase");
}
//////////////////////////////////
///// c o n d i t i o n s /////
//////////////////////////////////
private function startTimer(e:MouseEvent):void
{
timer.start();
timer.addEventListener(TimerEvent.TIMER_COMPLETE, complete);
}
private function stopTimer(e:MouseEvent):void
{
timer.stop()
}
public function complete(e:TimerEvent):void {
dispatchEvent(new HoldEvent(HoldEvent.HOLD_COMPLETE));
}
}
}
This obviously won't work, but should give you an idea of what I want to achieve. This should be possible because mouseevent can be applied to about everything.The main problem is that I don't know where I should set the requirements for the event to be executed to be able to apply it to movieclips and sprites.
You are almost there actually, just for the last part, wouldn't this be more of an OOP related issue than stricly a confusion about the way of using custom events ?
Usually, Events in AS3 are value objects whose sole responsibility is to transport information from the event dispatcher to the listener(s). The dispatcher dispatches the event each time a defined momentum is reached, and the listener(s) may or may not react when this happens.
In the example above, I guess it is up to the listener to start a timer and so on when a mouse-down has been detected. In a more sophisticated context the Event could independently trigger more than one listeners actioning separate tasks which neither the Dispatcher nor the Event itself should have to bother about, that is probably why it's worth avoiding amending the dispatcher or the event itself with any soever logic.
For your very example, you could maybe create a handler checking if the mouse has been held down?
The following is just pseudocode, and there are obviously tons of other ways to get to the same result:
public class MouseDownHandler
{
// ...
public function( target:Sprite ) {
this.target = target;
start();
}
public function start():void{
// Listen for the target's mouseUp event
}
public function dispose():void{
// Stop listeners and eventually the timer
}
private function onMouseDown(e:MouseEvent):void{
// Start timer + listening for the stage's mouse up event (target.stage)
}
private function onMouseUp(e:Event):void{
// Cancel timer
}
private function onTimerComplete(e:TimerEvent):void {
dispatchEvent(new HoldEvent(HoldEvent.HOLD_COMPLETE));
}
}
Which could be reused for example this way:
var mc:MovieClip = new MovieClip(); ...
var mouseHandler:MouseDownHandler = new MouseDownHandler(mc);
mouseHandler.addEventListener(HoldEvent.HOLD_COMPLETE, onMcHoldComplete);
... or this way :
public class TestMovieClip extends MovieClip
{
private var mouseHandler:MouseDownHandler;
public function TestMovieClip() {
mouseHandler = new MouseDownHandler(this);
mouseHandler.addEventListener(HoldEvent.HOLD_COMPLETE, onMouseHoldComplete);
}
private function onMouseHoldComplete(e:HoldEvent):void {
// Do something
}
}
I just use robber penners signals. Very easy to use.
http://github.com/robertpenner/as3-signals