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
45 QT_BEGIN_NAMESPACE
46
attachedObject(const QMetaObject * type,QObject * object,bool create=false)47 static 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 *>(qmlAttachedPropertiesObject(object, func, create));
53 }
54
findAttachedParent(const QMetaObject * type,QObject * object)55 static 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, parent);
63 if (attached)
64 return attached;
65
66 QQuickPopup *popup = qobject_cast<QQuickPopup *>(parent->parent());
67 if (popup)
68 return attachedObject(type, popup);
69
70 parent = parent->parentItem();
71 }
72
73 // fallback to item's window
74 QQuickAttachedObject *attached = attachedObject(type, 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, 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 *>(window->parent());
88 if (parentWindow) {
89 QQuickAttachedObject *attached = attachedObject(type, 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, engine, true);
103 engine->setProperty(name, QVariant::fromValue(attached));
104 }
105 return attached;
106 }
107 }
108
109 return nullptr;
110 }
111
findAttachedChildren(const QMetaObject * type,QObject * object)112 static 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 *>(child);
125 if (childWindow) {
126 QQuickAttachedObject *attached = attachedObject(type, 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, child);
138 if (attached)
139 children += attached;
140 else
141 children += findAttachedChildren(type, child);
142 }
143 }
144
145 return children;
146 }
147
findAttachedItem(QObject * parent)148 static QQuickItem *findAttachedItem(QObject *parent)
149 {
150 QQuickItem *item = qobject_cast<QQuickItem *>(parent);
151 if (!item) {
152 QQuickPopup *popup = qobject_cast<QQuickPopup *>(parent);
153 if (popup)
154 item = popup->popupItem();
155 }
156 return item;
157 }
158
159 class QQuickAttachedObjectPrivate : public QObjectPrivate, public QQuickItemChangeListener
160 {
161 Q_DECLARE_PUBLIC(QQuickAttachedObject)
162
163 public:
get(QQuickAttachedObject * attachedObject)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
attachTo(QObject * object)179 void QQuickAttachedObjectPrivate::attachTo(QObject *object)
180 {
181 QQuickItem *item = findAttachedItem(object);
182 if (item) {
183 connect(item, &QQuickItem::windowChanged, this, &QQuickAttachedObjectPrivate::itemWindowChanged);
184 QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Parent);
185 }
186 }
187
detachFrom(QObject * object)188 void QQuickAttachedObjectPrivate::detachFrom(QObject *object)
189 {
190 QQuickItem *item = findAttachedItem(object);
191 if (item) {
192 disconnect(item, &QQuickItem::windowChanged, this, &QQuickAttachedObjectPrivate::itemWindowChanged);
193 QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Parent);
194 }
195 }
196
itemWindowChanged(QQuickWindow * window)197 void QQuickAttachedObjectPrivate::itemWindowChanged(QQuickWindow *window)
198 {
199 Q_Q(QQuickAttachedObject);
200 QQuickAttachedObject *attachedParent = nullptr;
201 QQuickItem *item = qobject_cast<QQuickItem *>(q->sender());
202 if (item)
203 attachedParent = findAttachedParent(q->metaObject(), item);
204 if (!attachedParent)
205 attachedParent = attachedObject(q->metaObject(), window);
206 q->setAttachedParent(attachedParent);
207 }
208
itemParentChanged(QQuickItem * item,QQuickItem * parent)209 void QQuickAttachedObjectPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent)
210 {
211 Q_Q(QQuickAttachedObject);
212 Q_UNUSED(parent);
213 q->setAttachedParent(findAttachedParent(q->metaObject(), item));
214 }
215
QQuickAttachedObject(QObject * parent)216 QQuickAttachedObject::QQuickAttachedObject(QObject *parent)
217 : QObject(*(new QQuickAttachedObjectPrivate), parent)
218 {
219 Q_D(QQuickAttachedObject);
220 d->attachTo(parent);
221 }
222
~QQuickAttachedObject()223 QQuickAttachedObject::~QQuickAttachedObject()
224 {
225 Q_D(QQuickAttachedObject);
226 d->detachFrom(parent());
227 setAttachedParent(nullptr);
228 }
229
attachedChildren() const230 QList<QQuickAttachedObject *> QQuickAttachedObject::attachedChildren() const
231 {
232 Q_D(const QQuickAttachedObject);
233 return d->attachedChildren;
234 }
235
attachedParent() const236 QQuickAttachedObject *QQuickAttachedObject::attachedParent() const
237 {
238 Q_D(const QQuickAttachedObject);
239 return d->attachedParent;
240 }
241
setAttachedParent(QQuickAttachedObject * parent)242 void 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(d->attachedParent)->attachedChildren.removeOne(this);
251 d->attachedParent = parent;
252 if (parent)
253 QQuickAttachedObjectPrivate::get(parent)->attachedChildren.append(this);
254 attachedParentChange(parent, oldParent);
255 }
256
init()257 void QQuickAttachedObject::init()
258 {
259 QQuickAttachedObject *attachedParent = findAttachedParent(metaObject(), parent());
260 if (attachedParent)
261 setAttachedParent(attachedParent);
262
263 const QList<QQuickAttachedObject *> attachedChildren = findAttachedChildren(metaObject(), parent());
264 for (QQuickAttachedObject *child : attachedChildren)
265 child->setAttachedParent(this);
266 }
267
attachedParentChange(QQuickAttachedObject * newParent,QQuickAttachedObject * oldParent)268 void QQuickAttachedObject::attachedParentChange(QQuickAttachedObject *newParent, QQuickAttachedObject *oldParent)
269 {
270 Q_UNUSED(newParent);
271 Q_UNUSED(oldParent);
272 }
273
274 QT_END_NAMESPACE
275