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