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