Search code examples
c++macosqtqt5qwidget

QWidget on Mac OS X not focusing in Qt 5


I have QSystemTrayIcon with a QAction that opens a new window of type QWebView. When the window loses focus and I select the QAction again, the window should regain focus. It does on Linux, but doesn't on Mac OS X.

The problem is, that when I have another window open and active, let's say Google Chrome, when I call show() on the window I'm trying to open, it always gets opened under the Google Chrome, so I don't see it.

The same goes for focusing, when I have multiple windows open, and my QWebView might be the last one in the order, when I click the QAction to focus the window, it will always be under the Google Chrome window.

My guess is, that when I click the QAction, which is the part of my application's process, it will try to open/focus the window. But, in the middle of the operation, the Google Chrome window gets scheduled and gains focus, since QSystemTrayIcon cannot hold focus.

Because of that, when the window gets opened/focused, it will not steal the focus from Google Chrome, because the operating system does not allow it, so it will be placed under the currently focused window.

Here how I create/focus the window:

// ...
QPointer<QWebView> view;
// ...

void TrayIcon::webView() {
  if (!this->view) {
    this->view = new QWebView();
    this->view->load("http://example.com");
    this->view->show();
  } else {
    this->view->activateWindow();
    this->view->raise();
  }
}

Is there something I am doing incorrectly, or is there any known workaround?


Solution

  • I managed to fix the problem with platform-dependent code. I created Focuser class with code in the .mm file and that contained Objective-C code that called Cocoa.

    focuser.h

    #ifndef FOCUSER_H
    #define FOCUSER_H
    
    #include <QWidget>
    
    class Focuser {
      QWidget *widget;
    public:
      Focuser(QWidget *);
      void show();
      void focus();
    };
    
    #endif // FOCUSER_H
    

    focuser_mac.mm

    #include "focuser.h"
    #import <Cocoa/Cocoa.h>
    
    Focuser::Focuser(QWidget *w) {
      this->widget = w;
    }
    
    void Focuser::show() {
      this->widget->show();
      this->focus();
    }
    
    void Focuser::focus() {
      [NSApp activateIgnoringOtherApps:YES];
      this->widget->activateWindow();
      this->widget->raise();
    }
    

    focuser.cpp

    #include "focuser.h"
    
    Focuser::Focuser(QWidget *w) {
      this->widget = w;
    }
    
    void Focuser::show() {
      this->widget->show();
      this->focus();
    }
    
    void Focuser::focus() {
      this->widget->activateWindow();
      this->widget->raise();
    }
    

    So we've got one class that takes QWidget in the constructor and has two public methods, one show that shows the widget and focus that focuses the widget. Then we have two definitions of the class, one for Mac OS X in focuser_mac.mm and one in focuser.cpp for any other operating system. The one for the mac additionally calls:

    [NSApp activateIgnoringOtherApps:YES];
    

    Now, in order for it to compile as it should add this to your .pro file:

    HEADERS += focuser.h
    
    mac {
      OBJECTIVE_SOURCES += focuser_mac.mm
    }
    
    linux|win32 {
      SOURCES += focuser.cpp
    }
    

    When we're done, just add this code where you need your application to be focused:

    QWidget *w = new QWidget();
    // ...
    Focuser f(w);
    w.show(); // The widget will now be shown and focused by default.