aboutsummaryrefslogtreecommitdiffstats
path: root/src/quicktemplates/qquickstackview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quicktemplates/qquickstackview.cpp')
-rw-r--r--src/quicktemplates/qquickstackview.cpp1861
1 files changed, 1861 insertions, 0 deletions
diff --git a/src/quicktemplates/qquickstackview.cpp b/src/quicktemplates/qquickstackview.cpp
new file mode 100644
index 0000000000..2e0109863a
--- /dev/null
+++ b/src/quicktemplates/qquickstackview.cpp
@@ -0,0 +1,1861 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickstackview_p.h"
+#include "qquickstackview_p_p.h"
+#include "qquickstackelement_p_p.h"
+#if QT_CONFIG(quick_viewtransitions)
+#include "qquickstacktransition_p_p.h"
+#endif
+
+#include <QtCore/qscopedvaluerollback.h>
+#include <QtQml/qjsvalue.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlinfo.h>
+
+#include <private/qv4qobjectwrapper_p.h>
+#include <private/qqmlengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQuickStackViewArg::QQuickStackViewArg(QQuickItem *item)
+ : mItem(item)
+{
+}
+
+QQuickStackViewArg::QQuickStackViewArg(const QUrl &url)
+ : mUrl(url)
+{
+}
+
+QQuickStackViewArg::QQuickStackViewArg(QQmlComponent *component)
+ : mComponent(component)
+{
+}
+
+QQuickStackViewArg::QQuickStackViewArg(const QVariantMap &properties)
+ : mProperties(properties)
+{
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QQuickStackViewArg &arg)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "QQuickStackViewArg("
+ << "mItem=" << arg.mItem
+ << " mComponent=" << arg.mComponent
+ << " mUrl=" << arg.mUrl
+ << ")";
+ return debug;
+}
+#endif
+
+/*!
+ \qmltype StackView
+ \inherits Control
+//! \instantiates QQuickStackView
+ \inqmlmodule QtQuick.Controls
+ \since 5.7
+ \ingroup qtquickcontrols-navigation
+ \ingroup qtquickcontrols-containers
+ \ingroup qtquickcontrols-focusscopes
+ \brief Provides a stack-based navigation model.
+
+ \image qtquickcontrols-stackview-wireframe.png
+
+ StackView can be used with a set of inter-linked information pages. For
+ example, an email application with separate views to list the latest emails,
+ view a specific email, and list/view the attachments. The email list view
+ is pushed onto the stack as users open an email, and popped out as they
+ choose to go back.
+
+ The following snippet demonstrates a simple use case, where the \c mainView
+ is pushed onto and popped out of the stack on relevant button click:
+
+ \qml
+ ApplicationWindow {
+ title: qsTr("Hello World")
+ width: 640
+ height: 480
+ visible: true
+
+ StackView {
+ id: stack
+ initialItem: mainView
+ anchors.fill: parent
+ }
+
+ Component {
+ id: mainView
+
+ Row {
+ spacing: 10
+
+ Button {
+ text: "Push"
+ onClicked: stack.push(mainView)
+ }
+ Button {
+ text: "Pop"
+ enabled: stack.depth > 1
+ onClicked: stack.pop()
+
+ }
+ Text {
+ text: stack.depth
+ }
+ }
+ }
+ }
+ \endqml
+
+ \section1 Using StackView in an Application
+
+ Using StackView in an application is as simple as adding it as a child to
+ a Window. The stack is usually anchored to the edges of the window, except
+ at the top or bottom where it might be anchored to a status bar, or some
+ other similar UI component. The stack can then be used by invoking its
+ navigation methods. The first item to show in the StackView is the one
+ that was assigned to \l initialItem, or the topmost item if \l initialItem
+ is not set.
+
+ \section1 Basic Navigation
+
+ StackView supports three primary navigation operations: push(), pop(), and
+ replace(). These correspond to classic stack operations where "push" adds
+ an item to the top of a stack, "pop" removes the top item from the
+ stack, and "replace" is like a pop followed by a push, which replaces the
+ topmost item with the new item. The topmost item in the stack
+ corresponds to the one that is \l{StackView::currentItem}{currently}
+ visible on screen. Logically, "push" navigates forward or deeper into the
+ application UI, "pop" navigates backward, and "replace" replaces the
+ \l currentItem.
+
+ \section2 Pushing Items
+
+ In the following animation, three \l Label controls are pushed onto a
+ stack view with the \l push() function:
+
+ \image qtquickcontrols-stackview-push.gif
+
+ The stack now contains the following items: \c [A, B, C].
+
+ \note When the stack is empty, a push() operation will not have a
+ transition animation because there is nothing to transition from (typically
+ on application start-up).
+
+ \section2 Popping Items
+
+ Continuing on from the example above, the topmost item on the stack is
+ removed with a call to \l pop():
+
+ \image qtquickcontrols-stackview-pop.gif
+
+ The stack now contains the following items: \c [A, B].
+
+ \note A pop() operation on a stack with depth 1 or 0 does nothing. In such
+ cases, the stack can be emptied using the \l clear() method.
+
+ \section3 Unwinding Items via Pop
+
+ Sometimes, it is necessary to go back more than a single step in the stack.
+ For example, to return to a "main" item or some kind of section item in the
+ application. In such cases, it is possible to specify an item as a
+ parameter for pop(). This is called an "unwind" operation, where the stack
+ unwinds till the specified item. If the item is not found, stack unwinds
+ until it is left with one item, which becomes the \l currentItem. To
+ explicitly unwind to the bottom of the stack, it is recommended to use
+ \l{pop()}{pop(null)}, although any non-existent item will do.
+
+ In the following animation, we unwind the stack to the first item by
+ calling \c pop(null):
+
+ \image qtquickcontrols-stackview-unwind.gif
+
+ The stack now contains a single item: \c [A].
+
+ \section2 Replacing Items
+
+ In the following animation, we \l replace the topmost item with \c D:
+
+ \image qtquickcontrols-stackview-replace.gif
+
+ The stack now contains the following items: \c [A, B, D].
+
+ \section1 Deep Linking
+
+ \e{Deep linking} means launching an application into a particular state. For
+ example, a newspaper application could be launched into showing a
+ particular article, bypassing the topmost item. In terms of StackView, deep
+ linking means the ability to modify the state of the stack, so much so that
+ it is possible to push a set of items to the top of the stack, or to
+ completely reset the stack to a given state.
+
+ The API for deep linking in StackView is the same as for basic navigation.
+ Pushing an array instead of a single item adds all the items in that array
+ to the stack. The transition animation, however, is applied only for the
+ last item in the array. The normal semantics of push() apply for deep
+ linking, that is, it adds whatever is pushed onto the stack.
+
+ \note Only the last item of the array is loaded. The rest of the items are
+ loaded only when needed, either on subsequent calls to pop or on request to
+ get an item using get().
+
+ This gives us the following result, given the stack [A, B, C]:
+
+ \list
+ \li \l{push()}{push([D, E, F])} => [A, B, C, D, E, F] - "push" transition
+ animation between C and F
+ \li \l{replace()}{replace([D, E, F])} => [A, B, D, E, F] - "replace"
+ transition animation between C and F
+ \li \l{clear()} followed by \l{push()}{push([D, E, F])} => [D, E, F] - no
+ transition animation for pushing items as the stack was empty.
+ \endlist
+
+ \section1 Finding Items
+
+ An Item for which the application does not have a reference can be found
+ by calling find(). The method needs a callback function, which is invoked
+ for each item in the stack (starting at the top) until a match is found.
+ If the callback returns \c true, find() stops and returns the matching
+ item, otherwise \c null is returned.
+
+ The code below searches the stack for an item named "order_id" and unwinds
+ to that item.
+
+ \badcode
+ stackView.pop(stackView.find(function(item) {
+ return item.name == "order_id";
+ }));
+ \endcode
+
+ You can also get to an item in the stack using \l {get()}{get(index)}.
+
+ \badcode
+ previousItem = stackView.get(myItem.StackView.index - 1));
+ \endcode
+
+ \section1 Transitions
+
+ For each push or pop operation, different transition animations are applied
+ to entering and exiting items. These animations define how the entering item
+ should animate in, and the exiting item should animate out. The animations
+ can be customized by assigning different \l [QML] {Transition} {Transitions}
+ for the \l pushEnter, \l pushExit, \l popEnter, \l popExit, replaceEnter,
+ and \l replaceExit properties of StackView.
+
+ \note The transition animations affect each others' transitional behavior.
+ Customizing the animation for one and leaving the other may give unexpected
+ results.
+
+ The following snippet defines a simple fade transition for push and pop
+ operations:
+
+ \qml
+ StackView {
+ id: stackview
+ anchors.fill: parent
+
+ pushEnter: Transition {
+ PropertyAnimation {
+ property: "opacity"
+ from: 0
+ to:1
+ duration: 200
+ }
+ }
+ pushExit: Transition {
+ PropertyAnimation {
+ property: "opacity"
+ from: 1
+ to:0
+ duration: 200
+ }
+ }
+ popEnter: Transition {
+ PropertyAnimation {
+ property: "opacity"
+ from: 0
+ to:1
+ duration: 200
+ }
+ }
+ popExit: Transition {
+ PropertyAnimation {
+ property: "opacity"
+ from: 1
+ to:0
+ duration: 200
+ }
+ }
+ }
+ \endqml
+
+ \note Using anchors on the items added to a StackView is not supported.
+ Typically push, pop, and replace transitions animate the position,
+ which is not possible when anchors are applied. Notice that this
+ only applies to the root of the item. Using anchors for its children
+ works as expected.
+
+ \section1 Item Ownership
+
+ StackView only takes ownership of items that it creates itself. This means
+ that any item pushed onto a StackView will never be destroyed by the
+ StackView; only items that StackView creates from \l {Component}{Components}
+ or \l [QML] {url}{URLs} are destroyed by the StackView. To illustrate this,
+ the messages in the example below will only be printed when the StackView
+ is destroyed, not when the items are popped off the stack:
+
+ \qml
+ Component {
+ id: itemComponent
+
+ Item {
+ Component.onDestruction: print("Destroying second item")
+ }
+ }
+
+ StackView {
+ initialItem: Item {
+ Component.onDestruction: print("Destroying initial item")
+ }
+
+ Component.onCompleted: push(itemComponent.createObject(window))
+ }
+ \endqml
+
+ However, both of the items created from the URL and Component in the
+ following example will be destroyed by the StackView when they are popped
+ off of it:
+
+ \qml
+ Component {
+ id: itemComponent
+
+ Item {
+ Component.onDestruction: print("Destroying second item")
+ }
+ }
+
+ StackView {
+ initialItem: "Item1.qml"
+
+ Component.onCompleted: push(itemComponent)
+ }
+ \endqml
+
+ \section1 Size
+
+ StackView does not inherit an implicit size from items that are pushed onto
+ it. This means that using it as the \l {Popup::}{contentItem} of a
+ \l Dialog, for example, will not work as expected:
+
+ \code
+ Dialog {
+ StackView {
+ initialItem: Rectangle {
+ width: 200
+ height: 200
+ color: "salmon"
+ }
+ }
+ }
+ \endcode
+
+ There are several ways to ensure that StackView has a size in this
+ situation:
+
+ \list
+ \li Set \l[QtQuick]{Item::}{implicitWidth} and
+ \l[QtQuick]{Item::}{implicitHeight} on the StackView itself.
+ \li Set \l[QtQuick]{Item::}{implicitWidth} and
+ \l[QtQuick]{Item::}{implicitHeight} on the \l Rectangle.
+ \li Set \l {Popup::}{contentWidth} and \l {Popup::}{contentHeight} on
+ the Dialog.
+ \li Give the Dialog a size.
+ \endlist
+
+ \sa {Customizing StackView}, {Navigating with StackView}, {Navigation Controls},
+ {Container Controls}, {Focus Management in Qt Quick Controls}
+*/
+
+QQuickStackView::QQuickStackView(QQuickItem *parent)
+ : QQuickControl(*(new QQuickStackViewPrivate), parent)
+{
+ setFlag(ItemIsFocusScope);
+
+ Q_D(QQuickStackView);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
+}
+
+QQuickStackView::~QQuickStackView()
+{
+ Q_D(QQuickStackView);
+#if QT_CONFIG(quick_viewtransitions)
+ if (d->transitioner) {
+ d->transitioner->setChangeListener(nullptr);
+ delete d->transitioner;
+ }
+#endif
+ qDeleteAll(d->removing);
+ qDeleteAll(d->removed);
+ qDeleteAll(d->elements);
+}
+
+QQuickStackViewAttached *QQuickStackView::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickStackViewAttached(object);
+}
+
+/*!
+ \qmlproperty bool QtQuick.Controls::StackView::busy
+ \readonly
+ This property holds whether a transition is running.
+*/
+bool QQuickStackView::isBusy() const
+{
+ Q_D(const QQuickStackView);
+ return d->busy;
+}
+
+/*!
+ \qmlproperty int QtQuick.Controls::StackView::depth
+ \readonly
+ This property holds the number of items currently pushed onto the stack.
+*/
+int QQuickStackView::depth() const
+{
+ Q_D(const QQuickStackView);
+ return d->elements.size();
+}
+
+/*!
+ \qmlproperty Item QtQuick.Controls::StackView::currentItem
+ \readonly
+ This property holds the current top-most item in the stack.
+*/
+QQuickItem *QQuickStackView::currentItem() const
+{
+ Q_D(const QQuickStackView);
+ return d->currentItem;
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::get(index, behavior)
+
+ Returns the item at position \a index in the stack, or \c null if the index
+ is out of bounds.
+
+ Supported \a behavior values:
+ \value StackView.DontLoad The item is not forced to load (and \c null is returned if not yet loaded).
+ \value StackView.ForceLoad The item is forced to load.
+*/
+QQuickItem *QQuickStackView::get(int index, LoadBehavior behavior)
+{
+ Q_D(QQuickStackView);
+ QQuickStackElement *element = d->elements.value(index);
+ if (element) {
+ if (behavior == ForceLoad)
+ element->load(this);
+ return element->item;
+ }
+ return nullptr;
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::find(callback, behavior)
+
+ Search for a specific item inside the stack. The \a callback function is called
+ for each item in the stack (with the item and index as arguments) until the callback
+ function returns \c true. The return value is the item found. For example:
+
+ \code
+ stackView.find(function(item, index) {
+ return item.isTheOne
+ })
+ \endcode
+
+ Supported \a behavior values:
+ \value StackView.DontLoad Unloaded items are skipped (the callback function is not called for them).
+ \value StackView.ForceLoad Unloaded items are forced to load.
+*/
+QQuickItem *QQuickStackView::find(const QJSValue &callback, LoadBehavior behavior)
+{
+ Q_D(QQuickStackView);
+ QJSValue func(callback);
+ QQmlEngine *engine = qmlEngine(this);
+ if (!engine || !func.isCallable()) // TODO: warning?
+ return nullptr;
+
+ for (int i = d->elements.size() - 1; i >= 0; --i) {
+ QQuickStackElement *element = d->elements.at(i);
+ if (behavior == ForceLoad)
+ element->load(this);
+ if (element->item) {
+ QJSValue rv = func.call(QJSValueList() << engine->newQObject(element->item) << i);
+ if (rv.toBool())
+ return element->item;
+ }
+ }
+
+ return nullptr;
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::push(item, properties, operation)
+
+ Pushes an \a item onto the stack using an optional \a operation, and
+ optionally applies a set of \a properties on the item. The item can be
+ an \l Item, \l Component, or a \l [QML] url. Returns the item that became
+ current.
+
+ StackView creates an instance automatically if the pushed item is a \l Component,
+ or a \l [QML] url, and the instance will be destroyed when it is popped
+ off the stack. See \l {Item Ownership} for more information.
+
+ The optional \a properties argument specifies a map of initial
+ property values for the pushed item. For dynamically created items, these values
+ are applied before the creation is finalized. This is more efficient than setting
+ property values after creation, particularly where large sets of property values
+ are defined, and also allows property bindings to be set up (using \l{Qt::binding}
+ {Qt.binding()}) before the item is created.
+
+ Pushing a single item:
+ \code
+ stackView.push(rect)
+
+ // or with properties:
+ stackView.push(rect, {"color": "red"})
+ \endcode
+
+ Multiple items can be pushed at the same time either by passing them as
+ additional arguments, or as an array. The last item becomes the current
+ item. Each item can be followed by a set of properties to apply.
+
+ Passing a variable amount of arguments:
+ \code
+ stackView.push(rect1, rect2, rect3)
+
+ // or with properties:
+ stackView.push(rect1, {"color": "red"}, rect2, {"color": "green"}, rect3, {"color": "blue"})
+ \endcode
+
+ Pushing an array of items:
+ \code
+ stackView.push([rect1, rect2, rect3])
+
+ // or with properties:
+ stackView.push([rect1, {"color": "red"}, rect2, {"color": "green"}, rect3, {"color": "blue"}])
+ \endcode
+
+ An \a operation can be optionally specified as the last argument. Supported
+ operations:
+
+ \value StackView.Immediate An immediate operation without transitions.
+ \value StackView.PushTransition An operation with push transitions (since QtQuick.Controls 2.1).
+ \value StackView.ReplaceTransition An operation with replace transitions (since QtQuick.Controls 2.1).
+ \value StackView.PopTransition An operation with pop transitions (since QtQuick.Controls 2.1).
+
+ If no operation is provided, \c Immediate will be used if the stack is
+ empty, and \c PushTransition otherwise.
+
+ \note Items that already exist in the stack are not pushed.
+
+ \note If you are \l {The QML script compiler}{compiling QML}, use the
+ strongly-typed \l pushItem or \l pushItems functions instead.
+
+ \sa initialItem, {Pushing Items}
+*/
+void QQuickStackView::push(QQmlV4FunctionPtr args)
+{
+ Q_D(QQuickStackView);
+ const QString operationName = QStringLiteral("push");
+ if (d->modifyingElements) {
+ d->warnOfInterruption(operationName);
+ return;
+ }
+
+ QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
+ QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
+ if (args->length() <= 0) {
+ d->warn(QStringLiteral("missing arguments"));
+ args->setReturnValue(QV4::Encode::null());
+ return;
+ }
+
+ QV4::ExecutionEngine *v4 = args->v4engine();
+ QV4::Scope scope(v4);
+
+#if QT_CONFIG(quick_viewtransitions)
+ Operation operation = d->elements.isEmpty() ? Immediate : PushTransition;
+ QV4::ScopedValue lastArg(scope, (*args)[args->length() - 1]);
+ if (lastArg->isInt32())
+ operation = static_cast<Operation>(lastArg->toInt32());
+#endif
+
+ QStringList errors;
+ QList<QQuickStackElement *> elements = d->parseElements(0, args, &errors);
+ // Remove any items that are already in the stack, as they can't be in two places at once.
+ // not using erase_if, as we have to delete the elements first
+ auto removeIt = std::remove_if(elements.begin(), elements.end(), [&](QQuickStackElement *element) {
+ return element->item && d->findElement(element->item);
+ });
+ for (auto it = removeIt, end = elements.end(); it != end; ++it)
+ delete *it;
+ elements.erase(removeIt, elements.end());
+
+ if (!errors.isEmpty() || elements.isEmpty()) {
+ if (!errors.isEmpty()) {
+ for (const QString &error : std::as_const(errors))
+ d->warn(error);
+ } else {
+ d->warn(QStringLiteral("nothing to push"));
+ }
+ args->setReturnValue(QV4::Encode::null());
+ return;
+ }
+
+#if QT_CONFIG(quick_viewtransitions)
+ QQuickStackElement *exit = nullptr;
+ if (!d->elements.isEmpty())
+ exit = d->elements.top();
+#endif
+
+ int oldDepth = d->elements.size();
+ if (d->pushElements(elements)) {
+ d->depthChange(d->elements.size(), oldDepth);
+ QQuickStackElement *enter = d->elements.top();
+#if QT_CONFIG(quick_viewtransitions)
+ d->startTransition(QQuickStackTransition::pushEnter(operation, enter, this),
+ QQuickStackTransition::pushExit(operation, exit, this),
+ operation == Immediate);
+#endif
+ d->setCurrentItem(enter);
+ }
+
+ if (d->currentItem) {
+ QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(v4, d->currentItem));
+ args->setReturnValue(rv->asReturnedValue());
+ } else {
+ args->setReturnValue(QV4::Encode::null());
+ }
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pop(item, operation)
+
+ Pops one or more items off the stack. Returns the last item removed from the stack.
+
+ If the \a item argument is specified, all items down to (but not
+ including) \a item will be popped. If \a item is \c null, all
+ items down to (but not including) the first item is popped.
+ If not specified, only the current item is popped.
+
+ \note A pop() operation on a stack with depth 1 or 0 does nothing. In such
+ cases, the stack can be emptied using the \l clear() method.
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ An \a operation can be optionally specified as the last argument. Supported
+ operations:
+
+ \value StackView.Immediate An immediate operation without transitions.
+ \value StackView.PushTransition An operation with push transitions (since QtQuick.Controls 2.1).
+ \value StackView.ReplaceTransition An operation with replace transitions (since QtQuick.Controls 2.1).
+ \value StackView.PopTransition An operation with pop transitions (since QtQuick.Controls 2.1).
+
+ If no operation is provided, \c PopTransition will be used.
+
+ Examples:
+ \code
+ stackView.pop()
+ stackView.pop(someItem, StackView.Immediate)
+ stackView.pop(StackView.Immediate)
+ stackView.pop(null)
+ \endcode
+
+ \note If you are \l {The QML script compiler}{compiling QML}, use the
+ strongly-typed \l popToItem, \l popToIndex or \l popCurrentItem functions
+ instead.
+
+ \sa clear(), {Popping Items}, {Unwinding Items via Pop}
+*/
+void QQuickStackView::pop(QQmlV4FunctionPtr args)
+{
+ Q_D(QQuickStackView);
+ const QString operationName = QStringLiteral("pop");
+ if (d->modifyingElements) {
+ d->warnOfInterruption(operationName);
+ args->setReturnValue(QV4::Encode::null());
+ return;
+ }
+
+ QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
+ QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
+ int argc = args->length();
+ if (d->elements.size() <= 1 || argc > 2) {
+ if (argc > 2)
+ d->warn(QStringLiteral("too many arguments"));
+ args->setReturnValue(QV4::Encode::null());
+ return;
+ }
+
+ int oldDepth = d->elements.size();
+ QQuickStackElement *exit = d->elements.pop();
+ QQuickStackElement *enter = d->elements.top();
+
+ QV4::ExecutionEngine *v4 = args->v4engine();
+ QV4::Scope scope(v4);
+
+ if (argc > 0) {
+ QV4::ScopedValue value(scope, (*args)[0]);
+ if (value->isNull()) {
+ enter = d->elements.value(0);
+ } else if (const QV4::QObjectWrapper *o = value->as<QV4::QObjectWrapper>()) {
+ QQuickItem *item = qobject_cast<QQuickItem *>(o->object());
+ enter = d->findElement(item);
+ if (!enter) {
+ if (item != d->currentItem)
+ d->warn(QStringLiteral("can't find item to pop: ") + value->toQString());
+ args->setReturnValue(QV4::Encode::null());
+ d->elements.push(exit); // restore
+ return;
+ }
+ }
+ }
+
+#if QT_CONFIG(quick_viewtransitions)
+ Operation operation = PopTransition;
+ if (argc > 0) {
+ QV4::ScopedValue lastArg(scope, (*args)[argc - 1]);
+ if (lastArg->isInt32())
+ operation = static_cast<Operation>(lastArg->toInt32());
+ }
+#endif
+
+ QPointer<QQuickItem> previousItem;
+
+ if (d->popElements(enter)) {
+ if (exit) {
+ exit->removal = true;
+ d->removing.insert(exit);
+ previousItem = exit->item;
+ }
+ d->depthChange(d->elements.size(), oldDepth);
+#if QT_CONFIG(quick_viewtransitions)
+ d->startTransition(QQuickStackTransition::popExit(operation, exit, this),
+ QQuickStackTransition::popEnter(operation, enter, this),
+ operation == Immediate);
+#endif
+ d->setCurrentItem(enter);
+ }
+
+ if (previousItem) {
+ QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(v4, previousItem));
+ args->setReturnValue(rv->asReturnedValue());
+ } else {
+ args->setReturnValue(QV4::Encode::null());
+ }
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replace(target, item, properties, operation)
+
+ Replaces one or more items on the stack with the specified \a item and
+ optional \a operation, and optionally applies a set of \a properties on the
+ item. The item can be an \l Item, \l Component, or a \l [QML] url.
+ Returns the item that became current.
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ If the \a target argument is specified, all items down to the \a target
+ item will be replaced. If \a target is \c null, all items in the stack
+ will be replaced. If not specified, only the top item will be replaced.
+
+ StackView creates an instance automatically if the replacing item is a \l Component,
+ or a \l [QML] url. The optional \a properties argument specifies a map of initial
+ property values for the replacing item. For dynamically created items, these values
+ are applied before the creation is finalized. This is more efficient than setting
+ property values after creation, particularly where large sets of property values
+ are defined, and also allows property bindings to be set up (using \l{Qt::binding}
+ {Qt.binding()}) before the item is created.
+
+ Replace the top item:
+ \code
+ stackView.replace(rect)
+
+ // or with properties:
+ stackView.replace(rect, {"color": "red"})
+ \endcode
+
+ Multiple items can be replaced at the same time either by passing them as
+ additional arguments, or as an array. Each item can be followed by a set
+ of properties to apply.
+
+ Passing a variable amount of arguments:
+ \code
+ stackView.replace(rect1, rect2, rect3)
+
+ // or with properties:
+ stackView.replace(rect1, {"color": "red"}, rect2, {"color": "green"}, rect3, {"color": "blue"})
+ \endcode
+
+ Replacing an array of items:
+ \code
+ stackView.replace([rect1, rect2, rect3])
+
+ // or with properties:
+ stackView.replace([rect1, {"color": "red"}, rect2, {"color": "green"}, rect3, {"color": "blue"}])
+ \endcode
+
+ An \a operation can be optionally specified as the last argument. Supported
+ operations:
+
+ \value StackView.Immediate An immediate operation without transitions.
+ \value StackView.PushTransition An operation with push transitions (since QtQuick.Controls 2.1).
+ \value StackView.ReplaceTransition An operation with replace transitions (since QtQuick.Controls 2.1).
+ \value StackView.PopTransition An operation with pop transitions (since QtQuick.Controls 2.1).
+
+ If no operation is provided, \c Immediate will be used if the stack is
+ empty, and \c ReplaceTransition otherwise.
+
+ The following example illustrates the use of push and pop transitions with replace().
+
+ \code
+ StackView {
+ id: stackView
+
+ initialItem: Component {
+ id: page
+
+ Page {
+ Row {
+ spacing: 20
+ anchors.centerIn: parent
+
+ Button {
+ text: "<"
+ onClicked: stackView.replace(page, StackView.PopTransition)
+ }
+ Button {
+ text: ">"
+ onClicked: stackView.replace(page, StackView.PushTransition)
+ }
+ }
+ }
+ }
+ }
+ \endcode
+
+ \note If you are \l {The QML script compiler}{compiling QML}, use the
+ strongly-typed \l replaceCurrentItem functions instead.
+
+ \sa push(), {Replacing Items}
+*/
+void QQuickStackView::replace(QQmlV4FunctionPtr args)
+{
+ Q_D(QQuickStackView);
+ const QString operationName = QStringLiteral("replace");
+ if (d->modifyingElements) {
+ d->warnOfInterruption(operationName);
+ args->setReturnValue(QV4::Encode::null());
+ return;
+ }
+
+ QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
+ QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
+ if (args->length() <= 0) {
+ d->warn(QStringLiteral("missing arguments"));
+ args->setReturnValue(QV4::Encode::null());
+ return;
+ }
+
+ QV4::ExecutionEngine *v4 = args->v4engine();
+ QV4::Scope scope(v4);
+
+#if QT_CONFIG(quick_viewtransitions)
+ Operation operation = d->elements.isEmpty() ? Immediate : ReplaceTransition;
+ QV4::ScopedValue lastArg(scope, (*args)[args->length() - 1]);
+ if (lastArg->isInt32())
+ operation = static_cast<Operation>(lastArg->toInt32());
+#endif
+
+ QQuickStackElement *target = nullptr;
+ QV4::ScopedValue firstArg(scope, (*args)[0]);
+ if (firstArg->isNull())
+ target = d->elements.value(0);
+ else if (!firstArg->isInt32())
+ target = d->findElement(firstArg);
+
+ QStringList errors;
+ QList<QQuickStackElement *> elements = d->parseElements(target ? 1 : 0, args, &errors);
+ if (!errors.isEmpty() || elements.isEmpty()) {
+ if (!errors.isEmpty()) {
+ for (const QString &error : std::as_const(errors))
+ d->warn(error);
+ } else {
+ d->warn(QStringLiteral("nothing to push"));
+ }
+ args->setReturnValue(QV4::Encode::null());
+ return;
+ }
+
+ int oldDepth = d->elements.size();
+ QQuickStackElement* exit = nullptr;
+ if (!d->elements.isEmpty())
+ exit = d->elements.pop();
+
+ if (exit != target ? d->replaceElements(target, elements) : d->pushElements(elements)) {
+ d->depthChange(d->elements.size(), oldDepth);
+ if (exit) {
+ exit->removal = true;
+ d->removing.insert(exit);
+ }
+ QQuickStackElement *enter = d->elements.top();
+#if QT_CONFIG(quick_viewtransitions)
+ d->startTransition(QQuickStackTransition::replaceExit(operation, exit, this),
+ QQuickStackTransition::replaceEnter(operation, enter, this),
+ operation == Immediate);
+#endif
+ d->setCurrentItem(enter);
+ }
+
+ if (d->currentItem) {
+ QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(v4, d->currentItem));
+ args->setReturnValue(rv->asReturnedValue());
+ } else {
+ args->setReturnValue(QV4::Encode::null());
+ }
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pushItems(items, operation)
+ \since 6.7
+
+ Pushes \a items onto the stack using an optional \a operation, and
+ optionally applies a set of properties on each element. \a items is an array
+ of elements. Each element can be
+ an \l Item, \l Component, or \l [QML] url and can be followed by an optional
+ properties argument (see below). Returns the item that became
+ current (the last item).
+
+ StackView creates an instance automatically if the pushed element is a
+ \l Component or \l [QML] url, and the instance will be destroyed when it is
+ popped off the stack. See \l {Item Ownership} for more information.
+
+ \include qquickstackview.qdocinc optional-properties-after-each-item
+
+ \code
+ stackView.push([item, rectComponent, Qt.resolvedUrl("MyItem.qml")])
+
+ // With properties:
+ stackView.pushItems([
+ item, { "color": "red" },
+ rectComponent, { "color": "green" },
+ Qt.resolvedUrl("MyItem.qml"), { "color": "blue" }
+ ])
+
+ // With properties for only some items:
+ stackView.pushItems([
+ item, { "color": "yellow" },
+ rectComponent
+ ])
+ \endcode
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PushTransition will be used.
+
+ To push a single item, use the relevant \c pushItem function:
+ \list
+ \li \l {stackview-pushitem-item-overload}
+ {pushItem}(item, properties, operation)
+ \li \l {stackview-pushitem-component-overload}
+ {pushItem}(component, properties, operation)
+ \li \l {stackview-pushitem-url-overload}
+ {pushItem}(url, properties, operation)
+ \endlist
+
+ \note Items that already exist in the stack are not pushed.
+
+ \sa initialItem, pushItem, {Pushing Items}
+*/
+QQuickItem *QQuickStackView::pushItems(QList<QQuickStackViewArg> args, Operation operation)
+{
+ Q_D(QQuickStackView);
+ const QString operationName = QStringLiteral("pushItem");
+ if (d->modifyingElements) {
+ d->warnOfInterruption(operationName);
+ return nullptr;
+ }
+
+ QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
+ QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
+
+ const QList<QQuickStackElement *> stackElements = d->parseElements(args);
+
+#if QT_CONFIG(quick_viewtransitions)
+ QQuickStackElement *exit = nullptr;
+ if (!d->elements.isEmpty())
+ exit = d->elements.top();
+#endif
+
+ const int oldDepth = d->elements.size();
+ if (d->pushElements(stackElements)) {
+ d->depthChange(d->elements.size(), oldDepth);
+ QQuickStackElement *enter = d->elements.top();
+#if QT_CONFIG(quick_viewtransitions)
+ d->startTransition(QQuickStackTransition::pushEnter(operation, enter, this),
+ QQuickStackTransition::pushExit(operation, exit, this),
+ operation == Immediate);
+#endif
+ d->setCurrentItem(enter);
+ }
+
+ return d->currentItem;
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pushItem(item, properties, operation)
+ \keyword stackview-pushitem-item-overload
+ \since 6.7
+
+ Pushes an \a item onto the stack, optionally applying a set of
+ \a properties, using the optional \a operation. Returns the item that
+ became current (the last item).
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PushTransition will be used.
+
+ To push several items onto the stack, use \l pushItems().
+
+ \sa initialItem, {Pushing Items}
+*/
+QQuickItem *QQuickStackView::pushItem(QQuickItem *item, const QVariantMap &properties, Operation operation)
+{
+ return pushItems({ item, properties }, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pushItem(component, properties, operation)
+ \overload pushItem()
+ \keyword stackview-pushitem-component-overload
+ \since 6.7
+
+ Pushes a \a component onto the stack, optionally applying a set of
+ \a properties, using the optional \a operation. Returns the item that
+ became current (the last item).
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PushTransition will be used.
+
+ To push several items onto the stack, use \l pushItems().
+
+ \sa initialItem, {Pushing Items}
+*/
+QQuickItem *QQuickStackView::pushItem(QQmlComponent *component, const QVariantMap &properties, Operation operation)
+{
+ return pushItems({ component, properties }, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pushItem(url, properties, operation)
+ \overload pushItem()
+ \keyword stackview-pushitem-url-overload
+ \since 6.7
+
+ Pushes a \a url onto the stack, optionally applying a set of
+ \a properties, using the optional \a operation. Returns the item that
+ became current (the last item).
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PushTransition will be used.
+
+ To push several items onto the stack, use \l pushItems().
+
+ \sa initialItem, {Pushing Items}
+*/
+QQuickItem *QQuickStackView::pushItem(const QUrl &url, const QVariantMap &properties, Operation operation)
+{
+ return pushItems({ url, properties }, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::popToItem(item, operation)
+ \since 6.7
+
+ Pops all items down to (but not including) \a item. Returns the last item
+ removed from the stack.
+
+ If \a item is \c null, a warning is produced and \c null is returned.
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PopTransition will be used.
+
+ \code
+ stackView.popToItem(someItem, StackView.Immediate)
+ \endcode
+
+ \sa clear(), {Popping Items}, {Unwinding Items via Pop}
+*/
+QQuickItem *QQuickStackView::popToItem(QQuickItem *item, Operation operation)
+{
+ Q_D(QQuickStackView);
+ return d->popToItem(item, operation, QQuickStackViewPrivate::CurrentItemPolicy::DoNotPop);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::popToIndex(index, operation)
+ \since 6.7
+
+ Pops all items down to (but not including) \a index. Returns the last item
+ removed from the stack.
+
+ If \a index is out of bounds, a warning is produced and \c null is
+ returned.
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PopTransition will be used.
+
+ \code
+ stackView.popToIndex(stackView.depth - 2, StackView.Immediate)
+ \endcode
+
+ \sa clear(), {Popping Items}, {Unwinding Items via Pop}
+*/
+QQuickItem *QQuickStackView::popToIndex(int index, Operation operation)
+{
+ Q_D(QQuickStackView);
+ if (index < 0 || index >= d->elements.size()) {
+ d->warn(QString::fromLatin1("popToIndex: index %1 is out of bounds (%2 item(s))")
+ .arg(index).arg(d->elements.size()));
+ return nullptr;
+ }
+
+ if (index == d->elements.size() - 1) {
+ // This would pop down to the current item, which is a no-op.
+ return nullptr;
+ }
+
+ QQuickStackElement *element = d->elements.at(index);
+ element->load(this);
+ return d->popToItem(element->item, operation, QQuickStackViewPrivate::CurrentItemPolicy::Pop);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::popCurrentItem(operation)
+ \since 6.7
+
+ Pops \l currentItem from the stack. Returns the last item removed from the
+ stack, or \c null if \l depth was \c 1.
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PopTransition will be used.
+
+ This function is equivalent to \c popToIndex(stackView.currentIndex - 1).
+
+ \sa clear(), {Popping Items}, {Unwinding Items via Pop}
+*/
+QQuickItem *QQuickStackView::popCurrentItem(Operation operation)
+{
+ Q_D(QQuickStackView);
+ if (d->elements.size() == 1) {
+ auto lastItemRemoved = d->elements.last()->item;
+ clear(operation);
+ return lastItemRemoved;
+ }
+ return d->popToItem(d->currentItem, operation, QQuickStackViewPrivate::CurrentItemPolicy::Pop);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(items, operation)
+ \keyword stackview-replacecurrentitem-items-overload
+ \since 6.7
+
+ Pops \l currentItem from the stack and pushes \a items. If the optional
+ \a operation is specified, the relevant transition will be used. Each item
+ can be followed by an optional set of properties that will be applied to
+ that item. Returns the item that became current.
+
+ \include qquickstackview.qdocinc optional-properties-after-each-item
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c ReplaceTransition will be used.
+
+ \code
+ stackView.replaceCurrentItem([item, rectComponent, Qt.resolvedUrl("MyItem.qml")])
+
+ // With properties:
+ stackView.replaceCurrentItem([
+ item, { "color": "red" },
+ rectComponent, { "color": "green" },
+ Qt.resolvedUrl("MyItem.qml"), { "color": "blue" }
+ ])
+ \endcode
+
+ To push a single item, use the relevant overload:
+ \list
+ \li \l {stackview-replacecurrentitem-item-overload}
+ {replaceCurrentItem}(item, properties, operation)
+ \li \l {stackview-replacecurrentitem-component-overload}
+ {replaceCurrentItem}(component, properties, operation)
+ \li \l {stackview-replacecurrentitem-url-overload}
+ {replaceCurrentItem}(url, properties, operation)
+ \endlist
+
+ \sa push(), {Replacing Items}
+*/
+QQuickItem *QQuickStackView::replaceCurrentItem(const QList<QQuickStackViewArg> &args,
+ Operation operation)
+{
+ Q_D(QQuickStackView);
+ const QString operationName = QStringLiteral("replace");
+ if (d->modifyingElements) {
+ d->warnOfInterruption(operationName);
+ return nullptr;
+ }
+
+ QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
+ QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
+
+ QQuickStackElement *currentElement = !d->elements.isEmpty() ? d->elements.top() : nullptr;
+
+ const QList<QQuickStackElement *> stackElements = d->parseElements(args);
+
+ int oldDepth = d->elements.size();
+ QQuickStackElement* exit = nullptr;
+ if (!d->elements.isEmpty())
+ exit = d->elements.pop();
+
+ const bool successfullyReplaced = exit != currentElement
+ ? d->replaceElements(currentElement, stackElements)
+ : d->pushElements(stackElements);
+ if (successfullyReplaced) {
+ d->depthChange(d->elements.size(), oldDepth);
+ if (exit) {
+ exit->removal = true;
+ d->removing.insert(exit);
+ }
+ QQuickStackElement *enter = d->elements.top();
+#if QT_CONFIG(quick_viewtransitions)
+ d->startTransition(QQuickStackTransition::replaceExit(operation, exit, this),
+ QQuickStackTransition::replaceEnter(operation, enter, this),
+ operation == Immediate);
+#endif
+ d->setCurrentItem(enter);
+ }
+
+ return d->currentItem;
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(item, properties, operation)
+ \overload replaceCurrentItem()
+ \keyword stackview-replacecurrentitem-item-overload
+ \since 6.7
+
+ \include qquickstackview.qdocinc {replaceCurrentItem} {item}
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c ReplaceTransition will be used.
+
+ To push several items onto the stack, use
+ \l {stackview-replacecurrentitem-items-overload}
+ {replaceCurrentItem}(items, operation).
+
+ \sa {Replacing Items}
+*/
+QQuickItem *QQuickStackView::replaceCurrentItem(QQuickItem *item, const QVariantMap &properties,
+ Operation operation)
+{
+ const QList<QQuickStackViewArg> args = { QQuickStackViewArg(item), QQuickStackViewArg(properties) };
+ return replaceCurrentItem(args, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(component, properties, operation)
+ \overload replaceCurrentItem()
+ \keyword stackview-replacecurrentitem-component-overload
+ \since 6.7
+
+ \include qquickstackview.qdocinc {replaceCurrentItem} {component}
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c ReplaceTransition will be used.
+
+ To push several items onto the stack, use
+ \l {stackview-replacecurrentitem-items-overload}
+ {replaceCurrentItem}(items, operation).
+
+ \sa {Replacing Items}
+*/
+QQuickItem *QQuickStackView::replaceCurrentItem(QQmlComponent *component, const QVariantMap &properties,
+ Operation operation)
+{
+ const QList<QQuickStackViewArg> args = { QQuickStackViewArg(component), QQuickStackViewArg(properties) };
+ return replaceCurrentItem(args, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(url, properties, operation)
+ \keyword stackview-replacecurrentitem-url-overload
+ \overload replaceCurrentItem()
+ \since 6.7
+
+ \include qquickstackview.qdocinc {replaceCurrentItem} {url}
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c ReplaceTransition will be used.
+
+ To push several items onto the stack, use
+ \l {stackview-replacecurrentitem-items-overload}
+ {replaceCurrentItem}(items, operation).
+
+ \sa {Replacing Items}
+*/
+QQuickItem *QQuickStackView::replaceCurrentItem(const QUrl &url, const QVariantMap &properties,
+ Operation operation)
+{
+ const QList<QQuickStackViewArg> args = { QQuickStackViewArg(url), QQuickStackViewArg(properties) };
+ return replaceCurrentItem(args, operation);
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlproperty bool QtQuick.Controls::StackView::empty
+ \readonly
+
+ This property holds whether the stack is empty.
+
+ \sa depth
+*/
+bool QQuickStackView::isEmpty() const
+{
+ Q_D(const QQuickStackView);
+ return d->elements.isEmpty();
+}
+
+/*!
+ \qmlmethod void QtQuick.Controls::StackView::clear(transition)
+
+ Removes all items from the stack.
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ Since QtQuick.Controls 2.3, a \a transition can be optionally specified. Supported transitions:
+
+ \value StackView.Immediate Clear the stack immediately without any transition (default).
+ \value StackView.PushTransition Clear the stack with a push transition.
+ \value StackView.ReplaceTransition Clear the stack with a replace transition.
+ \value StackView.PopTransition Clear the stack with a pop transition.
+*/
+void QQuickStackView::clear(Operation operation)
+{
+#if !QT_CONFIG(quick_viewtransitions)
+ Q_UNUSED(operation)
+#endif
+ Q_D(QQuickStackView);
+ if (d->elements.isEmpty())
+ return;
+
+ const QString operationName = QStringLiteral("clear");
+ if (d->modifyingElements) {
+ d->warnOfInterruption(operationName);
+ return;
+ }
+
+ const int oldDepth = d->elements.size();
+
+ QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
+ QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
+#if QT_CONFIG(quick_viewtransitions)
+ if (operation != Immediate) {
+ QQuickStackElement *exit = d->elements.pop();
+ exit->removal = true;
+ d->removing.insert(exit);
+ d->startTransition(QQuickStackTransition::popExit(operation, exit, this),
+ QQuickStackTransition::popEnter(operation, nullptr, this), false);
+ }
+#endif
+
+ d->setCurrentItem(nullptr);
+ qDeleteAll(d->elements);
+ d->elements.clear();
+ d->depthChange(0, oldDepth);
+}
+
+/*!
+ \qmlproperty var QtQuick.Controls::StackView::initialItem
+
+ This property holds the initial item that should be shown when the StackView
+ is created. The initial item can be an \l Item, \l Component, or a \l [QML] url.
+ Specifying an initial item is equivalent to:
+ \code
+ Component.onCompleted: stackView.push(myInitialItem)
+ \endcode
+
+ \sa push()
+*/
+QJSValue QQuickStackView::initialItem() const
+{
+ Q_D(const QQuickStackView);
+ return d->initialItem;
+}
+
+void QQuickStackView::setInitialItem(const QJSValue &item)
+{
+ Q_D(QQuickStackView);
+ d->initialItem = item;
+}
+
+#if QT_CONFIG(quick_viewtransitions)
+/*!
+ \qmlproperty Transition QtQuick.Controls::StackView::popEnter
+
+ This property holds the transition that is applied to the item that
+ enters the stack when another item is popped off of it.
+
+ \sa {Customizing StackView}
+*/
+QQuickTransition *QQuickStackView::popEnter() const
+{
+ Q_D(const QQuickStackView);
+ if (d->transitioner)
+ return d->transitioner->removeDisplacedTransition;
+ return nullptr;
+}
+
+void QQuickStackView::setPopEnter(QQuickTransition *enter)
+{
+ Q_D(QQuickStackView);
+ d->ensureTransitioner();
+ if (d->transitioner->removeDisplacedTransition == enter)
+ return;
+
+ d->transitioner->removeDisplacedTransition = enter;
+ emit popEnterChanged();
+}
+
+/*!
+ \qmlproperty Transition QtQuick.Controls::StackView::popExit
+
+ This property holds the transition that is applied to the item that
+ exits the stack when the item is popped off of it.
+
+ \sa {Customizing StackView}
+*/
+QQuickTransition *QQuickStackView::popExit() const
+{
+ Q_D(const QQuickStackView);
+ if (d->transitioner)
+ return d->transitioner->removeTransition;
+ return nullptr;
+}
+
+void QQuickStackView::setPopExit(QQuickTransition *exit)
+{
+ Q_D(QQuickStackView);
+ d->ensureTransitioner();
+ if (d->transitioner->removeTransition == exit)
+ return;
+
+ d->transitioner->removeTransition = exit;
+ emit popExitChanged();
+}
+
+/*!
+ \qmlproperty Transition QtQuick.Controls::StackView::pushEnter
+
+ This property holds the transition that is applied to the item that
+ enters the stack when the item is pushed onto it.
+
+ \sa {Customizing StackView}
+*/
+QQuickTransition *QQuickStackView::pushEnter() const
+{
+ Q_D(const QQuickStackView);
+ if (d->transitioner)
+ return d->transitioner->addTransition;
+ return nullptr;
+}
+
+void QQuickStackView::setPushEnter(QQuickTransition *enter)
+{
+ Q_D(QQuickStackView);
+ d->ensureTransitioner();
+ if (d->transitioner->addTransition == enter)
+ return;
+
+ d->transitioner->addTransition = enter;
+ emit pushEnterChanged();
+}
+
+/*!
+ \qmlproperty Transition QtQuick.Controls::StackView::pushExit
+
+ This property holds the transition that is applied to the item that
+ exits the stack when another item is pushed onto it.
+
+ \sa {Customizing StackView}
+*/
+QQuickTransition *QQuickStackView::pushExit() const
+{
+ Q_D(const QQuickStackView);
+ if (d->transitioner)
+ return d->transitioner->addDisplacedTransition;
+ return nullptr;
+}
+
+void QQuickStackView::setPushExit(QQuickTransition *exit)
+{
+ Q_D(QQuickStackView);
+ d->ensureTransitioner();
+ if (d->transitioner->addDisplacedTransition == exit)
+ return;
+
+ d->transitioner->addDisplacedTransition = exit;
+ emit pushExitChanged();
+}
+
+/*!
+ \qmlproperty Transition QtQuick.Controls::StackView::replaceEnter
+
+ This property holds the transition that is applied to the item that
+ enters the stack when another item is replaced by it.
+
+ \sa {Customizing StackView}
+*/
+QQuickTransition *QQuickStackView::replaceEnter() const
+{
+ Q_D(const QQuickStackView);
+ if (d->transitioner)
+ return d->transitioner->moveTransition;
+ return nullptr;
+}
+
+void QQuickStackView::setReplaceEnter(QQuickTransition *enter)
+{
+ Q_D(QQuickStackView);
+ d->ensureTransitioner();
+ if (d->transitioner->moveTransition == enter)
+ return;
+
+ d->transitioner->moveTransition = enter;
+ emit replaceEnterChanged();
+}
+
+/*!
+ \qmlproperty Transition QtQuick.Controls::StackView::replaceExit
+
+ This property holds the transition that is applied to the item that
+ exits the stack when it is replaced by another item.
+
+ \sa {Customizing StackView}
+*/
+QQuickTransition *QQuickStackView::replaceExit() const
+{
+ Q_D(const QQuickStackView);
+ if (d->transitioner)
+ return d->transitioner->moveDisplacedTransition;
+ return nullptr;
+}
+
+void QQuickStackView::setReplaceExit(QQuickTransition *exit)
+{
+ Q_D(QQuickStackView);
+ d->ensureTransitioner();
+ if (d->transitioner->moveDisplacedTransition == exit)
+ return;
+
+ d->transitioner->moveDisplacedTransition = exit;
+ emit replaceExitChanged();
+}
+#endif
+
+void QQuickStackView::componentComplete()
+{
+ QQuickControl::componentComplete();
+
+ Q_D(QQuickStackView);
+ QScopedValueRollback<QString> operationNameRollback(d->operation, QStringLiteral("initialItem"));
+ QQuickStackElement *element = nullptr;
+ QString error;
+ int oldDepth = d->elements.size();
+ if (QObject *o = d->initialItem.toQObject())
+ element = QQuickStackElement::fromObject(o, this, &error);
+ else if (d->initialItem.isString())
+ element = QQuickStackElement::fromString(d->initialItem.toString(), this, &error);
+ if (!error.isEmpty()) {
+ d->warn(error);
+ delete element;
+ } else if (d->pushElement(element)) {
+ d->depthChange(d->elements.size(), oldDepth);
+ d->setCurrentItem(element);
+ element->setStatus(QQuickStackView::Active);
+ }
+}
+
+void QQuickStackView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ QQuickControl::geometryChange(newGeometry, oldGeometry);
+
+ Q_D(QQuickStackView);
+ for (QQuickStackElement *element : std::as_const(d->elements)) {
+ if (element->item) {
+ if (!element->widthValid)
+ element->item->setWidth(newGeometry.width());
+ if (!element->heightValid)
+ element->item->setHeight(newGeometry.height());
+ }
+ }
+}
+
+bool QQuickStackView::childMouseEventFilter(QQuickItem *item, QEvent *event)
+{
+ // in order to block accidental user interaction while busy/transitioning,
+ // StackView filters out childrens' mouse events. therefore we block all
+ // press events. however, since push() may be called from signal handlers
+ // such as onPressed or onDoubleClicked, we must let the current mouse
+ // grabber item receive the respective mouse release event to avoid
+ // breaking its state (QTBUG-50305).
+ if (event->type() == QEvent::MouseButtonPress)
+ return true;
+ if (event->type() == QEvent::UngrabMouse)
+ return false;
+ QQuickWindow *window = item->window();
+ return window && !window->mouseGrabberItem();
+}
+
+#if QT_CONFIG(quicktemplates2_multitouch)
+void QQuickStackView::touchEvent(QTouchEvent *event)
+{
+ event->ignore(); // QTBUG-65084
+}
+#endif
+
+#if QT_CONFIG(accessibility)
+QAccessible::Role QQuickStackView::accessibleRole() const
+{
+ return QAccessible::LayeredPane;
+}
+#endif
+
+void QQuickStackViewAttachedPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent)
+{
+ Q_Q(QQuickStackViewAttached);
+ int oldIndex = element ? element->index : -1;
+ QQuickStackView *oldView = element ? element->view : nullptr;
+ QQuickStackView::Status oldStatus = element ? element->status : QQuickStackView::Inactive;
+
+ QQuickStackView *newView = qobject_cast<QQuickStackView *>(parent);
+ element = newView ? QQuickStackViewPrivate::get(newView)->findElement(item) : nullptr;
+
+ int newIndex = element ? element->index : -1;
+ QQuickStackView::Status newStatus = element ? element->status : QQuickStackView::Inactive;
+
+ if (oldIndex != newIndex)
+ emit q->indexChanged();
+ if (oldView != newView)
+ emit q->viewChanged();
+ if (oldStatus != newStatus)
+ emit q->statusChanged();
+}
+
+QQuickStackViewAttached::QQuickStackViewAttached(QObject *parent)
+ : QObject(*(new QQuickStackViewAttachedPrivate), parent)
+{
+ Q_D(QQuickStackViewAttached);
+ QQuickItem *item = qobject_cast<QQuickItem *>(parent);
+ if (item) {
+ connect(item, &QQuickItem::visibleChanged, this, &QQuickStackViewAttached::visibleChanged);
+ QQuickItemPrivate::get(item)->addItemChangeListener(d, QQuickItemPrivate::Parent);
+ d->itemParentChanged(item, item->parentItem());
+ } else if (parent) {
+ qmlWarning(parent) << "StackView must be attached to an Item";
+ }
+}
+
+QQuickStackViewAttached::~QQuickStackViewAttached()
+{
+ Q_D(QQuickStackViewAttached);
+ QQuickItem *parentItem = qobject_cast<QQuickItem *>(parent());
+ if (parentItem)
+ QQuickItemPrivate::get(parentItem)->removeItemChangeListener(d, QQuickItemPrivate::Parent);
+}
+
+/*!
+ \qmlattachedproperty int QtQuick.Controls::StackView::index
+ \readonly
+
+ This attached property holds the stack index of the item it's
+ attached to, or \c -1 if the item is not in a stack.
+*/
+int QQuickStackViewAttached::index() const
+{
+ Q_D(const QQuickStackViewAttached);
+ return d->element ? d->element->index : -1;
+}
+
+/*!
+ \qmlattachedproperty StackView QtQuick.Controls::StackView::view
+ \readonly
+
+ This attached property holds the stack view of the item it's
+ attached to, or \c null if the item is not in a stack.
+*/
+QQuickStackView *QQuickStackViewAttached::view() const
+{
+ Q_D(const QQuickStackViewAttached);
+ return d->element ? d->element->view : nullptr;
+}
+
+/*!
+ \qmlattachedproperty enumeration QtQuick.Controls::StackView::status
+ \readonly
+
+ This attached property holds the stack status of the item it's
+ attached to, or \c StackView.Inactive if the item is not in a stack.
+
+ Available values:
+ \value StackView.Inactive The item is inactive (or not in a stack).
+ \value StackView.Deactivating The item is being deactivated (popped off).
+ \value StackView.Activating The item is being activated (becoming the current item).
+ \value StackView.Active The item is active, that is, the current item.
+*/
+QQuickStackView::Status QQuickStackViewAttached::status() const
+{
+ Q_D(const QQuickStackViewAttached);
+ return d->element ? d->element->status : QQuickStackView::Inactive;
+}
+
+/*!
+ \since QtQuick.Controls 2.2 (Qt 5.9)
+ \qmlattachedproperty bool QtQuick.Controls::StackView::visible
+
+ This attached property holds the visibility of the item it's attached to.
+ The value follows the value of \l Item::visible.
+
+ By default, StackView shows incoming items when the enter transition begins,
+ and hides outgoing items when the exit transition ends. Setting this property
+ explicitly allows the default behavior to be overridden, making it possible
+ to keep items that are below the top-most item visible.
+
+ \note The default transitions of most styles slide outgoing items outside the
+ view, and may also animate their opacity. In order to keep a full stack
+ of items visible, consider customizing the \l transitions so that the
+ items underneath can be seen.
+
+ \image qtquickcontrols-stackview-visible.png
+
+ \snippet qtquickcontrols-stackview-visible.qml 1
+*/
+bool QQuickStackViewAttached::isVisible() const
+{
+ const QQuickItem *parentItem = qobject_cast<QQuickItem *>(parent());
+ return parentItem && parentItem->isVisible();
+}
+
+void QQuickStackViewAttached::setVisible(bool visible)
+{
+ Q_D(QQuickStackViewAttached);
+ d->explicitVisible = true;
+ QQuickItem *parentItem = qobject_cast<QQuickItem *>(parent());
+ if (parentItem)
+ parentItem->setVisible(visible);
+}
+
+void QQuickStackViewAttached::resetVisible()
+{
+ Q_D(QQuickStackViewAttached);
+ d->explicitVisible = false;
+ if (!d->element || !d->element->view)
+ return;
+
+ QQuickItem *parentItem = qobject_cast<QQuickItem *>(parent());
+ if (parentItem)
+ parentItem->setVisible(parentItem == d->element->view->currentItem());
+}
+
+/*!
+ \qmlattachedsignal QtQuick.Controls::StackView::activated()
+ \since QtQuick.Controls 2.1 (Qt 5.8)
+
+ This attached signal is emitted when the item it's attached to is activated in the stack.
+
+ \sa status
+*/
+
+/*!
+ \qmlattachedsignal QtQuick.Controls::StackView::deactivated()
+ \since QtQuick.Controls 2.1 (Qt 5.8)
+
+ This attached signal is emitted when the item it's attached to is deactivated in the stack.
+
+ \sa status
+*/
+
+/*!
+ \qmlattachedsignal QtQuick.Controls::StackView::activating()
+ \since QtQuick.Controls 2.1 (Qt 5.8)
+
+ This attached signal is emitted when the item it's attached to is in the process of being
+ activated in the stack.
+
+ \sa status
+*/
+
+/*!
+ \qmlattachedsignal QtQuick.Controls::StackView::deactivating()
+ \since QtQuick.Controls 2.1 (Qt 5.8)
+
+ This attached signal is emitted when the item it's attached to is in the process of being
+ dectivated in the stack.
+
+ \sa status
+*/
+
+/*!
+ \qmlattachedsignal QtQuick.Controls::StackView::removed()
+ \since QtQuick.Controls 2.1 (Qt 5.8)
+
+ This attached signal is emitted when the item it's attached to has been
+ removed from the stack. It can be used to safely destroy an Item that was
+ pushed onto the stack, for example:
+
+ \code
+ Item {
+ StackView.onRemoved: destroy() // Will be destroyed sometime after this call.
+ }
+ \endcode
+
+ \sa status
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qquickstackview_p.cpp"