Adobe Flex PopUpManager -- multiple instances of a TitleWindow opened - apache-flex

Setup: My Flex application is one consisting of several "subapps". Basically, the main application area is an ApplicationControlBar with buttons for each of the subapps. The rest of the area is a canvas where the subapps are displayed. Only one subapp is visible at a time. When switching between subapps, we do a canvas.removeAllChildren(), then canvas.addChild(subAppSwitchedTo). It's essentially a manual implementation of a ViewStack (the pros and cons of which are not the topic of this, so refrain from commenting on this).
Problem: In one of my subapps (let's say subapp "A"), I have a search function where results are displayed in a TitleWindow that gets popped up. Workflow is like enter search criteria, click search button, TitleWindow pops up with results (multiple selection datagrid), choose desired result(s), click OK, popup goes away (PopUpManager.removePopUp), and continue working. This all works fine. The problem is if I switch to a different subapp (say "B" -- where A gets removeAllChildren()'d and B gets added), then switch back to A and search again, when the results TitleWindow pops open, there will be TWO stacked on top of each other. If I continue to navigate away and back to A, every time I search, there will be an additional popup in the "stack" of popups (one for each time A gets addChild()'d).
Has anyone else experienced this? I'm not sure what to do about it and it's causing a serious usability bug in my application. Does this ring any bells to anyone? It's like I somehow need to flush the PopUpManager or something (even though I'm correctly calling removePopUp() to remove the TitleWindow). Please help!
EDIT
Flex SDK = 4.5.1
// Subapp "A"
if (!certificateSearchTitleWindow)
{
certificateSearchTitleWindow = new CertificateSearchTitleWindow;
certificateSearchTitleWindow.addEventListener("searchAccept", searchOKPopupHandler);
certificateSearchTitleWindow.addEventListener("searchCancel", searchClosePopupHandler);
}
PopUpManager.addPopUp(certificateSearchTitleWindow, this, true);

My guess is that the popup is removed from the main display list when you remove its parent (this in the PopUpManager.addPopup() method), but not from its parent display list. Why don't you listen, in your subapps, to the Event.REMOVED event, and then remove your popup ? That would be :
private var pp:CertificateSearchTitleWindow;
private function onCreationComplete():void
{
addEventListener(Event.REMOVED, onRemovede);
}
private function addPopUp():void
{
if (!pp) {
pp = new CertificateSearchTitleWindow();
PopUpManager.addPopUp(pp, this, true);
}
}
private function onRemoved(event:Event):void
{
if (pp) {
PopupManager.removePopUp(pp);
pp = null;
}
}

Thank you to those who gave suggestions. It turned out I was re-registering an eventListener over and over.
I am using a singleton to act as "shared memory" between the subapps. I was setting singleton.addEventListener(someType, listener) in subapp A's creationComplete callback. So everytime I navigated back to A, the creationComplete was running and re-adding this listener. After the search, the listener method (that opened the popup) was being called multiple times, i.e., as many times as the event had been added.
xref: http://forums.adobe.com/message/3941163

Related

Stop AltGr From Triggering Menu Bar Items?

Introduction
The first MenuItem inside my MenuBar receives focus whenever I press down my AltGr key. This is by no means a wanted behavior—although it appears as though it’s the default behavior offered by the MenuBar itself.
This gets slightly annoying since I’m on a Swedish keyboard—meaning both the [] and the {} are called upon using the AltGr key.
Problem
I’d like to remove the functionality whereas the first MenuItem inside the MenuBar gets activated upon pressing down AltGr on a keyboard.
Research
As usual I’ve been browsing around Stackoverflow in the hope to find an answer—but in vain. It’s honestly not very surprising that no one has had this problem before due to the majority of Stackoverflow not actually using Swedish keyboard layouts.
Moreover
Perhaps someone has either seen a post like this somewhere—in that case, do mark this as a duplicate—in any other case, either point me and anyone who might come across this question in the right direction, or simply answer this question with a somewhat shallow example.
Looking at MenuBarSkin's constructor, it adds a scene event key handler which focuses the menu if any keypress involving Alt is not consumed by the time it reaches the scene:
// put focus on the first menu when the alt key is pressed
scene.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
if (e.isAltDown() && !e.isConsumed()) {
firstMenuRunnable.run();
}
});
I've worked around this by putting an event handler on the main content pane of my window, that looks for the key-pressed event of ALT_GRAPH and consumes it. Since my handler goes before the scene handler, it should prevent the menu-focus behaviour from triggering. Roughly:
tabPane.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.ALT_GRAPH)
{
e.consume();
return;
}
});
I'm not sure if AltGr always shows up as ALT_GRAPH; I believe I have seen it show up as ALT with e.isControlDown() being true, but you could also consume that event if none of your menu shortcuts involve Ctrl+Alt (which I'm guessing they won't, as they would be triggered by AltGr since it maps to Ctrl+Alt on Windows).
I had exactly the same problem and based on above answer I was able to solve it by using below code:
mainAnchorPane.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
if (e.isAltDown() || KeyCode.ALT_GRAPH == e.getCode()) {
e.consume();
}
});
On my environment when I pressed AltGr then event reported that Ctrl and AltGr buttons were pressed in this exact order. I didn't care about Ctrl but Alt was anoying so I kill both of my Alts with above code.

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.

Flex4 Right Click Spark List using ContextMenu()

I would like to add custom right clicks to a number of spark list controls.
I have tried the following as an item renderer. (as per the flex 4 cook book).
Full Render code here http://pastebin.com/Kx8tJ1cY
When I right click on the Spark List I simply get the Adobe Default Context menu.
This is the same default behaviour I had before I added any code to this.
Could anyone tell me how to add right clicks to List Items in Flex 4.
Please and Thank you.
I found the problem/solution. You cant use context menus if there are Vboxes or Tab Navigators. Which is insane because it means I cant do relative layout properly or decent variable width design.
Quoted from: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/ui/ContextMenu.html
For example, if a DataGrid control is a child of a TabNavigator or VBox container, the DataGrid control cannot have its own context menu.
Christopher Huyler posted something similar (source code available here). From the article:
Start out by grabbing the Javascript code from Google's code repository.
Step 1 – Setup custom context menu code
Create a new Flex project in Flex Builder. Copy rightclick.js and swfobject.js into the html-template folder of your project. From here, I had to make several changes…
I modified the RightClick.init() function to accept an object and container value as input. This allows me to pass in the name of the application as the object instead of having it be called the same thing every time.
I included rightclick.js and swfobject.js in the header of index.template.html.
I added a new div to the body called “flashcontent”.
I added an onload handler to the body tag to initialize RightClick
I replaced AC_FL_RunContent(…) with new SWFObject(…) making sure to maintain all template variables.
After making these changes, I verified that no right-click context menu appears in my application.
Step 2 – Listen for the rightClick event
Next I added a few lines to the main mxml file of my application to listen for the ExternalInterface event that will be dispatched when I right-click my appliction.
private function handleCreationComplete():void
{
ExternalInterface.addCallback("rightClick", handleRightClick);
}
private function handleRightClick():void
{
Alert.show("Right Click Success!");
}
Step 3 – Dispatch an event to the correct object
Getting the event to the main application is easy, but we actually want the appropriate child object to be notified when the right-click event occurs. Since I am not using any double-click events in my application I decided I would treat every right-click event like a double-click event. Users without a two button mouse (aka Mac users) can simply double-click to get the same menu while users with a two button mouse just have to right-click. Here is how I make sure the event is dispatched to the appropriate object.
private function handleRightClick():void
{
var objects:Array = systemManager.getObjectsUnderPoint(
new Point(mouseX,mouseY));
if (objects.length>0)
{
var object:Object = objects[objects.length-1];
var relatedObject:InteractiveObject;
if (object is InteractiveObject)
relatedObject = object as InteractiveObject;
else if (object.parent && object.parent is InteractiveObject)
relatedObject = object.parent;
var event:MouseEvent = new MouseEvent(
MouseEvent.DOUBLE_CLICK,true,false,mouseX,mouseY,
relatedObject);
object.dispatchEvent(event);
}
}
I hope this helps!

Qt-Right click mouseReleaseEvents aren't caught by eventfilter,other events though are caught

My application consists of a WebView widget. A mouse click on the widget is not handled by the mousePressEvent() of my application, but by the WebView widget. So, I installed an event filter to receive the events. Now, I get notified of all events, except the mouseReleaseEvent for the right click (Everything works fine for left clicks and mousePressEvent for the right click is also getting registered). I guess it has got something to do with context events getting generated by right clicks (a pop-up menu gets generated). But since I am using a filter, the event should first be sent to me. The following is the code for the event filter
in Jambi, but I hope I can modify an answer given in Qt for Jambi.
public boolean eventFilter(QObject o,QEvent event)
{
if (event.type()==QEvent.Type.MouseButtonPress) // One can call the mousePressEvent() functions from here,which can do this work but speed
{
if (((QMouseEvent)event).button()==Qt.MouseButton.LeftButton)
{
mousebuttontype=1;
clickedorpressed=1;
}
else
if (((QMouseEvent)event).button()==Qt.MouseButton.RightButton)
{
mousebuttontype=2;
System.out.println("right");
}
t1=QTime.currentTime();
t1.start();
}
else
if (event.type()==QEvent.Type.MouseButtonRelease)
{
if (t1.elapsed()>900)
{
switch(mousebuttontype)
{
case 1: browser.back();
break;
case 2: browser.forward();
break;
}
}System.out.println("choda");
}
return false;
}
MY BASIC AIM IS GETTING THE TIME INTERVAL FOR WHICH THE RIGHT CLICK WAS PRESSED. ANY WORKAROUND ?
Some digging around certainly seems to suggest that there may be no right-mouse release event being generated if that is the trigger for a context menu on your particular system. These are conveyed as a QContextMenuEvent.
This Qt Labs post hints about this too.
A work around may be tricky if it is system dependent. Have you tried overriding event() in the QApplication class to see if the event comes through there? Some events don't necessarily get to the children but everything goes through QApplication::event().
Another option for you is, subclass Qwebview and override mouseReleaseEvent event
I've found a fix for this that I dont think will be system dependent.
My particular case was using mouseReleaseEvent in order to catch the events, and use them myself. I didn't get these events.
On all child widgets of the widget that I want to handle the event, I added to the class definition:
protected:
void mouseReleaseEvent(QMouseEvent *event) {event->ignore();}
This overrides the default implementation for context menu's, and sends the mouseReleaseEvent back up the parent chain like it would for other mousebuttons.
http://doc.qt.io/qt-5/qevent.html#ignore
This shows that it will probably propagate to the parent widget. As the link indicates, this fixed it for me at Qt 5.9, but I think it should work for virtually all version.
(I am aware this question is literally 7 years old, but it doesn't contain the fix that I think would be the best fix, and shows up as result 2 on google(qt not getting mouse release event on rightclick). So I think it deserves an up to date answer.)
Without going in to broader implementation explanations, the core problem I needed to solve related to this issue was that I needed to know if a context menu swallowed the right-click so I could ensure some custom state was handled properly. The simplest workaround I was able to find was to implement contextMenuEvent, which gets called after mousePressEvent, to detect if the event was accepted (a context menu was opened somewhere in my QGraphicsScene/Items). Here is a minimal example in Python to demonstrate how the event state might be used:
def contextMenuEvent(self, event):
event.setAccepted(False)
super(MyGraphicsView, self).contextMenuEvent(event)
self.__context_menu_used = event.isAccepted()
Note you can also skip running the the base class contextMenuEvent entirely to block the scene/items from opening a context menu. This is handy if you want to do 3D-DCC-like alt-RMB-zooming without accidentally opening a context menu, e.g.:
def contextMenuEvent(self, event):
# Block context menus if alt is held down
if event.modifiers() & QtCore.Qt.AltModifier:
return
event.setAccepted(False)
super(MyGraphicsView, self).contextMenuEvent(event)
self.__context_menu_used = event.isAccepted()

Flex and fake Mxml initialisation without actually showing the component, (more insise)

I have a TitleWindow mxml class wich has several components, and listeners.
On its creationComplete and init state i add some listeners which listen for events on its gui.
This TitleWindow is only shown when the user click on a "button", i made TitleWindow a singleton with the following code:
public static function getInstance():MyWindow
{
if ( MyWindow.singleton )
{
return MyWindow.singleton;
}
else{
MyWindow.singleton = new MyWindow();
return MyWindow.singleton;
}
}
I needed a singleton because the user will call this window several times as much as he wants and i only need one.
The problem is the following on some special external events i need to "modify" some listeners (remove listeners and add new ones) on a button from MyWindow, before it was even shown once.
I still have MyWindow.getInstance() in memory when my application starts up.
However adding /removing listeners does not seem to have any effect if he actual rendering of the components did not happen, event when using the following code on app startup.
myWindow= MyWindow.getInstance();
myWindow.initialize();
Not suprisingly if i "show" ('render') the myWindow at least once then the events modifications on the myWindow instance works perfectly.
How can i fake the complete initialisation of this component without showing it on startup ?
Thanks !
Which sort of a container holds your button? If you are using a Multiple View Container you can try setting the creationPolicy to all. Single View Containers create all their children in one go and you shouldn't face this problem.
From Flex 3.0 docs I could retrieve this:
The default creation policy for all containers, except the Application container, is the policy of the parent container. The default policy for the Application container is auto.
This looks like the cause for all your troubles.
Update: I did not mention this earlier, since I thought this was to be expected :) Setting the creationPolicy to all makes your application load more slowly. So, read up on Ordered Creation -- this technique helps you to choose if the controls are displayed all in one go (which is the default behavior, after all of the controls have been created) or step-by-step, as and when they are created.

Resources