Is there a way to write a custom event that gets triggered when the user clicks outside of that custom component instance? Basically anywhere else in the main flex app.
Thanks.
You can use the FlexMouseEvent.MOUSE_DOWN_OUTSIDE event. For example:
myPopup.addEventListener(
FlexMouseEvent.MOUSE_DOWN_OUTSIDE,
function(mouseEvt:FlexMouseEvent):void
{
PopUpManager.removePopUp(myPopup);
}
);
stage.addEventListener( MouseEvent.CLICK, stgMouseListener, false, 0, true );
...
private function stgMouseListener( evt:MouseEvent ):void
{
trace("click on stage");
}
private function yourComponentListener( evt:MouseEvent ):void
{
trace("do your thing");
evt.stopPropagation();
}
Got this from Senocular. I think it applies to this subject, at least it did the trick for me. What jedierikb suggested seems to be the same, but a little incomplete.
Preventing Event Propagation
If you want to prevent an event from propagating further, you can stop it from doing so within an event listener using stopPropagation() (flash.events.Event.stopPropagation()) or stopImmediatePropagation() (flash.events.Event.stopImmediatePropagation()). These methods are called from the Event objects passed into event listeners and essentially stop the event from happening - at least past that point.
stopPropagation prevents any objects beyond the current from recieving the event, and this can be within any phase of the event. stopImmediatePropagation does the same but also takes the extra step of preventing additional events within the current target receiving the event from happening too. So where as stopPropagation would prevent sprite A's parent from receiving the event, stopImmediatePropagation would prevent sprite A's parent as well as any other listeners listening to sprite A from receiving the event.
Example: toggle between using stopPropagation and stopImmediatePropagation
ActionScript Code:
var circle:Sprite = new Sprite();
circle.graphics.beginFill(0x4080A0);
circle.graphics.drawCircle(50, 50, 25);
addChild(circle);
circle.addEventListener(MouseEvent.CLICK, clickCircle1);
circle.addEventListener(MouseEvent.CLICK, clickCircle2);
stage.addEventListener(MouseEvent.CLICK, clickStage);
function clickCircle1(evt:MouseEvent):void {
evt.stopPropagation();
// evt.stopImmediatePropagation();
trace("clickCircle1");
}
function clickCircle2(evt:MouseEvent):void {
trace("clickCircle2");
}
function clickStage(evt:MouseEvent):void {
trace("clickStage");
}
Click the circle and see how the event is stopped with each method. stopPropagation prevented the stage from receiving the event while stopImmediatePropagation also prevented clickCircle2 from recognizing the event
normal output
clickCircle1
clickCircle2
clickStage
stopPropagation output
clickCircle1
clickCircle2
stopImmediatePropagation output
clickCircle1
Flex/Actionscript 3 - close popupanchor on mouse clicked anywhere outside popup anchor
for 4.6 SDK try this..
frmPUA.popUp.addEventListener(FlexMouseEvent.MOUSE_DOWN_OUTSIDE, menuPopOutside, false, 0, true);
Full code is avaiable at
http://saravanakumargn.wordpress.com/2013/12/14/flexactionscript-3-close-popupanchor-on-mouse-clicked-anywhere-outside-popup-anchor-2/
Related
I'd like to avoid that mouse events triggered by the user don't get dispatched to their target objects, effectively "freezing" the GUI for the user.
In a sample application featuring just a single mx.controls.Button I called addEventListener on the button to get notified of mouse events. In the event handler, I called Event::stopImmediatePropagation on the event, assuming that this would "discard" the event. Clicking the button would call my event handler, but yet the button was "clicked" (it animated and triggered an event).
How could I do this?
button.mouseEnabled = false;
button.mouseChildren = false;
should work
Depending on how advanced your interface is, you could just throw an object (s:Rect in an s:Group would work) on top of everything, set width and height to 100%, and disable mouseChildren
USE removeEventListener()
var b:Button = new Button();
function init():void
{
b.addEventListener(MouseEvent.CLICK, onButtonClick);
}
function onButtonClick(event:MourseEvent):void
{
b.removeEventListener(MouseEvent.CLICK, onButtonClick);
}
I hava a custom component and it contains a child icon. If I add a mouse-click event listener to both component(click-listener1) and icon(click-listener2), the event dispatched sequence is click-listener2, then click-listener1. I can understand it. But if I add a custom event to component (listener1), and mouse-click event to icon(listener2), when icon is clicked, the component will dispatch the custom event. In my test, the event dispatched sequence is listener1, then listener2. It doesn't match with event-bubbles rule.
In my opinion The custom event is dispatched in listener2, which triggers listener1. Why event flow sequence is not listener2, listener1?
In component.
icon.addEventListener(MouseEvent.CLICK, iconClickHandler);
private function iconClickHandler(event:MouseEvent):void
{
trace ("Listener2");
var customEvent:CustomEvent= new CustomEvent(CustomEvent.CUSTOM_EVENT, true, true);
dispatchEvent(customEvent)
trace ("Listener3");
}
In Application, which contains component
component.addEventListener(CustomEvent.CUSTOM_EVENT, customEventHandler);
private function customEventHandler(event:CustomEvent):void {
trace ("Listener1");
}
UPD
You've got:
private function iconClickHandler(event:MouseEvent):void
{
trace("listener2");
var customEvent:CustomEvent= new CustomEvent(CustomEvent.CUSTOM_EVENT, true, true);
dispatchEvent(customEvent);
trace("listener3");
}
private function customEventHandler(event:CustomEvent):void
{
trace("listener1");
}
When MouseEvent.MOUSE_CLICK is dispatched, it triggers first lucky listener - it is your component function iconClickHandler. Here we trace "listener2" and dispatch custom event.
Due to syncronious nature of events, CUSTOM_EVENT listeners are triggered immediatly, that means that dispatching an event is similar to calling listener functions. Events are not stored anywhere, they are not delayed: listeners to events fires immediatly, in the same control flow, in the same thread.
CUSTOM_EVENT was dispatched, its listeners were triggered - we've got a call to customEventHandler and "listener1" in console.
When all the listeners were triggered, control returns to iconClickHandler and "listener3" is traced to console.
That's why we've got output:
listener2
listener1
listener3
I'm using code based on this post.
It uses a focus_out event to detect if there is a change that needs to be committed. However I notice that a FOCUS_OUT event is only called if you click away from the textfield but inside the component. Is there any way I can listen for clicks outside the component from within the component?
addEventListener(FocusEvent.FOCUS_OUT, onFocusOut);
protected function onFocusOut(event:FocusEvent):void
{
_updatedText = text;
if(_updatedText != _originalText){
dispatchEvent(new Event(Event.CHANGE));
}
setEditable(false);
}
In the component itself, you can do this:
systemManager.addEventListener( FocusEvent.KEY_FOCUS_CHANGE, focusChangeHandler );
systemManager.addEventListener( FocusEvent.MOUSE_FOCUS_CHANGE, focusChangeHandler );
Just be sure you clean up and remove the event listener before your component is removed from the stage (assuming it is added dynamically). That will prevent you from stacking up a bunch of event listeners.
Alternatively, if you just want to find out whenever someone clicks outside of a specific component, you can do something like this:
systemManager.addEventListener( MouseEvent.MOUSE_DOWN, system_mouseDownHandler );
private function system_mouseDownHandler( event:MouseEvent ):void {
if( !event.target != this && !this.contains(event.target as DisplayObject) ){
// Do Something Here
}
}
Again, make sure you cleanup any event listeners if this component is added/removed dynamically.
Hope this helps!
EDIT:
If you want to cleanup the eventListeners, do something like this (called when the remove event is triggered in your component):
<mx:Component remove="myRemoveHandler();" />
private function myRemoveHandler():void {
if( systemManager.hasEventListener( MouseEvent.MOUSE_DOWN ) systemManager.removeEventListener( MouseEvent.MOUSE_DOWN, system_mouseDownHandler );
}
Obviously substitute the event listeners that you ended up using (Focus or Mouse).
In LabelEditor class dispatches a Event.CHANGE event on focus out you can just listen for that event
I have a warning Popwindow with 2 buttons 'Submit' and 'Cancel'.
On clicking submit I invoke a function and dispatch 'submit' Event with bubble true. I want to handle this inside my parent application. I have already registered the event with the parent container as well as the popup instance.
Inside Parent.mxml :
private function launchWarningPopUp():void {
var win:Warning = PopUpManager.createPopUp(this, Warning, false ) as Warning;
win.addEventListener(SubmitQuizEvent.SUBMIT_QUIZ, submissionDone);
this.addEventListener(SubmitQuizEvent.SUBMIT_QUIZ, submissionDone);
PopUpManager.centerPopUp(win);
}
private function submissionDone():void{
Alert.show('Inside SubmissionDoneTwo');
}
Inside Warning.mxml:
private function submitHanlder():void {
dispatchEvent(new SubmitQuizEvent(SubmitQuizEvent.SUBMIT_QUIZ,true));
PopUpManager.removePopUp(this);
}
The event should bubble to Parent.mxml.
Am I doing something wrong here or is it simply not possible ?
I am stuck here, any help in this regard would be greatly appreciated.
Thanks in advance.
You should add your event listener to the systemManager because popups are direct children of the system manager
You are correct. the popup is not a child of the component that creates it. It is, as Florian said, a child of systemManager and events don't bubble as you might expect.
I was unable to catch this event inside my function as the signature was missing. After I add this the code worked.
private function submissionDone(event:SubmitQuizEvent):void{
Alert.show('Inside SubmissionDone with signature');
}
=================================================================================
I am facing another issue now.
The event is captured only when I use ' win.addEventListener....' and not with 'this.addEventListener ............'.This is surprizing to me.
If Parent.mxml is parent of 'win' then the events triggered inside 'win' should bubble and should be caught by the parent. This is not happening.
Is 'win' not considered child of the Parent.mxml ? And is treated as external component ?
Please let me know your view on this ??
I've done a lot of C# programming with both Winforms and WPF. I'm working on a Flex/Air app now for cross platform support. But this is my first flex project, so I'm learning as I go.
I've got a window that I want to popup, that the user will fill out a form, then hit OK or CANCEL. I set it up the same way I would've in C#, but it doesn't work, and I can't really see a way to make it do what I want.
EDIT:
So I'm trying events now, the events just don't seem to be handled...
EDIT again:
Oh, It's because the popup manager seems to create a new instance of the Form object, rather than using the one I created already.
so in the showWindow method, I put in this code rather than the popup manager:
parent.addChild(this);
then I remove it when I close it. The only problem is, it doesn't disable the rest of the parent like the popup manager does. Any suggestions on that?
PARENT:
private function btnAdd_Clicked():void
{
var form:Form = new Form();
form.addEventListener(CloseEvent.CLOSE, onFormClosed, false, 0, true);
recipeForm.showWindow(this);
}
private function onFormClosed(e:CloseEvent):void
{
//none of these Alerts are ever shown. I also tried breakpoints in debug to try an follow the code, with no luck
Alert.show("Closed");
if(e.detail == Alert.OK)
{
Alert.show("OK");
}
else if(e.detail == Alert.CANCEL)
{
Alert.show("Cancel");
}
}
CHILD:
private function btnCancel_Clicked():void
{
okClicked = false;
closeWindow();
}
public function closeWindow():void
{
var e:CloseEvent = new CloseEvent(CloseEvent.CLOSE);
e.detail = okClicked ? Alert.OK : Alert.CANCEL;
dispatchEvent(e);
PopUpManager.removePopUp(this);
}
public function showWindow(parent:WindowedApplication):void
{
var window:IFlexDisplayObject = PopUpManager.createPopUp(parent, RecipeForm, true);
PopUpManager.centerPopUp(window);
}
You can do this at least two different ways:
FIRST WAY: Using events
Let your Form class dispatch an event when either of the buttons is clicked. After Form is instantiated from the parent view, add an eventListener for the event(s) it's known to dispatch. When the Form dispatches the event, the eventListener will be invoked. You can even reuse Flex's CloseEvent and set the "detail" property to either Alert.OK or Alert.CANCEL before dispatching it.
In Form:
var e:CloseEvent = new CloseEvent(CloseEvent.CLOSE);
e.detail = okClicked ? Alert.OK : Alert.CANCEL;
dispatchEvent(e);
In parent:
var f:Form = new Form();
f.addEventListener(CloseEvent.CLOSE, onClose, false, 0, true);
...
private function onClose(e:CloseEvent):void
{
if (e.detail == Alert.OK)
// do something
else if (e.detail == Alert.CANCEL)
// do something else
}
SECOND WAY: Using callbacks
Add a public var of type "Function" to your Form class and supply a callback function from the parent. This does basically the same thing as #1 except with little less abstraction / indirection.
I would recommend #1 since the event model in Flex is pretty well-conceived and more flexible than the callback.
In Form:
var e:CloseEvent = new CloseEvent(CloseEvent.CLOSE);
e.detail = okClicked ? Alert.OK : Alert.CANCEL;
dispatchEvent(e);
In parent:
var f:Form = new Form();
f.addEventListener(CloseEvent.CLOSE, onClose, false, 0, true);
...
private function onClose(e:CloseEvent):void
{
if (e.detail == Alert.OK)
// do something
else if (e.detail == Alert.CANCEL)
// do something else
}
Not sure if this is still an open issue. I ran into this very same problem and I think I figured out what is wrong. At least I did for my problem.
I implemented things exactly as you did. I also have the close attribute set to closeWindow (I'm using a TitleWindow for my dialog).
So when the window is closed via the X at the top, it will call closeWindow, also if you click on the Cancel button, it will also call closeWindow.
The problem for me was that clicking cancel, dispatches a CloseEvent which seems to be caught by a Listener which calls closeWindow again (possibly via the close attribute which probably creates its own internal listener). I'm not sure if its an infinite loop but Flex does not like this.
My solution was to create two functions, one for the X close window to call and one for the Cancel button to dispatch a CloseEvent of its own. This seemed to work for me. Hope it helps you.