I have a Window
subclass in my project, and at runtime the instance is created and shown entirely on the QML side. I know that I can prevent the window from being minimized by not including the WindowMinimizeButtonHint
in the flags:
, but I actually need to have the minimize button present and enabled but be able to intercept the minimize button click, cancel the actual minimizing, and do something else (FYI my client is requiring this non-standard windowing behavior, not me).
So far, the only thing I've been able to achieve is to handle the onWindowStateChanged:
event, check if windowState === Qt.WindowStateMinimized
and call show()
from a timer (calling it inside the event handler directly does nothing). This results in the window moving down to the system tray and then suddenly coming back up to normal.
Is there any way to do this, something like an OnMinimized
event that can be cancelled?
Edit: based on Benjamin T's answer, I'm at least part way to a solution for 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;
}
In this example I'm able to intercept and cancel all NSKeyDown events (while leaving other events like mouse clicks etc. still working). The remaining problem is that I still don't know to intercept a minimize event - NSEvent.h doesn't seem to have anything that covers that. Perhaps I need to cast to a different type of event?
Edit 2 - working solution:
I was not able to find any way to intercept the minimize event proper and cancel it, so my workaround is to instead intercept the click on the window, determine if the click is over the minimize button (or the close or zoom buttons) and cancel the event if so (and send a notification to my qml window that the click occurred). I also handle the case of double-clicking the titlebar to zoom the window, and using the Command-M keys to minimize the window.
First step is to implement a QAbstractNativeEventFilter
. In your header:
#include <QAbstractNativeEventFilter>
class NativeFilter : public QAbstractNativeEventFilter {
public:
bool nativeEventFilter(const QByteArray &eventType, void *message,
long *result);
};
The implementation:
#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;
}
Then in main.cpp:
Application app(argc, argv);
app.installNativeEventFilter(new NativeFilter());