Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquickwindowcontainer.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 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
5
6#include <QtQuick/qquickrendercontrol.h>
7
8#include <QtQuick/private/qquickitem_p.h>
9#include <QtQuick/private/qquickrectangle_p.h>
10#include <QtQuick/private/qquickwindowmodule_p.h>
11#include <QtQuick/private/qquickimplicitsizeitem_p_p.h>
12
14
15Q_LOGGING_CATEGORY(lcWindowContainer, "qt.quick.window.container")
16
17using namespace Qt::StringLiterals;
18
102{
103 Q_DECLARE_PUBLIC(QQuickWindowContainer)
104protected:
105 bool transformChanged(QQuickItem *transformedItem) override;
106
107public:
108 QWindow *window = nullptr;
110};
111
126{
128
129 qCDebug(lcWindowContainer).verbosity(1) << "Creating window container"
130 << this << "with parent" << parent << "and" << containerMode;
131
132 d->containerMode = containerMode;
133
135
136 connect(this, &QQuickItem::windowChanged,
137 this, &QQuickWindowContainer::parentWindowChanged);
138
139 if (lcWindowContainer().isDebugEnabled()) {
140 auto *debugRectangle = new QQuickRectangle(this);
141 debugRectangle->setColor(QColor(255, 0, 255, 20));
142 auto *border = debugRectangle->border();
143 border->setColor(Qt::magenta);
144 border->setWidth(1.0);
145 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(debugRectangle);
146 rectPrivate->anchors()->setFill(this);
147 }
148}
149
151{
152 Q_D(const QQuickWindowContainer);
153 qCDebug(lcWindowContainer) << "Destructing window container" << this;
154
155 disconnect(this);
156 if (d->window) {
157 auto ownership = QJSEngine::objectOwnership(d->window);
158 qCDebug(lcWindowContainer) << "Contained window" << d->window
159 << "has" << (ownership == QQmlEngine::JavaScriptOwnership ?
160 "JavaScript" : "C++") << "ownership";
161 if (ownership == QQmlEngine::JavaScriptOwnership) {
162 delete d->window;
163 } else {
164 d->window->destroy();
165 d->window->setParent(nullptr);
166 }
167 }
168}
169
171{
172 Q_D(const QQuickWindowContainer);
173 qCDebug(lcWindowContainer) << "Destroying" << d->window
174 << "with platform window" << (d->window ? d->window->handle() : nullptr);
175 if (d->window)
176 d->window->destroy();
177}
178
180{
181 qCDebug(lcWindowContainer) << "Class begin for" << this;
182
184}
185
187{
188 Q_D(const QQuickWindowContainer);
189
190 qCDebug(lcWindowContainer) << "Component completed for" << this;
192
193 if (d->window)
194 initializeContainedWindow();
195}
196
198{
199 Q_D(const QQuickWindowContainer);
200 return d->window;
201}
202
204{
205 qCDebug(lcWindowContainer) << "Setting contained window for" << this << "to" << window;
206
208
209 if (window == d->window)
210 return;
211
212 if (auto *previousWindow = d->window) {
213 qCDebug(lcWindowContainer) << "Decoupling container from" << d->window;
214 previousWindow->disconnect(this);
215 previousWindow->removeEventFilter(this);
216 previousWindow->setParent(nullptr);
217 }
218
219 d->window = window;
220
221 if (d->window) {
222 if (d->containerMode == ItemControlsWindow) {
223 if (auto *quickWindow = qobject_cast<QQuickWindowQmlImpl*>(d->window)) {
224 // Make sure the Window reflects the window container as its visual parent
225 quickWindow->setVisualParent(this);
226 }
227 }
228
229 // When the window controls the container, we need to reflect any changes
230 // in the window back to the container, so they stay in sync. And when the
231 // container controls the window, we still want to reflect width/height as
232 // new implicit size, and override any other changes with the item state.
233 connect(d->window, &QWindow::xChanged, this, &QQuickWindowContainer::windowUpdated);
234 connect(d->window, &QWindow::yChanged, this, &QQuickWindowContainer::windowUpdated);
235 connect(d->window, &QWindow::widthChanged, this, &QQuickWindowContainer::windowUpdated);
236 connect(d->window, &QWindow::heightChanged, this, &QQuickWindowContainer::windowUpdated);
237 connect(d->window, &QWindow::visibleChanged, this, &QQuickWindowContainer::windowUpdated);
238
239 connect(d->window, &QObject::destroyed, this, &QQuickWindowContainer::windowDestroyed);
240
241 d->window->installEventFilter(this);
242
243 if (d->componentComplete)
244 initializeContainedWindow();
245 } else {
246 // Reset state based on not having a window
247 syncWindowToItem();
248 }
249
251}
252
253void QQuickWindowContainer::initializeContainedWindow()
254{
255 Q_D(const QQuickWindowContainer);
256 Q_ASSERT(d->componentComplete);
257 Q_ASSERT(d->window);
258
259 qCDebug(lcWindowContainer) << "Doing initial sync between" << d->window << "and" << this;
260
261 syncWindowToItem();
262 polish();
263}
264
266{
267 if (transform.isRotating()) {
268 // FIXME: Can we keep more here?
270 }
271
272 return transform;
273}
274
275void QQuickWindowContainer::syncWindowToItem()
276{
277 Q_D(const QQuickWindowContainer);
278
279 const auto windowGeometry = d->window ? d->window->geometry() : QRect();
280
281 qCDebug(lcWindowContainer) << "Syncing window state from" << d->window
282 << "with geometry" << windowGeometry << "to" << this
283 << "with mode" << d->containerMode;
284
285 const auto transform = sanitizeTransform(d->windowToItemTransform());
286
287 // The window might have a larger size than the item's natural
288 // size, if there's a scale applied somewhere in the hierarchy.
289 auto itemSize = d->window ? transform.mapRect(windowGeometry).size()
290 : QSize();
291
292 if (d->containerMode == WindowControlsItem) {
293 // When the Window controls the window container the position is
294 // set up front, when creating the window container, and from that
295 // point on set exclusively via the window container, so we skip
296 // setting the position here, and only set the size.
297 setSize(itemSize);
298 setVisible(d->window ? d->window->isVisible() : false);
299 } else {
300 // Position defined by item, so don't sync from window
301 // Visible defined by item, so don't sync from window
302 setImplicitWidth(itemSize.width());
303 setImplicitHeight(itemSize.height());
304 }
305}
306
315{
317
318 qCDebug(lcWindowContainer) << "Propagating" << this << "state"
319 << "to" << d->window;
320
321 auto *parentWindow = window();
322
323 // FIXME: If we are part of a QQuickWidget, we have a QQuickRenderControl,
324 // and should look up the parent window via that, and apply the offset we
325 // get to the item transform below. But at the moment it's not possible
326 // to observe changes to the offset, which is critical to support this
327 // for child windows.
328
329 if (!d->window || !parentWindow)
330 return;
331
332 if (d->window->parent() != parentWindow) {
333 qCDebug(lcWindowContainer) << "Updating window parent to" << parentWindow;
334 d->window->setParent(parentWindow);
335 }
336
337 auto transform = sanitizeTransform(d->itemToWindowTransform());
338
339 // Find the window's geometry, based on the item's bounding rect,
340 // mapped to the scene. The mapping includes any x/y position set
341 // on the item itself, as well as any transforms applied to the item
342 // or its ancestor (scale, translation).
343 const QRectF itemSceneRect = transform.mapRect(boundingRect());
344 // FIXME: Rounding to a QRect here means we'll have some jitter or off
345 // placement when the underlying item is not on a integer coordinate.
346 QRect windowGeometry = itemSceneRect.toRect();
347 if (windowGeometry != d->window->geometry()) {
348 QRectF itemRect(position(), size());
349 qCDebug(lcWindowContainer) << "Updating window geometry to" << windowGeometry
350 << "based on item rect" << itemRect << "and scene rect" << itemSceneRect;
351 d->window->setGeometry(windowGeometry);
352 }
353
354 // Clip the container to its own and ancestor clip rects, by setting
355 // a mask on the window. This does not necessarily clip native windows,
356 // as QWindow::setMask() is not guaranteed to visually clip the window,
357 // only to mask input, but in most cases we should be good. For the
358 // cases where this fails, we can potentially use an intermediate window
359 // as parent of the contained window, if the platform allows clipping
360 // child windows to parent window geometry. We do not want to resize the
361 // contained window, as that will just fill the content into a smaller
362 // area.
363 const auto clipMask = [&]{
364 if (clipRect() == boundingRect())
365 return QRect();
366
367 // The clip rect has all the relevant transforms applied to it,
368 // except for the item's own scale. As the mask is in window
369 // local coordinates in the possibly scaled window, we need
370 // to apply the scale manually.
371 auto scaleTransform = QTransform::fromScale(transform.m11(), transform.m22());
372 auto rect = scaleTransform.mapRect(clipRect()).toRect();
373
374 // An empty clip rect means clip away everything, while for a
375 // window, an empty mask means mask nothing. Fake the former
376 // by setting a mask outside of the window's bounds. We have
377 // to do this check after rounding the clip rect to a QRect.
378 // FIXME: Verify this works on all platforms
379 if (rect.isEmpty())
380 return QRect(-1, -1, 1, 1);
381
382 return rect;
383 }();
384
385 if (clipMask != d->window->mask().boundingRect()) {
386 qCDebug(lcWindowContainer) << "Updating window clip mask to" << clipMask
387 << "based on clip rect" << clipRect();
388 d->window->setMask(clipMask);
389 }
390
391 // FIXME: Opacity support. Need to calculate effective opacity ourselves,
392 // and there doesn't seem to be any existing observer for opacity changes.
393 // Not all platforms implement opacity for child windows yet.
394
395 // FIXME: If a scale is applied to the item or its parents, we end up
396 // with a bigger item, and window, but we don't translate the scale to
397 // an increase device-pixel-ratio of the window. As a result, the window
398 // will likely just render more content, instead of the same content at
399 // a potentially higher density.
400
401 if (d->window->isVisible() != isVisible()) {
402 qCDebug(lcWindowContainer) << "Updating window visibility"
403 << "based on item visible" << isVisible();
404 d->window->setVisible(isVisible());
405 }
406}
407
420{
422
424 if (viewport == this)
425 break;
426
427 if (viewport->flags().testFlag(QQuickItem::ItemClipsChildrenToShape)) {
428 // FIXME: This fails to take into account viewports that override clipRect()
429 const auto mappedViewportRect = mapRectFromItem(viewport, viewport->boundingRect());
430 rect = mappedViewportRect.intersected(rect);
431 }
432
433 if (viewport->viewportItem() == viewport)
434 break; // Content item returns itself as viewport
435 }
436
437 return rect;
438}
439
440// ----------------------- Window updates -----------------------
441
450void QQuickWindowContainer::windowUpdated()
451{
452 Q_D(const QQuickWindowContainer);
453
454 if (lcWindowContainer().isDebugEnabled()) {
455 auto metaMethod = sender()->metaObject()->method(senderSignalIndex());
456 auto signalName = QString::fromUtf8(metaMethod.name());
457 qCDebug(lcWindowContainer).noquote() << d->window << signalName;
458 }
459
460 syncWindowToItem();
461
462 if (d->containerMode == ItemControlsWindow) {
463 qCDebug(lcWindowContainer) << "Overriding window state by polishing";
464 // Ideally we'd always call ensurePolished() here, to synchronously
465 // override the window state ASAP, rather than wait for polish to
466 // trigger it asynchronously, but due to QWindowPrivate::setVisible
467 // emitting visibleChanged before updating the platform window, we
468 // end up applying our override temporarily, only to have QWindowPrivate
469 // follow up with the original change to the platform window.
470 if (d->window->isVisible() != isVisible())
471 polish();
472 else
473 ensurePolished();
474 }
475}
476
478{
479 Q_D(const QQuickWindowContainer);
480 Q_ASSERT(object == d->window);
481
482 if (event->type() == QEvent::PlatformSurface) {
483 auto type = static_cast<QPlatformSurfaceEvent*>(event)->surfaceEventType();
485 qCDebug(lcWindowContainer) << "Surface created for" << object;
486 syncWindowToItem();
487 // The surface creation has already resulted in the native window
488 // being added to its parent, on top of all other windows. We need
489 // to do a synchronous re-stacking of the windows here, to avoid
490 // leaving the window in the wrong position while waiting for the
491 // asynchronous callback to QQuickWindow::polishItems().
492 if (auto *quickWindow = qobject_cast<QQuickWindow*>(window()))
493 QQuickWindowPrivate::get(quickWindow)->updateChildWindowStackingOrder();
494 }
495 }
496
498}
499
500void QQuickWindowContainer::windowDestroyed()
501{
503 qCDebug(lcWindowContainer) << "Window" << (void*)d->window << "destroyed";
504
505 d->window->removeEventFilter(this);
506 d->window = nullptr;
507
508 syncWindowToItem(); // Reset state based on not having a window
510}
511
512// ----------------------- Item updates -----------------------
513
519void QQuickWindowContainer::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
520{
521 qCDebug(lcWindowContainer) << this << "geometry changed from"
522 << oldGeometry << "to" << newGeometry;
523
524 QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
525 if (newGeometry.isValid())
526 polish();
527}
528
535{
536 switch (change) {
538 qCDebug(lcWindowContainer) << "Visible changed for" << this << "to" << isVisible();
539 polish();
540 break;
541 default:
542 break;
543 }
544
546}
547
553void QQuickWindowContainer::parentWindowChanged(QQuickWindow *parentWindow)
554{
555 qCDebug(lcWindowContainer) << this << "parent window changed to" << parentWindow;
556
558
559 if (!parentWindow) {
560 // We have been removed from the window we were part of,
561 // possibly because the window is going away. We need to
562 // make sure the contained window is no longer a child of
563 // former window, as otherwise it will be wiped out along
564 // with it. We can't wait for updatePolish() to do that
565 // as polish has no effect when an item is not part of a
566 // window.
567 if (d->window) {
568 // The window should already be destroyed from the
569 // call to releaseResources(), which is part of the
570 // removal of an item from a scene, but just in case
571 // we do it here as well.
572 d->window->destroy();
573
574 d->window->setParent(nullptr);
575 }
576 } else {
577 polish();
578 }
579}
580
582{
584
585 if (this->window) {
586 auto *transformedItemPrivate = QQuickItemPrivate::get(transformedItem);
587 qCDebug(lcWindowContainer) << "Transform changed for" << transformedItem
588 << "with dirty state" << transformedItemPrivate->dirtyToString();
589
590 if (transformedItemPrivate->dirtyAttributes
592 // For some reason scale transforms, which result in the window
593 // being resized, end up with the window lagging a frame or two
594 // behind the item. Polish synchronously instead, to mitigate
595 // this, even if it may result in the opposite situation.
596 q->ensurePolished();
597 } else {
598 q->polish();
599 }
600 }
601
602 return QQuickItemPrivate::transformChanged(transformedItem);
603}
604
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
\inmodule QtCore
Definition qcoreevent.h:45
@ PlatformSurface
Definition qcoreevent.h:278
static ObjectOwnership objectOwnership(QObject *)
Returns the ownership of object.
@ JavaScriptOwnership
Definition qjsengine.h:281
\inmodule QtCore
Definition qobject.h:103
int senderSignalIndex() const
Definition qobject.cpp:2700
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
QObject * sender() const
Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; othe...
Definition qobject.cpp:2658
virtual bool eventFilter(QObject *watched, QEvent *event)
Filters events if this object has been installed as an event filter for the watched object.
Definition qobject.cpp:1555
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
The QPlatformSurfaceEvent class is used to notify about native platform surface events....
Definition qevent.h:531
virtual void classBegin()=0
Invoked after class creation, but before any properties have been set.
virtual void componentComplete()=0
Invoked after the root component that caused this instantiation has completed construction.
virtual bool transformChanged(QQuickItem *transformedItem)
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
void setSize(const QSizeF &size)
QQuickItem * viewportItem() const
If the \l ItemObservesViewport flag is set, returns the nearest parent with the \l ItemIsViewport fla...
void setFlag(Flag flag, bool enabled=true)
Enables the specified flag for this item if enabled is true; if enabled is false, the flag is disable...
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
bool isVisible() const
virtual QRectF boundingRect() const
Returns the extents of the item in its own coordinate system: a rectangle from {0,...
QSizeF size() const
QRectF mapRectFromItem(const QQuickItem *item, const QRectF &rect) const
Maps the given rect in item's coordinate system to the equivalent rectangular area within this item's...
void setVisible(bool)
virtual void itemChange(ItemChange, const ItemChangeData &)
Called when change occurs for this item.
QQuickItem * parent
\qmlproperty Item QtQuick::Item::parent This property holds the visual parent of the item.
Definition qquickitem.h:67
void setImplicitHeight(qreal)
QPointF position() const
ItemChange
Used in conjunction with QQuickItem::itemChange() to notify the item about certain types of changes.
Definition qquickitem.h:144
@ ItemVisibleHasChanged
Definition qquickitem.h:148
@ ItemObservesViewport
Definition qquickitem.h:138
@ ItemClipsChildrenToShape
Definition qquickitem.h:130
void setImplicitWidth(qreal)
void polish()
Schedules a polish event for this item.
\qmltype WindowContainer \inqmlmodule QtQuick\inherits Item
QQuickWindowContainer::ContainerMode containerMode
bool transformChanged(QQuickItem *transformedItem) override
void setContainedWindow(QWindow *window)
QQuickWindowContainer(QQuickItem *parent=nullptr, ContainerMode containerMode=ItemControlsWindow)
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &) override
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
void classBegin() override
\reimp Derived classes should call the base class method before adding their own action to perform at...
Q_SIGNAL void containedWindowChanged(QWindow *window)
QRectF clipRect() const override
void releaseResources() override
This function is called when an item should release graphics resources which are not already managed ...
bool eventFilter(QObject *object, QEvent *event) override
Filters events if this object has been installed as an event filter for the watched object.
static QQuickWindowPrivate * get(QQuickWindow *c)
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr bool isValid() const noexcept
Returns true if the rectangle is valid, otherwise returns false.
Definition qrect.h:666
\inmodule QtCore\reentrant
Definition qrect.h:30
\inmodule QtCore
Definition qsize.h:25
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
static QTransform fromScale(qreal dx, qreal dy)
Creates a matrix which corresponds to a scaling of sx horizontally and sy vertically.
static QTransform fromTranslate(qreal dx, qreal dy)
Creates a matrix which corresponds to a translation of dx along the x axis and dy along the y axis.
\inmodule QtGui
Definition qwindow.h:63
void heightChanged(int arg)
void xChanged(int arg)
void visibleChanged(bool arg)
void yChanged(int arg)
void widthChanged(int arg)
rect
[4]
Combined button and popup list for selecting options.
Definition qcompare.h:63
@ magenta
Definition qnamespace.h:39
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLuint object
[3]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLenum GLsizei GLsizei GLsizei GLint border
GLenum type
struct _cl_event * event
GLuint GLenum GLenum transform
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
static QTransform sanitizeTransform(const QTransform &transform)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
myObject disconnect()
[26]
view viewport() -> scroll(dx, dy, deviceRect)
aWidget window() -> setWindowTitle("New Window Title")
[2]
\inmodule QtQuick
Definition qquickitem.h:159