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. Таким образом, ваше приложение никогда не потеряет фокус.