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
49QT_BEGIN_NAMESPACE
50
51static QQuickStackViewAttached *attachedStackObject(QQuickStackElement *element)
52{
53 QQuickStackViewAttached *attached = qobject_cast<QQuickStackViewAttached *>(object: qmlAttachedPropertiesObject<QQuickStackView>(obj: element->item, create: false));
54 if (attached)
55 QQuickStackViewAttachedPrivate::get(attached)->element = element;
56 return attached;
57}
58
59class QQuickStackIncubator : public QQmlIncubator
60{
61public:
62 QQuickStackIncubator(QQuickStackElement *element)
63 : QQmlIncubator(Synchronous),
64 element(element)
65 {
66 }
67
68protected:
69 void setInitialState(QObject *object) override { element->incubate(object); }
70
71private:
72 QQuickStackElement *element;
73};
74
75QQuickStackElement::QQuickStackElement()
76 : QQuickItemViewTransitionableItem(nullptr)
77{
78}
79
80QQuickStackElement::~QQuickStackElement()
81{
82 if (item)
83 QQuickItemPrivate::get(item)->removeItemChangeListener(this, types: QQuickItemPrivate::Destroyed);
84
85 if (ownComponent)
86 delete component;
87
88 QQuickStackViewAttached *attached = attachedStackObject(element: 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, parent: nullptr);
105 }
106 }
107 }
108
109 if (attached)
110 emit attached->removed();
111
112 delete context;
113}
114
115QQuickStackElement *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
132QQuickStackElement *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
150bool QQuickStackElement::load(QQuickStackView *parent)
151{
152 setView(parent);
153 if (!item) {
154 ownItem = true;
155
156 if (component->isLoading()) {
157 QObject::connect(sender: component, signal: &QQmlComponent::statusChanged, slot: [this](QQmlComponent::Status status) {
158 if (status == QQmlComponent::Ready)
159 load(parent: view);
160 else if (status == QQmlComponent::Error)
161 QQuickStackViewPrivate::get(view)->warn(error: 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(view: parent)->warn(error: component->errorString().trimmed());
176 } else {
177 initialize();
178 }
179 return item;
180}
181
182void 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
192void 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(listener: this, types: QQuickItemPrivate::Destroyed);
204
205 if (!properties.isUndefined()) {
206 QQmlEngine *engine = qmlEngine(view);
207 Q_ASSERT(engine);
208 QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(e: 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(engine: v4, object: item));
214#if Q_QML_PRIVATE_API_VERSION >= 6
215 RequiredProperties requiredPropertiesCurrentlyNotSupported;
216 QQmlComponentPrivate::setInitialProperties(engine: v4, qmlContext, o: qmlObject, v: ipv, requiredProperties&: requiredPropertiesCurrentlyNotSupported, createdComponent: item);
217#else
218 QQmlComponentPrivate::setInitialProperties(v4, qmlContext, qmlObject, ipv);
219#endif
220 properties.clear();
221 }
222
223 init = true;
224}
225
226void QQuickStackElement::setIndex(int value)
227{
228 if (index == value)
229 return;
230
231 index = value;
232 QQuickStackViewAttached *attached = attachedStackObject(element: this);
233 if (attached)
234 emit attached->indexChanged();
235}
236
237void QQuickStackElement::setView(QQuickStackView *value)
238{
239 if (view == value)
240 return;
241
242 view = value;
243 QQuickStackViewAttached *attached = attachedStackObject(element: this);
244 if (attached)
245 emit attached->viewChanged();
246}
247
248void QQuickStackElement::setStatus(QQuickStackView::Status value)
249{
250 if (status == value)
251 return;
252
253 status = value;
254 QQuickStackViewAttached *attached = attachedStackObject(element: 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
279void QQuickStackElement::setVisible(bool visible)
280{
281 QQuickStackViewAttached *attached = attachedStackObject(element: this);
282 if (!item || (attached && QQuickStackViewAttachedPrivate::get(attached)->explicitVisible))
283 return;
284
285 item->setVisible(visible);
286}
287
288void QQuickStackElement::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
289{
290 if (transitioner)
291 transitioner->transitionNextReposition(item: this, type, isTarget: asTarget);
292}
293
294bool 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(me: 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
313void QQuickStackElement::startTransition(QQuickItemViewTransitioner *transitioner, QQuickStackView::Status status)
314{
315 setStatus(status);
316 if (transitioner)
317 QQuickItemViewTransitionableItem::startTransition(transitioner, index);
318}
319
320void QQuickStackElement::itemDestroyed(QQuickItem *)
321{
322 item = nullptr;
323}
324
325QT_END_NAMESPACE
326

source code of qtquickcontrols2/src/quicktemplates2/qquickstackelement.cpp