1/**************************************************************************** 2** 3** Copyright (C) 2016 The Qt Company Ltd. 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of the QtWidgets module of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:LGPL$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see https://www.qt.io/terms-conditions. For further 15** information use the contact form at https://www.qt.io/contact-us. 16** 17** GNU Lesser General Public License Usage 18** Alternatively, this file may be used under the terms of the GNU Lesser 19** General Public License version 3 as published by the Free Software 20** Foundation and appearing in the file LICENSE.LGPL3 included in the 21** packaging of this file. Please review the following information to 22** ensure the GNU Lesser General Public License version 3 requirements 23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24** 25** GNU General Public License Usage 26** Alternatively, this file may be used under the terms of the GNU 27** General Public License version 2.0 or (at your option) the GNU General 28** Public license version 3 or any later version approved by the KDE Free 29** Qt Foundation. The licenses are as published by the Free Software 30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31** included in the packaging of this file. Please review the following 32** information to ensure the GNU General Public License requirements will 33** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34** https://www.gnu.org/licenses/gpl-3.0.html. 35** 36** $QT_END_LICENSE$ 37** 38****************************************************************************/ 39 40#import <AppKit/AppKit.h> 41#include "qmaccocoaviewcontainer_mac.h" 42 43#include <QtCore/QDebug> 44#include <QtGui/QWindow> 45#include <qpa/qplatformnativeinterface.h> 46#include <private/qwidget_p.h> 47#include <private/qwindow_p.h> 48 49/*! 50 \class QMacCocoaViewContainer 51 \since 4.5 52 53 \brief The QMacCocoaViewContainer class provides a widget for \macos that can be used to wrap arbitrary 54 Cocoa views (i.e., NSView subclasses) and insert them into Qt hierarchies. 55 56 \ingroup advanced 57 \inmodule QtWidgets 58 59 While Qt offers a lot of classes for writing your application, Apple's 60 Cocoa frameworks offer functionality that is not currently available (or 61 may never end up) in Qt. Using QMacCocoaViewContainer, it is possible to take an 62 arbitrary NSView-derived class from Cocoa and put it in a Qt widgets hierarchy. 63 Depending on the level of integration you need, you can use QMacCocoaViewContainer 64 directly or subclass it to wrap more functionality of the underlying NSView. 65 66 It should be also noted that, at the Cocoa level, there is a difference 67 between top-level windows and views (widgets that are inside a window). 68 For this reason, make sure that the NSView that you are wrapping doesn't 69 end up as a top-level window. The best way to ensure this is to make sure 70 QMacCocoaViewContainer's parent widget is not null. 71 72 If you are using QMacCocoaViewContainer as a subclass and are accessing Cocoa API, 73 it is probably simpler to have your file end with \tt{.mm} instead of \tt{.cpp}. 74 Most Apple tools will correctly identify the source as Objective-C++. 75 76 QMacCocoaViewContainer requires knowledge of how Cocoa works, especially in 77 regard to its reference counting (retain/release) nature. It is noted in 78 the functions below if there is any change in the reference count. Cocoa 79 views often generate temporary objects that are released by an autorelease 80 pool. If this is done outside of a running event loop, it is up to the 81 developer to provide the autorelease pool. 82 83 The following is a snippet showing how to subclass QMacCocoaViewContainer 84 to wrap an NSSearchField. 85 86 \code 87 SearchWidget::SearchWidget(QWidget *parent) 88 : QMacCocoaViewContainer(0, parent) 89 { 90 // Many Cocoa objects create temporary autorelease objects, 91 // so create a pool to catch them. 92 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 93 94 // Create the NSSearchField, set it on the QCocoaViewContainer. 95 NSSearchField *search = [[NSSearchField alloc] init]; 96 setCocoaView(search); 97 98 // Use a Qt menu for the search field menu. 99 QMenu *qtMenu = createMenu(this); 100 NSMenu *nsMenu = qtMenu->macMenu(0); 101 [[search cell] setSearchMenuTemplate:nsMenu]; 102 103 // Release our reference, since our super class takes ownership and we 104 // don't need it anymore. 105 [search release]; 106 107 // Clean up our pool as we no longer need it. 108 [pool release]; 109 } 110 \endcode 111*/ 112 113QT_BEGIN_NAMESPACE 114 115class QMacCocoaViewContainerPrivate : public QWidgetPrivate 116{ 117 QT_IGNORE_DEPRECATIONS(Q_DECLARE_PUBLIC(QMacCocoaViewContainer)) 118public: 119 NSView *nsview; 120 QMacCocoaViewContainerPrivate(); 121 ~QMacCocoaViewContainerPrivate(); 122}; 123 124QMacCocoaViewContainerPrivate::QMacCocoaViewContainerPrivate() 125 : nsview(0) 126{ 127} 128 129QMacCocoaViewContainerPrivate::~QMacCocoaViewContainerPrivate() 130{ 131 [nsview release]; 132} 133 134/*! 135 Create a new QMacCocoaViewContainer using the NSView pointer in 136 the \a view with parent, \a parent. QMacCocoaViewContainer will 137 retain the \a view. 138 139*/ 140QMacCocoaViewContainer::QMacCocoaViewContainer(NSView *view, QWidget *parent) 141 : QWidget(*new QMacCocoaViewContainerPrivate, parent, {}) 142{ 143 // Ensures that we have a QWindow, even if we're not a top level widget 144 setAttribute(Qt::WA_NativeWindow); 145 146 setCocoaView(view); 147} 148 149/*! 150 Destroy the QMacCocoaViewContainer and release the wrapped view. 151*/ 152QMacCocoaViewContainer::~QMacCocoaViewContainer() 153{ 154 155} 156 157/*! 158 Returns the NSView that has been set on this container. 159*/ 160NSView *QMacCocoaViewContainer::cocoaView() const 161{ 162 Q_D(const QMacCocoaViewContainer); 163 return d->nsview; 164} 165 166/*! 167 Sets \a view as the NSView to contain and retains it. If this 168 container already had a view set, it will release the previously set view. 169*/ 170void QMacCocoaViewContainer::setCocoaView(NSView *view) 171{ 172 Q_D(QMacCocoaViewContainer); 173 NSView *oldView = d->nsview; 174 [view retain]; 175 d->nsview = view; 176 177 // Get rid of QWindow completely, and re-create a new vanilla one, which 178 // we will then re-configure to be a foreign window. 179 destroy(); 180 create(); 181 182 // Can't use QWindow::fromWinId() here due to QWidget controlling its 183 // QWindow, and can't use QWidget::createWindowContainer() due to the 184 // QMacCocoaViewContainer class being public API instead of a factory 185 // function. 186 QWindow *window = windowHandle(); 187 window->destroy(); 188 qt_window_private(window)->create(false, WId(view)); 189 Q_ASSERT(window->handle()); 190 191 [oldView release]; 192 193 // The QWindow::destroy()) call above will explicitly hide this widget. 194 // Clear the hidden state here so it can be implicitly shown again. 195 setAttribute(Qt::WA_WState_Hidden, false); 196 197} 198 199QT_END_NAMESPACE 200