2013-02-28 130 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); 
} 
相關問題