summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGabriel de Dietrich <gabriel.dedietrich@digia.com>2014-02-25 20:33:39 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-03-04 11:29:30 +0100
commit018d1ca5f3209949cb7e8e1306250ec030fee929 (patch)
tree4bdd42f21ecc24cc4b27d4da53889e2ec44f4160 /src
parentce909a138ab486c1ec5cf4b13232ef8d73ab5816 (diff)
Cocoa: Use helper class for event handling in native windows
QNSWindow and QNSPanel duplicate some code when it comes to event handling, which can be refactored. Also, it's currently not possible to keep an NSWindow derived instance temporarily alive as QCocoaWindow is not designed to keep track of more than one NSWindow. Finally, we can reduce the size of (and eventually remove) the QCocoaWindowCategory which polutes the NSWindow namespace. We move QNSWindow and QNSPanel specific API into QNSWindowProtocol, and define QCocoaNSWindow as NSWindow extended by that protocol. This gives us a type we can refer to any of the native windows QCocoaWindow instanciates. We introduce QNSWindowHelper which gathers the common code between QNSWindow and QNSPanel. This is a one-to-one mapping that keeps a weak (non-retaining) reference to the NSWindow and a weak reference to the QCocoaWindow. It has the same life span as its associated NSWindow. Task-number: QTBUG-33082 Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com> Change-Id: I38d001bf13f64a1ba4f1439291c5103c3f755183 Reviewed-by: Liang Qi <liang.qi@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.h50
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm225
2 files changed, 169 insertions, 106 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h
index 748280af6a..0f08cd18fb 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.h
+++ b/src/plugins/platforms/cocoa/qcocoawindow.h
@@ -52,27 +52,57 @@
QT_FORWARD_DECLARE_CLASS(QCocoaWindow)
-@interface QNSWindow : NSWindow
+@class QNSWindowHelper;
+
+@protocol QNSWindowProtocol
+
+@property (nonatomic, readonly) QNSWindowHelper *helper;
+
+- (void)superSendEvent:(NSEvent *)theEvent;
+- (void)closeAndRelease;
+
+@end
+
+typedef NSWindow<QNSWindowProtocol> QCocoaNSWindow;
+
+@interface QNSWindowHelper : NSObject
{
- @public QCocoaWindow *m_cocoaPlatformWindow;
+ QCocoaNSWindow *_window;
+ QCocoaWindow *_platformWindow;
}
+
+@property (nonatomic, readonly) QCocoaNSWindow *window;
+@property (nonatomic, readonly) QCocoaWindow *platformWindow;
+
+- (id)initWithNSWindow:(QCocoaNSWindow *)window platformWindow:(QCocoaWindow *)platformWindow;
+- (void)handleWindowEvent:(NSEvent *)theEvent;
+
+@end
+
+@interface QNSWindow : NSWindow<QNSWindowProtocol>
+{
+ QNSWindowHelper *_helper;
+}
+
+@property (nonatomic, readonly) QNSWindowHelper *helper;
+
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)windowStyle
qPlatformWindow:(QCocoaWindow *)qpw;
-- (void)clearPlatformWindow;
@end
-@interface QNSPanel : NSPanel
+@interface QNSPanel : NSPanel<QNSWindowProtocol>
{
- @public QCocoaWindow *m_cocoaPlatformWindow;
+ QNSWindowHelper *_helper;
}
+@property (nonatomic, readonly) QNSWindowHelper *helper;
+
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)windowStyle
qPlatformWindow:(QCocoaWindow *)qpw;
-- (void)clearPlatformWindow;
@end
@class QNSWindowDelegate;
@@ -183,9 +213,8 @@ public:
QWindow *childWindowAt(QPoint windowPoint);
protected:
void recreateWindow(const QPlatformWindow *parentWindow);
- NSWindow *createNSWindow();
- void setNSWindow(NSWindow *window);
- void clearNSWindow(NSWindow *window);
+ QCocoaNSWindow *createNSWindow();
+ void setNSWindow(QCocoaNSWindow *window);
bool shouldUseNSPanel();
@@ -202,7 +231,7 @@ public: // for QNSView
NSView *m_contentView;
QNSView *m_qtView;
- NSWindow *m_nsWindow;
+ QCocoaNSWindow *m_nsWindow;
QCocoaWindow *m_forwardWindow;
// TODO merge to one variable if possible
@@ -213,7 +242,6 @@ public: // for QNSView
bool m_isNSWindowChild; // this window is a non-top level QWindow with a NSWindow.
QList<QCocoaWindow *> m_childWindows;
- QNSWindowDelegate *m_nsWindowDelegate;
Qt::WindowFlags m_windowFlags;
Qt::WindowState m_synchedWindowState;
Qt::WindowModality m_windowModality;
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index bf41270d12..65e2a15cec 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -81,15 +81,10 @@ static bool isMouseEvent(NSEvent *ev)
}
@interface NSWindow (CocoaWindowCategory)
-- (void) clearPlatformWindow;
- (NSRect) legacyConvertRectFromScreen:(NSRect) rect;
@end
@implementation NSWindow (CocoaWindowCategory)
-- (void) clearPlatformWindow
-{
-}
-
- (NSRect) legacyConvertRectFromScreen:(NSRect) rect
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
@@ -103,8 +98,87 @@ static bool isMouseEvent(NSEvent *ev)
}
@end
+
+@implementation QNSWindowHelper
+
+@synthesize window = _window;
+@synthesize platformWindow = _platformWindow;
+
+- (id)initWithNSWindow:(QCocoaNSWindow *)window platformWindow:(QCocoaWindow *)platformWindow
+{
+ self = [super init];
+ if (self) {
+ _window = window;
+ _platformWindow = platformWindow;
+
+ _window.delegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:_platformWindow];
+
+ // Prevent Cocoa from releasing the window on close. Qt
+ // handles the close event asynchronously and we want to
+ // make sure that m_nsWindow stays valid until the
+ // QCocoaWindow is deleted by Qt.
+ [_window setReleasedWhenClosed:NO];
+ }
+
+ return self;
+}
+
+- (void)handleWindowEvent:(NSEvent *)theEvent
+{
+ QCocoaWindow *pw = self.platformWindow;
+ if (pw && pw->m_forwardWindow) {
+ if (theEvent.type == NSLeftMouseUp || theEvent.type == NSLeftMouseDragged) {
+ QNSView *forwardView = pw->m_qtView;
+ if (theEvent.type == NSLeftMouseUp) {
+ [forwardView mouseUp:theEvent];
+ pw->m_forwardWindow = 0;
+ } else {
+ [forwardView mouseDragged:theEvent];
+ }
+ }
+
+ if (!pw->m_isNSWindowChild && theEvent.type == NSLeftMouseDown) {
+ pw->m_forwardWindow = 0;
+ }
+ }
+
+ [self.window superSendEvent:theEvent];
+
+ if (!self.window.delegate)
+ return; // Already detached, pending NSAppKitDefined event
+
+ if (pw && pw->frameStrutEventsEnabled() && isMouseEvent(theEvent)) {
+ NSPoint loc = [theEvent locationInWindow];
+ NSRect windowFrame = [self.window legacyConvertRectFromScreen:[self.window frame]];
+ NSRect contentFrame = [[self.window contentView] frame];
+ if (NSMouseInRect(loc, windowFrame, NO) &&
+ !NSMouseInRect(loc, contentFrame, NO))
+ {
+ QNSView *contentView = (QNSView *)pw->contentView();
+ [contentView handleFrameStrutMouseEvent: theEvent];
+ }
+ }
+}
+
+- (void)detachFromPlatformWindow
+{
+ [self.window.delegate release];
+ self.window.delegate = nil;
+}
+
+- (void)dealloc
+{
+ _window = nil;
+ _platformWindow = 0;
+ [super dealloc];
+}
+
+@end
+
@implementation QNSWindow
+@synthesize helper = _helper;
+
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)windowStyle
qPlatformWindow:(QCocoaWindow *)qpw
@@ -116,7 +190,7 @@ static bool isMouseEvent(NSEvent *ev)
// set up before the window is shown and needs a proper window)
if (self) {
- m_cocoaPlatformWindow = qpw;
+ _helper = [[QNSWindowHelper alloc] initWithNSWindow:self platformWindow:qpw];
}
return self;
}
@@ -125,7 +199,8 @@ static bool isMouseEvent(NSEvent *ev)
{
// Prevent child NSWindows from becoming the key window in
// order keep the active apperance of the top-level window.
- if (!m_cocoaPlatformWindow || m_cocoaPlatformWindow->m_isNSWindowChild)
+ QCocoaWindow *pw = self.helper.platformWindow;
+ if (!pw || pw->m_isNSWindowChild)
return NO;
// The default implementation returns NO for title-bar less windows,
@@ -140,8 +215,8 @@ static bool isMouseEvent(NSEvent *ev)
// Windows with a transient parent (such as combobox popup windows)
// cannot become the main window:
- if (!m_cocoaPlatformWindow || m_cocoaPlatformWindow->m_isNSWindowChild
- || m_cocoaPlatformWindow->window()->transientParent())
+ QCocoaWindow *pw = self.helper.platformWindow;
+ if (!pw || pw->m_isNSWindowChild || pw->window()->transientParent())
canBecomeMain = NO;
return canBecomeMain;
@@ -149,51 +224,34 @@ static bool isMouseEvent(NSEvent *ev)
- (void) sendEvent: (NSEvent*) theEvent
{
- if (m_cocoaPlatformWindow && m_cocoaPlatformWindow->m_forwardWindow) {
- if (theEvent.type == NSLeftMouseUp || theEvent.type == NSLeftMouseDragged) {
- QNSView *forwardView = m_cocoaPlatformWindow->m_qtView;
- if (theEvent.type == NSLeftMouseUp) {
- [forwardView mouseUp:theEvent];
- m_cocoaPlatformWindow->m_forwardWindow = 0;
- } else {
- [forwardView mouseDragged:theEvent];
- }
-
- return;
- }
-
- if (theEvent.type == NSLeftMouseDown) {
- m_cocoaPlatformWindow->m_forwardWindow = 0;
- }
- }
-
- [super sendEvent: theEvent];
+ [self.helper handleWindowEvent:theEvent];
+}
- if (!m_cocoaPlatformWindow)
- return;
+- (void)superSendEvent:(NSEvent *)theEvent
+{
+ [super sendEvent:theEvent];
+}
- if (m_cocoaPlatformWindow->frameStrutEventsEnabled() && isMouseEvent(theEvent)) {
- NSPoint loc = [theEvent locationInWindow];
- NSRect windowFrame = [self legacyConvertRectFromScreen:[self frame]];
- NSRect contentFrame = [[self contentView] frame];
- if (NSMouseInRect(loc, windowFrame, NO) &&
- !NSMouseInRect(loc, contentFrame, NO))
- {
- QNSView *contentView = (QNSView *) m_cocoaPlatformWindow->contentView();
- [contentView handleFrameStrutMouseEvent: theEvent];
- }
- }
+- (void)closeAndRelease
+{
+ [self.helper detachFromPlatformWindow];
+ [self close];
+ [self release];
}
-- (void)clearPlatformWindow
+- (void)dealloc
{
- m_cocoaPlatformWindow = 0;
+ [_helper release];
+ _helper = nil;
+ [super dealloc];
}
@end
@implementation QNSPanel
+@synthesize helper = _helper;
+
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)windowStyle
qPlatformWindow:(QCocoaWindow *)qpw
@@ -205,47 +263,47 @@ static bool isMouseEvent(NSEvent *ev)
// set up before the window is shown and needs a proper window)
if (self) {
- m_cocoaPlatformWindow = qpw;
+ _helper = [[QNSWindowHelper alloc] initWithNSWindow:self platformWindow:qpw];
}
return self;
}
- (BOOL)canBecomeKeyWindow
{
- if (!m_cocoaPlatformWindow)
+ QCocoaWindow *pw = self.helper.platformWindow;
+ if (!pw)
return NO;
// Only tool or dialog windows should become key:
- if (m_cocoaPlatformWindow
- && (m_cocoaPlatformWindow->window()->type() == Qt::Tool ||
- m_cocoaPlatformWindow->window()->type() == Qt::Dialog))
+ Qt::WindowType type = pw->window()->type();
+ if (type == Qt::Tool || type == Qt::Dialog)
return YES;
+
return NO;
}
- (void) sendEvent: (NSEvent*) theEvent
{
- [super sendEvent: theEvent];
+ [self.helper handleWindowEvent:theEvent];
+}
- if (!m_cocoaPlatformWindow)
- return;
+- (void)superSendEvent:(NSEvent *)theEvent
+{
+ [super sendEvent:theEvent];
+}
- if (m_cocoaPlatformWindow->frameStrutEventsEnabled() && isMouseEvent(theEvent)) {
- NSPoint loc = [theEvent locationInWindow];
- NSRect windowFrame = [self legacyConvertRectFromScreen:[self frame]];
- NSRect contentFrame = [[self contentView] frame];
- if (NSMouseInRect(loc, windowFrame, NO) &&
- !NSMouseInRect(loc, contentFrame, NO))
- {
- QNSView *contentView = (QNSView *) m_cocoaPlatformWindow->contentView();
- [contentView handleFrameStrutMouseEvent: theEvent];
- }
- }
+- (void)closeAndRelease
+{
+ [self.helper detachFromPlatformWindow];
+ [self close];
+ [self release];
}
-- (void)clearPlatformWindow
+- (void)dealloc
{
- m_cocoaPlatformWindow = 0;
+ [_helper release];
+ _helper = nil;
+ [super dealloc];
}
@end
@@ -262,7 +320,6 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw)
, m_contentViewIsToBeEmbedded(false)
, m_parentCocoaWindow(0)
, m_isNSWindowChild(false)
- , m_nsWindowDelegate(0)
, m_synchedWindowState(Qt::WindowActive)
, m_windowModality(Qt::NonModal)
, m_windowUnderMouse(false)
@@ -328,7 +385,8 @@ QCocoaWindow::~QCocoaWindow()
#endif
QCocoaAutoReleasePool pool;
- clearNSWindow(m_nsWindow);
+ [m_nsWindow setContentView:nil];
+ [m_nsWindow.helper detachFromPlatformWindow];
if (m_isNSWindowChild) {
if (m_parentCocoaWindow)
m_parentCocoaWindow->removeChildWindow(this);
@@ -346,7 +404,6 @@ QCocoaWindow::~QCocoaWindow()
[m_contentView release];
[m_nsWindow release];
- [m_nsWindowDelegate release];
[m_windowCursor release];
}
@@ -1090,12 +1147,10 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow)
// Remove current window (if any)
if ((m_nsWindow && !needsNSWindow) || (usesNSPanel != shouldUseNSPanel())) {
- clearNSWindow(m_nsWindow);
- [m_nsWindow close];
- [m_nsWindow release];
+ [m_nsWindow closeAndRelease];
+ if (wasNSWindowChild && oldParentCocoaWindow)
+ oldParentCocoaWindow->removeChildWindow(this);
m_nsWindow = 0;
- [m_nsWindowDelegate release];
- m_nsWindowDelegate = 0;
}
if (needsNSWindow) {
@@ -1203,7 +1258,7 @@ bool QCocoaWindow::shouldUseNSPanel()
((type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog);
}
-NSWindow * QCocoaWindow::createNSWindow()
+QCocoaNSWindow * QCocoaWindow::createNSWindow()
{
QCocoaAutoReleasePool pool;
@@ -1219,7 +1274,7 @@ NSWindow * QCocoaWindow::createNSWindow()
} else {
styleMask = windowStyleMask(flags);
}
- NSWindow *createdWindow = 0;
+ QCocoaNSWindow *createdWindow = 0;
// Use NSPanel for popup-type windows. (Popup, Tool, ToolTip, SplashScreen)
// and dialogs
@@ -1270,17 +1325,8 @@ NSWindow * QCocoaWindow::createNSWindow()
return createdWindow;
}
-void QCocoaWindow::setNSWindow(NSWindow *window)
+void QCocoaWindow::setNSWindow(QCocoaNSWindow *window)
{
- m_nsWindowDelegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this];
- [window setDelegate:m_nsWindowDelegate];
-
- // Prevent Cocoa from releasing the window on close. Qt
- // handles the close event asynchronously and we want to
- // make sure that m_nsWindow stays valid until the
- // QCocoaWindow is deleted by Qt.
- [window setReleasedWhenClosed : NO];
-
if (window.contentView != m_contentView) {
[m_contentView setPostsFrameChangedNotifications: NO];
[window setContentView:m_contentView];
@@ -1288,17 +1334,6 @@ void QCocoaWindow::setNSWindow(NSWindow *window)
}
}
-void QCocoaWindow::clearNSWindow(NSWindow *window)
-{
- [window setContentView:nil];
- [window setDelegate:nil];
- [window clearPlatformWindow];
-
- if (m_isNSWindowChild) {
- m_parentCocoaWindow->removeChildWindow(this);
- }
-}
-
void QCocoaWindow::removeChildWindow(QCocoaWindow *child)
{
m_childWindows.removeOne(child);