Need QMacNativeWidget help/advice
-
Greetings -
I have a non-Qt application that dynamically loads a library, whose purpose is to display a Qt dialog widget. On Windows, I've successfully used the QWinWidget:
@
QApplication app(0, NULL);
HWND m_hwnd = GetWindowHandle(); // top-level window of Win32 application
QWinWidget winWidget(m_hwnd);
MyDialog dlg(&winWidget);
dlg.exec();
@On Mac, I have this:
@
extern void qt_mac_set_native_menubar(bool);
qt_mac_set_native_menubar(false);
QApplication app(0, NULL);
QMacNativeWidget macWidget;
MyDialog dlg(&macWidget);
dlg.exec();
@Note that this differs from the Windows version, in that the "native" widget does not take a "native" window as a parent argument in its constructor...As well, this differs from the (few) examples I've seen, in which a new Mac window is created (for example, see http://lists.qt.nokia.com/pipermail/qt-interest/2009-March/004365.html).
In any case, this more or less works - the dialog is displayed and is functional. It's modal due to the use of exec(). There is a problem, though: after the dialog is launched, if I hit command-H to hide the application, only my Qt dialog gets hidden; further, if I simply launch the dialog, use it, and close it, forever after that attempts to hide the application fail.
I can see perhaps how the "hide" operation works only on the dialog, when it is active, as the event handling is now under Qt's control via the exec() call. But, that does not explain why even bringing up the dialog once seems to disable application hiding for the remainder of the session. Finally, occasionally on command-Q to kill the application, I get a crash in Qt; this crash is at the bottom of a stack of what appear to be Cocoa calls. Given that the main application is Carbon, should I compile and use Carbon Qt libraries? Maybe combining Carbon (main app) and Cocoa (the Qt dialog) event handling is part/all of the problem?
I have the feeling that the way I'm using the QMacNativeWidget isn't sufficiently integrating the Qt dialog with the existing Carbon app, and that perhaps I should be in fact creating a window as is done in the examples. But, it's not at all clear how I would do this with a Qt dialog widget.
Any help/advice would be greatly appreciated...there seems to be very little of use on the web in general, on this topic.
Thanks!
-
Hmm...perhaps I really should be providing a "native" window id in the constructor for the QMacNativeWidget...but at this point, I need to do this programatically. Anyone know how to find the top-level window for an application on Mac, from inside a thread of the application? thanks...
-
I guess this must be quite an obscure topic, as I'm pretty much talking to myself here...So perhaps this will serve as some sort of reference for future searching...
I built Qt for Carbon, and got the top-level window to pass into the constructor for the QMacNativeWidget:
@
WindowRef windowRef = GetFrontWindowOfClass(kDocumentWindowClass, true);
HIViewRef root = HIViewGetRoot((WindowRef) windowRef);
@It is now very much parallel to the (working) Windows implementation.
What does not work, though, is dealing with Command-Q. I have found that the QApplication, QMacNativeWidget, and my custom dialog all need to be static variables, otherwise I get crashes. The result is that various Mac events get "caught" by Qt, including Command-Q. This causes the QApplication to quit, with rather disastrous consequences.
Now, QApplication has an overridable method
@
virtual bool macEventFilter(EventHandlerCallRef caller, EventRef event);
@
I implemented this in a subclassed QApplication, and detect the case when the event is kEventAppQuit. Rather than quitting, I need to re-route this event to the main application's event queue, so that the main application actually quits. According to the documentation, I should be able to return true in this case, to stop the event from being processed (and not quit my QApplication?). As well, I have tried several methods to send the event to the main event queue, without results.I have tried:
@
CallNextEventHandler(caller, event);
@and
@
PostEventToQueue(m_eventQueue, event, kEventPriorityHigh);
@and
@
SendEventToApplication();
@and
@
SendEventToWindow();
@In no case does the main application event loop get notified of the Command-Q.
By detecting the quit event, I can delete and then recreate the QApplication, QMacNativeWidget, and my custom dialog, and everything works well afterwards, but really the application ought to have quit when the user hit Command-Q.
Any help/advice appreciated. Probably I'm just not talking to the main event queue correctly?
-
Thought I had already followed up on this, but seems my previous attempt didn't "stick"...
The main issue turns out that Qt installs an event handler for kEventAppQuit by default, when one creates a QApplication instance. In the case where you're making a Qt widget with a native Mac window parent, and that widget is not the main window of your app, probably this is not what you want - "Quit" should be handled by the main app, and certainly not in a Qt dialog loaded by a plugin. The solution here is to simply add this line:
@
QApplication::setAttribute(Qt::AA_MacPluginApplication);
@prior to declaring/creating your QApplication, and the Quit event goes back to being handled by the main application, as it ought to be. So, no need for trying to "talk" to the main event loop, etc.
(see https://bugreports.qt-project.org/browse/QTBUG-8087 for relevant info...)