2013-02-28 63 views

回答

5

这是疯了,但我得到了它,并没有任何的Objective-C编码:

我得出的QApplication。在我的派生类的*的.cpp部分我把:

#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 

在我的派生的应用程序类的构造函数,我把:

#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,"[email protected]:"); 

     if (!test) 
     { 
      // failed to register handler... 
     } 
    } 

#endif 

添加这种简单的方法,以我的应用程序类(注意,它被称为从在我的回答顶部的处理程序)

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

像魅力工作。

+0

谢谢!显然,这段代码在Qt 4.8.7中停止了工作,但我设法使它工作(在Qt 5.4.1上),所以我发布了一个更新版本的答案。 – exscape 2015-03-02 20:19:02

+0

这不适合我。 exscape的答案是' – 2015-03-12 19:52:55

9

由于弃用警告(post-OS X 10.5)和类型错误,我无法正确编译原始答案;我改变了一些类型的名称,并将其编译,但代码仍然无法正常工作。

事实证明,较新版本的Qt(4.8.7+,包括5.x;我使用5.4.1)实现了我们想要添加的方法,如果该方法已经存在,则class_addMethod失败。见this QTBUG
注意:上述错误报告包含一个稍微不同的解决方案(我自己解决问题后发现它)。

对我而言,一种解决方案是检查方法是否存在。如果是这样,我们将其替换。如果不是,我们只需添加它。
我还没有在旧的Qt版本上测试过这个代码,但它使用OPs逻辑,所以它应该可以工作。

这是我的代码。就像在OP中一样,所有代码都在QApplication子类的.cpp文件中。

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

我QApplication的子类构造函数包含

#ifdef Q_OS_MAC 
    setupDockClickHandler(); 
#endif 

最后,地方在同一个文件(在底部,在我的情况):

#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, "[email protected]:")) 
       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,"[email protected]:")) 
       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 

另见Apple documentation on the applicationShouldHandleReopen:hasVisibleWindows: method

为了编译,您还需要链接一些额外的框架。
使用qmake的,我增加了以下我的.pro文件:

LIBS += -framework CoreFoundation -framework Carbon -lobjc 

这些标志当然正是你应该添加什么到C++或铛++命令行,如果你手动编译。
这应该是所需的一切。

+1

这是一个很好的答案,但是,我不认为有必要避免使用目标C。qmake处理它,而Qt头文件在从'.mm'文件包含时是有效的。这可以简单地用obj-C++写成'.mm'文件,并且在没有显式调用objc运行时的情况下可读性更强。 Mac上的Qt项目可以包含.m和.mm文件,您可以使用.mm文件中的Qt。 – 2015-07-06 12:44:05

0

与QEvent的问题:: ApplicationActivate是,它会发出每一个激活 - 例如,即使您切换到应用程序切换应用程序。原生行为是仅在Dock图标点击时显示应用程序,而不是在通过cmd +选项卡切换时显示应用程序。

但是,至少在Qt 5.9.1中有一个破解版。 Dock图标点击产生2个连续的QEvent :: ApplicationStateChangeEvent事件,同时cmd + tab - 只有一个。 因此,这段代码会相当准确地发出Dock点击信号。应用程序类是从QApplication继承的应用程序类,也是自身的事件过滤器。

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); 
} 
相关问题