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