[英]How can I intercept and cancel the minimizing of a Window?
我的項目中有一個Window
子類,並且在運行時創建了實例並將其完全顯示在QML端。 我知道我可以通過在flags:
不包括WindowMinimizeButtonHint
來防止窗口被最小化,但是我實際上需要顯示並啟用最小化按鈕,但是能夠攔截最小化按鈕單擊,取消實際的最小化,然后執行還有其他事情(僅供參考,我的客戶要求這種非標准的窗口行為,而不是我)。
到目前為止,我唯一能實現的就是處理onWindowStateChanged:
事件,檢查windowState === Qt.WindowStateMinimized
並從計時器中調用show()
(在事件處理程序中直接調用它不執行任何操作)。 這導致窗口向下移動到系統托盤,然后突然恢復正常。
有沒有辦法做到這一點,比如可以取消的OnMinimized
事件?
編輯:基於本傑明·T的回答,我至少是OSX解決方案的一部分:
#import <AppKit/AppKit.h>
bool NativeFilter::nativeEventFilter(const QByteArray &eventType,
void *message, long *result)
{
if (eventType == "mac_generic_NSEvent") {
NSEvent *event = static_cast<NSEvent *>(message);
if ([event type] == NSKeyDown) {
return true;
}
}
return false;
}
在此示例中,我能夠攔截並取消所有NSKeyDown事件(同時使其他事件(如鼠標單擊等)仍然有效)。 剩下的問題是我仍然不知道截取一個最小事件 -NSEvent.h似乎沒有任何東西可以解決這個問題。 也許我需要進行其他類型的活動?
編輯2-工作解決方案:
我無法找到適當的方法來攔截最小事件並將其取消,因此,我的解決方法是攔截窗口上的單擊,確定單擊是否在最小化按鈕(或關閉或縮放按鈕)上並取消如果是這樣,則為事件(並將點擊發生的通知發送至我的qml窗口)。 我還處理了雙擊標題欄以縮放窗口,並使用Command-M鍵最小化窗口的情況。
第一步是實現QAbstractNativeEventFilter
。 在標題中:
#include <QAbstractNativeEventFilter>
class NativeFilter : public QAbstractNativeEventFilter {
public:
bool nativeEventFilter(const QByteArray &eventType, void *message,
long *result);
};
實現:
#import <AppKit/AppKit.h>
#import <AppKit/NSWindow.h>
#import <AppKit/NSButton.h>
bool NativeFilter::nativeEventFilter(const QByteArray &eventType, void
*message, long *result)
{
if (eventType == "mac_generic_NSEvent") {
NSEvent *event = static_cast<NSEvent *>(message);
NSWindow *win = [event window];
// TODO: determine whether or not this is a window whose
// events you want to intercept. I did this by checking
// [win title] but you may want to find and use the
// window's id instead.
// Detect a double-click on the titlebar. If the zoom button
// is enabled, send the full-screen message to the window
if ([event type] == NSLeftMouseUp) {
if ([event clickCount] > 1) {
NSPoint pt = [event locationInWindow];
CGRect rect = [win frame];
// event coordinates have y going in the opposite direction from frame coordinates, very annoying
CGFloat yInverted = rect.size.height - pt.y;
if (yInverted <= 20) {
// TODO: need the proper metrics for the height of the title bar
NSButton *btn = [win standardWindowButton:NSWindowZoomButton];
if (btn.enabled) {
// notify qml of zoom button click
}
return true;
}
}
}
if ([event type] == NSKeyDown) {
// detect command-M (for minimize app)
if ([event modifierFlags] & NSCommandKeyMask) {
// M key
if ([event keyCode] == 46) {
// notify qml of miniaturize button click
return true;
}
}
// TODO: we may be requested to handle keyboard actions for close and zoom buttons. e.g. ctrl-cmd-F is zoom, I think,
// and Command-H is hide.
}
if ([event type] == NSLeftMouseDown) {
NSPoint pt = [event locationInWindow];
CGRect rect = [win frame];
// event coordinates have y going in the opposite direction from frame coordinates, very annoying
CGFloat yInverted = rect.size.height - pt.y;
NSButton *btn = [win standardWindowButton:NSWindowMiniaturizeButton];
CGRect rectButton = [btn frame];
if ((yInverted >= rectButton.origin.y) && (yInverted <= (rectButton.origin.y + rectButton.size.height))) {
if ((pt.x >= rectButton.origin.x) && (pt.x <= (rectButton.origin.x + rectButton.size.width))) {
// notify .qml of miniaturize button click
return true;
}
}
btn = [win standardWindowButton:NSWindowZoomButton];
rectButton = [btn frame];
if (btn.enabled) {
if ((yInverted >= rectButton.origin.y) && (yInverted <= (rectButton.origin.y + rectButton.size.height))) {
if ((pt.x >= rectButton.origin.x) && (pt.x <= (rectButton.origin.x + rectButton.size.width))) {
// notify qml of zoom button click
return true;
}
}
}
btn = [win standardWindowButton:NSWindowCloseButton];
rectButton = [btn frame];
if ((yInverted >= rectButton.origin.y) && (yInverted <= (rectButton.origin.y + rectButton.size.height))) {
if ((pt.x >= rectButton.origin.x) && (pt.x <= (rectButton.origin.x + rectButton.size.width))) {
// notify qml of close button click
return true;
}
}
}
return false;
}
return false;
}
然后在main.cpp中:
Application app(argc, argv);
app.installNativeEventFilter(new NativeFilter());
一般而言,您應該使用事件系統而不是信號/插槽來攔截事件和更改。
這樣做的最簡單方法是將您使用的對象子類化並重新實現適當的事件處理程序,或者使用事件過濾器。
由於您使用的是QML,因此子類化可能很困難,因為您無法訪問所有Qt內部類。
使用事件過濾時,代碼如下所示。
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
auto root = engine.rootObjects().first();
root->installEventFilter(new EventFilter());
return app.exec();
}
class EventFilter : public QObject
{
Q_OBJECT
public:
explicit EventFilter(QObject *parent = nullptr);
bool eventFilter(QObject *watched, QEvent *event) override;
};
bool EventFilter::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::WindowStateChange) {
auto e = static_cast<QWindowStateChangeEvent *>(event);
auto window = static_cast<QWindow *>(watched);
if (window->windowStates().testFlag(Qt::WindowMinimized)
&& ! e->oldState().testFlag(Qt::WindowMinimized))
{
// Restore old state
window->setWindowStates(e->oldState());
return true;
}
}
// Do not filter event
return false;
}
但是,您將很快遇到與使用信號/插槽機制相同的問題:Qt僅在窗口已最小化時通知您。 意味着此時恢復窗口將產生隱藏/顯示效果。
因此,您需要更深入地了解本機事件過濾器。
以下代碼在Windows上有效,您應該將其適配於macOS:
class NativeFilter : public QAbstractNativeEventFilter {
public:
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);
};
bool NativeFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
/* On Windows we interceot the click in the title bar. */
/* If we wait for the minimize event, it is already too late. */
#ifdef Q_OS_WIN
auto msg = static_cast<MSG *>(message);
// Filter out the event when the minimize button is pressed.
if (msg->message == WM_NCLBUTTONDOWN && msg->wParam == HTREDUCE)
return true;
#endif
/* Example macOS code from Qt doc, adapt to your need */
#ifdef Q_OS_MACOS
if (eventType == "mac_generic_NSEvent") {
NSEvent *event = static_cast<NSEvent *>(message);
if ([event type] == NSKeyDown) {
// Handle key event
qDebug() << QString::fromNSString([event characters]);
}
}
#endif
return false;
}
在您的main()中:
QGuiApplication app(argc, argv);
app.installNativeEventFilter(new NativeFilter());
有關更多信息,您可以閱讀有關QAbstractNativeEventFilter
的Qt文檔。
您可能需要使用QWindow::winId()
來檢查本機事件針對的窗口。
由於我不是macOS開發人員,所以我不知道您可以使用NSEvent
做什么。 另外,似乎NSWindowDelegate
類可能對您有用: https : //developer.apple.com/documentation/appkit/nswindowdelegate如果可以從QWindow::winId()
檢索NSWindow
,則應該可以使用它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.