QT on OS X, How to Determine How to Click a Dock Application Icon

I have an open Qt Mac application. I click the application icon

Is there a way to get a notification for this in the app?

+6
source share
4 answers

This is crazy, but I got it without any Objective-C encodings:

I got QApplication. In the * .cpp part of my derived class, I will put:

#ifdef Q_OS_MAC #include <objc/objc.h> #include <objc/message.h> bool dockClickHandler(id self,SEL _cmd,...) { Q_UNUSED(self) Q_UNUSED(_cmd) ((MyApplictionClass*)qApp)->onClickOnDock(); return true; } #endif 

in my derived constructor of the application class I put:

 #ifdef Q_OS_MAC objc_object* cls = objc_getClass("NSApplication"); SEL sharedApplication = sel_registerName("sharedApplication"); objc_object* appInst = objc_msgSend(cls,sharedApplication); if(appInst != NULL) { objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate")); objc_object* delClass = objc_msgSend(delegate, sel_registerName("class")); const char* tst = class_getName(delClass->isa); bool test = class_addMethod((objc_class*)delClass, sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:"), (IMP)dockClickHandler," B@ :"); if (!test) { // failed to register handler... } } #endif 

Added this simple method to my application class (note that it refers to a handler at the top of my answer)

 void MyApplictionClass::onClickOnDock() { // do something... } 

It works like a charm.

+5
source

I could not get the original answer for proper compilation due to dismissal warnings (post-OS X 10.5) and type error; I changed a few type names and got it for compilation, but the code still didn't work.

It turns out that newer versions of Qt (4.8.7+, including 5.x; I use 5.4.1) implement the method we want to add, and class_addMethod fails if this method already exists. See this QTBUG .
Note: the above error report contains a slightly different solution (I found it after fixing the problem myself).

One solution that works for me is to check if a method exists. If this happens, we will replace it. If not, we just add it.
I have not tested this code on older versions of Qt, but it uses OPs logic, so it should work.

Here is my code. As with OPs, all code is in the .cpp file of the QApplication subclass.

 #ifdef Q_OS_MAC #include <objc/objc.h> #include <objc/message.h> void setupDockClickHandler(); bool dockClickHandler(id self,SEL _cmd,...); #endif 

The subclass constructor of QQpplication contains

 #ifdef Q_OS_MAC setupDockClickHandler(); #endif 

And finally, somewhere in the same file (below, in my case):

 #ifdef Q_OS_MAC void setupDockClickHandler() { Class cls = objc_getClass("NSApplication"); objc_object *appInst = objc_msgSend((objc_object*)cls, sel_registerName("sharedApplication")); if(appInst != NULL) { objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate")); Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class")); SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:"); if (class_getInstanceMethod(delClass, shouldHandle)) { if (class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, " B@ :")) qDebug() << "Registered dock click handler (replaced original method)"; else qWarning() << "Failed to replace method for dock click handler"; } else { if (class_addMethod(delClass, shouldHandle, (IMP)dockClickHandler," B@ :")) qDebug() << "Registered dock click handler"; else qWarning() << "Failed to register dock click handler"; } } } bool dockClickHandler(id self,SEL _cmd,...) { Q_UNUSED(self) Q_UNUSED(_cmd) // Do something fun here! qDebug() << "Dock icon clicked!"; // Return NO (false) to suppress the default OS X actions return false; } #endif 

Also see Apple documentation on applicationShouldHandleRopen: hasVisibleWindows: method .

To compile this, you also need to bundle some additional frameworks.
Using qmake, I added the following to the .pro file:

 LIBS += -framework CoreFoundation -framework Carbon -lobjc 

These flags, of course, are exactly what you should add to the C ++ or clang ++ command line if you compile manually.
That should be all that is required.

+9
source

Starting with Qt5.4.0, you can handle the QEvent associated with clicking on the dock: QEvent :: ApplicationActivate.

https://bugreports.qt.io/browse/QTBUG-10899

https://doc.qt.io/qt-5/qevent.html

0
source

The problem with QEvent :: ApplicationActivate is that it will be emitted for each activation - for example, even if you switch to the application on the Application Switcher. Own behavior is to show the application only when you click on the Dock icon, and not when going to the cmd + tab.

But there is a hack that works at least for Qt 5.9.1. The Dock icon icon generates 2 consecutive QEvent :: ApplicationStateChangeEvent events, but for now, cmd + tab is only one. Thus, this code will quite accurately emit the click signal of the dock. The App class is an application class inherited from QApplication, as well as an event filter for itself.

 bool App::eventFilter(QObject* watched, QEvent* event) { #ifdef Q_OS_MACOS if (watched == this && event->type() == QEvent::ApplicationStateChange) { auto ev = static_cast<QApplicationStateChangeEvent*>(event); if (_prevAppState == Qt::ApplicationActive && ev->applicationState() == Qt::ApplicationActive) { emit clickedOnDock(); } _prevAppState = ev->applicationState(); } #endif // Q_OS_MACOS return QApplication::eventFilter(watched, event); } 
0
source

All Articles