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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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