QWidget на Mac OS X не фокусируется на Qt 5.x

У меня QSystemTrayIcon с QAction, который открывает новое окно типа QWebView. Когда окно теряет фокус, и я снова выбираю QAction, окно должно восстановить фокус. Он работает в Linux, но не работает в Mac OS X. Проблема заключается в том, что когда у меня есть другое окно, открытое и активное, скажем, Google Chrome, когда я вызываю show() в окне, которое я пытаюсь открыть, это всегда открывается под Google Chrome, поэтому я не вижу его. То же самое касается фокусировки, когда у меня открыто несколько окон, и мой QWebView может быть последним в заказе, когда я нажимаю QAction, чтобы сфокусировать окно, он всегда будет находиться в окне Google Chrome. Я предполагаю, что когда я нажимаю QAction, который является частью моего процесса приложения, он попытается открыть/сфокусировать окно, но в середине операции окно Google Chrome получает запланированное и получает фокус, так как QSystemTrayIcon не может удерживать фокус. Из-за этого, когда окно открывается/сфокусировано, оно не украдет фокус из Google Chrome, потому что операционная система не позволяет его, поэтому он будет помещен в окно с точной фокусировкой.

Вот как я создаю/фокусирую окно:

// ...
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();
  }
}

Есть ли что-то, что я делаю неправильно или есть какое-либо известное обходное решение?

Ответ 1

Поэтому мне удалось исправить проблему с помощью зависящего от платформы кода. Я создал класс Focuser с кодом в файле .mm и содержал код Objective-C, который назывался 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();
}

Итак, у нас есть один класс, который принимает QWidget в конструкторе и имеет два общедоступных метода: одно шоу, которое показывает виджет и фокус, который фокусирует виджет. Тогда у нас есть два определения класса: один для Mac OS X в focuser_mac.mm и один в focuser.cpp для любой другой операционной системы. Тот, который для mac дополнительно вызывает

[NSApp activateIgnoringOtherApps:YES];

Теперь, чтобы он мог компилироваться, поскольку он должен добавить это в ваш файл .pro:

HEADERS += focuser.h

mac {
  OBJECTIVE_SOURCES += focuser_mac.mm
}

linux|win32 {
  SOURCES += focuser.cpp
}

Когда мы закончим, просто добавьте этот код, где вам нужно, чтобы ваше приложение было сфокусировано:

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

Ответ 2

Немного offtopic, но это может быть полезно для некоторых пользователей:

Мое предложение - создать платформенный код для принудительного создания окна. На платформе Windows такая же проблема, поэтому я использую следующий хак:

    void Utils::forceForegroundWindow( WId winId )
    {
#ifdef Q_OS_WIN
        HWND hWnd = winId;

        if ( ::IsWindow( hWnd ) )
        {
            HWND hCurrWnd;
            int iMyTID;
            int iCurrTID;

            hCurrWnd = ::GetForegroundWindow();
            iMyTID   = ::GetCurrentThreadId();
            iCurrTID = ::GetWindowThreadProcessId( hCurrWnd, 0 );

            ::AttachThreadInput( iMyTID, iCurrTID, TRUE );

            ::ShowWindow( hWnd, SW_SHOWNORMAL );
            ::SetForegroundWindow( hWnd );

            ::AttachThreadInput( iMyTID, iCurrTID, FALSE );
        }

#endif
    }

Я по-прежнему не поддерживаю совместимость с Mac OS в своем проекте, поэтому этот код не имеет функций для платформ без выигрыша.

Другая идея: вы всегда должны держать сосредоточенное видимое окно. Попробуйте сделать это с помощью WA_TranslucentBackground | Атрибуты WA_TransparentForMouseEvents + флаг FramelessWindowHint. Таким образом, ваше приложение никогда не потеряет фокус.