Cocoa: Implement support for child windows.
authorMorten Johan Sorvig <morten.sorvig@nokia.com>
Tue, 13 Mar 2012 09:37:53 +0000 (10:37 +0100)
committerQt by Nokia <qt-info@nokia.com>
Tue, 20 Mar 2012 07:29:07 +0000 (08:29 +0100)
On OS X, child windows (in the Qt sense) are not
windows. Add special case to window creation that
links the content views instead of creating a
NSWindow. Add a similar special case to
setGeometry().

Refactor window (re)creation into recreateWindow(),
which is called from both the QCocoaWindow
constructor and setParent().

m_nsWindow may now be null, add null-pointer checks
to all usages. Change winId() to return the
m_contentView pointer instead of m_nsWindow.

QGLWidget now works, but probably has sibling window
stacking issues which we won't be able to fix without
moving to client-side compositing.

Change-Id: I2e74cf27734dba7076c150e0d8341f0a62d3de2d
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
src/plugins/platforms/cocoa/qcocoawindow.h
src/plugins/platforms/cocoa/qcocoawindow.mm

index 8bdb553..0920bc7 100644 (file)
@@ -92,6 +92,7 @@ public:
     ~QCocoaWindow();
 
     void setGeometry(const QRect &rect);
+    void setCocoaGeometry(const QRect &rect);
     void setVisible(bool visible);
     Qt::WindowFlags setWindowFlags(Qt::WindowFlags flags);
     void setWindowTitle(const QString &title);
@@ -117,6 +118,7 @@ public:
 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);
index 5c30e7f..d432740 100644 (file)
@@ -90,6 +90,7 @@
 
 QCocoaWindow::QCocoaWindow(QWindow *tlw)
     : QPlatformWindow(tlw)
+    , m_nsWindow(0)
     , m_glContext(0)
     , m_inConstructor(true)
 {
@@ -98,8 +99,7 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw)
     m_contentView = [[QNSView alloc] initWithQWindow:tlw platformWindow:this];
     setGeometry(tlw->geometry());
 
-    m_nsWindow = createNSWindow();
-    setNSWindow(m_nsWindow);
+    recreateWindow(parent());
 
     m_inConstructor = false;
 }
@@ -119,10 +119,18 @@ void QCocoaWindow::setGeometry(const QRect &rect)
     qDebug() << "QCocoaWindow::setGeometry" << this << rect;
 #endif
     QPlatformWindow::setGeometry(rect);
+    setCocoaGeometry(rect);
+}
 
-    NSRect bounds = qt_mac_flipRect(rect, window());
-    [m_nsWindow setContentSize : bounds.size];
-    [m_nsWindow setFrameOrigin : bounds.origin];
+void QCocoaWindow::setCocoaGeometry(const QRect &rect)
+{
+    if (m_nsWindow) {
+        NSRect bounds = qt_mac_flipRect(rect, window());
+        [m_nsWindow setContentSize : bounds.size];
+        [m_nsWindow setFrameOrigin : bounds.origin];
+    } else {
+        [m_contentView setFrame : NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height())];
+    }
 }
 
 void QCocoaWindow::setVisible(bool visible)
@@ -151,13 +159,16 @@ void QCocoaWindow::setVisible(bool visible)
         // Make sure the QWindow has a frame ready before we show the NSWindow.
         QWindowSystemInterface::handleSynchronousExposeEvent(window(), QRect(QPoint(), geometry().size()));
 
-        if ([m_nsWindow canBecomeKeyWindow])
-            [m_nsWindow makeKeyAndOrderFront:nil];
-        else
-            [m_nsWindow orderFront: nil];
+        if (m_nsWindow) {
+            if ([m_nsWindow canBecomeKeyWindow])
+                [m_nsWindow makeKeyAndOrderFront:nil];
+            else
+                [m_nsWindow orderFront: nil];
+        }
     } else {
         // qDebug() << "close" << this;
-        [m_nsWindow orderOut:m_nsWindow];
+        if (m_nsWindow)
+            [m_nsWindow orderOut:m_nsWindow];
     }
 }
 
@@ -170,6 +181,8 @@ Qt::WindowFlags QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
 void QCocoaWindow::setWindowTitle(const QString &title)
 {
     QCocoaAutoReleasePool pool;
+    if (!m_nsWindow)
+        return;
 
     CFStringRef windowTitle = QCFString::toCFStringRef(title);
     [m_nsWindow setTitle: const_cast<NSString *>(reinterpret_cast<const NSString *>(windowTitle))];
@@ -180,17 +193,21 @@ void QCocoaWindow::raise()
 {
     //qDebug() << "raise" << this;
     // ### handle spaces (see Qt 4 raise_sys in qwidget_mac.mm)
-    [m_nsWindow orderFront: m_nsWindow];
+    if (m_nsWindow)
+        [m_nsWindow orderFront: m_nsWindow];
 }
 
 void QCocoaWindow::lower()
 {
-    [m_nsWindow orderBack: m_nsWindow];
+    if (m_nsWindow)
+        [m_nsWindow orderBack: m_nsWindow];
 }
 
 void QCocoaWindow::propagateSizeHints()
 {
     QCocoaAutoReleasePool pool;
+    if (!m_nsWindow)
+        return;
 
     [m_nsWindow setMinSize : qt_mac_toNSSize(window()->minimumSize())];
     [m_nsWindow setMaxSize : qt_mac_toNSSize(window()->maximumSize())];
@@ -213,6 +230,9 @@ void QCocoaWindow::propagateSizeHints()
 
 bool QCocoaWindow::setKeyboardGrabEnabled(bool grab)
 {
+    if (!m_nsWindow)
+        return false;
+
     if (grab && ![m_nsWindow isKeyWindow])
         [m_nsWindow makeKeyWindow];
     else if (!grab && [m_nsWindow isKeyWindow])
@@ -222,6 +242,9 @@ bool QCocoaWindow::setKeyboardGrabEnabled(bool grab)
 
 bool QCocoaWindow::setMouseGrabEnabled(bool grab)
 {
+    if (!m_nsWindow)
+        return false;
+
     if (grab && ![m_nsWindow isKeyWindow])
         [m_nsWindow makeKeyWindow];
     else if (!grab && [m_nsWindow isKeyWindow])
@@ -231,23 +254,19 @@ bool QCocoaWindow::setMouseGrabEnabled(bool grab)
 
 WId QCocoaWindow::winId() const
 {
-    return WId(m_nsWindow);
+    return WId(m_contentView);
 }
 
-void QCocoaWindow::setParent(const QPlatformWindow *window)
+void QCocoaWindow::setParent(const QPlatformWindow *parentWindow)
 {
     // recreate the window for compatibility
-    clearNSWindow(m_nsWindow);
-    [m_nsWindow close];
-    [m_nsWindow release];
-
-    m_nsWindow = createNSWindow();
-    setNSWindow(m_nsWindow);
+    recreateWindow(parentWindow);
+    setCocoaGeometry(geometry());
 }
 
 NSView *QCocoaWindow::contentView() const
 {
-    return [m_nsWindow contentView];
+    return m_contentView;
 }
 
 void QCocoaWindow::windowWillMove()
@@ -266,6 +285,9 @@ void QCocoaWindow::windowDidMove()
 
 void QCocoaWindow::windowDidResize()
 {
+    if (!m_nsWindow)
+        return;
+
     NSRect rect = [[m_nsWindow contentView]frame];
     // Call setFrameSize which will trigger a frameDidChangeNotification on QNSView.
     [[m_nsWindow contentView] setFrameSize:rect.size];
@@ -286,6 +308,27 @@ QCocoaGLContext *QCocoaWindow::currentContext() const
     return m_glContext;
 }
 
+void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow)
+{
+    // Remove current window (if any)
+    if (m_nsWindow) {
+        clearNSWindow(m_nsWindow);
+        [m_nsWindow close];
+        [m_nsWindow release];
+        m_nsWindow = 0;
+    }
+
+    if (!parentWindow) {
+        // Create a new NSWindow if this is a top-level window.
+        m_nsWindow = createNSWindow();
+        setNSWindow(m_nsWindow);
+    } else {
+        // Child windows have no NSWindow, link the NSViews instead.
+        const QCocoaWindow *parentCococaWindow = static_cast<const QCocoaWindow *>(parentWindow);
+        [parentCococaWindow->m_contentView addSubview : m_contentView];
+    }
+}
+
 NSWindow * QCocoaWindow::createNSWindow()
 {
     QCocoaAutoReleasePool pool;
@@ -378,6 +421,9 @@ void QCocoaWindow::clearNSWindow(NSWindow *window)
 // Returns the current global screen geometry for the nswindow associated with this window.
 QRect QCocoaWindow::windowGeometry() const
 {
+    if (!m_nsWindow)
+        return geometry();
+
     NSRect rect = [m_nsWindow frame];
     QPlatformScreen *onScreen = QPlatformScreen::platformScreenForWindow(window());
     int flippedY = onScreen->geometry().height() - rect.origin.y - rect.size.height;  // account for nswindow inverted y.