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