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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtdeclarative/src/quick/items/qquickwindowmodule.cpp