【发布时间】:2018-07-25 01:07:10
【问题描述】:
我的项目中有一个Window 子类,在运行时该实例被创建并完全显示在 QML 端。我知道我可以通过在flags: 中不包含WindowMinimizeButtonHint 来防止窗口最小化,但我实际上需要存在并启用最小化按钮,但能够拦截最小化按钮单击,取消实际最小化,然后做其他事情(仅供参考,我的客户需要这种非标准的窗口行为,而不是我)。
到目前为止,我唯一能做到的就是处理onWindowStateChanged: 事件,检查windowState === Qt.WindowStateMinimized 并从计时器调用show()(直接在事件处理程序中调用它什么都不做)。这会导致窗口向下移动到系统托盘,然后突然恢复正常。
有没有办法做到这一点,比如可以取消的OnMinimized 事件?
编辑:根据 Benjamin 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());
【问题讨论】:
-
这是一个非常奇怪的行为。您需要做什么而不是 OS 最小化?你不能把这个功能放在你应用程序内的控件上吗? (因为窗口管理是操作系统工作)。到目前为止,我知道的唯一方法是实现您自己的标题栏并将其隐藏在操作系统中,然后您可以进行任何您喜欢的行为(但它会破坏样式)。
-
@ymoreau 我的客户希望第二个窗口基本上最小化到应用程序的主窗口而不是标题栏中。我们实际上已经实现了一个自定义标题栏,它可以做我们想要的,但是对于 OSX(仅)他们想要一个原生标题栏。我完全同意这是一种奇怪的行为,但这不是我的决定。
-
更正:而不是进入*系统托盘
-
您的需求听起来很像一个停靠小部件,而不是第二个操作系统窗口,但我在
QDockWidget中没有看到最小化功能,也没有 QML 项目可以做到这一点。跨度> -
@ymoreau 我将用 QDockWidget 做一个快速实验,看看它是否能满足我的需要。对于我从现在起八小时后的最后期限而言,这可能是一项过于激烈的手术。