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 "qquickstackview_p.h"
5#include "qquickstackview_p_p.h"
6#include "qquickstackelement_p_p.h"
7#if QT_CONFIG(quick_viewtransitions)
8#include "qquickstacktransition_p_p.h"
9#endif
10
11#include <QtCore/qscopedvaluerollback.h>
12#include <QtQml/qjsvalue.h>
13#include <QtQml/qqmlengine.h>
14#include <QtQml/qqmlinfo.h>
15
16#include <private/qv4qobjectwrapper_p.h>
17#include <private/qqmlengine_p.h>
18
19QT_BEGIN_NAMESPACE
20
21QQuickStackViewArg::QQuickStackViewArg(QQuickItem *item)
22 : mItem(item)
23{
24}
25
26QQuickStackViewArg::QQuickStackViewArg(const QUrl &url)
27 : mUrl(url)
28{
29}
30
31QQuickStackViewArg::QQuickStackViewArg(QQmlComponent *component)
32 : mComponent(component)
33{
34}
35
36QQuickStackViewArg::QQuickStackViewArg(const QVariantMap &properties)
37 : mProperties(properties)
38{
39}
40
41#ifndef QT_NO_DEBUG_STREAM
42QDebug operator<<(QDebug debug, const QQuickStackViewArg &arg)
43{
44 QDebugStateSaver saver(debug);
45 debug.nospace() << "QQuickStackViewArg("
46 << "mItem=" << arg.mItem
47 << " mComponent=" << arg.mComponent
48 << " mUrl=" << arg.mUrl
49 << ")";
50 return debug;
51}
52#endif
53
54/*!
55 \qmltype StackView
56 \inherits Control
57//! \nativetype QQuickStackView
58 \inqmlmodule QtQuick.Controls
59 \since 5.7
60 \ingroup qtquickcontrols-navigation
61 \ingroup qtquickcontrols-containers
62 \ingroup qtquickcontrols-focusscopes
63 \brief Provides a stack-based navigation model.
64
65 \image qtquickcontrols-stackview-wireframe.png
66
67 StackView can be used with a set of inter-linked information pages. For
68 example, an email application with separate views to list the latest emails,
69 view a specific email, and list/view the attachments. The email list view
70 is pushed onto the stack as users open an email, and popped out as they
71 choose to go back.
72
73 The following snippet demonstrates a simple use case, where the \c mainView
74 is pushed onto and popped out of the stack on relevant button click:
75
76 \qml
77 ApplicationWindow {
78 title: qsTr("Hello World")
79 width: 640
80 height: 480
81 visible: true
82
83 StackView {
84 id: stack
85 initialItem: mainView
86 anchors.fill: parent
87 }
88
89 Component {
90 id: mainView
91
92 Row {
93 spacing: 10
94
95 Button {
96 text: "Push"
97 onClicked: stack.push(mainView)
98 }
99 Button {
100 text: "Pop"
101 enabled: stack.depth > 1
102 onClicked: stack.pop()
103
104 }
105 Text {
106 text: stack.depth
107 }
108 }
109 }
110 }
111 \endqml
112
113 \section1 Using StackView in an Application
114
115 Using StackView in an application is as simple as adding it as a child to
116 a Window. The stack is usually anchored to the edges of the window, except
117 at the top or bottom where it might be anchored to a status bar, or some
118 other similar UI component. The stack can then be used by invoking its
119 navigation methods. The first item to show in the StackView is the one
120 that was assigned to \l initialItem, or the topmost item if \l initialItem
121 is not set.
122
123 \section1 Basic Navigation
124
125 StackView supports three primary navigation operations: push(), pop(), and
126 replace(). These correspond to classic stack operations where "push" adds
127 an item to the top of a stack, "pop" removes the top item from the
128 stack, and "replace" is like a pop followed by a push, which replaces the
129 topmost item with the new item. The topmost item in the stack
130 corresponds to the one that is \l{StackView::currentItem}{currently}
131 visible on screen. Logically, "push" navigates forward or deeper into the
132 application UI, "pop" navigates backward, and "replace" replaces the
133 \l currentItem.
134
135 \section2 Pushing Items
136
137 In the following animation, three \l Label controls are pushed onto a
138 stack view with the \l push() function:
139
140 \image qtquickcontrols-stackview-push.gif
141
142 The stack now contains the following items: \c [A, B, C].
143
144 \note When the stack is empty, a push() operation will not have a
145 transition animation because there is nothing to transition from (typically
146 on application start-up).
147
148 \section2 Popping Items
149
150 Continuing on from the example above, the topmost item on the stack is
151 removed with a call to \l pop():
152
153 \image qtquickcontrols-stackview-pop.gif
154
155 The stack now contains the following items: \c [A, B].
156
157 \note A pop() operation on a stack with depth 1 or 0 does nothing. In such
158 cases, the stack can be emptied using the \l clear() method.
159
160 \section3 Unwinding Items via Pop
161
162 Sometimes, it is necessary to go back more than a single step in the stack.
163 For example, to return to a "main" item or some kind of section item in the
164 application. In such cases, it is possible to specify an item as a
165 parameter for pop(). This is called an "unwind" operation, where the stack
166 unwinds till the specified item. If the item is not found, stack unwinds
167 until it is left with one item, which becomes the \l currentItem. To
168 explicitly unwind to the bottom of the stack, it is recommended to use
169 \l{pop()}{pop(null)}, although any non-existent item will do.
170
171 In the following animation, we unwind the stack to the first item by
172 calling \c pop(null):
173
174 \image qtquickcontrols-stackview-unwind.gif
175
176 The stack now contains a single item: \c [A].
177
178 \section2 Replacing Items
179
180 In the following animation, we \l replace the topmost item with \c D:
181
182 \image qtquickcontrols-stackview-replace.gif
183
184 The stack now contains the following items: \c [A, B, D].
185
186 \section1 Deep Linking
187
188 \e{Deep linking} means launching an application into a particular state. For
189 example, a newspaper application could be launched into showing a
190 particular article, bypassing the topmost item. In terms of StackView, deep
191 linking means the ability to modify the state of the stack, so much so that
192 it is possible to push a set of items to the top of the stack, or to
193 completely reset the stack to a given state.
194
195 The API for deep linking in StackView is the same as for basic navigation.
196 Pushing an array instead of a single item adds all the items in that array
197 to the stack. The transition animation, however, is applied only for the
198 last item in the array. The normal semantics of push() apply for deep
199 linking, that is, it adds whatever is pushed onto the stack.
200
201 \note Only the last item of the array is loaded. The rest of the items are
202 loaded only when needed, either on subsequent calls to pop or on request to
203 get an item using get().
204
205 This gives us the following result, given the stack [A, B, C]:
206
207 \list
208 \li \l{push()}{push([D, E, F])} => [A, B, C, D, E, F] - "push" transition
209 animation between C and F
210 \li \l{replace()}{replace([D, E, F])} => [A, B, D, E, F] - "replace"
211 transition animation between C and F
212 \li \l{clear()} followed by \l{push()}{push([D, E, F])} => [D, E, F] - no
213 transition animation for pushing items as the stack was empty.
214 \endlist
215
216 \section1 Finding Items
217
218 An Item for which the application does not have a reference can be found
219 by calling find(). The method needs a callback function, which is invoked
220 for each item in the stack (starting at the top) until a match is found.
221 If the callback returns \c true, find() stops and returns the matching
222 item, otherwise \c null is returned.
223
224 The code below searches the stack for an item named "order_id" and unwinds
225 to that item.
226
227 \badcode
228 stackView.pop(stackView.find(function(item) {
229 return item.name == "order_id";
230 }));
231 \endcode
232
233 You can also get to an item in the stack using \l {get()}{get(index)}.
234
235 \badcode
236 previousItem = stackView.get(myItem.StackView.index - 1));
237 \endcode
238
239 \section1 Transitions
240
241 For each push or pop operation, different transition animations are applied
242 to entering and exiting items. These animations define how the entering item
243 should animate in, and the exiting item should animate out. The animations
244 can be customized by assigning different \l [QML] {Transition} {Transitions}
245 for the \l pushEnter, \l pushExit, \l popEnter, \l popExit, replaceEnter,
246 and \l replaceExit properties of StackView.
247
248 \note The transition animations affect each others' transitional behavior.
249 Customizing the animation for one and leaving the other may give unexpected
250 results.
251
252 The following snippet defines a simple fade transition for push and pop
253 operations:
254
255 \qml
256 StackView {
257 id: stackview
258 anchors.fill: parent
259
260 pushEnter: Transition {
261 PropertyAnimation {
262 property: "opacity"
263 from: 0
264 to:1
265 duration: 200
266 }
267 }
268 pushExit: Transition {
269 PropertyAnimation {
270 property: "opacity"
271 from: 1
272 to:0
273 duration: 200
274 }
275 }
276 popEnter: Transition {
277 PropertyAnimation {
278 property: "opacity"
279 from: 0
280 to:1
281 duration: 200
282 }
283 }
284 popExit: Transition {
285 PropertyAnimation {
286 property: "opacity"
287 from: 1
288 to:0
289 duration: 200
290 }
291 }
292 }
293 \endqml
294
295 \note Using anchors on the items added to a StackView is not supported.
296 Typically push, pop, and replace transitions animate the position,
297 which is not possible when anchors are applied. Notice that this
298 only applies to the root of the item. Using anchors for its children
299 works as expected.
300
301 \section1 Item Ownership
302
303 StackView only takes ownership of items that it creates itself. This means
304 that any item pushed onto a StackView will never be destroyed by the
305 StackView; only items that StackView creates from \l {Component}{Components}
306 or \l [QML] {url}{URLs} are destroyed by the StackView. To illustrate this,
307 the messages in the example below will only be printed when the StackView
308 is destroyed, not when the items are popped off the stack:
309
310 \qml
311 Component {
312 id: itemComponent
313
314 Item {
315 Component.onDestruction: print("Destroying second item")
316 }
317 }
318
319 StackView {
320 initialItem: Item {
321 Component.onDestruction: print("Destroying initial item")
322 }
323
324 Component.onCompleted: push(itemComponent.createObject(window))
325 }
326 \endqml
327
328 However, both of the items created from the URL and Component in the
329 following example will be destroyed by the StackView when they are popped
330 off of it:
331
332 \qml
333 Component {
334 id: itemComponent
335
336 Item {
337 Component.onDestruction: print("Destroying second item")
338 }
339 }
340
341 StackView {
342 initialItem: "Item1.qml"
343
344 Component.onCompleted: push(itemComponent)
345 }
346 \endqml
347
348 \section1 Size
349
350 StackView does not inherit an implicit size from items that are pushed onto
351 it. This means that using it as the \l {Popup::}{contentItem} of a
352 \l Dialog, for example, will not work as expected:
353
354 \code
355 Dialog {
356 StackView {
357 initialItem: Rectangle {
358 width: 200
359 height: 200
360 color: "salmon"
361 }
362 }
363 }
364 \endcode
365
366 There are several ways to ensure that StackView has a size in this
367 situation:
368
369 \list
370 \li Set \l[QtQuick]{Item::}{implicitWidth} and
371 \l[QtQuick]{Item::}{implicitHeight} on the StackView itself.
372 \li Set \l[QtQuick]{Item::}{implicitWidth} and
373 \l[QtQuick]{Item::}{implicitHeight} on the \l Rectangle.
374 \li Set \l {Popup::}{contentWidth} and \l {Popup::}{contentHeight} on
375 the Dialog.
376 \li Give the Dialog a size.
377 \endlist
378
379 \sa {Customizing StackView}, {Navigating with StackView}, {Navigation Controls},
380 {Container Controls}, {Focus Management in Qt Quick Controls}
381*/
382
383QQuickStackView::QQuickStackView(QQuickItem *parent)
384 : QQuickControl(*(new QQuickStackViewPrivate), parent)
385{
386 setFlag(flag: ItemIsFocusScope);
387
388 Q_D(QQuickStackView);
389 d->setSizePolicy(horizontalPolicy: QLayoutPolicy::Preferred, verticalPolicy: QLayoutPolicy::Preferred);
390}
391
392QQuickStackView::~QQuickStackView()
393{
394 Q_D(QQuickStackView);
395#if QT_CONFIG(quick_viewtransitions)
396 if (d->transitioner) {
397 d->transitioner->setChangeListener(nullptr);
398 delete d->transitioner;
399 }
400#endif
401 qDeleteAll(c: d->removing);
402 qDeleteAll(c: d->removed);
403 qDeleteAll(c: d->elements);
404}
405
406QQuickStackViewAttached *QQuickStackView::qmlAttachedProperties(QObject *object)
407{
408 return new QQuickStackViewAttached(object);
409}
410
411/*!
412 \qmlproperty bool QtQuick.Controls::StackView::busy
413 \readonly
414 This property holds whether a transition is running.
415*/
416bool QQuickStackView::isBusy() const
417{
418 Q_D(const QQuickStackView);
419 return d->busy;
420}
421
422/*!
423 \qmlproperty int QtQuick.Controls::StackView::depth
424 \readonly
425 This property holds the number of items currently pushed onto the stack.
426*/
427int QQuickStackView::depth() const
428{
429 Q_D(const QQuickStackView);
430 return d->elements.size();
431}
432
433/*!
434 \qmlproperty Item QtQuick.Controls::StackView::currentItem
435 \readonly
436 This property holds the current top-most item in the stack.
437*/
438QQuickItem *QQuickStackView::currentItem() const
439{
440 Q_D(const QQuickStackView);
441 return d->currentItem;
442}
443
444/*!
445 \qmlmethod Item QtQuick.Controls::StackView::get(index, behavior)
446
447 Returns the item at position \a index in the stack, or \c null if the index
448 is out of bounds.
449
450 Supported \a behavior values:
451 \value StackView.DontLoad The item is not forced to load (and \c null is returned if not yet loaded).
452 \value StackView.ForceLoad The item is forced to load.
453*/
454QQuickItem *QQuickStackView::get(int index, LoadBehavior behavior)
455{
456 Q_D(QQuickStackView);
457 QQuickStackElement *element = d->elements.value(i: index);
458 if (element) {
459 if (behavior == ForceLoad)
460 element->load(parent: this);
461 return element->item;
462 }
463 return nullptr;
464}
465
466/*!
467 \qmlmethod Item QtQuick.Controls::StackView::find(callback, behavior)
468
469 Search for a specific item inside the stack. The \a callback function is called
470 for each item in the stack (with the item and index as arguments) until the callback
471 function returns \c true. The return value is the item found. For example:
472
473 \code
474 stackView.find(function(item, index) {
475 return item.isTheOne
476 })
477 \endcode
478
479 Supported \a behavior values:
480 \value StackView.DontLoad Unloaded items are skipped (the callback function is not called for them).
481 \value StackView.ForceLoad Unloaded items are forced to load.
482*/
483QQuickItem *QQuickStackView::find(const QJSValue &callback, LoadBehavior behavior)
484{
485 Q_D(QQuickStackView);
486 QJSValue func(callback);
487 QQmlEngine *engine = qmlEngine(this);
488 if (!engine || !func.isCallable()) // TODO: warning?
489 return nullptr;
490
491 for (int i = d->elements.size() - 1; i >= 0; --i) {
492 QQuickStackElement *element = d->elements.at(i);
493 if (behavior == ForceLoad)
494 element->load(parent: this);
495 if (element->item) {
496 QJSValue rv = func.call(args: QJSValueList() << engine->newQObject(object: element->item) << i);
497 if (rv.toBool())
498 return element->item;
499 }
500 }
501
502 return nullptr;
503}
504
505/*!
506 \qmlmethod Item QtQuick.Controls::StackView::push(item, properties, operation)
507
508 Pushes an \a item onto the stack using an optional \a operation, and
509 optionally applies a set of \a properties on the item. The item can be
510 an \l Item, \l Component, or a \l [QML] url. Returns the item that became
511 current.
512
513 StackView creates an instance automatically if the pushed item is a \l Component,
514 or a \l [QML] url, and the instance will be destroyed when it is popped
515 off the stack. See \l {Item Ownership} for more information.
516
517 The optional \a properties argument specifies a map of initial
518 property values for the pushed item. For dynamically created items, these values
519 are applied before the creation is finalized. This is more efficient than setting
520 property values after creation, particularly where large sets of property values
521 are defined, and also allows property bindings to be set up (using \l{Qt::binding}
522 {Qt.binding()}) before the item is created.
523
524 Pushing a single item:
525 \code
526 stackView.push(rect)
527
528 // or with properties:
529 stackView.push(rect, {"color": "red"})
530 \endcode
531
532 Multiple items can be pushed at the same time either by passing them as
533 additional arguments, or as an array. The last item becomes the current
534 item. Each item can be followed by a set of properties to apply.
535
536 Passing a variable amount of arguments:
537 \code
538 stackView.push(rect1, rect2, rect3)
539
540 // or with properties:
541 stackView.push(rect1, {"color": "red"}, rect2, {"color": "green"}, rect3, {"color": "blue"})
542 \endcode
543
544 Pushing an array of items:
545 \code
546 stackView.push([rect1, rect2, rect3])
547
548 // or with properties:
549 stackView.push([rect1, {"color": "red"}, rect2, {"color": "green"}, rect3, {"color": "blue"}])
550 \endcode
551
552 An \a operation can be optionally specified as the last argument. Supported
553 operations:
554
555 \value StackView.Immediate An immediate operation without transitions.
556 \value StackView.PushTransition An operation with push transitions (since QtQuick.Controls 2.1).
557 \value StackView.ReplaceTransition An operation with replace transitions (since QtQuick.Controls 2.1).
558 \value StackView.PopTransition An operation with pop transitions (since QtQuick.Controls 2.1).
559
560 If no operation is provided, \c Immediate will be used if the stack is
561 empty, and \c PushTransition otherwise.
562
563 \note Items that already exist in the stack are not pushed.
564
565 \note If you are \l {The QML script compiler}{compiling QML}, use the
566 strongly-typed \l pushItem or \l pushItems functions instead.
567
568 \sa initialItem, {Pushing Items}
569*/
570void QQuickStackView::push(QQmlV4FunctionPtr args)
571{
572 Q_D(QQuickStackView);
573 const QString operationName = QStringLiteral("push");
574 if (d->modifyingElements) {
575 d->warnOfInterruption(attemptedOperation: operationName);
576 return;
577 }
578
579 QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
580 QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
581 if (args->length() <= 0) {
582 d->warn(QStringLiteral("missing arguments"));
583 args->setReturnValue(QV4::Encode::null());
584 return;
585 }
586
587 QV4::ExecutionEngine *v4 = args->v4engine();
588 QV4::Scope scope(v4);
589
590#if QT_CONFIG(quick_viewtransitions)
591 Operation operation = d->elements.isEmpty() ? Immediate : PushTransition;
592 QV4::ScopedValue lastArg(scope, (*args)[args->length() - 1]);
593 if (lastArg->isInt32())
594 operation = static_cast<Operation>(lastArg->toInt32());
595#endif
596
597 QStringList errors;
598 QList<QQuickStackElement *> elements = d->parseElements(from: 0, args, errors: &errors);
599 // Remove any items that are already in the stack, as they can't be in two places at once.
600 // not using erase_if, as we have to delete the elements first
601 auto removeIt = std::remove_if(first: elements.begin(), last: elements.end(), pred: [&](QQuickStackElement *element) {
602 return element->item && d->findElement(item: element->item);
603 });
604 for (auto it = removeIt, end = elements.end(); it != end; ++it)
605 delete *it;
606 elements.erase(abegin: removeIt, aend: elements.end());
607
608 if (!errors.isEmpty() || elements.isEmpty()) {
609 if (!errors.isEmpty()) {
610 for (const QString &error : std::as_const(t&: errors))
611 d->warn(error);
612 } else {
613 d->warn(QStringLiteral("nothing to push"));
614 }
615 args->setReturnValue(QV4::Encode::null());
616 return;
617 }
618
619#if QT_CONFIG(quick_viewtransitions)
620 QQuickStackElement *exit = nullptr;
621 if (!d->elements.isEmpty())
622 exit = d->elements.top();
623#endif
624
625 int oldDepth = d->elements.size();
626 if (d->pushElements(elements)) {
627 d->depthChange(newDepth: d->elements.size(), oldDepth);
628 QQuickStackElement *enter = d->elements.top();
629#if QT_CONFIG(quick_viewtransitions)
630 d->startTransition(first: QQuickStackTransition::pushEnter(operation, element: enter, view: this),
631 second: QQuickStackTransition::pushExit(operation, element: exit, view: this),
632 immediate: operation == Immediate);
633#endif
634 d->setCurrentItem(enter);
635 }
636
637 if (d->currentItem) {
638 QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine: v4, object: d->currentItem));
639 args->setReturnValue(rv->asReturnedValue());
640 } else {
641 args->setReturnValue(QV4::Encode::null());
642 }
643}
644
645/*!
646 \qmlmethod Item QtQuick.Controls::StackView::pop(item, operation)
647
648 Pops one or more items off the stack. Returns the last item removed from the stack.
649
650 If the \a item argument is specified, all items down to (but not
651 including) \a item will be popped. If \a item is \c null, all
652 items down to (but not including) the first item is popped.
653 If not specified, only the current item is popped.
654
655 \note A pop() operation on a stack with depth 1 or 0 does nothing. In such
656 cases, the stack can be emptied using the \l clear() method.
657
658 \include qquickstackview.qdocinc pop-ownership
659
660 An \a operation can be optionally specified as the last argument. Supported
661 operations:
662
663 \value StackView.Immediate An immediate operation without transitions.
664 \value StackView.PushTransition An operation with push transitions (since QtQuick.Controls 2.1).
665 \value StackView.ReplaceTransition An operation with replace transitions (since QtQuick.Controls 2.1).
666 \value StackView.PopTransition An operation with pop transitions (since QtQuick.Controls 2.1).
667
668 If no operation is provided, \c PopTransition will be used.
669
670 Examples:
671 \code
672 stackView.pop()
673 stackView.pop(someItem, StackView.Immediate)
674 stackView.pop(StackView.Immediate)
675 stackView.pop(null)
676 \endcode
677
678 \note If you are \l {The QML script compiler}{compiling QML}, use the
679 strongly-typed \l popToItem, \l popToIndex or \l popCurrentItem functions
680 instead.
681
682 \sa clear(), {Popping Items}, {Unwinding Items via Pop}
683*/
684void QQuickStackView::pop(QQmlV4FunctionPtr args)
685{
686 Q_D(QQuickStackView);
687 const QString operationName = QStringLiteral("pop");
688 if (d->modifyingElements) {
689 d->warnOfInterruption(attemptedOperation: operationName);
690 args->setReturnValue(QV4::Encode::null());
691 return;
692 }
693
694 QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
695 QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
696 int argc = args->length();
697 if (d->elements.size() <= 1 || argc > 2) {
698 if (argc > 2)
699 d->warn(QStringLiteral("too many arguments"));
700 args->setReturnValue(QV4::Encode::null());
701 return;
702 }
703
704 int oldDepth = d->elements.size();
705 QQuickStackElement *exit = d->elements.pop();
706 QQuickStackElement *enter = d->elements.top();
707
708 QV4::ExecutionEngine *v4 = args->v4engine();
709 QV4::Scope scope(v4);
710
711 if (argc > 0) {
712 QV4::ScopedValue value(scope, (*args)[0]);
713 if (value->isNull()) {
714 enter = d->elements.value(i: 0);
715 } else if (const QV4::QObjectWrapper *o = value->as<QV4::QObjectWrapper>()) {
716 QQuickItem *item = qobject_cast<QQuickItem *>(o: o->object());
717 enter = d->findElement(item);
718 if (!enter) {
719 if (item != d->currentItem)
720 d->warn(QStringLiteral("can't find item to pop: ") + value->toQString());
721 args->setReturnValue(QV4::Encode::null());
722 d->elements.push(t: exit); // restore
723 return;
724 }
725 }
726 }
727
728#if QT_CONFIG(quick_viewtransitions)
729 Operation operation = PopTransition;
730 if (argc > 0) {
731 QV4::ScopedValue lastArg(scope, (*args)[argc - 1]);
732 if (lastArg->isInt32())
733 operation = static_cast<Operation>(lastArg->toInt32());
734 }
735#endif
736
737 QPointer<QQuickItem> previousItem;
738
739 if (d->popElements(element: enter)) {
740 if (exit) {
741 exit->removal = true;
742 d->removing.insert(value: exit);
743 previousItem = exit->item;
744 }
745 d->depthChange(newDepth: d->elements.size(), oldDepth);
746#if QT_CONFIG(quick_viewtransitions)
747 d->startTransition(first: QQuickStackTransition::popExit(operation, element: exit, view: this),
748 second: QQuickStackTransition::popEnter(operation, element: enter, view: this),
749 immediate: operation == Immediate);
750#endif
751 d->setCurrentItem(enter);
752 }
753
754 if (previousItem) {
755 QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine: v4, object: previousItem));
756 args->setReturnValue(rv->asReturnedValue());
757 } else {
758 args->setReturnValue(QV4::Encode::null());
759 }
760}
761
762/*!
763 \qmlmethod Item QtQuick.Controls::StackView::replace(target, item, properties, operation)
764
765 Replaces one or more items on the stack with the specified \a item and
766 optional \a operation, and optionally applies a set of \a properties on the
767 item. The item can be an \l Item, \l Component, or a \l [QML] url.
768 Returns the item that became current.
769
770 \include qquickstackview.qdocinc pop-ownership
771
772 If the \a target argument is specified, all items down to the \a target
773 item will be replaced. If \a target is \c null, all items in the stack
774 will be replaced. If not specified, only the top item will be replaced.
775
776 StackView creates an instance automatically if the replacing item is a \l Component,
777 or a \l [QML] url. The optional \a properties argument specifies a map of initial
778 property values for the replacing item. For dynamically created items, these values
779 are applied before the creation is finalized. This is more efficient than setting
780 property values after creation, particularly where large sets of property values
781 are defined, and also allows property bindings to be set up (using \l{Qt::binding}
782 {Qt.binding()}) before the item is created.
783
784 Replace the top item:
785 \code
786 stackView.replace(rect)
787
788 // or with properties:
789 stackView.replace(rect, {"color": "red"})
790 \endcode
791
792 Multiple items can be replaced at the same time either by passing them as
793 additional arguments, or as an array. Each item can be followed by a set
794 of properties to apply.
795
796 Passing a variable amount of arguments:
797 \code
798 stackView.replace(rect1, rect2, rect3)
799
800 // or with properties:
801 stackView.replace(rect1, {"color": "red"}, rect2, {"color": "green"}, rect3, {"color": "blue"})
802 \endcode
803
804 Replacing an array of items:
805 \code
806 stackView.replace([rect1, rect2, rect3])
807
808 // or with properties:
809 stackView.replace([rect1, {"color": "red"}, rect2, {"color": "green"}, rect3, {"color": "blue"}])
810 \endcode
811
812 An \a operation can be optionally specified as the last argument. Supported
813 operations:
814
815 \value StackView.Immediate An immediate operation without transitions.
816 \value StackView.PushTransition An operation with push transitions (since QtQuick.Controls 2.1).
817 \value StackView.ReplaceTransition An operation with replace transitions (since QtQuick.Controls 2.1).
818 \value StackView.PopTransition An operation with pop transitions (since QtQuick.Controls 2.1).
819
820 If no operation is provided, \c Immediate will be used if the stack is
821 empty, and \c ReplaceTransition otherwise.
822
823 The following example illustrates the use of push and pop transitions with replace().
824
825 \code
826 StackView {
827 id: stackView
828
829 initialItem: Component {
830 id: page
831
832 Page {
833 Row {
834 spacing: 20
835 anchors.centerIn: parent
836
837 Button {
838 text: "<"
839 onClicked: stackView.replace(page, StackView.PopTransition)
840 }
841 Button {
842 text: ">"
843 onClicked: stackView.replace(page, StackView.PushTransition)
844 }
845 }
846 }
847 }
848 }
849 \endcode
850
851 \note If you are \l {The QML script compiler}{compiling QML}, use the
852 strongly-typed \l replaceCurrentItem functions instead.
853
854 \sa push(), {Replacing Items}
855*/
856void QQuickStackView::replace(QQmlV4FunctionPtr args)
857{
858 Q_D(QQuickStackView);
859 const QString operationName = QStringLiteral("replace");
860 if (d->modifyingElements) {
861 d->warnOfInterruption(attemptedOperation: operationName);
862 args->setReturnValue(QV4::Encode::null());
863 return;
864 }
865
866 QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
867 QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
868 if (args->length() <= 0) {
869 d->warn(QStringLiteral("missing arguments"));
870 args->setReturnValue(QV4::Encode::null());
871 return;
872 }
873
874 QV4::ExecutionEngine *v4 = args->v4engine();
875 QV4::Scope scope(v4);
876
877#if QT_CONFIG(quick_viewtransitions)
878 Operation operation = d->elements.isEmpty() ? Immediate : ReplaceTransition;
879 QV4::ScopedValue lastArg(scope, (*args)[args->length() - 1]);
880 if (lastArg->isInt32())
881 operation = static_cast<Operation>(lastArg->toInt32());
882#endif
883
884 QQuickStackElement *target = nullptr;
885 QV4::ScopedValue firstArg(scope, (*args)[0]);
886 if (firstArg->isNull())
887 target = d->elements.value(i: 0);
888 else if (!firstArg->isInt32())
889 target = d->findElement(value: firstArg);
890
891 QStringList errors;
892 QList<QQuickStackElement *> elements = d->parseElements(from: target ? 1 : 0, args, errors: &errors);
893 if (!errors.isEmpty() || elements.isEmpty()) {
894 if (!errors.isEmpty()) {
895 for (const QString &error : std::as_const(t&: errors))
896 d->warn(error);
897 } else {
898 d->warn(QStringLiteral("nothing to push"));
899 }
900 args->setReturnValue(QV4::Encode::null());
901 return;
902 }
903
904 int oldDepth = d->elements.size();
905 QQuickStackElement* exit = nullptr;
906 if (!d->elements.isEmpty())
907 exit = d->elements.pop();
908
909 if (exit != target ? d->replaceElements(element: target, elements) : d->pushElements(elements)) {
910 d->depthChange(newDepth: d->elements.size(), oldDepth);
911 if (exit) {
912 exit->removal = true;
913 d->removing.insert(value: exit);
914 }
915 QQuickStackElement *enter = d->elements.top();
916#if QT_CONFIG(quick_viewtransitions)
917 d->startTransition(first: QQuickStackTransition::replaceExit(operation, element: exit, view: this),
918 second: QQuickStackTransition::replaceEnter(operation, element: enter, view: this),
919 immediate: operation == Immediate);
920#endif
921 d->setCurrentItem(enter);
922 }
923
924 if (d->currentItem) {
925 QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine: v4, object: d->currentItem));
926 args->setReturnValue(rv->asReturnedValue());
927 } else {
928 args->setReturnValue(QV4::Encode::null());
929 }
930}
931
932/*!
933 \qmlmethod Item QtQuick.Controls::StackView::pushItems(items, operation)
934 \since 6.7
935
936 Pushes \a items onto the stack using an optional \a operation, and
937 optionally applies a set of properties on each element. \a items is an array
938 of elements. Each element can be
939 an \l Item, \l Component, or \l [QML] url and can be followed by an optional
940 properties argument (see below). Returns the item that became
941 current (the last item).
942
943 StackView creates an instance automatically if the pushed element is a
944 \l Component or \l [QML] url, and the instance will be destroyed when it is
945 popped off the stack. See \l {Item Ownership} for more information.
946
947 \include qquickstackview.qdocinc optional-properties-after-each-item
948
949 \code
950 stackView.push([item, rectComponent, Qt.resolvedUrl("MyItem.qml")])
951
952 // With properties:
953 stackView.pushItems([
954 item, { "color": "red" },
955 rectComponent, { "color": "green" },
956 Qt.resolvedUrl("MyItem.qml"), { "color": "blue" }
957 ])
958
959 // With properties for only some items:
960 stackView.pushItems([
961 item, { "color": "yellow" },
962 rectComponent
963 ])
964 \endcode
965
966 \include qquickstackview.qdocinc operation-values
967
968 If no operation is provided, \c PushTransition will be used.
969
970 To push a single item, use the relevant \c pushItem function:
971 \list
972 \li \l {stackview-pushitem-item-overload}
973 {pushItem}(item, properties, operation)
974 \li \l {stackview-pushitem-component-overload}
975 {pushItem}(component, properties, operation)
976 \li \l {stackview-pushitem-url-overload}
977 {pushItem}(url, properties, operation)
978 \endlist
979
980 \note Items that already exist in the stack are not pushed.
981
982 \sa initialItem, pushItem, {Pushing Items}
983*/
984QQuickItem *QQuickStackView::pushItems(QList<QQuickStackViewArg> args, Operation operation)
985{
986 Q_D(QQuickStackView);
987 const QString operationName = QStringLiteral("pushItem");
988 if (d->modifyingElements) {
989 d->warnOfInterruption(attemptedOperation: operationName);
990 return nullptr;
991 }
992
993 QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
994 QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
995
996 const QList<QQuickStackElement *> stackElements = d->parseElements(args);
997
998#if QT_CONFIG(quick_viewtransitions)
999 QQuickStackElement *exit = nullptr;
1000 if (!d->elements.isEmpty())
1001 exit = d->elements.top();
1002#endif
1003
1004 const int oldDepth = d->elements.size();
1005 if (d->pushElements(elements: stackElements)) {
1006 d->depthChange(newDepth: d->elements.size(), oldDepth);
1007 QQuickStackElement *enter = d->elements.top();
1008#if QT_CONFIG(quick_viewtransitions)
1009 d->startTransition(first: QQuickStackTransition::pushEnter(operation, element: enter, view: this),
1010 second: QQuickStackTransition::pushExit(operation, element: exit, view: this),
1011 immediate: operation == Immediate);
1012#endif
1013 d->setCurrentItem(enter);
1014 }
1015
1016 return d->currentItem;
1017}
1018
1019/*!
1020 \qmlmethod Item QtQuick.Controls::StackView::pushItem(item, properties, operation)
1021 \keyword stackview-pushitem-item-overload
1022 \since 6.7
1023
1024 Pushes an \a item onto the stack, optionally applying a set of
1025 \a properties, using the optional \a operation. Returns the item that
1026 became current (the last item).
1027
1028 \include qquickstackview.qdocinc operation-values
1029
1030 If no operation is provided, \c PushTransition will be used.
1031
1032 To push several items onto the stack, use \l pushItems().
1033
1034 \sa initialItem, {Pushing Items}
1035*/
1036QQuickItem *QQuickStackView::pushItem(QQuickItem *item, const QVariantMap &properties, Operation operation)
1037{
1038 return pushItems(args: { item, properties }, operation);
1039}
1040
1041/*!
1042 \qmlmethod Item QtQuick.Controls::StackView::pushItem(component, properties, operation)
1043 \overload pushItem()
1044 \keyword stackview-pushitem-component-overload
1045 \since 6.7
1046
1047 Pushes a \a component onto the stack, optionally applying a set of
1048 \a properties, using the optional \a operation. Returns the item that
1049 became current (the last item).
1050
1051 \include qquickstackview.qdocinc operation-values
1052
1053 If no operation is provided, \c PushTransition will be used.
1054
1055 To push several items onto the stack, use \l pushItems().
1056
1057 \sa initialItem, {Pushing Items}
1058*/
1059QQuickItem *QQuickStackView::pushItem(QQmlComponent *component, const QVariantMap &properties, Operation operation)
1060{
1061 return pushItems(args: { component, properties }, operation);
1062}
1063
1064/*!
1065 \qmlmethod Item QtQuick.Controls::StackView::pushItem(url, properties, operation)
1066 \overload pushItem()
1067 \keyword stackview-pushitem-url-overload
1068 \since 6.7
1069
1070 Pushes a \a url onto the stack, optionally applying a set of
1071 \a properties, using the optional \a operation. Returns the item that
1072 became current (the last item).
1073
1074 \include qquickstackview.qdocinc operation-values
1075
1076 If no operation is provided, \c PushTransition will be used.
1077
1078 To push several items onto the stack, use \l pushItems().
1079
1080 \sa initialItem, {Pushing Items}
1081*/
1082QQuickItem *QQuickStackView::pushItem(const QUrl &url, const QVariantMap &properties, Operation operation)
1083{
1084 return pushItems(args: { url, properties }, operation);
1085}
1086
1087/*!
1088 \qmlmethod Item QtQuick.Controls::StackView::popToItem(item, operation)
1089 \since 6.7
1090
1091 Pops all items down to (but not including) \a item. Returns the last item
1092 removed from the stack.
1093
1094 If \a item is \c null, a warning is produced and \c null is returned.
1095
1096 \include qquickstackview.qdocinc pop-ownership
1097
1098 \include qquickstackview.qdocinc operation-values
1099
1100 If no operation is provided, \c PopTransition will be used.
1101
1102 \code
1103 stackView.popToItem(someItem, StackView.Immediate)
1104 \endcode
1105
1106 \sa clear(), {Popping Items}, {Unwinding Items via Pop}
1107*/
1108QQuickItem *QQuickStackView::popToItem(QQuickItem *item, Operation operation)
1109{
1110 Q_D(QQuickStackView);
1111 return d->popToItem(item, operation, currentItemPolicy: QQuickStackViewPrivate::CurrentItemPolicy::DoNotPop);
1112}
1113
1114/*!
1115 \qmlmethod Item QtQuick.Controls::StackView::popToIndex(index, operation)
1116 \since 6.7
1117
1118 Pops all items down to (but not including) \a index. Returns the last item
1119 removed from the stack.
1120
1121 If \a index is out of bounds, a warning is produced and \c null is
1122 returned.
1123
1124 \include qquickstackview.qdocinc pop-ownership
1125
1126 \include qquickstackview.qdocinc operation-values
1127
1128 If no operation is provided, \c PopTransition will be used.
1129
1130 \code
1131 stackView.popToIndex(stackView.depth - 2, StackView.Immediate)
1132 \endcode
1133
1134 \sa clear(), {Popping Items}, {Unwinding Items via Pop}
1135*/
1136QQuickItem *QQuickStackView::popToIndex(int index, Operation operation)
1137{
1138 Q_D(QQuickStackView);
1139 if (index < 0 || index >= d->elements.size()) {
1140 d->warn(error: QString::fromLatin1(ba: "popToIndex: index %1 is out of bounds (%2 item(s))")
1141 .arg(a: index).arg(a: d->elements.size()));
1142 return nullptr;
1143 }
1144
1145 if (index == d->elements.size() - 1) {
1146 // This would pop down to the current item, which is a no-op.
1147 return nullptr;
1148 }
1149
1150 QQuickStackElement *element = d->elements.at(i: index);
1151 element->load(parent: this);
1152 return d->popToItem(item: element->item, operation, currentItemPolicy: QQuickStackViewPrivate::CurrentItemPolicy::Pop);
1153}
1154
1155/*!
1156 \qmlmethod Item QtQuick.Controls::StackView::popCurrentItem(operation)
1157 \since 6.7
1158
1159 Pops \l currentItem from the stack. Returns the last item removed from the
1160 stack, or \c null if \l depth was \c 1.
1161
1162 \include qquickstackview.qdocinc pop-ownership
1163
1164 \include qquickstackview.qdocinc operation-values
1165
1166 If no operation is provided, \c PopTransition will be used.
1167
1168 This function is equivalent to \c popToIndex(stackView.currentIndex - 1).
1169
1170 \sa clear(), {Popping Items}, {Unwinding Items via Pop}
1171*/
1172QQuickItem *QQuickStackView::popCurrentItem(Operation operation)
1173{
1174 Q_D(QQuickStackView);
1175 if (d->elements.size() == 1) {
1176 auto lastItemRemoved = d->elements.last()->item;
1177 clear(operation);
1178 return lastItemRemoved;
1179 }
1180 return d->popToItem(item: d->currentItem, operation, currentItemPolicy: QQuickStackViewPrivate::CurrentItemPolicy::Pop);
1181}
1182
1183/*!
1184 \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(items, operation)
1185 \keyword stackview-replacecurrentitem-items-overload
1186 \since 6.7
1187
1188 Pops \l currentItem from the stack and pushes \a items. If the optional
1189 \a operation is specified, the relevant transition will be used. Each item
1190 can be followed by an optional set of properties that will be applied to
1191 that item. Returns the item that became current.
1192
1193 \include qquickstackview.qdocinc optional-properties-after-each-item
1194
1195 \include qquickstackview.qdocinc pop-ownership
1196
1197 \include qquickstackview.qdocinc operation-values
1198
1199 If no operation is provided, \c ReplaceTransition will be used.
1200
1201 \code
1202 stackView.replaceCurrentItem([item, rectComponent, Qt.resolvedUrl("MyItem.qml")])
1203
1204 // With properties:
1205 stackView.replaceCurrentItem([
1206 item, { "color": "red" },
1207 rectComponent, { "color": "green" },
1208 Qt.resolvedUrl("MyItem.qml"), { "color": "blue" }
1209 ])
1210 \endcode
1211
1212 To push a single item, use the relevant overload:
1213 \list
1214 \li \l {stackview-replacecurrentitem-item-overload}
1215 {replaceCurrentItem}(item, properties, operation)
1216 \li \l {stackview-replacecurrentitem-component-overload}
1217 {replaceCurrentItem}(component, properties, operation)
1218 \li \l {stackview-replacecurrentitem-url-overload}
1219 {replaceCurrentItem}(url, properties, operation)
1220 \endlist
1221
1222 \sa push(), {Replacing Items}
1223*/
1224QQuickItem *QQuickStackView::replaceCurrentItem(const QList<QQuickStackViewArg> &args,
1225 Operation operation)
1226{
1227 Q_D(QQuickStackView);
1228 const QString operationName = QStringLiteral("replace");
1229 if (d->modifyingElements) {
1230 d->warnOfInterruption(attemptedOperation: operationName);
1231 return nullptr;
1232 }
1233
1234 QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
1235 QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
1236
1237 QQuickStackElement *currentElement = !d->elements.isEmpty() ? d->elements.top() : nullptr;
1238
1239 const QList<QQuickStackElement *> stackElements = d->parseElements(args);
1240
1241 int oldDepth = d->elements.size();
1242 QQuickStackElement* exit = nullptr;
1243 if (!d->elements.isEmpty())
1244 exit = d->elements.pop();
1245
1246 const bool successfullyReplaced = exit != currentElement
1247 ? d->replaceElements(element: currentElement, elements: stackElements)
1248 : d->pushElements(elements: stackElements);
1249 if (successfullyReplaced) {
1250 d->depthChange(newDepth: d->elements.size(), oldDepth);
1251 if (exit) {
1252 exit->removal = true;
1253 d->removing.insert(value: exit);
1254 }
1255 QQuickStackElement *enter = d->elements.top();
1256#if QT_CONFIG(quick_viewtransitions)
1257 d->startTransition(first: QQuickStackTransition::replaceExit(operation, element: exit, view: this),
1258 second: QQuickStackTransition::replaceEnter(operation, element: enter, view: this),
1259 immediate: operation == Immediate);
1260#endif
1261 d->setCurrentItem(enter);
1262 }
1263
1264 return d->currentItem;
1265}
1266
1267/*!
1268 \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(item, properties, operation)
1269 \overload replaceCurrentItem()
1270 \keyword stackview-replacecurrentitem-item-overload
1271 \since 6.7
1272
1273 \include qquickstackview.qdocinc {replaceCurrentItem} {item}
1274
1275 \include qquickstackview.qdocinc pop-ownership
1276
1277 \include qquickstackview.qdocinc operation-values
1278
1279 If no operation is provided, \c ReplaceTransition will be used.
1280
1281 To push several items onto the stack, use
1282 \l {stackview-replacecurrentitem-items-overload}
1283 {replaceCurrentItem}(items, operation).
1284
1285 \sa {Replacing Items}
1286*/
1287QQuickItem *QQuickStackView::replaceCurrentItem(QQuickItem *item, const QVariantMap &properties,
1288 Operation operation)
1289{
1290 const QList<QQuickStackViewArg> args = { QQuickStackViewArg(item), QQuickStackViewArg(properties) };
1291 return replaceCurrentItem(args, operation);
1292}
1293
1294/*!
1295 \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(component, properties, operation)
1296 \overload replaceCurrentItem()
1297 \keyword stackview-replacecurrentitem-component-overload
1298 \since 6.7
1299
1300 \include qquickstackview.qdocinc {replaceCurrentItem} {component}
1301
1302 \include qquickstackview.qdocinc pop-ownership
1303
1304 \include qquickstackview.qdocinc operation-values
1305
1306 If no operation is provided, \c ReplaceTransition will be used.
1307
1308 To push several items onto the stack, use
1309 \l {stackview-replacecurrentitem-items-overload}
1310 {replaceCurrentItem}(items, operation).
1311
1312 \sa {Replacing Items}
1313*/
1314QQuickItem *QQuickStackView::replaceCurrentItem(QQmlComponent *component, const QVariantMap &properties,
1315 Operation operation)
1316{
1317 const QList<QQuickStackViewArg> args = { QQuickStackViewArg(component), QQuickStackViewArg(properties) };
1318 return replaceCurrentItem(args, operation);
1319}
1320
1321/*!
1322 \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(url, properties, operation)
1323 \keyword stackview-replacecurrentitem-url-overload
1324 \overload replaceCurrentItem()
1325 \since 6.7
1326
1327 \include qquickstackview.qdocinc {replaceCurrentItem} {url}
1328
1329 \include qquickstackview.qdocinc pop-ownership
1330
1331 \include qquickstackview.qdocinc operation-values
1332
1333 If no operation is provided, \c ReplaceTransition will be used.
1334
1335 To push several items onto the stack, use
1336 \l {stackview-replacecurrentitem-items-overload}
1337 {replaceCurrentItem}(items, operation).
1338
1339 \sa {Replacing Items}
1340*/
1341QQuickItem *QQuickStackView::replaceCurrentItem(const QUrl &url, const QVariantMap &properties,
1342 Operation operation)
1343{
1344 const QList<QQuickStackViewArg> args = { QQuickStackViewArg(url), QQuickStackViewArg(properties) };
1345 return replaceCurrentItem(args, operation);
1346}
1347
1348/*!
1349 \since QtQuick.Controls 2.3 (Qt 5.10)
1350 \qmlproperty bool QtQuick.Controls::StackView::empty
1351 \readonly
1352
1353 This property holds whether the stack is empty.
1354
1355 \sa depth
1356*/
1357bool QQuickStackView::isEmpty() const
1358{
1359 Q_D(const QQuickStackView);
1360 return d->elements.isEmpty();
1361}
1362
1363/*!
1364 \qmlmethod void QtQuick.Controls::StackView::clear(transition)
1365
1366 Removes all items from the stack.
1367
1368 \include qquickstackview.qdocinc pop-ownership
1369
1370 Since QtQuick.Controls 2.3, a \a transition can be optionally specified. Supported transitions:
1371
1372 \value StackView.Immediate Clear the stack immediately without any transition (default).
1373 \value StackView.PushTransition Clear the stack with a push transition.
1374 \value StackView.ReplaceTransition Clear the stack with a replace transition.
1375 \value StackView.PopTransition Clear the stack with a pop transition.
1376*/
1377void QQuickStackView::clear(Operation operation)
1378{
1379#if !QT_CONFIG(quick_viewtransitions)
1380 Q_UNUSED(operation)
1381#endif
1382 Q_D(QQuickStackView);
1383 if (d->elements.isEmpty())
1384 return;
1385
1386 const QString operationName = QStringLiteral("clear");
1387 if (d->modifyingElements) {
1388 d->warnOfInterruption(attemptedOperation: operationName);
1389 return;
1390 }
1391
1392 const int oldDepth = d->elements.size();
1393
1394 QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
1395 QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
1396#if QT_CONFIG(quick_viewtransitions)
1397 if (operation != Immediate) {
1398 QQuickStackElement *exit = d->elements.pop();
1399 exit->removal = true;
1400 d->removing.insert(value: exit);
1401 d->startTransition(first: QQuickStackTransition::popExit(operation, element: exit, view: this),
1402 second: QQuickStackTransition::popEnter(operation, element: nullptr, view: this), immediate: false);
1403 }
1404#endif
1405
1406 d->setCurrentItem(nullptr);
1407 qDeleteAll(c: d->elements);
1408 d->elements.clear();
1409 d->depthChange(newDepth: 0, oldDepth);
1410}
1411
1412/*!
1413 \qmlproperty var QtQuick.Controls::StackView::initialItem
1414
1415 This property holds the initial item that should be shown when the StackView
1416 is created. The initial item can be an \l Item, \l Component, or a \l [QML] url.
1417 Specifying an initial item is equivalent to:
1418 \code
1419 Component.onCompleted: stackView.push(myInitialItem)
1420 \endcode
1421
1422 \sa push()
1423*/
1424QJSValue QQuickStackView::initialItem() const
1425{
1426 Q_D(const QQuickStackView);
1427 return d->initialItem;
1428}
1429
1430void QQuickStackView::setInitialItem(const QJSValue &item)
1431{
1432 Q_D(QQuickStackView);
1433 d->initialItem = item;
1434}
1435
1436#if QT_CONFIG(quick_viewtransitions)
1437/*!
1438 \qmlproperty Transition QtQuick.Controls::StackView::popEnter
1439
1440 This property holds the transition that is applied to the item that
1441 enters the stack when another item is popped off of it.
1442
1443 \sa {Customizing StackView}
1444*/
1445QQuickTransition *QQuickStackView::popEnter() const
1446{
1447 Q_D(const QQuickStackView);
1448 if (d->transitioner)
1449 return d->transitioner->removeDisplacedTransition;
1450 return nullptr;
1451}
1452
1453void QQuickStackView::setPopEnter(QQuickTransition *enter)
1454{
1455 Q_D(QQuickStackView);
1456 d->ensureTransitioner();
1457 if (d->transitioner->removeDisplacedTransition == enter)
1458 return;
1459
1460 d->transitioner->removeDisplacedTransition = enter;
1461 emit popEnterChanged();
1462}
1463
1464/*!
1465 \qmlproperty Transition QtQuick.Controls::StackView::popExit
1466
1467 This property holds the transition that is applied to the item that
1468 exits the stack when the item is popped off of it.
1469
1470 \sa {Customizing StackView}
1471*/
1472QQuickTransition *QQuickStackView::popExit() const
1473{
1474 Q_D(const QQuickStackView);
1475 if (d->transitioner)
1476 return d->transitioner->removeTransition;
1477 return nullptr;
1478}
1479
1480void QQuickStackView::setPopExit(QQuickTransition *exit)
1481{
1482 Q_D(QQuickStackView);
1483 d->ensureTransitioner();
1484 if (d->transitioner->removeTransition == exit)
1485 return;
1486
1487 d->transitioner->removeTransition = exit;
1488 emit popExitChanged();
1489}
1490
1491/*!
1492 \qmlproperty Transition QtQuick.Controls::StackView::pushEnter
1493
1494 This property holds the transition that is applied to the item that
1495 enters the stack when the item is pushed onto it.
1496
1497 \sa {Customizing StackView}
1498*/
1499QQuickTransition *QQuickStackView::pushEnter() const
1500{
1501 Q_D(const QQuickStackView);
1502 if (d->transitioner)
1503 return d->transitioner->addTransition;
1504 return nullptr;
1505}
1506
1507void QQuickStackView::setPushEnter(QQuickTransition *enter)
1508{
1509 Q_D(QQuickStackView);
1510 d->ensureTransitioner();
1511 if (d->transitioner->addTransition == enter)
1512 return;
1513
1514 d->transitioner->addTransition = enter;
1515 emit pushEnterChanged();
1516}
1517
1518/*!
1519 \qmlproperty Transition QtQuick.Controls::StackView::pushExit
1520
1521 This property holds the transition that is applied to the item that
1522 exits the stack when another item is pushed onto it.
1523
1524 \sa {Customizing StackView}
1525*/
1526QQuickTransition *QQuickStackView::pushExit() const
1527{
1528 Q_D(const QQuickStackView);
1529 if (d->transitioner)
1530 return d->transitioner->addDisplacedTransition;
1531 return nullptr;
1532}
1533
1534void QQuickStackView::setPushExit(QQuickTransition *exit)
1535{
1536 Q_D(QQuickStackView);
1537 d->ensureTransitioner();
1538 if (d->transitioner->addDisplacedTransition == exit)
1539 return;
1540
1541 d->transitioner->addDisplacedTransition = exit;
1542 emit pushExitChanged();
1543}
1544
1545/*!
1546 \qmlproperty Transition QtQuick.Controls::StackView::replaceEnter
1547
1548 This property holds the transition that is applied to the item that
1549 enters the stack when another item is replaced by it.
1550
1551 \sa {Customizing StackView}
1552*/
1553QQuickTransition *QQuickStackView::replaceEnter() const
1554{
1555 Q_D(const QQuickStackView);
1556 if (d->transitioner)
1557 return d->transitioner->moveTransition;
1558 return nullptr;
1559}
1560
1561void QQuickStackView::setReplaceEnter(QQuickTransition *enter)
1562{
1563 Q_D(QQuickStackView);
1564 d->ensureTransitioner();
1565 if (d->transitioner->moveTransition == enter)
1566 return;
1567
1568 d->transitioner->moveTransition = enter;
1569 emit replaceEnterChanged();
1570}
1571
1572/*!
1573 \qmlproperty Transition QtQuick.Controls::StackView::replaceExit
1574
1575 This property holds the transition that is applied to the item that
1576 exits the stack when it is replaced by another item.
1577
1578 \sa {Customizing StackView}
1579*/
1580QQuickTransition *QQuickStackView::replaceExit() const
1581{
1582 Q_D(const QQuickStackView);
1583 if (d->transitioner)
1584 return d->transitioner->moveDisplacedTransition;
1585 return nullptr;
1586}
1587
1588void QQuickStackView::setReplaceExit(QQuickTransition *exit)
1589{
1590 Q_D(QQuickStackView);
1591 d->ensureTransitioner();
1592 if (d->transitioner->moveDisplacedTransition == exit)
1593 return;
1594
1595 d->transitioner->moveDisplacedTransition = exit;
1596 emit replaceExitChanged();
1597}
1598#endif
1599
1600void QQuickStackView::componentComplete()
1601{
1602 QQuickControl::componentComplete();
1603
1604 Q_D(QQuickStackView);
1605 QScopedValueRollback<QString> operationNameRollback(d->operation, QStringLiteral("initialItem"));
1606 QQuickStackElement *element = nullptr;
1607 QString error;
1608 int oldDepth = d->elements.size();
1609 if (QObject *o = d->initialItem.toQObject())
1610 element = QQuickStackElement::fromObject(object: o, view: this, error: &error);
1611 else if (d->initialItem.isString())
1612 element = QQuickStackElement::fromString(str: d->initialItem.toString(), view: this, error: &error);
1613 if (!error.isEmpty()) {
1614 d->warn(error);
1615 delete element;
1616 } else if (d->pushElement(element)) {
1617 d->depthChange(newDepth: d->elements.size(), oldDepth);
1618 d->setCurrentItem(element);
1619 element->setStatus(QQuickStackView::Active);
1620 }
1621}
1622
1623void QQuickStackView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
1624{
1625 QQuickControl::geometryChange(newGeometry, oldGeometry);
1626
1627 Q_D(QQuickStackView);
1628 for (QQuickStackElement *element : std::as_const(t&: d->elements)) {
1629 if (element->item) {
1630 if (!element->widthValid)
1631 element->item->setWidth(newGeometry.width());
1632 if (!element->heightValid)
1633 element->item->setHeight(newGeometry.height());
1634 }
1635 }
1636}
1637
1638bool QQuickStackView::childMouseEventFilter(QQuickItem *item, QEvent *event)
1639{
1640 // in order to block accidental user interaction while busy/transitioning,
1641 // StackView filters out childrens' mouse events. therefore we block all
1642 // press events. however, since push() may be called from signal handlers
1643 // such as onPressed or onDoubleClicked, we must let the current mouse
1644 // grabber item receive the respective mouse release event to avoid
1645 // breaking its state (QTBUG-50305).
1646 if (event->type() == QEvent::MouseButtonPress)
1647 return true;
1648 if (event->type() == QEvent::UngrabMouse)
1649 return false;
1650 QQuickWindow *window = item->window();
1651 return window && !window->mouseGrabberItem();
1652}
1653
1654#if QT_CONFIG(quicktemplates2_multitouch)
1655void QQuickStackView::touchEvent(QTouchEvent *event)
1656{
1657 event->ignore(); // QTBUG-65084
1658}
1659#endif
1660
1661#if QT_CONFIG(accessibility)
1662QAccessible::Role QQuickStackView::accessibleRole() const
1663{
1664 return QAccessible::LayeredPane;
1665}
1666#endif
1667
1668void QQuickStackViewAttachedPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent)
1669{
1670 Q_Q(QQuickStackViewAttached);
1671 int oldIndex = element ? element->index : -1;
1672 QQuickStackView *oldView = element ? element->view : nullptr;
1673 QQuickStackView::Status oldStatus = element ? element->status : QQuickStackView::Inactive;
1674
1675 QQuickStackView *newView = qobject_cast<QQuickStackView *>(object: parent);
1676 element = newView ? QQuickStackViewPrivate::get(view: newView)->findElement(item) : nullptr;
1677
1678 int newIndex = element ? element->index : -1;
1679 QQuickStackView::Status newStatus = element ? element->status : QQuickStackView::Inactive;
1680
1681 if (oldIndex != newIndex)
1682 emit q->indexChanged();
1683 if (oldView != newView)
1684 emit q->viewChanged();
1685 if (oldStatus != newStatus)
1686 emit q->statusChanged();
1687}
1688
1689QQuickStackViewAttached::QQuickStackViewAttached(QObject *parent)
1690 : QObject(*(new QQuickStackViewAttachedPrivate), parent)
1691{
1692 Q_D(QQuickStackViewAttached);
1693 QQuickItem *item = qobject_cast<QQuickItem *>(o: parent);
1694 if (item) {
1695 connect(sender: item, signal: &QQuickItem::visibleChanged, context: this, slot: &QQuickStackViewAttached::visibleChanged);
1696 QQuickItemPrivate::get(item)->addItemChangeListener(listener: d, types: QQuickItemPrivate::Parent);
1697 d->itemParentChanged(item, parent: item->parentItem());
1698 } else if (parent) {
1699 qmlWarning(me: parent) << "StackView must be attached to an Item";
1700 }
1701}
1702
1703QQuickStackViewAttached::~QQuickStackViewAttached()
1704{
1705 Q_D(QQuickStackViewAttached);
1706 QQuickItem *parentItem = qobject_cast<QQuickItem *>(o: parent());
1707 if (parentItem)
1708 QQuickItemPrivate::get(item: parentItem)->removeItemChangeListener(d, types: QQuickItemPrivate::Parent);
1709}
1710
1711/*!
1712 \qmlattachedproperty int QtQuick.Controls::StackView::index
1713 \readonly
1714
1715 This attached property holds the stack index of the item it's
1716 attached to, or \c -1 if the item is not in a stack.
1717*/
1718int QQuickStackViewAttached::index() const
1719{
1720 Q_D(const QQuickStackViewAttached);
1721 return d->element ? d->element->index : -1;
1722}
1723
1724/*!
1725 \qmlattachedproperty StackView QtQuick.Controls::StackView::view
1726 \readonly
1727
1728 This attached property holds the stack view of the item it's
1729 attached to, or \c null if the item is not in a stack.
1730*/
1731QQuickStackView *QQuickStackViewAttached::view() const
1732{
1733 Q_D(const QQuickStackViewAttached);
1734 return d->element ? d->element->view : nullptr;
1735}
1736
1737/*!
1738 \qmlattachedproperty enumeration QtQuick.Controls::StackView::status
1739 \readonly
1740
1741 This attached property holds the stack status of the item it's
1742 attached to, or \c StackView.Inactive if the item is not in a stack.
1743
1744 Available values:
1745 \value StackView.Inactive The item is inactive (or not in a stack).
1746 \value StackView.Deactivating The item is being deactivated (popped off).
1747 \value StackView.Activating The item is being activated (becoming the current item).
1748 \value StackView.Active The item is active, that is, the current item.
1749*/
1750QQuickStackView::Status QQuickStackViewAttached::status() const
1751{
1752 Q_D(const QQuickStackViewAttached);
1753 return d->element ? d->element->status : QQuickStackView::Inactive;
1754}
1755
1756/*!
1757 \since QtQuick.Controls 2.2 (Qt 5.9)
1758 \qmlattachedproperty bool QtQuick.Controls::StackView::visible
1759
1760 This attached property holds the visibility of the item it's attached to.
1761 The value follows the value of \l Item::visible.
1762
1763 By default, StackView shows incoming items when the enter transition begins,
1764 and hides outgoing items when the exit transition ends. Setting this property
1765 explicitly allows the default behavior to be overridden, making it possible
1766 to keep items that are below the top-most item visible.
1767
1768 \note The default transitions of most styles slide outgoing items outside the
1769 view, and may also animate their opacity. In order to keep a full stack
1770 of items visible, consider customizing the \l transitions so that the
1771 items underneath can be seen.
1772
1773 \image qtquickcontrols-stackview-visible.png
1774
1775 \snippet qtquickcontrols-stackview-visible.qml 1
1776*/
1777bool QQuickStackViewAttached::isVisible() const
1778{
1779 const QQuickItem *parentItem = qobject_cast<QQuickItem *>(o: parent());
1780 return parentItem && parentItem->isVisible();
1781}
1782
1783void QQuickStackViewAttached::setVisible(bool visible)
1784{
1785 Q_D(QQuickStackViewAttached);
1786 d->explicitVisible = true;
1787 QQuickItem *parentItem = qobject_cast<QQuickItem *>(o: parent());
1788 if (parentItem)
1789 parentItem->setVisible(visible);
1790}
1791
1792void QQuickStackViewAttached::resetVisible()
1793{
1794 Q_D(QQuickStackViewAttached);
1795 d->explicitVisible = false;
1796 if (!d->element || !d->element->view)
1797 return;
1798
1799 QQuickItem *parentItem = qobject_cast<QQuickItem *>(o: parent());
1800 if (parentItem)
1801 parentItem->setVisible(parentItem == d->element->view->currentItem());
1802}
1803
1804/*!
1805 \qmlattachedsignal QtQuick.Controls::StackView::activated()
1806 \since QtQuick.Controls 2.1 (Qt 5.8)
1807
1808 This attached signal is emitted when the item it's attached to is activated in the stack.
1809
1810 \sa status
1811*/
1812
1813/*!
1814 \qmlattachedsignal QtQuick.Controls::StackView::deactivated()
1815 \since QtQuick.Controls 2.1 (Qt 5.8)
1816
1817 This attached signal is emitted when the item it's attached to is deactivated in the stack.
1818
1819 \sa status
1820*/
1821
1822/*!
1823 \qmlattachedsignal QtQuick.Controls::StackView::activating()
1824 \since QtQuick.Controls 2.1 (Qt 5.8)
1825
1826 This attached signal is emitted when the item it's attached to is in the process of being
1827 activated in the stack.
1828
1829 \sa status
1830*/
1831
1832/*!
1833 \qmlattachedsignal QtQuick.Controls::StackView::deactivating()
1834 \since QtQuick.Controls 2.1 (Qt 5.8)
1835
1836 This attached signal is emitted when the item it's attached to is in the process of being
1837 dectivated in the stack.
1838
1839 \sa status
1840*/
1841
1842/*!
1843 \qmlattachedsignal QtQuick.Controls::StackView::removed()
1844 \since QtQuick.Controls 2.1 (Qt 5.8)
1845
1846 This attached signal is emitted when the item it's attached to has been
1847 removed from the stack. It can be used to safely destroy an Item that was
1848 pushed onto the stack, for example:
1849
1850 \code
1851 Item {
1852 StackView.onRemoved: destroy() // Will be destroyed sometime after this call.
1853 }
1854 \endcode
1855
1856 \sa status
1857*/
1858
1859QT_END_NAMESPACE
1860
1861#include "moc_qquickstackview_p.cpp"
1862

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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