aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quickcontrols/doc/src/includes/qquickstackview.qdocinc16
-rw-r--r--src/quicktemplates/qquickstackview.cpp210
-rw-r--r--src/quicktemplates/qquickstackview_p.cpp41
-rw-r--r--src/quicktemplates/qquickstackview_p.h11
-rw-r--r--src/quicktemplates/qquickstackview_p_p.h1
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_stackview.qml45
6 files changed, 283 insertions, 41 deletions
diff --git a/src/quickcontrols/doc/src/includes/qquickstackview.qdocinc b/src/quickcontrols/doc/src/includes/qquickstackview.qdocinc
index 0456a8d723..16d4e7374c 100644
--- a/src/quickcontrols/doc/src/includes/qquickstackview.qdocinc
+++ b/src/quickcontrols/doc/src/includes/qquickstackview.qdocinc
@@ -13,3 +13,19 @@ operations:
\value StackView.ReplaceTransition An operation with replace transitions.
\value StackView.PopTransition An operation with pop transitions.
//! [operation-values]
+
+//! [optional-properties-after-each-item]
+The optional properties arguments come after each item, and specify a
+map of initial property values. 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.
+//! [optional-properties-after-each-item]
+
+//! [replaceCurrentItem]
+Pops \l currentItem from the stack and pushes \a \1. If the optional
+\a operation is specified, the relevant transition will be used. If the
+optional \a properties are specified, they will be applied to the item.
+Returns the item that became current.
+//! [replaceCurrentItem]
diff --git a/src/quicktemplates/qquickstackview.cpp b/src/quicktemplates/qquickstackview.cpp
index 2db2e914b6..3bd2f6cfbb 100644
--- a/src/quicktemplates/qquickstackview.cpp
+++ b/src/quicktemplates/qquickstackview.cpp
@@ -843,6 +843,9 @@ void QQuickStackView::pop(QQmlV4Function *args)
}
\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(QQmlV4Function *args)
@@ -934,11 +937,7 @@ void QQuickStackView::replace(QQmlV4Function *args)
\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.
- The optional properties arguments come after each element, and specify a
- map of initial property values. 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.
+ \include qquickstackview.qdocinc optional-properties-after-each-item
\code
stackView.push([item, rectComponent, Qt.resolvedUrl("MyItem.qml")])
@@ -987,41 +986,7 @@ QQuickItem *QQuickStackView::pushItems(QList<QQuickStackViewArg> args, Operation
QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
- QList<QQuickStackElement *> stackElements;
- for (int i = 0; i < args.size(); ++i) {
- const QQuickStackViewArg &arg = args.at(i);
- QVariantMap properties;
- // Look ahead at the next arg in case it contains properties for this
- // Item/Component/URL.
- if (i < args.size() - 1) {
- const QQuickStackViewArg &nextArg = args.at(i + 1);
- // If mProperties isn't empty, the user passed properties.
- // If it is empty, but mItem, mComponent and mUrl also are,
- // then they passed an empty property map.
- if (!nextArg.mProperties.isEmpty()
- || (!nextArg.mItem && !nextArg.mComponent && !nextArg.mUrl.isValid())) {
- properties = nextArg.mProperties;
- ++i;
- }
- }
-
- // Remove any items that are already in the stack, as they can't be in two places at once.
- if (d->findElement(arg.mItem))
- continue;
-
- // We look ahead one index for each Item/Component/URL, so if this arg is
- // a property map, the user has passed two or more in a row.
- if (!arg.mProperties.isEmpty()) {
- qmlWarning(this) << "Properties must come after an Item, Component or URL";
- return nullptr;
- }
-
- QQuickStackElement *element = QQuickStackElement::fromStackViewArg(this, arg);
- QV4::ExecutionEngine *v4Engine = qmlEngine(this)->handle();
- element->properties.set(v4Engine, v4Engine->fromVariant(properties));
- element->qmlCallingContext.set(v4Engine, v4Engine->qmlContext());
- stackElements.append(element);
- }
+ const QList<QQuickStackElement *> stackElements = d->parseElements(args);
#if QT_CONFIG(quick_viewtransitions)
QQuickStackElement *exit = nullptr;
@@ -1209,6 +1174,171 @@ QQuickItem *QQuickStackView::popCurrentItem(Operation operation)
}
/*!
+ \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
diff --git a/src/quicktemplates/qquickstackview_p.cpp b/src/quicktemplates/qquickstackview_p.cpp
index f4b7a42e2d..5d9694906f 100644
--- a/src/quicktemplates/qquickstackview_p.cpp
+++ b/src/quicktemplates/qquickstackview_p.cpp
@@ -109,6 +109,47 @@ QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(int from, QQml
return elements;
}
+QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(const QList<QQuickStackViewArg> &args)
+{
+ Q_Q(QQuickStackView);
+ QList<QQuickStackElement *> stackElements;
+ for (int i = 0; i < args.size(); ++i) {
+ const QQuickStackViewArg &arg = args.at(i);
+ QVariantMap properties;
+ // Look ahead at the next arg in case it contains properties for this
+ // Item/Component/URL.
+ if (i < args.size() - 1) {
+ const QQuickStackViewArg &nextArg = args.at(i + 1);
+ // If mProperties isn't empty, the user passed properties.
+ // If it is empty, but mItem, mComponent and mUrl also are,
+ // then they passed an empty property map.
+ if (!nextArg.mProperties.isEmpty()
+ || (!nextArg.mItem && !nextArg.mComponent && !nextArg.mUrl.isValid())) {
+ properties = nextArg.mProperties;
+ ++i;
+ }
+ }
+
+ // Remove any items that are already in the stack, as they can't be in two places at once.
+ if (findElement(arg.mItem))
+ continue;
+
+ // We look ahead one index for each Item/Component/URL, so if this arg is
+ // a property map, the user has passed two or more in a row.
+ if (!arg.mProperties.isEmpty()) {
+ qmlWarning(q) << "Properties must come after an Item, Component or URL";
+ return {};
+ }
+
+ QQuickStackElement *element = QQuickStackElement::fromStackViewArg(q, arg);
+ QV4::ExecutionEngine *v4Engine = qmlEngine(q)->handle();
+ element->properties.set(v4Engine, v4Engine->fromVariant(properties));
+ element->qmlCallingContext.set(v4Engine, v4Engine->qmlContext());
+ stackElements.append(element);
+ }
+ return stackElements;
+}
+
QQuickStackElement *QQuickStackViewPrivate::findElement(QQuickItem *item) const
{
if (item) {
diff --git a/src/quicktemplates/qquickstackview_p.h b/src/quicktemplates/qquickstackview_p.h
index aa67af8edb..eb9c2b033d 100644
--- a/src/quicktemplates/qquickstackview_p.h
+++ b/src/quicktemplates/qquickstackview_p.h
@@ -53,7 +53,7 @@ public:
#endif
private:
- friend class QQuickStackView;
+ friend class QQuickStackViewPrivate;
friend class QQuickStackElement;
QQuickItem *mItem = nullptr;
@@ -159,6 +159,15 @@ public:
Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *popToIndex(int index, Operation operation = PopTransition);
Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *popCurrentItem(Operation operation = PopTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *replaceCurrentItem(const QList<QQuickStackViewArg> &args,
+ Operation operation = ReplaceTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *replaceCurrentItem(QQuickItem *item,
+ const QVariantMap &properties = {}, Operation operation = ReplaceTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *replaceCurrentItem(QQmlComponent *component,
+ const QVariantMap &properties = {}, Operation operation = ReplaceTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *replaceCurrentItem(const QUrl &url,
+ const QVariantMap &properties = {}, Operation operation = ReplaceTransition);
+
// 2.3 (Qt 5.10)
bool isEmpty() const;
diff --git a/src/quicktemplates/qquickstackview_p_p.h b/src/quicktemplates/qquickstackview_p_p.h
index ac1d5c326f..6acfaa93a1 100644
--- a/src/quicktemplates/qquickstackview_p_p.h
+++ b/src/quicktemplates/qquickstackview_p_p.h
@@ -50,6 +50,7 @@ public:
void setCurrentItem(QQuickStackElement *element);
QList<QQuickStackElement *> parseElements(int from, QQmlV4Function *args, QStringList *errors);
+ QList<QQuickStackElement *> parseElements(const QList<QQuickStackViewArg> &args);
QQuickStackElement *findElement(QQuickItem *item) const;
QQuickStackElement *findElement(const QV4::Value &value) const;
QQuickStackElement *createElement(const QV4::Value &value, const QQmlRefPointer<QQmlContextData> &context, QString *error);
diff --git a/tests/auto/quickcontrols/controls/data/tst_stackview.qml b/tests/auto/quickcontrols/controls/data/tst_stackview.qml
index 00bf3e3829..3ab6e1d9c2 100644
--- a/tests/auto/quickcontrols/controls/data/tst_stackview.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_stackview.qml
@@ -720,6 +720,51 @@ TestCase {
compare(control.currentItem, item8)
}
+ function test_replaceNew() {
+ let control = createTemporaryObject(stackViewComponent, testCase)
+ verify(control)
+
+ // replace(item) - replace currentItem
+ let item1 = itemComponent.createObject(control, {objectName:"1"})
+ compare(control.replaceCurrentItem(item1, {}, StackView.Immediate), item1)
+ compare(control.depth, 1)
+ compare(control.currentItem, item1)
+
+ // replace([item]) - replace currentItem
+ let item2 = itemComponent.createObject(control, {objectName:"2"})
+ compare(control.replaceCurrentItem(item2, {}, StackView.Immediate), item2)
+ compare(control.depth, 1)
+ compare(control.currentItem, item2)
+
+ // replace(item, {properties}) - replace currentItem
+ let item3 = itemComponent.createObject(control)
+ compare(control.replaceCurrentItem(item3, {objectName:"3"}, StackView.Immediate), item3)
+ compare(item3.objectName, "3")
+ compare(control.depth, 1)
+ compare(control.currentItem, item3)
+
+ // replace([item, {properties}]) - replace currentItem
+ let item4 = itemComponent.createObject(control)
+ compare(control.replaceCurrentItem([item4, {objectName:"4"}], StackView.Immediate), item4)
+ compare(item4.objectName, "4")
+ compare(control.depth, 1)
+ compare(control.currentItem, item4)
+
+ // replace(component, {properties}) - replace currentItem
+ let item5 = control.replaceCurrentItem(itemComponent, {objectName:"5"}, StackView.Immediate)
+ compare(control.currentItem, item5)
+ compare(item5.objectName, "5")
+ compare(control.depth, 1)
+ compare(control.currentItem, item5)
+
+ // replace([component, {properties}]) - replace currentItem
+ let item6 = control.replaceCurrentItem([itemComponent, {objectName:"6"}], StackView.Immediate)
+ compare(control.currentItem, item6)
+ compare(item6.objectName, "6")
+ compare(control.depth, 1)
+ compare(control.currentItem, item6)
+ }
+
function test_clear() {
var control = createTemporaryObject(stackViewComponent, testCase)
verify(control)