Cocoa: Merge QNSWindow and QNSPanel, inherit NSPanel

Since we will later have frameless NSWindow child QWindows, it makes
sense to be able to keep and reuse the instantiated NSWindows in cases
like QDockWidgets. If we keep these 2 different classes, it becomes
impossible to morph one into the other.

Task-number: QTBUG-33082
Change-Id: I351c628b887101b1cfd67689afbf689f119554de
Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com>
This commit is contained in:
Gabriel de Dietrich 2014-01-13 15:13:46 +01:00 committed by The Qt Project
parent 2d576f79f7
commit 79fb39a87c
2 changed files with 103 additions and 134 deletions

View File

@ -52,34 +52,32 @@
QT_FORWARD_DECLARE_CLASS(QCocoaWindow) QT_FORWARD_DECLARE_CLASS(QCocoaWindow)
@interface QNSWindow : NSWindow {
@public QCocoaWindow *m_cocoaPlatformWindow;
}
- (void)clearPlatformWindow;
- (BOOL)canBecomeKeyWindow;
@end
@interface QNSPanel : NSPanel {
@public QCocoaWindow *m_cocoaPlatformWindow;
}
- (void)clearPlatformWindow;
- (BOOL)canBecomeKeyWindow;
@end
@class QNSWindowDelegate; @class QNSWindowDelegate;
@interface QNSWindow : NSPanel {
@public
QCocoaWindow *m_cocoaPlatformWindow;
}
- (void)clearPlatformWindow;
@end
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// QCocoaWindow // QCocoaWindow
// //
// QCocoaWindow is an NSView (not an NSWindow!) in the sense // A QCocoaWindow is backed by a NSView and optionally a NSWindow.
// that it relies on a NSView for all event handling and
// graphics output and does not require a NSWindow, except for
// for the window-related functions like setWindowTitle.
// //
// As a consequence of this it is possible to embed the QCocoaWindow // The NSView is used for most event handling and graphics output.
// in an NSView hierarchy by getting a pointer to the "backing" //
// NSView and not calling QCocoaWindow::show(): // Top-level QWindows are always backed by a NSWindow in addition to
// the NSView. Child QWindows can also be backed by NSWindows, which
// enables proper stacking of GL Widgets and threaded GL rendering
// to multiple contexts.
//
// It is possible to embed the QCocoaWindow in an NSView hierarchy
// by getting a pointer to the backing NSView and not calling
// QCocoaWindow::show():
// //
// QWindow *qtWindow = new MyWindow(); // QWindow *qtWindow = new MyWindow();
// qtWindow->create(); // qtWindow->create();
@ -135,6 +133,7 @@ public:
void windowDidResize(); void windowDidResize();
bool windowShouldClose(); bool windowShouldClose();
bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const; bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const;
bool windowShouldBehaveAsPanel() const;
void setSynchedWindowStateFromWindow(); void setSynchedWindowStateFromWindow();
@ -170,9 +169,9 @@ protected:
// NSWindow handling. The QCocoaWindow/QNSView can either be displayed // NSWindow handling. The QCocoaWindow/QNSView can either be displayed
// in an existing NSWindow or in one created by Qt. // in an existing NSWindow or in one created by Qt.
void recreateWindow(const QPlatformWindow *parentWindow); void recreateWindow(const QPlatformWindow *parentWindow);
NSWindow *createNSWindow(); QNSWindow *createNSWindow();
void setNSWindow(NSWindow *window); void setNSWindow(QNSWindow *window);
void clearNSWindow(NSWindow *window); void clearNSWindow(QNSWindow *window);
QRect windowGeometry() const; QRect windowGeometry() const;
QCocoaWindow *parentCocoaWindow() const; QCocoaWindow *parentCocoaWindow() const;
@ -185,12 +184,14 @@ public: // for QNSView
NSView *m_contentView; NSView *m_contentView;
QNSView *m_qtView; QNSView *m_qtView;
NSWindow *m_nsWindow; QNSWindow *m_nsWindow;
// TODO merge to one variable if possible // TODO merge to one variable if possible
bool m_contentViewIsEmbedded; // true if the m_contentView is actually embedded in a "foreign" NSView hiearchy bool m_contentViewIsEmbedded; // true if the m_contentView is actually embedded in a "foreign" NSView hiearchy
bool m_contentViewIsToBeEmbedded; // true if the m_contentView is intended to be embedded in a "foreign" NSView hiearchy bool m_contentViewIsToBeEmbedded; // true if the m_contentView is intended to be embedded in a "foreign" NSView hiearchy
QCocoaWindow *m_parentCocoaWindow;
QNSWindowDelegate *m_nsWindowDelegate; QNSWindowDelegate *m_nsWindowDelegate;
Qt::WindowFlags m_windowFlags; Qt::WindowFlags m_windowFlags;
Qt::WindowState m_synchedWindowState; Qt::WindowState m_synchedWindowState;

View File

@ -80,15 +80,10 @@ static bool isMouseEvent(NSEvent *ev)
} }
@interface NSWindow (CocoaWindowCategory) @interface NSWindow (CocoaWindowCategory)
- (void) clearPlatformWindow;
- (NSRect) legacyConvertRectFromScreen:(NSRect) rect; - (NSRect) legacyConvertRectFromScreen:(NSRect) rect;
@end @end
@implementation NSWindow (CocoaWindowCategory) @implementation NSWindow (CocoaWindowCategory)
- (void) clearPlatformWindow
{
}
- (NSRect) legacyConvertRectFromScreen:(NSRect) rect - (NSRect) legacyConvertRectFromScreen:(NSRect) rect
{ {
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
@ -104,11 +99,39 @@ static bool isMouseEvent(NSEvent *ev)
@implementation QNSWindow @implementation QNSWindow
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)windowStyle
qPlatformWindow:(QCocoaWindow *)qpw
{
self = [super initWithContentRect:contentRect
styleMask:windowStyle
backing:NSBackingStoreBuffered
defer:NO]; // Deferring window creation breaks OpenGL (the GL context is
// set up before the window is shown and needs a proper window)
if (self) {
m_cocoaPlatformWindow = qpw;
}
return self;
}
- (BOOL)canBecomeKeyWindow - (BOOL)canBecomeKeyWindow
{ {
// The default implementation returns NO for title-bar less windows, if (!m_cocoaPlatformWindow)
// override and return yes here to make sure popup windows such as return NO;
// the combobox popup can become the key window.
// Only tool or dialog windows should become key:
if (m_cocoaPlatformWindow && m_cocoaPlatformWindow->windowShouldBehaveAsPanel()) {
Qt::WindowType type = m_cocoaPlatformWindow->window()->type();
if (m_cocoaPlatformWindow->m_overrideBecomeKey
|| type == Qt::Tool || type == Qt::Dialog)
return YES;
return NO;
}
// All other windows can become the key window. This includes
// popup windows such as the combobox popup, which is a title-bar
// less window that by default can't become key.
return YES; return YES;
} }
@ -121,6 +144,9 @@ static bool isMouseEvent(NSEvent *ev)
if (!m_cocoaPlatformWindow || m_cocoaPlatformWindow->window()->transientParent()) if (!m_cocoaPlatformWindow || m_cocoaPlatformWindow->window()->transientParent())
canBecomeMain = NO; canBecomeMain = NO;
if (m_cocoaPlatformWindow && m_cocoaPlatformWindow->windowShouldBehaveAsPanel())
canBecomeMain = NO;
return canBecomeMain; return canBecomeMain;
} }
@ -151,49 +177,6 @@ static bool isMouseEvent(NSEvent *ev)
@end @end
@implementation QNSPanel
- (BOOL)canBecomeKeyWindow
{
if (!m_cocoaPlatformWindow)
return NO;
// Only tool or dialog windows should become key:
if (m_cocoaPlatformWindow
&& (m_cocoaPlatformWindow->m_overrideBecomeKey ||
m_cocoaPlatformWindow->window()->type() == Qt::Tool ||
m_cocoaPlatformWindow->window()->type() == Qt::Dialog))
return YES;
return NO;
}
- (void) sendEvent: (NSEvent*) theEvent
{
[super sendEvent: theEvent];
if (!m_cocoaPlatformWindow)
return;
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)clearPlatformWindow
{
m_cocoaPlatformWindow = 0;
}
@end
const int QCocoaWindow::NoAlertRequest = -1; const int QCocoaWindow::NoAlertRequest = -1;
QCocoaWindow::QCocoaWindow(QWindow *tlw) QCocoaWindow::QCocoaWindow(QWindow *tlw)
@ -543,15 +526,21 @@ void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) {
Qt::WindowType type = window()->type(); NSWindowCollectionBehavior behavior = [m_nsWindow collectionBehavior];
if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) { if (windowShouldBehaveAsPanel()) {
NSWindowCollectionBehavior behavior = [m_nsWindow collectionBehavior]; behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
} else {
if (flags & Qt::WindowFullscreenButtonHint) if (flags & Qt::WindowFullscreenButtonHint)
behavior |= NSWindowCollectionBehaviorFullScreenPrimary; behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
else else
behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary; behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
[m_nsWindow setCollectionBehavior:behavior];
} }
[m_nsWindow setCollectionBehavior:behavior];
[m_nsWindow setAnimationBehavior:(flags & Qt::Popup) == Qt::Popup
? NSWindowAnimationBehaviorUtilityWindow
: NSWindowAnimationBehaviorDefault];
} }
#endif #endif
} }
@ -823,6 +812,14 @@ bool QCocoaWindow::windowIsPopupType(Qt::WindowType type) const
return ((type & Qt::Popup) == Qt::Popup); return ((type & Qt::Popup) == Qt::Popup);
} }
bool QCocoaWindow::windowShouldBehaveAsPanel() const
{
// Before merging QNSPanel and QNSWindow, we used NSPanel for popup-type
// windows (Popup, Tool, ToolTip, SplashScreen) and dialogs
Qt::WindowType type = window()->type();
return (type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog;
}
void QCocoaWindow::setCurrentContext(QCocoaGLContext *context) void QCocoaWindow::setCurrentContext(QCocoaGLContext *context)
{ {
m_glContext = context; m_glContext = context;
@ -879,63 +876,29 @@ void QCocoaWindow::requestActivateWindow()
[ window makeKeyWindow ]; [ window makeKeyWindow ];
} }
NSWindow * QCocoaWindow::createNSWindow() QNSWindow * QCocoaWindow::createNSWindow()
{ {
QCocoaAutoReleasePool pool; QCocoaAutoReleasePool pool;
QRect rect = initialGeometry(window(), window()->geometry(), defaultWindowWidth, defaultWindowHeight); QRect rect = initialGeometry(window(), window()->geometry(), defaultWindowWidth, defaultWindowHeight);
NSRect frame = qt_mac_flipRect(rect, window()); NSRect frame = qt_mac_flipRect(rect, window());
Qt::WindowType type = window()->type();
Qt::WindowFlags flags = window()->flags(); Qt::WindowFlags flags = window()->flags();
NSUInteger styleMask = windowStyleMask(flags); NSUInteger styleMask;
NSWindow *createdWindow = 0; styleMask = windowStyleMask(flags);
// Use NSPanel for popup-type windows. (Popup, Tool, ToolTip, SplashScreen) QNSWindow *createdWindow = [[QNSWindow alloc] initWithContentRect:frame styleMask:styleMask qPlatformWindow:this];
// and dialogs
if ((type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog) { Qt::WindowFlags type = window()->type();
QNSPanel *window; createdWindow.hidesOnDeactivate = type == Qt::Tool || type == Qt::ToolTip;
window = [[QNSPanel alloc] initWithContentRect:frame
styleMask: styleMask
backing:NSBackingStoreBuffered
defer:NO]; // Deferring window creation breaks OpenGL (the GL context is set up
// before the window is shown and needs a proper window.).
if ((type & Qt::Popup) == Qt::Popup)
[window setHasShadow:YES];
[window setHidesOnDeactivate: NO];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) {
// Make popup winows show on the same desktop as the parent full-screen window.
[window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
if ((type & Qt::Popup) == Qt::Popup)
[window setAnimationBehavior:NSWindowAnimationBehaviorUtilityWindow];
}
#endif
window->m_cocoaPlatformWindow = this;
createdWindow = window;
} else {
QNSWindow *window;
window = [[QNSWindow alloc] initWithContentRect:frame
styleMask: styleMask
backing:NSBackingStoreBuffered
defer:NO]; // Deferring window creation breaks OpenGL (the GL context is set up
// before the window is shown and needs a proper window.).
window->m_cocoaPlatformWindow = this;
createdWindow = window;
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if ([createdWindow respondsToSelector:@selector(setRestorable:)])
[createdWindow setRestorable: NO]; [createdWindow setRestorable: NO];
}
#endif #endif
NSInteger level = windowLevel(flags);
[createdWindow setLevel:level];
if (window()->format().alphaBufferSize() > 0) { if (window()->format().alphaBufferSize() > 0) {
[createdWindow setBackgroundColor:[NSColor clearColor]]; [createdWindow setBackgroundColor:[NSColor clearColor]];
[createdWindow setOpaque:NO]; [createdWindow setOpaque:NO];
@ -948,10 +911,12 @@ NSWindow * QCocoaWindow::createNSWindow()
return createdWindow; return createdWindow;
} }
void QCocoaWindow::setNSWindow(NSWindow *window) void QCocoaWindow::setNSWindow(QNSWindow *window)
{ {
m_nsWindowDelegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this]; if (!m_nsWindowDelegate) {
[window setDelegate:m_nsWindowDelegate]; m_nsWindowDelegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this];
[window setDelegate:m_nsWindowDelegate];
}
// Prevent Cocoa from releasing the window on close. Qt // Prevent Cocoa from releasing the window on close. Qt
// handles the close event asynchronously and we want to // handles the close event asynchronously and we want to
@ -959,25 +924,28 @@ void QCocoaWindow::setNSWindow(NSWindow *window)
// QCocoaWindow is deleted by Qt. // QCocoaWindow is deleted by Qt.
[window setReleasedWhenClosed : NO]; [window setReleasedWhenClosed : NO];
if (m_qtView) if (m_qtView)
[[NSNotificationCenter defaultCenter] addObserver:m_qtView [[NSNotificationCenter defaultCenter] addObserver:m_qtView
selector:@selector(windowNotification:) selector:@selector(windowNotification:)
name:nil // Get all notifications name:nil // Get all notifications
object:m_nsWindow]; object:m_nsWindow];
[m_contentView setPostsFrameChangedNotifications: NO]; if (window.contentView != m_contentView) {
[window setContentView:m_contentView]; [m_contentView setPostsFrameChangedNotifications: NO];
[m_contentView setPostsFrameChangedNotifications: YES]; [window setContentView:m_contentView];
[m_contentView setPostsFrameChangedNotifications: YES];
}
} }
void QCocoaWindow::clearNSWindow(NSWindow *window) void QCocoaWindow::clearNSWindow(QNSWindow *window)
{ {
[window setContentView:nil]; [window setContentView:nil];
[window setDelegate:nil]; [window setDelegate:nil];
[window clearPlatformWindow]; [window clearPlatformWindow];
[[NSNotificationCenter defaultCenter] removeObserver:m_contentView
name:nil object:window]; if (m_qtView)
[[NSNotificationCenter defaultCenter] removeObserver:m_qtView
name:nil object:window];
} }
// Returns the current global screen geometry for the nswindow associated with this window. // Returns the current global screen geometry for the nswindow associated with this window.