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
qquickwindowmodule.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
9#include "qquickview_p.h"
11#include "qquickitem_p.h"
12#include <QtQuick/QQuickWindow>
13#include <QtCore/QCoreApplication>
14#include <QtQml/QQmlEngine>
15
16#include <private/qguiapplication_p.h>
17#include <private/qqmlengine_p.h>
18#include <private/qv4qobjectwrapper_p.h>
19#include <private/qqmlglobal_p.h>
20#include <qpa/qplatformintegration.h>
21
23
24using namespace Qt::StringLiterals;
25
26Q_STATIC_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient")
27
28QQuickWindowQmlImplPrivate::QQuickWindowQmlImplPrivate() = default;
29
30QQuickWindowQmlImpl::QQuickWindowQmlImpl(QWindow *parent)
31 : QQuickWindowQmlImpl(*(new QQuickWindowQmlImplPrivate), parent)
32{
33}
34
35QQuickWindowQmlImpl::QQuickWindowQmlImpl(QQuickWindowQmlImplPrivate &dd, QWindow *parent)
36 : QQuickWindow(dd, parent)
37{
38 connect(this, &QWindow::visibleChanged, this, [&] {
39 Q_D(QQuickWindowQmlImpl);
40 d->visible = QWindow::isVisible();
41 emit QQuickWindowQmlImpl::visibleChanged(d->visible);
42 });
43 connect(this, &QWindow::visibilityChanged, this, [&]{
44 Q_D(QQuickWindowQmlImpl);
45 // Update the window's actual visibility and turn off visibilityExplicitlySet,
46 // so that future applyWindowVisibility() calls do not apply both window state
47 // and visible state, unless setVisibility() is called again by the user.
48 d->visibility = QWindow::visibility();
49 d->visibilityExplicitlySet = false;
50 emit QQuickWindowQmlImpl::visibilityChanged(d->visibility);
51 });
52
53 connect(this, &QWindow::screenChanged, this, [this]() {
54 Q_D(QQuickWindowQmlImpl);
55 delete d->screenInfo;
56 d->screenInfo = nullptr;
57
58 emit QQuickWindowQmlImpl::screenChanged();
59 });
60
61 // We shadow the x and y properties, so that we can re-map them in case
62 // we have an Item as our visual parent, which will result in creating an
63 // implicit window container that we control. Ensure that signals still work,
64 // and that they reflect the mapped values if a window container is used.
65 QObject::connect(this, &QWindow::xChanged, this, [this] { emit xChanged(x()); });
66 QObject::connect(this, &QWindow::yChanged, this, [this] { emit yChanged(y()); });
67}
68
69QQuickWindowQmlImpl::~QQuickWindowQmlImpl()
70{
71 // Destroy the window while we are still alive, so that any signals
72 // emitted by the destruction can be delivered properly.
73 destroy();
74}
75
76void QQuickWindowQmlImpl::classBegin()
77{
78 Q_D(QQuickWindowQmlImpl);
79 qCDebug(lcQuickWindow) << "Class begin for" << this;
80 d->componentComplete = false;
81
82 QQmlEngine* e = qmlEngine(this);
83
84 QQmlEngine::setContextForObject(contentItem(), e->rootContext());
85
86 //Give QQuickView behavior when created from QML with QQmlApplicationEngine
87 if (QCoreApplication::instance()->property("__qml_using_qqmlapplicationengine") == QVariant(true)) {
88 if (e && !e->incubationController())
89 e->setIncubationController(incubationController());
90 }
91 {
92 // The content item has CppOwnership policy (set in QQuickWindow). Ensure the presence of a JS
93 // wrapper so that the garbage collector can see the policy.
94 QV4::ExecutionEngine *v4 = e->handle();
95 QV4::QObjectWrapper::ensureWrapper(v4, d->contentItem);
96 }
97}
98
99void QQuickWindowQmlImpl::componentComplete()
100{
101 Q_D(QQuickWindowQmlImpl);
102 qCDebug(lcQuickWindow) << "Component completed for" << this;
103 d->componentComplete = true;
104
105 applyVisualParent();
106
107 // Apply automatic transient parent if needed, and opt in to future
108 // parent change events, so we can keep the transient parent in sync.
109 updateTransientParent();
110 d->receiveParentEvents = true;
111
112 applyWindowVisibility();
113
114 // If the transient parent changes, and we've deferred making
115 // the window visible, we need to re-evaluate our decision.
116 connect(this, &QWindow::transientParentChanged,
117 this, &QQuickWindowQmlImpl::applyWindowVisibility);
118}
119
120void QQuickWindowQmlImpl::setVisible(bool visible)
121{
122 Q_D(QQuickWindowQmlImpl);
123 d->visible = visible;
124 d->visibleExplicitlySet = true;
125 if (d->componentComplete)
126 applyWindowVisibility();
127}
128
129void QQuickWindowQmlImpl::setVisibility(Visibility visibility)
130{
131 Q_D(QQuickWindowQmlImpl);
132 d->visibility = visibility;
133 d->visibilityExplicitlySet = true;
134 if (d->componentComplete)
135 applyWindowVisibility();
136}
137
138bool QQuickWindowQmlImpl::event(QEvent *event)
139{
140 Q_D(QQuickWindowQmlImpl);
141
142 if (event->type() == QEvent::ParentWindowChange) {
143 qCDebug(lcQuickWindow) << "Parent of" << this << "changed to" << parent();
144 if (d->visualParent) {
145 // If the window parent changes, and we've deferred making
146 // the window visible, we need to re-evaluate our decision.
147 applyWindowVisibility();
148 } else {
149 QObject::disconnect(d->itemParentWindowChangeListener);
150 updateTransientParent();
151 }
152 }
153 return QQuickWindow::event(event);
154}
155
156/*
157 Update the transient parent of the window based on its
158 QObject parent (Item or Window), unless the user has
159 set an explicit transient parent.
160*/
161void QQuickWindowQmlImpl::updateTransientParent()
162{
163 Q_D(QQuickWindowQmlImpl);
164
165 // We defer updating the transient parent until the component
166 // has been fully completed, and we know whether an explicit
167 // transient parent has been set.
168 if (!d->componentComplete)
169 return;
170
171 // If an explicit transient parent has been set,
172 // we don't want to apply our magic.
173 if (d->transientParentPropertySet)
174 return;
175
176 // Nor if we have a visual parent that makes this a true child window
177 if (d->visualParent)
178 return;
179
180 auto *objectParent = QObject::parent();
181 qCDebug(lcTransient) << "Applying transient parent magic to"
182 << this << "based on object parent" << objectParent << "🪄";
183
184 QWindow *transientParent = nullptr;
185 if (auto *windowParent = qmlobject_cast<QWindow *>(objectParent)) {
186 transientParent = windowParent;
187 } else if (auto *itemParent = qmlobject_cast<QQuickItem *>(objectParent)) {
188 if (!d->itemParentWindowChangeListener) {
189 d->itemParentWindowChangeListener = connect(
190 itemParent, &QQuickItem::windowChanged,
191 this, &QQuickWindowQmlImpl::updateTransientParent);
192 }
193 transientParent = itemParent->window();
194 }
195
196 if (!transientParent) {
197 qCDebug(lcTransient) << "No transient parent resolved from object parent";
198 return;
199 }
200
201 qCDebug(lcTransient) << "Setting" << transientParent << "as transient parent of" << this;
202 setTransientParent(transientParent);
203
204 // We want to keep applying the automatic transient parent
205 d->transientParentPropertySet = false;
206}
207
208void QQuickWindowQmlImpl::applyWindowVisibility()
209{
210 Q_D(QQuickWindowQmlImpl);
211
212 Q_ASSERT(d->componentComplete);
213
214 const bool visible = d->visibilityExplicitlySet
215 ? d->visibility != Hidden : d->visible;
216
217 qCDebug(lcQuickWindow) << "Applying visible" << visible << "for" << this;
218
219 if (visible) {
220 if (d->visualParent) {
221 // Even though we're complete, and have a visual parent set,
222 // we may not be part of a window yet, or we may have been
223 // removed from a window that's going away. Showing this window
224 // now would make it a top level, which is not what we want.
225 if (!QWindow::parent()) {
226 qCDebug(lcQuickWindow) << "Waiting for visual parent to reparent us into a window";
227 // We apply the visibility again on ParentWindowChange
228 return;
229 }
230 } else {
231 // Handle deferred visibility due to possible transient parent
232 auto *itemParent = qmlobject_cast<QQuickItem *>(QObject::parent());
233 if (!d->transientParentPropertySet && itemParent && !itemParent->window()) {
234 qCDebug(lcTransient) << "Waiting for parent" << itemParent << "to resolve"
235 << "its window. Deferring visibility";
236 return;
237 }
238
239 const QWindow *transientParent = QWindow::transientParent();
240 if (transientParent && !transientParentVisible()) {
241 // Defer visibility of this window until the transient parent has
242 // been made visible, or we've get a new transient parent.
243 qCDebug(lcTransient) << "Transient parent" << transientParent
244 << "not visible yet. Deferring visibility";
245
246 // QWindowPrivate::setVisible emits visibleChanged _before_ actually
247 // propagating the visibility to the platform window, so we can't use
248 // a direct connection here, as that would result in showing this
249 // window before the transient parent.
250 connect(transientParent, &QQuickWindow::visibleChanged, this,
251 &QQuickWindowQmlImpl::applyWindowVisibility,
252 Qt::ConnectionType(Qt::QueuedConnection | Qt::SingleShotConnection));
253 return;
254 }
255 }
256 }
257
258 if (d->visibleExplicitlySet && d->visibilityExplicitlySet &&
259 ((d->visibility == Hidden && d->visible) ||
260 (d->visibility > AutomaticVisibility && !d->visible))) {
261 // FIXME: Should we bail out in this case?
262 qmlWarning(this) << "Conflicting properties 'visible' and 'visibility'";
263 }
264
265 if (d->visibility == AutomaticVisibility) {
266 // We're either showing for the first time, with the default
267 // visibility of AutomaticVisibility, or the user has called
268 // setVisibility with AutomaticVisibility at some point, so
269 // apply both window state and visible.
270 if (QWindow::parent() || visualParent())
271 setWindowState(Qt::WindowNoState);
272 else
273 setWindowState(QGuiApplicationPrivate::platformIntegration()->defaultWindowState(flags()));
274 QQuickWindow::setVisible(d->visible);
275 } else if (d->visibilityExplicitlySet) {
276 // We're not AutomaticVisibility, but the user has requested
277 // an explicit visibility, so apply both window state and visible.
278 QQuickWindow::setVisibility(d->visibility);
279 } else {
280 // Our window state should be up to date, so only apply visible
281 QQuickWindow::setVisible(d->visible);
282 }
283}
284
285bool QQuickWindowQmlImpl::transientParentVisible()
286{
287 Q_ASSERT(transientParent());
288 if (!transientParent()->isVisible()) {
289 // handle case where transient parent is offscreen window
290 QWindow *rw = QQuickRenderControl::renderWindowFor(qobject_cast<QQuickWindow*>(transientParent()));
291 return rw && rw->isVisible();
292 }
293 return true;
294}
295
296// -------------------------- Visual Parent ---------------------------
297
298/*!
299 \qmlproperty var QtQuick::Window::parent
300 \since 6.7
301 \internal
302
303 This property holds the visual parent of the window.
304
305 The visual parent can be either another Window, or an Item.
306
307 A window with a visual parent will result in the window becoming a child
308 window of its visual parent, either directly if the visual parent is another
309 Window, or indirectly via the visual parent Item's window.
310
311 Just like QtQuick::Item::parent, the window will be positioned relative to
312 its visual parent.
313
314 The stacking order between sibling Windows follows the document order,
315 just like Items, but can be customized via the Window's \l{QtQuick::Window::z}
316 {z-order} property.
317
318 Setting a visual parent on a Window will take precedence over the
319 \l{QtQuick::Window::transientParent}{transient parent}.
320
321 \sa{Concepts - Visual Parent in Qt Quick}, transientParent
322*/
323
324void QQuickWindowQmlImpl::setVisualParent(QObject *visualParent)
325{
326 Q_D(QQuickWindowQmlImpl);
327 if (visualParent == d->visualParent)
328 return;
329
330 qCDebug(lcQuickWindow) << "Setting visual parent of" << this << "to" << visualParent;
331
332 if (d->visualParent) {
333 // Disconnect from deferred window listener
334 d->visualParent->disconnect(this);
335 }
336
337 d->visualParent = visualParent;
338
339 if (d->componentComplete)
340 applyVisualParent();
341
342 emit visualParentChanged(d->visualParent);
343}
344
345void QQuickWindowQmlImpl::applyVisualParent()
346{
347 Q_D(QQuickWindowQmlImpl);
348 Q_ASSERT(d->componentComplete);
349
350 qCDebug(lcQuickWindow) << "Applying" << this << "visual parent" << d->visualParent;
351
352 if (!d->visualParent) {
353 if (d->windowContainer) {
354 d->windowContainer->setContainedWindow(nullptr);
355 delete std::exchange(d->windowContainer, nullptr);
356 }
357 QQuickWindow::setParent(nullptr);
358 return;
359 }
360
361 QQuickItem *parentItem = nullptr;
362 if ((parentItem = qobject_cast<QQuickItem*>(d->visualParent)))
363 ; // All good, can use directly
364 else if (auto *parentWindow = qobject_cast<QWindow*>(d->visualParent)) {
365 if (auto *parentQuickWindow = qobject_cast<QQuickWindow*>(parentWindow)) {
366 parentItem = parentQuickWindow->contentItem();
367 } else {
368 qmlWarning(this) << "Parenting into non-Quick window. "
369 << "Stacking, position, and destruction must be handled manually";
370 QQuickWindow::setParent(parentWindow); // Try our best
371 return;
372 }
373 }
374
375 if (!parentItem) {
376 qmlWarning(this) << "Unsupported visual parent type"
377 << d->visualParent->metaObject()->className();
378 return;
379 }
380
381 if (!parentItem->window()) {
382 qCDebug(lcQuickWindow) << "No window yet. Deferring.";
383 connect(parentItem, &QQuickItem::windowChanged, this, [this]{
384 qCDebug(lcQuickWindow) << "Got window. Applying deferred visual parent item.";
385 applyVisualParent();
386 }, Qt::SingleShotConnection);
387 return;
388 }
389
390 if (qobject_cast<QQuickWindowContainer*>(d->visualParent)) {
391 qCDebug(lcQuickWindow) << "Visual parent is window container, everything is in order";
392 return;
393 }
394
395 if (!d->windowContainer) {
396 d->windowContainer = new QQuickWindowContainer(parentItem,
397 QQuickWindowContainer::WindowControlsItem);
398 d->windowContainer->setObjectName(objectName() + "Container"_L1);
399
400 auto *objectParent = this->QObject::parent();
401 if (objectParent == parentItem) {
402 // We want to reflect the QML document order of sibling windows in the
403 // resulting stacking order of the windows. We can do so by carefully
404 // using the the information we have about the child object order.
405
406 // We know that the window's object child index is correct in relation
407 // to the other child windows of the parent. Since the window container
408 // is going to represent the window from now on, make the window container
409 // take the window's place in the parent's child object list.
410 auto &objectChildren = QObjectPrivate::get(objectParent)->children;
411 auto windowIndex = objectChildren.indexOf(this);
412 auto containerIndex = objectChildren.indexOf(d->windowContainer);
413 objectChildren.move(containerIndex, windowIndex);
414 containerIndex = windowIndex;
415
416 // The parent's item children are unfortunately managed separately from
417 // the object children. But thanks to the logic above we can use the now
418 // correct object order of the window container in the object children list
419 // to also ensure a correct stacking order between the sibling child items.
420 for (int i = containerIndex + 1; i < objectChildren.size(); ++i) {
421 if (auto *childItem = qobject_cast<QQuickItem*>(objectChildren.at(i))) {
422 qCDebug(lcQuickWindow) << "Stacking" << d->windowContainer
423 << "below" << childItem;
424 d->windowContainer->stackBefore(childItem);
425 break;
426 }
427 }
428 } else {
429 // Having another visual parent than the direct object parent will
430 // mess up the stacking order. This is also the case for normal items.
431 qCDebug(lcQuickWindow) << "Visual parent is not object parent."
432 << "Can not reflect document order as stacking order.";
433 }
434
435 QQmlEngine::setContextForObject(d->windowContainer, qmlContext(this));
436
437 d->windowContainer->classBegin();
438 d->windowContainer->setContainedWindow(this);
439 // Once the window has a window container, all x/y/z changes of
440 // the window will go through the container, and ensure the
441 // correct mapping. But any changes that happened prior to
442 // this have not been mapped yet, so do that now.
443 d->windowContainer->setPosition(position());
444 d->windowContainer->setZ(d->z);
445 d->windowContainer->componentComplete();
446
447 QObject::connect(d->windowContainer, &QQuickItem::zChanged,
448 this, &QQuickWindowQmlImpl::zChanged);
449 } else {
450 d->windowContainer->setParentItem(parentItem);
451 }
452}
453
454QObject *QQuickWindowQmlImpl::visualParent() const
455{
456 Q_D(const QQuickWindowQmlImpl);
457 return d->visualParent;
458}
459
460// We shadow the x and y properties of the Window, so that in case
461// the window has an Item as its visual parent we can re-map the
462// coordinates via the corresponding window container. We need to
463// do this also for the signal emissions, as otherwise the Window's
464// change signals will reflect different values than what we report
465// via the accessors. It would be nicer if this logic was contained
466// in the window container, for example via meta object property
467// interception, but that does not allow intercepting signal emissions.
468
469void QQuickWindowQmlImpl::setX(int x)
470{
471 Q_D(QQuickWindowQmlImpl);
472 if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
473 d->windowContainer->setX(x);
474 else
475 QQuickWindow::setX(x);
476}
477
478int QQuickWindowQmlImpl::x() const
479{
480 Q_D(const QQuickWindowQmlImpl);
481 if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
482 return d->windowContainer->x();
483 else
484 return QQuickWindow::x();
485}
486
487void QQuickWindowQmlImpl::setY(int y)
488{
489 Q_D(QQuickWindowQmlImpl);
490 if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
491 d->windowContainer->setY(y);
492 else
493 QQuickWindow::setY(y);
494}
495
496int QQuickWindowQmlImpl::y() const
497{
498 Q_D(const QQuickWindowQmlImpl);
499 if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
500 return d->windowContainer->y();
501 else
502 return QQuickWindow::y();
503}
504
505/*!
506 \qmlproperty real QtQuick::Window::z
507 \internal
508
509 Sets the stacking order of sibling windows.
510
511 By default the stacking order is 0.
512
513 Windows with a higher stacking value are drawn on top of windows with a
514 lower stacking order. Windows with the same stacking value are drawn
515 bottom up in the order they appear in the QML document.
516
517 \note This property only has an effect for child windows.
518
519 \sa QtQuick::Item::z
520*/
521
522void QQuickWindowQmlImpl::setZ(qreal z)
523{
524 Q_D(QQuickWindowQmlImpl);
525 if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
526 d->windowContainer->setZ(z);
527 else
528 d->z = z;
529}
530
531qreal QQuickWindowQmlImpl::z() const
532{
533 Q_D(const QQuickWindowQmlImpl);
534 if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
535 return d->windowContainer->z();
536 else
537 return d->z;
538}
539
540// --------------------------------------------------------------------
541
542QObject *QQuickWindowQmlImpl::screen() const
543{
544 Q_D(const QQuickWindowQmlImpl);
545 if (!d->screenInfo)
546 d->screenInfo = new QQuickScreenInfo(const_cast<QQuickWindowQmlImpl *>(this), QWindow::screen());
547 return d->screenInfo;
548}
549
550void QQuickWindowQmlImpl::setScreen(QObject *screen)
551{
552 QQuickScreenInfo *screenWrapper = qobject_cast<QQuickScreenInfo *>(screen);
553 QWindow::setScreen(screenWrapper ? screenWrapper->wrappedScreen() : nullptr);
554}
555
556QQuickWindowAttached *QQuickWindowQmlImpl::qmlAttachedProperties(QObject *object)
557{
558 return new QQuickWindowAttached(object);
559}
560
561QT_END_NAMESPACE
562
563#include "moc_qquickwindowmodule_p.cpp"
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")