From 7319c342c34f6bcc8f0322e9613c6296331f1487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 13 Feb 2019 12:52:33 +0100 Subject: macOS: Implement QNSWindow/QNSPanel mixin using preprocessor includes We want to share the implementation between the two classes, but Objective-C doesn't natively have a mixin-feature. Instead of using dynamic super-calls at runtime (which worked fine, but added complexity), we now do the mixin at compile time using the preprocessor. The dynamic-super feature is left in, in case we need it in other areas in the future. Change-Id: I95dfa7f18cba86cc518e963dd018944ef113ac06 Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qnswindow.h | 8 -- src/plugins/platforms/cocoa/qnswindow.mm | 170 ++++++++++++++----------------- 2 files changed, 75 insertions(+), 103 deletions(-) (limited to 'src/plugins/platforms/cocoa') diff --git a/src/plugins/platforms/cocoa/qnswindow.h b/src/plugins/platforms/cocoa/qnswindow.h index dcbcd58901..2827e41049 100644 --- a/src/plugins/platforms/cocoa/qnswindow.h +++ b/src/plugins/platforms/cocoa/qnswindow.h @@ -60,15 +60,7 @@ QT_FORWARD_DECLARE_CLASS(QCocoaWindow) #define QNSWindowProtocol QT_MANGLE_NAMESPACE(QNSWindowProtocol) @protocol QNSWindowProtocol -@optional -- (BOOL)canBecomeKeyWindow; -- (BOOL)worksWhenModal; -- (void)sendEvent:(NSEvent*)theEvent; - (void)closeAndRelease; -- (void)dealloc; -- (BOOL)isOpaque; -- (NSColor *)backgroundColor; -- (NSString *)description; @property (nonatomic, readonly) QCocoaWindow *platformWindow; @end diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm index 28a9fa8607..3a1a04f887 100644 --- a/src/plugins/platforms/cocoa/qnswindow.mm +++ b/src/plugins/platforms/cocoa/qnswindow.mm @@ -37,6 +37,8 @@ ** ****************************************************************************/ +#if !defined(QNSWINDOW_PROTOCOL_IMPLMENTATION) + #include "qnswindow.h" #include "qcocoawindow.h" #include "qcocoahelpers.h" @@ -89,36 +91,79 @@ static bool isMouseEvent(NSEvent *ev) } @end -#define super USE_qt_objcDynamicSuper_INSTEAD - @implementation QNSWindow +#define QNSWINDOW_PROTOCOL_IMPLMENTATION 1 +#include "qnswindow.mm" +#undef QNSWINDOW_PROTOCOL_IMPLMENTATION -+ (void)load ++ (void)applicationActivationChanged:(NSNotification*)notification { - const Class windowClass = [self class]; - const Class panelClass = [QNSPanel class]; - - unsigned int protocolCount; - Protocol **protocols = class_copyProtocolList(windowClass, &protocolCount); - for (unsigned int i = 0; i < protocolCount; ++i) { - Protocol *protocol = protocols[i]; - - unsigned int methodDescriptionsCount; - objc_method_description *methods = protocol_copyMethodDescriptionList( - protocol, NO, YES, &methodDescriptionsCount); - - for (unsigned int j = 0; j < methodDescriptionsCount; ++j) { - objc_method_description method = methods[j]; - class_addMethod(panelClass, method.name, - class_getMethodImplementation(windowClass, method.name), - method.types); + const id sender = self; + NSEnumerator *windowEnumerator = nullptr; + NSApplication *application = [NSApplication sharedApplication]; + + // Unfortunately there's no NSWindowListOrderedBackToFront, + // so we have to manually reverse the order using an array. + NSMutableArray *windows = [NSMutableArray new]; + [application enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack + usingBlock:^(NSWindow *window, BOOL *) { + // For some reason AppKit will give us nil-windows, skip those + if (!window) + return; + + [windows addObject:window]; + } + ]; + + windowEnumerator = windows.reverseObjectEnumerator; + + for (NSWindow *window in windowEnumerator) { + // We're meddling with normal and floating windows, so leave others alone + if (!(window.level == NSNormalWindowLevel || window.level == NSFloatingWindowLevel)) + continue; + + // Windows that hide automatically will keep their NSFloatingWindowLevel, + // and hence be on top of the window stack. We don't want to affect these + // windows, as otherwise we might end up with key windows being ordered + // behind these auto-hidden windows when activating the application by + // clicking on a new tool window. + if (window.hidesOnDeactivate) + continue; + + if ([window conformsToProtocol:@protocol(QNSWindowProtocol)]) { + QCocoaWindow *cocoaWindow = static_cast(window).platformWindow; + window.level = notification.name == NSApplicationWillResignActiveNotification ? + NSNormalWindowLevel : cocoaWindow->windowLevel(cocoaWindow->window()->flags()); } - free(methods); - } - free(protocols); + // The documentation says that "when a window enters a new level, it’s ordered + // in front of all its peers in that level", but that doesn't seem to be the + // case in practice. To keep the order correct after meddling with the window + // levels, we explicitly order each window to the front. Since we are iterating + // the windows in back-to-front order, this is okey. The call also triggers AppKit + // to re-evaluate the level in relation to windows from other applications, + // working around an issue where our tool windows would stay on top of other + // application windows if activation was transferred to another application by + // clicking on it instead of via the application switcher or Dock. Finally, we + // do this re-ordering for all windows (except auto-hiding ones), otherwise we would + // end up triggering a bug in AppKit where the tool windows would disappear behind + // the application window. + [window orderFront:sender]; + } } +@end + +@implementation QNSPanel +#define QNSWINDOW_PROTOCOL_IMPLMENTATION 1 +#include "qnswindow.mm" +#undef QNSWINDOW_PROTOCOL_IMPLMENTATION +@end + +#else // QNSWINDOW_PROTOCOL_IMPLMENTATION + +// The following methods are mixed in to the QNSWindow and QNSPanel classes via includes + - (QCocoaWindow *)platformWindow { return qnsview_cast(self.contentView).platformWindow; @@ -126,7 +171,7 @@ static bool isMouseEvent(NSEvent *ev) - (NSString *)description { - NSMutableString *description = [NSMutableString stringWithString:qt_objcDynamicSuper()]; + NSMutableString *description = [NSMutableString stringWithString:[super description]]; #ifndef QT_NO_DEBUG_STREAM QString contentViewDescription; @@ -187,13 +232,13 @@ static bool isMouseEvent(NSEvent *ev) } } - return qt_objcDynamicSuper(); + return [super worksWhenModal]; } - (BOOL)isOpaque { return self.platformWindow ? - self.platformWindow->isOpaque() : qt_objcDynamicSuper(); + self.platformWindow->isOpaque() : [super isOpaque]; } /*! @@ -209,7 +254,7 @@ static bool isMouseEvent(NSEvent *ev) - (NSColor *)backgroundColor { return self.styleMask == NSWindowStyleMaskBorderless - ? [NSColor clearColor] : qt_objcDynamicSuper(); + ? [NSColor clearColor] : [super backgroundColor]; } - (void)sendEvent:(NSEvent*)theEvent @@ -234,7 +279,7 @@ static bool isMouseEvent(NSEvent *ev) return; } - qt_objcDynamicSuper(theEvent); + [super sendEvent:theEvent]; if (!self.platformWindow) return; // Platform window went away while processing event @@ -256,77 +301,12 @@ static bool isMouseEvent(NSEvent *ev) [self release]; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wobjc-missing-super-calls" - (void)dealloc { qCDebug(lcQpaWindow) << "Deallocating" << self; self.delegate = nil; - qt_objcDynamicSuper(); + [super dealloc]; } -#pragma clang diagnostic pop -+ (void)applicationActivationChanged:(NSNotification*)notification -{ - const id sender = self; - NSEnumerator *windowEnumerator = nullptr; - NSApplication *application = [NSApplication sharedApplication]; - - // Unfortunately there's no NSWindowListOrderedBackToFront, - // so we have to manually reverse the order using an array. - NSMutableArray *windows = [NSMutableArray new]; - [application enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack - usingBlock:^(NSWindow *window, BOOL *) { - // For some reason AppKit will give us nil-windows, skip those - if (!window) - return; - - [windows addObject:window]; - } - ]; - - windowEnumerator = windows.reverseObjectEnumerator; - - for (NSWindow *window in windowEnumerator) { - // We're meddling with normal and floating windows, so leave others alone - if (!(window.level == NSNormalWindowLevel || window.level == NSFloatingWindowLevel)) - continue; - - // Windows that hide automatically will keep their NSFloatingWindowLevel, - // and hence be on top of the window stack. We don't want to affect these - // windows, as otherwise we might end up with key windows being ordered - // behind these auto-hidden windows when activating the application by - // clicking on a new tool window. - if (window.hidesOnDeactivate) - continue; - - if ([window conformsToProtocol:@protocol(QNSWindowProtocol)]) { - QCocoaWindow *cocoaWindow = static_cast(window).platformWindow; - window.level = notification.name == NSApplicationWillResignActiveNotification ? - NSNormalWindowLevel : cocoaWindow->windowLevel(cocoaWindow->window()->flags()); - } - - // The documentation says that "when a window enters a new level, it’s ordered - // in front of all its peers in that level", but that doesn't seem to be the - // case in practice. To keep the order correct after meddling with the window - // levels, we explicitly order each window to the front. Since we are iterating - // the windows in back-to-front order, this is okey. The call also triggers AppKit - // to re-evaluate the level in relation to windows from other applications, - // working around an issue where our tool windows would stay on top of other - // application windows if activation was transferred to another application by - // clicking on it instead of via the application switcher or Dock. Finally, we - // do this re-ordering for all windows (except auto-hiding ones), otherwise we would - // end up triggering a bug in AppKit where the tool windows would disappear behind - // the application window. - [window orderFront:sender]; - } -} - -@end - -@implementation QNSPanel -// Implementation shared with QNSWindow, see +[QNSWindow load] above -@end - -#undef super +#endif -- cgit v1.2.3