Flex 3 keyboard event handling - apache-flex

I'd like to arrange things so that I have a chain of keyboard event handlers in my flex application, all of whom are queried when key down events occur. Basically, when a visual component is on screen, it is a candidate for handling a key press event. Something like this (clearly this is pseudocode):
<application handles_keys="F5, F6">
<tabGroup>
<tab1 handles_keys="pgup, pgdn">
<control handles_keys="0,1,2,3,4,5,6,7,8,9" />
</tab1>
<tab2 handles_keys="pgup, left, right"/>
</tabGroup>
</application>
I have a class written that will respond to the key events the way I want it to, so how do I register one or more instances of this class to have the results I want? Also, note that there are some situations where this class should receive events that would ordinarily be handled by a UI component. The TAB key is the main example; I have a few cases where I want my key down event handler to fire even when the focus is on a text field.

The way you have your pseudocode setup right now, you'd have to subclass all the different containers and add a handles_key property to each one. You may want to externalize the functionality to a separate class like this:
<KeyHandler target="{tab1}" handlesKeys="pgup,left,right"/>
As for actually catching the events, you'll need to add a KeyboardEvent.KEY_DOWN listener on the UIComponent you're wanting to listen to. You'll need to set the useCapture argument of addEventListener() to true as well. This will let you capture the event and prevent it instead of the event hitting the object and bubbling up.
target.addEventListener(KeyboardEvent.KEY_DOWN, target_onKeyDown, true);
Inside your target_onKeyDown event, check to see if you have a match for one of your keys you want to handle and if there is a match, call event.stopImmediatePropagation() or event.preventDefault() depending on what you need to do.
The Flex 3 Language Reference gives a good explanation of event propagation and keyboard events:
http://livedocs.adobe.com/flex/3/langref/flash/events/Event.html

The correct approach turns out to have been a global keyboard event manager, added as a key down listener to the stage when the application's creationComplete event is called. This then dispatches domain events for each keypress or combination thereof. Sadly, the filtering per-component model didn't work out.
I'm going to be porting this app to the Cairngorm framework, which should work really well once I'm done it.

when the displayed object is added to screen then start listen for stage keyboard events
when the object was removed from the screen the remove the keyboard event listener
on keyboard events filter the events you need :)
The code for a Canvas class you need should look like this:
class MyCanvas extends Canvas
{
public function MyCanvas(){
super();
}
override protected function createChildren():void{
super.createChildren();
this.systemManager.stage.addEventListener(KeyboardEvent.KeyUp, onKeyUpHandler);
}
private function onKeyUpHandler(e:KeyboardEvent):void{
// handle your code
switch(e.charCode){
case Keyboard.1:
case Keyboard.2:
case Keyboard.3:
case Keyboard.4:
case Keyboard.5:
case Keyboard.6:
case Keyboard.7:
case Keyboard.8:
case Keyboard.9:{
// do my stuff
break;
}
case Keyboard.F1:{
// do other stuff
break;
}
}
}
}
public function destroy():void{
//don't forget to remove listeners for stupid flash garbage collector
this.systemManager.stage.removeEventListener(KeyboardEvent.KeyUp, onKeyUpHandler);
}
}
Ignore any miss-spells :D I wrote the code from my mind :))

Related

Tornadofx onFocus listener

Is there something like an onFocus() method, which I could override like the onDock() and onCreate() in the view class?
In the documentation there is only written about live reloading of views.
I tried combining that with, the onDock() and onCreate() method, but, even though it "works", it's not a very neat way of replicating onFocus behaviour.
Is there a simple way to have a "listener" method that's called when the view/fragment comes to front/on focus?
The View is a container, and not an UI element in the sense of the JavaFX context, so it cannot receive an onFocus callback. However, you can register one with the current window or even the root node of the View. If you're opening a window, you could register such an even with the currentWindow property. If you're not opening a window, you could register it with the root property of the View. Here is an example listening for a single focus change event from the currentWindow:
override fun onDock() {
currentStage?.focusedProperty()?.onChangeOnce {
}
}

Can I disable CHANGE event in a custom component?

I have a custom MXML component which has a change event declared as:
<mx:Metadata>
[Event(name="change", type="flash.events.Event")]
</mx:Metadata>
The problem is that this event is dispatched whenever I change ANYTHING inside the component including a simple var. There are times when I want to be able to change the value of a var in the component without dispatching a change event. Is there a simple way of disabling the change event and then re-enabling it once I have made the change I want?
I tried to use removeEventListener("change") but it appears I can only do that for a function that has an event listener added. Same for removeEventListener(Event.CHANGE).
Surely there must be a simple way of disabling events declared in
mx:Metadata
yes there are methods on event you can use. So although the event is still dispatched, this gives you complete control of what happens with it.
// first set the useCapture flag to true
// this tell flex that onChange gets the event before it starts bubbling
myCustomComponentThatDispatchesALotOfChangeEvents.addEventListener(Event.CHANGE, onChange, true);
private function onChange(event:Event):void{
// here is the method you want
// this stops the event from bubbling and lets you handle
// all functionality in this listener only
event.stopImmediatePropogation();
// now look at what happened and see if you want to
// do anything else based on what actually changed
}
as a side note, you can also look at Event.preventDefault() which cancel's the events default behavior
"change" is also a flex thing. It you want to only dispatch the event in one particular scenario, make a new event class that SubClasses Event like MyChangeEvent. The when you make your change...
dispatchEvent(new MyChangeEvent(MyChangeEvent.STUFF_CHANGED))
then
myCustomComponentThatDispatchesALotOfChangeEvents.addEventListener(MyChangeEvent.STUFF_CHANGED, onChange);

Flex AsyncToken for listening to Alert box buttons

Can I listen to Alert button click between components using AsyncToken?
Basically, I want to have a method that opens an Alert with buttons and have it return an AsyncToken so that other components calling that method can listen for button click.
Example:
var token:AsyncToken=methodThatOpensAlert();
token.addResponder(new mx.rpc.Responder(buttonClick));
What's the way to do that?
Thank you.
You might be able to use an AsyncToken to achieve this but you could also just register for custom events that you dispatch from the pop up, this is a much cleaner method IMO. Really you've got two relatively clean options I can think of. 1 you make your pop-up dispatch events like "okClicked" "cancelClicked" for different button clicks within the pop-up, you create an instance of the pop up and add listeners then call PopUpManager.addPopUp, or else you do PopUpManager.createPopUp and keep a reference to the returned display object (the instance of the pop-up it created) and add your listeners then. 2 you make two properties in the pop up typed as function, you use them as call backs, so when you create the pop-up you set the okClickedFunction and cancelClickedFunction (or whatever your buttons may be) then in the pop-up you put cilck handlers on the buttons, check to see if the appropriate call-back function is set and call it if so, like
if(okClickedFunction)
okClickedFunction();
Let me know if you have a specific need that makes you think you must use the AsyncToken, but from checking out the docs it looks as though it's strictly meant to work with the other RPC methods and lots of properties are read-only.
EDIT:
[SomeUtilClass.as]
private static function methodThatOpensAlert():CustomAlert
{
return PopUpManager.createPopUp(Application.application, CustomAlert) as CustomAlert;
}
[CustomAlert.as]
[Event(type="flash.events.Event", name="button1Clicked")]
[Event(type="flash.events.Event", name="button2Clicked")]
private function button1Clicked_handler(event:MouseEvent):void
{
dispatchEvent(new Event("button1Clicked", true));
}
private function button2Clicked_handler(event:MouseEvent):void
{
dispatchEvent(new Event("button2Clicked", true));
}
[ThingThatUsesAlert]
var ca:CustomAlert = SomeUtilClass.methodThatOpensAlert();
ca.addEventListener("button1Clicked", button1ClickHandler);
ca.addEventListener("button2Clicked", button2ClickHandler);
And I believe mouse events bubble by default anyhow still so you could really just listen for a click event on the pop up then use the event.target to determine if it was one of the buttons your interested in.
Let me know if you can make sense of this or need more info.

Flex: Do I need to remove event handlers on an AsyncResponder? If so, how?

Do I need to remove event listeners on AsyncResponder events?
i.e.
public function DeleteItem():void
{
var asyncResponse:AsyncResponder = new AsyncResponder(DeleteItem_Result, DeleteItem_Fail);
_myService.DeleteWorkout("test", asyncResponse);
}
private function DeleteItem_Result(event:Event):void
{
//If I do need to remove them, how do i remove the async responder event listeners?
}
If I do need to remove them, how do I do it?
Do I need to remove event listeners on AsyncResponder events?
No, you do not. If you are creating the AsyncResponder and using ot over and over again, then by all means leave the listeners in there.
However, in some cases, if you won't be reusing the component over and over; I would recommend you do remove the event listeners, as that will remove a dependency pointing at the asyncResponder which may allow it to be released for garbage collection as appropriate.
In the Adobe Flex Framework it is pretty common to add and remove listeners "as needed." We use the approach in the Flextras Calendar, for example, when dealing with effects. Before starting the effect we add some event listeners for 'effect end'. Those listeners are removed in that effect end method.
Update:
To remove an event listener you would use code similar to this:
asyncResponder.removeEventListener('result' ,UpdatePics_result);
asyncResponder.removeEventListener('fault' ,UpdatePics_fault);

Flex: Lose Component Focus

I have a (hopefully) quick question. I've got some stepper boxes. Though really this could apply to any interactive component. I want the selected box to lose focus when when I click anywhere else ( stage included ). Is there an easy way to do this? I can't seem to find an effective way to make it lose focus.
In case anyone else finds their way here seeking a solution to this problem, here is the answer:
private function onGlobalMouseUp(event : MouseEvent) : void {
var fm:FocusManager = new FocusManager(stage);
//Since Flash Player can set focus on subcomponents as well as on components themselves,
//findFocusManagerComponent is used to find the component that either has focus or contains the subcomponent
//that has focus. If, for example, a TextField contained within a TextArea component has focus, findFocusManagerComponent
//will return the TextArea component, and not the TextField. This being the case, we can definitely determine
//whether the target object of the MouseUp event is a component (or is part of a component). If the target
//object is NOT a component (nor contained within one), then we clear all component focus.
if(fm.findFocusManagerComponent(event.target as InteractiveObject) is UIComponent){
//target of MouseUp is either a UIComponent, or is contained within a UIComponent, so do nothing.
}else{
//target of MouseUp is neither a UIComponent, nor is it contained within a UIComponent, so set component focus to null.
fm.setFocus(null);
}
}
So here's the solution I've come up with that works very well. I have a function called add() which has been assigned to applicationComplete. In that function I include:
this.skin.addEventListener( MouseEvent.MOUSE_UP, loseFocus );
Which calls:
private function loseFocus( e : MouseEvent ) : void
{
if ( e.eventPhase == EventPhase.AT_TARGET )
{
this.focusManager.deactivate();
}
}
Simple enough, and does what I was looking for. "Phase" filter is necessary to keep other components from registering the clicks.
As an important note: this.skin needs to be the event target. The stage is never exposed to the mouse in a Flex application.
Example Application Code
If someone has a better solution, please suggest one!
Perhaps you should check out FocusManager.hideFocus().
Maybe tie it into the focusOut event of your UIComponent.
Flex API

Resources