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)
@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;
@interface QNSWindow : NSPanel {
@public
QCocoaWindow *m_cocoaPlatformWindow;
}
- (void)clearPlatformWindow;
@end
QT_BEGIN_NAMESPACE
// QCocoaWindow
//
// QCocoaWindow is an NSView (not an NSWindow!) in the sense
// 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.
// A QCocoaWindow is backed by a NSView and optionally a NSWindow.
//
// As a consequence of this it is possible to embed the QCocoaWindow
// in an NSView hierarchy by getting a pointer to the "backing"
// NSView and not calling QCocoaWindow::show():
// The NSView is used for most event handling and graphics output.
//
// 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();
// qtWindow->create();
@ -135,6 +133,7 @@ public:
void windowDidResize();
bool windowShouldClose();
bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const;
bool windowShouldBehaveAsPanel() const;
void setSynchedWindowStateFromWindow();
@ -170,9 +169,9 @@ protected:
// NSWindow handling. The QCocoaWindow/QNSView can either be displayed
// in an existing NSWindow or in one created by Qt.
void recreateWindow(const QPlatformWindow *parentWindow);
NSWindow *createNSWindow();
void setNSWindow(NSWindow *window);
void clearNSWindow(NSWindow *window);
QNSWindow *createNSWindow();
void setNSWindow(QNSWindow *window);
void clearNSWindow(QNSWindow *window);
QRect windowGeometry() const;
QCocoaWindow *parentCocoaWindow() const;
@ -185,12 +184,14 @@ public: // for QNSView
NSView *m_contentView;
QNSView *m_qtView;
NSWindow *m_nsWindow;
QNSWindow *m_nsWindow;
// 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_contentViewIsToBeEmbedded; // true if the m_contentView is intended to be embedded in a "foreign" NSView hiearchy
QCocoaWindow *m_parentCocoaWindow;
QNSWindowDelegate *m_nsWindowDelegate;
Qt::WindowFlags m_windowFlags;
Qt::WindowState m_synchedWindowState;

View File

@ -80,15 +80,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
@ -104,11 +99,39 @@ static bool isMouseEvent(NSEvent *ev)
@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
{
// 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.
if (!m_cocoaPlatformWindow)
return NO;
// 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;
}
@ -121,6 +144,9 @@ static bool isMouseEvent(NSEvent *ev)
if (!m_cocoaPlatformWindow || m_cocoaPlatformWindow->window()->transientParent())
canBecomeMain = NO;
if (m_cocoaPlatformWindow && m_cocoaPlatformWindow->windowShouldBehaveAsPanel())
canBecomeMain = NO;
return canBecomeMain;
}
@ -151,49 +177,6 @@ static bool isMouseEvent(NSEvent *ev)
@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;
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 (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) {
Qt::WindowType type = window()->type();
if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) {
NSWindowCollectionBehavior behavior = [m_nsWindow collectionBehavior];
if (windowShouldBehaveAsPanel()) {
behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
} else {
if (flags & Qt::WindowFullscreenButtonHint)
behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
else
behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
[m_nsWindow setCollectionBehavior:behavior];
}
[m_nsWindow setCollectionBehavior:behavior];
[m_nsWindow setAnimationBehavior:(flags & Qt::Popup) == Qt::Popup
? NSWindowAnimationBehaviorUtilityWindow
: NSWindowAnimationBehaviorDefault];
}
#endif
}
@ -823,6 +812,14 @@ bool QCocoaWindow::windowIsPopupType(Qt::WindowType type) const
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)
{
m_glContext = context;
@ -879,63 +876,29 @@ void QCocoaWindow::requestActivateWindow()
[ window makeKeyWindow ];
}
NSWindow * QCocoaWindow::createNSWindow()
QNSWindow * QCocoaWindow::createNSWindow()
{
QCocoaAutoReleasePool pool;
QRect rect = initialGeometry(window(), window()->geometry(), defaultWindowWidth, defaultWindowHeight);
NSRect frame = qt_mac_flipRect(rect, window());
Qt::WindowType type = window()->type();
Qt::WindowFlags flags = window()->flags();
NSUInteger styleMask = windowStyleMask(flags);
NSWindow *createdWindow = 0;
NSUInteger styleMask;
styleMask = windowStyleMask(flags);
// Use NSPanel for popup-type windows. (Popup, Tool, ToolTip, SplashScreen)
// and dialogs
if ((type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog) {
QNSPanel *window;
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];
QNSWindow *createdWindow = [[QNSWindow alloc] initWithContentRect:frame styleMask:styleMask qPlatformWindow:this];
Qt::WindowFlags type = window()->type();
createdWindow.hidesOnDeactivate = type == Qt::Tool || type == Qt::ToolTip;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_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];
}
#endif
NSInteger level = windowLevel(flags);
[createdWindow setLevel:level];
if (window()->format().alphaBufferSize() > 0) {
[createdWindow setBackgroundColor:[NSColor clearColor]];
[createdWindow setOpaque:NO];
@ -948,10 +911,12 @@ NSWindow * QCocoaWindow::createNSWindow()
return createdWindow;
}
void QCocoaWindow::setNSWindow(NSWindow *window)
void QCocoaWindow::setNSWindow(QNSWindow *window)
{
if (!m_nsWindowDelegate) {
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
@ -959,24 +924,27 @@ void QCocoaWindow::setNSWindow(NSWindow *window)
// QCocoaWindow is deleted by Qt.
[window setReleasedWhenClosed : NO];
if (m_qtView)
[[NSNotificationCenter defaultCenter] addObserver:m_qtView
selector:@selector(windowNotification:)
name:nil // Get all notifications
object:m_nsWindow];
if (window.contentView != m_contentView) {
[m_contentView setPostsFrameChangedNotifications: NO];
[window setContentView:m_contentView];
[m_contentView setPostsFrameChangedNotifications: YES];
}
}
void QCocoaWindow::clearNSWindow(NSWindow *window)
void QCocoaWindow::clearNSWindow(QNSWindow *window)
{
[window setContentView:nil];
[window setDelegate:nil];
[window clearPlatformWindow];
[[NSNotificationCenter defaultCenter] removeObserver:m_contentView
if (m_qtView)
[[NSNotificationCenter defaultCenter] removeObserver:m_qtView
name:nil object:window];
}