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 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | #if QT_CONFIG(quick_viewtransitions) |
20 | static 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 | |
29 | class QQuickStackIncubator : public QQmlIncubator |
30 | { |
31 | public: |
32 | QQuickStackIncubator(QQuickStackElement *element) |
33 | : QQmlIncubator(Synchronous), |
34 | element(element) |
35 | { |
36 | } |
37 | |
38 | protected: |
39 | void setInitialState(QObject *object) override |
40 | { |
41 | auto privIncubator = QQmlIncubatorPrivate::get(incubator: this); |
42 | element->incubate(object, requiredProperties: privIncubator->requiredProperties()); |
43 | } |
44 | |
45 | private: |
46 | QQuickStackElement *element; |
47 | }; |
48 | |
49 | QQuickStackElement::QQuickStackElement() |
50 | #if QT_CONFIG(quick_viewtransitions) |
51 | : QQuickItemViewTransitionableItem(nullptr) |
52 | #endif |
53 | { |
54 | } |
55 | |
56 | QQuickStackElement::~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 | |
93 | QQuickStackElement *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 | |
110 | QQuickStackElement *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 | |
130 | QQuickStackElement *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 | |
155 | bool 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 | |
185 | void 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 | |
195 | void 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 | |
237 | void 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 | |
250 | void 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 | |
263 | void 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 | |
296 | void 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) |
312 | void QQuickStackElement::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget) |
313 | { |
314 | if (transitioner) |
315 | transitioner->transitionNextReposition(item: this, type, isTarget: asTarget); |
316 | } |
317 | |
318 | bool 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 | |
337 | void QQuickStackElement::startTransition(QQuickItemViewTransitioner *transitioner, QQuickStackView::Status status) |
338 | { |
339 | setStatus(status); |
340 | if (transitioner) |
341 | QQuickItemViewTransitionableItem::startTransition(transitioner, index); |
342 | } |
343 | |
344 | void QQuickStackElement::completeTransition(QQuickTransition *quickTransition) |
345 | { |
346 | QQuickItemViewTransitionableItem::completeTransition(quickTransition); |
347 | } |
348 | #endif |
349 | |
350 | void QQuickStackElement::itemDestroyed(QQuickItem *) |
351 | { |
352 | #if QT_CONFIG(quick_viewtransitions) |
353 | item = nullptr; |
354 | #endif |
355 | } |
356 | |
357 | QT_END_NAMESPACE |
358 |
Definitions
Learn Advanced QML with KDAB
Find out more