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 Templates 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 "qquickstackelement_p_p.h"
38 #include "qquickstackview_p_p.h"
39
40 #include <QtQml/qqmlinfo.h>
41 #include <QtQml/qqmlengine.h>
42 #include <QtQml/qqmlcomponent.h>
43 #include <QtQml/qqmlincubator.h>
44 #include <QtQml/private/qv4qobjectwrapper_p.h>
45 #include <QtQml/private/qqmlcomponent_p.h>
46 #include <QtQml/private/qqmlengine_p.h>
47 #include <QtQml/private/qqmlapiversion_p.h>
48
49 QT_BEGIN_NAMESPACE
50
attachedStackObject(QQuickStackElement * element)51 static QQuickStackViewAttached *attachedStackObject(QQuickStackElement *element)
52 {
53 QQuickStackViewAttached *attached = qobject_cast<QQuickStackViewAttached *>(qmlAttachedPropertiesObject<QQuickStackView>(element->item, false));
54 if (attached)
55 QQuickStackViewAttachedPrivate::get(attached)->element = element;
56 return attached;
57 }
58
59 class QQuickStackIncubator : public QQmlIncubator
60 {
61 public:
QQuickStackIncubator(QQuickStackElement * element)62 QQuickStackIncubator(QQuickStackElement *element)
63 : QQmlIncubator(Synchronous),
64 element(element)
65 {
66 }
67
68 protected:
setInitialState(QObject * object)69 void setInitialState(QObject *object) override { element->incubate(object); }
70
71 private:
72 QQuickStackElement *element;
73 };
74
QQuickStackElement()75 QQuickStackElement::QQuickStackElement()
76 : QQuickItemViewTransitionableItem(nullptr)
77 {
78 }
79
~QQuickStackElement()80 QQuickStackElement::~QQuickStackElement()
81 {
82 if (item)
83 QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed);
84
85 if (ownComponent)
86 delete component;
87
88 QQuickStackViewAttached *attached = attachedStackObject(this);
89 if (item) {
90 if (ownItem) {
91 item->setParentItem(nullptr);
92 item->deleteLater();
93 item = nullptr;
94 } else {
95 setVisible(false);
96 if (!widthValid)
97 item->resetWidth();
98 if (!heightValid)
99 item->resetHeight();
100 if (item->parentItem() != originalParent) {
101 item->setParentItem(originalParent);
102 } else {
103 if (attached)
104 QQuickStackViewAttachedPrivate::get(attached)->itemParentChanged(item, nullptr);
105 }
106 }
107 }
108
109 if (attached)
110 emit attached->removed();
111
112 delete context;
113 }
114
fromString(const QString & str,QQuickStackView * view,QString * error)115 QQuickStackElement *QQuickStackElement::fromString(const QString &str, QQuickStackView *view, QString *error)
116 {
117 QUrl url(str);
118 if (!url.isValid()) {
119 *error = QStringLiteral("invalid url: ") + str;
120 return nullptr;
121 }
122
123 if (url.isRelative())
124 url = qmlContext(view)->resolvedUrl(url);
125
126 QQuickStackElement *element = new QQuickStackElement;
127 element->component = new QQmlComponent(qmlEngine(view), url, view);
128 element->ownComponent = true;
129 return element;
130 }
131
fromObject(QObject * object,QQuickStackView * view,QString * error)132 QQuickStackElement *QQuickStackElement::fromObject(QObject *object, QQuickStackView *view, QString *error)
133 {
134 Q_UNUSED(view);
135 QQmlComponent *component = qobject_cast<QQmlComponent *>(object);
136 QQuickItem *item = qobject_cast<QQuickItem *>(object);
137 if (!component && !item) {
138 *error = QQmlMetaType::prettyTypeName(object) + QStringLiteral(" is not supported. Must be Item or Component.");
139 return nullptr;
140 }
141
142 QQuickStackElement *element = new QQuickStackElement;
143 element->component = qobject_cast<QQmlComponent *>(object);
144 element->item = qobject_cast<QQuickItem *>(object);
145 if (element->item)
146 element->originalParent = element->item->parentItem();
147 return element;
148 }
149
load(QQuickStackView * parent)150 bool QQuickStackElement::load(QQuickStackView *parent)
151 {
152 setView(parent);
153 if (!item) {
154 ownItem = true;
155
156 if (component->isLoading()) {
157 QObject::connect(component, &QQmlComponent::statusChanged, [this](QQmlComponent::Status status) {
158 if (status == QQmlComponent::Ready)
159 load(view);
160 else if (status == QQmlComponent::Error)
161 QQuickStackViewPrivate::get(view)->warn(component->errorString().trimmed());
162 });
163 return true;
164 }
165
166 QQmlContext *creationContext = component->creationContext();
167 if (!creationContext)
168 creationContext = qmlContext(parent);
169 context = new QQmlContext(creationContext, parent);
170 context->setContextObject(parent);
171
172 QQuickStackIncubator incubator(this);
173 component->create(incubator, context);
174 if (component->isError())
175 QQuickStackViewPrivate::get(parent)->warn(component->errorString().trimmed());
176 } else {
177 initialize();
178 }
179 return item;
180 }
181
incubate(QObject * object)182 void QQuickStackElement::incubate(QObject *object)
183 {
184 item = qmlobject_cast<QQuickItem *>(object);
185 if (item) {
186 QQmlEngine::setObjectOwnership(item, QQmlEngine::CppOwnership);
187 item->setParent(view);
188 initialize();
189 }
190 }
191
initialize()192 void QQuickStackElement::initialize()
193 {
194 if (!item || init)
195 return;
196
197 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
198 if (!(widthValid = p->widthValid))
199 item->setWidth(view->width());
200 if (!(heightValid = p->heightValid))
201 item->setHeight(view->height());
202 item->setParentItem(view);
203 p->addItemChangeListener(this, QQuickItemPrivate::Destroyed);
204
205 if (!properties.isUndefined()) {
206 QQmlEngine *engine = qmlEngine(view);
207 Q_ASSERT(engine);
208 QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine);
209 Q_ASSERT(v4);
210 QV4::Scope scope(v4);
211 QV4::ScopedValue ipv(scope, properties.value());
212 QV4::Scoped<QV4::QmlContext> qmlContext(scope, qmlCallingContext.value());
213 QV4::ScopedValue qmlObject(scope, QV4::QObjectWrapper::wrap(v4, item));
214 #if Q_QML_PRIVATE_API_VERSION >= 6
215 RequiredProperties requiredPropertiesCurrentlyNotSupported;
216 QQmlComponentPrivate::setInitialProperties(v4, qmlContext, qmlObject, ipv, requiredPropertiesCurrentlyNotSupported, item);
217 #else
218 QQmlComponentPrivate::setInitialProperties(v4, qmlContext, qmlObject, ipv);
219 #endif
220 properties.clear();
221 }
222
223 init = true;
224 }
225
setIndex(int value)226 void QQuickStackElement::setIndex(int value)
227 {
228 if (index == value)
229 return;
230
231 index = value;
232 QQuickStackViewAttached *attached = attachedStackObject(this);
233 if (attached)
234 emit attached->indexChanged();
235 }
236
setView(QQuickStackView * value)237 void QQuickStackElement::setView(QQuickStackView *value)
238 {
239 if (view == value)
240 return;
241
242 view = value;
243 QQuickStackViewAttached *attached = attachedStackObject(this);
244 if (attached)
245 emit attached->viewChanged();
246 }
247
setStatus(QQuickStackView::Status value)248 void QQuickStackElement::setStatus(QQuickStackView::Status value)
249 {
250 if (status == value)
251 return;
252
253 status = value;
254 QQuickStackViewAttached *attached = attachedStackObject(this);
255 if (!attached)
256 return;
257
258 switch (value) {
259 case QQuickStackView::Inactive:
260 emit attached->deactivated();
261 break;
262 case QQuickStackView::Deactivating:
263 emit attached->deactivating();
264 break;
265 case QQuickStackView::Activating:
266 emit attached->activating();
267 break;
268 case QQuickStackView::Active:
269 emit attached->activated();
270 break;
271 default:
272 Q_UNREACHABLE();
273 break;
274 }
275
276 emit attached->statusChanged();
277 }
278
setVisible(bool visible)279 void QQuickStackElement::setVisible(bool visible)
280 {
281 QQuickStackViewAttached *attached = attachedStackObject(this);
282 if (!item || (attached && QQuickStackViewAttachedPrivate::get(attached)->explicitVisible))
283 return;
284
285 item->setVisible(visible);
286 }
287
transitionNextReposition(QQuickItemViewTransitioner * transitioner,QQuickItemViewTransitioner::TransitionType type,bool asTarget)288 void QQuickStackElement::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
289 {
290 if (transitioner)
291 transitioner->transitionNextReposition(this, type, asTarget);
292 }
293
prepareTransition(QQuickItemViewTransitioner * transitioner,const QRectF & viewBounds)294 bool QQuickStackElement::prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds)
295 {
296 if (transitioner) {
297 if (item) {
298 QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
299 // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors
300 if (anchors && (anchors->fill() || anchors->centerIn()))
301 qmlWarning(item) << "StackView has detected conflicting anchors. Transitions may not execute properly.";
302 }
303
304 // TODO: add force argument to QQuickItemViewTransitionableItem::prepareTransition()?
305 nextTransitionToSet = true;
306 nextTransitionFromSet = true;
307 nextTransitionFrom += QPointF(1, 1);
308 return QQuickItemViewTransitionableItem::prepareTransition(transitioner, index, viewBounds);
309 }
310 return false;
311 }
312
startTransition(QQuickItemViewTransitioner * transitioner,QQuickStackView::Status status)313 void QQuickStackElement::startTransition(QQuickItemViewTransitioner *transitioner, QQuickStackView::Status status)
314 {
315 setStatus(status);
316 if (transitioner)
317 QQuickItemViewTransitionableItem::startTransition(transitioner, index);
318 }
319
itemDestroyed(QQuickItem *)320 void QQuickStackElement::itemDestroyed(QQuickItem *)
321 {
322 item = nullptr;
323 }
324
325 QT_END_NAMESPACE
326