aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ-P Nurmi <jpnurmi@theqtcompany.com>2015-03-28 21:51:10 +0100
committerJ-P Nurmi <jpnurmi@theqtcompany.com>2015-04-09 21:54:23 +0000
commit1315f9d97170d7f1bb8ef28ab95e0fafc3e72b68 (patch)
treed7d7b58282ecdd07da4b6ef5305e1d091c623c05
parentb898b8c2317cd2c7349a60eba0c9bfea395e614c (diff)
Implement StackView in C++
Change-Id: Ia5387aa16325453c676a2542f80c827d4c069ca9 Reviewed-by: J-P Nurmi <jpnurmi@theqtcompany.com>
-rw-r--r--src/controls/controls.pri2
-rw-r--r--src/controls/qquickabstractstackview.cpp565
-rw-r--r--src/controls/qquickabstractstackview_p.cpp507
-rw-r--r--src/controls/qquickabstractstackview_p.h84
-rw-r--r--src/controls/qquickabstractstackview_p_p.h158
-rw-r--r--src/imports/controls/StackView.js63
-rw-r--r--src/imports/controls/StackView.qml914
-rw-r--r--src/imports/controls/StackViewDelegate.qml142
-rw-r--r--src/imports/controls/StackViewTransition.qml56
-rw-r--r--src/imports/controls/controls.pro3
-rw-r--r--src/imports/controls/qmldir2
-rw-r--r--tests/auto/controls/data/tst_stackview.qml230
-rw-r--r--tests/benchmarks/creationtime/tst_creationtime.cpp1
13 files changed, 1282 insertions, 1445 deletions
diff --git a/src/controls/controls.pri b/src/controls/controls.pri
index b1d2a0ea..aea9b06f 100644
--- a/src/controls/controls.pri
+++ b/src/controls/controls.pri
@@ -20,6 +20,7 @@ HEADERS += \
$$PWD/qquickabstractscrollindicator_p.h \
$$PWD/qquickabstractslider_p.h \
$$PWD/qquickabstractstackview_p.h \
+ $$PWD/qquickabstractstackview_p_p.h \
$$PWD/qquickabstractswitch_p.h \
$$PWD/qquickabstracttabbar_p.h \
$$PWD/qquickabstracttabbutton_p.h \
@@ -49,6 +50,7 @@ SOURCES += \
$$PWD/qquickabstractscrollindicator.cpp \
$$PWD/qquickabstractslider.cpp \
$$PWD/qquickabstractstackview.cpp \
+ $$PWD/qquickabstractstackview_p.cpp \
$$PWD/qquickabstractswitch.cpp \
$$PWD/qquickabstracttabbar.cpp \
$$PWD/qquickabstracttabbutton.cpp \
diff --git a/src/controls/qquickabstractstackview.cpp b/src/controls/qquickabstractstackview.cpp
index a849bd94..e5496697 100644
--- a/src/controls/qquickabstractstackview.cpp
+++ b/src/controls/qquickabstractstackview.cpp
@@ -35,7 +35,10 @@
****************************************************************************/
#include "qquickabstractstackview_p.h"
-#include "qquickabstractcontainer_p_p.h"
+#include "qquickabstractstackview_p_p.h"
+
+#include <QtQml/qjsvalue.h>
+#include <QtQml/qqmlengine.h>
QT_BEGIN_NAMESPACE
@@ -50,243 +53,407 @@ QT_BEGIN_NAMESPACE
TODO
*/
-class QQuickStackElement
+QQuickAbstractStackView::QQuickAbstractStackView(QQuickItem *parent) :
+ QQuickAbstractContainer(*(new QQuickAbstractStackViewPrivate), parent)
{
- QQuickStackElement() : ownItem(false), item(Q_NULLPTR), ownComponent(false), component(Q_NULLPTR) { }
-
-public:
- static QQuickStackElement *fromString(const QString &str, QQmlEngine *engine, QObject *parent)
- {
- QQuickStackElement *element = new QQuickStackElement;
- element->component = new QQmlComponent(engine, QUrl(str), parent);
- element->ownComponent = true;
- return element;
- }
+ setFlag(ItemIsFocusScope);
+}
- static QQuickStackElement *fromObject(QObject *object)
- {
- QQuickStackElement *element = new QQuickStackElement;
- element->component = qobject_cast<QQmlComponent *>(object);
- element->item = qobject_cast<QQuickItem *>(object);
- return element;
+QQuickAbstractStackView::~QQuickAbstractStackView()
+{
+ Q_D(QQuickAbstractStackView);
+ if (d->transitioner) {
+ d->transitioner->setChangeListener(Q_NULLPTR);
+ delete d->transitioner;
}
+ qDeleteAll(d->elements);
+}
- bool ownItem;
- QQuickItem *item;
-
- bool ownComponent;
- QQmlComponent *component;
-};
+QQuickStackAttached *QQuickAbstractStackView::qmlAttachedProperties(QObject *object)
+{
+ QQuickItem *item = qobject_cast<QQuickItem *>(object);
+ if (!item) {
+ qmlInfo(object) << "StackView must be attached to an Item";
+ return Q_NULLPTR;
+ }
+ return new QQuickStackAttached(item);
+}
-class QQuickAbstractStackViewPrivate : public QQuickAbstractContainerPrivate
+/*!
+ \qmlproperty bool QtQuickControls2::StackView::busy
+ \readonly
+ This property holds whether a transition is running.
+*/
+bool QQuickAbstractStackView::busy() const
{
- Q_DECLARE_PUBLIC(QQuickAbstractStackView)
+ Q_D(const QQuickAbstractStackView);
+ return d->transitioner && !d->transitioner->runningJobs.isEmpty();
+}
-public:
- QQuickAbstractStackViewPrivate() : busy(false), depth(0), currentItem(Q_NULLPTR) { }
+/*!
+ \qmlproperty int QtQuickControls2::StackView::depth
+ \readonly
+ This property holds the number of items currently pushed onto the stack.
+*/
+int QQuickAbstractStackView::depth() const
+{
+ Q_D(const QQuickAbstractStackView);
+ return d->elements.count();
+}
- void setBusy(bool busy);
- void setDepth(int depth);
- void setCurrentItem(QQuickItem *item);
+/*!
+ \qmlproperty Item QtQuickControls2::StackView::currentItem
+ \readonly
+ This property holds the current top-most item in the stack.
+*/
+QQuickItem *QQuickAbstractStackView::currentItem() const
+{
+ Q_D(const QQuickAbstractStackView);
+ return d->currentItem;
+}
- QList<QQuickStackElement *> createElements(const QV4::ScopedValue &value);
- QQuickItem *pushElements(const QList<QQuickStackElement *> &elements, QQuickAbstractStackView::Operation operation);
+/*!
+ \qmlmethod Item QtQuickControls2::StackView::get(index, behavior = DontLoad)
- bool busy;
- int depth;
- QVariant initialItem;
- QQuickItem *currentItem;
- QStack<QQuickStackElement *> elements;
-};
+ Supported behavior values:
+ \li StackView.DontLoad
+ \li StackView.ForceLoad
-void QQuickAbstractStackViewPrivate::setBusy(bool value)
+ TODO
+*/
+QQuickItem *QQuickAbstractStackView::get(int index, LoadBehavior behavior)
{
- Q_Q(QQuickAbstractStackView);
- if (busy != value) {
- busy = value;
- emit q->busyChanged();
+ Q_D(QQuickAbstractStackView);
+ QQuickStackElement *element = d->elements.value(index);
+ if (element) {
+ if (behavior == ForceLoad)
+ element->load(this);
+ return element->item;
}
+ return Q_NULLPTR;
}
-void QQuickAbstractStackViewPrivate::setDepth(int value)
+/*!
+ \qmlmethod Item QtQuickControls2::StackView::find(callback, behavior = DontLoad)
+
+ Supported behavior values:
+ \li StackView.DontLoad
+ \li StackView.ForceLoad
+
+ TODO
+*/
+QQuickItem *QQuickAbstractStackView::find(const QJSValue &callback, LoadBehavior behavior)
{
- Q_Q(QQuickAbstractStackView);
- if (depth != value) {
- depth = value;
- emit q->depthChanged();
+ Q_D(QQuickAbstractStackView);
+ QJSValue func(callback);
+ QQmlEngine *engine = qmlEngine(this);
+ if (!engine || !func.isCallable()) // TODO: warning?
+ return Q_NULLPTR;
+
+ for (int i = d->elements.count() - 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 Q_NULLPTR;
}
-void QQuickAbstractStackViewPrivate::setCurrentItem(QQuickItem *item)
+/*!
+ \qmlmethod Item QtQuickControls2::StackView::push(item, properties, operation)
+
+ TODO
+*/
+void QQuickAbstractStackView::push(QQmlV4Function *args)
{
- Q_Q(QQuickAbstractStackView);
- if (currentItem != item) {
- currentItem = item;
- emit q->currentItemChanged();
+ Q_D(QQuickAbstractStackView);
+ if (args->length() <= 0) {
+ qmlInfo(this) << "push: missing arguments";
+ args->setReturnValue(QV4::Encode::null());
+ return;
}
-}
-QList<QQuickStackElement *> QQuickAbstractStackViewPrivate::createElements(const QV4::ScopedValue &value)
-{
- Q_Q(QQuickAbstractStackView);
- QList<QQuickStackElement *> elements;
- if (QV4::String *s = value->asString()) {
- qDebug() << "### STRING:" << s->toQString();
- elements += QQuickStackElement::fromString(s->toQString(), qmlEngine(q), q);
- } else if (QV4::ArrayObject *a = value->asArrayObject()) {
- int len = a->getLength();
- qDebug() << "### ARRAY:" << len;
- for (int i = 0; i < len; ++i) {
- QV4::Scope scope(a->engine());
- QV4::ScopedValue v(scope, a->getIndexed(i));
- elements += createElements(v);
- }
- } else if (const QV4::QObjectWrapper *o =value->as<QV4::QObjectWrapper>()) {
- qDebug() << "### QOBJECT:" << o->object();
- elements += QQuickStackElement::fromObject(o->object());
+ QV4::ExecutionEngine *v4 = args->v4engine();
+ QV4::Scope scope(v4);
+
+ Operation operation = d->elements.isEmpty() ? Immediate : Transition;
+ QV4::ScopedValue lastArg(scope, (*args)[args->length() - 1]);
+ if (lastArg->isInt32())
+ operation = static_cast<Operation>(lastArg->toInt32());
+
+ QList<QQuickStackElement *> elements = d->parseElements(args);
+ if (elements.isEmpty()) {
+ qmlInfo(this) << "push: nothing to push";
+ args->setReturnValue(QV4::Encode::null());
+ return;
+ }
+
+ QQuickStackElement *exit = Q_NULLPTR;
+ if (!d->elements.isEmpty())
+ exit = d->elements.top();
+
+ if (d->pushElements(elements)) {
+ emit depthChanged();
+ QQuickStackElement *enter = d->elements.top();
+ d->pushTransition(enter, exit, boundingRect(), operation == Immediate);
+ d->setCurrentItem(enter->item);
+ }
+
+ if (d->currentItem) {
+ QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(v4, d->currentItem));
+ args->setReturnValue(rv->asReturnedValue());
} else {
- qDebug("### UNKNOWN");
+ args->setReturnValue(QV4::Encode::null());
}
- return elements;
}
-QQuickItem *QQuickAbstractStackViewPrivate::pushElements(const QList<QQuickStackElement *> &elems, QQuickAbstractStackView::Operation operation)
+/*!
+ \qmlmethod Item QtQuickControls2::StackView::pop(item = null, operation = Transition)
+
+ TODO
+*/
+void QQuickAbstractStackView::pop(QQmlV4Function *args)
{
- Q_Q(QQuickAbstractStackView);
- Q_UNUSED(operation); // TODO
- if (!elems.isEmpty()) {
- foreach (QQuickStackElement *elem, elems) {
- elements.push(elem);
+ Q_D(QQuickAbstractStackView);
+ int argc = args->length();
+ if (d->elements.count() <= 1 || argc > 2) {
+ if (argc > 2)
+ qmlInfo(this) << "pop: too many arguments";
+ args->setReturnValue(QV4::Encode::null());
+ return;
+ }
+
+ 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 (!value->isUndefined() && !value->isInt32()) {
+ enter = d->findElement(value);
+ if (!enter) {
+ qmlInfo(this) << "pop: unknown argument: " << value->toQString(); // TODO: safe?
+ args->setReturnValue(QV4::Encode::null());
+ d->elements.push(exit); // restore
+ return;
+ }
}
- emit q->depthChanged();
- // TODO: load
- return elems.last()->item;
}
- return Q_NULLPTR;
+
+ Operation operation = Transition;
+ if (argc > 0) {
+ QV4::ScopedValue lastArg(scope, (*args)[argc - 1]);
+ if (lastArg->isInt32())
+ operation = static_cast<Operation>(lastArg->toInt32());
+ }
+
+ QQuickItem *previousItem = Q_NULLPTR;
+
+ if (d->popElements(enter)) {
+ if (exit)
+ previousItem = exit->item;
+ emit depthChanged();
+ d->popTransition(enter, exit, boundingRect(), operation == Immediate);
+ d->setCurrentItem(enter->item);
+ }
+
+ if (previousItem) {
+ QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(v4, previousItem));
+ args->setReturnValue(rv->asReturnedValue());
+ } else {
+ args->setReturnValue(QV4::Encode::null());
+ }
}
-QQuickAbstractStackView::QQuickAbstractStackView(QQuickItem *parent) :
- QQuickAbstractContainer(*(new QQuickAbstractStackViewPrivate), parent)
+/*!
+ \qmlmethod Item QtQuickControls2::StackView::push(item, properties, operation = Transition)
+
+ TODO
+*/
+void QQuickAbstractStackView::replace(QQmlV4Function *args)
{
- setFlag(ItemIsFocusScope);
+ Q_D(QQuickAbstractStackView);
+ if (args->length() <= 0) {
+ qmlInfo(this) << "replace: missing arguments";
+ args->setReturnValue(QV4::Encode::null());
+ return;
+ }
+
+ QV4::ExecutionEngine *v4 = args->v4engine();
+ QV4::Scope scope(v4);
+
+ Operation operation = d->elements.isEmpty() ? Immediate : Transition;
+ QV4::ScopedValue lastArg(scope, (*args)[args->length() - 1]);
+ if (lastArg->isInt32())
+ operation = static_cast<Operation>(lastArg->toInt32());
+
+ QQuickStackElement *target = Q_NULLPTR;
+ QV4::ScopedValue firstArg(scope, (*args)[0]);
+ if (firstArg->isNull())
+ target = d->elements.value(0);
+ else if (!firstArg->isInt32())
+ target = d->findElement(firstArg);
+
+ QList<QQuickStackElement *> elements = d->parseElements(args, target ? 1 : 0);
+ if (elements.isEmpty()) {
+ qmlInfo(this) << "replace: nothing to push";
+ args->setReturnValue(QV4::Encode::null());
+ return;
+ }
+
+ int depth = d->elements.count();
+ QQuickStackElement* exit = Q_NULLPTR;
+ if (!d->elements.isEmpty())
+ exit = d->elements.pop();
+
+ if (d->replaceElements(target, elements)) {
+ if (depth != d->elements.count())
+ emit depthChanged();
+ QQuickStackElement *enter = d->elements.top();
+ d->replaceTransition(enter, exit, boundingRect(), operation == Immediate);
+ d->setCurrentItem(enter->item);
+ }
+
+ if (d->currentItem) {
+ QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(v4, d->currentItem));
+ args->setReturnValue(rv->asReturnedValue());
+ } else {
+ args->setReturnValue(QV4::Encode::null());
+ }
}
-QQuickAbstractStackView::~QQuickAbstractStackView()
+/*!
+ \qmlmethod Item QtQuickControls2::StackView::clear()
+
+ TODO
+*/
+void QQuickAbstractStackView::clear()
{
Q_D(QQuickAbstractStackView);
+ d->setCurrentItem(Q_NULLPTR);
qDeleteAll(d->elements);
+ d->elements.clear();
+ emit depthChanged();
}
/*!
- \qmlproperty bool QtQuickControls2::StackView::busy
- \readonly
- \c true if a transition is running, and \c false otherwise.
+ \qmlproperty var QtQuickControls2::StackView::initialItem
+
+ This property holds the initial item.
+
+ \sa push()
*/
-bool QQuickAbstractStackView::busy() const
+QVariant QQuickAbstractStackView::initialItem() const
{
Q_D(const QQuickAbstractStackView);
- return d->busy;
+ return d->initialItem;
}
-// TODO: remove
-void QQuickAbstractStackView::setBusy(bool busy)
+void QQuickAbstractStackView::setInitialItem(const QVariant &item)
{
Q_D(QQuickAbstractStackView);
- d->setBusy(busy);
+ d->initialItem = item;
}
/*!
- \qmlproperty int QtQuickControls2::StackView::depth
- \readonly
- The number of items currently pushed onto the stack.
+ \qmlproperty Transition QtQuickControls2::StackView::popEnter
+
+ TODO
*/
-int QQuickAbstractStackView::depth() const
+QQuickTransition *QQuickAbstractStackView::popEnter() const
{
Q_D(const QQuickAbstractStackView);
- return d->depth;
+ if (d->transitioner)
+ return d->transitioner->removeDisplacedTransition;
+ return Q_NULLPTR;
}
-// TODO: remove
-void QQuickAbstractStackView::setDepth(int depth)
+void QQuickAbstractStackView::setPopEnter(QQuickTransition *enter)
{
Q_D(QQuickAbstractStackView);
- d->setDepth(depth);
+ d->ensureTransitioner();
+ if (d->transitioner->removeDisplacedTransition != enter) {
+ d->transitioner->removeDisplacedTransition = enter;
+ emit popEnterChanged();
+ }
}
/*!
- \qmlproperty Item QtQuickControls2::StackView::currentItem
- \readonly
- The currently top-most item in the stack.
+ \qmlproperty Transition QtQuickControls2::StackView::popExit
+
+ TODO
*/
-QQuickItem *QQuickAbstractStackView::currentItem() const
+QQuickTransition *QQuickAbstractStackView::popExit() const
{
Q_D(const QQuickAbstractStackView);
- return d->currentItem;
-}
-
-// TODO: remove
-void QQuickAbstractStackView::setCurrentItem(QQuickItem *item)
-{
- Q_D(QQuickAbstractStackView);
- d->setCurrentItem(item);
+ if (d->transitioner)
+ return d->transitioner->removeTransition;
+ return Q_NULLPTR;
}
-QQuickItem *QQuickAbstractStackView::qpush(QQmlV4Function *args)
+void QQuickAbstractStackView::setPopExit(QQuickTransition *exit)
{
Q_D(QQuickAbstractStackView);
- QV4::ExecutionEngine *v4 = args->v4engine();
- QV4::Scope scope(v4);
-
- Operation operation = d->elements.isEmpty() ? Immediate : Transition;
- QList<QQuickStackElement *> elements;
- for (int i = 0; i < args->length(); ++i) {
- QV4::ScopedValue value(scope, (*args)[i]);
- if (value->isInt32())
- operation = static_cast<Operation>(value->toInt32());
- else
- elements += d->createElements(value);
+ d->ensureTransitioner();
+ if (d->transitioner->removeTransition != exit) {
+ d->transitioner->removeTransition = exit;
+ emit popExitChanged();
}
- return d->pushElements(elements, operation);
}
-QQuickItem *QQuickAbstractStackView::qpop(QQmlV4Function *args)
+/*!
+ \qmlproperty Transition QtQuickControls2::StackView::pushEnter
+
+ TODO
+*/
+QQuickTransition *QQuickAbstractStackView::pushEnter() const
{
- Q_UNUSED(args); // TODO
+ Q_D(const QQuickAbstractStackView);
+ if (d->transitioner)
+ return d->transitioner->addTransition;
return Q_NULLPTR;
}
-void QQuickAbstractStackView::qclear()
+void QQuickAbstractStackView::setPushEnter(QQuickTransition *enter)
{
- // TODO
+ Q_D(QQuickAbstractStackView);
+ d->ensureTransitioner();
+ if (d->transitioner->addTransition != enter) {
+ d->transitioner->addTransition = enter;
+ emit pushEnterChanged();
+ }
}
/*!
- \qmlproperty var QtQuickControls2::StackView::initialItem
-
- The first \l item that should be shown when the StackView is created.
- \a initialItem can take same value as the first argument to \l{StackView::push()}
- {StackView.push()}. Note that this is just a convenience for writing
- \c{Component.onCompleted: stackView.push(myInitialItem)}
-
- Examples:
+ \qmlproperty Transition QtQuickControls2::StackView::pushExit
- \list
- \li initialItem: Qt.resolvedUrl("MyItem.qml")
- \li initialItem: myItem
- \li initialItem: {"item" : Qt.resolvedUrl("MyRectangle.qml"), "properties" : {"color" : "red"}}
- \endlist
- \sa push
+ TODO
*/
-QVariant QQuickAbstractStackView::initialItem() const
+QQuickTransition *QQuickAbstractStackView::pushExit() const
{
Q_D(const QQuickAbstractStackView);
- return d->initialItem;
+ if (d->transitioner)
+ return d->transitioner->addDisplacedTransition;
+ return Q_NULLPTR;
}
-void QQuickAbstractStackView::setInitialItem(const QVariant &item)
+void QQuickAbstractStackView::setPushExit(QQuickTransition *exit)
{
Q_D(QQuickAbstractStackView);
- d->initialItem = item;
+ d->ensureTransitioner();
+ if (d->transitioner->addDisplacedTransition != exit) {
+ d->transitioner->addDisplacedTransition = exit;
+ emit pushExitChanged();
+ }
}
void QQuickAbstractStackView::componentComplete()
@@ -294,59 +461,93 @@ void QQuickAbstractStackView::componentComplete()
QQuickAbstractContainer::componentComplete();
Q_D(QQuickAbstractStackView);
+ QQuickStackElement *element = Q_NULLPTR;
if (QObject *o = d->initialItem.value<QObject *>())
- d->pushElements(QList<QQuickStackElement *>() << QQuickStackElement::fromObject(o), Immediate);
+ element = QQuickStackElement::fromObject(o, this);
else if (d->initialItem.canConvert<QString>())
- d->pushElements(QList<QQuickStackElement *>() << QQuickStackElement::fromString(d->initialItem.toString(), qmlEngine(this), this), Immediate);
+ element = QQuickStackElement::fromString(d->initialItem.toString(), this);
+ if (d->pushElement(element)) {
+ emit depthChanged();
+ d->setCurrentItem(element->item);
+ }
}
-/*!
- \qmltype Stack
- \inherits QtObject
- \instantiates QQuickStackAttached
- \inqmlmodule QtQuick.Controls
- \ingroup navigation
- \brief TODO
+void QQuickAbstractStackView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ QQuickAbstractContainer::geometryChanged(newGeometry, oldGeometry);
- TODO
-*/
+ Q_D(QQuickAbstractStackView);
+ foreach (QQuickStackElement *element, d->elements) {
+ if (element->item) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(element->item);
+ if (!p->widthValid) {
+ element->item->setWidth(newGeometry.width());
+ p->widthValid = false;
+ }
+ if (!p->heightValid) {
+ element->item->setHeight(newGeometry.height());
+ p->heightValid = false;
+ }
+ }
+ }
+}
-class QQuickStackAttachedPrivate : public QObjectPrivate
+void QQuickStackAttachedPrivate::init()
{
-public:
- QQuickStackAttachedPrivate() : status(QQuickStackAttached::Inactive) { }
-
- QQuickStackAttached::Status status;
-};
+ QQuickItem *item = qobject_cast<QQuickItem *>(parent);
+ if (item) {
+ QQuickAbstractStackView *view = qobject_cast<QQuickAbstractStackView *>(item->parentItem());
+ if (view) {
+ element = QQuickAbstractStackViewPrivate::get(view)->findElement(item);
+ if (element)
+ initialized = true;
+ }
+ }
+}
-QQuickStackAttached::QQuickStackAttached(QObject *parent) :
- QObject(*(new QQuickStackAttachedPrivate), parent)
+void QQuickStackAttachedPrivate::reset()
{
+ Q_Q(QQuickStackAttached);
+ int oldIndex = element ? element->index : -1;
+ QQuickAbstractStackView::Status oldStatus = element ? element->status : QQuickAbstractStackView::Inactive;
+
+ element = Q_NULLPTR;
+
+ if (oldIndex != -1)
+ emit q->indexChanged();
+ if (oldStatus != QQuickAbstractStackView::Inactive)
+ emit q->statusChanged();
}
-QQuickStackAttached *QQuickStackAttached::qmlAttachedProperties(QObject *object)
+QQuickStackAttached::QQuickStackAttached(QQuickItem *parent) :
+ QObject(*(new QQuickStackAttachedPrivate), parent)
{
- return new QQuickStackAttached(object);
}
/*!
- \qmlattachedproperty enumeration QtQuickControls2::Stack::status
+ \qmlattachedproperty int QtQuickControls2::StackView::index
TODO
*/
-QQuickStackAttached::Status QQuickStackAttached::status() const
+int QQuickStackAttached::index() const
{
Q_D(const QQuickStackAttached);
- return d->status;
+ if (!d->initialized)
+ const_cast<QQuickStackAttachedPrivate *>(d)->init();
+ return d->element ? d->element->index : -1;
}
-void QQuickStackAttached::setStatus(Status status)
+/*!
+ \qmlattachedproperty enumeration QtQuickControls2::StackView::status
+
+ TODO
+*/
+QQuickAbstractStackView::Status QQuickStackAttached::status() const
{
- Q_D(QQuickStackAttached);
- if (d->status != status) {
- d->status = status;
- emit statusChanged();
- }
+ Q_D(const QQuickStackAttached);
+ if (!d->initialized)
+ const_cast<QQuickStackAttachedPrivate *>(d)->init();
+ return d->element ? d->element->status : QQuickAbstractStackView::Inactive;
}
QT_END_NAMESPACE
diff --git a/src/controls/qquickabstractstackview_p.cpp b/src/controls/qquickabstractstackview_p.cpp
new file mode 100644
index 00000000..76061461
--- /dev/null
+++ b/src/controls/qquickabstractstackview_p.cpp
@@ -0,0 +1,507 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickabstractstackview_p_p.h"
+
+#include <QtQml/qqmllist.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlincubator.h>
+#include <QtQml/private/qqmlcomponent_p.h>
+#include <QtQuick/private/qquickanimation_p.h>
+#include <QtQuick/private/qquicktransition_p.h>
+#include <QtQuick/private/qquickitemviewtransition_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static QQuickStackAttached *attachedStackObject(QQuickItem *item)
+{
+ return qobject_cast<QQuickStackAttached *>(qmlAttachedPropertiesObject<QQuickAbstractStackView>(item, false));
+}
+
+class QQuickStackIncubator : public QQmlIncubator
+{
+public:
+ QQuickStackIncubator(QQuickStackElement *element) : QQmlIncubator(Synchronous), element(element) { }
+
+protected:
+ void setInitialState(QObject *object) Q_DECL_OVERRIDE { element->initItem(object); }
+
+private:
+ QQuickStackElement *element;
+};
+
+QQuickStackElement::QQuickStackElement() : QQuickItemViewTransitionableItem(Q_NULLPTR),
+ index(-1), removal(false), ownItem(false), ownComponent(false),
+ context(Q_NULLPTR), component(Q_NULLPTR), incubator(Q_NULLPTR), view(Q_NULLPTR),
+ status(QQuickAbstractStackView::Inactive)
+{
+}
+
+QQuickStackElement::~QQuickStackElement()
+{
+ if (item) {
+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed);
+
+ QQuickStackAttached *attached = attachedStackObject(item);
+ if (attached)
+ QQuickStackAttachedPrivate::get(attached)->reset();
+ }
+
+ if (ownComponent)
+ delete component;
+
+ if (ownItem && item) {
+ item->setParentItem(Q_NULLPTR);
+ item->deleteLater();
+ item = Q_NULLPTR;
+ } else if (item) {
+ item->setVisible(false);
+ item->setParentItem(originalParent);
+ }
+
+ delete context;
+ delete incubator;
+}
+
+QQuickStackElement *QQuickStackElement::fromString(const QString &str, QQuickAbstractStackView *view)
+{
+ QQuickStackElement *element = new QQuickStackElement;
+ element->component = new QQmlComponent(qmlEngine(view), QUrl(str), view);
+ element->ownComponent = true;
+ return element;
+}
+
+QQuickStackElement *QQuickStackElement::fromObject(QObject *object, QQuickAbstractStackView *view)
+{
+ QQuickStackElement *element = new QQuickStackElement;
+ element->component = qobject_cast<QQmlComponent *>(object);
+ if (!element->component) {
+ element->component = new QQmlComponent(qmlEngine(view), view);
+ element->ownComponent = true;
+ }
+ element->item = qobject_cast<QQuickItem *>(object);
+ if (element->item) {
+ element->originalParent = element->item->parentItem();
+ element->item->setParentItem(view);
+ QQuickItemPrivate::get(element->item)->addItemChangeListener(element, QQuickItemPrivate::Destroyed);
+ }
+ return element;
+}
+
+bool QQuickStackElement::load(QQuickAbstractStackView *parent)
+{
+ view = parent;
+ if (!item) {
+ ownItem = true;
+
+ QQmlContext *creationContext = component->creationContext();
+ if (!creationContext)
+ creationContext = qmlContext(parent);
+ context = new QQmlContext(creationContext);
+ context->setContextObject(parent);
+
+ delete incubator;
+ incubator = new QQuickStackIncubator(this);
+ component->create(*incubator, context);
+ }
+ initProperties();
+ return item;
+}
+
+void QQuickStackElement::initItem(QObject *object)
+{
+ item = qmlobject_cast<QQuickItem *>(object);
+ if (item) {
+ QQmlEngine::setObjectOwnership(item, QQmlEngine::CppOwnership);
+ QQuickItemPrivate *p = QQuickItemPrivate::get(item);
+ if (!p->widthValid) {
+ item->setWidth(view->width());
+ p->widthValid = false;
+ }
+ if (!p->heightValid) {
+ item->setHeight(view->height());
+ p->heightValid = false;
+ }
+ item->setParentItem(view);
+ p->addItemChangeListener(this, QQuickItemPrivate::Destroyed);
+ }
+ initProperties();
+}
+
+void QQuickStackElement::initProperties()
+{
+ if (!properties.isUndefined()) {
+ QQmlComponentPrivate *d = QQmlComponentPrivate::get(component);
+ Q_ASSERT(d && d->engine);
+ QV4::ExecutionEngine *v4 = qmlGlobal.engine();
+ Q_ASSERT(v4);
+ QV4::Scope scope(v4);
+ QV4::ScopedValue ipv(scope, properties.value());
+ d->initializeObjectWithInitialProperties(*qmlGlobal.valueRef(), ipv, item);
+ properties.clear();
+ }
+}
+
+void QQuickStackElement::setIndex(int value)
+{
+ if (index != value) {
+ index = value;
+ QQuickStackAttached *attached = attachedStackObject(item);
+ if (attached)
+ emit attached->indexChanged();
+ }
+}
+
+void QQuickStackElement::setStatus(QQuickAbstractStackView::Status value)
+{
+ if (status != value) {
+ status = value;
+ QQuickStackAttached *attached = attachedStackObject(item);
+ if (attached)
+ emit attached->statusChanged();
+ }
+}
+
+void QQuickStackElement::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
+{
+ if (transitioner)
+ transitioner->transitionNextReposition(this, type, asTarget);
+}
+
+bool QQuickStackElement::prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds)
+{
+ if (transitioner) {
+ // TODO: add force argument to QQuickItemViewTransitionableItem::prepareTransition()?
+ nextTransitionToSet = true;
+ nextTransitionFromSet = true;
+ nextTransitionFrom += QPointF(1, 1);
+ return QQuickItemViewTransitionableItem::prepareTransition(transitioner, index, viewBounds);
+ }
+ return false;
+}
+
+void QQuickStackElement::startTransition(QQuickItemViewTransitioner *transitioner)
+{
+ if (transitioner)
+ QQuickItemViewTransitionableItem::startTransition(transitioner, index);
+}
+
+void QQuickStackElement::itemDestroyed(QQuickItem *)
+{
+ item = Q_NULLPTR;
+}
+
+QQuickAbstractStackViewPrivate::QQuickAbstractStackViewPrivate() : currentItem(Q_NULLPTR), transitioner(Q_NULLPTR)
+{
+}
+
+void QQuickAbstractStackViewPrivate::setCurrentItem(QQuickItem *item)
+{
+ Q_Q(QQuickAbstractStackView);
+ if (currentItem != item) {
+ currentItem = item;
+ if (item)
+ item->setVisible(true);
+ emit q->currentItemChanged();
+ }
+}
+
+static bool initProperties(QQuickStackElement *element, const QV4::Value &props, QQmlV4Function *args)
+{
+ if (props.isObject()) {
+ const QV4::QObjectWrapper *wrapper = props.as<QV4::QObjectWrapper>();
+ if (!wrapper) {
+ QV4::ExecutionEngine *v4 = args->v4engine();
+ element->properties.set(v4, props);
+ element->qmlGlobal.set(v4, args->qmlGlobal());
+ return true;
+ }
+ }
+ return false;
+}
+
+QList<QQuickStackElement *> QQuickAbstractStackViewPrivate::parseElements(QQmlV4Function *args, int from)
+{
+ QV4::ExecutionEngine *v4 = args->v4engine();
+ QV4::Scope scope(v4);
+
+ QList<QQuickStackElement *> elements;
+
+ int argc = args->length();
+ for (int i = from; i < argc; ++i) {
+ QV4::ScopedValue arg(scope, (*args)[i]);
+ if (QV4::ArrayObject *array = arg->asArrayObject()) {
+ int len = array->getLength();
+ for (int j = 0; j < len; ++j) {
+ QV4::ScopedValue value(scope, array->getIndexed(j));
+ QQuickStackElement *element = createElement(value);
+ if (element) {
+ if (j < len - 1) {
+ QV4::ScopedValue props(scope, array->getIndexed(j + 1));
+ if (initProperties(element, props, args))
+ ++j;
+ }
+ elements += element;
+ }
+ }
+ } else {
+ QQuickStackElement *element = createElement(arg);
+ if (element) {
+ if (i < argc - 1) {
+ QV4::ScopedValue props(scope, (*args)[i + 1]);
+ if (initProperties(element, props, args))
+ ++i;
+ }
+ elements += element;
+ }
+ }
+ }
+ return elements;
+}
+
+QQuickStackElement *QQuickAbstractStackViewPrivate::findElement(QQuickItem *item) const
+{
+ if (item) {
+ foreach (QQuickStackElement *e, elements) {
+ if (e->item == item)
+ return e;
+ }
+ }
+ return Q_NULLPTR;
+}
+
+QQuickStackElement *QQuickAbstractStackViewPrivate::findElement(const QV4::Value &value) const
+{
+ if (const QV4::QObjectWrapper *o = value.as<QV4::QObjectWrapper>())
+ return findElement(qobject_cast<QQuickItem *>(o->object()));
+ return Q_NULLPTR;
+}
+
+QQuickStackElement *QQuickAbstractStackViewPrivate::createElement(const QV4::Value &value)
+{
+ Q_Q(QQuickAbstractStackView);
+ if (QV4::String *s = value.asString())
+ return QQuickStackElement::fromString(s->toQString(), q);
+ if (const QV4::QObjectWrapper *o = value.as<QV4::QObjectWrapper>())
+ return QQuickStackElement::fromObject(o->object(), q);
+ return Q_NULLPTR;
+}
+
+bool QQuickAbstractStackViewPrivate::pushElements(const QList<QQuickStackElement *> &elems)
+{
+ Q_Q(QQuickAbstractStackView);
+ if (!elems.isEmpty()) {
+ foreach (QQuickStackElement *e, elems) {
+ e->setIndex(elements.count());
+ elements += e;
+ }
+ return elements.top()->load(q);
+ }
+ return false;
+}
+
+bool QQuickAbstractStackViewPrivate::pushElement(QQuickStackElement *element)
+{
+ if (element)
+ return pushElements(QList<QQuickStackElement *>() << element);
+ return false;
+}
+
+bool QQuickAbstractStackViewPrivate::popElements(QQuickStackElement *element)
+{
+ Q_Q(QQuickAbstractStackView);
+ if (elements.count() > 1) {
+ while (elements.count() > 1 && elements.top() != element) {
+ delete elements.pop();
+ if (!element)
+ break;
+ }
+ }
+ return elements.top()->load(q);
+}
+
+bool QQuickAbstractStackViewPrivate::replaceElements(QQuickStackElement *target, const QList<QQuickStackElement *> &elems)
+{
+ if (target) {
+ while (!elements.isEmpty()) {
+ QQuickStackElement* top = elements.pop();
+ delete top;
+ if (top == target)
+ break;
+ }
+ }
+ return pushElements(elems);
+}
+
+void QQuickAbstractStackViewPrivate::ensureTransitioner()
+{
+ if (!transitioner) {
+ transitioner = new QQuickItemViewTransitioner;
+ transitioner->setChangeListener(this);
+ }
+}
+
+void QQuickAbstractStackViewPrivate::popTransition(QQuickStackElement *enter, QQuickStackElement *exit, const QRectF &viewBounds, bool immediate)
+{
+ Q_Q(QQuickAbstractStackView);
+ if (exit) {
+ exit->setStatus(QQuickAbstractStackView::Deactivating);
+ exit->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, true);
+ }
+ if (enter) {
+ enter->setStatus(QQuickAbstractStackView::Activating);
+ enter->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
+ }
+
+ if (exit && exit->prepareTransition(transitioner, viewBounds)) {
+ if (immediate)
+ completeTransition(exit, transitioner->removeTransition);
+ else
+ exit->startTransition(transitioner);
+ }
+ if (enter && enter->prepareTransition(transitioner, viewBounds)) {
+ if (immediate)
+ completeTransition(enter, transitioner->removeDisplacedTransition);
+ else
+ enter->startTransition(transitioner);
+ }
+
+ if (!immediate)
+ emit q->busyChanged();
+
+ if (transitioner)
+ transitioner->resetTargetLists();
+}
+
+void QQuickAbstractStackViewPrivate::pushTransition(QQuickStackElement *enter, QQuickStackElement *exit, const QRectF &viewBounds, bool immediate)
+{
+ Q_Q(QQuickAbstractStackView);
+ if (enter) {
+ enter->setStatus(QQuickAbstractStackView::Activating);
+ enter->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
+ }
+ if (exit) {
+ exit->setStatus(QQuickAbstractStackView::Deactivating);
+ exit->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
+ }
+
+ if (enter && enter->prepareTransition(transitioner, viewBounds)) {
+ if (immediate)
+ completeTransition(enter, transitioner->addTransition);
+ else
+ enter->startTransition(transitioner);
+ }
+ if (exit && exit->prepareTransition(transitioner, viewBounds)) {
+ if (immediate)
+ completeTransition(exit, transitioner->addDisplacedTransition);
+ else
+ exit->startTransition(transitioner);
+ }
+
+ if (!immediate)
+ emit q->busyChanged();
+
+ if (transitioner)
+ transitioner->resetTargetLists();
+}
+
+void QQuickAbstractStackViewPrivate::replaceTransition(QQuickStackElement *enter, QQuickStackElement *exit, const QRectF &viewBounds, bool immediate)
+{
+ Q_Q(QQuickAbstractStackView);
+ if (enter) {
+ enter->setStatus(QQuickAbstractStackView::Activating);
+ enter->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
+ }
+ if (exit) {
+ exit->removal = true;
+ exit->setStatus(QQuickAbstractStackView::Deactivating);
+ exit->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
+ }
+
+ if (enter && enter->prepareTransition(transitioner, viewBounds)) {
+ if (immediate)
+ completeTransition(enter, transitioner->addTransition);
+ else
+ enter->startTransition(transitioner);
+ }
+ if (exit && exit->prepareTransition(transitioner, viewBounds)) {
+ if (immediate)
+ completeTransition(exit, transitioner->addDisplacedTransition);
+ else
+ exit->startTransition(transitioner);
+ }
+
+ if (!immediate)
+ emit q->busyChanged();
+
+ if (transitioner)
+ transitioner->resetTargetLists();
+}
+
+void QQuickAbstractStackViewPrivate::completeTransition(QQuickStackElement *element, QQuickTransition *transition)
+{
+ if (transition) {
+ // TODO: add a proper way to complete a transition
+ QQmlListProperty<QQuickAbstractAnimation> animations = transition->animations();
+ int count = animations.count(&animations);
+ for (int i = 0; i < count; ++i) {
+ QQuickAbstractAnimation *anim = animations.at(&animations, i);
+ anim->complete();
+ }
+ viewItemTransitionFinished(element);
+ }
+}
+
+void QQuickAbstractStackViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *transitionable)
+{
+ Q_Q(QQuickAbstractStackView);
+
+ QQuickStackElement *element = static_cast<QQuickStackElement *>(transitionable);
+ if (element->status == QQuickAbstractStackView::Activating) {
+ element->setStatus(QQuickAbstractStackView::Active);
+ } else if (element->status == QQuickAbstractStackView::Deactivating) {
+ element->setStatus(QQuickAbstractStackView::Inactive);
+ element->item->setVisible(false);
+ if (element->removal || element->isPendingRemoval())
+ delete element;
+ }
+
+ if (transitioner->runningJobs.isEmpty())
+ emit q->busyChanged();
+}
+
+QT_END_NAMESPACE
diff --git a/src/controls/qquickabstractstackview_p.h b/src/controls/qquickabstractstackview_p.h
index 5bb09464..d15a7be4 100644
--- a/src/controls/qquickabstractstackview_p.h
+++ b/src/controls/qquickabstractstackview_p.h
@@ -53,51 +53,90 @@
QT_BEGIN_NAMESPACE
class QQmlV4Function;
+class QQuickTransition;
+class QQuickStackElement;
+class QQuickStackAttached;
class QQuickAbstractStackViewPrivate;
class Q_QUICKCONTROLS_EXPORT QQuickAbstractStackView : public QQuickAbstractContainer
{
Q_OBJECT
- Q_PROPERTY(bool busy READ busy WRITE setBusy NOTIFY busyChanged FINAL) // TODO: hide setBusy
- Q_PROPERTY(int depth READ depth WRITE setDepth NOTIFY depthChanged FINAL) // TODO: hide setDepth
- Q_PROPERTY(QQuickItem *currentItem READ currentItem WRITE setCurrentItem NOTIFY currentItemChanged FINAL) // TODO: hide setCurrentItem
+ Q_PROPERTY(bool busy READ busy NOTIFY busyChanged FINAL)
+ Q_PROPERTY(int depth READ depth NOTIFY depthChanged FINAL)
+ Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentItemChanged FINAL)
Q_PROPERTY(QVariant initialItem READ initialItem WRITE setInitialItem FINAL)
+ Q_PROPERTY(QQuickTransition *popEnter READ popEnter WRITE setPopEnter NOTIFY popEnterChanged FINAL)
+ Q_PROPERTY(QQuickTransition *popExit READ popExit WRITE setPopExit NOTIFY popExitChanged FINAL)
+ Q_PROPERTY(QQuickTransition *pushEnter READ pushEnter WRITE setPushEnter NOTIFY pushEnterChanged FINAL)
+ Q_PROPERTY(QQuickTransition *pushExit READ pushExit WRITE setPushExit NOTIFY pushExitChanged FINAL)
public:
explicit QQuickAbstractStackView(QQuickItem *parent = Q_NULLPTR);
~QQuickAbstractStackView();
- bool busy() const;
- void setBusy(bool busy); // TODO: hide
+ static QQuickStackAttached *qmlAttachedProperties(QObject *object);
+ bool busy() const;
int depth() const;
- void setDepth(int depth); // TODO: hide
-
QQuickItem *currentItem() const;
- void setCurrentItem(QQuickItem *item); // TODO: hide
+
+ enum Status {
+ Inactive = 0,
+ Deactivating = 1,
+ Activating = 2,
+ Active = 3
+ };
+ Q_ENUM(Status)
QVariant initialItem() const;
void setInitialItem(const QVariant &item);
+ QQuickTransition *popEnter() const;
+ void setPopEnter(QQuickTransition *enter);
+
+ QQuickTransition *popExit() const;
+ void setPopExit(QQuickTransition *exit);
+
+ QQuickTransition *pushEnter() const;
+ void setPushEnter(QQuickTransition *enter);
+
+ QQuickTransition *pushExit() const;
+ void setPushExit(QQuickTransition *exit);
+
+ enum LoadBehavior {
+ DontLoad,
+ ForceLoad
+ };
+ Q_ENUM(LoadBehavior)
+
+ Q_INVOKABLE QQuickItem *get(int index, LoadBehavior behavior = DontLoad);
+ Q_INVOKABLE QQuickItem *find(const QJSValue &callback, LoadBehavior behavior = DontLoad);
+
enum Operation {
Transition,
Immediate
};
Q_ENUM(Operation)
- Q_INVOKABLE QQuickItem *qpush(QQmlV4Function *args);
- Q_INVOKABLE QQuickItem *qpop(QQmlV4Function *args);
+ Q_INVOKABLE void push(QQmlV4Function *args);
+ Q_INVOKABLE void pop(QQmlV4Function *args);
+ Q_INVOKABLE void replace(QQmlV4Function *args);
public Q_SLOTS:
- void qclear();
+ void clear();
Q_SIGNALS:
void busyChanged();
void depthChanged();
void currentItemChanged();
+ void popEnterChanged();
+ void popExitChanged();
+ void pushEnterChanged();
+ void pushExitChanged();
protected:
void componentComplete() Q_DECL_OVERRIDE;
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
private:
Q_DISABLE_COPY(QQuickAbstractStackView)
@@ -109,25 +148,17 @@ class QQuickStackAttachedPrivate;
class Q_QUICKCONTROLS_EXPORT QQuickStackAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(Status status READ status WRITE setStatus NOTIFY statusChanged)
+ Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
+ Q_PROPERTY(QQuickAbstractStackView::Status status READ status NOTIFY statusChanged FINAL)
public:
- explicit QQuickStackAttached(QObject *parent = Q_NULLPTR);
-
- static QQuickStackAttached *qmlAttachedProperties(QObject *object);
-
- enum Status {
- Inactive = 0,
- Deactivating = 1,
- Activating = 2,
- Active = 3
- };
- Q_ENUM(Status)
+ explicit QQuickStackAttached(QQuickItem *parent = Q_NULLPTR);
- Status status() const;
- void setStatus(Status status);
+ int index() const;
+ QQuickAbstractStackView::Status status() const;
Q_SIGNALS:
+ void indexChanged();
void statusChanged();
private:
@@ -137,6 +168,7 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPEINFO(QQuickStackAttached, QML_HAS_ATTACHED_PROPERTIES)
+QML_DECLARE_TYPEINFO(QQuickAbstractStackView, QML_HAS_ATTACHED_PROPERTIES)
+QML_DECLARE_TYPE(QQuickAbstractStackView)
#endif // QQUICKABSTRACTSTACKVIEW_P_H
diff --git a/src/controls/qquickabstractstackview_p_p.h b/src/controls/qquickabstractstackview_p_p.h
new file mode 100644
index 00000000..5cda34f9
--- /dev/null
+++ b/src/controls/qquickabstractstackview_p_p.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKABSTRACTSTACKVIEW_P_P_H
+#define QQUICKABSTRACTSTACKVIEW_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuickControls/private/qquickabstractstackview_p.h>
+#include <QtQuickControls/private/qquickabstractcontainer_p_p.h>
+#include <QtQuick/private/qquickitemviewtransition_p.h>
+#include <QtQuick/private/qquickitemchangelistener_p.h>
+#include <QtQml/private/qv4persistent_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlContext;
+class QQmlComponent;
+class QQmlIncubator;
+
+class QQuickStackElement : public QQuickItemViewTransitionableItem, public QQuickItemChangeListener
+{
+ QQuickStackElement();
+
+public:
+ ~QQuickStackElement();
+
+ static QQuickStackElement *fromString(const QString &str, QQuickAbstractStackView *view);
+ static QQuickStackElement *fromObject(QObject *object, QQuickAbstractStackView *view);
+
+ bool load(QQuickAbstractStackView *parent);
+ void initItem(QObject *object);
+ void initProperties();
+
+ void setIndex(int index);
+ void setStatus(QQuickAbstractStackView::Status status);
+
+ void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
+ bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
+ void startTransition(QQuickItemViewTransitioner *transitioner);
+
+ void itemDestroyed(QQuickItem *item) Q_DECL_OVERRIDE;
+
+ int index;
+ bool removal;
+ bool ownItem;
+ bool ownComponent;
+ QQmlContext *context;
+ QQmlComponent *component;
+ QQmlIncubator *incubator;
+ QQuickAbstractStackView *view;
+ QPointer<QQuickItem> originalParent;
+ QQuickAbstractStackView::Status status;
+ QV4::PersistentValue properties;
+ QV4::PersistentValue qmlGlobal;
+};
+
+class QQuickAbstractStackViewPrivate : public QQuickAbstractContainerPrivate, public QQuickItemViewTransitionChangeListener
+{
+ Q_DECLARE_PUBLIC(QQuickAbstractStackView)
+
+public:
+ QQuickAbstractStackViewPrivate();
+
+ static QQuickAbstractStackViewPrivate *get(QQuickAbstractStackView *view)
+ {
+ return view->d_func();
+ }
+
+ void setCurrentItem(QQuickItem *item);
+
+ QList<QQuickStackElement *> parseElements(QQmlV4Function *args, int from = 0);
+ QQuickStackElement *findElement(QQuickItem *item) const;
+ QQuickStackElement *findElement(const QV4::Value &value) const;
+ QQuickStackElement *createElement(const QV4::Value &value);
+ bool pushElements(const QList<QQuickStackElement *> &elements);
+ bool pushElement(QQuickStackElement *element);
+ bool popElements(QQuickStackElement *element);
+ bool replaceElements(QQuickStackElement *element, const QList<QQuickStackElement *> &elements);
+
+ void ensureTransitioner();
+ void popTransition(QQuickStackElement *enter, QQuickStackElement *exit, const QRectF &viewBounds, bool immediate);
+ void pushTransition(QQuickStackElement *enter, QQuickStackElement *exit, const QRectF &viewBounds, bool immediate);
+ void replaceTransition(QQuickStackElement *enter, QQuickStackElement *exit, const QRectF &viewBounds, bool immediate);
+ void completeTransition(QQuickStackElement *element, QQuickTransition *transition);
+
+ void viewItemTransitionFinished(QQuickItemViewTransitionableItem *item) Q_DECL_OVERRIDE;
+
+ QVariant initialItem;
+ QQuickItem *currentItem;
+ QStack<QQuickStackElement *> elements;
+ QQuickItemViewTransitioner *transitioner;
+};
+
+class QQuickStackAttachedPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickStackAttached)
+
+public:
+ QQuickStackAttachedPrivate() : initialized(false), element(Q_NULLPTR) { }
+
+ static QQuickStackAttachedPrivate *get(QQuickStackAttached *attached)
+ {
+ return attached->d_func();
+ }
+
+ void init();
+ void reset();
+
+ bool initialized;
+ QQuickStackElement *element;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKABSTRACTSTACKVIEW_P_P_H
diff --git a/src/imports/controls/StackView.js b/src/imports/controls/StackView.js
deleted file mode 100644
index 37e2066d..00000000
--- a/src/imports/controls/StackView.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the Qt Quick Controls module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL3$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPLv3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or later as published by the Free
-** Software Foundation and appearing in the file LICENSE.GPL included in
-** the packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 2.0 requirements will be
-** met: http://www.gnu.org/licenses/gpl-2.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-var stackView = [];
-
-function push(p)
-{
- if (!p)
- return
- stackView.push(p)
- __depth++
- return p
-}
-
-function pop()
-{
- if (stackView.length === 0)
- return null
- var p = stackView.pop()
- __depth--
- return p
-}
-
-function current()
-{
- if (stackView.length === 0)
- return null
- return stackView[stackView.length-1]
-}
-
diff --git a/src/imports/controls/StackView.qml b/src/imports/controls/StackView.qml
index 2c75f063..396778b7 100644
--- a/src/imports/controls/StackView.qml
+++ b/src/imports/controls/StackView.qml
@@ -34,921 +34,25 @@
**
****************************************************************************/
-import QtQuick 2.2
+import QtQuick 2.4
import QtQuick.Controls 2.0
-import "StackView.js" as JSArray
-
-/*!
- \qmltype StackView
- \inherits Item
- \ingroup views
- \inqmlmodule QtQuick.Controls
- \since 5.1
-
- \brief Provides a stack-based navigation model.
-
- \image stackview.png
-
- StackView implements a stack-based navigation model, which can be used
- with a set of interlinked information pages. Items are pushed onto the stack
- as the user navigates deeper into the material, and popped off again when he
- chooses to go back.
-
- The \l{Qt Quick Controls - Touch Gallery}{touch gallery} example is a good
- starting point to understand how StackView works. The following snippet
- from the example shows how it can be used:
-
- \qml
- StackView {
- id: stack
- initialItem: view
-
- Component {
- id: view
-
- MouseArea {
- Text {
- text: stack.depth
- anchors.centerIn: parent
- }
- onClicked: stack.push(view)
- }
- }
- }
- \endqml
-
- \section1 Using StackView in an Application
- Using the StackView in the application is typically a simple matter of adding
- the StackView as a child of 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 commonly loaded assigning it to \l initialItem.
-
- \note Items pushed onto the stack view have \l{Supported Attached Properties}{Stack attached properties}.
-
- \section1 Basic Navigation
- There are three primary navigation operations in StackView: push(), pop(), and
- replace (replace by specifying argument \c replace to push()).
- 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 in that it replaces the topmost item on the stack with
- a new item (but the applied transtition might be different). The topmost item
- in the stack corresponds to the one that is \l{StackView::currentItem} {currently}
- visible on the screen. That means that "push" is the logical equivalent of navigating
- forward or deeper into the application, "pop" is the equivalent of navigating back,
- and "replace" is the equivalent of replacing the current item.
-
- 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.
- For this use case, it is possible to specify an item as a parameter for pop().
- This is called an "unwind" operation as the stack gets unwound to the specified item.
- If the item is not found then the stack unwinds until there is only a single item in
- the stack, which becomes the current item. To explicitly unwind to the bottom
- of the stack it is recommended to use \l{pop()} {pop(null)}, though technically any
- non-existent item will do.
-
- Given the stack [A, B, C]:
-
- \list
- \li \l{push()}{push(D)} => [A, B, C, D] - "push" transition animation between C and D
- \li pop() => [A, B] - "pop" transition animation between C and B
- \li \l{push()}{push(D, replace)} => [A, B, D] - "replace" transition between C and D
- \li \l{pop()}{pop(A)} => [A] - "pop" transition between C and A
- \endlist
-
- \note Note that when the stack is empty, a push() will not perform a
- transition animation because there is nothing to transition from (typically during
- application start-up). A pop() on a stack with depth 1 or 0 is a no-operation.
- If removing all items from the stack is needed, a separate function clear() is
- available.
-
- Calling push() returns the item that was pushed onto the stack.
- Calling pop() returns the item that was popped off the stack. When pop() is
- called in an unwind operation the top-most item (the first item that was
- popped, which will also be the one transitioning out) is returned.
-
- \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 front item (and possibly a section item) that would normally have
- to be navigated through to get to the article concerned. 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, will involve that all the items in that array will
- be pushed onto the stack. The transition animation, however, will be conducted as
- if only the last item in the array was pushed onto the stack. The normal semantics
- of push() apply for deep linking, meaning that push() adds whatever is pushed onto
- the stack. Note also that only the last item of the array will be loaded.
- The rest will be lazy-loaded as needed when entering the screen upon subsequent
- calls to pop (or when requesting the item by using \a 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{push()}{push([D, E, F], replace)} => [A, B, D, E, F] - "replace" transition animation between C and F
- \li clear(); \l{push()}{push([D, E, F])} => [D, E, F] - no transition animation (since the stack was empty)
- \endlist
-
- \section1 Pushing items
-
- An item pushed onto the StackView can be either an Item, a URL, a string
- with a URL, or a Component. To push it, assign it to a property "item"
- inside a property list, and send it as an argument to \l{StackView::push}{push}:
-
- \code
- stackView.push({item: yourItem})
- \endcode
-
- The list can contain several properties that control how the item should be pushed:
- \list
- \li \c item: this property is required, and holds the item to be pushed.
- \li \c properties: a list of QML properties to be assigned to the item upon push. These
- properties will be copied into the item at load time, or when the item will become
- the current item (normally upon push).
- \li \c immediate: set this property to \c true to skip transition effects. When pushing
- an array, this property only needs to be set on the first element to make the
- whole operation immediate.
- \li \c replace: set this property to replace the current item on the stack. When pushing
- an array, you only need to set this property on the first element to replace
- as many elements on the stack as inside the array.
- \li \c destroyOnPop: set this boolean to true if StackView needs to destroy the item when
- it is popped off the stack. By default (if \a destroyOnPop is not specified), StackView
- will destroy items pushed as components or URLs. Items not destroyed will be re-parented
- back to the original parents they had before being pushed onto the stack and hidden.
- If you need to set this property, do it with care, so that items are not leaked.
- \endlist
-
- If the only argument needed is "item", the following short-hand notation can be applied:
-
- \code
- stackView.push(yourItem)
- \endcode
-
- You can push several items in one go by using an array of property lists. This is
- optimizing compared to pushing items one by one, since StackView then can load only the
- last item in the list. The rest will be loaded as they are about to become
- the current item (which happens when the stack is popped). The following example shows how
- to push an array of items:
-
- \code
- stackView.push([{item: yourItem1}, {item: yourItem2}])
- \endcode
-
- If an inline item is pushed, the item is temporarily re-parented into the StackView. When the item
- is later popped off, it gets re-parented back to its original owner again.
- If, however, an item is pushed as a component or a URL, the actual item will be created as an
- item from that component. This happens automatically when the item is about to become the current
- item in the stack. Ownership of the item will then normally be taken by the StackView. It will as
- such automatically destroy the item when it is later popped off. The component that declared the item, by
- contrast, remains in the ownership of the application and is not destroyed by the stack.
- This can be overridden by explicitly setting \c{destroyOnPop} in the list of arguments given to push.
-
- If the \c properties to be pushed are specified, they will be copied into the item at loading time
- (in case of a component or URL), or when the item will become the current item (in case of an inline
- item). The following example shows how this can be done:
-
- \code
- stackView.push({item: someItem, properties: {fgcolor: "red", bgcolor: "blue"}})
- \endcode
-
-
- \note Note that if an item is declared inside another item, and if that parent gets destroyed,
- (even if a component was used), that child item will also be destroyed.
- This follows normal Qt parent-child destruction rules, but sometimes comes as a surprise
- for developers.
-
- \section1 Lifecycle
- An item's lifecycle in the StackView can have the following transitions:
- \list 1
- \li instantiation
- \li inactive
- \li activating
- \li active
- \li deactivating
- \li inactive
- \li destruction
- \endlist
-
- It can move any number of times between inactive and active. When an item is activated,
- it's visible on the screen and is considered to be the current item. An item
- in a StackView that is not visible is not activated, even if the item is currently the
- top-most item in the stack. When the stack becomes visible, the item that is top-most gets
- activated. Likewise if the stack is then hidden, the topmost item would be deactivated.
- Popping the item off the top of the stack at this point would not result in further
- deactivation since the item is not active.
-
- There is an attached \l{Stack::status}{Stack.status} property that tracks the lifecycle. The
- status values list is an enumeration with values \c Stack.Inactive, \c Stack.Activating,
- \c Stack.Active and \c Stack.Deactivating. Combined with the normal \c Component.onComplete and
- \c Component.onDestruction signals the entire lifecycle is thus:
-
- \list
- \li Created: Component.onCompleted()
- \li Activating: Stack.onStatusChanged (Stack.status is Stack.Activating)
- \li Acivated: Stack.onStatusChanged (Stack.status is Stack.Active)
- \li Deactivating: Stack.onStatusChanged (Stack.status is Stack.Deactivating)
- \li Deactivated: Stack.onStatusChanged (Stack.status is Stack.Inactive)
- \li Destruction: Component.onDestruction()
- \endlist
-
- \section1 Finding items
- Sometimes it is necessary to search for an item, for example, in order to unwind the stack to
- an item to which the application does not have a reference. This is facilitated using a
- function find() in StackView. The find() function takes a callback function as its
- only argument. The callback gets invoked for each item in the stack (starting at the top).
- If the callback returns true, then it signals that a match has been found and the find()
- function returns that item. If the callback fails to return true (no match is found),
- then find() returns \c null.
-
- The code below searches for an item in the stack that has a name "order_id" and then unwinds to
- that item. Note that since find() returns \c {null} if no item is found, and since pop unwinds to
- the bottom of the stack if null is given as the target item, the code works well even in
- case no matching item is found.
-
- \code
- 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)}. You should use
- this function if your item depends on another item in the stack, as the function will
- ensure that the item at the given index gets loaded before it is returned.
-
- \code
- previousItem = stackView.get(myItem.Stack.index - 1));
- \endcode
-
- \section1 Transitions
-
- A transition is performed whenever a item is pushed or popped, and consists of
- two items: enterItem and exitItem. The StackView itself will never move items
- around, but instead delegate the job to an external animation set provided
- by the style or the application developer. How items should visually enter and leave the stack
- (and the geometry they should end up with) is therefore completely controlled from the outside.
-
- When the transition starts, the StackView will search for a transition that
- matches the operation executed. There are three transitions to choose
- from: pushTransition, popTransition, and replaceTransition. Each implements how
- enterItem should animate in, and exitItem out. The transitions are
- collected inside a StackViewDelegate object assigned to
- \l {StackView::delegate}{delegate}. By default, popTransition and
- replaceTransition will be the same as pushTransition, unless you set them
- to something else.
-
- A simple fade transition could be implemented as:
-
- \qml
- StackView {
- delegate: StackViewDelegate {
- function transitionFinished(properties)
- {
- properties.exitItem.opacity = 1
- }
-
- pushTransition: StackViewTransition {
- PropertyAnimation {
- target: enterItem
- property: "opacity"
- from: 0
- to: 1
- }
- PropertyAnimation {
- target: exitItem
- property: "opacity"
- from: 1
- to: 0
- }
- }
- }
- }
- \endqml
-
- PushTransition needs to inherit from StackViewTransition, which is a ParallelAnimation that
- contains the properties \c enterItem and \c exitItem. You set the target of your
- inner animations to those items. Since the same items instance can be pushed several
- times to a StackView, you should always override
- \l {StackViewDelegate::transitionFinished()}{StackViewDelegate.transitionFinished()}.
- Implement this function to reset any properties animated on the exitItem so that later
- transitions can expect the items to be in a default state.
-
- A more complex example could look like the following. Here, the items are lying on the side before
- being rotated to an upright position:
-
- \qml
- StackView {
- delegate: StackViewDelegate {
- function transitionFinished(properties)
- {
- properties.exitItem.x = 0
- properties.exitItem.rotation = 0
- }
-
- pushTransition: StackViewTransition {
- SequentialAnimation {
- ScriptAction {
- script: enterItem.rotation = 90
- }
- PropertyAnimation {
- target: enterItem
- property: "x"
- from: enterItem.width
- to: 0
- }
- PropertyAnimation {
- target: enterItem
- property: "rotation"
- from: 90
- to: 0
- }
- }
- PropertyAnimation {
- target: exitItem
- property: "x"
- from: 0
- to: -exitItem.width
- }
- }
- }
- }
- \endqml
-
- \section2 Advanced usage
-
- When the StackView needs a new transition, it first calls
- \l {StackViewDelegate::getTransition()}{StackViewDelegate.getTransition()}.
- The base implementation of this function just looks for a property named \c properties.name inside
- itself (root), which is how it finds \c {property Component pushTransition} in the examples above.
-
- \code
- function getTransition(properties)
- {
- return root[properties.name]
- }
- \endcode
-
- You can override this function for your delegate if you need extra logic to decide which
- transition to return. You could for example introspect the items, and return different animations
- depending on the their internal state. StackView will expect you to return a Component that
- contains a StackViewTransition, or a StackViewTransition directly. The former is easier, as StackView will
- then create the transition and later destroy it when it's done, while avoiding any sideeffects
- caused by the transition being alive long after it has run. Returning a StackViewTransition directly
- can be useful if you need to write some sort of transition caching for performance reasons.
- As an optimization, you can also return \c null to signal that you just want to show/hide the items
- immediately without creating or running any transitions. You can also override this function if
- you need to alter the items in any way before the transition starts.
-
- \c properties contains the properties that will be assigned to the StackViewTransition before
- it runs. In fact, you can add more properties to this object during the call
- if you need to initialize additional properties of your custom StackViewTransition when the returned
- component is instantiated.
-
- The following example shows how you can decide which animation to use during runtime :
-
- \qml
- StackViewDelegate {
- function getTransition(properties)
- {
- return (properties.enterItem.Stack.index % 2) ? horizontalTransition : verticalTransition
- }
-
- function transitionFinished(properties)
- {
- properties.exitItem.x = 0
- properties.exitItem.y = 0
- }
-
- property Component horizontalTransition: StackViewTransition {
- PropertyAnimation {
- target: enterItem
- property: "x"
- from: target.width
- to: 0
- duration: 300
- }
- PropertyAnimation {
- target: exitItem
- property: "x"
- from: 0
- to: target.width
- duration: 300
- }
- }
-
- property Component verticalTransition: StackViewTransition {
- PropertyAnimation {
- target: enterItem
- property: "y"
- from: target.height
- to: 0
- duration: 300
- }
- PropertyAnimation {
- target: exitItem
- property: "y"
- from: 0
- to: target.height
- duration: 300
- }
- }
- }
- \endqml
-
- \section1 Supported Attached Properties
-
- Items in a StackView support these attached properties:
- \list
- \li \l{Stack::index}{Stack.index} - Contains the index of the item inside the StackView
- \li \l{Stack::view}{Stack.view} - Contains the StackView the item is in
- \li \l{Stack::status}{Stack.status} - Contains the status of the item
- \endlist
-*/
AbstractStackView {
id: root
- depth: root.__depth
- currentItem: root.__currentItem
- initialItem: null
- busy: __currentTransition !== null
-
- /*! The transitions to use when pushing or popping items.
- For better understanding on how to apply custom transitions, read \l{Transitions}.
- \sa {Stack::transitions}{Stack.transitions} */
- property StackViewDelegate delegate: StackViewDelegate {}
-
- /*! Pushes an item onto the stack. The function takes a property list as argument, which
- should contain one or more of the following properties:
- \list
- \li \c item: this property is required, and holds the item you want to push.
- \li \c properties: a list of QML properties that should be assigned
- to the item upon push. These properties will be copied into the item when it is
- loaded (in case of a component or URL), or when it becomes the current item for the
- first time (normally upon push).
- \li \c immediate: set this property to \c true to skip transition effects. When pushing
- an array, you only need to set this property on the first element to make the
- whole operation immediate.
- \li \c replace: set this property to replace the current item on the stack. When pushing
- an array, you only need to set this property on the first element to replace
- as many elements on the stack as inside the array.
- \li \c destroyOnPop: set this property to specify if the item needs to be destroyed
- when its popped off the stack. By default (if \a destroyOnPop is not specified),
- StackView will destroy items pushed as components or URLs. Items
- not destroyed will be re-parented to the original parents they had before being
- pushed onto the stack, and hidden. If you need to set this property, do it with
- care, so that items are not leaked.
- \endlist
-
- You can also push an array of items (property lists) if you need to push several items
- in one go. A transition will then only occur between the current item and the last
- item in the list. Loading the other items will be deferred until needed.
-
- Examples:
- \list
- \li stackView.push({item:anItem})
- \li stackView.push({item:aURL, immediate: true, replace: true})
- \li stackView.push({item:aRectangle, properties:{color:"red"}})
- \li stackView.push({item:aComponent, properties:{color:"red"}})
- \li stackView.push({item:aComponent.createObject(), destroyOnPop:true})
- \li stackView.push([{item:anitem, immediate:true}, {item:aURL}])
- \endlist
-
- \note Note: if the only argument needed is "item", you can apply the following short-
- hand notation: \c{stackView.push(anItem)}.
-
- Returns the item that became current.
-
- \sa initialItem
- \sa {Pushing items}
- */
- function push(item) {
- // Note: we support two different APIs in this function; The old meego API, and
- // the new "property list" API. Hence the reason for hiding the fact that you
- // can pass more arguments than shown in the signature:
- if (__recursionGuard(true))
- return
- var properties = arguments[1]
- var immediate = arguments[2]
- var replace = arguments[3]
- var arrayPushed = (item instanceof Array)
- var firstItem = arrayPushed ? item[0] : item
- immediate = (immediate || JSArray.stackView.length === 0)
-
- if (firstItem && firstItem.item && firstItem.hasOwnProperty("x") === false) {
- // Property list API used:
- immediate = immediate || firstItem.immediate
- replace = replace || firstItem.replace
- }
-
- // Create, and push, a new javascript object, called "element", onto the stack.
- // This element contains all the information necessary to construct the item, and
- // will, after loaded, also contain the loaded item:
- if (arrayPushed) {
- if (item.length === 0)
- return
- var outElement = replace ? JSArray.pop() : JSArray.current()
- for (var i=0; i<item.length; ++i)
- JSArray.push({itemComponent:item[i], loaded: false, index: __depth, properties: properties});
- } else {
- outElement = replace ? JSArray.pop() : JSArray.current()
- JSArray.push({itemComponent:item, loaded: false, index: __depth, properties: properties})
- }
-
- var currentElement = JSArray.current()
- var transition = {
- inElement: currentElement,
- outElement: outElement,
- immediate: immediate,
- replace: replace,
- push: true
- }
- __performTransition(transition)
- __recursionGuard(false)
- return __currentItem
+ popEnter: Transition {
+ NumberAnimation { property: "x"; from: -root.width; to: 0; duration: 400; easing.type: Easing.OutCubic }
}
- /*! Pops one or more items off the stack. The function takes a property list as argument
- which can contain one or more of the following properties:
- \list
- \li \c item: if specified, all items down to (but not including) \a item will be
- popped off. If \a item is \c null, all items down to (but not including) the
- first item will be popped. If not specified, only the current item will be
- popped.
- \li \c immediate: set this property to \c true to skip transition effects.
- \endlist
-
- Examples:
- \list
- \li stackView.pop()
- \li stackView.pop({item:someItem, immediate: true})
- \li stackView.pop({immediate: true})
- \li stackView.pop(null)
- \endlist
-
- \note Note: If the only argument needed is "item", you can apply the following short-
- hand notation: \c{stackView.pop(anItem)}.
-
- Returns the item that was popped off
- \sa clear()
- */
- function pop(item) {
- if (__depth <= 1)
- return null
- if (item && item.hasOwnProperty("x") === false) {
- // Property list API used:
- var immediate = (item.immediate === true)
- item = item.item
- } else {
- immediate = (arguments[1] === true)
- }
-
- if (item === __currentItem)
- return
-
- if (__recursionGuard(true))
- return
-
- var outElement = JSArray.pop()
- var inElement = JSArray.current()
-
- if (__depth > 1 && item !== undefined && item !== inElement.item) {
- // Pop from the top until we find 'item', and return the corresponding
- // element. Skip all non-loaded items (except the first), since no one
- // has any references to such items anyway:
- while (__depth > 1 && !JSArray.current().loaded)
- JSArray.pop()
- inElement = JSArray.current()
- while (__depth > 1 && item !== inElement.item) {
- JSArray.pop()
- __cleanup(inElement)
- while (__depth > 1 && !JSArray.current().loaded)
- JSArray.pop()
- inElement = JSArray.current()
- }
- }
-
- var transition = {
- inElement: inElement,
- outElement: outElement,
- immediate: immediate,
- replace: false,
- push: false
- }
- __performTransition(transition)
- __recursionGuard(false)
- return outElement.item;
- }
-
- /*! Remove all items from the stack. No animations will be applied. */
- function clear() {
- if (__recursionGuard(true))
- return
- if (__currentTransition)
- __currentTransition.animation.complete()
- __currentItem = null
- var count = __depth
- for (var i=0; i<count; ++i) {
- var element = JSArray.pop()
- if (element.item)
- __cleanup(element);
- }
- __recursionGuard(false)
- }
-
- /*! Search for a specific item inside the stack. \a func will
- be called for each item in the stack (with the item as argument)
- until the function returns true. Return value will be the item found. For
- example:
- find(function(item, index) { return item.isTheOne })
- Set \a onlySearchLoadedItems to \c true to not load items that are
- not loaded into memory */
- function find(func, onlySearchLoadedItems) {
- for (var i=__depth-1; i>=0; --i) {
- var element = JSArray.stackView[i];
- if (onlySearchLoadedItems !== true)
- __loadElement(element)
- else if (!element.item)
- continue
- if (func(element.item, i))
- return element.item
- }
- return null;
- }
-
- /*! Returns the item at position \a index in
- the stack. If \a dontLoad is true, the
- item will not be forced to load (and \c null
- will be returned if not yet loaded) */
- function get(index, dontLoad)
- {
- if (index < 0 || index >= JSArray.stackView.length)
- return null
- var element = JSArray.stackView[index]
- if (dontLoad !== true) {
- __loadElement(element)
- return element.item
- } else if (element.item) {
- return element.item
- } else {
- return null
- }
- }
-
- /*! Immediately completes any ongoing transition.
- /sa Animation.complete
- */
- function completeTransition()
- {
- if (__recursionGuard(true))
- return
- if (__currentTransition)
- __currentTransition.animation.complete()
- __recursionGuard(false)
- }
-
- /********* DEPRECATED API *********/
-
- /*! \internal
- \deprecated Use Push() instead */
- function replace(item, properties, immediate) {
- push(item, properties, immediate, true)
+ popExit: Transition {
+ NumberAnimation { property: "x"; from: 0; to: root.width; duration: 400; easing.type: Easing.OutCubic }
}
- /********* PRIVATE API *********/
-
- /*! \internal The currently top-most item on the stack. */
- property Item __currentItem: null
- /*! \internal The number of items currently pushed onto the stack. */
- property int __depth: 0
- /*! \internal Stores the transition info while a transition is ongoing */
- property var __currentTransition: null
- /*! \internal Stops the user from pushing items while preparing a transition */
- property bool __guard: false
-
- Component.onCompleted: {
- if (initialItem)
- push(initialItem)
- }
-
- Component.onDestruction: {
- if (__currentTransition)
- __currentTransition.animation.complete()
- __currentItem = null
- }
-
- /*! \internal */
- function __recursionGuard(use)
- {
- if (use && __guard) {
- console.warn("Warning: StackView: You cannot push/pop recursively!")
- console.trace()
- return true
- }
- __guard = use
- }
-
- /*! \internal */
- function __loadElement(element)
- {
- if (element.loaded) {
- if (!element.item) {
- element.item = invalidItemReplacement.createObject(root)
- element.item.text = "\nError: The item has been deleted outside StackView!"
- }
- return
- }
- if (!element.itemComponent) {
- element.item = invalidItemReplacement.createObject(root)
- element.item.text = "\nError: Invalid item (item was 'null'). "
- + "This might indicate that the item was deleted outside StackView!"
- return
- }
-
- var comp = __resolveComponent(element.itemComponent, element)
-
- // Assign properties to item:
- if (!element.properties)
- element.properties = {}
-
- if (comp.hasOwnProperty("createObject")) {
- if (comp.status === Component.Error) {
- element.item = invalidItemReplacement.createObject(root)
- element.item.text = "\nError: Could not load: " + comp.errorString()
- } else {
- element.item = comp.createObject(root, element.properties)
- // Destroy items we create unless the user specified something else:
- if (!element.hasOwnProperty("destroyOnPop"))
- element.destroyOnPop = true
- }
- } else {
- // comp is already an Item, so just re-parent it into the StackView:
- element.item = comp
- element.originalParent = parent
- element.item.parent = root
- for (var prop in element.properties) {
- if (element.item.hasOwnProperty(prop))
- element.item[prop] = element.properties[prop];
- }
- // Do not destroy items we didn't create, unless the user specified something else:
- if (!element.hasOwnProperty("destroyOnPop"))
- element.destroyOnPop = false
- }
-
-//TODO element.item.Stack.__index = element.index
-//TODO element.item.Stack.__view = root
- // Let item fill all available space by default:
- element.item.width = Qt.binding(function() { return root.width })
- element.item.height = Qt.binding(function() { return root.height })
- element.loaded = true
- }
-
- /*! \internal */
- function __resolveComponent(unknownObjectType, element)
- {
- // We need this extra resolve function since we don't really
- // know what kind of object the user pushed. So we try to
- // figure it out by inspecting the object:
- if (unknownObjectType.hasOwnProperty("createObject")) {
- return unknownObjectType
- } else if (typeof unknownObjectType == "string") {
- return Qt.createComponent(unknownObjectType)
- } else if (unknownObjectType.hasOwnProperty("x")) {
- return unknownObjectType
- } else if (unknownObjectType.hasOwnProperty("item")) {
- // INVARIANT: user pushed a JS-object
- element.properties = unknownObjectType.properties
- if (!unknownObjectType.item)
- unknownObjectType.item = invalidItemReplacement
- if (unknownObjectType.hasOwnProperty("destroyOnPop"))
- element.destroyOnPop = unknownObjectType.destroyOnPop
- return __resolveComponent(unknownObjectType.item, element)
- } else {
- // We cannot determine the type, so assume its a URL:
- return Qt.createComponent(unknownObjectType)
- }
- }
-
- /*! \internal */
- function __cleanup(element) {
- // INVARIANT: element has been removed from JSArray. Destroy its
- // item, or re-parent it back to the parent it had before it was pushed:
- var item = element.item
- if (element.destroyOnPop) {
- item.destroy()
- } else {
- // Mark the item as no longer part of the StackView. It
- // might reenter on pop if pushed several times:
- item.visible = false
- __setStatus(item, Stack.Inactive)
-//TODO item.Stack.__view = null
-//TODO item.Stack.__index = -1
- if (element.originalParent)
- item.parent = element.originalParent
- }
- }
-
- /*! \internal */
- function __setStatus(item, status) {
- item.Stack.status = status
- }
-
- /*! \internal */
- function __performTransition(transition)
- {
- // Animate item in "outElement" out, and item in "inElement" in. Set a guard to protect
- // the user from pushing new items on signals that will fire while preparing for the transition
- // (e.g Stack.onCompleted, Stack.onStatusChanged, Stack.onIndexChanged etc). Otherwise, we will enter
- // this function several times, which causes the items to be updated half-way.
- if (__currentTransition)
- __currentTransition.animation.complete()
- __loadElement(transition.inElement)
-
- transition.name = transition.replace ? "replaceTransition" : (transition.push ? "pushTransition" : "popTransition")
- var enterItem = transition.inElement.item
- transition.enterItem = enterItem
-
- // Since an item can be pushed several times, we need to update its properties:
- enterItem.parent = root
-//TODO enterItem.Stack.__view = root
-//TODO enterItem.Stack.__index = transition.inElement.index
- __currentItem = enterItem
-
- if (!transition.outElement) {
- // A transition consists of two items, but we got just one. So just show the item:
- enterItem.visible = true
- __setStatus(enterItem, Stack.Activating)
- __setStatus(enterItem, Stack.Active)
- return
- }
-
- var exitItem = transition.outElement.item
- transition.exitItem = exitItem
- if (enterItem === exitItem)
- return
-
- if (root.delegate) {
- transition.properties = {
- "name":transition.name,
- "enterItem":transition.enterItem,
- "exitItem":transition.exitItem,
- "immediate":transition.immediate }
- var anim = root.delegate.getTransition(transition.properties)
- if (anim.createObject) {
- anim = anim.createObject(null, transition.properties)
- anim.runningChanged.connect(function(){ if (anim.running === false) anim.destroy() })
- }
- transition.animation = anim
- }
-
- if (!transition.animation) {
- console.warn("Warning: StackView: no", transition.name, "found!")
- return
- }
- if (enterItem.anchors.fill || exitItem.anchors.fill)
- console.warn("Warning: StackView: cannot transition an item that is anchored!")
-
- __currentTransition = transition
- __setStatus(exitItem, Stack.Deactivating)
- enterItem.visible = true
- __setStatus(enterItem, Stack.Activating)
- transition.animation.runningChanged.connect(animationFinished)
- transition.animation.start()
- // NB! For empty animations, "animationFinished" is already
- // executed at this point, leaving __animation === null:
- if (transition.immediate === true && transition.animation)
- transition.animation.complete()
- }
-
- /*! \internal */
- function animationFinished()
- {
- if (!__currentTransition || __currentTransition.animation.running)
- return
-
- __currentTransition.animation.runningChanged.disconnect(animationFinished)
- __currentTransition.exitItem.visible = false
- __setStatus(__currentTransition.exitItem, Stack.Inactive);
- __setStatus(__currentTransition.enterItem, Stack.Active);
- __currentTransition.properties.animation = __currentTransition.animation
- root.delegate.transitionFinished(__currentTransition.properties)
-
- if (!__currentTransition.push || __currentTransition.replace)
- __cleanup(__currentTransition.outElement)
-
- __currentTransition = null
+ pushEnter: Transition {
+ NumberAnimation { property: "x"; from: root.width; to: 0; duration: 400; easing.type: Easing.OutCubic }
}
- /*! \internal */
- property Component invalidItemReplacement: Component {
- Text {
- width: parent.width
- height: parent.height
- wrapMode: Text.WrapAtWordBoundaryOrAnywhere
- }
+ pushExit: Transition {
+ NumberAnimation { property: "x"; from: 0; to: -root.width; duration: 400; easing.type: Easing.OutCubic }
}
}
diff --git a/src/imports/controls/StackViewDelegate.qml b/src/imports/controls/StackViewDelegate.qml
deleted file mode 100644
index 2d4355bd..00000000
--- a/src/imports/controls/StackViewDelegate.qml
+++ /dev/null
@@ -1,142 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the Qt Quick Controls module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL3$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPLv3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or later as published by the Free
-** Software Foundation and appearing in the file LICENSE.GPL included in
-** the packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 2.0 requirements will be
-** met: http://www.gnu.org/licenses/gpl-2.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.2
-
-/*!
- \qmltype StackViewDelegate
- \inqmlmodule QtQuick.Controls
- \since 5.1
-
- \brief A delegate used by StackView for loading transitions.
-
- See the documentation for the \l {StackView} component.
-
-*/
-QtObject {
- id: root
-
- property bool horizontal: true
-
- function getTransition(properties)
- {
- return root[horizontal ? "horizontalSlide" : "verticalSlide"][properties.name]
- }
-
- function transitionFinished(properties)
- {
- properties.exitItem.x = 0
- properties.exitItem.y = 0
- }
-
- property QtObject horizontalSlide: QtObject {
- property Component pushTransition: StackViewTransition {
- PropertyAnimation {
- target: enterItem
- property: "x"
- from: target.width
- to: 0
- duration: 400
- easing.type: Easing.OutCubic
- }
- PropertyAnimation {
- target: exitItem
- property: "x"
- from: 0
- to: -target.width
- duration: 400
- easing.type: Easing.OutCubic
- }
- }
-
- property Component popTransition: StackViewTransition {
- PropertyAnimation {
- target: enterItem
- property: "x"
- from: -target.width
- to: 0
- duration: 400
- easing.type: Easing.OutCubic
- }
- PropertyAnimation {
- target: exitItem
- property: "x"
- from: 0
- to: target.width
- duration: 400
- easing.type: Easing.OutCubic
- }
- }
- property Component replaceTransition: pushTransition
- }
-
- property QtObject verticalSlide: QtObject {
- property Component pushTransition: StackViewTransition {
- PropertyAnimation {
- target: enterItem
- property: "y"
- from: target.height
- to: 0
- duration: 300
- }
- PropertyAnimation {
- target: exitItem
- property: "y"
- from: 0
- to: -target.height
- duration: 300
- }
- }
-
- property Component popTransition: StackViewTransition {
- PropertyAnimation {
- target: enterItem
- property: "y"
- from: -target.height
- to: 0
- duration: 300
- }
- PropertyAnimation {
- target: exitItem
- property: "y"
- from: 0
- to: target.height
- duration: 300
- }
- property Component replaceTransition: pushTransition
- }
- }
-}
diff --git a/src/imports/controls/StackViewTransition.qml b/src/imports/controls/StackViewTransition.qml
deleted file mode 100644
index f057791c..00000000
--- a/src/imports/controls/StackViewTransition.qml
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the Qt Quick Controls module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL3$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPLv3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or later as published by the Free
-** Software Foundation and appearing in the file LICENSE.GPL included in
-** the packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 2.0 requirements will be
-** met: http://www.gnu.org/licenses/gpl-2.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.2
-
-ParallelAnimation {
- id: root
- /*! The name of the animation that is running. Can be one of the following:
- \list
- \li 'PushTransition'
- \li 'PopTransition'
- \li 'ReplaceTransition'
- \endlist
- */
- property string name
- /*! The page that is transitioning in. */
- property Item enterItem
- /*! The page that is transitioning out */
- property Item exitItem
- /*! Set to \c true if the transition is told to
- fast-forward directly to its end-state */
- property bool immediate
-}
diff --git a/src/imports/controls/controls.pro b/src/imports/controls/controls.pro
index 44e2a5e6..48c8cd48 100644
--- a/src/imports/controls/controls.pro
+++ b/src/imports/controls/controls.pro
@@ -22,10 +22,7 @@ QML_FILES = \
ScrollBar.qml \
ScrollIndicator.qml \
Slider.qml \
- StackView.js \
StackView.qml \
- StackViewDelegate.qml \
- StackViewTransition.qml \
Switch.qml \
TabBar.qml \
TabButton.qml \
diff --git a/src/imports/controls/qmldir b/src/imports/controls/qmldir
index b54df22c..eaedbbb5 100644
--- a/src/imports/controls/qmldir
+++ b/src/imports/controls/qmldir
@@ -15,8 +15,6 @@ ScrollBar 2.0 ScrollBar.qml
ScrollIndicator 2.0 ScrollIndicator.qml
Slider 2.0 Slider.qml
StackView 2.0 StackView.qml
-StackViewDelegate 2.0 StackViewDelegate.qml
-StackViewTransition 2.0 StackViewTransition.qml
Switch 2.0 Switch.qml
TabBar 2.0 TabBar.qml
TabButton 2.0 TabButton.qml
diff --git a/tests/auto/controls/data/tst_stackview.qml b/tests/auto/controls/data/tst_stackview.qml
index ed826ad2..3e87853d 100644
--- a/tests/auto/controls/data/tst_stackview.qml
+++ b/tests/auto/controls/data/tst_stackview.qml
@@ -50,7 +50,7 @@ TestCase {
when: windowShown
name: "StackView"
- Item { id: item }
+ Item { id: item }
TextField { id: textField }
Component { id: component; Item { } }
@@ -62,11 +62,14 @@ TestCase {
function test_defaults() {
var control = stackView.createObject(testCase)
verify(control)
- verify(control.delegate)
+ verify(control.pushEnter)
+ verify(control.pushExit)
+ verify(control.popEnter)
+ verify(control.popExit)
compare(control.depth, 0)
compare(control.busy, false)
compare(control.currentItem, null)
- compare(control.initialItem, null)
+ compare(control.initialItem, undefined)
control.destroy()
}
@@ -85,7 +88,7 @@ TestCase {
compare(control.currentItem, item)
control.push(component)
verify(control.currentItem !== item)
- control.pop({immediate: true})
+ control.pop(AbstractStackView.Immediate)
compare(control.currentItem, item)
control.destroy()
}
@@ -108,23 +111,44 @@ TestCase {
var control = stackView.createObject(testCase)
var item1 = component.createObject(control)
- compare(item1.Stack.status, Stack.Inactive)
+ compare(item1.AbstractStackView.status, AbstractStackView.Inactive)
control.push(item1)
- compare(item1.Stack.status, Stack.Active)
+ compare(item1.AbstractStackView.status, AbstractStackView.Active)
var item2 = component.createObject(control)
- compare(item2.Stack.status, Stack.Inactive)
+ compare(item2.AbstractStackView.status, AbstractStackView.Inactive)
control.push(item2)
- compare(item2.Stack.status, Stack.Activating)
- compare(item1.Stack.status, Stack.Deactivating)
- tryCompare(item2.Stack, "status", Stack.Active)
- tryCompare(item1.Stack, "status", Stack.Inactive)
+ compare(item2.AbstractStackView.status, AbstractStackView.Activating)
+ compare(item1.AbstractStackView.status, AbstractStackView.Deactivating)
+ tryCompare(item2.AbstractStackView, "status", AbstractStackView.Active)
+ tryCompare(item1.AbstractStackView, "status", AbstractStackView.Inactive)
control.pop()
- compare(item2.Stack.status, Stack.Deactivating)
- compare(item1.Stack.status, Stack.Activating)
- tryCompare(item2.Stack, "status", Stack.Inactive)
- tryCompare(item1.Stack, "status", Stack.Active)
+ compare(item2.AbstractStackView.status, AbstractStackView.Deactivating)
+ compare(item1.AbstractStackView.status, AbstractStackView.Activating)
+ tryCompare(item2.AbstractStackView, "status", AbstractStackView.Inactive)
+ tryCompare(item1.AbstractStackView, "status", AbstractStackView.Active)
+
+ control.destroy()
+ }
+
+ function test_index() {
+ var control = stackView.createObject(testCase)
+
+ var item1 = component.createObject(control)
+ compare(item1.AbstractStackView.index, -1)
+ control.push(item1, AbstractStackView.Immediate)
+ compare(item1.AbstractStackView.index, 0)
+
+ var item2 = component.createObject(control)
+ compare(item2.AbstractStackView.index, -1)
+ control.push(item2, AbstractStackView.Immediate)
+ compare(item2.AbstractStackView.index, 1)
+ compare(item1.AbstractStackView.index, 0)
+
+ control.pop(AbstractStackView.Immediate)
+ compare(item2.AbstractStackView.index, -1)
+ compare(item1.AbstractStackView.index, 0)
control.destroy()
}
@@ -132,17 +156,17 @@ TestCase {
function test_depth() {
var control = stackView.createObject(testCase)
compare(control.depth, 0)
- control.push(item)
+ control.push(item, AbstractStackView.Immediate)
compare(control.depth, 1)
- control.push(item)
+ control.push(item, AbstractStackView.Immediate)
compare(control.depth, 2)
- control.pop()
+ control.pop(AbstractStackView.Immediate)
compare(control.depth, 1)
- control.push(component)
+ control.push(component, AbstractStackView.Immediate)
compare(control.depth, 2)
- control.pop()
+ control.pop(AbstractStackView.Immediate)
compare(control.depth, 1)
- control.pop() // ignored
+ control.pop(AbstractStackView.Immediate) // ignored
compare(control.depth, 1)
control.clear()
compare(control.depth, 0)
@@ -165,12 +189,12 @@ TestCase {
control.forceActiveFocus()
verify(control.activeFocus)
- control.push({item: textField, immediate: true})
+ control.push(textField, AbstractStackView.Immediate)
compare(control.currentItem, textField)
textField.forceActiveFocus()
verify(textField.activeFocus)
- control.pop({immediate: true})
+ control.pop(AbstractStackView.Immediate)
compare(control.currentItem, item)
verify(control.activeFocus)
verify(!textField.activeFocus)
@@ -185,9 +209,9 @@ TestCase {
var item2 = component.createObject(control, {objectName: "2"})
var item3 = component.createObject(control, {objectName: "3"})
- control.push(item1, {immediate: true})
- control.push(item2, {immediate: true})
- control.push(item3, {immediate: true})
+ control.push(item1, AbstractStackView.Immediate)
+ control.push(item2, AbstractStackView.Immediate)
+ control.push(item3, AbstractStackView.Immediate)
compare(control.find(function(item, index) { return index === 0 }), item1)
compare(control.find(function(item) { return item.objectName === "1" }), item1)
@@ -207,82 +231,156 @@ TestCase {
function test_get() {
var control = stackView.createObject(testCase)
- control.push([item, component, component])
+ control.push([item, component, component], AbstractStackView.Immediate)
- verify(!control.get(0, true)) // dontLoad=true
- compare(control.get(0, false), item) // dontLoad=false
+ verify(control.get(0, AbstractStackView.DontLoad))
+ compare(control.get(0, AbstractStackView.ForceLoad), item)
- verify(!control.get(1, true)) // dontLoad=true
- verify(control.get(1, false)) // dontLoad=false
+ verify(!control.get(1, AbstractStackView.DontLoad))
- verify(control.get(2, true)) // dontLoad=true
- verify(control.get(2, false)) // dontLoad=false
+ verify(control.get(2, AbstractStackView.DontLoad))
+ verify(control.get(2, AbstractStackView.ForceLoad))
control.destroy()
}
- function test_pushpop() {
+ function test_push() {
var control = stackView.createObject(testCase)
+ // missing arguments ### TODO: TestCase.ignoreWarning()
+ compare(control.push(), null)
+
+ // nothing to push ### TODO: TestCase.ignoreWarning()
+ compare(control.push(AbstractStackView.Immediate), null)
+
+ // push(item)
var item1 = component.createObject(control, {objectName:"1"})
- compare(control.push(item1), item1)
+ compare(control.push(item1, AbstractStackView.Immediate), item1)
compare(control.depth, 1)
compare(control.currentItem, item1)
+ // push([item])
var item2 = component.createObject(control, {objectName:"2"})
- compare(control.push({item: item2}), item2)
+ compare(control.push([item2], AbstractStackView.Immediate), item2)
compare(control.depth, 2)
compare(control.currentItem, item2)
- var item3 = component.createObject(control, {objectName:"3"})
- compare(control.push(item3, {}, true), item3)
+ // push(item, {properties})
+ var item3 = component.createObject(control)
+ compare(control.push(item3, {objectName:"3"}, AbstractStackView.Immediate), item3)
+ compare(item3.objectName, "3")
compare(control.depth, 3)
compare(control.currentItem, item3)
- var item4 = component.createObject(control, {objectName:"4"})
- compare(control.push(item4, {}, true, true), item4)
- compare(control.depth, 3)
+ // push([item, {properties}])
+ var item4 = component.createObject(control)
+ compare(control.push([item4, {objectName:"4"}], AbstractStackView.Immediate), item4)
+ compare(item4.objectName, "4")
+ compare(control.depth, 4)
compare(control.currentItem, item4)
- var item5 = component.createObject(control, {objectName:"5"})
- compare(control.push({item:item5, immediate:true, replace:true}), item5)
- compare(control.depth, 3)
+ // push(component, {properties})
+ var item5 = control.push(component, {objectName:"5"}, AbstractStackView.Immediate)
+ compare(item5.objectName, "5")
+ compare(control.depth, 5)
compare(control.currentItem, item5)
- var item6 = control.push(component, {objectName:"6"})
- compare(control.depth, 4)
+ // push([component, {properties}])
+ var item6 = control.push([component, {objectName:"6"}], AbstractStackView.Immediate)
+ compare(item6.objectName, "6")
+ compare(control.depth, 6)
compare(control.currentItem, item6)
- var item7 = control.push({item:component, properties:{objectName:"7"}})
- compare(control.depth, 5)
- compare(control.currentItem, item7)
+ control.destroy()
+ }
+
+ function test_pop() {
+ var control = stackView.createObject(testCase)
- var item8 = component.createObject(control, {objectName:"8"})
- var item9 = component.createObject(control, {objectName:"9"})
- compare(control.push([component, {item:component, properties:{objectName:"?"}}, {item:item8}, item9]), item9)
- compare(control.depth, 9)
- compare(control.currentItem, item9)
+ var items = []
+ for (var i = 0; i < 7; ++i)
+ items.push(component.createObject(control, {objectName:i}))
- compare(control.pop(), item9)
- compare(control.depth, 8)
- compare(control.currentItem, item8)
+ control.push(items, AbstractStackView.Immediate)
- compare(control.pop(), item8)
- compare(control.depth, 7)
+ // too many arguments ### TODO: TestCase.ignoreWarning()
+ compare(control.pop(1, 2, 3), null)
- verify(control.pop({immediate:true}))
- verify(control.pop({immediate:false}))
- compare(control.depth, 5)
- compare(control.currentItem, item7)
+ // pop the top most item
+ compare(control.pop(AbstractStackView.Immediate), items[6])
+ compare(control.depth, 6)
+ compare(control.currentItem, items[5])
- compare(control.pop(item5), item7)
+ // pop down to (but not including) the Nth item
+ compare(control.pop(items[3], AbstractStackView.Immediate), items[5])
+ compare(control.depth, 4)
+ compare(control.currentItem, items[3])
+
+ // pop the top most item
+ compare(control.pop(undefined, AbstractStackView.Immediate), items[3])
compare(control.depth, 3)
- compare(control.currentItem, item5)
+ compare(control.currentItem, items[2])
- control.pop(null)
+ // don't pop non-existent item
+ compare(control.pop(testCase, AbstractStackView.Immediate), null)
+ compare(control.depth, 3)
+ compare(control.currentItem, items[2])
+
+ // pop all items down to (but not including) the 1st item
+ control.pop(null, AbstractStackView.Immediate)
+ compare(control.depth, 1)
+ compare(control.currentItem, items[0])
+
+ control.destroy()
+ }
+
+ function test_replace() {
+ var control = stackView.createObject(testCase)
+
+ // missing arguments ### TODO: TestCase.ignoreWarning()
+ compare(control.replace(), null)
+
+ // nothing to push ### TODO: TestCase.ignoreWarning()
+ compare(control.replace(AbstractStackView.Immediate), null)
+
+ // replace(item)
+ var item1 = component.createObject(control, {objectName:"1"})
+ compare(control.replace(item1, AbstractStackView.Immediate), item1)
compare(control.depth, 1)
compare(control.currentItem, item1)
+ // replace([item])
+ var item2 = component.createObject(control, {objectName:"2"})
+ compare(control.replace([item2], AbstractStackView.Immediate), item2)
+ compare(control.depth, 1)
+ compare(control.currentItem, item2)
+
+ // replace(item, {properties})
+ var item3 = component.createObject(control)
+ compare(control.replace(item3, {objectName:"3"}, AbstractStackView.Immediate), item3)
+ compare(item3.objectName, "3")
+ compare(control.depth, 1)
+ compare(control.currentItem, item3)
+
+ // replace([item, {properties}])
+ var item4 = component.createObject(control)
+ compare(control.replace([item4, {objectName:"4"}], AbstractStackView.Immediate), item4)
+ compare(item4.objectName, "4")
+ compare(control.depth, 1)
+ compare(control.currentItem, item4)
+
+ // replace(component, {properties})
+ var item5 = control.replace(component, {objectName:"5"}, AbstractStackView.Immediate)
+ compare(item5.objectName, "5")
+ compare(control.depth, 1)
+ compare(control.currentItem, item5)
+
+ // replace([component, {properties}])
+ var item6 = control.replace([component, {objectName:"6"}], AbstractStackView.Immediate)
+ compare(item6.objectName, "6")
+ compare(control.depth, 1)
+ compare(control.currentItem, item6)
+
control.destroy()
}
}
diff --git a/tests/benchmarks/creationtime/tst_creationtime.cpp b/tests/benchmarks/creationtime/tst_creationtime.cpp
index a6b19d6c..3b051907 100644
--- a/tests/benchmarks/creationtime/tst_creationtime.cpp
+++ b/tests/benchmarks/creationtime/tst_creationtime.cpp
@@ -105,6 +105,7 @@ void tst_CreationTime::testControls_data()
QTest::newRow("ScrollIndicator") << QByteArray("ScrollIndicator");
#endif
QTest::newRow("Slider") << QByteArray("Slider");
+ QTest::newRow("StackView") << QByteArray("StackView");
QTest::newRow("Switch") << QByteArray("Switch");
#ifndef QT_QUICK_CONTROLS_V1
QTest::newRow("TabBar") << QByteArray("TabBar");