diff options
Diffstat (limited to 'src/controls/qquickabstractstackview.cpp')
-rw-r--r-- | src/controls/qquickabstractstackview.cpp | 565 |
1 files changed, 383 insertions, 182 deletions
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 |