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

source code of qtdeclarative/src/quicktemplates/qquickstackelement.cpp