diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 1f4f9cd276f..4478895538f 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -55,9 +55,6 @@ #include #include -#include -#include - Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSView)); QT_BEGIN_NAMESPACE @@ -191,131 +188,5 @@ QT_END_NAMESPACE QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanelContentsWrapper); -// ------------------------------------------------------------------------- - -// Depending on the ABI of the platform, we may need to use objc_msgSendSuper_stret: -// - http://www.sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html -// - https://lists.apple.com/archives/cocoa-dev/2008/Feb/msg02338.html -template -struct objc_msgsend_requires_stret -{ static const bool value = -#if defined(Q_PROCESSOR_X86) - // Any return value larger than two registers on i386/x86_64 - sizeof(T) > sizeof(void*) * 2; -#elif defined(Q_PROCESSOR_ARM_32) - // Any return value larger than a single register on arm - sizeof(T) > sizeof(void*); -#elif defined(Q_PROCESSOR_ARM_64) - // Stret not used on arm64 - false; -#endif -}; - -template <> -struct objc_msgsend_requires_stret -{ static const bool value = false; }; - -template -ReturnType qt_msgSendSuper(id receiver, SEL selector, Args... args) -{ - static_assert(!objc_msgsend_requires_stret::value, - "The given return type requires stret on this platform"); - - typedef ReturnType (*SuperFn)(objc_super *, SEL, Args...); - SuperFn superFn = reinterpret_cast(objc_msgSendSuper); - objc_super sup = { receiver, class_getSuperclass(object_getClass(receiver)) }; - return superFn(&sup, selector, args...); -} - -template -ReturnType qt_msgSendSuper_stret(id receiver, SEL selector, Args... args) -{ - static_assert(objc_msgsend_requires_stret::value, - "The given return type does not use stret on this platform"); - - typedef void (*SuperStretFn)(ReturnType *, objc_super *, SEL, Args...); - SuperStretFn superStretFn = reinterpret_cast(objc_msgSendSuper_stret); - - objc_super sup = { receiver, class_getSuperclass(object_getClass(receiver)) }; - ReturnType ret; - superStretFn(&ret, &sup, selector, args...); - return ret; -} - -template -class QSendSuperHelper { -public: - QSendSuperHelper(id receiver, SEL sel, Args... args) - : m_receiver(receiver), m_selector(sel), m_args(std::make_tuple(args...)), m_sent(false) - { - } - - ~QSendSuperHelper() - { - if (!m_sent) - msgSendSuper(m_args); - } - - template - operator ReturnType() - { -#if defined(QT_DEBUG) - Method method = class_getInstanceMethod(object_getClass(m_receiver), m_selector); - char returnTypeEncoding[256]; - method_getReturnType(method, returnTypeEncoding, sizeof(returnTypeEncoding)); - NSUInteger alignedReturnTypeSize = 0; - NSGetSizeAndAlignment(returnTypeEncoding, nullptr, &alignedReturnTypeSize); - Q_ASSERT(alignedReturnTypeSize == sizeof(ReturnType)); -#endif - m_sent = true; - return msgSendSuper(m_args); - } - -private: - template - struct index {}; - - template - struct gen_seq : gen_seq {}; - - template - struct gen_seq<0, Ts...> : index {}; - - template - using if_requires_stret = typename std::enable_if::value == V, ReturnType>::type; - - template - if_requires_stret msgSendSuper(std::tuple& args, index) - { - return qt_msgSendSuper(m_receiver, m_selector, std::get(args)...); - } - - template - if_requires_stret msgSendSuper(std::tuple& args, index) - { - return qt_msgSendSuper_stret(m_receiver, m_selector, std::get(args)...); - } - - template - ReturnType msgSendSuper(std::tuple& args) - { - return msgSendSuper(args, gen_seq{}); - } - - id m_receiver; - SEL m_selector; - std::tuple m_args; - bool m_sent; -}; - -template -QSendSuperHelper qt_objcDynamicSuperHelper(id receiver, SEL selector, Args... args) -{ - return QSendSuperHelper(receiver, selector, args...); -} - -// Same as calling super, but the super_class field resolved at runtime instead of compile time -#define qt_objcDynamicSuper(...) qt_objcDynamicSuperHelper(self, _cmd, ##__VA_ARGS__) - #endif //QCOCOAHELPERS_H diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 7b1e6893884..1df74c986a0 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -218,7 +218,7 @@ QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const continue; id proto = static_cast >(nsWindow); - QCocoaWindow *cocoaWindow = proto.platformWindow; + QCocoaWindow *cocoaWindow = proto.helper.platformWindow; if (!cocoaWindow) continue; window = cocoaWindow->window(); diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index e906f0fd1c2..d3f26df6c55 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -210,6 +210,7 @@ QCocoaWindow::~QCocoaWindow() QMacAutoReleasePool pool; [m_nsWindow makeFirstResponder:nil]; [m_nsWindow setContentView:nil]; + [m_nsWindow.helper detachFromPlatformWindow]; if ([m_view superview]) [m_view removeFromSuperview]; @@ -1278,34 +1279,10 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel) // Create NSWindow Class windowClass = shouldBePanel ? [QNSPanel class] : [QNSWindow class]; QCocoaNSWindow *nsWindow = [[windowClass alloc] initWithContentRect:frame - styleMask:windowStyleMask(flags) - // Deferring window creation breaks OpenGL (the GL context is - // set up before the window is shown and needs a proper window) - backing:NSBackingStoreBuffered defer:NO - screen:cocoaScreen->nativeScreen()]; - + screen:cocoaScreen->nativeScreen() styleMask:windowStyleMask(flags) qPlatformWindow:this]; Q_ASSERT_X(nsWindow.screen == cocoaScreen->nativeScreen(), "QCocoaWindow", "Resulting NSScreen should match the requested NSScreen"); - nsWindow.delegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this]; - - // Prevent Cocoa from releasing the window on close. Qt - // handles the close event asynchronously and we want to - // make sure that NSWindow stays valid until the - // QCocoaWindow is deleted by Qt. - [nsWindow setReleasedWhenClosed:NO]; - - if (alwaysShowToolWindow()) { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center addObserver:[QNSWindow class] selector:@selector(applicationActivationChanged:) - name:NSApplicationWillResignActiveNotification object:nil]; - [center addObserver:[QNSWindow class] selector:@selector(applicationActivationChanged:) - name:NSApplicationWillBecomeActiveNotification object:nil]; - }); - } - if (targetScreen != window()->screen()) QWindowSystemInterface::handleWindowScreenChanged(window(), targetScreen); diff --git a/src/plugins/platforms/cocoa/qnswindow.h b/src/plugins/platforms/cocoa/qnswindow.h index ac9cbb978fe..b13b6d42a9e 100644 --- a/src/plugins/platforms/cocoa/qnswindow.h +++ b/src/plugins/platforms/cocoa/qnswindow.h @@ -47,26 +47,62 @@ #include QT_FORWARD_DECLARE_CLASS(QCocoaWindow) +Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSWindowHelper)); + +// ------------------------------------------------------------------------- @interface NSWindow (FullScreenProperty) @property(readonly) BOOL qt_fullScreen; @end +// ------------------------------------------------------------------------- + @protocol QNSWindowProtocol -@optional -- (BOOL)canBecomeKeyWindow; -- (void)sendEvent:(NSEvent*)theEvent; + +@property (nonatomic, readonly) QT_MANGLE_NAMESPACE(QNSWindowHelper) *helper; + +- (id)initWithContentRect:(NSRect)contentRect screen:(NSScreen*)screen + styleMask:(NSUInteger)windowStyle qPlatformWindow:(QCocoaWindow *)qpw; + +- (void)superSendEvent:(NSEvent *)theEvent; - (void)closeAndRelease; -- (void)dealloc; -@property (nonatomic, readonly) QCocoaWindow *platformWindow; + @end typedef NSWindow QCocoaNSWindow; -@interface QT_MANGLE_NAMESPACE(QNSWindow) : NSWindow @end +// ------------------------------------------------------------------------- + +@interface QT_MANGLE_NAMESPACE(QNSWindowHelper) : NSObject +{ + QPointer _platformWindow; +} + +@property (nonatomic, readonly) QCocoaNSWindow *window; +@property (nonatomic, readonly) QCocoaWindow *platformWindow; + +- (id)initWithNSWindow:(QCocoaNSWindow *)window platformWindow:(QCocoaWindow *)platformWindow; +- (void)handleWindowEvent:(NSEvent *)theEvent; +- (void)detachFromPlatformWindow; + +@end + +QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSWindowHelper); + +// ------------------------------------------------------------------------- + +@interface QT_MANGLE_NAMESPACE(QNSWindow) : NSWindow +@end + QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSWindow); -@interface QT_MANGLE_NAMESPACE(QNSPanel) : NSPanel @end +// ------------------------------------------------------------------------- + +@interface QT_MANGLE_NAMESPACE(QNSPanel) : NSPanel +@end + QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanel); +// ------------------------------------------------------------------------- + #endif // QNSWINDOW_H diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm index e5ddd3ca0fa..e44db3ff3b1 100644 --- a/src/plugins/platforms/cocoa/qnswindow.mm +++ b/src/plugins/platforms/cocoa/qnswindow.mm @@ -88,72 +88,32 @@ static bool isMouseEvent(NSEvent *ev) } @end -#define super USE_qt_objcDynamicSuper_INSTEAD - -@implementation QNSWindow - -+ (void)load -{ - const Class windowClass = [self class]; - const Class panelClass = [QNSPanel class]; - - unsigned int methodDescriptionsCount; - objc_method_description *methods = protocol_copyMethodDescriptionList( - objc_getProtocol("QNSWindowProtocol"), NO, YES, &methodDescriptionsCount); - - for (unsigned int i = 0; i < methodDescriptionsCount; ++i) { - objc_method_description method = methods[i]; - class_addMethod(panelClass, method.name, - class_getMethodImplementation(windowClass, method.name), - method.types); - } - - free(methods); -} +@implementation QNSWindowHelper - (QCocoaWindow *)platformWindow { - return qnsview_cast(self.contentView).platformWindow; + return _platformWindow.data(); } -- (BOOL)canBecomeKeyWindow +- (id)initWithNSWindow:(QCocoaNSWindow *)window platformWindow:(QCocoaWindow *)platformWindow { - QCocoaWindow *pw = self.platformWindow; - if (!pw) - return NO; + if (self = [super init]) { + _window = window; + _platformWindow = platformWindow; - if (pw->shouldRefuseKeyWindowAndFirstResponder()) - return NO; + _window.delegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:_platformWindow]; - if ([self isKindOfClass:[QNSPanel class]]) { - // Only tool or dialog windows should become key: - Qt::WindowType type = pw->window()->type(); - if (type == Qt::Tool || type == Qt::Dialog) - return YES; - - return NO; - } else { - // The default implementation returns NO for title-bar less windows, - // override and return yes here to make sure popup windows such as - // the combobox popup can become the key window. - return YES; + // Prevent Cocoa from releasing the window on close. Qt + // handles the close event asynchronously and we want to + // make sure that NSWindow stays valid until the + // QCocoaWindow is deleted by Qt. + [_window setReleasedWhenClosed:NO]; } + + return self; } -- (BOOL)canBecomeMainWindow -{ - BOOL canBecomeMain = YES; // By default, windows can become the main window - - // Windows with a transient parent (such as combobox popup windows) - // cannot become the main window: - QCocoaWindow *pw = self.platformWindow; - if (!pw || pw->window()->transientParent()) - canBecomeMain = NO; - - return canBecomeMain; -} - -- (void)sendEvent:(NSEvent*)theEvent +- (void)handleWindowEvent:(NSEvent *)theEvent { // We might get events for a NSWindow after the corresponding platform // window has been deleted, as the NSWindow can outlive the QCocoaWindow @@ -169,7 +129,7 @@ static bool isMouseEvent(NSEvent *ev) return; } - qt_objcDynamicSuper(theEvent); + [self.window superSendEvent:theEvent]; if (!self.platformWindow) return; // Platform window went away while processing event @@ -177,31 +137,108 @@ static bool isMouseEvent(NSEvent *ev) QCocoaWindow *pw = self.platformWindow; if (pw->frameStrutEventsEnabled() && isMouseEvent(theEvent)) { NSPoint loc = [theEvent locationInWindow]; - NSRect windowFrame = [self convertRectFromScreen:self.frame]; - NSRect contentFrame = self.contentView.frame; + NSRect windowFrame = [self.window convertRectFromScreen:[self.window frame]]; + NSRect contentFrame = [[self.window contentView] frame]; if (NSMouseInRect(loc, windowFrame, NO) && !NSMouseInRect(loc, contentFrame, NO)) [qnsview_cast(pw->view()) handleFrameStrutMouseEvent:theEvent]; } } +- (void)detachFromPlatformWindow +{ + _platformWindow.clear(); + [self.window.delegate release]; + self.window.delegate = nil; +} + +- (void)dealloc +{ + _window = nil; + _platformWindow.clear(); + [super dealloc]; +} + +@end + +// Deferring window creation breaks OpenGL (the GL context is +// set up before the window is shown and needs a proper window) +static const bool kNoDefer = NO; + +@implementation QNSWindow + +@synthesize helper = _helper; + +- (id)initWithContentRect:(NSRect)contentRect + screen:(NSScreen*)screen + styleMask:(NSUInteger)windowStyle + qPlatformWindow:(QCocoaWindow *)qpw +{ + if (self = [super initWithContentRect:contentRect styleMask:windowStyle + backing:NSBackingStoreBuffered defer:kNoDefer screen:screen]) { + _helper = [[QNSWindowHelper alloc] initWithNSWindow:self platformWindow:qpw]; + } + return self; +} + +- (BOOL)canBecomeKeyWindow +{ + QCocoaWindow *pw = self.helper.platformWindow; + if (!pw) + return NO; + + if (pw->shouldRefuseKeyWindowAndFirstResponder()) + return NO; + + // The default implementation returns NO for title-bar less windows, + // override and return yes here to make sure popup windows such as + // the combobox popup can become the key window. + return YES; +} + +- (BOOL)canBecomeMainWindow +{ + BOOL canBecomeMain = YES; // By default, windows can become the main window + + // Windows with a transient parent (such as combobox popup windows) + // cannot become the main window: + QCocoaWindow *pw = self.helper.platformWindow; + if (!pw || pw->window()->transientParent()) + canBecomeMain = NO; + + return canBecomeMain; +} + +- (void)sendEvent:(NSEvent*)theEvent +{ + [self.helper handleWindowEvent:theEvent]; +} + +- (void)superSendEvent:(NSEvent *)theEvent +{ + [super sendEvent:theEvent]; +} + - (void)closeAndRelease { qCDebug(lcQpaCocoaWindow) << "closeAndRelease" << self; - [self.delegate release]; - self.delegate = nil; - + [self.helper detachFromPlatformWindow]; [self close]; [self release]; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wobjc-missing-super-calls" - (void)dealloc { - qt_objcDynamicSuper(); + [_helper release]; + _helper = nil; + [super dealloc]; } -#pragma clang diagnostic pop + +@end + +@implementation QNSPanel + +@synthesize helper = _helper; + (void)applicationActivationChanged:(NSNotification*)notification { @@ -247,7 +284,7 @@ static bool isMouseEvent(NSEvent *ev) continue; if ([window conformsToProtocol:@protocol(QNSWindowProtocol)]) { - QCocoaWindow *cocoaWindow = static_cast(window).platformWindow; + QCocoaWindow *cocoaWindow = static_cast>(window).helper.platformWindow; window.level = notification.name == NSApplicationWillResignActiveNotification ? NSNormalWindowLevel : cocoaWindow->windowLevel(cocoaWindow->window()->flags()); } @@ -268,10 +305,70 @@ static bool isMouseEvent(NSEvent *ev) } } -@end +- (id)initWithContentRect:(NSRect)contentRect + screen:(NSScreen*)screen + styleMask:(NSUInteger)windowStyle + qPlatformWindow:(QCocoaWindow *)qpw +{ + if (self = [super initWithContentRect:contentRect styleMask:windowStyle + backing:NSBackingStoreBuffered defer:kNoDefer screen:screen]) { + _helper = [[QNSWindowHelper alloc] initWithNSWindow:self platformWindow:qpw]; -@implementation QNSPanel -// Implementation shared with QNSWindow, see +[QNSWindow load] above -@end + if (qpw->alwaysShowToolWindow()) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center addObserver:[self class] selector:@selector(applicationActivationChanged:) + name:NSApplicationWillResignActiveNotification object:nil]; + [center addObserver:[self class] selector:@selector(applicationActivationChanged:) + name:NSApplicationWillBecomeActiveNotification object:nil]; + }); + } + } + return self; +} -#undef super +- (BOOL)canBecomeKeyWindow +{ + QCocoaWindow *pw = self.helper.platformWindow; + if (!pw) + return NO; + + if (pw->shouldRefuseKeyWindowAndFirstResponder()) + return NO; + + // Only tool or dialog windows should become key: + Qt::WindowType type = pw->window()->type(); + if (type == Qt::Tool || type == Qt::Dialog) + return YES; + + return NO; +} + +- (void)sendEvent:(NSEvent*)theEvent +{ + [self.helper handleWindowEvent:theEvent]; +} + +- (void)superSendEvent:(NSEvent *)theEvent +{ + [super sendEvent:theEvent]; +} + +- (void)closeAndRelease +{ + qCDebug(lcQpaCocoaWindow) << "closeAndRelease" << self; + + [self.helper detachFromPlatformWindow]; + [self close]; + [self release]; +} + +- (void)dealloc +{ + [_helper release]; + _helper = nil; + [super dealloc]; +} + +@end