1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtQuick3D/qquick3dobject.h>
5#include <QtQuick3D/private/qquick3ditem2d_p.h>
6
7#include <QtQuick/private/qquickitem_p.h>
8#include <QtQuick/private/qsgrenderer_p.h>
9#include <QtQuick/private/qquickwindow_p.h>
10
11#include <QtQuick3DRuntimeRender/private/qssgrenderitem2d_p.h>
12#include "qquick3dnode_p_p.h"
13
14#include <QtGui/rhi/qrhi.h>
15
16QT_BEGIN_NAMESPACE
17
18/*
19internal
20*/
21
22QQuick3DItem2D::QQuick3DItem2D(QQuickItem *item, QQuick3DNode *parent)
23 : QQuick3DNode(*(new QQuick3DNodePrivate(QQuick3DNodePrivate::Type::Item2D)), parent)
24{
25 m_contentItem = new QQuickItem();
26 m_contentItem->setObjectName(QLatin1String("parent of ") + item->objectName()); // for debugging
27 // No size is set for m_contentItem. This is intentional, otherwise item2d anchoring breaks.
28 QQuickItemPrivate::get(item: m_contentItem)->ensureSubsceneDeliveryAgent();
29 QQmlEngine::setObjectOwnership(m_contentItem, QQmlEngine::CppOwnership);
30
31 connect(sender: m_contentItem, signal: &QQuickItem::childrenChanged, context: this, slot: &QQuick3DObject::update);
32 addChildItem(item);
33}
34
35QQuick3DItem2D::~QQuick3DItem2D()
36{
37 delete m_contentItem;
38}
39
40void QQuick3DItem2D::addChildItem(QQuickItem *item)
41{
42 item->setParent(m_contentItem);
43 item->setParentItem(m_contentItem);
44 QQuickItemPrivate::get(item)->addItemChangeListener(listener: this, types: QQuickItemPrivate::ChangeType::Destroyed);
45 connect(sender: item, signal: &QQuickItem::enabledChanged, context: this, slot: &QQuick3DItem2D::updatePicking);
46 connect(sender: item, signal: &QQuickItem::visibleChanged, context: this, slot: &QQuick3DItem2D::updatePicking);
47 m_sourceItems.append(t: item);
48 update();
49}
50void QQuick3DItem2D::removeChildItem(QQuickItem *item)
51{
52 m_sourceItems.removeOne(t: item);
53 if (item)
54 QQuickItemPrivate::get(item)->removeItemChangeListener(this, types: QQuickItemPrivate::ChangeType::Destroyed);
55 if (m_sourceItems.isEmpty())
56 emit allChildrenRemoved();
57 else
58 update();
59}
60
61QQuickItem *QQuick3DItem2D::contentItem() const
62{
63 return m_contentItem;
64}
65
66void QQuick3DItem2D::itemDestroyed(QQuickItem *item)
67{
68 removeChildItem(item);
69}
70
71void QQuick3DItem2D::updatePicking()
72{
73 m_pickingDirty = true;
74 update();
75}
76
77QSSGRenderGraphObject *QQuick3DItem2D::updateSpatialNode(QSSGRenderGraphObject *node)
78{
79 auto *sourceItemPrivate = QQuickItemPrivate::get(item: m_contentItem);
80 QQuickWindow *window = m_contentItem->window();
81
82 if (!window) {
83 const auto &manager = QQuick3DObjectPrivate::get(item: this)->sceneManager;
84 window = manager->window();
85 }
86
87 if (!node) {
88 markAllDirty();
89 node = new QSSGRenderItem2D();
90 }
91
92 QQuick3DNode::updateSpatialNode(node);
93
94 auto itemNode = static_cast<QSSGRenderItem2D *>(node);
95
96 itemNode->m_rootNode = sourceItemPrivate->rootNode();
97 if (!itemNode->m_rootNode) {
98 QQuickWindowPrivate::get(c: window)->updateDirtyNode(m_contentItem);
99 itemNode->m_rootNode = sourceItemPrivate->rootNode();
100 if (!itemNode->m_rootNode) {
101 qWarning() << "Item2D is not initialized. It will not be shown.";
102 return nullptr;
103 }
104 }
105
106 if (m_pickingDirty) {
107 m_pickingDirty = false;
108 bool isPickable = false;
109 for (const auto &item : std::as_const(t&: m_sourceItems)) {
110 // Enable picking for Item2D if any of its child is visible and enabled.
111 if (item->isVisible() && item->isEnabled()) {
112 isPickable = true;
113 break;
114 }
115 }
116 itemNode->setState(state: QSSGRenderNode::LocalState::Pickable, on: isPickable);
117 }
118
119 return node;
120}
121
122void QQuick3DItem2D::markAllDirty()
123{
124 m_pickingDirty = true;
125
126 QQuick3DNode::markAllDirty();
127}
128
129void QQuick3DItem2D::preSync()
130{
131 const auto &manager = QQuick3DObjectPrivate::get(item: this)->sceneManager;
132 auto *sourcePrivate = QQuickItemPrivate::get(item: m_contentItem);
133 auto *window = manager->window();
134 if (m_window != window) {
135 update(); // Just schedule an upate immediately.
136 if (m_window) {
137 disconnect(sender: m_window, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(derefWindow(QObject*)));
138 sourcePrivate->derefWindow();
139 }
140 m_window = window;
141 sourcePrivate->refWindow(window);
142 connect(sender: window, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(derefWindow(QObject*)));
143 sourcePrivate->refFromEffectItem(hide: true);
144 }
145}
146
147static void detachWindow(QQuickItem *item, QObject *win)
148{
149 auto *itemPriv = QQuickItemPrivate::get(item);
150
151 if (win == itemPriv->window) {
152 itemPriv->window = nullptr;
153 itemPriv->windowRefCount = 0;
154
155 itemPriv->prevDirtyItem = nullptr;
156 itemPriv->nextDirtyItem = nullptr;
157 }
158
159 for (auto *child: itemPriv->childItems)
160 detachWindow(item: child, win);
161}
162
163void QQuick3DItem2D::derefWindow(QObject *win)
164{
165 detachWindow(item: m_contentItem, win);
166}
167
168QT_END_NAMESPACE
169

source code of qtquick3d/src/quick3d/qquick3ditem2d.cpp