2013-02-28 14 views

risposta

5

E 'pazzesco, ma ho preso, e senza alcuna codifica Objective-C:

ho derivato QApplication. Nel * parte cpp della mia classe derivata ho messo:

#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 

nel mio costruttore della classe di applicazione derivata ho messo:

#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 

Aggiunto questo semplice metodo per la mia classe di applicazione (nota è indicato da il gestore in cima alla mia risposta)

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

Funziona come il fascino.

+0

Grazie per questo! A quanto pare, questo codice ha smesso di funzionare in Qt 4.8.7, ma sono riuscito a farlo funzionare (su Qt 5.4.1), così ho postato una risposta con una versione aggiornata. – exscape

+0

Questo non funziona per me. La risposta di exscape funziona –

9

Non ho potuto ottenere la risposta originale per compilare correttamente a causa di avvertimenti di deprecazione (post-OS X 10.5) e errori di tipo; Ho cambiato alcuni nomi di tipo e l'ho compilato, ma il codice non funzionava ancora.

Si scopre che le versioni più recenti di Qt (4.8.7+, incluso 5.x; io uso 5.4.1) implementano il metodo che si desidera aggiungere e class_addMethod non riesce se il metodo esiste già. Vedi this QTBUG.
Nota: il rapporto bug sopra riportato contiene una soluzione leggermente diversa (l'ho trovata dopo aver risolto il problema personalmente).

Una soluzione, che funziona per me, consiste nel verificare se il metodo esiste. Se lo fa, lo sostituiamo. In caso contrario, semplicemente lo aggiungiamo.
Non ho testato questo codice su versioni precedenti di Qt, ma utilizza la logica OP, quindi dovrebbe funzionare.

Ecco il mio codice. Come nel caso degli OP, tutto il codice si trova nel file .cpp di una sottoclasse QApplication.

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

mio costruttore QApplication sottoclasse contiene

#ifdef Q_OS_MAC 
    setupDockClickHandler(); 
#endif 

E, infine, da qualche parte nello stesso file (in fondo, nel mio caso):

#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 

vedere anche il Apple documentation on the applicationShouldHandleReopen:hasVisibleWindows: method.

Per poter essere compilato, è inoltre necessario creare un collegamento con alcuni framework aggiuntivi.
Utilizzando qmake, ho aggiunto il seguente al mio file .pro:

LIBS += -framework CoreFoundation -framework Carbon -lobjc 

quelle bandiere sono naturalmente esattamente ciò che si dovrebbe aggiungere al C++ o clang ++ riga di comando, se si compila manualmente.
Questo dovrebbe essere tutto ciò che è richiesto.

+1

E 'una bella risposta, ma, come un a parte, non credo che sia necessario evitare l'uso di Objective C. qmake maniglie, e le intestazioni Qt sono validi quando incluso da un file '.mm'. Questo potrebbe essere semplicemente scritto in obj-C++ in un file '.mm' e sarebbe un po 'più leggibile senza scali espliciti runtime objc. I progetti Qt su mac possono includere file .m e .mm e puoi usare Qt da un file .mm. –

0

Il problema con QEvent :: ApplicationActivate è che sarà emesso per ogni attivazione -. Per esempio, anche se si passa per l'applicazione su Application Switcher. Il comportamento nativo è quello di mostrare l'app solo sul clic sull'icona del Dock, non quando passi da cmd + tab.

Ma, c'è un hack che funziona, almeno per Qt 5.9.1. L'icona del Dock click produce 2 QEvent :: ApplicationStateChangeEvent eventi sequenziali, nel frattempo cmd + TAB - solo uno. Quindi, questo codice emetterà il segnale di clic del Dock in modo abbastanza accurato. La classe app è la classe dell'applicazione ereditata da QApplication e anche un filtro eventi per se stesso.

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); 
}