summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa/qnswindow.mm
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2017-08-17 19:01:40 +0200
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2017-08-24 23:54:43 +0000
commita980250a666bc87e5db006b8668c6af9340915f2 (patch)
treed9c3f6eca8a9941cf734dca4deea07f272515835 /src/plugins/platforms/cocoa/qnswindow.mm
parentf537dc0da288949c4df903c1f2b21156e62fbae5 (diff)
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 duplicated code. This duplication would expand in the future, as for each method added to the QNSWindowProtocol, we would have to add forwarding functions in both QNSWindow and QNSPanel, forwarding to QNSWindowHelper, and then two more functions in QNSWindow and QNSPanel in case we wanted to call super from the helper, similar to [QNSWindow superSendEvent]. 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. The helper class QSendSuperHelper provides compile time implicit instantiation of the right template without having to provide the return type as a template argument, via operator T and a fallback for the case of no return type via the destructor. Change-Id: Iaf13f27675d90f884470f5005270ea0d9d0316f3 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/plugins/platforms/cocoa/qnswindow.mm')
-rw-r--r--src/plugins/platforms/cocoa/qnswindow.mm237
1 files changed, 70 insertions, 167 deletions
diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm
index e44db3ff3b..e5ddd3ca0f 100644
--- a/src/plugins/platforms/cocoa/qnswindow.mm
+++ b/src/plugins/platforms/cocoa/qnswindow.mm
@@ -88,32 +88,72 @@ static bool isMouseEvent(NSEvent *ev)
}
@end
-@implementation QNSWindowHelper
+#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);
+}
- (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;
+
+ 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;
- // 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 +169,7 @@ static bool isMouseEvent(NSEvent *ev)
return;
}
- [self.window superSendEvent:theEvent];
+ qt_objcDynamicSuper(theEvent);
if (!self.platformWindow)
return; // Platform window went away while processing event
@@ -137,108 +177,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];
+ qt_objcDynamicSuper();
}
-
-@end
-
-@implementation QNSPanel
-
-@synthesize helper = _helper;
+#pragma clang diagnostic pop
+ (void)applicationActivationChanged:(NSNotification*)notification
{
@@ -284,7 +247,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 +268,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