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