Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwindowcontainer.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6#include "qwidget_p.h"
8#include <QtGui/qwindow.h>
9#include <QtGui/private/qwindow_p.h>
10#include <QtGui/private/qguiapplication_p.h>
11#include <qpa/qplatformintegration.h>
12#include <QDebug>
13
14#if QT_CONFIG(mdiarea)
15#include <QMdiSubWindow>
16#endif
17#include <QAbstractScrollArea>
18#include <QPainter>
19
20#include <QtCore/qpointer.h>
21
23
24using namespace Qt::StringLiterals;
25
27{
28public:
29 Q_DECLARE_PUBLIC(QWindowContainer)
30
32 : window(nullptr)
33 , usesNativeWidgets(false)
34 {
35 }
36
38
41 if (wc)
42 return wc->d_func();
43 return nullptr;
44 }
45
48 if (!q->isWindow() && (q->geometry().bottom() <= 0 || q->geometry().right() <= 0))
49 /* Qt (e.g. QSplitter) sometimes prefer to hide a widget by *not* calling
50 setVisible(false). This is often done by setting its coordinates to a sufficiently
51 negative value so that its clipped outside the parent. Since a QWindow is not clipped
52 to widgets in general, it needs to be dealt with as a special case.
53 */
55 else if (usesNativeWidgets)
57 else
59 }
60
62 {
63 if (window->parent() == nullptr)
64 return;
67 return;
68 if (q->internalWinId()) {
69 // Allow use native widgets if the window container is already a native widget
70 usesNativeWidgets = true;
71 return;
72 }
73 bool nativeWidgetSet = false;
74 QWidget *p = q->parentWidget();
75 while (p) {
76 if (false
77#if QT_CONFIG(mdiarea)
78 || qobject_cast<QMdiSubWindow *>(p) != 0
79#endif
80#if QT_CONFIG(scrollarea)
82#endif
83 ) {
84 q->winId();
85 nativeWidgetSet = true;
86 break;
87 }
88 p = p->parentWidget();
89 }
91 }
92
95 QWidget *p = q;
96 while (p) {
98 d->createExtra();
99 d->extra->hasWindowContainer = true;
100 p = p->parentWidget();
101 }
102 }
103
104 bool isStillAnOrphan() const {
105 return window->parent() == &fakeParent;
106 }
107
110
112};
113
114
115
116/*!
117 \fn QWidget *QWidget::createWindowContainer(QWindow *window, QWidget *parent, Qt::WindowFlags flags);
118
119 Creates a QWidget that makes it possible to embed \a window into
120 a QWidget-based application.
121
122 The window container is created as a child of \a parent and with
123 window flags \a flags.
124
125 Once the window has been embedded into the container, the
126 container will control the window's geometry and
127 visibility. Explicit calls to QWindow::setGeometry(),
128 QWindow::show() or QWindow::hide() on an embedded window is not
129 recommended.
130
131 The container takes over ownership of \a window. The window can
132 be removed from the window container with a call to
133 QWindow::setParent().
134
135 The window container is attached as a native child window to the
136 toplevel window it is a child of. When a window container is used
137 as a child of a QAbstractScrollArea or QMdiArea, it will
138 create a \l {Native Widgets vs Alien Widgets} {native window} for
139 every widget in its parent chain to allow for proper stacking and
140 clipping in this use case. Creating a native window for the window
141 container also allows for proper stacking and clipping. This must
142 be done before showing the window container. Applications with
143 many native child windows may suffer from performance issues.
144
145 The window container has a number of known limitations:
146
147 \list
148
149 \li Stacking order; The embedded window will stack on top of the
150 widget hierarchy as an opaque box. The stacking order of multiple
151 overlapping window container instances is undefined.
152
153 \li Rendering Integration; The window container does not interoperate
154 with QGraphicsProxyWidget, QWidget::render() or similar functionality.
155
156 \li Focus Handling; It is possible to let the window container
157 instance have any focus policy and it will delegate focus to the
158 window via a call to QWindow::requestActivate(). However,
159 returning to the normal focus chain from the QWindow instance will
160 be up to the QWindow instance implementation itself. Also, whether
161 QWindow::requestActivate() actually gives the window focus, is
162 platform dependent.
163
164 Since 6.8, if embedding a Qt Quick based window, tab presses will
165 transition in and out of the embedded QML window, allowing focus to move
166 to the next or previous focusable object in the window container chain.
167
168 \li Using many window container instances in a QWidget-based
169 application can greatly hurt the overall performance of the
170 application.
171
172 \li Since 6.7, if \a window belongs to a widget (that is, \a window
173 was received from calling \l windowHandle()), no container will be
174 created. Instead, this function will return the widget itself, after
175 being reparented to \l parent. Since no container will be created,
176 \a flags will be ignored. In other words, if \a window belongs to
177 a widget, consider just reparenting that widget to \a parent instead
178 of using this function.
179
180 \endlist
181 */
182
183QWidget *QWidget::createWindowContainer(QWindow *window, QWidget *parent, Qt::WindowFlags flags)
184{
185 // Embedding a QWidget in a window container doesn't make sense,
186 // and has various issues in practice, so just return the widget
187 // itself.
188 if (auto *widgetWindow = qobject_cast<QWidgetWindow *>(window)) {
189 QWidget *widget = widgetWindow->widget();
190 if (flags != Qt::WindowFlags()) {
191 qWarning() << window << "refers to a widget:" << widget
192 << "WindowFlags" << flags << "will be ignored.";
193 }
194 widget->setParent(parent);
195 return widget;
196 }
197 return new QWindowContainer(window, parent, flags);
198}
199
200/*!
201 \internal
202 */
203
204QWindowContainer::QWindowContainer(QWindow *embeddedWindow, QWidget *parent, Qt::WindowFlags flags)
205 : QWidget(*new QWindowContainerPrivate, parent, flags)
206{
207 Q_D(QWindowContainer);
208 if (Q_UNLIKELY(!embeddedWindow)) {
209 qWarning("QWindowContainer: embedded window cannot be null");
210 return;
211 }
212
213 d->window = embeddedWindow;
214 d->window->installEventFilter(this);
215
216 QString windowName = d->window->objectName();
217 if (windowName.isEmpty())
218 windowName = QString::fromUtf8(d->window->metaObject()->className());
219 d->fakeParent.setObjectName(windowName + "ContainerFakeParent"_L1);
220
221 d->window->setParent(&d->fakeParent);
222 d->window->parent()->installEventFilter(this);
223 d->window->setFlag(Qt::SubWindow);
224
225 setAcceptDrops(true);
226
227 connect(containedWindow(), &QWindow::minimumHeightChanged, this, &QWindowContainer::updateGeometry);
228 connect(containedWindow(), &QWindow::minimumWidthChanged, this, &QWindowContainer::updateGeometry);
229}
230
231QWindow *QWindowContainer::containedWindow() const
232{
233 Q_D(const QWindowContainer);
234 return d->window;
235}
236
237/*!
238 \internal
239 */
240
241QWindowContainer::~QWindowContainer()
242{
243 Q_D(QWindowContainer);
244
245 // Call destroy() explicitly first. The dtor would do this too, but
246 // QEvent::PlatformSurface delivery relies on virtuals. Getting
247 // SurfaceAboutToBeDestroyed can be essential for OpenGL, Vulkan, etc.
248 // QWindow subclasses in particular. Keep these working.
249 if (d->window) {
250 d->window->removeEventFilter(this);
251 d->window->destroy();
252 }
253
254 delete d->window;
255}
256
257/*!
258 \internal
259 */
260
261bool QWindowContainer::eventFilter(QObject *o, QEvent *e)
262{
263 Q_D(QWindowContainer);
264 if (!d->window)
265 return false;
266
267 if (e->type() == QEvent::ChildRemoved) {
268 QChildEvent *ce = static_cast<QChildEvent *>(e);
269 if (ce->child() == d->window) {
270 o->removeEventFilter(this);
271 d->window->removeEventFilter(this);
272 d->window = nullptr;
273 }
274 } else if (e->type() == QEvent::FocusIn) {
275 if (o == d->window)
276 setFocus(Qt::ActiveWindowFocusReason);
277 }
278 return false;
279}
280
281/*!
282 \internal
283 */
284
285bool QWindowContainer::event(QEvent *e)
286{
287 Q_D(QWindowContainer);
288 if (!d->window)
289 return QWidget::event(e);
290
291 QEvent::Type type = e->type();
292 switch (type) {
293 // The only thing we are interested in is making sure our sizes stay
294 // in sync, so do a catch-all case.
295 case QEvent::Resize:
296 d->updateGeometry();
297 break;
298 case QEvent::Move:
299 d->updateGeometry();
300 break;
301 case QEvent::PolishRequest:
302 d->updateGeometry();
303 break;
304 case QEvent::Show:
305 d->updateUsesNativeWidgets();
306 if (d->isStillAnOrphan()) {
307 d->window->parent()->removeEventFilter(this);
308 d->window->setParent(d->usesNativeWidgets
309 ? windowHandle()
310 : window()->windowHandle());
311 d->fakeParent.destroy();
312 if (d->window->parent())
313 d->window->parent()->installEventFilter(this);
314 }
315 if (d->window->parent()) {
316 d->markParentChain();
317 d->window->show();
318 }
319 break;
320 case QEvent::Hide:
321 if (d->window->parent())
322 d->window->hide();
323 break;
324 case QEvent::FocusIn:
325 if (d->window->parent()) {
326 if (QGuiApplication::focusWindow() != d->window) {
327 QFocusEvent *event = static_cast<QFocusEvent *>(e);
328 const auto reason = event->reason();
329 QWindowPrivate::FocusTarget target = QWindowPrivate::FocusTarget::Current;
330 if (reason == Qt::TabFocusReason)
331 target = QWindowPrivate::FocusTarget::First;
332 else if (reason == Qt::BacktabFocusReason)
333 target = QWindowPrivate::FocusTarget::Last;
334 qt_window_private(d->window)->setFocusToTarget(target, reason);
335 d->window->requestActivate();
336 }
337 }
338 break;
339#if QT_CONFIG(draganddrop)
340 case QEvent::Drop:
341 case QEvent::DragMove:
342 case QEvent::DragLeave:
343 QCoreApplication::sendEvent(d->window, e);
344 return e->isAccepted();
345 case QEvent::DragEnter:
346 // Don't reject drag events for the entire widget when one
347 // item rejects the drag enter
348 QCoreApplication::sendEvent(d->window, e);
349 e->accept();
350 return true;
351#endif
352
353 case QEvent::Paint:
354 {
355 static bool needsPunch = !QGuiApplicationPrivate::platformIntegration()->hasCapability(
356 QPlatformIntegration::TopStackedNativeChildWindows);
357 if (needsPunch) {
358 QPainter p(this);
359 p.setCompositionMode(QPainter::CompositionMode_Source);
360 p.fillRect(rect(), Qt::transparent);
361 }
362 break;
363 }
364
365 default:
366 break;
367 }
368
369 return QWidget::event(e);
370}
371
372QSize QWindowContainer::minimumSizeHint() const
373{
374 return containedWindow() ? containedWindow()->minimumSize() : QSize(0, 0);
375}
376
377typedef void (*qwindowcontainer_traverse_callback)(QWidget *parent);
379{
380 const QObjectList &children = parent->children();
381 for (int i=0; i<children.size(); ++i) {
382 QWidget *w = qobject_cast<QWidget *>(children.at(i));
383 if (w) {
384 QWidgetPrivate *wd = static_cast<QWidgetPrivate *>(QWidgetPrivate::get(w));
385 if (wd->extra && wd->extra->hasWindowContainer)
386 callback(w);
387 }
388 }
389}
390
391void QWindowContainer::toplevelAboutToBeDestroyed(QWidget *parent)
392{
393 if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
394 if (d->window->parent())
395 d->window->parent()->removeEventFilter(parent);
396 d->window->setParent(&d->fakeParent);
397 d->window->parent()->installEventFilter(parent);
398 }
399 qwindowcontainer_traverse(parent, toplevelAboutToBeDestroyed);
400}
401
402void QWindowContainer::parentWasChanged(QWidget *parent)
403{
404 if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
405 if (d->window->parent()) {
406 d->updateUsesNativeWidgets();
407 d->markParentChain();
408 QWidget *toplevel = d->usesNativeWidgets ? parent : parent->window();
409 if (!toplevel->windowHandle()) {
410 QWidgetPrivate *tld = static_cast<QWidgetPrivate *>(QWidgetPrivate::get(toplevel));
411 tld->createTLExtra();
412 tld->createTLSysExtra();
413 Q_ASSERT(toplevel->windowHandle());
414 }
415 d->window->parent()->removeEventFilter(parent);
416 d->window->setParent(toplevel->windowHandle());
417 toplevel->windowHandle()->installEventFilter(parent);
418 d->fakeParent.destroy();
419 d->updateGeometry();
420 }
421 }
422 qwindowcontainer_traverse(parent, parentWasChanged);
423}
424
425void QWindowContainer::parentWasMoved(QWidget *parent)
426{
427 if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
428 if (!d->window)
429 return;
430 else if (d->window->parent())
431 d->updateGeometry();
432 }
433 qwindowcontainer_traverse(parent, parentWasMoved);
434}
435
436void QWindowContainer::parentWasRaised(QWidget *parent)
437{
438 if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
439 if (!d->window)
440 return;
441 else if (d->window->parent())
442 d->window->raise();
443 }
444 qwindowcontainer_traverse(parent, parentWasRaised);
445}
446
447void QWindowContainer::parentWasLowered(QWidget *parent)
448{
449 if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
450 if (!d->window)
451 return;
452 else if (d->window->parent())
453 d->window->lower();
454 }
455 qwindowcontainer_traverse(parent, parentWasLowered);
456}
457
458QT_END_NAMESPACE
459
460#include "moc_qwindowcontainer_p.cpp"
void(* qwindowcontainer_traverse_callback)(QWidget *parent)
static void qwindowcontainer_traverse(QWidget *parent, qwindowcontainer_traverse_callback callback)