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 If embedding a Qt Quick based window, Qt will automatically ensure
165 that tab presses will transition in and out of the embedded QML window,
166 allowing focus to move to the next or previous focusable object in the
167 window container chain.
168
169 \li Using many window container instances in a QWidget-based
170 application can greatly hurt the overall performance of the
171 application.
172
173 \endlist
174
175 \note If \a window belongs to a widget (that is, \a window
176 was received from calling \l windowHandle()), no container will be
177 created. Instead, this function will return the widget itself, after
178 being reparented to \l parent. Since no container will be created,
179 \a flags will be ignored. In other words, if \a window belongs to
180 a widget, consider just reparenting that widget to \a parent instead
181 of using this function.
182 */
183
184QWidget *QWidget::createWindowContainer(QWindow *window, QWidget *parent, Qt::WindowFlags flags)
185{
186 // Embedding a QWidget in a window container doesn't make sense,
187 // and has various issues in practice, so just return the widget
188 // itself.
189 if (auto *widgetWindow = qobject_cast<QWidgetWindow *>(window)) {
190 QWidget *widget = widgetWindow->widget();
191 if (flags != Qt::WindowFlags()) {
192 qWarning() << window << "refers to a widget:" << widget
193 << "WindowFlags" << flags << "will be ignored.";
194 }
195 widget->setParent(parent);
196 return widget;
197 }
198 return new QWindowContainer(window, parent, flags);
199}
200
201/*!
202 \internal
203 */
204
205QWindowContainer::QWindowContainer(QWindow *embeddedWindow, QWidget *parent, Qt::WindowFlags flags)
206 : QWidget(*new QWindowContainerPrivate, parent, flags)
207{
208 Q_D(QWindowContainer);
209 if (Q_UNLIKELY(!embeddedWindow)) {
210 qWarning("QWindowContainer: embedded window cannot be null");
211 return;
212 }
213
214 d->window = embeddedWindow;
215 d->window->installEventFilter(this);
216
217 QString windowName = d->window->objectName();
218 if (windowName.isEmpty())
219 windowName = QString::fromUtf8(d->window->metaObject()->className());
220 d->fakeParent.setObjectName(windowName + "ContainerFakeParent"_L1);
221
222 d->window->setParent(&d->fakeParent);
223 d->window->parent()->installEventFilter(this);
224 d->window->setFlag(Qt::SubWindow);
225
226 setAcceptDrops(true);
227
228 connect(containedWindow(), &QWindow::minimumHeightChanged, this, &QWindowContainer::updateGeometry);
229 connect(containedWindow(), &QWindow::minimumWidthChanged, this, &QWindowContainer::updateGeometry);
230}
231
232QWindow *QWindowContainer::containedWindow() const
233{
234 Q_D(const QWindowContainer);
235 return d->window;
236}
237
238/*!
239 \internal
240 */
241
242QWindowContainer::~QWindowContainer()
243{
244 Q_D(QWindowContainer);
245
246 // Call destroy() explicitly first. The dtor would do this too, but
247 // QEvent::PlatformSurface delivery relies on virtuals. Getting
248 // SurfaceAboutToBeDestroyed can be essential for OpenGL, Vulkan, etc.
249 // QWindow subclasses in particular. Keep these working.
250 if (d->window) {
251 d->window->removeEventFilter(this);
252 d->window->destroy();
253 }
254
255 delete d->window;
256}
257
258/*!
259 \internal
260 */
261
262bool QWindowContainer::eventFilter(QObject *o, QEvent *e)
263{
264 Q_D(QWindowContainer);
265 if (!d->window)
266 return false;
267
268 if (e->type() == QEvent::ChildRemoved) {
269 QChildEvent *ce = static_cast<QChildEvent *>(e);
270 if (ce->child() == d->window) {
271 o->removeEventFilter(this);
272 d->window->removeEventFilter(this);
273 d->window = nullptr;
274 }
275 } else if (e->type() == QEvent::FocusIn) {
276 if (o == d->window)
277 setFocus(Qt::ActiveWindowFocusReason);
278 }
279 return false;
280}
281
282/*!
283 \internal
284 */
285
286bool QWindowContainer::event(QEvent *e)
287{
288 Q_D(QWindowContainer);
289 if (!d->window)
290 return QWidget::event(e);
291
292 QEvent::Type type = e->type();
293 switch (type) {
294 // The only thing we are interested in is making sure our sizes stay
295 // in sync, so do a catch-all case.
296 case QEvent::Resize:
297 d->updateGeometry();
298 break;
299 case QEvent::Move:
300 d->updateGeometry();
301 break;
302 case QEvent::PolishRequest:
303 d->updateGeometry();
304 break;
305 case QEvent::Show:
306 d->updateUsesNativeWidgets();
307 if (d->isStillAnOrphan()) {
308 d->window->parent()->removeEventFilter(this);
309 d->window->setParent(d->usesNativeWidgets
310 ? windowHandle()
311 : window()->windowHandle());
312 d->fakeParent.destroy();
313 if (d->window->parent())
314 d->window->parent()->installEventFilter(this);
315 }
316 if (d->window->parent()) {
317 d->markParentChain();
318 d->window->show();
319 }
320 break;
321 case QEvent::Hide:
322 if (d->window->parent())
323 d->window->hide();
324 break;
325 case QEvent::FocusIn:
326 if (d->window->parent()) {
327 if (QGuiApplication::focusWindow() != d->window) {
328 QFocusEvent *event = static_cast<QFocusEvent *>(e);
329 const auto reason = event->reason();
330 QWindowPrivate::FocusTarget target = QWindowPrivate::FocusTarget::Current;
331 if (reason == Qt::TabFocusReason)
332 target = QWindowPrivate::FocusTarget::First;
333 else if (reason == Qt::BacktabFocusReason)
334 target = QWindowPrivate::FocusTarget::Last;
335 qt_window_private(d->window)->setFocusToTarget(target, reason);
336 d->window->requestActivate();
337 }
338 }
339 break;
340#if QT_CONFIG(draganddrop)
341 case QEvent::Drop:
342 case QEvent::DragMove:
343 case QEvent::DragLeave:
344 QCoreApplication::sendEvent(d->window, e);
345 return e->isAccepted();
346 case QEvent::DragEnter:
347 // Don't reject drag events for the entire widget when one
348 // item rejects the drag enter
349 QCoreApplication::sendEvent(d->window, e);
350 e->accept();
351 return true;
352#endif
353
354 case QEvent::Paint:
355 {
356 static bool needsPunch = !QGuiApplicationPrivate::platformIntegration()->hasCapability(
357 QPlatformIntegration::TopStackedNativeChildWindows);
358 if (needsPunch) {
359 QPainter p(this);
360 p.setCompositionMode(QPainter::CompositionMode_Source);
361 p.fillRect(rect(), Qt::transparent);
362 }
363 break;
364 }
365
366 default:
367 break;
368 }
369
370 return QWidget::event(e);
371}
372
373QSize QWindowContainer::minimumSizeHint() const
374{
375 return containedWindow() ? containedWindow()->minimumSize() : QSize(0, 0);
376}
377
378typedef void (*qwindowcontainer_traverse_callback)(QWidget *parent);
380{
381 const QObjectList &children = parent->children();
382 for (int i=0; i<children.size(); ++i) {
383 QWidget *w = qobject_cast<QWidget *>(children.at(i));
384 if (w) {
385 QWidgetPrivate *wd = static_cast<QWidgetPrivate *>(QWidgetPrivate::get(w));
386 if (wd->extra && wd->extra->hasWindowContainer)
387 callback(w);
388 }
389 }
390}
391
392void QWindowContainer::toplevelAboutToBeDestroyed(QWidget *parent)
393{
394 if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
395 if (d->window->parent())
396 d->window->parent()->removeEventFilter(parent);
397 d->window->setParent(&d->fakeParent);
398 d->window->parent()->installEventFilter(parent);
399 }
400 qwindowcontainer_traverse(parent, toplevelAboutToBeDestroyed);
401}
402
403void QWindowContainer::parentWasChanged(QWidget *parent)
404{
405 if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
406 if (d->window->parent()) {
407 d->updateUsesNativeWidgets();
408 d->markParentChain();
409 QWidget *toplevel = d->usesNativeWidgets ? parent : parent->window();
410 if (!toplevel->windowHandle()) {
411 QWidgetPrivate *tld = static_cast<QWidgetPrivate *>(QWidgetPrivate::get(toplevel));
412 tld->createTLExtra();
413 tld->createTLSysExtra();
414 Q_ASSERT(toplevel->windowHandle());
415 }
416 d->window->parent()->removeEventFilter(parent);
417 d->window->setParent(toplevel->windowHandle());
418 toplevel->windowHandle()->installEventFilter(parent);
419 d->fakeParent.destroy();
420 d->updateGeometry();
421 }
422 }
423 qwindowcontainer_traverse(parent, parentWasChanged);
424}
425
426void QWindowContainer::parentWasMoved(QWidget *parent)
427{
428 if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
429 if (!d->window)
430 return;
431 else if (d->window->parent())
432 d->updateGeometry();
433 }
434 qwindowcontainer_traverse(parent, parentWasMoved);
435}
436
437void QWindowContainer::parentWasRaised(QWidget *parent)
438{
439 if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
440 if (!d->window)
441 return;
442 else if (d->window->parent())
443 d->window->raise();
444 }
445 qwindowcontainer_traverse(parent, parentWasRaised);
446}
447
448void QWindowContainer::parentWasLowered(QWidget *parent)
449{
450 if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
451 if (!d->window)
452 return;
453 else if (d->window->parent())
454 d->window->lower();
455 }
456 qwindowcontainer_traverse(parent, parentWasLowered);
457}
458
459QT_END_NAMESPACE
460
461#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)