1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qquickattachedobject_p.h"
38
39#include <QtCore/qpointer.h>
40#include <QtQuick/qquickwindow.h>
41#include <QtQuick/private/qquickitem_p.h>
42#include <QtQuick/private/qquickitemchangelistener_p.h>
43#include <QtQuickTemplates2/private/qquickpopup_p.h>
44
45QT_BEGIN_NAMESPACE
46
47static QQuickAttachedObject *attachedObject(const QMetaObject *type, QObject *object, bool create = false)
48{
49 if (!object)
50 return nullptr;
51 auto func = qmlAttachedPropertiesFunction(object, type);
52 return qobject_cast<QQuickAttachedObject *>(object: qmlAttachedPropertiesObject(object, func, create));
53}
54
55static QQuickAttachedObject *findAttachedParent(const QMetaObject *type, QObject *object)
56{
57 QQuickItem *item = qobject_cast<QQuickItem *>(object);
58 if (item) {
59 // lookup parent items and popups
60 QQuickItem *parent = item->parentItem();
61 while (parent) {
62 QQuickAttachedObject *attached = attachedObject(type, object: parent);
63 if (attached)
64 return attached;
65
66 QQuickPopup *popup = qobject_cast<QQuickPopup *>(object: parent->parent());
67 if (popup)
68 return attachedObject(type, object: popup);
69
70 parent = parent->parentItem();
71 }
72
73 // fallback to item's window
74 QQuickAttachedObject *attached = attachedObject(type, object: item->window());
75 if (attached)
76 return attached;
77 } else {
78 // lookup popup's window
79 QQuickPopup *popup = qobject_cast<QQuickPopup *>(object);
80 if (popup)
81 return attachedObject(type, object: popup->popupItem()->window());
82 }
83
84 // lookup parent window
85 QQuickWindow *window = qobject_cast<QQuickWindow *>(object);
86 if (window) {
87 QQuickWindow *parentWindow = qobject_cast<QQuickWindow *>(object: window->parent());
88 if (parentWindow) {
89 QQuickAttachedObject *attached = attachedObject(type, object: window);
90 if (attached)
91 return attached;
92 }
93 }
94
95 // fallback to engine (global)
96 if (object) {
97 QQmlEngine *engine = qmlEngine(object);
98 if (engine) {
99 QByteArray name = QByteArray("_q_") + type->className();
100 QQuickAttachedObject *attached = engine->property(name).value<QQuickAttachedObject *>();
101 if (!attached) {
102 attached = attachedObject(type, object: engine, create: true);
103 engine->setProperty(name, value: QVariant::fromValue(value: attached));
104 }
105 return attached;
106 }
107 }
108
109 return nullptr;
110}
111
112static QList<QQuickAttachedObject *> findAttachedChildren(const QMetaObject *type, QObject *object)
113{
114 QList<QQuickAttachedObject *> children;
115
116 QQuickItem *item = qobject_cast<QQuickItem *>(object);
117 if (!item) {
118 QQuickWindow *window = qobject_cast<QQuickWindow *>(object);
119 if (window) {
120 item = window->contentItem();
121
122 const auto &windowChildren = window->children();
123 for (QObject *child : windowChildren) {
124 QQuickWindow *childWindow = qobject_cast<QQuickWindow *>(object: child);
125 if (childWindow) {
126 QQuickAttachedObject *attached = attachedObject(type, object: childWindow);
127 if (attached)
128 children += attached;
129 }
130 }
131 }
132 }
133
134 if (item) {
135 const auto childItems = item->childItems();
136 for (QQuickItem *child : childItems) {
137 QQuickAttachedObject *attached = attachedObject(type, object: child);
138 if (attached)
139 children += attached;
140 else
141 children += findAttachedChildren(type, object: child);
142 }
143 }
144
145 return children;
146}
147
148static QQuickItem *findAttachedItem(QObject *parent)
149{
150 QQuickItem *item = qobject_cast<QQuickItem *>(object: parent);
151 if (!item) {
152 QQuickPopup *popup = qobject_cast<QQuickPopup *>(object: parent);
153 if (popup)
154 item = popup->popupItem();
155 }
156 return item;
157}
158
159class QQuickAttachedObjectPrivate : public QObjectPrivate, public QQuickItemChangeListener
160{
161 Q_DECLARE_PUBLIC(QQuickAttachedObject)
162
163public:
164 static QQuickAttachedObjectPrivate *get(QQuickAttachedObject *attachedObject)
165 {
166 return attachedObject->d_func();
167 }
168
169 void attachTo(QObject *object);
170 void detachFrom(QObject *object);
171
172 void itemWindowChanged(QQuickWindow *window);
173 void itemParentChanged(QQuickItem *item, QQuickItem *parent) override;
174
175 QList<QQuickAttachedObject *> attachedChildren;
176 QPointer<QQuickAttachedObject> attachedParent;
177};
178
179void QQuickAttachedObjectPrivate::attachTo(QObject *object)
180{
181 QQuickItem *item = findAttachedItem(parent: object);
182 if (item) {
183 connect(sender: item, signal: &QQuickItem::windowChanged, receiverPrivate: this, slot: &QQuickAttachedObjectPrivate::itemWindowChanged);
184 QQuickItemPrivate::get(item)->addItemChangeListener(listener: this, types: QQuickItemPrivate::Parent);
185 }
186}
187
188void QQuickAttachedObjectPrivate::detachFrom(QObject *object)
189{
190 QQuickItem *item = findAttachedItem(parent: object);
191 if (item) {
192 disconnect(sender: item, signal: &QQuickItem::windowChanged, receiverPrivate: this, slot: &QQuickAttachedObjectPrivate::itemWindowChanged);
193 QQuickItemPrivate::get(item)->removeItemChangeListener(this, types: QQuickItemPrivate::Parent);
194 }
195}
196
197void QQuickAttachedObjectPrivate::itemWindowChanged(QQuickWindow *window)
198{
199 Q_Q(QQuickAttachedObject);
200 QQuickAttachedObject *attachedParent = nullptr;
201 QQuickItem *item = qobject_cast<QQuickItem *>(object: q->sender());
202 if (item)
203 attachedParent = findAttachedParent(type: q->metaObject(), object: item);
204 if (!attachedParent)
205 attachedParent = attachedObject(type: q->metaObject(), object: window);
206 q->setAttachedParent(attachedParent);
207}
208
209void QQuickAttachedObjectPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent)
210{
211 Q_Q(QQuickAttachedObject);
212 Q_UNUSED(parent);
213 q->setAttachedParent(findAttachedParent(type: q->metaObject(), object: item));
214}
215
216QQuickAttachedObject::QQuickAttachedObject(QObject *parent)
217 : QObject(*(new QQuickAttachedObjectPrivate), parent)
218{
219 Q_D(QQuickAttachedObject);
220 d->attachTo(object: parent);
221}
222
223QQuickAttachedObject::~QQuickAttachedObject()
224{
225 Q_D(QQuickAttachedObject);
226 d->detachFrom(object: parent());
227 setAttachedParent(nullptr);
228}
229
230QList<QQuickAttachedObject *> QQuickAttachedObject::attachedChildren() const
231{
232 Q_D(const QQuickAttachedObject);
233 return d->attachedChildren;
234}
235
236QQuickAttachedObject *QQuickAttachedObject::attachedParent() const
237{
238 Q_D(const QQuickAttachedObject);
239 return d->attachedParent;
240}
241
242void QQuickAttachedObject::setAttachedParent(QQuickAttachedObject *parent)
243{
244 Q_D(QQuickAttachedObject);
245 if (d->attachedParent == parent)
246 return;
247
248 QQuickAttachedObject *oldParent = d->attachedParent;
249 if (d->attachedParent)
250 QQuickAttachedObjectPrivate::get(attachedObject: d->attachedParent)->attachedChildren.removeOne(t: this);
251 d->attachedParent = parent;
252 if (parent)
253 QQuickAttachedObjectPrivate::get(attachedObject: parent)->attachedChildren.append(t: this);
254 attachedParentChange(newParent: parent, oldParent);
255}
256
257void QQuickAttachedObject::init()
258{
259 QQuickAttachedObject *attachedParent = findAttachedParent(type: metaObject(), object: parent());
260 if (attachedParent)
261 setAttachedParent(attachedParent);
262
263 const QList<QQuickAttachedObject *> attachedChildren = findAttachedChildren(type: metaObject(), object: parent());
264 for (QQuickAttachedObject *child : attachedChildren)
265 child->setAttachedParent(this);
266}
267
268void QQuickAttachedObject::attachedParentChange(QQuickAttachedObject *newParent, QQuickAttachedObject *oldParent)
269{
270 Q_UNUSED(newParent);
271 Q_UNUSED(oldParent);
272}
273
274QT_END_NAMESPACE
275

source code of qtquickcontrols2/src/quickcontrols2/qquickattachedobject.cpp