Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- commit 4c1988604e40c057d149a8330964094b10f66524
- Author: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
- Date: Thu Aug 17 19:01:40 2017 +0200
- macOS: Deduplicate QNSWindow/QNSPanel code
- By sharing the implementations of the methods between QNSWindow and
- QNSPanel we don't need a helper, and can remove a lot of duplicated
- code.
- The only snag is that calls to super are hard-coded to a specific
- superclass during complication, so we provide our wrapper for
- objc_msgSendSuper that resolves the superclass at runtime.
- Change-Id: Iaf13f27675d90f884470f5005270ea0d9d0316f3
- diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h
- index 4478895538..97bd0c4b3f 100644
- --- a/src/plugins/platforms/cocoa/qcocoahelpers.h
- +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h
- @@ -55,6 +55,9 @@
- #include <QtGui/qpalette.h>
- #include <QtGui/qscreen.h>
- +#include <objc/runtime.h>
- +#include <objc/message.h>
- +
- Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSView));
- QT_BEGIN_NAMESPACE
- @@ -188,5 +191,103 @@ QT_END_NAMESPACE
- QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanelContentsWrapper);
- +// -------------------------------------------------------------------------
- +
- +template<typename... Args>
- +class DynamicSendSuper {
- +public:
- + DynamicSendSuper(id receiver, SEL sel, Args... args)
- + : m_receiver(receiver), m_selector(sel), m_args(std::make_tuple(args...))
- + {
- + }
- +
- + ~DynamicSendSuper()
- + {
- + if (m_selector)
- + msgSendSuper<id>(m_args);
- + }
- +
- + template <typename ReturnType>
- + 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
- + ReturnType ret = msgSendSuper<ReturnType>(m_args);
- + m_selector = nullptr;
- + return ret;
- + }
- +
- +private:
- + template <std::size_t... Ts>
- + struct index {};
- +
- + template <std::size_t N, std::size_t... Ts>
- + struct gen_seq : gen_seq<N - 1, N - 1, Ts...> {};
- +
- + template <std::size_t... Ts>
- + struct gen_seq<0, Ts...> : index<Ts...> {};
- +
- + template <typename T, size_t S>
- + using equal_or_below = typename std::enable_if<(sizeof(T) <= S), bool>::type;
- +
- + template <typename T, size_t S>
- + using larger_than = typename std::enable_if<(sizeof(T) > S), bool>::type;
- +
- + // Depending on the ABI of the platform, we may need to use objc_msgSendSuper_stret
- + static Q_CONSTEXPR size_t stretCutoff =
- +#if defined(Q_PROCESSOR_X86)
- + sizeof(void*) * 2; // Any return value larger than two registers on i386/x86_64
- +#elif defined(Q_PROCESSOR_ARM_32)
- + sizeof(void*); // Any return value larger than a single registers on arm
- +#elif defined(Q_PROCESSOR_ARM_64)
- + std::numeric_limits<std::size_t>::max(); // Stret never used on arm64
- +#endif
- +
- + template <typename ReturnType, equal_or_below<ReturnType, stretCutoff> = true, std::size_t... Is>
- + ReturnType msgSendSuper(std::tuple<Args...>& args, index<Is...>)
- + {
- + typedef ReturnType (*SuperFn)(objc_super *, SEL, Args...);
- + SuperFn superFn = reinterpret_cast<SuperFn>(objc_msgSendSuper);
- + objc_super sup = { m_receiver, class_getSuperclass(object_getClass(m_receiver)) };
- + return superFn(&sup, m_selector, std::get<Is>(args)...);
- + }
- +
- + template <typename ReturnType, larger_than<ReturnType, stretCutoff> = true, std::size_t... Is>
- + ReturnType msgSendSuper(std::tuple<Args...>& args, index<Is...>)
- + {
- + typedef void (*SuperStretFn)(ReturnType *, objc_super *, SEL, Args...);
- + SuperStretFn superStretFn = reinterpret_cast<SuperStretFn>(objc_msgSendSuper_stret);
- + objc_super sup = { m_receiver, class_getSuperclass(object_getClass(m_receiver)) };
- + ReturnType ret;
- + superStretFn(&ret, &sup, m_selector, std::get<Is>(args)...);
- + return ret;
- + }
- +
- + template <typename ReturnType>
- + ReturnType msgSendSuper(std::tuple<Args...>& args)
- + {
- + return msgSendSuper<ReturnType>(args, gen_seq<sizeof...(Args)>{});
- + }
- +
- + id m_receiver;
- + SEL m_selector;
- + std::tuple<Args...> m_args;
- +};
- +
- +template<typename... Args>
- +DynamicSendSuper<Args...> callSuper(id receiver, SEL selector, Args... args)
- +{
- + return DynamicSendSuper<Args...>(receiver, selector, args...);
- +}
- +
- +// Same as calling super, but the super_class field resolved at runtime instead of compile time
- +#define dynamicSuper(...) callSuper(self, _cmd, ##__VA_ARGS__)
- +
- #endif //QCOCOAHELPERS_H
- diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
- index 1df74c986a..7b1e689388 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<QNSWindowProtocol> proto = static_cast<id<QNSWindowProtocol> >(nsWindow);
- - QCocoaWindow *cocoaWindow = proto.helper.platformWindow;
- + QCocoaWindow *cocoaWindow = proto.platformWindow;
- if (!cocoaWindow)
- continue;
- window = cocoaWindow->window();
- diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
- index e8550887cb..caab104954 100644
- --- a/src/plugins/platforms/cocoa/qcocoawindow.mm
- +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
- @@ -210,7 +210,6 @@ QCocoaWindow::~QCocoaWindow()
- QMacAutoReleasePool pool;
- [m_nsWindow makeFirstResponder:nil];
- [m_nsWindow setContentView:nil];
- - [m_nsWindow.helper detachFromPlatformWindow];
- if ([m_view superview])
- [m_view removeFromSuperview];
- @@ -1300,10 +1299,34 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel)
- // Create NSWindow
- Class windowClass = shouldBePanel ? [QNSPanel class] : [QNSWindow class];
- QCocoaNSWindow *nsWindow = [[windowClass alloc] initWithContentRect:frame
- - screen:cocoaScreen->nativeScreen() styleMask:windowStyleMask(flags) qPlatformWindow:this];
- + 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()];
- +
- 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 b13b6d42a9..ac9cbb978f 100644
- --- a/src/plugins/platforms/cocoa/qnswindow.h
- +++ b/src/plugins/platforms/cocoa/qnswindow.h
- @@ -47,62 +47,26 @@
- #include <AppKit/AppKit.h>
- 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
- -
- -@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;
- +@optional
- +- (BOOL)canBecomeKeyWindow;
- +- (void)sendEvent:(NSEvent*)theEvent;
- - (void)closeAndRelease;
- -
- -@end
- -
- -typedef NSWindow<QNSWindowProtocol> QCocoaNSWindow;
- -
- -// -------------------------------------------------------------------------
- -
- -@interface QT_MANGLE_NAMESPACE(QNSWindowHelper) : NSObject
- -{
- - QPointer<QCocoaWindow> _platformWindow;
- -}
- -
- -@property (nonatomic, readonly) QCocoaNSWindow *window;
- +- (void)dealloc;
- @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<QNSWindowProtocol>
- -@end
- +typedef NSWindow<QNSWindowProtocol> QCocoaNSWindow;
- +@interface QT_MANGLE_NAMESPACE(QNSWindow) : NSWindow<QNSWindowProtocol> @end
- QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSWindow);
- -// -------------------------------------------------------------------------
- -
- -@interface QT_MANGLE_NAMESPACE(QNSPanel) : NSPanel<QNSWindowProtocol>
- -@end
- -
- +@interface QT_MANGLE_NAMESPACE(QNSPanel) : NSPanel<QNSWindowProtocol> @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 e44db3ff3b..e99155a5d7 100644
- --- a/src/plugins/platforms/cocoa/qnswindow.mm
- +++ b/src/plugins/platforms/cocoa/qnswindow.mm
- @@ -88,32 +88,70 @@ static bool isMouseEvent(NSEvent *ev)
- }
- @end
- -@implementation QNSWindowHelper
- +#define super USE_dynamicSuper_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);
- + }
- +}
- - (QCocoaWindow *)platformWindow
- {
- - return _platformWindow.data();
- + return qnsview_cast(self.contentView).platformWindow;
- }
- -- (id)initWithNSWindow:(QCocoaNSWindow *)window platformWindow:(QCocoaWindow *)platformWindow
- +- (BOOL)canBecomeKeyWindow
- {
- - if (self = [super init]) {
- - _window = window;
- - _platformWindow = platformWindow;
- + QCocoaWindow *pw = self.platformWindow;
- + if (!pw)
- + return NO;
- - _window.delegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:_platformWindow];
- + if (pw->shouldRefuseKeyWindowAndFirstResponder())
- + return NO;
- +
- + 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;
- - // 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 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;
- }
- +}
- +
- +- (BOOL)canBecomeMainWindow
- +{
- + BOOL canBecomeMain = YES; // By default, windows can become the main window
- - return self;
- + // 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)handleWindowEvent:(NSEvent *)theEvent
- +- (void)sendEvent:(NSEvent*)theEvent
- {
- // We might get events for a NSWindow after the corresponding platform
- // window has been deleted, as the NSWindow can outlive the QCocoaWindow
- @@ -129,7 +167,7 @@ static bool isMouseEvent(NSEvent *ev)
- return;
- }
- - [self.window superSendEvent:theEvent];
- + dynamicSuper(theEvent);
- if (!self.platformWindow)
- return; // Platform window went away while processing event
- @@ -137,108 +175,31 @@ static bool isMouseEvent(NSEvent *ev)
- QCocoaWindow *pw = self.platformWindow;
- if (pw->frameStrutEventsEnabled() && isMouseEvent(theEvent)) {
- NSPoint loc = [theEvent locationInWindow];
- - NSRect windowFrame = [self.window convertRectFromScreen:[self.window frame]];
- - NSRect contentFrame = [[self.window contentView] frame];
- + NSRect windowFrame = [self convertRectFromScreen:self.frame];
- + NSRect contentFrame = self.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.helper detachFromPlatformWindow];
- + [self.delegate release];
- + self.delegate = nil;
- +
- [self close];
- [self release];
- }
- +#pragma clang diagnostic push
- +#pragma clang diagnostic ignored "-Wobjc-missing-super-calls"
- - (void)dealloc
- {
- - [_helper release];
- - _helper = nil;
- - [super dealloc];
- + dynamicSuper();
- }
- -
- -@end
- -
- -@implementation QNSPanel
- -
- -@synthesize helper = _helper;
- +#pragma clang diagnostic pop
- + (void)applicationActivationChanged:(NSNotification*)notification
- {
- @@ -284,7 +245,7 @@ static const bool kNoDefer = NO;
- continue;
- if ([window conformsToProtocol:@protocol(QNSWindowProtocol)]) {
- - QCocoaWindow *cocoaWindow = static_cast<id<QNSWindowProtocol>>(window).helper.platformWindow;
- + QCocoaWindow *cocoaWindow = static_cast<QCocoaNSWindow *>(window).platformWindow;
- window.level = notification.name == NSApplicationWillResignActiveNotification ?
- NSNormalWindowLevel : cocoaWindow->windowLevel(cocoaWindow->window()->flags());
- }
- @@ -305,70 +266,10 @@ static const bool kNoDefer = NO;
- }
- }
- -- (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];
- -
- - 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;
- -}
- -
- -- (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
- +@implementation QNSPanel
- +// Implementation shared with QNSWindow, see +[QNSWindow load] above
- @end
- +
- +#undef super
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement