FLEX - Disable spacebar - actionscript-3

I've got an application, which is tabbed (using TabNavigator).
When it starts, I switch tab to nr.2 (default is tab 1). I need to be able to hold down spacebar and drag the mouse to pan, but when I do this it switches back to the first tab. So the spacebar is triggering it to switch.
I've tried using a custom lass that extends tabNavigator, like the code below, but it's not working. Also tried setting focusEnabled = false without luck.
Any idea how I would solve this?
Thanks a lot in advance,
Stian Berg Larsen
package components
{
import mx.containers.TabNavigator;
import flash.events.KeyboardEvent;
public class myTabNavigator extends TabNavigator
{
public function myTabNavigator()
{
super();
}
protected override function keyDownHandler(e : KeyboardEvent) : void {
if (e.keyCode == 32) { // Spacebar
return;
}
super.keyDownHandler(e);
}
}
}

That is probably caused because you are not stopping your event from bubbling.
Try this:
protected override function keyDownHandler(e : KeyboardEvent) : void {
if (e.keyCode == 32) { // Spacebar
e.preventDefault();
e.stopImmediatePropagation();
return;
}
super.keyDownHandler(e);
}

Related

AS3: class not functioning properly

Sorry for a blurry title, but this is probably the best way I can describe the issue that seems absurd to me by now.
I need a simple action done by an MC: going to a certain frame. I couldn't get it to work, although I had an exact same type of action done by another Movie Clip in the same class code. Here's how I did it:
if (currentItem.type == "blue") {
guy.gotoAndPlay("blue")
}
Yes, the class I'm referring to ('guy') is extended as a Movie Clip. Again, exact same code works fine with other Clips. I tried another method: switching the frame from the Clip's class, the frame it switches to is defined by a variable which is changed by main class. But somehow, this doesn't work either. It gives me the 1069 error. Here's the code of the 'guy' class:
package
{
import flash.display.MovieClip;
import flash.events.Event;
public class guy extends MovieClip
{
public static var gotoer:String = "fffuuu"
public function shaman_armsUp()
{
super();
addEventListener(Event.ADDED_TO_STAGE, init)
}
public function init(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, init)
armLoop()
}
public function armLoop():void {
if (gotoer == "brown") {
this.gotoAndPlay("brown")
}
if (gotoer == "red") {
trace(gotoer)
this.gotoAndPlay("red")
}
}
}
}
Is there anyone who has a logical explanation for this? Can this be caused by a bug?
maybe the code you are writing in the method shaman_armsUp() should be moved to the constructor?
Follow my edited version of your class guy (also, renamed to Guy, follow the class name convention)
package
{
import flash.display.FrameLabel;
import flash.display.MovieClip;
import flash.events.Event;
// renaming the class name (guy to Guy)
public class Guy extends MovieClip
{
// not sure if using an static variable for this case is the best idea, but I decided to keep because I don't know your initial idea
public static var gotoer:String = "fffuuu";
public function Guy()
{
// move the addedToStage event to the constructor
super();
addEventListener(Event.ADDED_TO_STAGE, init, false, 0, true);
}
public function shaman_armsUp()
{
}
public function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
armLoop();
}
public function armLoop():void
{
// Note: the value of gotoer is "fffuuu" and there is no conditional for it.
// are you chaging this value before calling armLoop? because when the init method is called it has the same value
// why not try something like:
// checking if the frame exist and if so, calling the gotoAndPlay method (try to imagine if you have 100 frame names? the old approach will be very hard to maintain
if (hasFrameLabel(gotoer))
{
this.gotoAndPlay(gotoer);
}
else
{
trace('frame: ', gotoer, ' not found');
}
/*
if (gotoer == "brown")
{
this.gotoAndPlay("brown");
}
if (gotoer == "red")
{
trace(gotoer);
this.gotoAndPlay("red");
}
*/
}
// helper function to avoid calling a frame that doesn't exist
private function hasFrameLabel(frameLabel:String):Boolean
{
var returnValue:Boolean;
const obj:Object = this.currentLabels;
for each (var i:FrameLabel in obj)
{
if (i.name == frameLabel)
{
returnValue = true;
break;
}
}
return returnValue;
}
}
}

Intercepting EventListener ActionScript

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);
}
}

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);
}
}
}

Flash AS3 KeyboardEvent not firing

I have a class like this:
public class GameOverScreen extends MovieClip {
public function GameOverScreen(useMouseControl:Boolean) {
if(useMouseControl){
Mouse.show();
restartButton.addEventListener(MouseEvent.CLICK, onClickRestart);
}
else{
this.addEventListener(KeyboardEvent.KEY_DOWN, onPushSpace);
}
}
public function onClickRestart(mouseEvent:MouseEvent):void{
dispatchEvent(new NavigationEvent(NavigationEvent.RESTART));
}
public function onPushSpace(keyboardEvent:KeyboardEvent):void{
trace(keyboardEvent);
dispatchEvent(new NavigationEvent(NavigationEvent.RESTART));
}...
It's an ending screen of a game. (surprise!) I want it to restart my game if I push down the space button, or click on the restartButton on the screen. As you can see the screen gets a boolean value in the constructor, wich decide that we're using keyboard or mouse to control the game. It works well with mouse, but with the key, i have to click on the restart button (wich is on the screen), till it does nothing, and after clicking it, and I push a button I get the playScreen, but my keylistener is somewhy still in work, and if i push any key, it restarts the game.
The point of my main class is: if the player dies, he get a gameOverScreen, and the playscreen will be dismissed, the gameOverScreen also gets a listener, it listens for an event called RESTART, if the event is dispatched, a new playScreen is created, and the game over dismissed.
public class Avoider extends MovieClip { ....
public function onAvatarDeath(avatarEvent:AvatarEvent):void {
var finalScore:Number = playScreen.getFinalScore();
var finalTime:Number = playScreen.getFinalTime();
gameOverScreen = new GameOverScreen(useMouseControl);
gameOverScreen.addEventListener(NavigationEvent.RESTART, onRequestRestart);
gameOverScreen.setFinalScore(finalScore);
gameOverScreen.setFinalTime(finalTime);
addChild(gameOverScreen);
playScreen = null;
}
public function restartGame():void {
playScreen = new PlayScreen(useMouseControl);
playScreen.addEventListener(AvatarEvent.DEAD, onAvatarDeath);
addChild(playScreen);
gameOverScreen = null;
}
public function onRequestRestart(navigationEvent:NavigationEvent):void {
restartGame();
}
I hope it's understandable, if not please note it what is not clean.
Thanks
UPDATE
my onAddToStage function
public function onAddToStage(event: Event):void{
stage.focus = this;
this.addEventListener(KeyboardEvent.KEY_DOWN, onPushSpace);
}
Try adding your key listener to the stage:
stage.addEventListener(KeyboardEvent.KEY_DOWN, onPushSpace);
Otherwise your current class needs to be in focus, which is why only works until you've clicked it. Be sure to remove that listener when your game over screen is done though.
Alternatively you could give your game over screen focus through code when it loads (in the constructor):
public function GameOverScreen(useMouseControl:Boolean) {
this.addEventListener(Event.ADDED_TO_STAGE,addedToStage,false,0,true);
if(useMouseControl){
Mouse.show();
restartButton.addEventListener(MouseEvent.CLICK, onClickRestart, false, 0, true);
}
else{
this.addEventListener(KeyboardEvent.KEY_DOWN, onPushSpace, false, 0, true);
}
}
private function addedToStage(e:Event):void {
stage.focus = this;
stage.stageFocusRect = false; //make sure there's no dumb yellow rectangle
}
Also a little tip - I notice your not removing your game over screen from the display list once it's finished. You'll want to do that to make it truly go away (and remove your restart event listener).
public function restartGame():void {
playScreen = new PlayScreen(useMouseControl);
playScreen.addEventListener(AvatarEvent.DEAD, onAvatarDeath);
addChild(playScreen);
gameOverScreen.removeEventListener(NavigationEvent.RESTART, onRequestRestart);
removeChild(gameOverScreen);
gameOverScreen = null;
}

Handle MouseEvent.CLICK, MOUSE_DOWN in subclasses

I'm building a panel of buttons, that are enclosed in Canvas container. For this purpose I created a MyButton class, which is a subclass of UIComponent. MyButton class has 2 other subclasses: MyRadioButton and MyMenuButton, which have another behavior on MouseEvent.MOUSE_DOWN. MyMenuButtoncreates and shows a menu, that I build from XML and it builds ok.
I add listeners in the superclass like this:
this.addEventListener(MouseEvent.CLICK, handleClick);
this.addEventListener(MouseEvent.MOUSE_DOWN, handleMouse);
this.addEventListener(MouseEvent.MOUSE_UP, handleMouse);
It's done at the creation stage.
In my subclasses I override handleMouse handler.
override protected handleMouse(event:MouseEvent) : void
{
// subclass specific handling
}
These button objects are added to canvas container as following:
in class MyButtonsContainer.as:
this.rowChildren.addChild(button);
These buttons draw perfectly and in place. The problem is the behavior:
The event doesn't come to handleClick handler of superclass. And that's the question actually - why can it be?
Thanks in advance
EDIT: It appears that MOUSE_DOWN & MOUSE_UP interfere with CLICK event. When I remove listeners to them I get to click handler.How to cause them live together?
I managed to catch CLICK event as timing difference between MOUSE_DOWN & MOUSE_UP.
EDIT: A short/long explanation: For some reason my listeners for MOUSE_DOWN/MOUSE_UP events were interfering with MouseEvent.CLICK. I was suggested by somebody to give up on listening to MouseEvent.CLICK and instead start a timer in MouseEvent.MOUSE_DOWN and check again the timer in MouseEvent.MOUSE_UP. If the difference less than 0.1 (you can put there another threshold) then it was a click actually.
Now some sample code:
public class MyButton extends UIComponent
{
...
private var _clickTime : Number = 0.;
public function void MyButton()
{
super();
addEventListener(MouseEvent.MOUSE_DOWN, handleMouse);
addEventListener(MouseEvent.MOUSE_UP, handleMouse);
}
protected function checkForClick(event:MouseEvent) : Boolean
{
var down : Boolean = event.type == MouseEvent.MOUSE_DOWN;
if (down)
_clickTime = getTimer();
else {
var diff : Number = getTimer() - _clickTime;
if (diff/1000. < 1.) {
handleClick();
return true;
}
}
return false;
}
protected function handleClick(event:MouseEvent) : void
{
// handle the click here
}
protected function handleMouse(event:MouseEvent) : void
{
checkForClick(event);
...
// take care of your button graphic state
}
DISCLAIMER: This code works for me and good for me and my application needs. If you disagree with this way or have a better way to suggest, please do it in comments. Since this answer answers my own question and this EDIT was added only because I was asked to do so, please don't downvote it if you don't agree with it.
call the superclass method like so
override protected handleMouse(event:MouseEvent) : void
{
super.handleMouse(event);
// subclass specific handling
}
If I'm understanding you correctly, you just need to make sure that in your overriden handlers you end each one with a call to super -
override protected handleMouse(event:MouseEvent):void {
// my logic here for the subclass
trace("blah blah blah");
//now redispatch up to the superclass.
super.handleMouse(event);
}
I'm not 100% sure what you're asking, but I'd override click actions like this..
Base class:
public class MyButton extends UIComponent
{
/**
* Constructor
*/
public function MyButton()
{
// listeners
addEventListener(MouseEvent.CLICK, _click);
// ...other listeners
}
/**
* Called on dispatch of MouseEvent.CLICK
*/
private function _click(e:MouseEvent):void
{
doClick();
}
/**
* Override this method and fill with actions to
* do when this is clicked
*/
protected function doClick():void
{
trace("base class was clicked");
}
}
And the extending class:
public class MyOtherButton extends MyButton
{
/**
* Override doClick
*/
override protected function doClick():void
{
super.doClick();
trace("extending class was clicked");
}
}
After closely inspecting your code:
You've added two different listeners to one function for one, shouldn't these have seperate functions as they are called by contrasting events?
this.addEventListener(MouseEvent.MOUSE_DOWN, handleMouse);
this.addEventListener(MouseEvent.MOUSE_UP, handleMouse);
Also, it seems like you're expecting handleMouse to be fired by a click event judging by the below code, which isn't the case.
override protected handleMouse(event:MouseEvent) : void
{
// subclass specific handling
}
Either that, or you're forgetting to override handleClick as well - which is the method that you're running on a click event. You possibly need to add this code:
override protected handleClick(event:MouseEvent) : void
{
// subclass specific handling for a click
}