diff options
Diffstat (limited to 'src/quick/util')
57 files changed, 19927 insertions, 0 deletions
diff --git a/src/quick/util/qdeclarativeanimation.cpp b/src/quick/util/qdeclarativeanimation.cpp new file mode 100644 index 0000000000..8c21d11037 --- /dev/null +++ b/src/quick/util/qdeclarativeanimation.cpp @@ -0,0 +1,2441 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeanimation_p.h" +#include "qdeclarativeanimation_p_p.h" + +#include <private/qdeclarativestateoperations_p.h> +#include <private/qdeclarativecontext_p.h> + +#include <qdeclarativepropertyvaluesource.h> +#include <qdeclarative.h> +#include <qdeclarativeinfo.h> +#include <qdeclarativeexpression.h> +#include <private/qdeclarativestringconverters_p.h> +#include <private/qdeclarativeglobal_p.h> +#include <private/qdeclarativemetatype_p.h> +#include <private/qdeclarativevaluetype_p.h> +#include <private/qdeclarativeproperty_p.h> +#include <private/qdeclarativeengine_p.h> + +#include <qvariant.h> +#include <qcolor.h> +#include <qfile.h> +#include <QParallelAnimationGroup> +#include <QSequentialAnimationGroup> +#include <QtCore/qset.h> +#include <QtCore/qrect.h> +#include <QtCore/qpoint.h> +#include <QtCore/qsize.h> +#include <QtCore/qmath.h> + +#include <private/qvariantanimation_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Animation QDeclarativeAbstractAnimation + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \brief The Animation element is the base of all QML animations. + + The Animation element cannot be used directly in a QML file. It exists + to provide a set of common properties and methods, available across all the + other animation types that inherit from it. Attempting to use the Animation + element directly will result in an error. +*/ + +QDeclarativeAbstractAnimation::QDeclarativeAbstractAnimation(QObject *parent) +: QObject(*(new QDeclarativeAbstractAnimationPrivate), parent) +{ +} + +QDeclarativeAbstractAnimation::~QDeclarativeAbstractAnimation() +{ +} + +QDeclarativeAbstractAnimation::QDeclarativeAbstractAnimation(QDeclarativeAbstractAnimationPrivate &dd, QObject *parent) +: QObject(dd, parent) +{ +} + +/*! + \qmlproperty bool QtQuick2::Animation::running + This property holds whether the animation is currently running. + + The \c running property can be set to declaratively control whether or not + an animation is running. The following example will animate a rectangle + whenever the \l MouseArea is pressed. + + \code + Rectangle { + width: 100; height: 100 + NumberAnimation on x { + running: myMouse.pressed + from: 0; to: 100 + } + MouseArea { id: myMouse } + } + \endcode + + Likewise, the \c running property can be read to determine if the animation + is running. In the following example the text element will indicate whether + or not the animation is running. + + \code + NumberAnimation { id: myAnimation } + Text { text: myAnimation.running ? "Animation is running" : "Animation is not running" } + \endcode + + Animations can also be started and stopped imperatively from JavaScript + using the \c start() and \c stop() methods. + + By default, animations are not running. Though, when the animations are assigned to properties, + as property value sources using the \e on syntax, they are set to running by default. +*/ +bool QDeclarativeAbstractAnimation::isRunning() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->running; +} + +// the behavior calls this function +void QDeclarativeAbstractAnimation::notifyRunningChanged(bool running) +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->disableUserControl && d->running != running) { + d->running = running; + emit runningChanged(running); + } +} + +//commence is called to start an animation when it is used as a +//simple animation, and not as part of a transition +void QDeclarativeAbstractAnimationPrivate::commence() +{ + Q_Q(QDeclarativeAbstractAnimation); + + QDeclarativeStateActions actions; + QDeclarativeProperties properties; + q->transition(actions, properties, QDeclarativeAbstractAnimation::Forward); + + q->qtAnimation()->start(); + if (q->qtAnimation()->state() == QAbstractAnimation::Stopped) { + running = false; + emit q->completed(); + } +} + +QDeclarativeProperty QDeclarativeAbstractAnimationPrivate::createProperty(QObject *obj, const QString &str, QObject *infoObj) +{ + QDeclarativeProperty prop(obj, str, qmlContext(infoObj)); + if (!prop.isValid()) { + qmlInfo(infoObj) << QDeclarativeAbstractAnimation::tr("Cannot animate non-existent property \"%1\"").arg(str); + return QDeclarativeProperty(); + } else if (!prop.isWritable()) { + qmlInfo(infoObj) << QDeclarativeAbstractAnimation::tr("Cannot animate read-only property \"%1\"").arg(str); + return QDeclarativeProperty(); + } + return prop; +} + +void QDeclarativeAbstractAnimation::setRunning(bool r) +{ + Q_D(QDeclarativeAbstractAnimation); + if (!d->componentComplete) { + d->running = r; + if (r == false) + d->avoidPropertyValueSourceStart = true; + else if (!d->registered) { + d->registered = true; + QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this)); + engPriv->registerFinalizeCallback(this, this->metaObject()->indexOfSlot("componentFinalized()")); + } + return; + } + + if (d->running == r) + return; + + if (d->group || d->disableUserControl) { + qmlInfo(this) << "setRunning() cannot be used on non-root animation nodes."; + return; + } + + d->running = r; + if (d->running) { + bool supressStart = false; + if (d->alwaysRunToEnd && d->loopCount != 1 + && qtAnimation()->state() == QAbstractAnimation::Running) { + //we've restarted before the final loop finished; restore proper loop count + if (d->loopCount == -1) + qtAnimation()->setLoopCount(d->loopCount); + else + qtAnimation()->setLoopCount(qtAnimation()->currentLoop() + d->loopCount); + supressStart = true; //we want the animation to continue, rather than restart + } + + if (!d->connectedTimeLine) { + FAST_CONNECT(qtAnimation(), SIGNAL(finished()), this, SLOT(timelineComplete())) + d->connectedTimeLine = true; + } + if (!supressStart) + d->commence(); + emit started(); + } else { + if (d->alwaysRunToEnd) { + if (d->loopCount != 1) + qtAnimation()->setLoopCount(qtAnimation()->currentLoop()+1); //finish the current loop + } else + qtAnimation()->stop(); + + emit completed(); + } + + emit runningChanged(d->running); +} + +/*! + \qmlproperty bool QtQuick2::Animation::paused + This property holds whether the animation is currently paused. + + The \c paused property can be set to declaratively control whether or not + an animation is paused. + + Animations can also be paused and resumed imperatively from JavaScript + using the \c pause() and \c resume() methods. + + By default, animations are not paused. +*/ +bool QDeclarativeAbstractAnimation::isPaused() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->paused; +} + +void QDeclarativeAbstractAnimation::setPaused(bool p) +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->paused == p) + return; + + if (d->group || d->disableUserControl) { + qmlInfo(this) << "setPaused() cannot be used on non-root animation nodes."; + return; + } + + d->paused = p; + + if (!d->componentComplete) + return; + + if (d->paused) + qtAnimation()->pause(); + else + qtAnimation()->resume(); + + emit pausedChanged(d->paused); +} + +void QDeclarativeAbstractAnimation::classBegin() +{ + Q_D(QDeclarativeAbstractAnimation); + d->componentComplete = false; +} + +void QDeclarativeAbstractAnimation::componentComplete() +{ + Q_D(QDeclarativeAbstractAnimation); + d->componentComplete = true; +} + +void QDeclarativeAbstractAnimation::componentFinalized() +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->running) { + d->running = false; + setRunning(true); + } + if (d->paused) { + d->paused = false; + setPaused(true); + } +} + +/*! + \qmlproperty bool QtQuick2::Animation::alwaysRunToEnd + This property holds whether the animation should run to completion when it is stopped. + + If this true the animation will complete its current iteration when it + is stopped - either by setting the \c running property to false, or by + calling the \c stop() method. The \c complete() method is not effected + by this value. + + This behavior is most useful when the \c repeat property is set, as the + animation will finish playing normally but not restart. + + By default, the alwaysRunToEnd property is not set. + + \note alwaysRunToEnd has no effect on animations in a Transition. +*/ +bool QDeclarativeAbstractAnimation::alwaysRunToEnd() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->alwaysRunToEnd; +} + +void QDeclarativeAbstractAnimation::setAlwaysRunToEnd(bool f) +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->alwaysRunToEnd == f) + return; + + d->alwaysRunToEnd = f; + emit alwaysRunToEndChanged(f); +} + +/*! + \qmlproperty int QtQuick2::Animation::loops + This property holds the number of times the animation should play. + + By default, \c loops is 1: the animation will play through once and then stop. + + If set to Animation.Infinite, the animation will continuously repeat until it is explicitly + stopped - either by setting the \c running property to false, or by calling + the \c stop() method. + + In the following example, the rectangle will spin indefinitely. + + \code + Rectangle { + width: 100; height: 100; color: "green" + RotationAnimation on rotation { + loops: Animation.Infinite + from: 0 + to: 360 + } + } + \endcode +*/ +int QDeclarativeAbstractAnimation::loops() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->loopCount; +} + +void QDeclarativeAbstractAnimation::setLoops(int loops) +{ + Q_D(QDeclarativeAbstractAnimation); + if (loops < 0) + loops = -1; + + if (loops == d->loopCount) + return; + + d->loopCount = loops; + qtAnimation()->setLoopCount(loops); + emit loopCountChanged(loops); +} + + +int QDeclarativeAbstractAnimation::currentTime() +{ + return qtAnimation()->currentLoopTime(); +} + +void QDeclarativeAbstractAnimation::setCurrentTime(int time) +{ + qtAnimation()->setCurrentTime(time); +} + +QDeclarativeAnimationGroup *QDeclarativeAbstractAnimation::group() const +{ + Q_D(const QDeclarativeAbstractAnimation); + return d->group; +} + +void QDeclarativeAbstractAnimation::setGroup(QDeclarativeAnimationGroup *g) +{ + Q_D(QDeclarativeAbstractAnimation); + if (d->group == g) + return; + if (d->group) + static_cast<QDeclarativeAnimationGroupPrivate *>(d->group->d_func())->animations.removeAll(this); + + d->group = g; + + if (d->group && !static_cast<QDeclarativeAnimationGroupPrivate *>(d->group->d_func())->animations.contains(this)) + static_cast<QDeclarativeAnimationGroupPrivate *>(d->group->d_func())->animations.append(this); + + //if (g) //if removed from a group, then the group should no longer be the parent + setParent(g); +} + +/*! + \qmlmethod QtQuick2::Animation::start() + \brief Starts the animation. + + If the animation is already running, calling this method has no effect. The + \c running property will be true following a call to \c start(). +*/ +void QDeclarativeAbstractAnimation::start() +{ + setRunning(true); +} + +/*! + \qmlmethod QtQuick2::Animation::pause() + \brief Pauses the animation. + + If the animation is already paused, calling this method has no effect. The + \c paused property will be true following a call to \c pause(). +*/ +void QDeclarativeAbstractAnimation::pause() +{ + setPaused(true); +} + +/*! + \qmlmethod QtQuick2::Animation::resume() + \brief Resumes a paused animation. + + If the animation is not paused, calling this method has no effect. The + \c paused property will be false following a call to \c resume(). +*/ +void QDeclarativeAbstractAnimation::resume() +{ + setPaused(false); +} + +/*! + \qmlmethod QtQuick2::Animation::stop() + \brief Stops the animation. + + If the animation is not running, calling this method has no effect. The + \c running property will be false following a call to \c stop(). + + Normally \c stop() stops the animation immediately, and the animation has + no further influence on property values. In this example animation + \code + Rectangle { + NumberAnimation on x { from: 0; to: 100; duration: 500 } + } + \endcode + was stopped at time 250ms, the \c x property will have a value of 50. + + However, if the \c alwaysRunToEnd property is set, the animation will + continue running until it completes and then stop. The \c running property + will still become false immediately. +*/ +void QDeclarativeAbstractAnimation::stop() +{ + setRunning(false); +} + +/*! + \qmlmethod QtQuick2::Animation::restart() + \brief Restarts the animation. + + This is a convenience method, and is equivalent to calling \c stop() and + then \c start(). +*/ +void QDeclarativeAbstractAnimation::restart() +{ + stop(); + start(); +} + +/*! + \qmlmethod QtQuick2::Animation::complete() + \brief Stops the animation, jumping to the final property values. + + If the animation is not running, calling this method has no effect. The + \c running property will be false following a call to \c complete(). + + Unlike \c stop(), \c complete() immediately fast-forwards the animation to + its end. In the following example, + \code + Rectangle { + NumberAnimation on x { from: 0; to: 100; duration: 500 } + } + \endcode + calling \c stop() at time 250ms will result in the \c x property having + a value of 50, while calling \c complete() will set the \c x property to + 100, exactly as though the animation had played the whole way through. +*/ +void QDeclarativeAbstractAnimation::complete() +{ + if (isRunning()) { + qtAnimation()->setCurrentTime(qtAnimation()->duration()); + } +} + +void QDeclarativeAbstractAnimation::setTarget(const QDeclarativeProperty &p) +{ + Q_D(QDeclarativeAbstractAnimation); + d->defaultProperty = p; + + if (!d->avoidPropertyValueSourceStart) + setRunning(true); +} + +/* + we rely on setTarget only being called when used as a value source + so this function allows us to do the same thing as setTarget without + that assumption +*/ +void QDeclarativeAbstractAnimation::setDefaultTarget(const QDeclarativeProperty &p) +{ + Q_D(QDeclarativeAbstractAnimation); + d->defaultProperty = p; +} + +/* + don't allow start/stop/pause/resume to be manually invoked, + because something else (like a Behavior) already has control + over the animation. +*/ +void QDeclarativeAbstractAnimation::setDisableUserControl() +{ + Q_D(QDeclarativeAbstractAnimation); + d->disableUserControl = true; +} + +void QDeclarativeAbstractAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_UNUSED(actions); + Q_UNUSED(modified); + Q_UNUSED(direction); +} + +void QDeclarativeAbstractAnimation::timelineComplete() +{ + Q_D(QDeclarativeAbstractAnimation); + setRunning(false); + if (d->alwaysRunToEnd && d->loopCount != 1) { + //restore the proper loopCount for the next run + qtAnimation()->setLoopCount(d->loopCount); + } +} + +/*! + \qmlclass PauseAnimation QDeclarativePauseAnimation + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \inherits Animation + \brief The PauseAnimation element provides a pause for an animation. + + When used in a SequentialAnimation, PauseAnimation is a step when + nothing happens, for a specified duration. + + A 500ms animation sequence, with a 100ms pause between two animations: + \code + SequentialAnimation { + NumberAnimation { ... duration: 200 } + PauseAnimation { duration: 100 } + NumberAnimation { ... duration: 200 } + } + \endcode + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativePauseAnimation::QDeclarativePauseAnimation(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativePauseAnimationPrivate), parent) +{ + Q_D(QDeclarativePauseAnimation); + d->init(); +} + +QDeclarativePauseAnimation::~QDeclarativePauseAnimation() +{ +} + +void QDeclarativePauseAnimationPrivate::init() +{ + Q_Q(QDeclarativePauseAnimation); + pa = new QPauseAnimation; + QDeclarative_setParent_noEvent(pa, q); +} + +/*! + \qmlproperty int QtQuick2::PauseAnimation::duration + This property holds the duration of the pause in milliseconds + + The default value is 250. +*/ +int QDeclarativePauseAnimation::duration() const +{ + Q_D(const QDeclarativePauseAnimation); + return d->pa->duration(); +} + +void QDeclarativePauseAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QDeclarativePauseAnimation); + if (d->pa->duration() == duration) + return; + d->pa->setDuration(duration); + emit durationChanged(duration); +} + +QAbstractAnimation *QDeclarativePauseAnimation::qtAnimation() +{ + Q_D(QDeclarativePauseAnimation); + return d->pa; +} + +/*! + \qmlclass ColorAnimation QDeclarativeColorAnimation + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \inherits PropertyAnimation + \brief The ColorAnimation element animates changes in color values. + + ColorAnimation is a specialized PropertyAnimation that defines an + animation to be applied when a color value changes. + + Here is a ColorAnimation applied to the \c color property of a \l Rectangle + as a property value source. It animates the \c color property's value from + its current value to a value of "red", over 1000 milliseconds: + + \snippet doc/src/snippets/declarative/coloranimation.qml 0 + + Like any other animation element, a ColorAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + For convenience, when a ColorAnimation is used in a \l Transition, it will + animate any \c color properties that have been modified during the state + change. If a \l{PropertyAnimation::}{property} or + \l{PropertyAnimation::}{properties} are explicitly set for the animation, + then those are used instead. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeColorAnimation::QDeclarativeColorAnimation(QObject *parent) +: QDeclarativePropertyAnimation(parent) +{ + Q_D(QDeclarativePropertyAnimation); + d->interpolatorType = QMetaType::QColor; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultToInterpolatorType = true; +} + +QDeclarativeColorAnimation::~QDeclarativeColorAnimation() +{ +} + +/*! + \qmlproperty color QtQuick2::ColorAnimation::from + This property holds the color value at which the animation should begin. + + For example, the following animation is not applied until a color value + has reached "#c0c0c0": + + \qml + Item { + states: [ + // States are defined here... + ] + + transition: Transition { + NumberAnimation { from: "#c0c0c0"; duration: 2000 } + } + } + \endqml + + If the ColorAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +QColor QDeclarativeColorAnimation::from() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->from.value<QColor>(); +} + +void QDeclarativeColorAnimation::setFrom(const QColor &f) +{ + QDeclarativePropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty color QtQuick2::ColorAnimation::to + + This property holds the color value at which the animation should end. + + If the ColorAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +QColor QDeclarativeColorAnimation::to() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->to.value<QColor>(); +} + +void QDeclarativeColorAnimation::setTo(const QColor &t) +{ + QDeclarativePropertyAnimation::setTo(t); +} + + + +/*! + \qmlclass ScriptAction QDeclarativeScriptAction + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \inherits Animation + \brief The ScriptAction element allows scripts to be run during an animation. + + ScriptAction can be used to run a script at a specific point in an animation. + + \qml + SequentialAnimation { + NumberAnimation { + // ... + } + ScriptAction { script: doSomething(); } + NumberAnimation { + // ... + } + } + \endqml + + When used as part of a Transition, you can also target a specific + StateChangeScript to run using the \c scriptName property. + + \snippet doc/src/snippets/declarative/states/statechangescript.qml state and transition + + \sa StateChangeScript +*/ +QDeclarativeScriptAction::QDeclarativeScriptAction(QObject *parent) + :QDeclarativeAbstractAnimation(*(new QDeclarativeScriptActionPrivate), parent) +{ + Q_D(QDeclarativeScriptAction); + d->init(); +} + +QDeclarativeScriptAction::~QDeclarativeScriptAction() +{ +} + +void QDeclarativeScriptActionPrivate::init() +{ + Q_Q(QDeclarativeScriptAction); + rsa = new QActionAnimation(&proxy); + QDeclarative_setParent_noEvent(rsa, q); +} + +/*! + \qmlproperty script QtQuick2::ScriptAction::script + This property holds the script to run. +*/ +QDeclarativeScriptString QDeclarativeScriptAction::script() const +{ + Q_D(const QDeclarativeScriptAction); + return d->script; +} + +void QDeclarativeScriptAction::setScript(const QDeclarativeScriptString &script) +{ + Q_D(QDeclarativeScriptAction); + d->script = script; +} + +/*! + \qmlproperty string QtQuick2::ScriptAction::scriptName + This property holds the the name of the StateChangeScript to run. + + This property is only valid when ScriptAction is used as part of a transition. + If both script and scriptName are set, scriptName will be used. + + \note When using scriptName in a reversible transition, the script will only + be run when the transition is being run forwards. +*/ +QString QDeclarativeScriptAction::stateChangeScriptName() const +{ + Q_D(const QDeclarativeScriptAction); + return d->name; +} + +void QDeclarativeScriptAction::setStateChangeScriptName(const QString &name) +{ + Q_D(QDeclarativeScriptAction); + d->name = name; +} + +void QDeclarativeScriptActionPrivate::execute() +{ + Q_Q(QDeclarativeScriptAction); + if (hasRunScriptScript && reversing) + return; + + QDeclarativeScriptString scriptStr = hasRunScriptScript ? runScriptScript : script; + + if (!scriptStr.script().isEmpty()) { + QDeclarativeExpression expr(scriptStr); + expr.evaluate(); + if (expr.hasError()) + qmlInfo(q) << expr.error(); + } +} + +void QDeclarativeScriptAction::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeScriptAction); + Q_UNUSED(modified); + + d->hasRunScriptScript = false; + d->reversing = (direction == Backward); + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + + if (action.event && action.event->typeName() == QLatin1String("StateChangeScript") + && static_cast<QDeclarativeStateChangeScript*>(action.event)->name() == d->name) { + d->runScriptScript = static_cast<QDeclarativeStateChangeScript*>(action.event)->script(); + d->hasRunScriptScript = true; + action.actionDone = true; + break; //only match one (names should be unique) + } + } +} + +QAbstractAnimation *QDeclarativeScriptAction::qtAnimation() +{ + Q_D(QDeclarativeScriptAction); + return d->rsa; +} + + + +/*! + \qmlclass PropertyAction QDeclarativePropertyAction + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \inherits Animation + \brief The PropertyAction element allows immediate property changes during animation. + + PropertyAction is used to specify an immediate property change during an + animation. The property change is not animated. + + It is useful for setting non-animated property values during an animation. + + For example, here is a SequentialAnimation that sets the image's + \l {Image::}{smooth} property to \c true, animates the width of the image, + then sets \l {Image::}{smooth} back to \c false: + + \snippet doc/src/snippets/declarative/propertyaction.qml standalone + + PropertyAction is also useful for setting the exact point at which a property + change should occur during a \l Transition. For example, if PropertyChanges + was used in a \l State to rotate an item around a particular + \l {Item::}{transformOrigin}, it might be implemented like this: + + \snippet doc/src/snippets/declarative/propertyaction.qml transition + + However, with this code, the \c transformOrigin is not set until \e after + the animation, as a \l State is taken to define the values at the \e end of + a transition. The animation would rotate at the default \c transformOrigin, + then jump to \c Item.BottomRight. To fix this, insert a PropertyAction + before the RotationAnimation begins: + + \snippet doc/src/snippets/declarative/propertyaction-sequential.qml sequential + + This immediately sets the \c transformOrigin property to the value defined + in the end state of the \l Transition (i.e. the value defined in the + PropertyAction object) so that the rotation animation begins with the + correct transform origin. + + \sa {QML Animation and Transitions}, QtDeclarative +*/ +QDeclarativePropertyAction::QDeclarativePropertyAction(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativePropertyActionPrivate), parent) +{ + Q_D(QDeclarativePropertyAction); + d->init(); +} + +QDeclarativePropertyAction::~QDeclarativePropertyAction() +{ +} + +void QDeclarativePropertyActionPrivate::init() +{ + Q_Q(QDeclarativePropertyAction); + spa = new QActionAnimation; + QDeclarative_setParent_noEvent(spa, q); +} + +QObject *QDeclarativePropertyAction::target() const +{ + Q_D(const QDeclarativePropertyAction); + return d->target; +} + +void QDeclarativePropertyAction::setTarget(QObject *o) +{ + Q_D(QDeclarativePropertyAction); + if (d->target == o) + return; + d->target = o; + emit targetChanged(); +} + +QString QDeclarativePropertyAction::property() const +{ + Q_D(const QDeclarativePropertyAction); + return d->propertyName; +} + +void QDeclarativePropertyAction::setProperty(const QString &n) +{ + Q_D(QDeclarativePropertyAction); + if (d->propertyName == n) + return; + d->propertyName = n; + emit propertyChanged(); +} + +/*! + \qmlproperty Object QtQuick2::PropertyAction::target + \qmlproperty list<Object> QtQuick2::PropertyAction::targets + \qmlproperty string QtQuick2::PropertyAction::property + \qmlproperty string QtQuick2::PropertyAction::properties + + These properties determine the items and their properties that are + affected by this action. + + The details of how these properties are interpreted in different situations + is covered in the \l{PropertyAnimation::properties}{corresponding} PropertyAnimation + documentation. + + \sa exclude +*/ +QString QDeclarativePropertyAction::properties() const +{ + Q_D(const QDeclarativePropertyAction); + return d->properties; +} + +void QDeclarativePropertyAction::setProperties(const QString &p) +{ + Q_D(QDeclarativePropertyAction); + if (d->properties == p) + return; + d->properties = p; + emit propertiesChanged(p); +} + +QDeclarativeListProperty<QObject> QDeclarativePropertyAction::targets() +{ + Q_D(QDeclarativePropertyAction); + return QDeclarativeListProperty<QObject>(this, d->targets); +} + +/*! + \qmlproperty list<Object> QtQuick2::PropertyAction::exclude + This property holds the objects that should not be affected by this action. + + \sa targets +*/ +QDeclarativeListProperty<QObject> QDeclarativePropertyAction::exclude() +{ + Q_D(QDeclarativePropertyAction); + return QDeclarativeListProperty<QObject>(this, d->exclude); +} + +/*! + \qmlproperty any QtQuick2::PropertyAction::value + This property holds the value to be set on the property. + + If the PropertyAction is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. +*/ +QVariant QDeclarativePropertyAction::value() const +{ + Q_D(const QDeclarativePropertyAction); + return d->value; +} + +void QDeclarativePropertyAction::setValue(const QVariant &v) +{ + Q_D(QDeclarativePropertyAction); + if (d->value.isNull || d->value != v) { + d->value = v; + emit valueChanged(v); + } +} + +QAbstractAnimation *QDeclarativePropertyAction::qtAnimation() +{ + Q_D(QDeclarativePropertyAction); + return d->spa; +} + +void QDeclarativePropertyAction::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativePropertyAction); + Q_UNUSED(direction); + + struct QDeclarativeSetPropertyAnimationAction : public QAbstractAnimationAction + { + QDeclarativeStateActions actions; + virtual void doAction() + { + for (int ii = 0; ii < actions.count(); ++ii) { + const QDeclarativeAction &action = actions.at(ii); + QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } + } + }; + + QStringList props = d->properties.isEmpty() ? QStringList() : d->properties.split(QLatin1Char(',')); + for (int ii = 0; ii < props.count(); ++ii) + props[ii] = props.at(ii).trimmed(); + if (!d->propertyName.isEmpty()) + props << d->propertyName; + + QList<QObject*> targets = d->targets; + if (d->target) + targets.append(d->target); + + bool hasSelectors = !props.isEmpty() || !targets.isEmpty() || !d->exclude.isEmpty(); + + if (d->defaultProperty.isValid() && !hasSelectors) { + props << d->defaultProperty.name(); + targets << d->defaultProperty.object(); + } + + QDeclarativeSetPropertyAnimationAction *data = new QDeclarativeSetPropertyAnimationAction; + + bool hasExplicit = false; + //an explicit animation has been specified + if (d->value.isValid()) { + for (int i = 0; i < props.count(); ++i) { + for (int j = 0; j < targets.count(); ++j) { + QDeclarativeAction myAction; + myAction.property = d->createProperty(targets.at(j), props.at(i), this); + if (myAction.property.isValid()) { + myAction.toValue = d->value; + QDeclarativePropertyAnimationPrivate::convertVariant(myAction.toValue, myAction.property.propertyType()); + data->actions << myAction; + hasExplicit = true; + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + if (action.property.object() == myAction.property.object() && + myAction.property.name() == action.property.name()) { + modified << action.property; + break; //### any chance there could be multiples? + } + } + } + } + } + } + + if (!hasExplicit) + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + QObject *sObj = action.specifiedObject; + QString sPropertyName = action.specifiedProperty; + bool same = (obj == sObj); + + if ((targets.isEmpty() || targets.contains(obj) || (!same && targets.contains(sObj))) && + (!d->exclude.contains(obj)) && (same || (!d->exclude.contains(sObj))) && + (props.contains(propertyName) || (!same && props.contains(sPropertyName)))) { + QDeclarativeAction myAction = action; + + if (d->value.isValid()) + myAction.toValue = d->value; + QDeclarativePropertyAnimationPrivate::convertVariant(myAction.toValue, myAction.property.propertyType()); + + modified << action.property; + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + + if (data->actions.count()) { + d->spa->setAnimAction(data, QAbstractAnimation::DeleteWhenStopped); + } else { + delete data; + } +} + +/*! + \qmlclass NumberAnimation QDeclarativeNumberAnimation + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \inherits PropertyAnimation + \brief The NumberAnimation element animates changes in qreal-type values. + + NumberAnimation is a specialized PropertyAnimation that defines an + animation to be applied when a numerical value changes. + + Here is a NumberAnimation applied to the \c x property of a \l Rectangle + as a property value source. It animates the \c x value from its current + value to a value of 50, over 1000 milliseconds: + + \snippet doc/src/snippets/declarative/numberanimation.qml 0 + + Like any other animation element, a NumberAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + Note that NumberAnimation may not animate smoothly if there are irregular + changes in the number value that it is tracking. If this is the case, use + SmoothedAnimation instead. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeNumberAnimation::QDeclarativeNumberAnimation(QObject *parent) +: QDeclarativePropertyAnimation(parent) +{ + init(); +} + +QDeclarativeNumberAnimation::QDeclarativeNumberAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent) +: QDeclarativePropertyAnimation(dd, parent) +{ + init(); +} + +QDeclarativeNumberAnimation::~QDeclarativeNumberAnimation() +{ +} + +void QDeclarativeNumberAnimation::init() +{ + Q_D(QDeclarativePropertyAnimation); + d->interpolatorType = QMetaType::QReal; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); +} + +/*! + \qmlproperty real QtQuick2::NumberAnimation::from + This property holds the starting value for the animation. + + For example, the following animation is not applied until the \c x value + has reached 100: + + \qml + Item { + states: [ + // ... + ] + + transition: Transition { + NumberAnimation { properties: "x"; from: 100; duration: 200 } + } + } + \endqml + + If the NumberAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ + +qreal QDeclarativeNumberAnimation::from() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->from.toReal(); +} + +void QDeclarativeNumberAnimation::setFrom(qreal f) +{ + QDeclarativePropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real QtQuick2::NumberAnimation::to + This property holds the end value for the animation. + + If the NumberAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +qreal QDeclarativeNumberAnimation::to() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->to.toReal(); +} + +void QDeclarativeNumberAnimation::setTo(qreal t) +{ + QDeclarativePropertyAnimation::setTo(t); +} + + + +/*! + \qmlclass Vector3dAnimation QDeclarativeVector3dAnimation + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \inherits PropertyAnimation + \brief The Vector3dAnimation element animates changes in QVector3d values. + + Vector3dAnimation is a specialized PropertyAnimation that defines an + animation to be applied when a Vector3d value changes. + + Like any other animation element, a Vector3dAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeVector3dAnimation::QDeclarativeVector3dAnimation(QObject *parent) +: QDeclarativePropertyAnimation(parent) +{ + Q_D(QDeclarativePropertyAnimation); + d->interpolatorType = QMetaType::QVector3D; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultToInterpolatorType = true; +} + +QDeclarativeVector3dAnimation::~QDeclarativeVector3dAnimation() +{ +} + +/*! + \qmlproperty real QtQuick2::Vector3dAnimation::from + This property holds the starting value for the animation. + + If the Vector3dAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +QVector3D QDeclarativeVector3dAnimation::from() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->from.value<QVector3D>(); +} + +void QDeclarativeVector3dAnimation::setFrom(QVector3D f) +{ + QDeclarativePropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real QtQuick2::Vector3dAnimation::to + This property holds the end value for the animation. + + If the Vector3dAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +QVector3D QDeclarativeVector3dAnimation::to() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->to.value<QVector3D>(); +} + +void QDeclarativeVector3dAnimation::setTo(QVector3D t) +{ + QDeclarativePropertyAnimation::setTo(t); +} + + + +/*! + \qmlclass RotationAnimation QDeclarativeRotationAnimation + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \inherits PropertyAnimation + \brief The RotationAnimation element animates changes in rotation values. + + RotationAnimation is a specialized PropertyAnimation that gives control + over the direction of rotation during an animation. + + By default, it rotates in the direction + of the numerical change; a rotation from 0 to 240 will rotate 240 degrees + clockwise, while a rotation from 240 to 0 will rotate 240 degrees + counterclockwise. The \l direction property can be set to specify the + direction in which the rotation should occur. + + In the following example we use RotationAnimation to animate the rotation + between states via the shortest path: + + \snippet doc/src/snippets/declarative/rotationanimation.qml 0 + + Notice the RotationAnimation did not need to set a \l target + value. As a convenience, when used in a transition, RotationAnimation will rotate all + properties named "rotation" or "angle". You can override this by providing + your own properties via \l {PropertyAnimation::properties}{properties} or + \l {PropertyAnimation::property}{property}. + + Also, note the \l Rectangle will be rotated around its default + \l {Item::}{transformOrigin} (which is \c Item.Center). To use a different + transform origin, set the origin in the PropertyChanges object and apply + the change at the start of the animation using PropertyAction. See the + PropertyAction documentation for more details. + + Like any other animation element, a RotationAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QVariant _q_interpolateShortestRotation(qreal &f, qreal &t, qreal progress) +{ + qreal newt = t; + qreal diff = t-f; + while(diff > 180.0){ + newt -= 360.0; + diff -= 360.0; + } + while(diff < -180.0){ + newt += 360.0; + diff += 360.0; + } + return QVariant(f + (newt - f) * progress); +} + +QVariant _q_interpolateClockwiseRotation(qreal &f, qreal &t, qreal progress) +{ + qreal newt = t; + qreal diff = t-f; + while(diff < 0.0){ + newt += 360.0; + diff += 360.0; + } + return QVariant(f + (newt - f) * progress); +} + +QVariant _q_interpolateCounterclockwiseRotation(qreal &f, qreal &t, qreal progress) +{ + qreal newt = t; + qreal diff = t-f; + while(diff > 0.0){ + newt -= 360.0; + diff -= 360.0; + } + return QVariant(f + (newt - f) * progress); +} + +QDeclarativeRotationAnimation::QDeclarativeRotationAnimation(QObject *parent) +: QDeclarativePropertyAnimation(*(new QDeclarativeRotationAnimationPrivate), parent) +{ + Q_D(QDeclarativeRotationAnimation); + d->interpolatorType = QMetaType::QReal; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultProperties = QLatin1String("rotation,angle"); +} + +QDeclarativeRotationAnimation::~QDeclarativeRotationAnimation() +{ +} + +/*! + \qmlproperty real QtQuick2::RotationAnimation::from + This property holds the starting value for the animation. + + For example, the following animation is not applied until the \c angle value + has reached 100: + + \qml + Item { + states: [ + // ... + ] + + transition: Transition { + RotationAnimation { properties: "angle"; from: 100; duration: 2000 } + } + } + \endqml + + If the RotationAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +qreal QDeclarativeRotationAnimation::from() const +{ + Q_D(const QDeclarativeRotationAnimation); + return d->from.toReal(); +} + +void QDeclarativeRotationAnimation::setFrom(qreal f) +{ + QDeclarativePropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real QtQuick2::RotationAnimation::to + This property holds the end value for the animation.. + + If the RotationAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +qreal QDeclarativeRotationAnimation::to() const +{ + Q_D(const QDeclarativeRotationAnimation); + return d->to.toReal(); +} + +void QDeclarativeRotationAnimation::setTo(qreal t) +{ + QDeclarativePropertyAnimation::setTo(t); +} + +/*! + \qmlproperty enumeration QtQuick2::RotationAnimation::direction + This property holds the direction of the rotation. + + Possible values are: + + \list + \o RotationAnimation.Numerical (default) - Rotate by linearly interpolating between the two numbers. + A rotation from 10 to 350 will rotate 340 degrees clockwise. + \o RotationAnimation.Clockwise - Rotate clockwise between the two values + \o RotationAnimation.Counterclockwise - Rotate counterclockwise between the two values + \o RotationAnimation.Shortest - Rotate in the direction that produces the shortest animation path. + A rotation from 10 to 350 will rotate 20 degrees counterclockwise. + \endlist +*/ +QDeclarativeRotationAnimation::RotationDirection QDeclarativeRotationAnimation::direction() const +{ + Q_D(const QDeclarativeRotationAnimation); + return d->direction; +} + +void QDeclarativeRotationAnimation::setDirection(QDeclarativeRotationAnimation::RotationDirection direction) +{ + Q_D(QDeclarativeRotationAnimation); + if (d->direction == direction) + return; + + d->direction = direction; + switch(d->direction) { + case Clockwise: + d->interpolator = reinterpret_cast<QVariantAnimation::Interpolator>(&_q_interpolateClockwiseRotation); + break; + case Counterclockwise: + d->interpolator = reinterpret_cast<QVariantAnimation::Interpolator>(&_q_interpolateCounterclockwiseRotation); + break; + case Shortest: + d->interpolator = reinterpret_cast<QVariantAnimation::Interpolator>(&_q_interpolateShortestRotation); + break; + default: + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + break; + } + + emit directionChanged(); +} + + + +QDeclarativeAnimationGroup::QDeclarativeAnimationGroup(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativeAnimationGroupPrivate), parent) +{ +} + +QDeclarativeAnimationGroup::QDeclarativeAnimationGroup(QDeclarativeAnimationGroupPrivate &dd, QObject *parent) + : QDeclarativeAbstractAnimation(dd, parent) +{ +} + +void QDeclarativeAnimationGroupPrivate::append_animation(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list, QDeclarativeAbstractAnimation *a) +{ + QDeclarativeAnimationGroup *q = qobject_cast<QDeclarativeAnimationGroup *>(list->object); + if (q) { + a->setGroup(q); + // This is an optimization for the parenting that already occurs via addAnimation + QDeclarative_setParent_noEvent(a->qtAnimation(), q->d_func()->ag); + q->d_func()->ag->addAnimation(a->qtAnimation()); + } +} + +void QDeclarativeAnimationGroupPrivate::clear_animation(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list) +{ + QDeclarativeAnimationGroup *q = qobject_cast<QDeclarativeAnimationGroup *>(list->object); + if (q) { + while (q->d_func()->animations.count()) { + QDeclarativeAbstractAnimation *firstAnim = q->d_func()->animations.at(0); + QDeclarative_setParent_noEvent(firstAnim->qtAnimation(), 0); + q->d_func()->ag->removeAnimation(firstAnim->qtAnimation()); + firstAnim->setGroup(0); + } + } +} + +QDeclarativeAnimationGroup::~QDeclarativeAnimationGroup() +{ +} + +QDeclarativeListProperty<QDeclarativeAbstractAnimation> QDeclarativeAnimationGroup::animations() +{ + Q_D(QDeclarativeAnimationGroup); + QDeclarativeListProperty<QDeclarativeAbstractAnimation> list(this, d->animations); + list.append = &QDeclarativeAnimationGroupPrivate::append_animation; + list.clear = &QDeclarativeAnimationGroupPrivate::clear_animation; + return list; +} + +/*! + \qmlclass SequentialAnimation QDeclarativeSequentialAnimation + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \inherits Animation + \brief The SequentialAnimation element allows animations to be run sequentially. + + The SequentialAnimation and ParallelAnimation elements allow multiple + animations to be run together. Animations defined in a SequentialAnimation + are run one after the other, while animations defined in a ParallelAnimation + are run at the same time. + + The following example runs two number animations in a sequence. The \l Rectangle + animates to a \c x position of 50, then to a \c y position of 50. + + \snippet doc/src/snippets/declarative/sequentialanimation.qml 0 + + Animations defined within a \l Transition are automatically run in parallel, + so SequentialAnimation can be used to enclose the animations in a \l Transition + if this is the preferred behavior. + + Like any other animation element, a SequentialAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \note Once an animation has been grouped into a SequentialAnimation or + ParallelAnimation, it cannot be individually started and stopped; the + SequentialAnimation or ParallelAnimation must be started and stopped as a group. + + \sa ParallelAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ + +QDeclarativeSequentialAnimation::QDeclarativeSequentialAnimation(QObject *parent) : + QDeclarativeAnimationGroup(parent) +{ + Q_D(QDeclarativeAnimationGroup); + d->ag = new QSequentialAnimationGroup; + QDeclarative_setParent_noEvent(d->ag, this); +} + +QDeclarativeSequentialAnimation::~QDeclarativeSequentialAnimation() +{ +} + +QAbstractAnimation *QDeclarativeSequentialAnimation::qtAnimation() +{ + Q_D(QDeclarativeAnimationGroup); + return d->ag; +} + +void QDeclarativeSequentialAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeAnimationGroup); + + int inc = 1; + int from = 0; + if (direction == Backward) { + inc = -1; + from = d->animations.count() - 1; + } + + bool valid = d->defaultProperty.isValid(); + for (int ii = from; ii < d->animations.count() && ii >= 0; ii += inc) { + if (valid) + d->animations.at(ii)->setDefaultTarget(d->defaultProperty); + d->animations.at(ii)->transition(actions, modified, direction); + } +} + + + +/*! + \qmlclass ParallelAnimation QDeclarativeParallelAnimation + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \inherits Animation + \brief The ParallelAnimation element allows animations to be run in parallel. + + The SequentialAnimation and ParallelAnimation elements allow multiple + animations to be run together. Animations defined in a SequentialAnimation + are run one after the other, while animations defined in a ParallelAnimation + are run at the same time. + + The following animation runs two number animations in parallel. The \l Rectangle + moves to (50,50) by animating its \c x and \c y properties at the same time. + + \snippet doc/src/snippets/declarative/parallelanimation.qml 0 + + Like any other animation element, a ParallelAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \note Once an animation has been grouped into a SequentialAnimation or + ParallelAnimation, it cannot be individually started and stopped; the + SequentialAnimation or ParallelAnimation must be started and stopped as a group. + + \sa SequentialAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarativeParallelAnimation::QDeclarativeParallelAnimation(QObject *parent) : + QDeclarativeAnimationGroup(parent) +{ + Q_D(QDeclarativeAnimationGroup); + d->ag = new QParallelAnimationGroup; + QDeclarative_setParent_noEvent(d->ag, this); +} + +QDeclarativeParallelAnimation::~QDeclarativeParallelAnimation() +{ +} + +QAbstractAnimation *QDeclarativeParallelAnimation::qtAnimation() +{ + Q_D(QDeclarativeAnimationGroup); + return d->ag; +} + +void QDeclarativeParallelAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeAnimationGroup); + bool valid = d->defaultProperty.isValid(); + for (int ii = 0; ii < d->animations.count(); ++ii) { + if (valid) + d->animations.at(ii)->setDefaultTarget(d->defaultProperty); + d->animations.at(ii)->transition(actions, modified, direction); + } +} + + + +//convert a variant from string type to another animatable type +void QDeclarativePropertyAnimationPrivate::convertVariant(QVariant &variant, int type) +{ + if (variant.userType() != QVariant::String) { + variant.convert((QVariant::Type)type); + return; + } + + switch (type) { + case QVariant::Rect: { + variant.setValue(QDeclarativeStringConverters::rectFFromString(variant.toString()).toRect()); + break; + } + case QVariant::RectF: { + variant.setValue(QDeclarativeStringConverters::rectFFromString(variant.toString())); + break; + } + case QVariant::Point: { + variant.setValue(QDeclarativeStringConverters::pointFFromString(variant.toString()).toPoint()); + break; + } + case QVariant::PointF: { + variant.setValue(QDeclarativeStringConverters::pointFFromString(variant.toString())); + break; + } + case QVariant::Size: { + variant.setValue(QDeclarativeStringConverters::sizeFFromString(variant.toString()).toSize()); + break; + } + case QVariant::SizeF: { + variant.setValue(QDeclarativeStringConverters::sizeFFromString(variant.toString())); + break; + } + case QVariant::Color: { + variant.setValue(QDeclarativeStringConverters::colorFromString(variant.toString())); + break; + } + case QVariant::Vector3D: { + variant.setValue(QDeclarativeStringConverters::vector3DFromString(variant.toString())); + break; + } + default: + if (QDeclarativeValueTypeFactory::isValueType((uint)type)) { + variant.convert((QVariant::Type)type); + } else { + QDeclarativeMetaType::StringConverter converter = QDeclarativeMetaType::customStringConverter(type); + if (converter) + variant = converter(variant.toString()); + } + break; + } +} + +/*! + \qmlclass PropertyAnimation QDeclarativePropertyAnimation + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \inherits Animation + \brief The PropertyAnimation element animates changes in property values. + + PropertyAnimation provides a way to animate changes to a property's value. + + It can be used to define animations in a number of ways: + + \list + \o In a \l Transition + + For example, to animate any objects that have changed their \c x or \c y properties + as a result of a state change, using an \c InOutQuad easing curve: + + \snippet doc/src/snippets/declarative/propertyanimation.qml transition + + + \o In a \l Behavior + + For example, to animate all changes to a rectangle's \c x property: + + \snippet doc/src/snippets/declarative/propertyanimation.qml behavior + + + \o As a property value source + + For example, to repeatedly animate the rectangle's \c x property: + + \snippet doc/src/snippets/declarative/propertyanimation.qml propertyvaluesource + + + \o In a signal handler + + For example, to fade out \c theObject when clicked: + \qml + MouseArea { + anchors.fill: theObject + onClicked: PropertyAnimation { target: theObject; property: "opacity"; to: 0 } + } + \endqml + + \o Standalone + + For example, to animate \c rect's \c width property over 500ms, from its current width to 30: + + \snippet doc/src/snippets/declarative/propertyanimation.qml standalone + + \endlist + + Depending on how the animation is used, the set of properties normally used will be + different. For more information see the individual property documentation, as well + as the \l{QML Animation and Transitions} introduction. + + Note that PropertyAnimation inherits the abstract \l Animation element. + This includes additional properties and methods for controlling the animation. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ + +QDeclarativePropertyAnimation::QDeclarativePropertyAnimation(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativePropertyAnimationPrivate), parent) +{ + Q_D(QDeclarativePropertyAnimation); + d->init(); +} + +QDeclarativePropertyAnimation::QDeclarativePropertyAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent) +: QDeclarativeAbstractAnimation(dd, parent) +{ + Q_D(QDeclarativePropertyAnimation); + d->init(); +} + +QDeclarativePropertyAnimation::~QDeclarativePropertyAnimation() +{ +} + +void QDeclarativePropertyAnimationPrivate::init() +{ + Q_Q(QDeclarativePropertyAnimation); + va = new QDeclarativeBulkValueAnimator; + QDeclarative_setParent_noEvent(va, q); +} + +/*! + \qmlproperty int QtQuick2::PropertyAnimation::duration + This property holds the duration of the animation, in milliseconds. + + The default value is 250. +*/ +int QDeclarativePropertyAnimation::duration() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->va->duration(); +} + +void QDeclarativePropertyAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QDeclarativePropertyAnimation); + if (d->va->duration() == duration) + return; + d->va->setDuration(duration); + emit durationChanged(duration); +} + +/*! + \qmlproperty real QtQuick2::PropertyAnimation::from + This property holds the starting value for the animation. + + If the PropertyAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +QVariant QDeclarativePropertyAnimation::from() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->from; +} + +void QDeclarativePropertyAnimation::setFrom(const QVariant &f) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->fromIsDefined && f == d->from) + return; + d->from = f; + d->fromIsDefined = f.isValid(); + emit fromChanged(f); +} + +/*! + \qmlproperty real QtQuick2::PropertyAnimation::to + This property holds the end value for the animation. + + If the PropertyAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +QVariant QDeclarativePropertyAnimation::to() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->to; +} + +void QDeclarativePropertyAnimation::setTo(const QVariant &t) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->toIsDefined && t == d->to) + return; + d->to = t; + d->toIsDefined = t.isValid(); + emit toChanged(t); +} + +/*! + \qmlproperty enumeration QtQuick2::PropertyAnimation::easing.type + \qmlproperty real QtQuick2::PropertyAnimation::easing.amplitude + \qmlproperty real QtQuick2::PropertyAnimation::easing.overshoot + \qmlproperty real QtQuick2::PropertyAnimation::easing.period + \brief the easing curve used for the animation. + + To specify an easing curve you need to specify at least the type. For some curves you can also specify + amplitude, period and/or overshoot (more details provided after the table). The default easing curve is + \c Easing.Linear. + + \qml + PropertyAnimation { properties: "y"; easing.type: Easing.InOutElastic; easing.amplitude: 2.0; easing.period: 1.5 } + \endqml + + Available types are: + + \table + \row + \o \c Easing.Linear + \o Easing curve for a linear (t) function: velocity is constant. + \o \inlineimage qeasingcurve-linear.png + \row + \o \c Easing.InQuad + \o Easing curve for a quadratic (t^2) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquad.png + \row + \o \c Easing.OutQuad + \o Easing curve for a quadratic (t^2) function: decelerating to zero velocity. + \o \inlineimage qeasingcurve-outquad.png + \row + \o \c Easing.InOutQuad + \o Easing curve for a quadratic (t^2) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquad.png + \row + \o \c Easing.OutInQuad + \o Easing curve for a quadratic (t^2) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquad.png + \row + \o \c Easing.InCubic + \o Easing curve for a cubic (t^3) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-incubic.png + \row + \o \c Easing.OutCubic + \o Easing curve for a cubic (t^3) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outcubic.png + \row + \o \c Easing.InOutCubic + \o Easing curve for a cubic (t^3) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutcubic.png + \row + \o \c Easing.OutInCubic + \o Easing curve for a cubic (t^3) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outincubic.png + \row + \o \c Easing.InQuart + \o Easing curve for a quartic (t^4) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquart.png + \row + \o \c Easing.OutQuart + \o Easing curve for a quartic (t^4) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outquart.png + \row + \o \c Easing.InOutQuart + \o Easing curve for a quartic (t^4) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquart.png + \row + \o \c Easing.OutInQuart + \o Easing curve for a quartic (t^4) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquart.png + \row + \o \c Easing.InQuint + \o Easing curve for a quintic (t^5) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquint.png + \row + \o \c Easing.OutQuint + \o Easing curve for a quintic (t^5) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outquint.png + \row + \o \c Easing.InOutQuint + \o Easing curve for a quintic (t^5) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquint.png + \row + \o \c Easing.OutInQuint + \o Easing curve for a quintic (t^5) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquint.png + \row + \o \c Easing.InSine + \o Easing curve for a sinusoidal (sin(t)) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-insine.png + \row + \o \c Easing.OutSine + \o Easing curve for a sinusoidal (sin(t)) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outsine.png + \row + \o \c Easing.InOutSine + \o Easing curve for a sinusoidal (sin(t)) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutsine.png + \row + \o \c Easing.OutInSine + \o Easing curve for a sinusoidal (sin(t)) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinsine.png + \row + \o \c Easing.InExpo + \o Easing curve for an exponential (2^t) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inexpo.png + \row + \o \c Easing.OutExpo + \o Easing curve for an exponential (2^t) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outexpo.png + \row + \o \c Easing.InOutExpo + \o Easing curve for an exponential (2^t) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutexpo.png + \row + \o \c Easing.OutInExpo + \o Easing curve for an exponential (2^t) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinexpo.png + \row + \o \c Easing.InCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-incirc.png + \row + \o \c Easing.OutCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outcirc.png + \row + \o \c Easing.InOutCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutcirc.png + \row + \o \c Easing.OutInCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outincirc.png + \row + \o \c Easing.InElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: accelerating from zero velocity. + \br The peak amplitude can be set with the \e amplitude parameter, and the period of decay by the \e period parameter. + \o \inlineimage qeasingcurve-inelastic.png + \row + \o \c Easing.OutElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: decelerating from zero velocity. + \br The peak amplitude can be set with the \e amplitude parameter, and the period of decay by the \e period parameter. + \o \inlineimage qeasingcurve-outelastic.png + \row + \o \c Easing.InOutElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutelastic.png + \row + \o \c Easing.OutInElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinelastic.png + \row + \o \c Easing.InBack + \o Easing curve for a back (overshooting cubic function: (s+1)*t^3 - s*t^2) easing in: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inback.png + \row + \o \c Easing.OutBack + \o Easing curve for a back (overshooting cubic function: (s+1)*t^3 - s*t^2) easing out: decelerating to zero velocity. + \o \inlineimage qeasingcurve-outback.png + \row + \o \c Easing.InOutBack + \o Easing curve for a back (overshooting cubic function: (s+1)*t^3 - s*t^2) easing in/out: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutback.png + \row + \o \c Easing.OutInBack + \o Easing curve for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out/in: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinback.png + \row + \o \c Easing.InBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inbounce.png + \row + \o \c Easing.OutBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outbounce.png + \row + \o \c Easing.InOutBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function easing in/out: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutbounce.png + \row + \o \c Easing.OutInBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function easing out/in: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinbounce.png + \endtable + + \c easing.amplitude is only applicable for bounce and elastic curves (curves of type + \c Easing.InBounce, \c Easing.OutBounce, \c Easing.InOutBounce, \c Easing.OutInBounce, \c Easing.InElastic, + \c Easing.OutElastic, \c Easing.InOutElastic or \c Easing.OutInElastic). + + \c easing.overshoot is only applicable if \c easing.type is: \c Easing.InBack, \c Easing.OutBack, + \c Easing.InOutBack or \c Easing.OutInBack. + + \c easing.period is only applicable if easing.type is: \c Easing.InElastic, \c Easing.OutElastic, + \c Easing.InOutElastic or \c Easing.OutInElastic. + + See the \l {declarative/animation/easing}{easing} example for a demonstration of + the different easing settings. +*/ +QEasingCurve QDeclarativePropertyAnimation::easing() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->va->easingCurve(); +} + +void QDeclarativePropertyAnimation::setEasing(const QEasingCurve &e) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->va->easingCurve() == e) + return; + + d->va->setEasingCurve(e); + emit easingChanged(e); +} + +QObject *QDeclarativePropertyAnimation::target() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->target; +} + +void QDeclarativePropertyAnimation::setTarget(QObject *o) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->target == o) + return; + d->target = o; + emit targetChanged(); +} + +QString QDeclarativePropertyAnimation::property() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->propertyName; +} + +void QDeclarativePropertyAnimation::setProperty(const QString &n) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->propertyName == n) + return; + d->propertyName = n; + emit propertyChanged(); +} + +QString QDeclarativePropertyAnimation::properties() const +{ + Q_D(const QDeclarativePropertyAnimation); + return d->properties; +} + +void QDeclarativePropertyAnimation::setProperties(const QString &prop) +{ + Q_D(QDeclarativePropertyAnimation); + if (d->properties == prop) + return; + + d->properties = prop; + emit propertiesChanged(prop); +} + +/*! + \qmlproperty string QtQuick2::PropertyAnimation::properties + \qmlproperty list<Object> QtQuick2::PropertyAnimation::targets + \qmlproperty string QtQuick2::PropertyAnimation::property + \qmlproperty Object QtQuick2::PropertyAnimation::target + + These properties are used as a set to determine which properties should be animated. + The singular and plural forms are functionally identical, e.g. + \qml + NumberAnimation { target: theItem; property: "x"; to: 500 } + \endqml + has the same meaning as + \qml + NumberAnimation { targets: theItem; properties: "x"; to: 500 } + \endqml + The singular forms are slightly optimized, so if you do have only a single target/property + to animate you should try to use them. + + The \c targets property allows multiple targets to be set. For example, this animates the + \c x property of both \c itemA and \c itemB: + + \qml + NumberAnimation { targets: [itemA, itemB]; properties: "x"; to: 500 } + \endqml + + In many cases these properties do not need to be explicitly specified, as they can be + inferred from the animation framework: + + \table 80% + \row + \o Value Source / Behavior + \o When an animation is used as a value source or in a Behavior, the default target and property + name to be animated can both be inferred. + \qml + Rectangle { + id: theRect + width: 100; height: 100 + color: Qt.rgba(0,0,1) + NumberAnimation on x { to: 500; loops: Animation.Infinite } //animate theRect's x property + Behavior on y { NumberAnimation {} } //animate theRect's y property + } + \endqml + \row + \o Transition + \o When used in a transition, a property animation is assumed to match \e all targets + but \e no properties. In practice, that means you need to specify at least the properties + in order for the animation to do anything. + \qml + Rectangle { + id: theRect + width: 100; height: 100 + color: Qt.rgba(0,0,1) + Item { id: uselessItem } + states: State { + name: "state1" + PropertyChanges { target: theRect; x: 200; y: 200; z: 4 } + PropertyChanges { target: uselessItem; x: 10; y: 10; z: 2 } + } + transitions: Transition { + //animate both theRect's and uselessItem's x and y to their final values + NumberAnimation { properties: "x,y" } + + //animate theRect's z to its final value + NumberAnimation { target: theRect; property: "z" } + } + } + \endqml + \row + \o Standalone + \o When an animation is used standalone, both the target and property need to be + explicitly specified. + \qml + Rectangle { + id: theRect + width: 100; height: 100 + color: Qt.rgba(0,0,1) + //need to explicitly specify target and property + NumberAnimation { id: theAnim; target: theRect; property: "x"; to: 500 } + MouseArea { + anchors.fill: parent + onClicked: theAnim.start() + } + } + \endqml + \endtable + + As seen in the above example, properties is specified as a comma-separated string of property names to animate. + + \sa exclude, {QML Animation and Transitions} +*/ +QDeclarativeListProperty<QObject> QDeclarativePropertyAnimation::targets() +{ + Q_D(QDeclarativePropertyAnimation); + return QDeclarativeListProperty<QObject>(this, d->targets); +} + +/*! + \qmlproperty list<Object> QtQuick2::PropertyAnimation::exclude + This property holds the items not to be affected by this animation. + \sa PropertyAnimation::targets +*/ +QDeclarativeListProperty<QObject> QDeclarativePropertyAnimation::exclude() +{ + Q_D(QDeclarativePropertyAnimation); + return QDeclarativeListProperty<QObject>(this, d->exclude); +} + +QAbstractAnimation *QDeclarativePropertyAnimation::qtAnimation() +{ + Q_D(QDeclarativePropertyAnimation); + return d->va; +} + +void QDeclarativeAnimationPropertyUpdater::setValue(qreal v) +{ + bool deleted = false; + wasDeleted = &deleted; + if (reverse) //QVariantAnimation sends us 1->0 when reversed, but we are expecting 0->1 + v = 1 - v; + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + + if (v == 1.) + QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + else { + if (!fromSourced && !fromDefined) { + action.fromValue = action.property.read(); + if (interpolatorType) + QDeclarativePropertyAnimationPrivate::convertVariant(action.fromValue, interpolatorType); + } + if (!interpolatorType) { + int propType = action.property.propertyType(); + if (!prevInterpolatorType || prevInterpolatorType != propType) { + prevInterpolatorType = propType; + interpolator = QVariantAnimationPrivate::getInterpolator(prevInterpolatorType); + } + } + if (interpolator) + QDeclarativePropertyPrivate::write(action.property, interpolator(action.fromValue.constData(), action.toValue.constData(), v), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } + if (deleted) + return; + } + wasDeleted = 0; + fromSourced = true; +} + +void QDeclarativePropertyAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativePropertyAnimation); + + QStringList props = d->properties.isEmpty() ? QStringList() : d->properties.split(QLatin1Char(',')); + for (int ii = 0; ii < props.count(); ++ii) + props[ii] = props.at(ii).trimmed(); + if (!d->propertyName.isEmpty()) + props << d->propertyName; + + QList<QObject*> targets = d->targets; + if (d->target) + targets.append(d->target); + + bool hasSelectors = !props.isEmpty() || !targets.isEmpty() || !d->exclude.isEmpty(); + bool useType = (props.isEmpty() && d->defaultToInterpolatorType) ? true : false; + + if (d->defaultProperty.isValid() && !hasSelectors) { + props << d->defaultProperty.name(); + targets << d->defaultProperty.object(); + } + + if (props.isEmpty() && !d->defaultProperties.isEmpty()) { + props << d->defaultProperties.split(QLatin1Char(',')); + } + + QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater; + data->interpolatorType = d->interpolatorType; + data->interpolator = d->interpolator; + data->reverse = direction == Backward ? true : false; + data->fromSourced = false; + data->fromDefined = d->fromIsDefined; + + bool hasExplicit = false; + //an explicit animation has been specified + if (d->toIsDefined) { + for (int i = 0; i < props.count(); ++i) { + for (int j = 0; j < targets.count(); ++j) { + QDeclarativeAction myAction; + myAction.property = d->createProperty(targets.at(j), props.at(i), this); + if (myAction.property.isValid()) { + if (d->fromIsDefined) { + myAction.fromValue = d->from; + d->convertVariant(myAction.fromValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + } + myAction.toValue = d->to; + d->convertVariant(myAction.toValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + data->actions << myAction; + hasExplicit = true; + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + if (action.property.object() == myAction.property.object() && + myAction.property.name() == action.property.name()) { + modified << action.property; + break; //### any chance there could be multiples? + } + } + } + } + } + } + + if (!hasExplicit) + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + QObject *sObj = action.specifiedObject; + QString sPropertyName = action.specifiedProperty; + bool same = (obj == sObj); + + if ((targets.isEmpty() || targets.contains(obj) || (!same && targets.contains(sObj))) && + (!d->exclude.contains(obj)) && (same || (!d->exclude.contains(sObj))) && + (props.contains(propertyName) || (!same && props.contains(sPropertyName)) + || (useType && action.property.propertyType() == d->interpolatorType))) { + QDeclarativeAction myAction = action; + + if (d->fromIsDefined) + myAction.fromValue = d->from; + else + myAction.fromValue = QVariant(); + if (d->toIsDefined) + myAction.toValue = d->to; + + d->convertVariant(myAction.fromValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + d->convertVariant(myAction.toValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + + modified << action.property; + + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + + if (data->actions.count()) { + if (!d->rangeIsSet) { + d->va->setStartValue(qreal(0)); + d->va->setEndValue(qreal(1)); + d->rangeIsSet = true; + } + d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped); + d->va->setFromSourcedValue(&data->fromSourced); + d->actions = &data->actions; + } else { + delete data; + d->va->setFromSourcedValue(0); //clear previous data + d->va->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped); //clear previous data + d->actions = 0; + } +} + + +QDeclarativeScriptActionPrivate::QDeclarativeScriptActionPrivate() + : QDeclarativeAbstractAnimationPrivate(), hasRunScriptScript(false), reversing(false), proxy(this), rsa(0) {} + + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativeanimation_p.h b/src/quick/util/qdeclarativeanimation_p.h new file mode 100644 index 0000000000..6c8f552888 --- /dev/null +++ b/src/quick/util/qdeclarativeanimation_p.h @@ -0,0 +1,456 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANIMATION_H +#define QDECLARATIVEANIMATION_H + +#include "qdeclarativestate_p.h" +#include <QtGui/qvector3d.h> + +#include <qdeclarativepropertyvaluesource.h> +#include <qdeclarative.h> +#include <qdeclarativescriptstring.h> + +#include <QtCore/qvariant.h> +#include <QtCore/qeasingcurve.h> +#include <QtCore/QAbstractAnimation> +#include <QtGui/qcolor.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeAbstractAnimationPrivate; +class QDeclarativeAnimationGroup; +class Q_QUICK_PRIVATE_EXPORT QDeclarativeAbstractAnimation : public QObject, public QDeclarativePropertyValueSource, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAbstractAnimation) + + Q_INTERFACES(QDeclarativeParserStatus) + Q_INTERFACES(QDeclarativePropertyValueSource) + Q_ENUMS(Loops) + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged) + Q_PROPERTY(bool alwaysRunToEnd READ alwaysRunToEnd WRITE setAlwaysRunToEnd NOTIFY alwaysRunToEndChanged) + Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopCountChanged) + Q_CLASSINFO("DefaultMethod", "start()") + +public: + QDeclarativeAbstractAnimation(QObject *parent=0); + virtual ~QDeclarativeAbstractAnimation(); + + enum Loops { Infinite = -2 }; + + bool isRunning() const; + void setRunning(bool); + bool isPaused() const; + void setPaused(bool); + bool alwaysRunToEnd() const; + void setAlwaysRunToEnd(bool); + + int loops() const; + void setLoops(int); + + int currentTime(); + void setCurrentTime(int); + + QDeclarativeAnimationGroup *group() const; + void setGroup(QDeclarativeAnimationGroup *); + + void setDefaultTarget(const QDeclarativeProperty &); + void setDisableUserControl(); + + void classBegin(); + void componentComplete(); + +Q_SIGNALS: + void started(); + void completed(); + void runningChanged(bool); + void pausedChanged(bool); + void alwaysRunToEndChanged(bool); + void loopCountChanged(int); + +public Q_SLOTS: + void restart(); + void start(); + void pause(); + void resume(); + void stop(); + void complete(); + +protected: + QDeclarativeAbstractAnimation(QDeclarativeAbstractAnimationPrivate &dd, QObject *parent); + +public: + enum TransitionDirection { Forward, Backward }; + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation() = 0; + +private Q_SLOTS: + void timelineComplete(); + void componentFinalized(); +private: + virtual void setTarget(const QDeclarativeProperty &); + void notifyRunningChanged(bool running); + friend class QDeclarativeBehavior; + + +}; + +class QDeclarativePauseAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativePauseAnimation : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePauseAnimation) + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + +public: + QDeclarativePauseAnimation(QObject *parent=0); + virtual ~QDeclarativePauseAnimation(); + + int duration() const; + void setDuration(int); + +Q_SIGNALS: + void durationChanged(int); + +protected: + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativeScriptActionPrivate; +class Q_QUICK_PRIVATE_EXPORT QDeclarativeScriptAction : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeScriptAction) + + Q_PROPERTY(QDeclarativeScriptString script READ script WRITE setScript) + Q_PROPERTY(QString scriptName READ stateChangeScriptName WRITE setStateChangeScriptName) + +public: + QDeclarativeScriptAction(QObject *parent=0); + virtual ~QDeclarativeScriptAction(); + + QDeclarativeScriptString script() const; + void setScript(const QDeclarativeScriptString &); + + QString stateChangeScriptName() const; + void setStateChangeScriptName(const QString &); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativePropertyActionPrivate; +class QDeclarativePropertyAction : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAction) + + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged) + Q_PROPERTY(QString properties READ properties WRITE setProperties NOTIFY propertiesChanged) + Q_PROPERTY(QDeclarativeListProperty<QObject> targets READ targets) + Q_PROPERTY(QDeclarativeListProperty<QObject> exclude READ exclude) + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + +public: + QDeclarativePropertyAction(QObject *parent=0); + virtual ~QDeclarativePropertyAction(); + + QObject *target() const; + void setTarget(QObject *); + + QString property() const; + void setProperty(const QString &); + + QString properties() const; + void setProperties(const QString &); + + QDeclarativeListProperty<QObject> targets(); + QDeclarativeListProperty<QObject> exclude(); + + QVariant value() const; + void setValue(const QVariant &); + +Q_SIGNALS: + void valueChanged(const QVariant &); + void propertiesChanged(const QString &); + void targetChanged(); + void propertyChanged(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativePropertyAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativePropertyAnimation : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(QVariant from READ from WRITE setFrom NOTIFY fromChanged) + Q_PROPERTY(QVariant to READ to WRITE setTo NOTIFY toChanged) + Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged) + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged) + Q_PROPERTY(QString properties READ properties WRITE setProperties NOTIFY propertiesChanged) + Q_PROPERTY(QDeclarativeListProperty<QObject> targets READ targets) + Q_PROPERTY(QDeclarativeListProperty<QObject> exclude READ exclude) + +public: + QDeclarativePropertyAnimation(QObject *parent=0); + virtual ~QDeclarativePropertyAnimation(); + + virtual int duration() const; + virtual void setDuration(int); + + QVariant from() const; + void setFrom(const QVariant &); + + QVariant to() const; + void setTo(const QVariant &); + + QEasingCurve easing() const; + void setEasing(const QEasingCurve &); + + QObject *target() const; + void setTarget(QObject *); + + QString property() const; + void setProperty(const QString &); + + QString properties() const; + void setProperties(const QString &); + + QDeclarativeListProperty<QObject> targets(); + QDeclarativeListProperty<QObject> exclude(); + +protected: + QDeclarativePropertyAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent); + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + +Q_SIGNALS: + void durationChanged(int); + void fromChanged(QVariant); + void toChanged(QVariant); + void easingChanged(const QEasingCurve &); + void propertiesChanged(const QString &); + void targetChanged(); + void propertyChanged(); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeColorAnimation : public QDeclarativePropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) + Q_PROPERTY(QColor from READ from WRITE setFrom) + Q_PROPERTY(QColor to READ to WRITE setTo) + +public: + QDeclarativeColorAnimation(QObject *parent=0); + virtual ~QDeclarativeColorAnimation(); + + QColor from() const; + void setFrom(const QColor &); + + QColor to() const; + void setTo(const QColor &); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeNumberAnimation : public QDeclarativePropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) + + Q_PROPERTY(qreal from READ from WRITE setFrom) + Q_PROPERTY(qreal to READ to WRITE setTo) + +public: + QDeclarativeNumberAnimation(QObject *parent=0); + virtual ~QDeclarativeNumberAnimation(); + + qreal from() const; + void setFrom(qreal); + + qreal to() const; + void setTo(qreal); + +protected: + QDeclarativeNumberAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent); + +private: + void init(); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeVector3dAnimation : public QDeclarativePropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) + + Q_PROPERTY(QVector3D from READ from WRITE setFrom) + Q_PROPERTY(QVector3D to READ to WRITE setTo) + +public: + QDeclarativeVector3dAnimation(QObject *parent=0); + virtual ~QDeclarativeVector3dAnimation(); + + QVector3D from() const; + void setFrom(QVector3D); + + QVector3D to() const; + void setTo(QVector3D); +}; + +class QDeclarativeRotationAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeRotationAnimation : public QDeclarativePropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeRotationAnimation) + Q_ENUMS(RotationDirection) + + Q_PROPERTY(qreal from READ from WRITE setFrom) + Q_PROPERTY(qreal to READ to WRITE setTo) + Q_PROPERTY(RotationDirection direction READ direction WRITE setDirection NOTIFY directionChanged) + +public: + QDeclarativeRotationAnimation(QObject *parent=0); + virtual ~QDeclarativeRotationAnimation(); + + qreal from() const; + void setFrom(qreal); + + qreal to() const; + void setTo(qreal); + + enum RotationDirection { Numerical, Shortest, Clockwise, Counterclockwise }; + RotationDirection direction() const; + void setDirection(RotationDirection direction); + +Q_SIGNALS: + void directionChanged(); +}; + +class QDeclarativeAnimationGroupPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeAnimationGroup : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnimationGroup) + + Q_CLASSINFO("DefaultProperty", "animations") + Q_PROPERTY(QDeclarativeListProperty<QDeclarativeAbstractAnimation> animations READ animations) + +public: + QDeclarativeAnimationGroup(QObject *parent); + virtual ~QDeclarativeAnimationGroup(); + + QDeclarativeListProperty<QDeclarativeAbstractAnimation> animations(); + friend class QDeclarativeAbstractAnimation; + +protected: + QDeclarativeAnimationGroup(QDeclarativeAnimationGroupPrivate &dd, QObject *parent); +}; + +class QDeclarativeSequentialAnimation : public QDeclarativeAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnimationGroup) + +public: + QDeclarativeSequentialAnimation(QObject *parent=0); + virtual ~QDeclarativeSequentialAnimation(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarativeParallelAnimation : public QDeclarativeAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnimationGroup) + +public: + QDeclarativeParallelAnimation(QObject *parent=0); + virtual ~QDeclarativeParallelAnimation(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeAbstractAnimation) +QML_DECLARE_TYPE(QDeclarativePauseAnimation) +QML_DECLARE_TYPE(QDeclarativeScriptAction) +QML_DECLARE_TYPE(QDeclarativePropertyAction) +QML_DECLARE_TYPE(QDeclarativePropertyAnimation) +QML_DECLARE_TYPE(QDeclarativeColorAnimation) +QML_DECLARE_TYPE(QDeclarativeNumberAnimation) +QML_DECLARE_TYPE(QDeclarativeSequentialAnimation) +QML_DECLARE_TYPE(QDeclarativeParallelAnimation) +QML_DECLARE_TYPE(QDeclarativeVector3dAnimation) +QML_DECLARE_TYPE(QDeclarativeRotationAnimation) + +QT_END_HEADER + +#endif // QDECLARATIVEANIMATION_H diff --git a/src/quick/util/qdeclarativeanimation_p_p.h b/src/quick/util/qdeclarativeanimation_p_p.h new file mode 100644 index 0000000000..dfeb767877 --- /dev/null +++ b/src/quick/util/qdeclarativeanimation_p_p.h @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANIMATION_P_H +#define QDECLARATIVEANIMATION_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 "qdeclarativeanimation_p.h" + +#include <private/qdeclarativenullablevalue_p_p.h> + +#include <qdeclarative.h> +#include <qdeclarativecontext.h> + +#include <QtCore/QPauseAnimation> +#include <QtCore/QVariantAnimation> +#include <QtCore/QAnimationGroup> +#include <QDebug> + +#include <private/qobject_p.h> +#include <private/qvariantanimation_p.h> + +QT_BEGIN_NAMESPACE + +//interface for classes that provide animation actions for QActionAnimation +class QAbstractAnimationAction +{ +public: + virtual ~QAbstractAnimationAction() {} + virtual void doAction() = 0; +}; + +//templated animation action +//allows us to specify an action that calls a function of a class. +//(so that class doesn't have to inherit QDeclarativeAbstractAnimationAction) +template<class T, void (T::*method)()> +class QAnimationActionProxy : public QAbstractAnimationAction +{ +public: + QAnimationActionProxy(T *p) : m_p(p) {} + virtual void doAction() { (m_p->*method)(); } + +private: + T *m_p; +}; + +//performs an action of type QAbstractAnimationAction +class Q_AUTOTEST_EXPORT QActionAnimation : public QAbstractAnimation +{ + Q_OBJECT +public: + QActionAnimation(QObject *parent = 0) : QAbstractAnimation(parent), animAction(0), policy(KeepWhenStopped) {} + QActionAnimation(QAbstractAnimationAction *action, QObject *parent = 0) + : QAbstractAnimation(parent), animAction(action), policy(KeepWhenStopped) {} + ~QActionAnimation() { if (policy == DeleteWhenStopped) { delete animAction; animAction = 0; } } + virtual int duration() const { return 0; } + void setAnimAction(QAbstractAnimationAction *action, DeletionPolicy p) + { + if (state() == Running) + stop(); + if (policy == DeleteWhenStopped) + delete animAction; + animAction = action; + policy = p; + } +protected: + virtual void updateCurrentTime(int) {} + + virtual void updateState(State newState, State /*oldState*/) + { + if (newState == Running) { + if (animAction) { + animAction->doAction(); + if (state() == Stopped && policy == DeleteWhenStopped) { + delete animAction; + animAction = 0; + } + } + } + } + +private: + QAbstractAnimationAction *animAction; + DeletionPolicy policy; +}; + +class QDeclarativeBulkValueUpdater +{ +public: + virtual ~QDeclarativeBulkValueUpdater() {} + virtual void setValue(qreal value) = 0; +}; + +//animates QDeclarativeBulkValueUpdater (assumes start and end values will be reals or compatible) +class Q_AUTOTEST_EXPORT QDeclarativeBulkValueAnimator : public QVariantAnimation +{ + Q_OBJECT +public: + QDeclarativeBulkValueAnimator(QObject *parent = 0) : QVariantAnimation(parent), animValue(0), fromSourced(0), policy(KeepWhenStopped) {} + ~QDeclarativeBulkValueAnimator() { if (policy == DeleteWhenStopped) { delete animValue; animValue = 0; } } + void setAnimValue(QDeclarativeBulkValueUpdater *value, DeletionPolicy p) + { + if (state() == Running) + stop(); + if (policy == DeleteWhenStopped) + delete animValue; + animValue = value; + policy = p; + } + QDeclarativeBulkValueUpdater *getAnimValue() const + { + return animValue; + } + void setFromSourcedValue(bool *value) + { + fromSourced = value; + } +protected: + virtual void updateCurrentValue(const QVariant &value) + { + if (state() == QAbstractAnimation::Stopped) + return; + + if (animValue) + animValue->setValue(value.toReal()); + } + virtual void updateState(State newState, State oldState) + { + QVariantAnimation::updateState(newState, oldState); + if (newState == Running) { + //check for new from every loop + if (fromSourced) + *fromSourced = false; + } + } + +private: + QDeclarativeBulkValueUpdater *animValue; + bool *fromSourced; + DeletionPolicy policy; +}; + +//an animation that just gives a tick +template<class T, void (T::*method)(int)> +class QTickAnimationProxy : public QAbstractAnimation +{ + //Q_OBJECT //doesn't work with templating +public: + QTickAnimationProxy(T *p, QObject *parent = 0) : QAbstractAnimation(parent), m_p(p) {} + virtual int duration() const { return -1; } +protected: + virtual void updateCurrentTime(int msec) { (m_p->*method)(msec); } + +private: + T *m_p; +}; + +class QDeclarativeAbstractAnimationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeAbstractAnimation) +public: + QDeclarativeAbstractAnimationPrivate() + : running(false), paused(false), alwaysRunToEnd(false), + connectedTimeLine(false), componentComplete(true), + avoidPropertyValueSourceStart(false), disableUserControl(false), + registered(false), loopCount(1), group(0) {} + + bool running:1; + bool paused:1; + bool alwaysRunToEnd:1; + bool connectedTimeLine:1; + bool componentComplete:1; + bool avoidPropertyValueSourceStart:1; + bool disableUserControl:1; + bool registered:1; + + int loopCount; + + void commence(); + + QDeclarativeProperty defaultProperty; + + QDeclarativeAnimationGroup *group; + + static QDeclarativeProperty createProperty(QObject *obj, const QString &str, QObject *infoObj); +}; + +class QDeclarativePauseAnimationPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePauseAnimation) +public: + QDeclarativePauseAnimationPrivate() + : QDeclarativeAbstractAnimationPrivate(), pa(0) {} + + void init(); + + QPauseAnimation *pa; +}; + +class QDeclarativeScriptActionPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeScriptAction) +public: + QDeclarativeScriptActionPrivate(); + + void init(); + + QDeclarativeScriptString script; + QString name; + QDeclarativeScriptString runScriptScript; + bool hasRunScriptScript; + bool reversing; + + void execute(); + + QAnimationActionProxy<QDeclarativeScriptActionPrivate, + &QDeclarativeScriptActionPrivate::execute> proxy; + QActionAnimation *rsa; +}; + +class QDeclarativePropertyActionPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePropertyAction) +public: + QDeclarativePropertyActionPrivate() + : QDeclarativeAbstractAnimationPrivate(), target(0), spa(0) {} + + void init(); + + QObject *target; + QString propertyName; + QString properties; + QList<QObject *> targets; + QList<QObject *> exclude; + + QDeclarativeNullableValue<QVariant> value; + + QActionAnimation *spa; +}; + +class QDeclarativeAnimationGroupPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeAnimationGroup) +public: + QDeclarativeAnimationGroupPrivate() + : QDeclarativeAbstractAnimationPrivate(), ag(0) {} + + static void append_animation(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list, QDeclarativeAbstractAnimation *role); + static void clear_animation(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list); + QList<QDeclarativeAbstractAnimation *> animations; + QAnimationGroup *ag; +}; + +class QDeclarativePropertyAnimationPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePropertyAnimation) +public: + QDeclarativePropertyAnimationPrivate() + : QDeclarativeAbstractAnimationPrivate(), target(0), fromSourced(false), fromIsDefined(false), toIsDefined(false), + rangeIsSet(false), defaultToInterpolatorType(0), interpolatorType(0), interpolator(0), va(0), actions(0) {} + + void init(); + + QVariant from; + QVariant to; + + QObject *target; + QString propertyName; + QString properties; + QList<QObject *> targets; + QList<QObject *> exclude; + QString defaultProperties; + + bool fromSourced; + bool fromIsDefined:1; + bool toIsDefined:1; + bool rangeIsSet:1; + bool defaultToInterpolatorType:1; + int interpolatorType; + QVariantAnimation::Interpolator interpolator; + + QDeclarativeBulkValueAnimator *va; + + // for animations that don't use the QDeclarativeBulkValueAnimator + QDeclarativeStateActions *actions; + + static QVariant interpolateVariant(const QVariant &from, const QVariant &to, qreal progress); + static void convertVariant(QVariant &variant, int type); +}; + +class QDeclarativeRotationAnimationPrivate : public QDeclarativePropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeRotationAnimation) +public: + QDeclarativeRotationAnimationPrivate() : direction(QDeclarativeRotationAnimation::Numerical) {} + + QDeclarativeRotationAnimation::RotationDirection direction; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeAnimationPropertyUpdater : public QDeclarativeBulkValueUpdater +{ +public: + QDeclarativeStateActions actions; + int interpolatorType; //for Number/ColorAnimation + int prevInterpolatorType; //for generic + QVariantAnimation::Interpolator interpolator; + bool reverse; + bool fromSourced; + bool fromDefined; + bool *wasDeleted; + QDeclarativeAnimationPropertyUpdater() : prevInterpolatorType(0), wasDeleted(0) {} + ~QDeclarativeAnimationPropertyUpdater() { if (wasDeleted) *wasDeleted = true; } + void setValue(qreal v); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEANIMATION_P_H diff --git a/src/quick/util/qdeclarativebehavior.cpp b/src/quick/util/qdeclarativebehavior.cpp new file mode 100644 index 0000000000..6fb36197ef --- /dev/null +++ b/src/quick/util/qdeclarativebehavior.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativebehavior_p.h" + +#include "qdeclarativeanimation_p.h" +#include <qdeclarativecontext.h> +#include <qdeclarativeinfo.h> +#include <private/qdeclarativeproperty_p.h> +#include <private/qdeclarativeguard_p.h> +#include <private/qdeclarativeengine_p.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativeBehaviorPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeBehavior) +public: + QDeclarativeBehaviorPrivate() : animation(0), enabled(true), finalized(false) + , blockRunningChanged(false) {} + + QDeclarativeProperty property; + QVariant targetValue; + QDeclarativeGuard<QDeclarativeAbstractAnimation> animation; + bool enabled; + bool finalized; + bool blockRunningChanged; +}; + +/*! + \qmlclass Behavior QDeclarativeBehavior + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \brief The Behavior element allows you to specify a default animation for a property change. + + A Behavior defines the default animation to be applied whenever a + particular property value changes. + + For example, the following Behavior defines a NumberAnimation to be run + whenever the \l Rectangle's \c width value changes. When the MouseArea + is clicked, the \c width is changed, triggering the behavior's animation: + + \snippet doc/src/snippets/declarative/behavior.qml 0 + + Note that a property cannot have more than one assigned Behavior. To provide + multiple animations within a Behavior, use ParallelAnimation or + SequentialAnimation. + + If a \l{QML States}{state change} has a \l Transition that matches the same property as a + Behavior, the \l Transition animation overrides the Behavior for that + state change. For general advice on using Behaviors to animate state changes, see + \l{Using QML Behaviors with States}. + + \sa {QML Animation and Transitions}, {declarative/animation/behaviors}{Behavior example}, QtDeclarative +*/ + + +QDeclarativeBehavior::QDeclarativeBehavior(QObject *parent) + : QObject(*(new QDeclarativeBehaviorPrivate), parent) +{ +} + +QDeclarativeBehavior::~QDeclarativeBehavior() +{ +} + +/*! + \qmlproperty Animation QtQuick2::Behavior::animation + \default + + This property holds the animation to run when the behavior is triggered. +*/ + +QDeclarativeAbstractAnimation *QDeclarativeBehavior::animation() +{ + Q_D(QDeclarativeBehavior); + return d->animation; +} + +void QDeclarativeBehavior::setAnimation(QDeclarativeAbstractAnimation *animation) +{ + Q_D(QDeclarativeBehavior); + if (d->animation) { + qmlInfo(this) << tr("Cannot change the animation assigned to a Behavior."); + return; + } + + d->animation = animation; + if (d->animation) { + d->animation->setDefaultTarget(d->property); + d->animation->setDisableUserControl(); + FAST_CONNECT(d->animation->qtAnimation(), + SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)), + this, + SLOT(qtAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State))) + } +} + + +void QDeclarativeBehavior::qtAnimationStateChanged(QAbstractAnimation::State newState,QAbstractAnimation::State) +{ + Q_D(QDeclarativeBehavior); + if (!d->blockRunningChanged) + d->animation->notifyRunningChanged(newState == QAbstractAnimation::Running); +} + + +/*! + \qmlproperty bool QtQuick2::Behavior::enabled + + This property holds whether the behavior will be triggered when the tracked + property changes value. + + By default a Behavior is enabled. +*/ + +bool QDeclarativeBehavior::enabled() const +{ + Q_D(const QDeclarativeBehavior); + return d->enabled; +} + +void QDeclarativeBehavior::setEnabled(bool enabled) +{ + Q_D(QDeclarativeBehavior); + if (d->enabled == enabled) + return; + d->enabled = enabled; + emit enabledChanged(); +} + +void QDeclarativeBehavior::write(const QVariant &value) +{ + Q_D(QDeclarativeBehavior); + bool bypass = !d->enabled || !d->finalized; + if (!bypass) + qmlExecuteDeferred(this); + if (!d->animation || bypass) { + QDeclarativePropertyPrivate::write(d->property, value, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + d->targetValue = value; + return; + } + + if (d->animation->isRunning() && value == d->targetValue) + return; + + const QVariant ¤tValue = d->property.read(); + d->targetValue = value; + + if (d->animation->qtAnimation()->duration() != -1 + && d->animation->qtAnimation()->state() != QAbstractAnimation::Stopped) { + d->blockRunningChanged = true; + d->animation->qtAnimation()->stop(); + } + + QDeclarativeStateOperation::ActionList actions; + QDeclarativeAction action; + action.property = d->property; + action.fromValue = currentValue; + action.toValue = value; + actions << action; + + QList<QDeclarativeProperty> after; + d->animation->transition(actions, after, QDeclarativeAbstractAnimation::Forward); + d->animation->qtAnimation()->start(); + d->blockRunningChanged = false; + if (!after.contains(d->property)) + QDeclarativePropertyPrivate::write(d->property, value, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); +} + +void QDeclarativeBehavior::setTarget(const QDeclarativeProperty &property) +{ + Q_D(QDeclarativeBehavior); + d->property = property; + if (d->animation) + d->animation->setDefaultTarget(property); + + QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this)); + engPriv->registerFinalizeCallback(this, this->metaObject()->indexOfSlot("componentFinalized()")); +} + +void QDeclarativeBehavior::componentFinalized() +{ + Q_D(QDeclarativeBehavior); + d->finalized = true; +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativebehavior_p.h b/src/quick/util/qdeclarativebehavior_p.h new file mode 100644 index 0000000000..1f8873de22 --- /dev/null +++ b/src/quick/util/qdeclarativebehavior_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBEHAVIOR_H +#define QDECLARATIVEBEHAVIOR_H + +#include <private/qtquickglobal_p.h> + +#include <private/qdeclarativepropertyvalueinterceptor_p.h> +#include <qdeclarative.h> +#include <QtCore/QAbstractAnimation> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeAbstractAnimation; +class QDeclarativeBehaviorPrivate; +class Q_QUICK_PRIVATE_EXPORT QDeclarativeBehavior : public QObject, public QDeclarativePropertyValueInterceptor +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeBehavior) + + Q_INTERFACES(QDeclarativePropertyValueInterceptor) + Q_CLASSINFO("DefaultProperty", "animation") + Q_PROPERTY(QDeclarativeAbstractAnimation *animation READ animation WRITE setAnimation) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_CLASSINFO("DeferredPropertyNames", "animation") + +public: + QDeclarativeBehavior(QObject *parent=0); + ~QDeclarativeBehavior(); + + virtual void setTarget(const QDeclarativeProperty &); + virtual void write(const QVariant &value); + + QDeclarativeAbstractAnimation *animation(); + void setAnimation(QDeclarativeAbstractAnimation *); + + bool enabled() const; + void setEnabled(bool enabled); + +Q_SIGNALS: + void enabledChanged(); + +private Q_SLOTS: + void componentFinalized(); + void qtAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeBehavior) + +QT_END_HEADER + +#endif // QDECLARATIVEBEHAVIOR_H diff --git a/src/quick/util/qdeclarativebind.cpp b/src/quick/util/qdeclarativebind.cpp new file mode 100644 index 0000000000..20b2caa582 --- /dev/null +++ b/src/quick/util/qdeclarativebind.cpp @@ -0,0 +1,309 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativebind_p.h" + +#include <private/qdeclarativenullablevalue_p_p.h> +#include <private/qdeclarativeproperty_p.h> +#include <private/qdeclarativebinding_p.h> +#include <private/qdeclarativeguard_p.h> + +#include <qdeclarativeengine.h> +#include <qdeclarativecontext.h> +#include <qdeclarativeproperty.h> +#include <qdeclarativeinfo.h> + +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativeBindPrivate : public QObjectPrivate +{ +public: + QDeclarativeBindPrivate() : componentComplete(true), obj(0), prevBind(0) {} + ~QDeclarativeBindPrivate() { if (prevBind) prevBind->destroy(); } + + QDeclarativeNullableValue<bool> when; + bool componentComplete; + QDeclarativeGuard<QObject> obj; + QString propName; + QDeclarativeNullableValue<QVariant> value; + QDeclarativeProperty prop; + QDeclarativeAbstractBinding *prevBind; +}; + + +/*! + \qmlclass Binding QDeclarativeBind + \inqmlmodule QtQuick 2 + \ingroup qml-working-with-data + \brief The Binding element allows arbitrary property bindings to be created. + + \section1 Binding to an inaccessible property + + Sometimes it is necessary to bind to a property of an object that wasn't + directly instantiated by QML - generally a property of a class exported + to QML by C++. In these cases, regular property binding doesn't work. Binding + allows you to bind any value to any property. + + For example, imagine a C++ application that maps an "app.enteredText" + property into QML. You could use Binding to update the enteredText property + like this. + \code + TextEdit { id: myTextField; text: "Please type here..." } + Binding { target: app; property: "enteredText"; value: myTextField.text } + \endcode + Whenever the text in the TextEdit is updated, the C++ property will be + updated also. + + \section1 "Single-branch" conditional binding + + In some circumstances you may want to control the value of a property + only when a certain condition is true (and relinquish control in all + other cirumstances). This often isn't possible to accomplish with a direct + binding, as you need to supply values for all possible branches. + + \qml + // warning: "Unable to assign [undefined] to double value" + value: if (mouse.pressed) mouse.mouseX + \endqml + + The above example will produce a warning whenever we release the mouse, as the value + of the binding is undefined when the mouse isn't pressed. We can use the Binding + element to rewrite the above code and avoid the warning. + + \qml + Binding on value { + when: mouse.pressed + value: mouse.mouseX + } + \endqml + + The Binding element will also restore any previously set direct bindings on + the property. In that sense, it functions much like a simplified State. + + \qml + // this is equivilant to the above Binding + State { + name: "pressed" + when: mouse.pressed + PropertyChanges { + target: obj + value: mouse.mouseX + } + } + \endqml + + If the binding target or binding property is changed, the bound value is + immediately pushed onto the new target. + + \sa QtDeclarative +*/ +QDeclarativeBind::QDeclarativeBind(QObject *parent) + : QObject(*(new QDeclarativeBindPrivate), parent) +{ +} + +QDeclarativeBind::~QDeclarativeBind() +{ +} + +/*! + \qmlproperty bool QtQuick2::Binding::when + + This property holds when the binding is active. + This should be set to an expression that evaluates to true when you want the binding to be active. + + \code + Binding { + target: contactName; property: 'text' + value: name; when: list.ListView.isCurrentItem + } + \endcode + + When the binding becomes inactive again, any direct bindings that were previously + set on the property will be restored. +*/ +bool QDeclarativeBind::when() const +{ + Q_D(const QDeclarativeBind); + return d->when; +} + +void QDeclarativeBind::setWhen(bool v) +{ + Q_D(QDeclarativeBind); + if (!d->when.isNull && d->when == v) + return; + + d->when = v; + eval(); +} + +/*! + \qmlproperty Object QtQuick2::Binding::target + + The object to be updated. +*/ +QObject *QDeclarativeBind::object() +{ + Q_D(const QDeclarativeBind); + return d->obj; +} + +void QDeclarativeBind::setObject(QObject *obj) +{ + Q_D(QDeclarativeBind); + if (d->obj && d->when.isValid() && d->when) { + /* if we switch the object at runtime, we need to restore the + previous binding on the old object before continuing */ + d->when = false; + eval(); + d->when = true; + } + d->obj = obj; + if (d->componentComplete) + d->prop = QDeclarativeProperty(d->obj, d->propName); + eval(); +} + +/*! + \qmlproperty string QtQuick2::Binding::property + + The property to be updated. +*/ +QString QDeclarativeBind::property() const +{ + Q_D(const QDeclarativeBind); + return d->propName; +} + +void QDeclarativeBind::setProperty(const QString &p) +{ + Q_D(QDeclarativeBind); + if (!d->propName.isEmpty() && d->when.isValid() && d->when) { + /* if we switch the property name at runtime, we need to restore the + previous binding on the old object before continuing */ + d->when = false; + eval(); + d->when = true; + } + d->propName = p; + if (d->componentComplete) + d->prop = QDeclarativeProperty(d->obj, d->propName); + eval(); +} + +/*! + \qmlproperty any QtQuick2::Binding::value + + The value to be set on the target object and property. This can be a + constant (which isn't very useful), or a bound expression. +*/ +QVariant QDeclarativeBind::value() const +{ + Q_D(const QDeclarativeBind); + return d->value.value; +} + +void QDeclarativeBind::setValue(const QVariant &v) +{ + Q_D(QDeclarativeBind); + d->value = v; + eval(); +} + +void QDeclarativeBind::setTarget(const QDeclarativeProperty &p) +{ + Q_D(QDeclarativeBind); + d->prop = p; +} + +void QDeclarativeBind::classBegin() +{ + Q_D(QDeclarativeBind); + d->componentComplete = false; +} + +void QDeclarativeBind::componentComplete() +{ + Q_D(QDeclarativeBind); + d->componentComplete = true; + if (!d->prop.isValid()) + d->prop = QDeclarativeProperty(d->obj, d->propName); + eval(); +} + +void QDeclarativeBind::eval() +{ + Q_D(QDeclarativeBind); + if (!d->prop.isValid() || d->value.isNull || !d->componentComplete) + return; + + if (d->when.isValid()) { + if (!d->when) { + //restore any previous binding + if (d->prevBind) { + QDeclarativeAbstractBinding *tmp = d->prevBind; + d->prevBind = 0; + tmp = QDeclarativePropertyPrivate::setBinding(d->prop, tmp); + if (tmp) //should this ever be true? + tmp->destroy(); + } + return; + } + + //save any set binding for restoration + QDeclarativeAbstractBinding *tmp; + tmp = QDeclarativePropertyPrivate::setBinding(d->prop, 0); + if (tmp && d->prevBind) + d->prevBind->destroy(); + else if (!d->prevBind) + d->prevBind = tmp; + } + + d->prop.write(d->value.value); +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativebind_p.h b/src/quick/util/qdeclarativebind_p.h new file mode 100644 index 0000000000..dfb6103a37 --- /dev/null +++ b/src/quick/util/qdeclarativebind_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBIND_H +#define QDECLARATIVEBIND_H + +#include <qdeclarative.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeBindPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeBind : public QObject, public QDeclarativePropertyValueSource, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeBind) + Q_INTERFACES(QDeclarativeParserStatus) + Q_INTERFACES(QDeclarativePropertyValueSource) + Q_PROPERTY(QObject *target READ object WRITE setObject) + Q_PROPERTY(QString property READ property WRITE setProperty) + Q_PROPERTY(QVariant value READ value WRITE setValue) + Q_PROPERTY(bool when READ when WRITE setWhen) + +public: + QDeclarativeBind(QObject *parent=0); + ~QDeclarativeBind(); + + bool when() const; + void setWhen(bool); + + QObject *object(); + void setObject(QObject *); + + QString property() const; + void setProperty(const QString &); + + QVariant value() const; + void setValue(const QVariant &); + +protected: + virtual void setTarget(const QDeclarativeProperty &); + virtual void classBegin(); + virtual void componentComplete(); + +private: + void eval(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeBind) + +QT_END_HEADER + +#endif diff --git a/src/quick/util/qdeclarativechangeset.cpp b/src/quick/util/qdeclarativechangeset.cpp new file mode 100644 index 0000000000..81cbe3e10a --- /dev/null +++ b/src/quick/util/qdeclarativechangeset.cpp @@ -0,0 +1,479 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativechangeset_p.h" + +QT_BEGIN_NAMESPACE + +QDeclarativeChangeSet::QDeclarativeChangeSet() + : m_moveCounter(0) + , m_difference(0) +{ +} + +QDeclarativeChangeSet::QDeclarativeChangeSet(const QDeclarativeChangeSet &changeSet) + : m_removes(changeSet.m_removes) + , m_inserts(changeSet.m_inserts) + , m_changes(changeSet.m_changes) + , m_moveCounter(changeSet.m_moveCounter) + , m_difference(0) +{ +} + +QDeclarativeChangeSet::~QDeclarativeChangeSet() +{ +} + +QDeclarativeChangeSet &QDeclarativeChangeSet::operator =(const QDeclarativeChangeSet &changeSet) +{ + m_removes = changeSet.m_removes; + m_inserts = changeSet.m_inserts; + m_changes = changeSet.m_changes; + m_moveCounter = changeSet.m_moveCounter; + m_difference = changeSet.m_difference; + return *this; +} + +void QDeclarativeChangeSet::insert(int index, int count) +{ + applyInsertions(QVector<Insert>() << Insert(index, count)); +} + +void QDeclarativeChangeSet::remove(int index, int count) +{ + QVector<Insert> i; + applyRemovals(QVector<Remove>() << Remove(index, count), i); +} + +void QDeclarativeChangeSet::move(int from, int to, int count) +{ + apply(QVector<Remove>() << Remove(from, count, -2), QVector<Insert>() << Insert(to, count, -2)); +} + +void QDeclarativeChangeSet::change(int index, int count) +{ + applyChanges(QVector<Change>() << Change(index, count)); +} + +void QDeclarativeChangeSet::apply(const QDeclarativeChangeSet &changeSet) +{ + apply(changeSet.m_removes, changeSet.m_inserts, changeSet.m_changes); +} + +void QDeclarativeChangeSet::apply(const QVector<Remove> &removals) +{ + QVector<Remove> r = removals; + QVector<Insert> i; + applyRemovals(r, i); +} + +void QDeclarativeChangeSet::apply(const QVector<Insert> &insertions) +{ + QVector<Insert> i = insertions; + applyInsertions(i); +} + +void QDeclarativeChangeSet::apply(const QVector<Change> &changes) +{ + QVector<Change> c = changes; + applyChanges(c); +} + +void QDeclarativeChangeSet::apply(const QVector<Remove> &removals, const QVector<Insert> &insertions, const QVector<Change> &changes) +{ + QVector<Remove> r = removals; + QVector<Insert> i = insertions; + QVector<Change> c = changes; + applyRemovals(r, i); + applyInsertions(i); + applyChanges(c); +} + +void QDeclarativeChangeSet::applyRemovals(QVector<Remove> &removals, QVector<Insert> &insertions) +{ + int removeCount = 0; + int insertCount = 0; + QVector<Insert>::iterator insert = m_inserts.begin(); + QVector<Change>::iterator change = m_changes.begin(); + QVector<Remove>::iterator rit = removals.begin(); + for (; rit != removals.end(); ++rit) { + int index = rit->index + removeCount; + int count = rit->count; + + QVector<Insert>::iterator iit = insertions.begin(); + for (; rit->moveId != -1 && iit != insertions.end() && iit->moveId != rit->moveId; ++iit) {} + + for (QVector<Remove>::iterator nrit = rit + 1; nrit != removals.end(); nrit = rit + 1) { + if (nrit->index != rit->index || (rit->moveId == -1) != (nrit->moveId == -1)) + break; + if (nrit->moveId != -1) { + QVector<Insert>::iterator niit = iit + 1; + if (niit->moveId != nrit->moveId || niit->index != iit->index + iit->count) + break; + niit->index = iit->index; + niit->count += iit->count; + iit = insertions.erase(iit); + } + nrit->count += rit->count; + rit = removals.erase(rit); + } + + for (; change != m_changes.end() && change->end() < rit->index; ++change) {} + for (; change != m_changes.end() && change->index > rit->end(); ++change) { + change->count -= qMin(change->end(), rit->end()) - qMax(change->index, rit->index); + if (change->count == 0) { + change = m_changes.erase(change); + } else if (rit->index < change->index) { + change->index = rit->index; + } + } + for (; insert != m_inserts.end() && insert->end() <= index; ++insert) { + insertCount += insert->count; + insert->index -= removeCount; + } + for (; insert != m_inserts.end() && insert->index < index + count; ++insert) { + const int offset = insert->index - index; + const int difference = qMin(insert->end(), index + count) - qMax(insert->index, index); + const int moveId = rit->moveId != -1 ? m_moveCounter++ : -1; + if (insert->moveId != -1) { + QVector<Remove>::iterator remove = m_removes.begin(); + for (; remove != m_removes.end() && remove->moveId != insert->moveId; ++remove) {} + Q_ASSERT(remove != m_removes.end()); + const int offset = index - insert->index; + if (rit->moveId != -1 && offset < 0) { + const int moveId = m_moveCounter++; + iit = insertions.insert(iit, Insert(iit->index, -offset, moveId)); + ++iit; + iit->index += -offset; + iit->count -= -offset; + rit = removals.insert(rit, Remove(rit->index, -offset, moveId)); + ++rit; + rit->count -= -offset; + } + + if (offset > 0) { + const int moveId = m_moveCounter++; + insert = m_inserts.insert(insert, Insert(insert->index, offset, moveId)); + ++insert; + insert->index += offset; + insert->count -= offset; + remove = m_removes.insert(remove, Remove(remove->index, offset, moveId)); + ++remove; + remove->count -= offset; + rit->index -= offset; + index += offset; + count -= offset; + } + + if (remove->count == difference) { + remove->moveId = moveId; + } else { + remove = m_removes.insert(remove, Remove(remove->index, difference, moveId)); + ++remove; + remove->count -= difference; + } + } else if (rit->moveId != -1 && offset > 0) { + const int moveId = m_moveCounter++; + iit = insertions.insert(iit, Insert(iit->index, offset, moveId)); + ++iit; + iit->index += offset; + iit->count -= offset; + rit = removals.insert(rit, Remove(rit->index, offset, moveId)); + ++rit; + rit->count -= offset; + index += offset; + count -= offset; + } + + if (rit->moveId != -1 && difference > 0) { + iit = insertions.insert(iit, Insert( + iit->index, difference, insert->moveId != -1 ? moveId : -1)); + ++iit; + iit->index += difference; + iit->count -= difference; + } + + insert->count -= difference; + rit->count -= difference; + if (insert->count == 0) { + insert = m_inserts.erase(insert); + --insert; + } else if (index <= insert->index) { + insert->index = rit->index; + } else { + rit->index -= insert->count; + } + index += difference; + count -= difference; + removeCount += difference; + } + rit->index -= insertCount; + removeCount += rit->count; + + if (rit->count == 0) { + if (rit->moveId != -1 && iit->count == 0) + insertions.erase(iit); + rit = removals.erase(rit); + --rit; + } else if (rit->moveId != -1) { + const int moveId = m_moveCounter++; + rit->moveId = moveId; + iit->moveId = moveId; + } + } + for (; change != m_changes.end(); ++change) + change->index -= removeCount; + for (; insert != m_inserts.end(); ++insert) + insert->index -= removeCount; + + removeCount = 0; + QVector<Remove>::iterator remove = m_removes.begin(); + for (rit = removals.begin(); rit != removals.end(); ++rit) { + QVector<Insert>::iterator iit = insertions.begin(); + int index = rit->index + removeCount; + for (; rit->moveId != -1 && iit != insertions.end() && iit->moveId != rit->moveId; ++iit) {} + for (; remove != m_removes.end() && index > remove->index; ++remove) + remove->index -= removeCount; + while (remove != m_removes.end() && index + rit->count > remove->index) { + int count = 0; + const int offset = remove->index - index - removeCount; + QVector<Remove>::iterator rend = remove; + for (; rend != m_removes.end() + && rit->moveId == -1 + && rend->moveId == -1 + && rit->index + rit->count >= rend->index; ++rend) { + count += rend->count; + } + if (remove != rend) { + const int difference = rend == m_removes.end() || rit->index + rit->count < rend->index - removeCount + ? rit->count + : offset; + count += difference; + + index += difference; + rit->count -= difference; + removeCount += difference; + remove->index = rit->index; + remove->count = count; + remove = m_removes.erase(++remove, rend); + } else if (rit->moveId != -1) { + if (offset > 0) { + const int moveId = m_moveCounter++; + iit = insertions.insert(iit, Insert(iit->index, offset, moveId)); + ++iit; + iit->index += offset; + iit->count -= offset; + remove = m_removes.insert(remove, Remove(rit->index, offset, moveId)); + ++remove; + rit->count -= offset; + removeCount += offset; + } + remove->index = rit->index; + index += offset; + + ++remove; + } else { + if (offset > 0) { + remove = m_removes.insert(remove, Remove(rit->index, offset)); + ++remove; + rit->count -= offset; + removeCount += offset; + } + remove->index = rit->index; + index += offset; + + ++remove; + } + index += count; + } + + if (rit->count > 0) { + remove = m_removes.insert(remove, *rit); + ++remove; + } + removeCount += rit->count; + } + for (; remove != m_removes.end(); ++remove) + remove->index -= removeCount; + m_difference -= removeCount; +} + +void QDeclarativeChangeSet::applyInsertions(QVector<Insert> &insertions) +{ + int insertCount = 0; + QVector<Insert>::iterator insert = m_inserts.begin(); + QVector<Change>::iterator change = m_changes.begin(); + for (QVector<Insert>::iterator iit = insertions.begin(); iit != insertions.end(); ++iit) { + int index = iit->index - insertCount; + int count = iit->count; + for (; change != m_changes.end() && change->index >= index; ++change) + change->index += insertCount; + if (change != m_changes.end() && change->index < index + count) { + int offset = index - change->index; + change = m_changes.insert(change, Change(change->index + insertCount, offset)); + ++change; + change->index += count + offset; + change->count -= offset; + } + for (; insert != m_inserts.end() && iit->index > insert->index + insert->count; ++insert) + insert->index += insertCount; + if (insert == m_inserts.end()) { + insert = m_inserts.insert(insert, *iit); + ++insert; + insertCount += iit->count; + } else { + const int offset = index - insert->index; + if (offset < 0 || (offset == 0 && (iit->moveId != -1 || insert->moveId != -1))) { + insert = m_inserts.insert(insert, *iit); + ++insert; + } else if (iit->moveId == -1 && insert->moveId == -1) { + insert->index -= iit->count; + insert->count += iit->count; + } else if (offset < insert->count) { + const int moveId = insert->moveId != -1 ? m_moveCounter++ : -1; + insert = m_inserts.insert(insert, Insert(insert->index + insertCount, offset, moveId)); + ++insert; + insert->index += offset; + insert->count -= offset; + insert = m_inserts.insert(insert, *iit); + ++insert; + + if (insert->moveId != -1) { + QVector<Remove>::iterator remove = m_removes.begin(); + for (; remove != m_removes.end() && remove->moveId != insert->moveId; ++remove) {} + Q_ASSERT(remove != m_removes.end()); + if (remove->count == offset) { + remove->moveId = moveId; + } else { + remove = m_removes.insert(remove, Remove(remove->index, offset, moveId)); + ++remove; + remove->count -= offset; + } + } + } else { + ++insert; + insert = m_inserts.insert(insert, *iit); + ++insert; + } + insertCount += iit->count; + } + } + for (; change != m_changes.end(); ++change) + change->index += insertCount; + for (; insert != m_inserts.end(); ++insert) + insert->index += insertCount; + m_difference += insertCount; +} + +void QDeclarativeChangeSet::applyChanges(QVector<Change> &changes) +{ + QVector<Insert>::iterator insert = m_inserts.begin(); + QVector<Change>::iterator change = m_changes.begin(); + for (QVector<Change>::iterator cit = changes.begin(); cit != changes.end(); ++cit) { + for (; insert != m_inserts.end() && insert->end() < cit->index; ++insert) {} + for (; insert != m_inserts.end() && insert->index < cit->end(); ++insert) { + const int offset = insert->index - cit->index; + const int count = cit->count + cit->index - insert->index - insert->count; + if (offset == 0) { + cit->index = insert->index + insert->count; + cit->count = count; + } else { + cit = changes.insert(++cit, Change(insert->index + insert->count, count)); + --cit; + cit->count = offset; + } + } + + for (; change != m_changes.end() && change->index + change->count < cit->index; ++change) {} + if (change == m_changes.end() || change->index > cit->index + cit->count) { + if (cit->count > 0) { + change = m_changes.insert(change, *cit); + ++change; + } + } else { + if (cit->index < change->index) { + change->count += change->index - cit->index; + change->index = cit->index; + } + + if (cit->index + cit->count > change->index + change->count) { + change->count = cit->index + cit->count - change->index; + QVector<Change>::iterator rbegin = change; + QVector<Change>::iterator rend = ++rbegin; + for (; rend != m_changes.end() && rend->index <= change->index + change->count; ++rend) { + if (rend->index + rend->count > change->index + change->count) + change->count = rend->index + rend->count - change->index; + } + if (rbegin != rend) { + change = m_changes.erase(rbegin, rend); + --change; + } + } + } + } +} + +QDebug operator <<(QDebug debug, const QDeclarativeChangeSet &set) +{ + debug.nospace() << "QDeclarativeChangeSet("; + foreach (const QDeclarativeChangeSet::Remove &remove, set.removes()) debug << remove; + foreach (const QDeclarativeChangeSet::Insert &insert, set.inserts()) debug << insert; + foreach (const QDeclarativeChangeSet::Change &change, set.changes()) debug << change; + return debug.nospace() << ")"; +} + +QDebug operator <<(QDebug debug, const QDeclarativeChangeSet::Remove &remove) +{ + return (debug.nospace() << "Remove(" << remove.index << "," << remove.count << "," << remove.moveId << ")").space(); +} + +QDebug operator <<(QDebug debug, const QDeclarativeChangeSet::Insert &insert) +{ + return (debug.nospace() << "Insert(" << insert.index << "," << insert.count << "," << insert.moveId << ")").space(); +} + +QDebug operator <<(QDebug debug, const QDeclarativeChangeSet::Change &change) +{ + return (debug.nospace() << "Change(" << change.index << "," << change.count << ")").space(); +} + +QT_END_NAMESPACE + diff --git a/src/quick/util/qdeclarativechangeset_p.h b/src/quick/util/qdeclarativechangeset_p.h new file mode 100644 index 0000000000..b7554da503 --- /dev/null +++ b/src/quick/util/qdeclarativechangeset_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECHANGESET_P_H +#define QDECLARATIVECHANGESET_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 <QtCore/qdebug.h> +#include <QtCore/qvector.h> + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QDeclarativeChangeSet +{ +public: + struct MoveKey + { + MoveKey() : moveId(-1), offset(0) {} + MoveKey(int moveId, int offset) : moveId(moveId), offset(offset) {} + int moveId; + int offset; + }; + + struct Change + { + Change() : index(0), count(0), moveId(-1) {} + Change(int index, int count) : index(index), count(count), moveId(-1) {} + Change(int index, int count, int moveId) : index(index), count(count), moveId(moveId) {} + + int index; + int count; + int moveId; + + bool isMove() const { return moveId >= 0; } + + MoveKey moveKey(int index) const { return MoveKey(moveId, index - Change::index); } + + int start() const { return index; } + int end() const { return index + count; } + }; + + + struct Insert : public Change + { + Insert() {} + Insert(int index, int count) : Change(index, count) {} + Insert(int index, int count, int moveId) : Change(index, count, moveId) {} + }; + + struct Remove : public Change + { + Remove() {} + Remove(int index, int count) : Change(index, count) {} + Remove(int index, int count, int moveId) : Change(index, count, moveId) {} + }; + + QDeclarativeChangeSet(); + QDeclarativeChangeSet(const QDeclarativeChangeSet &changeSet); + ~QDeclarativeChangeSet(); + + QDeclarativeChangeSet &operator =(const QDeclarativeChangeSet &changeSet); + + const QVector<Remove> &removes() const { return m_removes; } + const QVector<Insert> &inserts() const { return m_inserts; } + const QVector<Change> &changes() const {return m_changes; } + + void insert(int index, int count); + void remove(int index, int count); + void move(int from, int to, int count); + void change(int index, int count); + + void apply(const QDeclarativeChangeSet &changeSet); + void apply(const QVector<Remove> &removals); + void apply(const QVector<Insert> &insertions); + void apply(const QVector<Change> &changes); + void apply( + const QVector<Remove> &removals, + const QVector<Insert> &insertions, + const QVector<Change> &changes = QVector<Change>()); + + bool isEmpty() const { return m_removes.empty() && m_inserts.empty() && m_changes.empty(); } + + void clear() + { + m_removes.clear(); + m_inserts.clear(); + m_changes.clear(); + m_moveCounter = 0; + m_difference = 0; + } + + int moveCounter() const { return m_moveCounter; } + int difference() const { return m_difference; } + +private: + void applyRemovals(QVector<Remove> &removals, QVector<Insert> &insertions); + void applyInsertions(QVector<Insert> &insertions); + void applyChanges(QVector<Change> &changes); + + QVector<Remove> m_removes; + QVector<Insert> m_inserts; + QVector<Change> m_changes; + int m_moveCounter; + int m_difference; +}; + +inline uint qHash(const QDeclarativeChangeSet::MoveKey &key) { return qHash(qMakePair(key.moveId, key.offset)); } +inline bool operator ==(const QDeclarativeChangeSet::MoveKey &l, const QDeclarativeChangeSet::MoveKey &r) { + return l.moveId == r.moveId && l.offset == r.offset; } + +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeChangeSet &change); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeChangeSet::Remove &remove); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeChangeSet::Insert &insert); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeChangeSet::Change &change); + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/util/qdeclarativeconnections.cpp b/src/quick/util/qdeclarativeconnections.cpp new file mode 100644 index 0000000000..ee2c6edcc5 --- /dev/null +++ b/src/quick/util/qdeclarativeconnections.cpp @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeconnections_p.h" + +#include <private/qdeclarativeexpression_p.h> +#include <private/qdeclarativeproperty_p.h> +#include <private/qdeclarativeboundsignal_p.h> +#include <qdeclarativecontext.h> +#include <private/qdeclarativecontext_p.h> +#include <qdeclarativeinfo.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qstringlist.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativeConnectionsPrivate : public QObjectPrivate +{ +public: + QDeclarativeConnectionsPrivate() : target(0), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {} + + QList<QDeclarativeBoundSignal*> boundsignals; + QObject *target; + + bool targetSet; + bool ignoreUnknownSignals; + bool componentcomplete; + + QByteArray data; +}; + +/*! + \qmlclass Connections QDeclarativeConnections + \inqmlmodule QtQuick 2 + \ingroup qml-utility-elements + \brief A Connections element describes generalized connections to signals. + + A Connections object creates a connection to a QML signal. + + When connecting to signals in QML, the usual way is to create an + "on<Signal>" handler that reacts when a signal is received, like this: + + \qml + MouseArea { + onClicked: { foo(parameters) } + } + \endqml + + However, it is not possible to connect to a signal in this way in some + cases, such as when: + + \list + \i Multiple connections to the same signal are required + \i Creating connections outside the scope of the signal sender + \i Connecting to targets not defined in QML + \endlist + + When any of these are needed, the Connections element can be used instead. + + For example, the above code can be changed to use a Connections object, + like this: + + \qml + MouseArea { + Connections { + onClicked: foo(parameters) + } + } + \endqml + + More generally, the Connections object can be a child of some object other than + the sender of the signal: + + \qml + MouseArea { + id: area + } + // ... + \endqml + \qml + Connections { + target: area + onClicked: foo(parameters) + } + \endqml + + \sa QtDeclarative +*/ +QDeclarativeConnections::QDeclarativeConnections(QObject *parent) : + QObject(*(new QDeclarativeConnectionsPrivate), parent) +{ +} + +QDeclarativeConnections::~QDeclarativeConnections() +{ +} + +/*! + \qmlproperty Object QtQuick2::Connections::target + This property holds the object that sends the signal. + + If this property is not set, the \c target defaults to the parent of the Connection. + + If set to null, no connection is made and any signal handlers are ignored + until the target is not null. +*/ +QObject *QDeclarativeConnections::target() const +{ + Q_D(const QDeclarativeConnections); + return d->targetSet ? d->target : parent(); +} + +void QDeclarativeConnections::setTarget(QObject *obj) +{ + Q_D(QDeclarativeConnections); + d->targetSet = true; // even if setting to 0, it is *set* + if (d->target == obj) + return; + foreach (QDeclarativeBoundSignal *s, d->boundsignals) { + // It is possible that target is being changed due to one of our signal + // handlers -> use deleteLater(). + if (s->isEvaluating()) + s->deleteLater(); + else + delete s; + } + d->boundsignals.clear(); + d->target = obj; + connectSignals(); + emit targetChanged(); +} + +/*! + \qmlproperty bool QtQuick2::Connections::ignoreUnknownSignals + + Normally, a connection to a non-existent signal produces runtime errors. + + If this property is set to \c true, such errors are ignored. + This is useful if you intend to connect to different types of objects, handling + a different set of signals for each object. +*/ +bool QDeclarativeConnections::ignoreUnknownSignals() const +{ + Q_D(const QDeclarativeConnections); + return d->ignoreUnknownSignals; +} + +void QDeclarativeConnections::setIgnoreUnknownSignals(bool ignore) +{ + Q_D(QDeclarativeConnections); + d->ignoreUnknownSignals = ignore; +} + + + +QByteArray +QDeclarativeConnectionsParser::compile(const QList<QDeclarativeCustomParserProperty> &props) +{ + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + for(int ii = 0; ii < props.count(); ++ii) + { + QString propName = props.at(ii).name(); + int propLine = props.at(ii).location().line; + + if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) { + error(props.at(ii), QDeclarativeConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); + return QByteArray(); + } + + QList<QVariant> values = props.at(ii).assignedValues(); + + for (int i = 0; i < values.count(); ++i) { + const QVariant &value = values.at(i); + + if (value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) { + error(props.at(ii), QDeclarativeConnections::tr("Connections: nested objects not allowed")); + return QByteArray(); + } else if (value.userType() == qMetaTypeId<QDeclarativeCustomParserProperty>()) { + error(props.at(ii), QDeclarativeConnections::tr("Connections: syntax error")); + return QByteArray(); + } else { + QDeclarativeScript::Variant v = qvariant_cast<QDeclarativeScript::Variant>(value); + if (v.isScript()) { + ds << propName; + ds << rewriteSignalHandler(v.asScript(), propName); + ds << propLine; + } else { + error(props.at(ii), QDeclarativeConnections::tr("Connections: script expected")); + return QByteArray(); + } + } + } + } + + return rv; +} + +void QDeclarativeConnectionsParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QDeclarativeConnectionsPrivate *p = + static_cast<QDeclarativeConnectionsPrivate *>(QObjectPrivate::get(object)); + p->data = data; +} + + +void QDeclarativeConnections::connectSignals() +{ + Q_D(QDeclarativeConnections); + if (!d->componentcomplete || (d->targetSet && !target())) + return; + + QDataStream ds(d->data); + while (!ds.atEnd()) { + QString propName; + ds >> propName; + QString script; + ds >> script; + int line; + ds >> line; + QDeclarativeProperty prop(target(), propName); + if (prop.isValid() && (prop.type() & QDeclarativeProperty::SignalProperty)) { + QDeclarativeBoundSignal *signal = + new QDeclarativeBoundSignal(target(), prop.method(), this); + + QString location; + QDeclarativeContextData *ctxtdata = 0; + QDeclarativeData *ddata = QDeclarativeData::get(this); + if (ddata) { + ctxtdata = ddata->outerContext; + if (ctxtdata && !ctxtdata->url.isEmpty()) + location = ddata->outerContext->url.toString(); + } + + QDeclarativeExpression *expression = ctxtdata ? + QDeclarativeExpressionPrivate::create(ctxtdata, 0, script, true, location, line) : 0; + signal->setExpression(expression); + d->boundsignals += signal; + } else { + if (!d->ignoreUnknownSignals) + qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName); + } + } +} + +void QDeclarativeConnections::classBegin() +{ + Q_D(QDeclarativeConnections); + d->componentcomplete=false; +} + +void QDeclarativeConnections::componentComplete() +{ + Q_D(QDeclarativeConnections); + d->componentcomplete=true; + connectSignals(); +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativeconnections_p.h b/src/quick/util/qdeclarativeconnections_p.h new file mode 100644 index 0000000000..bab29e5521 --- /dev/null +++ b/src/quick/util/qdeclarativeconnections_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECONNECTIONS_H +#define QDECLARATIVECONNECTIONS_H + +#include <qdeclarative.h> +#include <private/qdeclarativecustomparser_p.h> + +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeBoundSignal; +class QDeclarativeContext; +class QDeclarativeConnectionsPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeConnections : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeConnections) + + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals) + +public: + QDeclarativeConnections(QObject *parent=0); + ~QDeclarativeConnections(); + + QObject *target() const; + void setTarget(QObject *); + + bool ignoreUnknownSignals() const; + void setIgnoreUnknownSignals(bool ignore); + +Q_SIGNALS: + void targetChanged(); + +private: + void connectSignals(); + void classBegin(); + void componentComplete(); +}; + +class QDeclarativeConnectionsParser : public QDeclarativeCustomParser +{ +public: + virtual QByteArray compile(const QList<QDeclarativeCustomParserProperty> &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeConnections) + +QT_END_HEADER + +#endif diff --git a/src/quick/util/qdeclarativefontloader.cpp b/src/quick/util/qdeclarativefontloader.cpp new file mode 100644 index 0000000000..e95bd11a7b --- /dev/null +++ b/src/quick/util/qdeclarativefontloader.cpp @@ -0,0 +1,335 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativefontloader_p.h" + +#include <qdeclarativecontext.h> +#include <qdeclarativeengine.h> + +#include <QStringList> +#include <QUrl> +#include <QDebug> +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QFontDatabase> + +#include <private/qobject_p.h> +#include <private/qdeclarativeengine_p.h> +#include <qdeclarativeinfo.h> + +QT_BEGIN_NAMESPACE + +#define FONTLOADER_MAXIMUM_REDIRECT_RECURSION 16 + +class QDeclarativeFontObject : public QObject +{ +Q_OBJECT + +public: + QDeclarativeFontObject(int _id); + + void download(const QUrl &url, QNetworkAccessManager *manager); + +Q_SIGNALS: + void fontDownloaded(const QString&, QDeclarativeFontLoader::Status); + +private Q_SLOTS: + void replyFinished(); + +public: + int id; + +private: + QNetworkReply *reply; + int redirectCount; + + Q_DISABLE_COPY(QDeclarativeFontObject) +}; + +QDeclarativeFontObject::QDeclarativeFontObject(int _id = -1) + : QObject(0), id(_id), reply(0), redirectCount(0) {} + + +void QDeclarativeFontObject::download(const QUrl &url, QNetworkAccessManager *manager) +{ + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + reply = manager->get(req); + QObject::connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); +} + +void QDeclarativeFontObject::replyFinished() +{ + if (reply) { + redirectCount++; + if (redirectCount < FONTLOADER_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = reply->url().resolved(redirect.toUrl()); + QNetworkAccessManager *manager = reply->manager(); + reply->deleteLater(); + reply = 0; + download(url, manager); + return; + } + } + redirectCount = 0; + + if (!reply->error()) { + id = QFontDatabase::addApplicationFontFromData(reply->readAll()); + if (id != -1) + emit fontDownloaded(QFontDatabase::applicationFontFamilies(id).at(0), QDeclarativeFontLoader::Ready); + else + emit fontDownloaded(QString(), QDeclarativeFontLoader::Error); + } else { + emit fontDownloaded(QString(), QDeclarativeFontLoader::Error); + } + reply->deleteLater(); + reply = 0; + } +} + + +class QDeclarativeFontLoaderPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeFontLoader) + +public: + QDeclarativeFontLoaderPrivate() : status(QDeclarativeFontLoader::Null) {} + + QUrl url; + QString name; + QDeclarativeFontLoader::Status status; + static QHash<QUrl, QDeclarativeFontObject*> fonts; +}; + +QHash<QUrl, QDeclarativeFontObject*> QDeclarativeFontLoaderPrivate::fonts; + +/*! + \qmlclass FontLoader QDeclarativeFontLoader + \inqmlmodule QtQuick 2 + \ingroup qml-utility-elements + \brief The FontLoader element allows fonts to be loaded by name or URL. + + The FontLoader element is used to load fonts by name or URL. + + The \l status indicates when the font has been loaded, which is useful + for fonts loaded from remote sources. + + For example: + \qml + import QtQuick 1.0 + + Column { + FontLoader { id: fixedFont; name: "Courier" } + FontLoader { id: webFont; source: "http://www.mysite.com/myfont.ttf" } + + Text { text: "Fixed-size font"; font.family: fixedFont.name } + Text { text: "Fancy font"; font.family: webFont.name } + } + \endqml + + \sa {declarative/text/fonts}{Fonts example} +*/ +QDeclarativeFontLoader::QDeclarativeFontLoader(QObject *parent) + : QObject(*(new QDeclarativeFontLoaderPrivate), parent) +{ +} + +QDeclarativeFontLoader::~QDeclarativeFontLoader() +{ +} + +/*! + \qmlproperty url QtQuick2::FontLoader::source + The url of the font to load. +*/ +QUrl QDeclarativeFontLoader::source() const +{ + Q_D(const QDeclarativeFontLoader); + return d->url; +} + +void QDeclarativeFontLoader::setSource(const QUrl &url) +{ + Q_D(QDeclarativeFontLoader); + if (url == d->url) + return; + d->url = url; + emit sourceChanged(); + + QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url); + if (!localFile.isEmpty()) { + if (!d->fonts.contains(d->url)) { + int id = QFontDatabase::addApplicationFont(localFile); + if (id != -1) { + updateFontInfo(QFontDatabase::applicationFontFamilies(id).at(0), Ready); + QDeclarativeFontObject *fo = new QDeclarativeFontObject(id); + d->fonts[d->url] = fo; + } else { + updateFontInfo(QString(), Error); + } + } else { + updateFontInfo(QFontDatabase::applicationFontFamilies(d->fonts[d->url]->id).at(0), Ready); + } + } else { + if (!d->fonts.contains(d->url)) { + QDeclarativeFontObject *fo = new QDeclarativeFontObject; + d->fonts[d->url] = fo; + fo->download(d->url, qmlEngine(this)->networkAccessManager()); + d->status = Loading; + emit statusChanged(); + QObject::connect(fo, SIGNAL(fontDownloaded(QString,QDeclarativeFontLoader::Status)), + this, SLOT(updateFontInfo(QString,QDeclarativeFontLoader::Status))); + } else { + QDeclarativeFontObject *fo = d->fonts[d->url]; + if (fo->id == -1) { + d->status = Loading; + emit statusChanged(); + QObject::connect(fo, SIGNAL(fontDownloaded(QString,QDeclarativeFontLoader::Status)), + this, SLOT(updateFontInfo(QString,QDeclarativeFontLoader::Status))); + } + else + updateFontInfo(QFontDatabase::applicationFontFamilies(fo->id).at(0), Ready); + } + } +} + +void QDeclarativeFontLoader::updateFontInfo(const QString& name, QDeclarativeFontLoader::Status status) +{ + Q_D(QDeclarativeFontLoader); + + if (name != d->name) { + d->name = name; + emit nameChanged(); + } + if (status != d->status) { + if (status == Error) + qmlInfo(this) << "Cannot load font: \"" << d->url.toString() << "\""; + d->status = status; + emit statusChanged(); + } +} + +/*! + \qmlproperty string QtQuick2::FontLoader::name + + This property holds the name of the font family. + It is set automatically when a font is loaded using the \c url property. + + Use this to set the \c font.family property of a \c Text item. + + Example: + \qml + Item { + width: 200; height: 50 + + FontLoader { + id: webFont + source: "http://www.mysite.com/myfont.ttf" + } + Text { + text: "Fancy font" + font.family: webFont.name + } + } + \endqml +*/ +QString QDeclarativeFontLoader::name() const +{ + Q_D(const QDeclarativeFontLoader); + return d->name; +} + +void QDeclarativeFontLoader::setName(const QString &name) +{ + Q_D(QDeclarativeFontLoader); + if (d->name == name) + return; + d->name = name; + emit nameChanged(); + d->status = Ready; + emit statusChanged(); +} + +/*! + \qmlproperty enumeration QtQuick2::FontLoader::status + + This property holds the status of font loading. It can be one of: + \list + \o FontLoader.Null - no font has been set + \o FontLoader.Ready - the font has been loaded + \o FontLoader.Loading - the font is currently being loaded + \o FontLoader.Error - an error occurred while loading the font + \endlist + + Use this status to provide an update or respond to the status change in some way. + For example, you could: + + \list + \o Trigger a state change: + \qml + State { name: 'loaded'; when: loader.status == FontLoader.Ready } + \endqml + + \o Implement an \c onStatusChanged signal handler: + \qml + FontLoader { + id: loader + onStatusChanged: if (loader.status == FontLoader.Ready) console.log('Loaded') + } + \endqml + + \o Bind to the status value: + \qml + Text { text: loader.status == FontLoader.Ready ? 'Loaded' : 'Not loaded' } + \endqml + \endlist +*/ +QDeclarativeFontLoader::Status QDeclarativeFontLoader::status() const +{ + Q_D(const QDeclarativeFontLoader); + return d->status; +} + +QT_END_NAMESPACE + +#include <qdeclarativefontloader.moc> diff --git a/src/quick/util/qdeclarativefontloader_p.h b/src/quick/util/qdeclarativefontloader_p.h new file mode 100644 index 0000000000..2511908a8e --- /dev/null +++ b/src/quick/util/qdeclarativefontloader_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFONTLOADER_H +#define QDECLARATIVEFONTLOADER_H + +#include <qdeclarative.h> + +#include <QtCore/qobject.h> +#include <QtCore/qurl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeFontLoaderPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeFontLoader : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeFontLoader) + Q_ENUMS(Status) + + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + +public: + enum Status { Null = 0, Ready, Loading, Error }; + + QDeclarativeFontLoader(QObject *parent = 0); + ~QDeclarativeFontLoader(); + + QUrl source() const; + void setSource(const QUrl &url); + + QString name() const; + void setName(const QString &name); + + Status status() const; + +private Q_SLOTS: + void updateFontInfo(const QString&, QDeclarativeFontLoader::Status); + +Q_SIGNALS: + void sourceChanged(); + void nameChanged(); + void statusChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeFontLoader) + +QT_END_HEADER + +#endif // QDECLARATIVEFONTLOADER_H + diff --git a/src/quick/util/qdeclarativelistaccessor.cpp b/src/quick/util/qdeclarativelistaccessor.cpp new file mode 100644 index 0000000000..0063514e59 --- /dev/null +++ b/src/quick/util/qdeclarativelistaccessor.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativelistaccessor_p.h" + +#include <private/qdeclarativemetatype_p.h> + +#include <QtCore/qstringlist.h> +#include <QtCore/qdebug.h> + +// ### Remove me +#include <private/qdeclarativeengine_p.h> + +QT_BEGIN_NAMESPACE + +QDeclarativeListAccessor::QDeclarativeListAccessor() +: m_type(Invalid) +{ +} + +QDeclarativeListAccessor::~QDeclarativeListAccessor() +{ +} + +QVariant QDeclarativeListAccessor::list() const +{ + return d; +} + +void QDeclarativeListAccessor::setList(const QVariant &v, QDeclarativeEngine *engine) +{ + d = v; + + QDeclarativeEnginePrivate *enginePrivate = engine?QDeclarativeEnginePrivate::get(engine):0; + + if (!d.isValid()) { + m_type = Invalid; + } else if (d.userType() == QVariant::StringList) { + m_type = StringList; + } else if (d.userType() == QMetaType::QVariantList) { + m_type = VariantList; + } else if (d.canConvert(QVariant::Int)) { + m_type = Integer; + } else if ((!enginePrivate && QDeclarativeMetaType::isQObject(d.userType())) || + (enginePrivate && enginePrivate->isQObject(d.userType()))) { + QObject *data = enginePrivate?enginePrivate->toQObject(v):QDeclarativeMetaType::toQObject(v); + d = QVariant::fromValue(data); + m_type = Instance; + } else if (d.userType() == qMetaTypeId<QDeclarativeListReference>()) { + m_type = ListProperty; + } else { + m_type = Instance; + } +} + +int QDeclarativeListAccessor::count() const +{ + switch(m_type) { + case StringList: + return qvariant_cast<QStringList>(d).count(); + case VariantList: + return qvariant_cast<QVariantList>(d).count(); + case ListProperty: + return ((QDeclarativeListReference *)d.constData())->count(); + case Instance: + return 1; + case Integer: + return d.toInt(); + default: + case Invalid: + return 0; + } +} + +QVariant QDeclarativeListAccessor::at(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < count()); + switch(m_type) { + case StringList: + return QVariant::fromValue(qvariant_cast<QStringList>(d).at(idx)); + case VariantList: + return qvariant_cast<QVariantList>(d).at(idx); + case ListProperty: + return QVariant::fromValue(((QDeclarativeListReference *)d.constData())->at(idx)); + case Instance: + return d; + case Integer: + return QVariant(idx); + default: + case Invalid: + return QVariant(); + } +} + +bool QDeclarativeListAccessor::isValid() const +{ + return m_type != Invalid; +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativelistaccessor_p.h b/src/quick/util/qdeclarativelistaccessor_p.h new file mode 100644 index 0000000000..b9fcdd01d2 --- /dev/null +++ b/src/quick/util/qdeclarativelistaccessor_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTACCESSOR_H +#define QDECLARATIVELISTACCESSOR_H + +#include <QtCore/QVariant> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class Q_AUTOTEST_EXPORT QDeclarativeListAccessor +{ +public: + QDeclarativeListAccessor(); + ~QDeclarativeListAccessor(); + + QVariant list() const; + void setList(const QVariant &, QDeclarativeEngine * = 0); + + bool isValid() const; + + int count() const; + QVariant at(int) const; + + enum Type { Invalid, StringList, VariantList, ListProperty, Instance, Integer }; + Type type() const { return m_type; } + +private: + Type m_type; + QVariant d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVELISTACCESSOR_H diff --git a/src/quick/util/qdeclarativelistcompositor.cpp b/src/quick/util/qdeclarativelistcompositor.cpp new file mode 100644 index 0000000000..d73d76e386 --- /dev/null +++ b/src/quick/util/qdeclarativelistcompositor.cpp @@ -0,0 +1,1203 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativelistcompositor_p.h" + +#include <QtCore/qvarlengtharray.h> + +//#define QT_DECLARATIVE_VERIFY_MINIMAL +//#define QT_DECLARATIVE_VERIFY_INTEGRITY + +QT_BEGIN_NAMESPACE + +#ifdef QT_DECLARATIVE_VERIFY_MINIMAL +#define QT_DECLARATIVE_VERIFY_INTEGRITY +static bool qt_verifyMinimal( + const QDeclarativeListCompositor::iterator &begin, + const QDeclarativeListCompositor::iterator &end) +{ + bool minimal = true; + int index = 0; + + for (const QDeclarativeListCompositor::Range *range = begin->next; range != *end; range = range->next, ++index) { + if (range->previous->list == range->list + && range->previous->flags == (range->flags & ~QDeclarativeListCompositor::AppendFlag) + && range->previous->end() == range->index) { + qWarning() << index << "Consecutive ranges"; + qWarning() << *range->previous; + qWarning() << *range; + minimal = false; + } + } + + return minimal; +} + +#endif + +#ifdef QT_DECLARATIVE_VERIFY_INTEGRITY +static bool qt_printInfo(const QDeclarativeListCompositor &compositor) +{ + qWarning() << compositor; + return true; +} + +static bool qt_verifyIntegrity( + const QDeclarativeListCompositor::iterator &begin, + const QDeclarativeListCompositor::iterator &end, + const QDeclarativeListCompositor::iterator &cachedIt) +{ + bool valid = true; + + int index = 0; + QDeclarativeListCompositor::iterator it; + for (it = begin; *it != *end; *it = it->next) { + if (it->count == 0 && !it->append()) { + qWarning() << index << "Empty non-append range"; + valid = false; + } + if (it->count < 0) { + qWarning() << index << "Negative count"; + valid = false; + } + if (it->list && it->flags != QDeclarativeListCompositor::CacheFlag && it->index < 0) { + qWarning() << index <<"Negative index"; + valid = false; + } + if (it->previous->next != it.range) { + qWarning() << index << "broken list: it->previous->next != it.range"; + valid = false; + } + if (it->next->previous != it.range) { + qWarning() << index << "broken list: it->next->previous != it.range"; + valid = false; + } + if (*it == *cachedIt) { + for (int i = 0; i < end.groupCount; ++i) { + int groupIndex = it.index[i]; + if (cachedIt->flags & (1 << i)) + groupIndex += cachedIt.offset; + if (groupIndex != cachedIt.index[i]) { + qWarning() << index + << "invalid cached index" + << QDeclarativeListCompositor::Group(i) + << "Expected:" + << groupIndex + << "Actual" + << cachedIt.index[i] + << cachedIt; + valid = false; + } + } + } + it.incrementIndexes(it->count); + ++index; + } + + for (int i = 0; i < end.groupCount; ++i) { + if (end.index[i] != it.index[i]) { + qWarning() << "Group" << i << "count invalid. Expected:" << end.index[i] << "Actual:" << it.index[i]; + valid = false; + } + } + return valid; +} +#endif + +#if defined(QT_DECLARATIVE_VERIFY_MINIMAL) +# define QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!(qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \ + && qt_verifyMinimal(iterator(m_ranges.next, 0, Default, m_groupCount), m_end)) \ + && qt_printInfo(*this))); +#elif defined(QT_DECLARATIVE_VERIFY_INTEGRITY) +# define QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \ + && qt_printInfo(*this))); +#else +# define QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR +#endif + +//#define QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(args) qDebug() << m_end.index[1] << m_end.index[0] << Q_FUNC_INFO args; +#define QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(args) + +QDeclarativeListCompositor::iterator &QDeclarativeListCompositor::iterator::operator +=(int difference) +{ + Q_ASSERT(difference >= 0); + while (!(range->flags & groupFlag)) { + incrementIndexes(range->count - offset); + offset = 0; + range = range->next; + } + decrementIndexes(offset); + offset += difference; + while (offset >= range->count || !(range->flags & groupFlag)) { + if (range->flags & groupFlag) + offset -= range->count; + incrementIndexes(range->count); + range = range->next; + } + incrementIndexes(offset); + return *this; +} + +QDeclarativeListCompositor::iterator &QDeclarativeListCompositor::iterator::operator -=(int difference) +{ + Q_ASSERT(difference >= 0); + while (!(range->flags & groupFlag)) { + decrementIndexes(offset); + range = range->previous; + offset = range->count; + } + decrementIndexes(offset); + offset -= difference; + while (offset < 0) { + range = range->previous; + if (range->flags & groupFlag) + offset += range->count; + decrementIndexes(range->count); + } + incrementIndexes(offset); + return *this; +} + +QDeclarativeListCompositor::insert_iterator &QDeclarativeListCompositor::insert_iterator::operator +=(int difference) +{ + Q_ASSERT(difference >= 0); + while (!(range->flags & groupFlag)) { + incrementIndexes(range->count - offset); + offset = 0; + range = range->next; + } + decrementIndexes(offset); + offset += difference; + while (offset > range->count + || (offset == range->count && !range->append() && offset > 0) + || (!(range->flags & groupFlag) && offset > 0)) { + if (range->flags & groupFlag) + offset -= range->count; + incrementIndexes(range->count); + range = range->next; + } + incrementIndexes(offset); + return *this; +} + +QDeclarativeListCompositor::insert_iterator &QDeclarativeListCompositor::insert_iterator::operator -=(int difference) +{ + Q_ASSERT(difference >= 0); + while (!(range->flags & groupFlag) && range->previous->flags) { + decrementIndexes(offset); + range = range->previous; + offset = (range->flags & (GroupMask | CacheFlag)) ? range->count : 0; + } + decrementIndexes(offset); + offset -= difference; + while (offset < 0) { + range = range->previous; + if (range->flags & groupFlag) + offset += range->count; + decrementIndexes(range->count); + } + incrementIndexes(offset); + for (Range *previous = range->previous; offset == 0 && previous->prepend(); previous = previous->previous) { + if (previous->append() && previous->inGroup()) { + offset = previous->count; + range = previous; + } else if (!previous->inGroup()) { + break; + } + } + + return *this; +} + +QDeclarativeListCompositor::QDeclarativeListCompositor() + : m_end(m_ranges.next, 0, Default, 2) + , m_cacheIt(m_end) + , m_groupCount(2) + , m_defaultFlags(PrependFlag | DefaultFlag) + , m_removeFlags(AppendFlag | PrependFlag | GroupMask) +{ +} + +QDeclarativeListCompositor::~QDeclarativeListCompositor() +{ + for (Range *next, *range = m_ranges.next; range != &m_ranges; range = next) { + next = range->next; + delete range; + } +} + +inline QDeclarativeListCompositor::Range *QDeclarativeListCompositor::insert( + Range *before, void *list, int index, int count, int flags) +{ + return new Range(before, list, index, count, flags); +} + +inline QDeclarativeListCompositor::Range *QDeclarativeListCompositor::erase( + Range *range) +{ + Range *next = range->next; + next->previous = range->previous; + next->previous->next = range->next; + delete range; + return next; +} + +void QDeclarativeListCompositor::setGroupCount(int count) +{ + m_groupCount = count; + m_end = iterator(&m_ranges, 0, Default, m_groupCount); + m_cacheIt = m_end; +} + +int QDeclarativeListCompositor::count(Group group) const +{ + return m_end.index[group]; +} + +QDeclarativeListCompositor::iterator QDeclarativeListCompositor::find(Group group, int index) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< group << index) + Q_ASSERT(index >=0 && index < count(group)); + if (m_cacheIt == m_end) { + m_cacheIt = iterator(m_ranges.next, 0, group, m_groupCount); + m_cacheIt += index; + } else { + const int offset = index - m_cacheIt.index[group]; + m_cacheIt.setGroup(group); + if (offset > 0) { + m_cacheIt += offset; + } else if (offset < 0) { + m_cacheIt -= -offset; + } else if (offset == 0) { + m_cacheIt -= 0; + m_cacheIt += 0; + } + } + Q_ASSERT(m_cacheIt.index[group] == index); + Q_ASSERT(m_cacheIt->inGroup(group)); + QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR + return m_cacheIt; +} + +QDeclarativeListCompositor::iterator QDeclarativeListCompositor::find(Group group, int index) const +{ + return const_cast<QDeclarativeListCompositor *>(this)->find(group, index); +} + +QDeclarativeListCompositor::insert_iterator QDeclarativeListCompositor::findInsertPosition(Group group, int index) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< group << index) + Q_ASSERT(index >=0 && index <= count(group)); + insert_iterator it; + if (m_cacheIt == m_end) { + it = iterator(m_ranges.next, 0, group, m_groupCount); + it += index; + } else { + const int offset = index - m_cacheIt.index[group]; + it = m_cacheIt; + it.setGroup(group); + if (offset > 0) { + it += offset; + } else if (offset < 0) { + it -= -offset; + } else if (offset == 0) { + it -= 0; + it += 0; + } + } + Q_ASSERT(it.index[group] == index); + return it; +} + +QDeclarativeListCompositor::iterator QDeclarativeListCompositor::begin(Group group) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< group) + m_cacheIt = iterator(m_ranges.next, 0, group, m_groupCount); + m_cacheIt += 0; + return m_cacheIt; +} + +void QDeclarativeListCompositor::append( + void *list, int index, int count, int flags, QVector<Insert> *inserts) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << index << count << flags) + insert(m_end, list, index, count, flags, inserts); +} + +void QDeclarativeListCompositor::insert( + Group group, int before, void *list, int index, int count, int flags, QVector<Insert> *inserts) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< group << before << list << index << count << flags) + insert(findInsertPosition(group, before), list, index, count, flags, inserts); +} + +QDeclarativeListCompositor::iterator QDeclarativeListCompositor::insert( + iterator before, void *list, int index, int count, int flags, QVector<Insert> *inserts) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< before << list << index << count << flags) + if (inserts) { + inserts->append(Insert(before, count, flags & GroupMask)); + } + if (before.offset > 0) { + *before = insert( + *before, before->list, before->index, before.offset, before->flags & ~AppendFlag)->next; + before->index += before.offset; + before->count -= before.offset; + before.offset = 0; + } + + if (!(flags & AppendFlag) && *before != m_ranges.next + && before->previous->list == list + && before->previous->flags == flags + && (!list || before->previous->end() == index)) { + before->previous->count += count; + before.incrementIndexes(count, flags); + } else { + *before = insert(*before, list, index, count, flags); + before.offset = 0; + } + + if (!(flags & AppendFlag) && before->next != &m_ranges + && before->list == before->next->list + && before->flags == before->next->flags + && (!list || before->end() == before->next->index)) { + before->next->index = before->index; + before->next->count += before->count; + *before = erase(*before); + } + + m_end.incrementIndexes(count, flags); + m_cacheIt = before; + QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR + return before; +} + +void QDeclarativeListCompositor::setFlags( + Group group, int index, int count, int flags, QVector<Insert> *inserts) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< group << index << count << flags) + setFlags(find(group, index), count, flags, inserts); +} + +void QDeclarativeListCompositor::setFlags( + iterator from, int count, int flags, QVector<Insert> *inserts) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< from << count << flags) + if (!flags || !count) + return; + + if (from.offset > 0) { + *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next; + from->index += from.offset; + from->count -= from.offset; + from.offset = 0; + } + + for (; count > 0; *from = from->next) { + if (from != from.group) { + from.incrementIndexes(from->count); + continue; + } + const int difference = qMin(count, from->count); + count -= difference; + + const int insertFlags = ~from->flags & flags; + const int setFlags = (from->flags | flags) & ~AppendFlag; + if (insertFlags && inserts) + inserts->append(Insert(from, difference, insertFlags | (from->flags & CacheFlag))); + m_end.incrementIndexes(difference, insertFlags); + from.incrementIndexes(difference, setFlags); + + if (from->previous != &m_ranges + && from->previous->list == from->list + && (!from->list || from->previous->end() == from->index) + && from->previous->flags == setFlags) { + from->previous->count += difference; + from->index += difference; + from->count -= difference; + if (from->count == 0) { + if (from->append()) + from->previous->flags |= AppendFlag; + *from = erase(*from)->previous; + continue; + } else { + break; + } + } else if (!insertFlags) { + from.incrementIndexes(from->count - difference); + continue; + } else if (difference < from->count) { + *from = insert(*from, from->list, from->index, difference, setFlags)->next; + from->index += difference; + from->count -= difference; + } else { + from->flags |= flags; + continue; + } + from.incrementIndexes(from->count); + } + + if (from->previous != &m_ranges + && from->previous->list == from->list + && (!from->list || from->previous->end() == from->index) + && from->previous->flags == (from->flags & ~AppendFlag)) { + from.offset = from->previous->count; + from->previous->count += from->count; + from->previous->flags = from->flags; + *from = erase(*from)->previous; + } + m_cacheIt = from; + QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR +} + +void QDeclarativeListCompositor::clearFlags( + Group group, int index, int count, int flags, QVector<Remove> *removes) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< group << index << count << flags) + clearFlags(find(group, index), count, flags, removes); +} + +void QDeclarativeListCompositor::clearFlags( + iterator from, int count, int flags, QVector<Remove> *removes) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< from << count << flags) + if (!flags || !count) + return; + + const bool clearCache = flags & CacheFlag; + + if (from.offset > 0) { + *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next; + from->index += from.offset; + from->count -= from.offset; + from.offset = 0; + } + + for (; count > 0; *from = from->next) { + if (from != from.group) { + from.incrementIndexes(from->count); + continue; + } + const int difference = qMin(count, from->count); + count -= difference; + + const int removeFlags = from->flags & flags & ~(AppendFlag | PrependFlag); + const int clearedFlags = from->flags & ~(flags | AppendFlag); + if (removeFlags && removes) { + const int maskedFlags = clearCache + ? (removeFlags & ~CacheFlag) + : (removeFlags | (from->flags & CacheFlag)); + if (maskedFlags) + removes->append(Remove(from, difference, maskedFlags)); + } + m_end.decrementIndexes(difference, removeFlags); + from.incrementIndexes(difference, clearedFlags); + + if (from->previous != &m_ranges + && from->previous->list == from->list + && (!from->list || clearedFlags == CacheFlag || from->previous->end() == from->index) + && from->previous->flags == clearedFlags) { + from->previous->count += difference; + from->index += difference; + from->count -= difference; + if (from->count == 0) { + if (from->append()) + from->previous->flags |= AppendFlag; + *from = erase(*from)->previous; + } else { + from.incrementIndexes(from->count); + } + } else if (difference < from->count) { + if (clearedFlags) + *from = insert(*from, from->list, from->index, difference, clearedFlags)->next; + from->index += difference; + from->count -= difference; + from.incrementIndexes(from->count); + } else if (clearedFlags) { + from->flags &= ~flags; + } else { + *from = erase(*from)->previous; + } + } + + if (*from != &m_ranges && from->previous != &m_ranges + && from->previous->list == from->list + && (!from->list || from->previous->end() == from->index) + && from->previous->flags == (from->flags & ~AppendFlag)) { + from.offset = from->previous->count; + from->previous->count += from->count; + from->previous->flags = from->flags; + *from = erase(*from)->previous; + } + m_cacheIt = from; + QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR +} + +void QDeclarativeListCompositor::removeList(void *list, QVector<Remove> *removes, bool destroyed) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << destroyed) + for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { + if (it->list == list) { + const int flags = it->flags & (GroupMask | CacheFlag); + if (flags) { + removes->append(Remove(it, it->count, flags)); + m_end.decrementIndexes(it->count, flags); + } + if (destroyed) + it->list = 0; + if (it->inCache()) { + it->flags = CacheFlag; + it.cacheIndex += it->count; + } else { + *it = erase(*it)->previous; + } + } else { + it.incrementIndexes(it->count); + } + } + m_cacheIt = m_end; + QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR +} + +bool QDeclarativeListCompositor::verifyMoveTo( + Group fromGroup, int from, Group toGroup, int to, int count) const +{ + if (fromGroup != toGroup) { + // determine how many items from the destination group intersect with the source group. + iterator fromIt = find(fromGroup, from); + + int intersectingCount = 0; + + for (; count > 0; *fromIt = fromIt->next) { + if (*fromIt == &m_ranges) + return false; + if (!fromIt->inGroup(fromGroup)) + continue; + if (fromIt->inGroup(toGroup)) + intersectingCount += qMin(count, fromIt->count - fromIt.offset); + count -= fromIt->count - fromIt.offset; + fromIt.offset = 0; + } + count = intersectingCount; + } + + return to >= 0 && to + count <= m_end.index[toGroup]; +} + +void QDeclarativeListCompositor::move( + Group fromGroup, + int from, + Group toGroup, + int to, + int count, + QVector<Remove> *removes, + QVector<Insert> *inserts) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< fromGroup << from << toGroup << to << count) + Q_ASSERT(count != 0); + Q_ASSERT(from >=0 && from + count <= m_end.index[toGroup]); + Q_ASSERT(verifyMoveTo(fromGroup, from, toGroup, to, count)); + + iterator fromIt = find(fromGroup, from); + if (fromIt.offset > 0) { + *fromIt = insert( + *fromIt, fromIt->list, fromIt->index, fromIt.offset, fromIt->flags & ~AppendFlag)->next; + fromIt->index += fromIt.offset; + fromIt->count -= fromIt.offset; + fromIt.offset = 0; + } + + Range movedFlags; + for (int moveId = 0; count > 0;) { + if (fromIt != fromIt.group) { + fromIt.incrementIndexes(fromIt->count); + *fromIt = fromIt->next; + continue; + } + int difference = qMin(count, fromIt->count); + + new Range( + &movedFlags, + fromIt->list, + fromIt->index, + difference, + fromIt->flags & ~(PrependFlag | AppendFlag)); + if (removes) + removes->append(Remove(fromIt, difference, fromIt->flags, moveId++)); + count -= difference; + fromIt->count -= difference; + + int removeIndex = fromIt->index; + if (fromIt->prepend() + && fromIt->previous != &m_ranges + && fromIt->previous->flags == PrependFlag + && fromIt->previous->list == fromIt->list + && fromIt->previous->end() == fromIt->index) { + fromIt->previous->count += difference; + } else if (fromIt->prepend()) { + *fromIt = insert(*fromIt, fromIt->list, removeIndex, difference, PrependFlag)->next; + } + fromIt->index += difference; + + if (fromIt->count == 0) { + if (fromIt->append()) + fromIt->previous->flags |= AppendFlag; + *fromIt = erase(*fromIt); + + if (*fromIt != m_ranges.next && fromIt->flags == PrependFlag + && fromIt->previous != &m_ranges + && fromIt->previous->flags == PrependFlag + && fromIt->previous->list == fromIt->list + && fromIt->previous->end() == fromIt->index) { + fromIt.incrementIndexes(fromIt->count); + fromIt->previous->count += fromIt->count; + *fromIt = erase(*fromIt); + } + } else if (count > 0) { + *fromIt = fromIt->next; + } + } + + if (*fromIt != m_ranges.next + && *fromIt != &m_ranges + && fromIt->previous->list == fromIt->list + && (!fromIt->list || fromIt->previous->end() == fromIt->index) + && fromIt->previous->flags == (fromIt->flags & ~AppendFlag)) { + if (fromIt == fromIt.group) + fromIt.offset = fromIt->previous->count; + fromIt.offset = fromIt->previous->count; + fromIt->previous->count += fromIt->count; + fromIt->previous->flags = fromIt->flags; + *fromIt = erase(*fromIt)->previous; + } + + insert_iterator toIt = fromIt; + toIt.setGroup(toGroup); + const int difference = to - toIt.index[toGroup]; + if (difference > 0) + toIt += difference; + else + toIt -= -difference; + + if (toIt.offset > 0) { + *toIt = insert(*toIt, toIt->list, toIt->index, toIt.offset, toIt->flags & ~AppendFlag)->next; + toIt->index += toIt.offset; + toIt->count -= toIt.offset; + toIt.offset = 0; + } + + for (Range *range = movedFlags.previous; range != &movedFlags; range = range->previous) { + if (*toIt != &m_ranges + && range->list == toIt->list + && (!range->list || range->end() == toIt->index) + && range->flags == (toIt->flags & ~AppendFlag)) { + toIt->index -= range->count; + toIt->count += range->count; + } else { + *toIt = insert(*toIt, range->list, range->index, range->count, range->flags); + } + } + + if (*toIt != m_ranges.next + && toIt->previous->list == toIt->list + && (!toIt->list || (toIt->previous->end() == toIt->index && toIt->previous->flags == (toIt->flags & ~AppendFlag)))) { + toIt.offset = toIt->previous->count; + toIt->previous->count += toIt->count; + toIt->previous->flags = toIt->flags; + *toIt = erase(*toIt)->previous; + } + Insert insert(toIt, 0, 0, 0); + for (Range *next, *range = movedFlags.next; range != &movedFlags; range = next) { + insert.count = range->count; + insert.flags = range->flags; + if (inserts) + inserts->append(insert); + for (int i = 0; i < m_groupCount; ++i) { + if (insert.inGroup(i)) + insert.index[i] += range->count; + } + ++insert.moveId; + next = range->next; + delete range; + } + + m_cacheIt = toIt; + QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR +} + +void QDeclarativeListCompositor::clear() +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR( ) + for (Range *range = m_ranges.next; range != &m_ranges; range = erase(range)) {} + m_end = iterator(m_ranges.next, 0, Default, m_groupCount); + m_cacheIt = m_end; +} + +void QDeclarativeListCompositor::listItemsInserted( + QVector<Insert> *translatedInsertions, + void *list, + const QVector<QDeclarativeChangeSet::Insert> &insertions, + const QVector<MovedFlags> *movedFlags) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << insertions) + for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { + if (it->list != list || it->flags == CacheFlag) { + it.incrementIndexes(it->count); + continue; + } else if (it->flags & MovedFlag) { + it->flags &= ~MovedFlag; + it.incrementIndexes(it->count); + continue; + } + foreach (const QDeclarativeChangeSet::Insert &insertion, insertions) { + int offset = insertion.index - it->index; + if ((offset > 0 && offset < it->count) + || (offset == 0 && it->prepend()) + || (offset == it->count && it->append())) { + if (it->prepend()) { + int flags = m_defaultFlags; + if (insertion.isMove()) { + for (QVector<MovedFlags>::const_iterator move = movedFlags->begin(); + move != movedFlags->end(); + ++move) { + if (move->moveId == insertion.moveId) { + flags = move->flags; + break; + } + } + } + if (flags & ~(AppendFlag | PrependFlag)) { + Insert translatedInsert(it, insertion.count, flags, insertion.moveId); + for (int i = 0; i < m_groupCount; ++i) { + if (it->inGroup(i)) + translatedInsert.index[i] += offset; + } + translatedInsertions->append(translatedInsert); + } + if ((it->flags & ~AppendFlag) == flags) { + it->count += insertion.count; + } else if (offset == 0 + && it->previous != &m_ranges + && it->previous->list == list + && it->previous->end() == insertion.index + && it->previous->flags == flags) { + it->previous->count += insertion.count; + it->index += insertion.count; + it.incrementIndexes(insertion.count); + } else { + if (offset > 0) { + it.incrementIndexes(offset); + *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; + } + *it = insert(*it, it->list, insertion.index, insertion.count, flags)->next; + it.incrementIndexes(insertion.count, flags); + it->index += offset + insertion.count; + it->count -= offset; + } + m_end.incrementIndexes(insertion.count, flags); + } else { + if (offset > 0) { + *it = insert(*it, it->list, it->index, offset, it->flags)->next; + it->index += offset; + it->count -= offset; + } + it->index += insertion.count; + } + } else if (offset <= 0) { + it->index += insertion.count; + } + } + it.incrementIndexes(it->count); + } + m_cacheIt = m_end; + QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR +} + +void QDeclarativeListCompositor::listItemsInserted( + void *list, int index, int count, QVector<Insert> *translatedInsertions) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << index << count) + Q_ASSERT(count > 0); + + QVector<QDeclarativeChangeSet::Insert> insertions; + insertions.append(QDeclarativeChangeSet::Insert(index, count)); + + listItemsInserted(translatedInsertions, list, insertions); +} + +void QDeclarativeListCompositor::listItemsRemoved( + QVector<Remove> *translatedRemovals, + void *list, + QVector<QDeclarativeChangeSet::Remove> *removals, + QVector<QDeclarativeChangeSet::Insert> *insertions, + QVector<MovedFlags> *movedFlags, int moveId) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << *removals) + + for (iterator it(m_ranges.next, 0, Default, m_groupCount); + *it != &m_ranges && !removals->isEmpty(); + *it = it->next) { + if (it->list != list || it->flags == CacheFlag) { + it.incrementIndexes(it->count); + continue; + } + bool removed = false; + for (QVector<QDeclarativeChangeSet::Remove>::iterator removal = removals->begin(); + !removed && removal != removals->end(); + ++removal) { + int relativeIndex = removal->index - it->index; + int itemsRemoved = removal->count; + if (relativeIndex + removal->count > 0 && relativeIndex < it->count) { + const int offset = qMax(0, relativeIndex); + int removeCount = qMin(it->count, relativeIndex + removal->count) - offset; + it->count -= removeCount; + int removeFlags = it->flags & m_removeFlags; + Remove translatedRemoval(it, removeCount, it->flags); + for (int i = 0; i < m_groupCount; ++i) { + if (it->inGroup(i)) + translatedRemoval.index[i] += offset; + } + if (removal->isMove()) { + QVector<QDeclarativeChangeSet::Insert>::iterator insertion = insertions->begin(); + for (; insertion != insertions->end() && insertion->moveId != removal->moveId; + ++insertion) {} + Q_ASSERT(insertion != insertions->end()); + Q_ASSERT(insertion->count == removal->count); + + if (relativeIndex < 0) { + int splitMoveId = ++moveId; + removal = removals->insert(removal, QDeclarativeChangeSet::Remove( + removal->index, -relativeIndex, splitMoveId)); + ++removal; + removal->count -= -relativeIndex; + insertion = insertions->insert(insertion, QDeclarativeChangeSet::Insert( + insertion->index, -relativeIndex, splitMoveId)); + ++insertion; + insertion->index += -relativeIndex; + insertion->count -= -relativeIndex; + } + + if (it->prepend()) { + removeFlags |= it->flags & CacheFlag; + translatedRemoval.moveId = ++moveId; + movedFlags->append(MovedFlags(moveId, it->flags & ~AppendFlag)); + + removal = removals->insert(removal, QDeclarativeChangeSet::Remove( + removal->index, removeCount, translatedRemoval.moveId)); + ++removal; + insertion = insertions->insert(insertion, QDeclarativeChangeSet::Insert( + insertion->index, removeCount, translatedRemoval.moveId)); + ++insertion; + + removal->count -= removeCount; + insertion->index += removeCount; + insertion->count -= removeCount; + if (removal->count == 0) { + removal = removals->erase(removal); + insertion = insertions->erase(insertion); + --removal; + --insertion; + } + } else { + if (offset > 0) { + *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; + it->index += offset; + it->count -= offset; + it.incrementIndexes(offset); + } + if (it->previous != &m_ranges + && it->previous->list == it->list + && it->end() == insertion->index + && it->previous->flags == (it->flags | MovedFlag)) { + it->previous->count += removeCount; + } else { + *it = insert(*it, it->list, insertion->index, removeCount, it->flags | MovedFlag)->next; + } + translatedRemoval.flags = 0; + removeFlags = 0; + } + } else if (it->inCache()) { + if (offset > 0) { + *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; + it->index += offset; + it->count -= offset; + it.incrementIndexes(offset); + } + if (it->previous != &m_ranges + && it->previous->list == it->list + && it->previous->flags == CacheFlag) { + it->previous->count += removeCount; + } else { + *it = insert(*it, it->list, -1, removeCount, CacheFlag)->next; + } + it.index[Cache] += removeCount; + } + if (removeFlags & GroupMask) + translatedRemovals->append(translatedRemoval); + m_end.decrementIndexes(removeCount, removeFlags); + if (it->count == 0 && !it->append()) { + *it = erase(*it)->previous; + removed = true; + } else if (relativeIndex <= 0) { + it->index = removal->index; + } + } else if (relativeIndex < 0) { + it->index -= itemsRemoved; + + if (it->previous != &m_ranges + && it->previous->list == it->list + && it->previous->end() == it->index + && it->previous->flags == (it->flags & ~AppendFlag)) { + it->previous->count += it->count; + it->previous->flags = it->flags; + it.incrementIndexes(it->count); + *it = erase(*it); + removed = true; + } + } + } + if (it->flags == CacheFlag && it->next->flags == CacheFlag && it->next->list == it->list) { + it.index[Cache] += it->next->count; + it->count += it->next->count; + erase(it->next); + } else if (!removed) { + it.incrementIndexes(it->count); + } + } + m_cacheIt = m_end; + QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR +} + +void QDeclarativeListCompositor::listItemsRemoved( + void *list, int index, int count, QVector<Remove> *translatedRemovals) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << index << count) + Q_ASSERT(count >= 0); + + QVector<QDeclarativeChangeSet::Remove> removals; + removals.append(QDeclarativeChangeSet::Remove(index, count)); + listItemsRemoved(translatedRemovals, list, &removals, 0, 0, 0); +} + +void QDeclarativeListCompositor::listItemsMoved( + void *list, + int from, + int to, + int count, + QVector<Remove> *translatedRemovals, + QVector<Insert> *translatedInsertions) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << from << to << count) + Q_ASSERT(count >= 0); + + QVector<QDeclarativeChangeSet::Remove> removals; + QVector<QDeclarativeChangeSet::Insert> insertions; + QVector<MovedFlags> movedFlags; + removals.append(QDeclarativeChangeSet::Remove(from, count, 0)); + insertions.append(QDeclarativeChangeSet::Insert(to, count, 0)); + + listItemsRemoved(translatedRemovals, list, &removals, &insertions, &movedFlags, 0); + listItemsInserted(translatedInsertions, list, insertions, &movedFlags); +} + +void QDeclarativeListCompositor::listItemsChanged( + QVector<Change> *translatedChanges, + void *list, + const QVector<QDeclarativeChangeSet::Change> &changes) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << changes) + for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { + if (it->list != list || it->flags == CacheFlag) { + it.incrementIndexes(it->count); + continue; + } else if (!it->inGroup()) { + continue; + } + foreach (const QDeclarativeChangeSet::Change &change, changes) { + const int offset = change.index - it->index; + if (offset + change.count > 0 && offset < it->count) { + const int changeOffset = qMax(0, offset); + const int changeCount = qMin(it->count, offset + change.count) - changeOffset; + + Change translatedChange(it, changeCount, it->flags); + for (int i = 0; i < m_groupCount; ++i) { + if (it->inGroup(i)) + translatedChange.index[i] += changeOffset; + } + translatedChanges->append(translatedChange); + } + } + it.incrementIndexes(it->count); + } +} + +void QDeclarativeListCompositor::listItemsChanged( + void *list, int index, int count, QVector<Change> *translatedChanges) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << index << count) + Q_ASSERT(count >= 0); + QVector<QDeclarativeChangeSet::Change> changes; + changes.append(QDeclarativeChangeSet::Change(index, count)); + listItemsChanged(translatedChanges, list, changes); +} + +void QDeclarativeListCompositor::listChanged( + void *list, + const QDeclarativeChangeSet &changeSet, + QVector<Remove> *translatedRemovals, + QVector<Insert> *translatedInsertions, + QVector<Change> *translatedChanges) +{ + QVector<QDeclarativeChangeSet::Remove> removals = changeSet.removes(); + QVector<QDeclarativeChangeSet::Insert> insertions = changeSet.inserts(); + QVector<MovedFlags> movedFlags; + listItemsRemoved(translatedRemovals, list, &removals, &insertions, &movedFlags, changeSet.moveCounter()); + listItemsInserted(translatedInsertions, list, insertions, &movedFlags); + listItemsChanged(translatedChanges, list, changeSet.changes()); +} + +void QDeclarativeListCompositor::transition( + Group from, + Group to, + QVector<QDeclarativeChangeSet::Remove> *removes, + QVector<QDeclarativeChangeSet::Insert> *inserts) +{ + int removeCount = 0; + for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { + if (it == from && it != to) { + removes->append(QDeclarativeChangeSet::Remove(it.index[from]- removeCount, it->count)); + removeCount += it->count; + } else if (it != from && it == to) { + inserts->append(QDeclarativeChangeSet::Insert(it.index[to], it->count)); + } + it.incrementIndexes(it->count); + } +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Group &group) +{ + switch (group) { + case QDeclarativeListCompositor::Cache: return debug << "Cache"; + case QDeclarativeListCompositor::Default: return debug << "Default"; + default: return (debug.nospace() << "Group" << int(group)).space(); + } + +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Range &range) +{ + (debug.nospace() + << "Range(" + << range.list) << " " + << range.index << " " + << range.count << " " + << (range.append() ? "A" : "0") + << (range.prepend() ? "P" : "0"); + for (int i = QDeclarativeListCompositor::MaximumGroupCount - 1; i >= 2; --i) + debug << (range.inGroup(i) ? "1" : "0"); + return (debug + << (range.inGroup(QDeclarativeListCompositor::Default) ? "D" : "0") + << (range.inGroup(QDeclarativeListCompositor::Cache) ? "C" : "0")); +} + +static void qt_print_indexes(QDebug &debug, int count, const int *indexes) +{ + for (int i = count - 1; i >= 0; --i) + debug << indexes[i]; +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::iterator &it) +{ + (debug.nospace() << "iterator(" << it.group).space() << "offset:" << it.offset; + qt_print_indexes(debug, it.groupCount, it.index); + return ((debug << **it).nospace() << ")").space(); +} + +static QDebug qt_print_change(QDebug debug, const char *name, const QDeclarativeListCompositor::Change &change) +{ + debug.nospace() << name << "(" << change.moveId << " " << change.count << " "; + for (int i = QDeclarativeListCompositor::MaximumGroupCount - 1; i >= 2; --i) + debug << (change.inGroup(i) ? "1" : "0"); + debug << (change.inGroup(QDeclarativeListCompositor::Default) ? "D" : "0") + << (change.inGroup(QDeclarativeListCompositor::Cache) ? "C" : "0"); + int i = QDeclarativeListCompositor::MaximumGroupCount - 1; + for (; i >= 0 && !change.inGroup(i); --i) {} + for (; i >= 0; --i) + debug << " " << change.index[i]; + return (debug << ")").maybeSpace(); +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Change &change) +{ + return qt_print_change(debug, "Change", change); +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Remove &remove) +{ + return qt_print_change(debug, "Remove", remove); +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Insert &insert) +{ + return qt_print_change(debug, "Insert", insert); +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor &list) +{ + int indexes[QDeclarativeListCompositor::MaximumGroupCount]; + for (int i = 0; i < QDeclarativeListCompositor::MaximumGroupCount; ++i) + indexes[i] = 0; + debug.nospace() << "QDeclarativeListCompositor("; + qt_print_indexes(debug, list.m_groupCount, list.m_end.index); + for (QDeclarativeListCompositor::Range *range = list.m_ranges.next; range != &list.m_ranges; range = range->next) { + (debug << "\n").space(); + qt_print_indexes(debug, list.m_groupCount, indexes); + debug << " " << *range; + + for (int i = 0; i < list.m_groupCount; ++i) { + if (range->inGroup(i)) + indexes[i] += range->count; + } + } + return (debug << ")").maybeSpace(); +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativelistcompositor_p.h b/src/quick/util/qdeclarativelistcompositor_p.h new file mode 100644 index 0000000000..c808f6fe80 --- /dev/null +++ b/src/quick/util/qdeclarativelistcompositor_p.h @@ -0,0 +1,371 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTCOMPOSITOR_P_H +#define QDECLARATIVELISTCOMPOSITOR_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 <QtCore/qglobal.h> +#include <QtCore/qvector.h> + +#include <private/qdeclarativechangeset_p.h> + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QDeclarativeListCompositor +{ +public: + enum { MaximumGroupCount = 11 }; + + enum Group + { + Cache = 0, + Default = 1, + Persisted = 2 + }; + + enum Flag + { + CacheFlag = 0x000001, + DefaultFlag = 0x000002, + PersistedFlag = 0x000004, + GroupMask = 0x00FFFE, + PrependFlag = 0x100000, + AppendFlag = 0x200000, + MovedFlag = 0x400000 + }; + + class Range + { + public: + Range() : next(this), previous(this), list(0), index(0), count(0), flags(0) {} + Range(Range *next, void *list, int index, int count, uint flags) + : next(next), previous(next->previous), list(list), index(index), count(count), flags(flags) { + next->previous = this; previous->next = this; } + + Range *next; + Range *previous; + void *list; + int index; + int count; + int flags; + + inline int start() const { return index; } + inline int end() const { return index + count; } + + inline int groups() const { return flags & GroupMask; } + + inline bool inGroup() const { return flags & GroupMask; } + inline bool inCache() const { return flags & CacheFlag; } + inline bool inGroup(int group) const { return flags & (1 << group); } + + inline bool prepend() const { return flags & PrependFlag; } + inline bool append() const { return flags & AppendFlag; } + }; + + class Q_AUTOTEST_EXPORT iterator + { + public: + inline iterator(); + inline iterator(const iterator &it); + inline iterator(Range *range, int offset, Group group, int groupCount); + inline ~iterator() {} + + bool operator ==(const iterator &it) const { return range == it.range && offset == it.offset; } + bool operator !=(const iterator &it) const { return range != it.range || offset != it.offset; } + + bool operator ==(Group group) const { return range->flags & (1 << group); } + bool operator !=(Group group) const { return !(range->flags & (1 << group)); } + + Range *&operator *() { return range; } + Range * const &operator *() const { return range; } + Range *operator ->() { return range; } + const Range *operator ->() const { return range; } + + iterator &operator +=(int difference); + iterator &operator -=(int difference); + + template<typename T> T *list() const { return static_cast<T *>(range->list); } + int modelIndex() const { return range->index + offset; } + + void incrementIndexes(int difference) { incrementIndexes(difference, range->flags); } + void decrementIndexes(int difference) { decrementIndexes(difference, range->flags); } + + inline void incrementIndexes(int difference, int flags); + inline void decrementIndexes(int difference, int flags); + + void setGroup(Group g) { group = g; groupFlag = 1 << g; } + + Range *range; + int offset; + Group group; + int groupFlag; + int groupCount; + union { + struct { + int cacheIndex; + }; + int index[MaximumGroupCount]; + }; + }; + + class Q_AUTOTEST_EXPORT insert_iterator : public iterator + { + public: + inline insert_iterator() {} + inline insert_iterator(const iterator &it) : iterator(it) {} + inline insert_iterator(Range *, int, Group, int); + inline ~insert_iterator() {} + + insert_iterator &operator +=(int difference); + insert_iterator &operator -=(int difference); + }; + + struct Change + { + inline Change() {} + inline Change(iterator it, int count, int flags, int moveId = -1); + int count; + int flags; + int moveId; + union { + struct { + int cacheIndex; + }; + int index[MaximumGroupCount]; + }; + + inline bool isMove() const { return moveId >= 0; } + inline bool inCache() const { return flags & CacheFlag; } + inline bool inGroup() const { return flags & GroupMask; } + inline bool inGroup(int group) const { return flags & (CacheFlag << group); } + + inline int groups() const { return flags & GroupMask; } + }; + + struct Insert : public Change + { + Insert() {} + Insert(iterator it, int count, int flags, int moveId = -1) + : Change(it, count, flags, moveId) {} + }; + + struct Remove : public Change + { + Remove() {} + Remove(iterator it, int count, int flags, int moveId = -1) + : Change(it, count, flags, moveId) {} + }; + + QDeclarativeListCompositor(); + ~QDeclarativeListCompositor(); + + int defaultGroups() const { return m_defaultFlags & ~PrependFlag; } + void setDefaultGroups(int groups) { m_defaultFlags = groups | PrependFlag; } + void setDefaultGroup(Group group) { m_defaultFlags |= (1 << group); } + void clearDefaultGroup(Group group) { m_defaultFlags &= ~(1 << group); } + void setRemoveGroups(int groups) { m_removeFlags = PrependFlag | AppendFlag | groups; } + void setGroupCount(int count); + + int count(Group group) const; + iterator find(Group group, int index); + iterator find(Group group, int index) const; + insert_iterator findInsertPosition(Group group, int index); + + iterator begin(Group group); + const iterator &end() { return m_end; } + + void append(void *list, int index, int count, int flags, QVector<Insert> *inserts = 0); + void insert(Group group, int before, void *list, int index, int count, int flags, QVector<Insert> *inserts = 0); + iterator insert(iterator before, void *list, int index, int count, int flags, QVector<Insert> *inserts = 0); + + void setFlags(Group group, int index, int count, int flags, QVector<Insert> *inserts = 0); + void setFlags(iterator from, int count, int flags, QVector<Insert> *inserts = 0); + + void clearFlags(Group group, int index, int count, int flags, QVector<Remove> *removals = 0); + void clearFlags(iterator from, int count, int flags, QVector<Remove> *removals = 0); + + void removeList(void *list, QVector<Remove> *removals, bool destroyed); + + bool verifyMoveTo(Group fromGroup, int from, Group toGroup, int to, int count) const; + + void move( + Group fromGroup, + int from, + Group toGroup, + int to, + int count, + QVector<Remove> *removals = 0, + QVector<Insert> *inserts = 0); + void clear(); + + void listItemsInserted(void *list, int index, int count, QVector<Insert> *inserts); + void listItemsRemoved(void *list, int index, int count, QVector<Remove> *removals); + void listItemsMoved(void *list, int from, int to, int count, QVector<Remove> *removals, QVector<Insert> *inserts); + void listItemsChanged(void *list, int index, int count, QVector<Change> *changes); + void listChanged( + void *list, + const QDeclarativeChangeSet &changeSet, + QVector<Remove> *removals, + QVector<Insert> *inserts, + QVector<Change> *changes); + + void transition( + Group from, + Group to, + QVector<QDeclarativeChangeSet::Remove> *removes, + QVector<QDeclarativeChangeSet::Insert> *inserts); + +private: + Range m_ranges; + iterator m_end; + iterator m_cacheIt; + int m_groupCount; + int m_defaultFlags; + int m_removeFlags; + + inline Range *insert(Range *before, void *list, int index, int count, int flags); + inline Range *erase(Range *range); + + struct MovedFlags + { + MovedFlags() {} + MovedFlags(int moveId, int flags) : moveId(moveId), flags(flags) {} + + int moveId; + int flags; + }; + + void listItemsRemoved( + QVector<Remove> *translatedRemovals, + void *list, + QVector<QDeclarativeChangeSet::Remove> *removals, + QVector<QDeclarativeChangeSet::Insert> *insertions = 0, + QVector<MovedFlags> *movedFlags = 0, + int moveId = 0); + void listItemsInserted( + QVector<Insert> *translatedInsertions, + void *list, + const QVector<QDeclarativeChangeSet::Insert> &insertions, + const QVector<MovedFlags> *movedFlags = 0); + void listItemsChanged( + QVector<Change> *translatedChanges, + void *list, + const QVector<QDeclarativeChangeSet::Change> &changes); + + friend Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor &list); +}; + +inline QDeclarativeListCompositor::iterator::iterator() + : range(0), offset(0), group(Default), groupCount(0) {} +inline QDeclarativeListCompositor::iterator::iterator(const iterator &it) + : range(it.range) + , offset(it.offset) + , group(it.group) + , groupFlag(it.groupFlag) + , groupCount(it.groupCount) +{ + for (int i = 0; i < groupCount; ++i) + index[i] = it.index[i]; +} + +inline QDeclarativeListCompositor::iterator::iterator( + Range *range, int offset, Group group, int groupCount) + : range(range) + , offset(offset) + , group(group) + , groupFlag(1 << group) + , groupCount(groupCount) +{ + for (int i = 0; i < groupCount; ++i) + index[i] = 0; +} + +inline void QDeclarativeListCompositor::iterator::incrementIndexes(int difference, int flags) +{ + for (int i = 0; i < groupCount; ++i) { + if (flags & (1 << i)) + index[i] += difference; + } +} + +inline void QDeclarativeListCompositor::iterator::decrementIndexes(int difference, int flags) +{ + for (int i = 0; i < groupCount; ++i) { + if (flags & (1 << i)) + index[i] -= difference; + } +} + +inline QDeclarativeListCompositor::insert_iterator::insert_iterator( + Range *range, int offset, Group group, int groupCount) + : iterator(range, offset, group, groupCount) {} + +inline QDeclarativeListCompositor::Change::Change(iterator it, int count, int flags, int moveId) + : count(count), flags(flags), moveId(moveId) +{ + for (int i = 0; i < MaximumGroupCount; ++i) + index[i] = it.index[i]; +} + +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Group &group); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Range &range); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::iterator &it); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Change &change); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Remove &remove); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Insert &insert); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor &list); + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/util/qdeclarativepackage.cpp b/src/quick/util/qdeclarativepackage.cpp new file mode 100644 index 0000000000..1d46574f02 --- /dev/null +++ b/src/quick/util/qdeclarativepackage.cpp @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepackage_p.h" + +#include <private/qobject_p.h> +#include <private/qdeclarativeguard_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Package QDeclarativePackage + \inqmlmodule QtQuick 2 + \ingroup qml-working-with-data + \brief Package provides a collection of named items. + + The Package class is used in conjunction with + VisualDataModel to enable delegates with a shared context + to be provided to multiple views. + + Any item within a Package may be assigned a name via the + \l{Package::name}{Package.name} attached property. + + The example below creates a Package containing two named items; + \e list and \e grid. The third element in the package (the \l Rectangle) is parented to whichever + delegate it should appear in. This allows an item to move + between views. + + \snippet examples/declarative/modelviews/package/Delegate.qml 0 + + These named items are used as the delegates by the two views who + reference the special \l{VisualDataModel::parts} property to select + a model which provides the chosen delegate. + + \snippet examples/declarative/modelviews/package/view.qml 0 + + \sa {declarative/modelviews/package}{Package example}, {declarative/photoviewer}{Photo Viewer example}, QtDeclarative +*/ + +/*! + \qmlattachedproperty string QtQuick2::Package::name + This attached property holds the name of an item within a Package. +*/ + + +class QDeclarativePackagePrivate : public QObjectPrivate +{ +public: + QDeclarativePackagePrivate() {} + + struct DataGuard : public QDeclarativeGuard<QObject> + { + DataGuard(QObject *obj, QList<DataGuard> *l) : list(l) { (QDeclarativeGuard<QObject>&)*this = obj; } + QList<DataGuard> *list; + void objectDestroyed(QObject *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + + QList<DataGuard> dataList; + static void data_append(QDeclarativeListProperty<QObject> *prop, QObject *o) { + QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); + list->append(DataGuard(o, list)); + } + static void data_clear(QDeclarativeListProperty<QObject> *prop) { + QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); + list->clear(); + } + static QObject *data_at(QDeclarativeListProperty<QObject> *prop, int index) { + QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); + return list->at(index); + } + static int data_count(QDeclarativeListProperty<QObject> *prop) { + QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); + return list->count(); + } +}; + +QHash<QObject *, QDeclarativePackageAttached *> QDeclarativePackageAttached::attached; + +QDeclarativePackageAttached::QDeclarativePackageAttached(QObject *parent) +: QObject(parent) +{ + attached.insert(parent, this); +} + +QDeclarativePackageAttached::~QDeclarativePackageAttached() +{ + attached.remove(parent()); +} + +QString QDeclarativePackageAttached::name() const +{ + return _name; +} + +void QDeclarativePackageAttached::setName(const QString &n) +{ + _name = n; +} + +QDeclarativePackage::QDeclarativePackage(QObject *parent) + : QObject(*(new QDeclarativePackagePrivate), parent) +{ +} + +QDeclarativePackage::~QDeclarativePackage() +{ + Q_D(QDeclarativePackage); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + obj->setParent(this); + } +} + +QDeclarativeListProperty<QObject> QDeclarativePackage::data() +{ + Q_D(QDeclarativePackage); + return QDeclarativeListProperty<QObject>(this, &d->dataList, QDeclarativePackagePrivate::data_append, + QDeclarativePackagePrivate::data_count, + QDeclarativePackagePrivate::data_at, + QDeclarativePackagePrivate::data_clear); +} + +bool QDeclarativePackage::hasPart(const QString &name) +{ + Q_D(QDeclarativePackage); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QDeclarativePackageAttached *a = QDeclarativePackageAttached::attached.value(obj); + if (a && a->name() == name) + return true; + } + return false; +} + +QObject *QDeclarativePackage::part(const QString &name) +{ + Q_D(QDeclarativePackage); + if (name.isEmpty() && !d->dataList.isEmpty()) + return d->dataList.at(0); + + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QDeclarativePackageAttached *a = QDeclarativePackageAttached::attached.value(obj); + if (a && a->name() == name) + return obj; + } + + if (name == QLatin1String("default") && !d->dataList.isEmpty()) + return d->dataList.at(0); + + return 0; +} + +QDeclarativePackageAttached *QDeclarativePackage::qmlAttachedProperties(QObject *o) +{ + return new QDeclarativePackageAttached(o); +} + + + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativepackage_p.h b/src/quick/util/qdeclarativepackage_p.h new file mode 100644 index 0000000000..76797430ce --- /dev/null +++ b/src/quick/util/qdeclarativepackage_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPACKAGE_H +#define QDECLARATIVEPACKAGE_H + +#include <qdeclarative.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativePackagePrivate; +class QDeclarativePackageAttached; +class Q_AUTOTEST_EXPORT QDeclarativePackage : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePackage) + + Q_CLASSINFO("DefaultProperty", "data") + Q_PROPERTY(QDeclarativeListProperty<QObject> data READ data) + +public: + QDeclarativePackage(QObject *parent=0); + virtual ~QDeclarativePackage(); + + QDeclarativeListProperty<QObject> data(); + + QObject *part(const QString & = QString()); + bool hasPart(const QString &); + + static QDeclarativePackageAttached *qmlAttachedProperties(QObject *); +}; + +class QDeclarativePackageAttached : public QObject +{ +Q_OBJECT +Q_PROPERTY(QString name READ name WRITE setName) +public: + QDeclarativePackageAttached(QObject *parent); + virtual ~QDeclarativePackageAttached(); + + QString name() const; + void setName(const QString &n); + + static QHash<QObject *, QDeclarativePackageAttached *> attached; +private: + QString _name; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePackage) +QML_DECLARE_TYPEINFO(QDeclarativePackage, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif // QDECLARATIVEPACKAGE_H diff --git a/src/quick/util/qdeclarativepath.cpp b/src/quick/util/qdeclarativepath.cpp new file mode 100644 index 0000000000..2ba7c372e2 --- /dev/null +++ b/src/quick/util/qdeclarativepath.cpp @@ -0,0 +1,1484 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepath_p.h" +#include "qdeclarativepath_p_p.h" +#include "qdeclarativesvgparser_p.h" + +#include <QSet> +#include <QTime> + +#include <private/qbezier_p.h> +#include <QtCore/qmath.h> +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass PathElement QDeclarativePathElement + \inqmlmodule QtQuick 2 + \ingroup qml-view-elements + \brief PathElement is the base path type. + + This type is the base for all path types. It cannot + be instantiated. + + \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic +*/ + +/*! + \qmlclass Path QDeclarativePath + \inqmlmodule QtQuick 2 + \ingroup qml-view-elements + \brief A Path object defines a path for use by \l PathView. + + A Path is composed of one or more path segments - PathLine, PathQuad, + PathCubic. + + The spacing of the items along the Path can be adjusted via a + PathPercent object. + + PathAttribute allows named attributes with values to be defined + along the path. + + \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic +*/ +QDeclarativePath::QDeclarativePath(QObject *parent) + : QObject(*(new QDeclarativePathPrivate), parent) +{ +} + +QDeclarativePath::~QDeclarativePath() +{ +} + +/*! + \qmlproperty real QtQuick2::Path::startX + \qmlproperty real QtQuick2::Path::startY + These properties hold the starting position of the path. +*/ +qreal QDeclarativePath::startX() const +{ + Q_D(const QDeclarativePath); + return d->startX.isNull ? 0 : d->startX.value; +} + +void QDeclarativePath::setStartX(qreal x) +{ + Q_D(QDeclarativePath); + if (d->startX.isValid() && qFuzzyCompare(x, d->startX)) + return; + d->startX = x; + emit startXChanged(); + processPath(); +} + +bool QDeclarativePath::hasStartX() const +{ + Q_D(const QDeclarativePath); + return d->startX.isValid(); +} + +qreal QDeclarativePath::startY() const +{ + Q_D(const QDeclarativePath); + return d->startY.isNull ? 0 : d->startY.value; +} + +void QDeclarativePath::setStartY(qreal y) +{ + Q_D(QDeclarativePath); + if (d->startY.isValid() && qFuzzyCompare(y, d->startY)) + return; + d->startY = y; + emit startYChanged(); + processPath(); +} + +bool QDeclarativePath::hasStartY() const +{ + Q_D(const QDeclarativePath); + return d->startY.isValid(); +} + +/*! + \qmlproperty bool QtQuick2::Path::closed + This property holds whether the start and end of the path are identical. +*/ +bool QDeclarativePath::isClosed() const +{ + Q_D(const QDeclarativePath); + return d->closed; +} + +bool QDeclarativePath::hasEnd() const +{ + Q_D(const QDeclarativePath); + for (int i = d->_pathElements.count() - 1; i > -1; --i) { + if (QDeclarativeCurve *curve = qobject_cast<QDeclarativeCurve *>(d->_pathElements.at(i))) { + if ((!curve->hasX() && !curve->hasRelativeX()) || (!curve->hasY() && !curve->hasRelativeY())) + return false; + else + return true; + } + } + return hasStartX() && hasStartY(); +} + +/*! + \qmlproperty list<PathElement> QtQuick2::Path::pathElements + This property holds the objects composing the path. + + \default + + A path can contain the following path objects: + \list + \i \l PathLine - a straight line to a given position. + \i \l PathQuad - a quadratic Bezier curve to a given position with a control point. + \i \l PathCubic - a cubic Bezier curve to a given position with two control points. + \i \l PathAttribute - an attribute at a given position in the path. + \i \l PathPercent - a way to spread out items along various segments of the path. + \endlist + + \snippet doc/src/snippets/declarative/pathview/pathattributes.qml 2 +*/ + +QDeclarativeListProperty<QDeclarativePathElement> QDeclarativePath::pathElements() +{ + Q_D(QDeclarativePath); + return QDeclarativeListProperty<QDeclarativePathElement>(this, d->_pathElements); +} + +void QDeclarativePath::interpolate(int idx, const QString &name, qreal value) +{ + Q_D(QDeclarativePath); + interpolate(d->_attributePoints, idx, name, value); +} + +void QDeclarativePath::interpolate(QList<AttributePoint> &attributePoints, int idx, const QString &name, qreal value) +{ + if (!idx) + return; + + qreal lastValue = 0; + qreal lastPercent = 0; + int search = idx - 1; + while(search >= 0) { + const AttributePoint &point = attributePoints.at(search); + if (point.values.contains(name)) { + lastValue = point.values.value(name); + lastPercent = point.origpercent; + break; + } + --search; + } + + ++search; + + const AttributePoint &curPoint = attributePoints.at(idx); + + for (int ii = search; ii < idx; ++ii) { + AttributePoint &point = attributePoints[ii]; + + qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent); + point.values.insert(name, val); + } +} + +void QDeclarativePath::endpoint(const QString &name) +{ + Q_D(QDeclarativePath); + const AttributePoint &first = d->_attributePoints.first(); + qreal val = first.values.value(name); + for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) { + const AttributePoint &point = d->_attributePoints.at(ii); + if (point.values.contains(name)) { + for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) { + AttributePoint &setPoint = d->_attributePoints[jj]; + setPoint.values.insert(name, val); + } + return; + } + } +} + +void QDeclarativePath::endpoint(QList<AttributePoint> &attributePoints, const QString &name) +{ + const AttributePoint &first = attributePoints.first(); + qreal val = first.values.value(name); + for (int ii = attributePoints.count() - 1; ii >= 0; ii--) { + const AttributePoint &point = attributePoints.at(ii); + if (point.values.contains(name)) { + for (int jj = ii + 1; jj < attributePoints.count(); ++jj) { + AttributePoint &setPoint = attributePoints[jj]; + setPoint.values.insert(name, val); + } + return; + } + } +} + +static QString percentString(QLatin1String("_qfx_percent")); + +void QDeclarativePath::processPath() +{ + Q_D(QDeclarativePath); + + if (!d->componentComplete) + return; + + d->_pointCache.clear(); + d->prevBez.isValid = false; + + d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed); + + emit changed(); +} + +QPainterPath QDeclarativePath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed) +{ + Q_D(QDeclarativePath); + + pathLength = 0; + attributePoints.clear(); + + if (!d->componentComplete) + return QPainterPath(); + + QPainterPath path; + + AttributePoint first; + for (int ii = 0; ii < attributes.count(); ++ii) + first.values[attributes.at(ii)] = 0; + attributePoints << first; + + qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x(); + qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y(); + path.moveTo(startX, startY); + + bool usesPercent = false; + int index = 0; + foreach (QDeclarativePathElement *pathElement, d->_pathElements) { + if (QDeclarativeCurve *curve = qobject_cast<QDeclarativeCurve *>(pathElement)) { + QDeclarativePathData data; + data.index = index; + data.endPoint = endPoint; + data.curves = d->_pathCurves; + curve->addToPath(path, data); + AttributePoint p; + p.origpercent = path.length(); + attributePoints << p; + ++index; + } else if (QDeclarativePathAttribute *attribute = qobject_cast<QDeclarativePathAttribute *>(pathElement)) { + AttributePoint &point = attributePoints.last(); + point.values[attribute->name()] = attribute->value(); + interpolate(attributePoints, attributePoints.count() - 1, attribute->name(), attribute->value()); + } else if (QDeclarativePathPercent *percent = qobject_cast<QDeclarativePathPercent *>(pathElement)) { + AttributePoint &point = attributePoints.last(); + point.values[percentString] = percent->value(); + interpolate(attributePoints, attributePoints.count() - 1, percentString, percent->value()); + usesPercent = true; + } + } + + // Fixup end points + const AttributePoint &last = attributePoints.last(); + for (int ii = 0; ii < attributes.count(); ++ii) { + if (!last.values.contains(attributes.at(ii))) + endpoint(attributePoints, attributes.at(ii)); + } + if (usesPercent && !last.values.contains(percentString)) { + d->_attributePoints.last().values[percentString] = 1; + interpolate(d->_attributePoints.count() - 1, percentString, 1); + } + + + // Adjust percent + qreal length = path.length(); + qreal prevpercent = 0; + qreal prevorigpercent = 0; + for (int ii = 0; ii < attributePoints.count(); ++ii) { + const AttributePoint &point = attributePoints.at(ii); + if (point.values.contains(percentString)) { //special string for QDeclarativePathPercent + if ( ii > 0) { + qreal scale = (attributePoints[ii].origpercent/length - prevorigpercent) / + (point.values.value(percentString)-prevpercent); + attributePoints[ii].scale = scale; + } + attributePoints[ii].origpercent /= length; + attributePoints[ii].percent = point.values.value(percentString); + prevorigpercent = attributePoints[ii].origpercent; + prevpercent = attributePoints[ii].percent; + } else { + attributePoints[ii].origpercent /= length; + attributePoints[ii].percent = attributePoints[ii].origpercent; + } + } + + if (closed) { + QPointF end = path.currentPosition(); + *closed = length > 0 && startX == end.x() && startY == end.y(); + } + pathLength = length; + + return path; +} + +void QDeclarativePath::classBegin() +{ + Q_D(QDeclarativePath); + d->componentComplete = false; +} + +void QDeclarativePath::componentComplete() +{ + Q_D(QDeclarativePath); + QSet<QString> attrs; + d->componentComplete = true; + + // First gather up all the attributes + foreach (QDeclarativePathElement *pathElement, d->_pathElements) { + if (QDeclarativeCurve *curve = + qobject_cast<QDeclarativeCurve *>(pathElement)) + d->_pathCurves.append(curve); + else if (QDeclarativePathAttribute *attribute = + qobject_cast<QDeclarativePathAttribute *>(pathElement)) + attrs.insert(attribute->name()); + } + d->_attributes = attrs.toList(); + + processPath(); + + foreach (QDeclarativePathElement *pathElement, d->_pathElements) + connect(pathElement, SIGNAL(changed()), this, SLOT(processPath())); +} + +QPainterPath QDeclarativePath::path() const +{ + Q_D(const QDeclarativePath); + return d->_path; +} + +QStringList QDeclarativePath::attributes() const +{ + Q_D(const QDeclarativePath); + if (!d->componentComplete) { + QSet<QString> attrs; + + // First gather up all the attributes + foreach (QDeclarativePathElement *pathElement, d->_pathElements) { + if (QDeclarativePathAttribute *attribute = + qobject_cast<QDeclarativePathAttribute *>(pathElement)) + attrs.insert(attribute->name()); + } + return attrs.toList(); + } + return d->_attributes; +} + +static inline QBezier nextBezier(const QPainterPath &path, int *current, qreal *bezLength, bool reverse = false) +{ + const int lastElement = reverse ? 0 : path.elementCount() - 1; + const int start = reverse ? *current - 1 : *current + 1; + for (int i=start; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) { + const QPainterPath::Element &e = path.elementAt(i); + + switch (e.type) { + case QPainterPath::MoveToElement: + break; + case QPainterPath::LineToElement: + { + QLineF line(path.elementAt(i-1), e); + *bezLength = line.length(); + QPointF a = path.elementAt(i-1); + QPointF delta = e - a; + *current = i; + return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e); + } + case QPainterPath::CurveToElement: + { + QBezier b = QBezier::fromPoints(path.elementAt(i-1), + e, + path.elementAt(i+1), + path.elementAt(i+2)); + *bezLength = b.length(); + *current = i; + return b; + } + default: + break; + } + } + *current = lastElement; + *bezLength = 0; + return QBezier(); +} + +//derivative of the equation +static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d) +{ + return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a); +} + +void QDeclarativePath::createPointCache() const +{ + Q_D(const QDeclarativePath); + qreal pathLength = d->pathLength; + if (pathLength <= 0 || qIsNaN(pathLength)) + return; + // more points means less jitter between items as they move along the + // path, but takes longer to generate + const int points = qCeil(pathLength*5); + const int lastElement = d->_path.elementCount() - 1; + d->_pointCache.resize(points+1); + + int currElement = -1; + qreal bezLength = 0; + QBezier currBez = nextBezier(d->_path, &currElement, &bezLength); + qreal currLength = bezLength; + qreal epc = currLength / pathLength; + + for (int i = 0; i < d->_pointCache.size(); i++) { + //find which set we are in + qreal prevPercent = 0; + qreal prevOrigPercent = 0; + for (int ii = 0; ii < d->_attributePoints.count(); ++ii) { + qreal percent = qreal(i)/points; + const AttributePoint &point = d->_attributePoints.at(ii); + if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item + qreal elementPercent = (percent - prevPercent); + + qreal spc = prevOrigPercent + elementPercent * point.scale; + + while (spc > epc) { + if (currElement > lastElement) + break; + currBez = nextBezier(d->_path, &currElement, &bezLength); + if (bezLength == 0.0) { + currLength = pathLength; + epc = 1.0; + break; + } + currLength += bezLength; + epc = currLength / pathLength; + } + qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength; + d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1))); + break; + } + prevOrigPercent = point.origpercent; + prevPercent = point.percent; + } + } +} + +QPointF QDeclarativePath::sequentialPointAt(qreal p, qreal *angle) const +{ + Q_D(const QDeclarativePath); + return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle); +} + +QPointF QDeclarativePath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle) +{ + if (!prevBez.isValid) + return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) : + forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle); + + return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) : + forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle); +} + +QPointF QDeclarativePath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle) +{ + if (pathLength <= 0 || qIsNaN(pathLength)) + return path.pointAtPercent(0); //expensive? + + const int lastElement = path.elementCount() - 1; + bool haveCachedBez = prevBez.isValid; + int currElement = haveCachedBez ? prevBez.element : -1; + qreal bezLength = haveCachedBez ? prevBez.bezLength : 0; + QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength); + qreal currLength = haveCachedBez ? prevBez.currLength : bezLength; + qreal epc = currLength / pathLength; + + //find which set we are in + qreal prevPercent = 0; + qreal prevOrigPercent = 0; + for (int ii = 0; ii < attributePoints.count(); ++ii) { + qreal percent = p; + const AttributePoint &point = attributePoints.at(ii); + if (percent < point.percent || ii == attributePoints.count() - 1) { + qreal elementPercent = (percent - prevPercent); + + qreal spc = prevOrigPercent + elementPercent * point.scale; + + while (spc > epc) { + Q_ASSERT(!(currElement > lastElement)); + Q_UNUSED(lastElement); + currBez = nextBezier(path, &currElement, &bezLength); + currLength += bezLength; + epc = currLength / pathLength; + } + prevBez.element = currElement; + prevBez.bezLength = bezLength; + prevBez.currLength = currLength; + prevBez.bezier = currBez; + prevBez.p = p; + prevBez.isValid = true; + + qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength; + + if (angle) { + qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4); + qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4); + *angle = QLineF(0, 0, m1, m2).angle(); + } + + return currBez.pointAt(qBound(qreal(0), realT, qreal(1))); + } + prevOrigPercent = point.origpercent; + prevPercent = point.percent; + } + + return QPointF(0,0); +} + +//ideally this should be merged with forwardsPointAt +QPointF QDeclarativePath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle) +{ + if (pathLength <= 0 || qIsNaN(pathLength)) + return path.pointAtPercent(0); + + const int firstElement = 0; + bool haveCachedBez = prevBez.isValid; + int currElement = haveCachedBez ? prevBez.element : path.elementCount(); + qreal bezLength = haveCachedBez ? prevBez.bezLength : 0; + QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength, true /*reverse*/); + qreal currLength = haveCachedBez ? prevBez.currLength : pathLength; + qreal prevLength = currLength - bezLength; + qreal epc = prevLength / pathLength; + + for (int ii = attributePoints.count() - 1; ii > 0; --ii) { + qreal percent = p; + const AttributePoint &point = attributePoints.at(ii); + const AttributePoint &prevPoint = attributePoints.at(ii-1); + if (percent > prevPoint.percent || ii == 1) { + qreal elementPercent = (percent - prevPoint.percent); + + qreal spc = prevPoint.origpercent + elementPercent * point.scale; + + while (spc < epc) { + Q_ASSERT(!(currElement < firstElement)); + Q_UNUSED(firstElement); + currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/); + currLength = prevLength; + prevLength = currLength - bezLength; + epc = prevLength / pathLength; + } + prevBez.element = currElement; + prevBez.bezLength = bezLength; + prevBez.currLength = currLength; + prevBez.bezier = currBez; + prevBez.p = p; + prevBez.isValid = true; + + qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength; + + if (angle) { + qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4); + qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4); + *angle = QLineF(0, 0, m1, m2).angle(); + } + + return currBez.pointAt(qBound(qreal(0), realT, qreal(1))); + } + } + + return QPointF(0,0); +} + +QPointF QDeclarativePath::pointAt(qreal p) const +{ + Q_D(const QDeclarativePath); + if (d->_pointCache.isEmpty()) { + createPointCache(); + if (d->_pointCache.isEmpty()) + return QPointF(); + } + int idx = qRound(p*d->_pointCache.size()); + if (idx >= d->_pointCache.size()) + idx = d->_pointCache.size() - 1; + else if (idx < 0) + idx = 0; + return d->_pointCache.at(idx); +} + +qreal QDeclarativePath::attributeAt(const QString &name, qreal percent) const +{ + Q_D(const QDeclarativePath); + if (percent < 0 || percent > 1) + return 0; + + for (int ii = 0; ii < d->_attributePoints.count(); ++ii) { + const AttributePoint &point = d->_attributePoints.at(ii); + + if (point.percent == percent) { + return point.values.value(name); + } else if (point.percent > percent) { + qreal lastValue = + ii?(d->_attributePoints.at(ii - 1).values.value(name)):0; + qreal lastPercent = + ii?(d->_attributePoints.at(ii - 1).percent):0; + qreal curValue = point.values.value(name); + qreal curPercent = point.percent; + + return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent); + } + } + + return 0; +} + +/****************************************************************************/ + +qreal QDeclarativeCurve::x() const +{ + return _x.isNull ? 0 : _x.value; +} + +void QDeclarativeCurve::setX(qreal x) +{ + if (_x.isNull || _x != x) { + _x = x; + emit xChanged(); + emit changed(); + } +} + +bool QDeclarativeCurve::hasX() +{ + return _x.isValid(); +} + +qreal QDeclarativeCurve::y() const +{ + return _y.isNull ? 0 : _y.value; +} + +void QDeclarativeCurve::setY(qreal y) +{ + if (_y.isNull || _y != y) { + _y = y; + emit yChanged(); + emit changed(); + } +} + +bool QDeclarativeCurve::hasY() +{ + return _y.isValid(); +} + +qreal QDeclarativeCurve::relativeX() const +{ + return _relativeX; +} + +void QDeclarativeCurve::setRelativeX(qreal x) +{ + if (_relativeX.isNull || _relativeX != x) { + _relativeX = x; + emit relativeXChanged(); + emit changed(); + } +} + +bool QDeclarativeCurve::hasRelativeX() +{ + return _relativeX.isValid(); +} + +qreal QDeclarativeCurve::relativeY() const +{ + return _relativeY; +} + +void QDeclarativeCurve::setRelativeY(qreal y) +{ + if (_relativeY.isNull || _relativeY != y) { + _relativeY = y; + emit relativeYChanged(); + emit changed(); + } +} + +bool QDeclarativeCurve::hasRelativeY() +{ + return _relativeY.isValid(); +} + +/****************************************************************************/ + +/*! + \qmlclass PathAttribute QDeclarativePathAttribute + \inqmlmodule QtQuick 2 + \ingroup qml-view-elements + \brief The PathAttribute allows setting an attribute at a given position in a Path. + + The PathAttribute object allows attributes consisting of a name and + a value to be specified for various points along a path. The + attributes are exposed to the delegate as + \l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}. + The value of an attribute at any particular point along the path is interpolated + from the PathAttributes bounding that point. + + The example below shows a path with the items scaled to 30% with + opacity 50% at the top of the path and scaled 100% with opacity + 100% at the bottom. Note the use of the PathView.iconScale and + PathView.iconOpacity attached properties to set the scale and opacity + of the delegate. + + \table + \row + \o \image declarative-pathattribute.png + \o + \snippet doc/src/snippets/declarative/pathview/pathattributes.qml 0 + (see the PathView documentation for the specification of ContactModel.qml + used for ContactModel above.) + \endtable + + + \sa Path +*/ + +/*! + \qmlproperty string QtQuick2::PathAttribute::name + This property holds the name of the attribute to change. + + This attribute will be available to the delegate as PathView.<name> + + Note that using an existing Item property name such as "opacity" as an + attribute is allowed. This is because path attributes add a new + \l{qdeclarativeintroduction.html#attached-properties} {Attached Property} + which in no way clashes with existing properties. +*/ + +/*! + the name of the attribute to change. +*/ + +QString QDeclarativePathAttribute::name() const +{ + return _name; +} + +void QDeclarativePathAttribute::setName(const QString &name) +{ + if (_name == name) + return; + _name = name; + emit nameChanged(); +} + +/*! + \qmlproperty real QtQuick2::PathAttribute::value + This property holds the value for the attribute. + + The value specified can be used to influence the visual appearance + of an item along the path. For example, the following Path specifies + an attribute named \e itemRotation, which has the value \e 0 at the + beginning of the path, and the value 90 at the end of the path. + + \qml + Path { + startX: 0 + startY: 0 + PathAttribute { name: "itemRotation"; value: 0 } + PathLine { x: 100; y: 100 } + PathAttribute { name: "itemRotation"; value: 90 } + } + \endqml + + In our delegate, we can then bind the \e rotation property to the + \l{qdeclarativeintroduction.html#attached-properties} {Attached Property} + \e PathView.itemRotation created for this attribute. + + \qml + Rectangle { + width: 10; height: 10 + rotation: PathView.itemRotation + } + \endqml + + As each item is positioned along the path, it will be rotated accordingly: + an item at the beginning of the path with be not be rotated, an item at + the end of the path will be rotated 90 degrees, and an item mid-way along + the path will be rotated 45 degrees. +*/ + +/*! + the new value of the attribute. +*/ +qreal QDeclarativePathAttribute::value() const +{ + return _value; +} + +void QDeclarativePathAttribute::setValue(qreal value) +{ + if (_value != value) { + _value = value; + emit valueChanged(); + emit changed(); + } +} + +/****************************************************************************/ + +/*! + \qmlclass PathLine QDeclarativePathLine + \inqmlmodule QtQuick 2 + \ingroup qml-view-elements + \brief The PathLine defines a straight line. + + The example below creates a path consisting of a straight line from + 0,100 to 200,100: + + \qml + Path { + startX: 0; startY: 100 + PathLine { x: 200; y: 100 } + } + \endqml + + \sa Path, PathQuad, PathCubic +*/ + +/*! + \qmlproperty real QtQuick2::PathLine::x + \qmlproperty real QtQuick2::PathLine::y + + Defines the end point of the line. +*/ + +inline QPointF positionForCurve(const QDeclarativePathData &data, const QPointF &prevPoint) +{ + QDeclarativeCurve *curve = data.curves.at(data.index); + bool isEnd = data.index == data.curves.size() - 1; + return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(), + curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y()); +} + +void QDeclarativePathLine::addToPath(QPainterPath &path, const QDeclarativePathData &data) +{ + path.lineTo(positionForCurve(data, path.currentPosition())); +} + +/****************************************************************************/ + +/*! + \qmlclass PathQuad QDeclarativePathQuad + \inqmlmodule QtQuick 2 + \ingroup qml-view-elements + \brief The PathQuad defines a quadratic Bezier curve with a control point. + + The following QML produces the path shown below: + \table + \row + \o \image declarative-pathquad.png + \o + \qml + Path { + startX: 0; startY: 0 + PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 } + } + \endqml + \endtable + + \sa Path, PathCubic, PathLine +*/ + +/*! + \qmlproperty real QtQuick2::PathQuad::x + \qmlproperty real QtQuick2::PathQuad::y + + Defines the end point of the curve. +*/ + +/*! + \qmlproperty real QtQuick2::PathQuad::controlX + \qmlproperty real QtQuick2::PathQuad::controlY + + Defines the position of the control point. +*/ + +/*! + the x position of the control point. +*/ +qreal QDeclarativePathQuad::controlX() const +{ + return _controlX; +} + +void QDeclarativePathQuad::setControlX(qreal x) +{ + if (_controlX != x) { + _controlX = x; + emit controlXChanged(); + emit changed(); + } +} + + +/*! + the y position of the control point. +*/ +qreal QDeclarativePathQuad::controlY() const +{ + return _controlY; +} + +void QDeclarativePathQuad::setControlY(qreal y) +{ + if (_controlY != y) { + _controlY = y; + emit controlYChanged(); + emit changed(); + } +} + +qreal QDeclarativePathQuad::relativeControlX() const +{ + return _relativeControlX; +} + +void QDeclarativePathQuad::setRelativeControlX(qreal x) +{ + if (_relativeControlX.isNull || _relativeControlX != x) { + _relativeControlX = x; + emit relativeControlXChanged(); + emit changed(); + } +} + +bool QDeclarativePathQuad::hasRelativeControlX() +{ + return _relativeControlX.isValid(); +} + +qreal QDeclarativePathQuad::relativeControlY() const +{ + return _relativeControlY; +} + +void QDeclarativePathQuad::setRelativeControlY(qreal y) +{ + if (_relativeControlY.isNull || _relativeControlY != y) { + _relativeControlY = y; + emit relativeControlYChanged(); + emit changed(); + } +} + +bool QDeclarativePathQuad::hasRelativeControlY() +{ + return _relativeControlY.isValid(); +} + +void QDeclarativePathQuad::addToPath(QPainterPath &path, const QDeclarativePathData &data) +{ + const QPointF &prevPoint = path.currentPosition(); + QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(), + hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY()); + path.quadTo(controlPoint, positionForCurve(data, path.currentPosition())); +} + +/****************************************************************************/ + +/*! + \qmlclass PathCubic QDeclarativePathCubic + \inqmlmodule QtQuick 2 + \ingroup qml-view-elements + \brief The PathCubic defines a cubic Bezier curve with two control points. + + The following QML produces the path shown below: + \table + \row + \o \image declarative-pathcubic.png + \o + \qml + Path { + startX: 20; startY: 0 + PathCubic { + x: 180; y: 0 + control1X: -10; control1Y: 90 + control2X: 210; control2Y: 90 + } + } + \endqml + \endtable + + \sa Path, PathQuad, PathLine +*/ + +/*! + \qmlproperty real QtQuick2::PathCubic::x + \qmlproperty real QtQuick2::PathCubic::y + + Defines the end point of the curve. +*/ + +/*! + \qmlproperty real QtQuick2::PathCubic::control1X + \qmlproperty real QtQuick2::PathCubic::control1Y + + Defines the position of the first control point. +*/ +qreal QDeclarativePathCubic::control1X() const +{ + return _control1X; +} + +void QDeclarativePathCubic::setControl1X(qreal x) +{ + if (_control1X != x) { + _control1X = x; + emit control1XChanged(); + emit changed(); + } +} + +qreal QDeclarativePathCubic::control1Y() const +{ + return _control1Y; +} + +void QDeclarativePathCubic::setControl1Y(qreal y) +{ + if (_control1Y != y) { + _control1Y = y; + emit control1YChanged(); + emit changed(); + } +} + +/*! + \qmlproperty real QtQuick2::PathCubic::control2X + \qmlproperty real QtQuick2::PathCubic::control2Y + + Defines the position of the second control point. +*/ +qreal QDeclarativePathCubic::control2X() const +{ + return _control2X; +} + +void QDeclarativePathCubic::setControl2X(qreal x) +{ + if (_control2X != x) { + _control2X = x; + emit control2XChanged(); + emit changed(); + } +} + +qreal QDeclarativePathCubic::control2Y() const +{ + return _control2Y; +} + +void QDeclarativePathCubic::setControl2Y(qreal y) +{ + if (_control2Y != y) { + _control2Y = y; + emit control2YChanged(); + emit changed(); + } +} + +qreal QDeclarativePathCubic::relativeControl1X() const +{ + return _relativeControl1X; +} + +void QDeclarativePathCubic::setRelativeControl1X(qreal x) +{ + if (_relativeControl1X.isNull || _relativeControl1X != x) { + _relativeControl1X = x; + emit relativeControl1XChanged(); + emit changed(); + } +} + +bool QDeclarativePathCubic::hasRelativeControl1X() +{ + return _relativeControl1X.isValid(); +} + +qreal QDeclarativePathCubic::relativeControl1Y() const +{ + return _relativeControl1Y; +} + +void QDeclarativePathCubic::setRelativeControl1Y(qreal y) +{ + if (_relativeControl1Y.isNull || _relativeControl1Y != y) { + _relativeControl1Y = y; + emit relativeControl1YChanged(); + emit changed(); + } +} + +bool QDeclarativePathCubic::hasRelativeControl1Y() +{ + return _relativeControl1Y.isValid(); +} + +qreal QDeclarativePathCubic::relativeControl2X() const +{ + return _relativeControl2X; +} + +void QDeclarativePathCubic::setRelativeControl2X(qreal x) +{ + if (_relativeControl2X.isNull || _relativeControl2X != x) { + _relativeControl2X = x; + emit relativeControl2XChanged(); + emit changed(); + } +} + +bool QDeclarativePathCubic::hasRelativeControl2X() +{ + return _relativeControl2X.isValid(); +} + +qreal QDeclarativePathCubic::relativeControl2Y() const +{ + return _relativeControl2Y; +} + +void QDeclarativePathCubic::setRelativeControl2Y(qreal y) +{ + if (_relativeControl2Y.isNull || _relativeControl2Y != y) { + _relativeControl2Y = y; + emit relativeControl2YChanged(); + emit changed(); + } +} + +bool QDeclarativePathCubic::hasRelativeControl2Y() +{ + return _relativeControl2Y.isValid(); +} + +void QDeclarativePathCubic::addToPath(QPainterPath &path, const QDeclarativePathData &data) +{ + const QPointF &prevPoint = path.currentPosition(); + QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(), + hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y()); + QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(), + hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y()); + path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition())); +} + +/****************************************************************************/ + +inline QPointF previousPathPosition(const QPainterPath &path) +{ + int count = path.elementCount(); + if (count < 1) + return QPointF(); + + int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2; + return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0); +} + +void QDeclarativePathCatmullRomCurve::addToPath(QPainterPath &path, const QDeclarativePathData &data) +{ + //here we convert catmull-rom spline to bezier for use in QPainterPath. + //basic conversion algorithm: + // catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points + //each point in the catmull-rom spline produces a bezier endpoint + 2 control points + //calculations for each point use a moving window of 4 points + // (previous 2 points + current point + next point) + QPointF prevFar, prev, point, next; + + //get previous points + int index = data.index - 1; + QDeclarativeCurve *curve = index == -1 ? 0 : data.curves.at(index); + if (qobject_cast<QDeclarativePathCatmullRomCurve*>(curve)) { + prev = path.currentPosition(); + prevFar = previousPathPosition(path); + } else + prevFar = prev = path.currentPosition(); + + //get current point + point = positionForCurve(data, path.currentPosition()); + + //get next point + index = data.index + 1; + if (index < data.curves.count() && qobject_cast<QDeclarativePathCatmullRomCurve*>(data.curves.at(index))) { + QDeclarativePathData nextData; + nextData.index = index; + nextData.endPoint = data.endPoint; + nextData.curves = data.curves; + next = positionForCurve(nextData, point); + } else + next = point; + + /* + full conversion matrix (inverse bezier * catmull-rom): + 0.000, 1.000, 0.000, 0.000, + -0.167, 1.000, 0.167, 0.000, + 0.000, 0.167, 1.000, -0.167, + 0.000, 0.000, 1.000, 0.000 + + conversion doesn't require full matrix multiplication, + so below we simplify + */ + QPointF control1(prevFar.x() * qreal(-0.167) + + prev.x() + + point.x() * qreal(0.167), + prevFar.y() * qreal(-0.167) + + prev.y() + + point.y() * qreal(0.167)); + + QPointF control2(prev.x() * qreal(0.167) + + point.x() + + next.x() * qreal(-0.167), + prev.y() * qreal(0.167) + + point.y() + + next.y() * qreal(-0.167)); + + path.cubicTo(control1, control2, point); +} + +/****************************************************************************/ + +qreal QDeclarativePathArc::radiusX() const +{ + return _radiusX; +} + +void QDeclarativePathArc::setRadiusX(qreal radius) +{ + if (_radiusX == radius) + return; + + _radiusX = radius; + emit radiusXChanged(); +} + +qreal QDeclarativePathArc::radiusY() const +{ + return _radiusY; +} + +void QDeclarativePathArc::setRadiusY(qreal radius) +{ + if (_radiusY == radius) + return; + + _radiusY = radius; + emit radiusYChanged(); +} + +bool QDeclarativePathArc::useLargeArc() const +{ + return _useLargeArc; +} + +void QDeclarativePathArc::setUseLargeArc(bool largeArc) +{ + if (_useLargeArc == largeArc) + return; + + _useLargeArc = largeArc; + emit useLargeArcChanged(); +} + +QDeclarativePathArc::ArcDirection QDeclarativePathArc::direction() const +{ + return _direction; +} + +void QDeclarativePathArc::setDirection(ArcDirection direction) +{ + if (_direction == direction) + return; + + _direction = direction; + emit directionChanged(); +} + +void QDeclarativePathArc::addToPath(QPainterPath &path, const QDeclarativePathData &data) +{ + const QPointF &startPoint = path.currentPosition(); + const QPointF &endPoint = positionForCurve(data, startPoint); + QDeclarativeSvgParser::pathArc(path, + _radiusX, + _radiusY, + 0, //xAxisRotation + _useLargeArc, + _direction == Clockwise ? 1 : 0, + endPoint.x(), + endPoint.y(), + startPoint.x(), startPoint.y()); +} + +/****************************************************************************/ + +QString QDeclarativePathSvg::path() const +{ + return _path; +} + +void QDeclarativePathSvg::setPath(const QString &path) +{ + if (_path == path) + return; + + _path = path; + emit pathChanged(); +} + +void QDeclarativePathSvg::addToPath(QPainterPath &path, const QDeclarativePathData &) +{ + QDeclarativeSvgParser::parsePathDataFast(_path, path); +} + +/****************************************************************************/ + +/*! + \qmlclass PathPercent QDeclarativePathPercent + \inqmlmodule QtQuick 2 + \ingroup qml-view-elements + \brief The PathPercent manipulates the way a path is interpreted. + + PathPercent allows you to manipulate the spacing between items on a + PathView's path. You can use it to bunch together items on part of + the path, and spread them out on other parts of the path. + + The examples below show the normal distribution of items along a path + compared to a distribution which places 50% of the items along the + PathLine section of the path. + \table + \row + \o \image declarative-nopercent.png + \o + \qml + PathView { + // ... + Path { + startX: 20; startY: 0 + PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 } + PathLine { x: 150; y: 80 } + PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 } + } + } + \endqml + \row + \o \image declarative-percent.png + \o + \qml + PathView { + // ... + Path { + startX: 20; startY: 0 + PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 } + PathPercent { value: 0.25 } + PathLine { x: 150; y: 80 } + PathPercent { value: 0.75 } + PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 } + PathPercent { value: 1 } + } + } + \endqml + \endtable + + \sa Path +*/ + +/*! + \qmlproperty real QtQuick2::PathPercent::value + The proportion of items that should be laid out up to this point. + + This value should always be higher than the last value specified + by a PathPercent at a previous position in the Path. + + In the following example we have a Path made up of three PathLines. + Normally, the items of the PathView would be laid out equally along + this path, with an equal number of items per line segment. PathPercent + allows us to specify that the first and third lines should each hold + 10% of the laid out items, while the second line should hold the remaining + 80%. + + \qml + PathView { + // ... + Path { + startX: 0; startY: 0 + PathLine { x:100; y: 0; } + PathPercent { value: 0.1 } + PathLine { x: 100; y: 100 } + PathPercent { value: 0.9 } + PathLine { x: 100; y: 0 } + PathPercent { value: 1 } + } + } + \endqml +*/ + +qreal QDeclarativePathPercent::value() const +{ + return _value; +} + +void QDeclarativePathPercent::setValue(qreal value) +{ + if (_value != value) { + _value = value; + emit valueChanged(); + emit changed(); + } +} +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativepath_p.h b/src/quick/util/qdeclarativepath_p.h new file mode 100644 index 0000000000..afa5b0240e --- /dev/null +++ b/src/quick/util/qdeclarativepath_p.h @@ -0,0 +1,450 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPATH_H +#define QDECLARATIVEPATH_H + +#include <qdeclarative.h> + +#include <private/qdeclarativenullablevalue_p_p.h> +#include <private/qbezier_p.h> + +#include <QtCore/QObject> +#include <QtGui/QPainterPath> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeCurve; +struct QDeclarativePathData +{ + int index; + QPointF endPoint; + QList<QDeclarativeCurve*> curves; +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathElement : public QObject +{ + Q_OBJECT +public: + QDeclarativePathElement(QObject *parent=0) : QObject(parent) {} +Q_SIGNALS: + void changed(); +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathAttribute : public QDeclarativePathElement +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged) +public: + QDeclarativePathAttribute(QObject *parent=0) : QDeclarativePathElement(parent), _value(0) {} + + + QString name() const; + void setName(const QString &name); + + qreal value() const; + void setValue(qreal value); + +Q_SIGNALS: + void nameChanged(); + void valueChanged(); + +private: + QString _name; + qreal _value; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeCurve : public QDeclarativePathElement +{ + Q_OBJECT + + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) + Q_PROPERTY(qreal relativeX READ relativeX WRITE setRelativeX NOTIFY relativeXChanged) + Q_PROPERTY(qreal relativeY READ relativeY WRITE setRelativeY NOTIFY relativeYChanged) +public: + QDeclarativeCurve(QObject *parent=0) : QDeclarativePathElement(parent) {} + + qreal x() const; + void setX(qreal x); + bool hasX(); + + qreal y() const; + void setY(qreal y); + bool hasY(); + + qreal relativeX() const; + void setRelativeX(qreal x); + bool hasRelativeX(); + + qreal relativeY() const; + void setRelativeY(qreal y); + bool hasRelativeY(); + + virtual void addToPath(QPainterPath &, const QDeclarativePathData &) {} + +Q_SIGNALS: + void xChanged(); + void yChanged(); + void relativeXChanged(); + void relativeYChanged(); + +private: + QDeclarativeNullableValue<qreal> _x; + QDeclarativeNullableValue<qreal> _y; + QDeclarativeNullableValue<qreal> _relativeX; + QDeclarativeNullableValue<qreal> _relativeY; +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathLine : public QDeclarativeCurve +{ + Q_OBJECT +public: + QDeclarativePathLine(QObject *parent=0) : QDeclarativeCurve(parent) {} + + void addToPath(QPainterPath &path, const QDeclarativePathData &); +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathQuad : public QDeclarativeCurve +{ + Q_OBJECT + + Q_PROPERTY(qreal controlX READ controlX WRITE setControlX NOTIFY controlXChanged) + Q_PROPERTY(qreal controlY READ controlY WRITE setControlY NOTIFY controlYChanged) + Q_PROPERTY(qreal relativeControlX READ relativeControlX WRITE setRelativeControlX NOTIFY relativeControlXChanged) + Q_PROPERTY(qreal relativeControlY READ relativeControlY WRITE setRelativeControlY NOTIFY relativeControlYChanged) +public: + QDeclarativePathQuad(QObject *parent=0) : QDeclarativeCurve(parent), _controlX(0), _controlY(0) {} + + qreal controlX() const; + void setControlX(qreal x); + + qreal controlY() const; + void setControlY(qreal y); + + qreal relativeControlX() const; + void setRelativeControlX(qreal x); + bool hasRelativeControlX(); + + qreal relativeControlY() const; + void setRelativeControlY(qreal y); + bool hasRelativeControlY(); + + void addToPath(QPainterPath &path, const QDeclarativePathData &); + +Q_SIGNALS: + void controlXChanged(); + void controlYChanged(); + void relativeControlXChanged(); + void relativeControlYChanged(); + +private: + qreal _controlX; + qreal _controlY; + QDeclarativeNullableValue<qreal> _relativeControlX; + QDeclarativeNullableValue<qreal> _relativeControlY; +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathCubic : public QDeclarativeCurve +{ + Q_OBJECT + + Q_PROPERTY(qreal control1X READ control1X WRITE setControl1X NOTIFY control1XChanged) + Q_PROPERTY(qreal control1Y READ control1Y WRITE setControl1Y NOTIFY control1YChanged) + Q_PROPERTY(qreal control2X READ control2X WRITE setControl2X NOTIFY control2XChanged) + Q_PROPERTY(qreal control2Y READ control2Y WRITE setControl2Y NOTIFY control2YChanged) + Q_PROPERTY(qreal relativeControl1X READ relativeControl1X WRITE setRelativeControl1X NOTIFY relativeControl1XChanged) + Q_PROPERTY(qreal relativeControl1Y READ relativeControl1Y WRITE setRelativeControl1Y NOTIFY relativeControl1YChanged) + Q_PROPERTY(qreal relativeControl2X READ relativeControl2X WRITE setRelativeControl2X NOTIFY relativeControl2XChanged) + Q_PROPERTY(qreal relativeControl2Y READ relativeControl2Y WRITE setRelativeControl2Y NOTIFY relativeControl2YChanged) +public: + QDeclarativePathCubic(QObject *parent=0) : QDeclarativeCurve(parent), _control1X(0), _control1Y(0), _control2X(0), _control2Y(0) {} + + qreal control1X() const; + void setControl1X(qreal x); + + qreal control1Y() const; + void setControl1Y(qreal y); + + qreal control2X() const; + void setControl2X(qreal x); + + qreal control2Y() const; + void setControl2Y(qreal y); + + qreal relativeControl1X() const; + void setRelativeControl1X(qreal x); + bool hasRelativeControl1X(); + + qreal relativeControl1Y() const; + void setRelativeControl1Y(qreal y); + bool hasRelativeControl1Y(); + + qreal relativeControl2X() const; + void setRelativeControl2X(qreal x); + bool hasRelativeControl2X(); + + qreal relativeControl2Y() const; + void setRelativeControl2Y(qreal y); + bool hasRelativeControl2Y(); + + void addToPath(QPainterPath &path, const QDeclarativePathData &); + +Q_SIGNALS: + void control1XChanged(); + void control1YChanged(); + void control2XChanged(); + void control2YChanged(); + void relativeControl1XChanged(); + void relativeControl1YChanged(); + void relativeControl2XChanged(); + void relativeControl2YChanged(); + +private: + qreal _control1X; + qreal _control1Y; + qreal _control2X; + qreal _control2Y; + QDeclarativeNullableValue<qreal> _relativeControl1X; + QDeclarativeNullableValue<qreal> _relativeControl1Y; + QDeclarativeNullableValue<qreal> _relativeControl2X; + QDeclarativeNullableValue<qreal> _relativeControl2Y; +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathCatmullRomCurve : public QDeclarativeCurve +{ + Q_OBJECT +public: + QDeclarativePathCatmullRomCurve(QObject *parent=0) : QDeclarativeCurve(parent) {} + + void addToPath(QPainterPath &path, const QDeclarativePathData &); +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathArc : public QDeclarativeCurve +{ + Q_OBJECT + Q_PROPERTY(qreal radiusX READ radiusX WRITE setRadiusX NOTIFY radiusXChanged) + Q_PROPERTY(qreal radiusY READ radiusY WRITE setRadiusY NOTIFY radiusYChanged) + Q_PROPERTY(bool useLargeArc READ useLargeArc WRITE setUseLargeArc NOTIFY useLargeArcChanged) + Q_PROPERTY(ArcDirection direction READ direction WRITE setDirection NOTIFY directionChanged) + +public: + QDeclarativePathArc(QObject *parent=0) + : QDeclarativeCurve(parent), _radiusX(0), _radiusY(0), _useLargeArc(false), _direction(Clockwise) {} + + enum ArcDirection { Clockwise, Counterclockwise }; + Q_ENUMS(ArcDirection) + + qreal radiusX() const; + void setRadiusX(qreal); + + qreal radiusY() const; + void setRadiusY(qreal); + + bool useLargeArc() const; + void setUseLargeArc(bool); + + ArcDirection direction() const; + void setDirection(ArcDirection direction); + + void addToPath(QPainterPath &path, const QDeclarativePathData &); + +Q_SIGNALS: + void radiusXChanged(); + void radiusYChanged(); + void useLargeArcChanged(); + void directionChanged(); + +private: + qreal _radiusX; + qreal _radiusY; + bool _useLargeArc; + ArcDirection _direction; +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathSvg : public QDeclarativeCurve +{ + Q_OBJECT + Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged) +public: + QDeclarativePathSvg(QObject *parent=0) : QDeclarativeCurve(parent) {} + + QString path() const; + void setPath(const QString &path); + + void addToPath(QPainterPath &path, const QDeclarativePathData &); + +Q_SIGNALS: + void pathChanged(); + +private: + QString _path; +}; + +class Q_AUTOTEST_EXPORT QDeclarativePathPercent : public QDeclarativePathElement +{ + Q_OBJECT + Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged) +public: + QDeclarativePathPercent(QObject *parent=0) : QDeclarativePathElement(parent) {} + + qreal value() const; + void setValue(qreal value); + +signals: + void valueChanged(); + +private: + qreal _value; +}; + +struct QDeclarativeCachedBezier +{ + QDeclarativeCachedBezier() : isValid(false) {} + QBezier bezier; + int element; + qreal bezLength; + qreal currLength; + qreal p; + bool isValid; +}; + +class QDeclarativePathPrivate; +class Q_AUTOTEST_EXPORT QDeclarativePath : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(QDeclarativeListProperty<QDeclarativePathElement> pathElements READ pathElements) + Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged) + Q_PROPERTY(qreal startY READ startY WRITE setStartY NOTIFY startYChanged) + Q_PROPERTY(bool closed READ isClosed NOTIFY changed) + Q_CLASSINFO("DefaultProperty", "pathElements") + Q_INTERFACES(QDeclarativeParserStatus) +public: + QDeclarativePath(QObject *parent=0); + ~QDeclarativePath(); + + QDeclarativeListProperty<QDeclarativePathElement> pathElements(); + + qreal startX() const; + void setStartX(qreal x); + bool hasStartX() const; + + qreal startY() const; + void setStartY(qreal y); + bool hasStartY() const; + + bool isClosed() const; + bool hasEnd() const; + + QPainterPath path() const; + QStringList attributes() const; + qreal attributeAt(const QString &, qreal) const; + QPointF pointAt(qreal) const; + QPointF sequentialPointAt(qreal p, qreal *angle = 0) const; + +Q_SIGNALS: + void changed(); + void startXChanged(); + void startYChanged(); + +protected: + virtual void componentComplete(); + virtual void classBegin(); + +private Q_SLOTS: + void processPath(); + +private: + struct AttributePoint { + AttributePoint() : percent(0), scale(1), origpercent(0) {} + AttributePoint(const AttributePoint &other) + : percent(other.percent), scale(other.scale), origpercent(other.origpercent), values(other.values) {} + AttributePoint &operator=(const AttributePoint &other) { + percent = other.percent; scale = other.scale; origpercent = other.origpercent; values = other.values; return *this; + } + qreal percent; //massaged percent along the painter path + qreal scale; + qreal origpercent; //'real' percent along the painter path + QHash<QString, qreal> values; + }; + + void interpolate(int idx, const QString &name, qreal value); + void endpoint(const QString &name); + void createPointCache() const; + + static void interpolate(QList<AttributePoint> &points, int idx, const QString &name, qreal value); + static void endpoint(QList<AttributePoint> &attributePoints, const QString &name); + static QPointF forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle = 0); + static QPointF backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle = 0); + +private: + Q_DISABLE_COPY(QDeclarativePath) + Q_DECLARE_PRIVATE(QDeclarativePath) + friend class QQuickPathAnimationUpdater; + +public: + QPainterPath createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed = 0); + static QPointF sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle = 0); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePathElement) +QML_DECLARE_TYPE(QDeclarativePathAttribute) +QML_DECLARE_TYPE(QDeclarativeCurve) +QML_DECLARE_TYPE(QDeclarativePathLine) +QML_DECLARE_TYPE(QDeclarativePathQuad) +QML_DECLARE_TYPE(QDeclarativePathCubic) +QML_DECLARE_TYPE(QDeclarativePathCatmullRomCurve) +QML_DECLARE_TYPE(QDeclarativePathArc) +QML_DECLARE_TYPE(QDeclarativePathSvg) +QML_DECLARE_TYPE(QDeclarativePathPercent) +QML_DECLARE_TYPE(QDeclarativePath) + +QT_END_HEADER + +#endif // QDECLARATIVEPATH_H diff --git a/src/quick/util/qdeclarativepath_p_p.h b/src/quick/util/qdeclarativepath_p_p.h new file mode 100644 index 0000000000..910b7627d3 --- /dev/null +++ b/src/quick/util/qdeclarativepath_p_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPATH_P_H +#define QDECLARATIVEPATH_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 "qdeclarativepath_p.h" + +#include <qdeclarative.h> +#include <QtCore/QStringList> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativePathPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePath) + +public: + QDeclarativePathPrivate() : pathLength(0), closed(false), componentComplete(true) { } + + QPainterPath _path; + QList<QDeclarativePathElement*> _pathElements; + mutable QVector<QPointF> _pointCache; + QList<QDeclarativePath::AttributePoint> _attributePoints; + QStringList _attributes; + QList<QDeclarativeCurve*> _pathCurves; + mutable QDeclarativeCachedBezier prevBez; + QDeclarativeNullableValue<qreal> startX; + QDeclarativeNullableValue<qreal> startY; + qreal pathLength; + bool closed; + bool componentComplete; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/util/qdeclarativepathinterpolator.cpp b/src/quick/util/qdeclarativepathinterpolator.cpp new file mode 100644 index 0000000000..569884d33a --- /dev/null +++ b/src/quick/util/qdeclarativepathinterpolator.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepathinterpolator_p.h" + +#include "qdeclarativepath_p.h" + +QT_BEGIN_NAMESPACE + +QDeclarativePathInterpolator::QDeclarativePathInterpolator(QObject *parent) : + QObject(parent), _path(0), _x(0), _y(0), _angle(0), _progress(0) +{ +} + +QDeclarativePath *QDeclarativePathInterpolator::path() const +{ + return _path; +} + +void QDeclarativePathInterpolator::setPath(QDeclarativePath *path) +{ + if (_path == path) + return; + if (_path) + disconnect(_path, SIGNAL(changed()), this, SLOT(_q_pathUpdated())); + _path = path; + connect(_path, SIGNAL(changed()), this, SLOT(_q_pathUpdated())); + emit pathChanged(); +} + +qreal QDeclarativePathInterpolator::progress() const +{ + return _progress; +} + +void QDeclarativePathInterpolator::setProgress(qreal progress) +{ + if (progress == _progress) + return; + _progress = progress; + emit progressChanged(); + _q_pathUpdated(); +} + +qreal QDeclarativePathInterpolator::x() const +{ + return _x; +} + +qreal QDeclarativePathInterpolator::y() const +{ + return _y; +} + +qreal QDeclarativePathInterpolator::angle() const +{ + return _angle; +} + +void QDeclarativePathInterpolator::_q_pathUpdated() +{ + if (! _path) + return; + + qreal angle = 0; + const QPointF pt = _path->sequentialPointAt(_progress, &angle); + + if (_x != pt.x()) { + _x = pt.x(); + emit xChanged(); + } + + if (_y != pt.y()) { + _y = pt.y(); + emit yChanged(); + } + + if (angle != _angle) { + _angle = angle; + emit angleChanged(); + } +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativepathinterpolator_p.h b/src/quick/util/qdeclarativepathinterpolator_p.h new file mode 100644 index 0000000000..11370a53fe --- /dev/null +++ b/src/quick/util/qdeclarativepathinterpolator_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPATHINTERPOLATOR_P_H +#define QDECLARATIVEPATHINTERPOLATOR_P_H + +#include <qdeclarative.h> +#include <QObject> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativePath; +class Q_AUTOTEST_EXPORT QDeclarativePathInterpolator : public QObject +{ + Q_OBJECT + Q_PROPERTY(QDeclarativePath *path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged) + Q_PROPERTY(qreal x READ x NOTIFY xChanged) + Q_PROPERTY(qreal y READ y NOTIFY yChanged) + Q_PROPERTY(qreal angle READ angle NOTIFY angleChanged) +public: + explicit QDeclarativePathInterpolator(QObject *parent = 0); + + QDeclarativePath *path() const; + void setPath(QDeclarativePath *path); + + qreal progress() const; + void setProgress(qreal progress); + + qreal x() const; + qreal y() const; + qreal angle() const; + +Q_SIGNALS: + void pathChanged(); + void progressChanged(); + void xChanged(); + void yChanged(); + void angleChanged(); + +private Q_SLOTS: + void _q_pathUpdated(); + +private: + QDeclarativePath *_path; + qreal _x; + qreal _y; + qreal _angle; + qreal _progress; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePathInterpolator) + +QT_END_HEADER + +#endif // QDECLARATIVEPATHINTERPOLATOR_P_H diff --git a/src/quick/util/qdeclarativepixmapcache.cpp b/src/quick/util/qdeclarativepixmapcache.cpp new file mode 100644 index 0000000000..ba155abe98 --- /dev/null +++ b/src/quick/util/qdeclarativepixmapcache.cpp @@ -0,0 +1,1241 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepixmapcache_p.h" +#include <qdeclarativenetworkaccessmanagerfactory.h> +#include <qdeclarativeimageprovider.h> + +#include <qdeclarativeengine.h> +#include <private/qdeclarativeglobal_p.h> +#include <private/qdeclarativeengine_p.h> + +#include <QtQuick/private/qsgtexture_p.h> + +#include <QCoreApplication> +#include <QImageReader> +#include <QHash> +#include <QNetworkReply> +#include <QPixmapCache> +#include <QFile> +#include <QThread> +#include <QMutex> +#include <QMutexLocker> +#include <QWaitCondition> +#include <QBuffer> +#include <QWaitCondition> +#include <QtCore/qdebug.h> +#include <private/qobject_p.h> +#include <QSslError> +#include <QOpenGLContext> + +#define IMAGEREQUEST_MAX_REQUEST_COUNT 8 +#define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16 +#define CACHE_EXPIRE_TIME 30 +#define CACHE_REMOVAL_FRACTION 4 + +QT_BEGIN_NAMESPACE + +// The cache limit describes the maximum "junk" in the cache. +static int cache_limit = 2048 * 1024; // 2048 KB cache limit for embedded in qpixmapcache.cpp + +QSGTexture *QDeclarativeDefaultTextureFactory::createTexture() const +{ + QSGPlainTexture *t = new QSGPlainTexture(); + t->setImage(im); + return t; +} + +class QDeclarativePixmapReader; +class QDeclarativePixmapData; +class QDeclarativePixmapReply : public QObject +{ + Q_OBJECT +public: + enum ReadError { NoError, Loading, Decoding }; + + QDeclarativePixmapReply(QDeclarativePixmapData *); + ~QDeclarativePixmapReply(); + + QDeclarativePixmapData *data; + QDeclarativeEngine *engineForReader; // always access reader inside readerMutex + QSize requestSize; + QUrl url; + + bool loading; + int redirectCount; + + class Event : public QEvent { + public: + Event(ReadError, const QString &, const QSize &, QDeclarativeTextureFactory *factory, const QImage &image); + + ReadError error; + QString errorString; + QSize implicitSize; + QImage image; + QDeclarativeTextureFactory *textureFactory; + }; + void postReply(ReadError, const QString &, const QSize &, const QImage &image); + void postReply(ReadError, const QString &, const QSize &, QDeclarativeTextureFactory *factory, const QImage &image); + + +Q_SIGNALS: + void finished(); + void downloadProgress(qint64, qint64); + +protected: + bool event(QEvent *event); + +private: + Q_DISABLE_COPY(QDeclarativePixmapReply) + +public: + static int finishedIndex; + static int downloadProgressIndex; +}; + +class QDeclarativePixmapReaderThreadObject : public QObject { + Q_OBJECT +public: + QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *); + void processJobs(); + virtual bool event(QEvent *e); +private slots: + void networkRequestDone(); +private: + QDeclarativePixmapReader *reader; +}; + +class QDeclarativePixmapData; +class QDeclarativePixmapReader : public QThread +{ + Q_OBJECT +public: + QDeclarativePixmapReader(QDeclarativeEngine *eng); + ~QDeclarativePixmapReader(); + + QDeclarativePixmapReply *getImage(QDeclarativePixmapData *); + void cancel(QDeclarativePixmapReply *rep); + + static QDeclarativePixmapReader *instance(QDeclarativeEngine *engine); + static QDeclarativePixmapReader *existingInstance(QDeclarativeEngine *engine); + +protected: + void run(); + +private: + friend class QDeclarativePixmapReaderThreadObject; + void processJobs(); + void processJob(QDeclarativePixmapReply *, const QUrl &, const QSize &); + void networkRequestDone(QNetworkReply *); + + QList<QDeclarativePixmapReply*> jobs; + QList<QDeclarativePixmapReply*> cancelled; + QDeclarativeEngine *engine; + QObject *eventLoopQuitHack; + + QMutex mutex; + QDeclarativePixmapReaderThreadObject *threadObject; + QWaitCondition waitCondition; + + QNetworkAccessManager *networkAccessManager(); + QNetworkAccessManager *accessManager; + + QHash<QNetworkReply*,QDeclarativePixmapReply*> replies; + + static int replyDownloadProgress; + static int replyFinished; + static int downloadProgress; + static int threadNetworkRequestDone; + static QHash<QDeclarativeEngine *,QDeclarativePixmapReader*> readers; +public: + static QMutex readerMutex; +}; + +class QDeclarativePixmapData +{ +public: + QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QUrl &u, const QSize &s, const QString &e) + : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Error), + url(u), errorString(e), requestSize(s), textureFactory(0), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + declarativePixmaps.insert(pixmap); + } + + QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QUrl &u, const QSize &r) + : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Loading), + url(u), requestSize(r), textureFactory(0), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0), + nextUnreferenced(0) + { + declarativePixmaps.insert(pixmap); + } + + QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QUrl &u, const QImage &p, const QSize &s, const QSize &r) + : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready), + url(u), image(p), implicitSize(s), requestSize(r), textureFactory(new QDeclarativeDefaultTextureFactory(p)), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + declarativePixmaps.insert(pixmap); + } + + QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QUrl &u, QDeclarativeTextureFactory *factory, const QImage &p, const QSize &s, const QSize &r) + : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready), + url(u), image(p), implicitSize(s), requestSize(r), textureFactory(factory), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + declarativePixmaps.insert(pixmap); + } + + QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QImage &p) + : refCount(1), inCache(false), privatePixmap(true), pixmapStatus(QDeclarativePixmap::Ready), + image(p), implicitSize(p.size()), requestSize(p.size()), textureFactory(new QDeclarativeDefaultTextureFactory(p)), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + declarativePixmaps.insert(pixmap); + } + + ~QDeclarativePixmapData() + { + while (!declarativePixmaps.isEmpty()) { + QDeclarativePixmap *referencer = declarativePixmaps.first(); + declarativePixmaps.remove(referencer); + referencer->d = 0; + } + delete textureFactory; + } + + int cost() const; + void addref(); + void release(); + void addToCache(); + void removeFromCache(); + + uint refCount; + + bool inCache:1; + bool privatePixmap:1; + + QDeclarativePixmap::Status pixmapStatus; + QUrl url; + QString errorString; + QImage image; + QSize implicitSize; + QSize requestSize; + + QDeclarativeTextureFactory *textureFactory; + + QIntrusiveList<QDeclarativePixmap, &QDeclarativePixmap::dataListNode> declarativePixmaps; + QDeclarativePixmapReply *reply; + + QDeclarativePixmapData *prevUnreferenced; + QDeclarativePixmapData**prevUnreferencedPtr; + QDeclarativePixmapData *nextUnreferenced; +}; + +int QDeclarativePixmapReply::finishedIndex = -1; +int QDeclarativePixmapReply::downloadProgressIndex = -1; + +// XXX +QHash<QDeclarativeEngine *,QDeclarativePixmapReader*> QDeclarativePixmapReader::readers; +QMutex QDeclarativePixmapReader::readerMutex; + +int QDeclarativePixmapReader::replyDownloadProgress = -1; +int QDeclarativePixmapReader::replyFinished = -1; +int QDeclarativePixmapReader::downloadProgress = -1; +int QDeclarativePixmapReader::threadNetworkRequestDone = -1; + + +void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString, + const QSize &implicitSize, const QImage &image) +{ + loading = false; + QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, new QDeclarativeDefaultTextureFactory(image), image)); +} + +void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString, + const QSize &implicitSize, QDeclarativeTextureFactory *factory, + const QImage &image) +{ + loading = false; + QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, factory, image)); +} + +QDeclarativePixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, QDeclarativeTextureFactory *factory, const QImage &i) + : QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), image(i), textureFactory(factory) +{ +} + +QNetworkAccessManager *QDeclarativePixmapReader::networkAccessManager() +{ + if (!accessManager) { + Q_ASSERT(threadObject); + accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(threadObject); + } + return accessManager; +} + +static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, + const QSize &requestSize) +{ + QImageReader imgio(dev); + + bool force_scale = false; + if (url.path().endsWith(QLatin1String(".svg"),Qt::CaseInsensitive)) { + imgio.setFormat("svg"); // QSvgPlugin::capabilities bug QTBUG-9053 + force_scale = true; + } + + bool scaled = false; + if (requestSize.width() > 0 || requestSize.height() > 0) { + QSize s = imgio.size(); + if (requestSize.width() && (force_scale || requestSize.width() < s.width())) { + if (requestSize.height() <= 0) + s.setHeight(s.height()*requestSize.width()/s.width()); + s.setWidth(requestSize.width()); scaled = true; + } + if (requestSize.height() && (force_scale || requestSize.height() < s.height())) { + if (requestSize.width() <= 0) + s.setWidth(s.width()*requestSize.height()/s.height()); + s.setHeight(requestSize.height()); scaled = true; + } + if (scaled) { imgio.setScaledSize(s); } + } + + if (impsize) + *impsize = imgio.size(); + + if (imgio.read(image)) { + if (impsize && impsize->width() < 0) + *impsize = image->size(); + return true; + } else { + if (errorString) + *errorString = QDeclarativePixmap::tr("Error decoding: %1: %2").arg(url.toString()) + .arg(imgio.errorString()); + return false; + } +} + +QDeclarativePixmapReader::QDeclarativePixmapReader(QDeclarativeEngine *eng) +: QThread(eng), engine(eng), threadObject(0), accessManager(0) +{ + eventLoopQuitHack = new QObject; + eventLoopQuitHack->moveToThread(this); + connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection); + start(QThread::LowestPriority); +} + +QDeclarativePixmapReader::~QDeclarativePixmapReader() +{ + readerMutex.lock(); + readers.remove(engine); + readerMutex.unlock(); + + mutex.lock(); + // manually cancel all outstanding jobs. + foreach (QDeclarativePixmapReply *reply, jobs) { + delete reply; + } + jobs.clear(); + QList<QDeclarativePixmapReply*> activeJobs = replies.values(); + foreach (QDeclarativePixmapReply *reply, activeJobs) { + if (reply->loading) { + cancelled.append(reply); + reply->data = 0; + } + } + if (threadObject) threadObject->processJobs(); + mutex.unlock(); + + eventLoopQuitHack->deleteLater(); + wait(); +} + +void QDeclarativePixmapReader::networkRequestDone(QNetworkReply *reply) +{ + QDeclarativePixmapReply *job = replies.take(reply); + + if (job) { + job->redirectCount++; + if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) { + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = reply->url().resolved(redirect.toUrl()); + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + + reply->deleteLater(); + reply = networkAccessManager()->get(req); + + QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress); + QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); + + replies.insert(reply, job); + return; + } + } + + QImage image; + QDeclarativePixmapReply::ReadError error = QDeclarativePixmapReply::NoError; + QString errorString; + QSize readSize; + if (reply->error()) { + error = QDeclarativePixmapReply::Loading; + errorString = reply->errorString(); + } else { + QByteArray all = reply->readAll(); + QBuffer buff(&all); + buff.open(QIODevice::ReadOnly); + if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize)) + error = QDeclarativePixmapReply::Decoding; + } + // send completion event to the QDeclarativePixmapReply + mutex.lock(); + if (!cancelled.contains(job)) { + job->postReply(error, errorString, readSize, image); + } + mutex.unlock(); + } + reply->deleteLater(); + + // kick off event loop again incase we have dropped below max request count + threadObject->processJobs(); +} + +QDeclarativePixmapReaderThreadObject::QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *i) +: reader(i) +{ +} + +void QDeclarativePixmapReaderThreadObject::processJobs() +{ + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} + +bool QDeclarativePixmapReaderThreadObject::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + reader->processJobs(); + return true; + } else { + return QObject::event(e); + } +} + +void QDeclarativePixmapReaderThreadObject::networkRequestDone() +{ + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + reader->networkRequestDone(reply); +} + +void QDeclarativePixmapReader::processJobs() +{ + QMutexLocker locker(&mutex); + + while (true) { + if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT)) + return; // Nothing else to do + + // Clean cancelled jobs + if (cancelled.count()) { + for (int i = 0; i < cancelled.count(); ++i) { + QDeclarativePixmapReply *job = cancelled.at(i); + QNetworkReply *reply = replies.key(job, 0); + if (reply && reply->isRunning()) { + // cancel any jobs already started + replies.remove(reply); + reply->close(); + } + // deleteLater, since not owned by this thread + job->deleteLater(); + } + cancelled.clear(); + } + + if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) { + QDeclarativePixmapReply *runningJob = jobs.takeLast(); + runningJob->loading = true; + + QUrl url = runningJob->url; + QSize requestSize = runningJob->requestSize; + locker.unlock(); + processJob(runningJob, url, requestSize); + locker.relock(); + } + } +} + +void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, const QUrl &url, + const QSize &requestSize) +{ + // fetch + if (url.scheme() == QLatin1String("image")) { + // Use QmlImageProvider + QSize readSize; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url); + if (imageType == QDeclarativeImageProvider::Invalid) { + QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::Loading; + QString errorStr = QDeclarativePixmap::tr("Invalid image provider: %1").arg(url.toString()); + QImage image; + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(errorCode, errorStr, readSize, image); + mutex.unlock(); + } else if (imageType == QDeclarativeImageProvider::Image) { + QImage image = ep->getImageFromProvider(url, &readSize, requestSize); + QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError; + QString errorStr; + if (image.isNull()) { + errorCode = QDeclarativePixmapReply::Loading; + errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + } + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(errorCode, errorStr, readSize, image); + mutex.unlock(); + } else { + QDeclarativeTextureFactory *t = ep->getTextureFromProvider(url, &readSize, requestSize); + QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError; + QString errorStr; + if (!t) { + errorCode = QDeclarativePixmapReply::Loading; + errorStr = QDeclarativePixmap::tr("Failed to get texture from provider: %1").arg(url.toString()); + } + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(errorCode, errorStr, readSize, t, QImage()); + mutex.unlock(); + + } + + } else { + QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + if (!lf.isEmpty()) { + // Image is local - load/decode immediately + QImage image; + QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError; + QString errorStr; + QFile f(lf); + QSize readSize; + if (f.open(QIODevice::ReadOnly)) { + if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize)) + errorCode = QDeclarativePixmapReply::Loading; + } else { + errorStr = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString()); + errorCode = QDeclarativePixmapReply::Loading; + } + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(errorCode, errorStr, readSize, image); + mutex.unlock(); + } else { + // Network resource + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + QNetworkReply *reply = networkAccessManager()->get(req); + + QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress); + QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); + + replies.insert(reply, runningJob); + } + } +} + +QDeclarativePixmapReader *QDeclarativePixmapReader::instance(QDeclarativeEngine *engine) +{ + // XXX NOTE: must be called within readerMutex locking. + QDeclarativePixmapReader *reader = readers.value(engine); + if (!reader) { + reader = new QDeclarativePixmapReader(engine); + readers.insert(engine, reader); + } + + return reader; +} + +QDeclarativePixmapReader *QDeclarativePixmapReader::existingInstance(QDeclarativeEngine *engine) +{ + // XXX NOTE: must be called within readerMutex locking. + return readers.value(engine, 0); +} + +QDeclarativePixmapReply *QDeclarativePixmapReader::getImage(QDeclarativePixmapData *data) +{ + mutex.lock(); + QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(data); + reply->engineForReader = engine; + jobs.append(reply); + // XXX + if (threadObject) threadObject->processJobs(); + mutex.unlock(); + return reply; +} + +void QDeclarativePixmapReader::cancel(QDeclarativePixmapReply *reply) +{ + mutex.lock(); + if (reply->loading) { + cancelled.append(reply); + reply->data = 0; + // XXX + if (threadObject) threadObject->processJobs(); + } else { + jobs.removeAll(reply); + delete reply; + } + mutex.unlock(); +} + +void QDeclarativePixmapReader::run() +{ + if (replyDownloadProgress == -1) { + const QMetaObject *nr = &QNetworkReply::staticMetaObject; + const QMetaObject *pr = &QDeclarativePixmapReply::staticMetaObject; + const QMetaObject *ir = &QDeclarativePixmapReaderThreadObject::staticMetaObject; + replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)"); + replyFinished = nr->indexOfSignal("finished()"); + downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)"); + threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()"); + } + + mutex.lock(); + threadObject = new QDeclarativePixmapReaderThreadObject(this); + mutex.unlock(); + + processJobs(); + exec(); + + delete threadObject; + threadObject = 0; +} + +class QDeclarativePixmapKey +{ +public: + const QUrl *url; + const QSize *size; +}; + +inline bool operator==(const QDeclarativePixmapKey &lhs, const QDeclarativePixmapKey &rhs) +{ + return *lhs.size == *rhs.size && *lhs.url == *rhs.url; +} + +inline uint qHash(const QDeclarativePixmapKey &key) +{ + return qHash(*key.url) ^ key.size->width() ^ key.size->height(); +} + +class QSGContext; + +class QDeclarativePixmapStore : public QObject +{ + Q_OBJECT +public: + QDeclarativePixmapStore(); + ~QDeclarativePixmapStore(); + + void unreferencePixmap(QDeclarativePixmapData *); + void referencePixmap(QDeclarativePixmapData *); + +protected: + virtual void timerEvent(QTimerEvent *); + +public: + QHash<QDeclarativePixmapKey, QDeclarativePixmapData *> m_cache; + +private: + void shrinkCache(int remove); + + QDeclarativePixmapData *m_unreferencedPixmaps; + QDeclarativePixmapData *m_lastUnreferencedPixmap; + + int m_unreferencedCost; + int m_timerId; +}; +Q_GLOBAL_STATIC(QDeclarativePixmapStore, pixmapStore); + + +QDeclarativePixmapStore::QDeclarativePixmapStore() +: m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1) +{ +} + +QDeclarativePixmapStore::~QDeclarativePixmapStore() +{ + int leakedPixmaps = 0; + QList<QDeclarativePixmapData*> cachedData = m_cache.values(); + + // unreference all (leaked) pixmaps + foreach (QDeclarativePixmapData* pixmap, cachedData) { + int currRefCount = pixmap->refCount; + if (currRefCount) { + leakedPixmaps++; + while (currRefCount > 0) { + pixmap->release(); + currRefCount--; + } + } + } + + // free all unreferenced pixmaps + while (m_lastUnreferencedPixmap) { + shrinkCache(20); + } + + if (leakedPixmaps) + qDebug("Number of leaked pixmaps: %i", leakedPixmaps); +} + +void QDeclarativePixmapStore::unreferencePixmap(QDeclarativePixmapData *data) +{ + Q_ASSERT(data->prevUnreferenced == 0); + Q_ASSERT(data->prevUnreferencedPtr == 0); + Q_ASSERT(data->nextUnreferenced == 0); + + data->nextUnreferenced = m_unreferencedPixmaps; + data->prevUnreferencedPtr = &m_unreferencedPixmaps; + + m_unreferencedPixmaps = data; + if (m_unreferencedPixmaps->nextUnreferenced) { + m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps; + m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced; + } + + if (!m_lastUnreferencedPixmap) + m_lastUnreferencedPixmap = data; + + m_unreferencedCost += data->cost(); + + shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit + + if (m_timerId == -1 && m_unreferencedPixmaps) + m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000); +} + +void QDeclarativePixmapStore::referencePixmap(QDeclarativePixmapData *data) +{ + Q_ASSERT(data->prevUnreferencedPtr); + + *data->prevUnreferencedPtr = data->nextUnreferenced; + if (data->nextUnreferenced) { + data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr; + data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced; + } + if (m_lastUnreferencedPixmap == data) + m_lastUnreferencedPixmap = data->prevUnreferenced; + + data->nextUnreferenced = 0; + data->prevUnreferencedPtr = 0; + data->prevUnreferenced = 0; + + m_unreferencedCost -= data->cost(); +} + +void QDeclarativePixmapStore::shrinkCache(int remove) +{ + while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) { + QDeclarativePixmapData *data = m_lastUnreferencedPixmap; + Q_ASSERT(data->nextUnreferenced == 0); + + *data->prevUnreferencedPtr = 0; + m_lastUnreferencedPixmap = data->prevUnreferenced; + data->prevUnreferencedPtr = 0; + data->prevUnreferenced = 0; + + remove -= data->cost(); + m_unreferencedCost -= data->cost(); + data->removeFromCache(); + delete data; + } +} + +void QDeclarativePixmapStore::timerEvent(QTimerEvent *) +{ + int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION; + + shrinkCache(removalCost); + + if (m_unreferencedPixmaps == 0) { + killTimer(m_timerId); + m_timerId = -1; + } +} + +QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d) +: data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), redirectCount(0) +{ + if (finishedIndex == -1) { + finishedIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()"); + downloadProgressIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); + } +} + +QDeclarativePixmapReply::~QDeclarativePixmapReply() +{ +} + +bool QDeclarativePixmapReply::event(QEvent *event) +{ + if (event->type() == QEvent::User) { + + if (data) { + Event *de = static_cast<Event *>(event); + data->pixmapStatus = (de->error == NoError) ? QDeclarativePixmap::Ready : QDeclarativePixmap::Error; + + if (data->pixmapStatus == QDeclarativePixmap::Ready) { + if (de->textureFactory) { + data->textureFactory = de->textureFactory; + } + data->image = de->image; + data->implicitSize = de->implicitSize; + } else { + data->errorString = de->errorString; + data->removeFromCache(); // We don't continue to cache error'd pixmaps + } + + data->reply = 0; + emit finished(); + } + + delete this; + return true; + } else { + return QObject::event(event); + } +} + +int QDeclarativePixmapData::cost() const +{ + if (textureFactory) + return textureFactory->textureByteCount(); + return image.byteCount(); +} + +void QDeclarativePixmapData::addref() +{ + ++refCount; + if (prevUnreferencedPtr) + pixmapStore()->referencePixmap(this); +} + +void QDeclarativePixmapData::release() +{ + Q_ASSERT(refCount > 0); + --refCount; + if (refCount == 0) { + if (reply) { + QDeclarativePixmapReply *cancelReply = reply; + reply->data = 0; + reply = 0; + QDeclarativePixmapReader::readerMutex.lock(); + QDeclarativePixmapReader *reader = QDeclarativePixmapReader::existingInstance(cancelReply->engineForReader); + if (reader) + reader->cancel(cancelReply); + QDeclarativePixmapReader::readerMutex.unlock(); + } + + if (pixmapStatus == QDeclarativePixmap::Ready) { + pixmapStore()->unreferencePixmap(this); + } else { + removeFromCache(); + delete this; + } + } +} + +void QDeclarativePixmapData::addToCache() +{ + if (!inCache) { + QDeclarativePixmapKey key = { &url, &requestSize }; + pixmapStore()->m_cache.insert(key, this); + inCache = true; + } +} + +void QDeclarativePixmapData::removeFromCache() +{ + if (inCache) { + QDeclarativePixmapKey key = { &url, &requestSize }; + pixmapStore()->m_cache.remove(key); + inCache = false; + } +} + +static QDeclarativePixmapData* createPixmapDataSync(QDeclarativePixmap *declarativePixmap, QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok) +{ + if (url.scheme() == QLatin1String("image")) { + QSize readSize; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url); + + switch (imageType) { + case QDeclarativeImageProvider::Invalid: + return new QDeclarativePixmapData(declarativePixmap, url, requestSize, + QDeclarativePixmap::tr("Invalid image provider: %1").arg(url.toString())); + case QDeclarativeImageProvider::Texture: + { + QDeclarativeTextureFactory *texture = ep->getTextureFromProvider(url, &readSize, requestSize); + if (texture) { + *ok = true; + return new QDeclarativePixmapData(declarativePixmap, url, texture, QImage(), readSize, requestSize); + } + } + + case QDeclarativeImageProvider::Image: + { + QImage image = ep->getImageFromProvider(url, &readSize, requestSize); + if (!image.isNull()) { + *ok = true; + return new QDeclarativePixmapData(declarativePixmap, url, image, readSize, requestSize); + } + } + case QDeclarativeImageProvider::Pixmap: + { + QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize); + if (!pixmap.isNull()) { + *ok = true; + return new QDeclarativePixmapData(declarativePixmap, url, pixmap.toImage(), readSize, requestSize); + } + } + } + + // provider has bad image type, or provider returned null image + return new QDeclarativePixmapData(declarativePixmap, url, requestSize, + QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString())); + } + + QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + if (localFile.isEmpty()) + return 0; + + QFile f(localFile); + QSize readSize; + QString errorString; + + if (f.open(QIODevice::ReadOnly)) { + QImage image; + + if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) { + *ok = true; + return new QDeclarativePixmapData(declarativePixmap, url, image, readSize, requestSize); + } + errorString = QDeclarativePixmap::tr("Invalid image data: %1").arg(url.toString()); + + } else { + errorString = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString()); + } + return new QDeclarativePixmapData(declarativePixmap, url, requestSize, errorString); +} + + +struct QDeclarativePixmapNull { + QUrl url; + QImage image; + QSize size; +}; +Q_GLOBAL_STATIC(QDeclarativePixmapNull, nullPixmap); + +QDeclarativePixmap::QDeclarativePixmap() +: d(0) +{ +} + +QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url) +: d(0) +{ + load(engine, url); +} + +QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url, const QSize &size) +: d(0) +{ + load(engine, url, size); +} + +QDeclarativePixmap::~QDeclarativePixmap() +{ + if (d) { + d->declarativePixmaps.remove(this); + d->release(); + d = 0; + } +} + +bool QDeclarativePixmap::isNull() const +{ + return d == 0; +} + +bool QDeclarativePixmap::isReady() const +{ + return status() == Ready; +} + +bool QDeclarativePixmap::isError() const +{ + return status() == Error; +} + +bool QDeclarativePixmap::isLoading() const +{ + return status() == Loading; +} + +QString QDeclarativePixmap::error() const +{ + if (d) + return d->errorString; + else + return QString(); +} + +QDeclarativePixmap::Status QDeclarativePixmap::status() const +{ + if (d) + return d->pixmapStatus; + else + return Null; +} + +const QUrl &QDeclarativePixmap::url() const +{ + if (d) + return d->url; + else + return nullPixmap()->url; +} + +const QSize &QDeclarativePixmap::implicitSize() const +{ + if (d) + return d->implicitSize; + else + return nullPixmap()->size; +} + +const QSize &QDeclarativePixmap::requestSize() const +{ + if (d) + return d->requestSize; + else + return nullPixmap()->size; +} + +QDeclarativeTextureFactory *QDeclarativePixmap::textureFactory() const +{ + if (d) + return d->textureFactory; + + return 0; +} + +const QImage &QDeclarativePixmap::image() const +{ + if (d) + return d->image; + else + return nullPixmap()->image; +} + +void QDeclarativePixmap::setImage(const QImage &p) +{ + clear(); + + if (!p.isNull()) + d = new QDeclarativePixmapData(this, p); +} + +int QDeclarativePixmap::width() const +{ + if (d) + return d->textureFactory ? d->textureFactory->textureSize().width() : d->image.width(); + else + return 0; +} + +int QDeclarativePixmap::height() const +{ + if (d) + return d->textureFactory ? d->textureFactory->textureSize().height() : d->image.height(); + else + return 0; +} + +QRect QDeclarativePixmap::rect() const +{ + if (d) + return d->textureFactory ? QRect(QPoint(), d->textureFactory->textureSize()) : d->image.rect(); + else + return QRect(); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url) +{ + load(engine, url, QSize(), QDeclarativePixmap::Cache); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, QDeclarativePixmap::Options options) +{ + load(engine, url, QSize(), options); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &size) +{ + load(engine, url, size, QDeclarativePixmap::Cache); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, QDeclarativePixmap::Options options) +{ + if (d) { + d->declarativePixmaps.remove(this); + d->release(); + d = 0; + } + + QDeclarativePixmapKey key = { &url, &requestSize }; + QDeclarativePixmapStore *store = pixmapStore(); + + QHash<QDeclarativePixmapKey, QDeclarativePixmapData *>::Iterator iter = store->m_cache.find(key); + + if (iter == store->m_cache.end()) { + if (options & QDeclarativePixmap::Asynchronous) { + // pixmaps can only be loaded synchronously + if (url.scheme() == QLatin1String("image") + && QDeclarativeEnginePrivate::get(engine)->getImageProviderType(url) == QDeclarativeImageProvider::Pixmap) { + options &= ~QDeclarativePixmap::Asynchronous; + } + } + + if (!(options & QDeclarativePixmap::Asynchronous)) { + bool ok = false; + d = createPixmapDataSync(this, engine, url, requestSize, &ok); + if (ok) { + if (options & QDeclarativePixmap::Cache) + d->addToCache(); + return; + } + if (d) // loadable, but encountered error while loading + return; + } + + if (!engine) + return; + + d = new QDeclarativePixmapData(this, url, requestSize); + if (options & QDeclarativePixmap::Cache) + d->addToCache(); + + QDeclarativePixmapReader::readerMutex.lock(); + d->reply = QDeclarativePixmapReader::instance(engine)->getImage(d); + QDeclarativePixmapReader::readerMutex.unlock(); + } else { + d = *iter; + d->addref(); + d->declarativePixmaps.insert(this); + } +} + +void QDeclarativePixmap::clear() +{ + if (d) { + d->declarativePixmaps.remove(this); + d->release(); + d = 0; + } +} + +void QDeclarativePixmap::clear(QObject *obj) +{ + if (d) { + if (d->reply) + QObject::disconnect(d->reply, 0, obj, 0); + d->declarativePixmaps.remove(this); + d->release(); + d = 0; + } +} + +bool QDeclarativePixmap::connectFinished(QObject *object, const char *method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectFinished() called when not loading."); + return false; + } + + return QObject::connect(d->reply, SIGNAL(finished()), object, method); +} + +bool QDeclarativePixmap::connectFinished(QObject *object, int method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectFinished() called when not loading."); + return false; + } + + return QMetaObject::connect(d->reply, QDeclarativePixmapReply::finishedIndex, object, method); +} + +bool QDeclarativePixmap::connectDownloadProgress(QObject *object, const char *method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading."); + return false; + } + + return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method); +} + +bool QDeclarativePixmap::connectDownloadProgress(QObject *object, int method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading."); + return false; + } + + return QMetaObject::connect(d->reply, QDeclarativePixmapReply::downloadProgressIndex, object, method); +} + +QT_END_NAMESPACE + +#include <qdeclarativepixmapcache.moc> diff --git a/src/quick/util/qdeclarativepixmapcache_p.h b/src/quick/util/qdeclarativepixmapcache_p.h new file mode 100644 index 0000000000..ee55519f93 --- /dev/null +++ b/src/quick/util/qdeclarativepixmapcache_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPIXMAPCACHE_H +#define QDECLARATIVEPIXMAPCACHE_H + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qstring.h> +#include <QtGui/qpixmap.h> +#include <QtCore/qurl.h> +#include <QtQuick/qtquickglobal.h> + +#include <private/qintrusivelist_p.h> +#include <qdeclarativeimageprovider.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class QDeclarativePixmapData; +class QDeclarativeTextureFactory; + +class QDeclarativeDefaultTextureFactory : public QDeclarativeTextureFactory +{ + Q_OBJECT +public: + QDeclarativeDefaultTextureFactory(const QImage &i) + : im(i) + { + } + + QSGTexture *createTexture() const; + QSize textureSize() const { return im.size(); } + int textureByteCount() const { return im.byteCount(); } + QImage image() const { return im; } + +private: + QImage im; +}; + +class Q_QUICK_EXPORT QDeclarativePixmap +{ + Q_DECLARE_TR_FUNCTIONS(QDeclarativePixmap) +public: + QDeclarativePixmap(); + QDeclarativePixmap(QDeclarativeEngine *, const QUrl &); + QDeclarativePixmap(QDeclarativeEngine *, const QUrl &, const QSize &); + ~QDeclarativePixmap(); + + enum Status { Null, Ready, Error, Loading }; + + enum Option { + Asynchronous = 0x00000001, + Cache = 0x00000002 + }; + Q_DECLARE_FLAGS(Options, Option) + + bool isNull() const; + bool isReady() const; + bool isError() const; + bool isLoading() const; + + Status status() const; + QString error() const; + const QUrl &url() const; + const QSize &implicitSize() const; + const QSize &requestSize() const; + const QImage &image() const; + void setImage(const QImage &); + + QDeclarativeTextureFactory *textureFactory() const; + + QRect rect() const; + int width() const; + int height() const; + + void load(QDeclarativeEngine *, const QUrl &); + void load(QDeclarativeEngine *, const QUrl &, QDeclarativePixmap::Options options); + void load(QDeclarativeEngine *, const QUrl &, const QSize &); + void load(QDeclarativeEngine *, const QUrl &, const QSize &, QDeclarativePixmap::Options options); + + void clear(); + void clear(QObject *); + + bool connectFinished(QObject *, const char *); + bool connectFinished(QObject *, int); + bool connectDownloadProgress(QObject *, const char *); + bool connectDownloadProgress(QObject *, int); + +private: + Q_DISABLE_COPY(QDeclarativePixmap) + QDeclarativePixmapData *d; + QIntrusiveListNode dataListNode; + friend class QDeclarativePixmapData; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativePixmap::Options) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEPIXMAPCACHE_H diff --git a/src/quick/util/qdeclarativepropertychanges.cpp b/src/quick/util/qdeclarativepropertychanges.cpp new file mode 100644 index 0000000000..d6035fa9d2 --- /dev/null +++ b/src/quick/util/qdeclarativepropertychanges.cpp @@ -0,0 +1,796 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepropertychanges_p.h" + +#include <private/qdeclarativeopenmetaobject_p.h> +#include <private/qdeclarativerewrite_p.h> +#include <private/qdeclarativeengine_p.h> + +#include <qdeclarativeinfo.h> +#include <private/qdeclarativecustomparser_p.h> +#include <private/qdeclarativescript_p.h> +#include <qdeclarativeexpression.h> +#include <private/qdeclarativebinding_p.h> +#include <qdeclarativecontext.h> +#include <private/qdeclarativeguard_p.h> +#include <private/qdeclarativeproperty_p.h> +#include <private/qdeclarativecontext_p.h> +#include <private/qdeclarativestate_p_p.h> + +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass PropertyChanges QDeclarativePropertyChanges + \inqmlmodule QtQuick 2 + \ingroup qml-state-elements + \brief The PropertyChanges element describes new property bindings or values for a state. + + PropertyChanges is used to define the property values or bindings in a + \l State. This enables an item's property values to be changed when it + \l {QML States}{changes between states}. + + To create a PropertyChanges object, specify the \l target item whose + properties are to be modified, and define the new property values or + bindings. For example: + + \snippet doc/src/snippets/declarative/propertychanges.qml import + \codeline + \snippet doc/src/snippets/declarative/propertychanges.qml 0 + + When the mouse is pressed, the \l Rectangle changes to the \e resized + state. In this state, the PropertyChanges object sets the rectangle's + color to blue and the \c height value to that of \c container.height. + + Note this automatically binds \c rect.height to \c container.height + in the \e resized state. If a property binding should not be + established, and the height should just be set to the value of + \c container.height at the time of the state change, set the \l explicit + property to \c true. + + A PropertyChanges object can also override the default signal handler + for an object to implement a signal handler specific to the new state: + + \qml + PropertyChanges { + target: myMouseArea + onClicked: doSomethingDifferent() + } + \endqml + + \note PropertyChanges can be used to change anchor margins, but not other anchor + values; use AnchorChanges for this instead. Similarly, to change an \l Item's + \l {Item::}{parent} value, use ParentChanges instead. + + + \section2 Resetting property values + + The \c undefined value can be used to reset the property value for a state. + In the following example, when \c myText changes to the \e widerText + state, its \c width property is reset, giving the text its natural width + and displaying the whole string on a single line. + + \snippet doc/src/snippets/declarative/propertychanges.qml reset + + + \section2 Immediate property changes in transitions + + When \l{QML Animation and Transitions}{Transitions} are used to animate + state changes, they animate properties from their values in the current + state to those defined in the new state (as defined by PropertyChanges + objects). However, it is sometimes desirable to set a property value + \e immediately during a \l Transition, without animation; in these cases, + the PropertyAction element can be used to force an immediate property + change. + + See the PropertyAction documentation for more details. + + \sa {declarative/animation/states}{states example}, {qmlstate}{States}, QtDeclarative +*/ + +/*! + \qmlproperty Object QtQuick2::PropertyChanges::target + This property holds the object which contains the properties to be changed. +*/ + +class QDeclarativeReplaceSignalHandler : public QDeclarativeActionEvent +{ +public: + QDeclarativeReplaceSignalHandler() : expression(0), reverseExpression(0), + rewindExpression(0), ownedExpression(0) {} + ~QDeclarativeReplaceSignalHandler() { + delete ownedExpression; + } + + virtual QString typeName() const { return QLatin1String("ReplaceSignalHandler"); } + + QDeclarativeProperty property; + QDeclarativeExpression *expression; + QDeclarativeExpression *reverseExpression; + QDeclarativeExpression *rewindExpression; + QDeclarativeGuard<QDeclarativeExpression> ownedExpression; + + virtual void execute(Reason) { + ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, expression); + if (ownedExpression == expression) + ownedExpression = 0; + } + + virtual bool isReversable() { return true; } + virtual void reverse(Reason) { + ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, reverseExpression); + if (ownedExpression == reverseExpression) + ownedExpression = 0; + } + + virtual void saveOriginals() { + saveCurrentValues(); + reverseExpression = rewindExpression; + } + + virtual bool needsCopy() { return true; } + virtual void copyOriginals(QDeclarativeActionEvent *other) + { + QDeclarativeReplaceSignalHandler *rsh = static_cast<QDeclarativeReplaceSignalHandler*>(other); + saveCurrentValues(); + if (rsh == this) + return; + reverseExpression = rsh->reverseExpression; + if (rsh->ownedExpression == reverseExpression) { + ownedExpression = rsh->ownedExpression; + rsh->ownedExpression = 0; + } + } + + virtual void rewind() { + ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, rewindExpression); + if (ownedExpression == rewindExpression) + ownedExpression = 0; + } + virtual void saveCurrentValues() { + rewindExpression = QDeclarativePropertyPrivate::signalExpression(property); + } + + virtual bool override(QDeclarativeActionEvent*other) { + if (other == this) + return true; + if (other->typeName() != typeName()) + return false; + if (static_cast<QDeclarativeReplaceSignalHandler*>(other)->property == property) + return true; + return false; + } +}; + + +class QDeclarativePropertyChangesPrivate : public QDeclarativeStateOperationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePropertyChanges) +public: + QDeclarativePropertyChangesPrivate() : decoded(true), restore(true), + isExplicit(false) {} + + QDeclarativeGuard<QObject> object; + QByteArray data; + + bool decoded : 1; + bool restore : 1; + bool isExplicit : 1; + + void decode(); + + class ExpressionChange { + public: + ExpressionChange(const QString &_name, + QDeclarativeBinding::Identifier _id, + QDeclarativeExpression *_expr) + : name(_name), id(_id), expression(_expr) {} + QString name; + QDeclarativeBinding::Identifier id; + QDeclarativeExpression *expression; + }; + + QList<QPair<QString, QVariant> > properties; + QList<ExpressionChange> expressions; + QList<QDeclarativeReplaceSignalHandler*> signalReplacements; + + QDeclarativeProperty property(const QString &); +}; + +void +QDeclarativePropertyChangesParser::compileList(QList<QPair<QString, QVariant> > &list, + const QString &pre, + const QDeclarativeCustomParserProperty &prop) +{ + QString propName = pre + prop.name(); + + QList<QVariant> values = prop.assignedValues(); + for (int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if (value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) { + error(qvariant_cast<QDeclarativeCustomParserNode>(value), + QDeclarativePropertyChanges::tr("PropertyChanges does not support creating state-specific objects.")); + continue; + } else if(value.userType() == qMetaTypeId<QDeclarativeCustomParserProperty>()) { + + QDeclarativeCustomParserProperty prop = + qvariant_cast<QDeclarativeCustomParserProperty>(value); + QString pre = propName + QLatin1Char('.'); + compileList(list, pre, prop); + + } else { + list << qMakePair(propName, value); + } + } +} + +QByteArray +QDeclarativePropertyChangesParser::compile(const QList<QDeclarativeCustomParserProperty> &props) +{ + QList<QPair<QString, QVariant> > data; + for(int ii = 0; ii < props.count(); ++ii) + compileList(data, QString(), props.at(ii)); + + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + ds << data.count(); + for(int ii = 0; ii < data.count(); ++ii) { + QDeclarativeScript::Variant v = qvariant_cast<QDeclarativeScript::Variant>(data.at(ii).second); + QVariant var; + bool isScript = v.isScript(); + QDeclarativeBinding::Identifier id = 0; + switch(v.type()) { + case QDeclarativeScript::Variant::Boolean: + var = QVariant(v.asBoolean()); + break; + case QDeclarativeScript::Variant::Number: + var = QVariant(v.asNumber()); + break; + case QDeclarativeScript::Variant::String: + var = QVariant(v.asString()); + break; + case QDeclarativeScript::Variant::Invalid: + case QDeclarativeScript::Variant::Script: + var = QVariant(v.asScript()); + { + // Pre-rewrite the expression + id = rewriteBinding(v, data.at(ii).first); + } + break; + } + + ds << data.at(ii).first << isScript << var; + if (isScript) + ds << id; + } + + return rv; +} + +void QDeclarativePropertyChangesPrivate::decode() +{ + Q_Q(QDeclarativePropertyChanges); + if (decoded) + return; + + QDataStream ds(&data, QIODevice::ReadOnly); + + int count; + ds >> count; + for (int ii = 0; ii < count; ++ii) { + QString name; + bool isScript; + QVariant data; + QDeclarativeBinding::Identifier id = QDeclarativeBinding::Invalid; + ds >> name; + ds >> isScript; + ds >> data; + if (isScript) + ds >> id; + + QDeclarativeProperty prop = property(name); //### better way to check for signal property? + if (prop.type() & QDeclarativeProperty::SignalProperty) { + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), object, data.toString()); + QDeclarativeData *ddata = QDeclarativeData::get(q); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + QDeclarativeReplaceSignalHandler *handler = new QDeclarativeReplaceSignalHandler; + handler->property = prop; + handler->expression = expression; + signalReplacements << handler; + } else if (isScript) { + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), object, data.toString()); + QDeclarativeData *ddata = QDeclarativeData::get(q); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + expressions << ExpressionChange(name, id, expression); + } else { + properties << qMakePair(name, data); + } + } + + decoded = true; + data.clear(); +} + +void QDeclarativePropertyChangesParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QDeclarativePropertyChangesPrivate *p = + static_cast<QDeclarativePropertyChangesPrivate *>(QObjectPrivate::get(object)); + p->data = data; + p->decoded = false; +} + +QDeclarativePropertyChanges::QDeclarativePropertyChanges() +: QDeclarativeStateOperation(*(new QDeclarativePropertyChangesPrivate)) +{ +} + +QDeclarativePropertyChanges::~QDeclarativePropertyChanges() +{ + Q_D(QDeclarativePropertyChanges); + for(int ii = 0; ii < d->expressions.count(); ++ii) + delete d->expressions.at(ii).expression; + for(int ii = 0; ii < d->signalReplacements.count(); ++ii) + delete d->signalReplacements.at(ii); +} + +QObject *QDeclarativePropertyChanges::object() const +{ + Q_D(const QDeclarativePropertyChanges); + return d->object; +} + +void QDeclarativePropertyChanges::setObject(QObject *o) +{ + Q_D(QDeclarativePropertyChanges); + d->object = o; +} + +/*! + \qmlproperty bool QtQuick2::PropertyChanges::restoreEntryValues + + This property holds whether the previous values should be restored when + leaving the state. + + The default value is \c true. Setting this value to \c false creates a + temporary state that has permanent effects on property values. +*/ +bool QDeclarativePropertyChanges::restoreEntryValues() const +{ + Q_D(const QDeclarativePropertyChanges); + return d->restore; +} + +void QDeclarativePropertyChanges::setRestoreEntryValues(bool v) +{ + Q_D(QDeclarativePropertyChanges); + d->restore = v; +} + +QDeclarativeProperty +QDeclarativePropertyChangesPrivate::property(const QString &property) +{ + Q_Q(QDeclarativePropertyChanges); + QDeclarativeProperty prop(object, property, qmlContext(q)); + if (!prop.isValid()) { + qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to non-existent property \"%1\"").arg(property); + return QDeclarativeProperty(); + } else if (!(prop.type() & QDeclarativeProperty::SignalProperty) && !prop.isWritable()) { + qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to read-only property \"%1\"").arg(property); + return QDeclarativeProperty(); + } + return prop; +} + +QDeclarativePropertyChanges::ActionList QDeclarativePropertyChanges::actions() +{ + Q_D(QDeclarativePropertyChanges); + + d->decode(); + + ActionList list; + + for (int ii = 0; ii < d->properties.count(); ++ii) { + + QDeclarativeAction a(d->object, d->properties.at(ii).first, + qmlContext(this), d->properties.at(ii).second); + + if (a.property.isValid()) { + a.restore = restoreEntryValues(); + list << a; + } + } + + for (int ii = 0; ii < d->signalReplacements.count(); ++ii) { + + QDeclarativeReplaceSignalHandler *handler = d->signalReplacements.at(ii); + + if (handler->property.isValid()) { + QDeclarativeAction a; + a.event = handler; + list << a; + } + } + + for (int ii = 0; ii < d->expressions.count(); ++ii) { + + const QString &property = d->expressions.at(ii).name; + QDeclarativeProperty prop = d->property(property); + + if (prop.isValid()) { + QDeclarativeAction a; + a.restore = restoreEntryValues(); + a.property = prop; + a.fromValue = a.property.read(); + a.specifiedObject = d->object; + a.specifiedProperty = property; + + if (d->isExplicit) { + a.toValue = d->expressions.at(ii).expression->evaluate(); + } else { + QDeclarativeExpression *e = d->expressions.at(ii).expression; + + QDeclarativeBinding::Identifier id = d->expressions.at(ii).id; + QDeclarativeBinding *newBinding = id != QDeclarativeBinding::Invalid ? QDeclarativeBinding::createBinding(id, object(), qmlContext(this), e->sourceFile(), e->lineNumber()) : 0; + if (!newBinding) { + newBinding = new QDeclarativeBinding(e->expression(), object(), qmlContext(this)); + newBinding->setSourceLocation(e->sourceFile(), e->lineNumber()); + } + newBinding->setTarget(prop); + a.toBinding = newBinding; + a.deletableToBinding = true; + } + + list << a; + } + } + + return list; +} + +/*! + \qmlproperty bool QtQuick2::PropertyChanges::explicit + + If explicit is set to true, any potential bindings will be interpreted as + once-off assignments that occur when the state is entered. + + In the following example, the addition of explicit prevents \c myItem.width from + being bound to \c parent.width. Instead, it is assigned the value of \c parent.width + at the time of the state change. + \qml + PropertyChanges { + target: myItem + explicit: true + width: parent.width + } + \endqml + + By default, explicit is false. +*/ +bool QDeclarativePropertyChanges::isExplicit() const +{ + Q_D(const QDeclarativePropertyChanges); + return d->isExplicit; +} + +void QDeclarativePropertyChanges::setIsExplicit(bool e) +{ + Q_D(QDeclarativePropertyChanges); + d->isExplicit = e; +} + +bool QDeclarativePropertyChanges::containsValue(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair<QString, QVariant> PropertyEntry; + + QListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return true; + } + } + + return false; +} + +bool QDeclarativePropertyChanges::containsExpression(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + return true; + } + } + + return false; +} + +bool QDeclarativePropertyChanges::containsProperty(const QString &name) const +{ + return containsValue(name) || containsExpression(name); +} + +void QDeclarativePropertyChanges::changeValue(const QString &name, const QVariant &value) +{ + Q_D(QDeclarativePropertyChanges); + typedef QPair<QString, QVariant> PropertyEntry; + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + expressionIterator.remove(); + if (state() && state()->isStateActive()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(d->property(name), 0); + oldBinding->destroy(); + } + d->property(name).write(value); + } + + d->properties.append(PropertyEntry(name, value)); + return; + } + } + + QMutableListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + entry.second = value; + if (state() && state()->isStateActive()) + d->property(name).write(value); + return; + } + } + + QDeclarativeAction action; + action.restore = restoreEntryValues(); + action.property = d->property(name); + action.fromValue = action.property.read(); + action.specifiedObject = object(); + action.specifiedProperty = name; + action.toValue = value; + + propertyIterator.insert(PropertyEntry(name, value)); + if (state() && state()->isStateActive()) { + state()->addEntryToRevertList(action); + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(action.property); + if (oldBinding) + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + d->property(name).write(value); + } +} + +void QDeclarativePropertyChanges::changeExpression(const QString &name, const QString &expression) +{ + Q_D(QDeclarativePropertyChanges); + typedef QPair<QString, QVariant> PropertyEntry; + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + bool hadValue = false; + + QMutableListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + propertyIterator.remove(); + hadValue = true; + break; + } + } + + QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + entry.expression->setExpression(expression); + if (state() && state()->isStateActive()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(d->property(name), 0); + oldBinding->destroy(); + } + + QDeclarativeBinding *newBinding = new QDeclarativeBinding(expression, object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + QDeclarativePropertyPrivate::setBinding(d->property(name), newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } + return; + } + } + + QDeclarativeExpression *newExpression = new QDeclarativeExpression(qmlContext(this), d->object, expression); + expressionIterator.insert(ExpressionEntry(name, QDeclarativeBinding::Invalid, newExpression)); + + if (state() && state()->isStateActive()) { + if (hadValue) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + state()->changeBindingInRevertList(object(), name, oldBinding); + } + + QDeclarativeBinding *newBinding = new QDeclarativeBinding(expression, object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + QDeclarativePropertyPrivate::setBinding(d->property(name), newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } else { + QDeclarativeAction action; + action.restore = restoreEntryValues(); + action.property = d->property(name); + action.fromValue = action.property.read(); + action.specifiedObject = object(); + action.specifiedProperty = name; + + + if (d->isExplicit) { + action.toValue = newExpression->evaluate(); + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(newExpression->expression(), object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + action.toBinding = newBinding; + action.deletableToBinding = true; + + state()->addEntryToRevertList(action); + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(action.property); + if (oldBinding) + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + + QDeclarativePropertyPrivate::setBinding(action.property, newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } + } + } + // what about the signal handler? +} + +QVariant QDeclarativePropertyChanges::property(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair<QString, QVariant> PropertyEntry; + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return entry.second; + } + } + + QListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + return QVariant(entry.expression->expression()); + } + } + + return QVariant(); +} + +void QDeclarativePropertyChanges::removeProperty(const QString &name) +{ + Q_D(QDeclarativePropertyChanges); + typedef QPair<QString, QVariant> PropertyEntry; + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + expressionIterator.remove(); + state()->removeEntryFromRevertList(object(), name); + return; + } + } + + QMutableListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + propertyIterator.remove(); + state()->removeEntryFromRevertList(object(), name); + return; + } + } +} + +QVariant QDeclarativePropertyChanges::value(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair<QString, QVariant> PropertyEntry; + + QListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return entry.second; + } + } + + return QVariant(); +} + +QString QDeclarativePropertyChanges::expression(const QString &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + return entry.expression->expression(); + } + } + + return QString(); +} + +void QDeclarativePropertyChanges::detachFromState() +{ + if (state()) + state()->removeAllEntriesFromRevertList(object()); +} + +void QDeclarativePropertyChanges::attachToState() +{ + if (state()) + state()->addEntriesToRevertList(actions()); +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativepropertychanges_p.h b/src/quick/util/qdeclarativepropertychanges_p.h new file mode 100644 index 0000000000..73f308874d --- /dev/null +++ b/src/quick/util/qdeclarativepropertychanges_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPROPERTYCHANGES_H +#define QDECLARATIVEPROPERTYCHANGES_H + +#include "qdeclarativestateoperations_p.h" +#include <private/qdeclarativecustomparser_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativePropertyChangesPrivate; +class Q_QUICK_PRIVATE_EXPORT QDeclarativePropertyChanges : public QDeclarativeStateOperation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativePropertyChanges) + + Q_PROPERTY(QObject *target READ object WRITE setObject) + Q_PROPERTY(bool restoreEntryValues READ restoreEntryValues WRITE setRestoreEntryValues) + Q_PROPERTY(bool explicit READ isExplicit WRITE setIsExplicit) +public: + QDeclarativePropertyChanges(); + ~QDeclarativePropertyChanges(); + + QObject *object() const; + void setObject(QObject *); + + bool restoreEntryValues() const; + void setRestoreEntryValues(bool); + + bool isExplicit() const; + void setIsExplicit(bool); + + virtual ActionList actions(); + + bool containsProperty(const QString &name) const; + bool containsValue(const QString &name) const; + bool containsExpression(const QString &name) const; + void changeValue(const QString &name, const QVariant &value); + void changeExpression(const QString &name, const QString &expression); + void removeProperty(const QString &name); + QVariant value(const QString &name) const; + QString expression(const QString &name) const; + + void detachFromState(); + void attachToState(); + + QVariant property(const QString &name) const; +}; + +class QDeclarativePropertyChangesParser : public QDeclarativeCustomParser +{ +public: + QDeclarativePropertyChangesParser() + : QDeclarativeCustomParser(AcceptsAttachedProperties) {} + + void compileList(QList<QPair<QString, QVariant> > &list, const QString &pre, const QDeclarativeCustomParserProperty &prop); + + virtual QByteArray compile(const QList<QDeclarativeCustomParserProperty> &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePropertyChanges) + +QT_END_HEADER + +#endif // QDECLARATIVEPROPERTYCHANGES_H diff --git a/src/quick/util/qdeclarativesmoothedanimation.cpp b/src/quick/util/qdeclarativesmoothedanimation.cpp new file mode 100644 index 0000000000..45c6885fbb --- /dev/null +++ b/src/quick/util/qdeclarativesmoothedanimation.cpp @@ -0,0 +1,503 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativesmoothedanimation_p.h" +#include "qdeclarativesmoothedanimation_p_p.h" + +#include "qdeclarativeanimation_p_p.h" + +#include <qdeclarativeproperty.h> +#include <private/qdeclarativeproperty_p.h> + +#include <private/qdeclarativeglobal_p.h> + +#include <QtCore/qdebug.h> + +#include <math.h> + +#define DELAY_STOP_TIMER_INTERVAL 32 + +QT_BEGIN_NAMESPACE + +QSmoothedAnimation::QSmoothedAnimation(QObject *parent) + : QAbstractAnimation(parent), to(0), velocity(200), userDuration(-1), maximumEasingTime(-1), + reversingMode(QDeclarativeSmoothedAnimation::Eased), initialVelocity(0), + trackVelocity(0), initialValue(0), invert(false), finalDuration(-1), lastTime(0) +{ +} + +void QSmoothedAnimation::restart() +{ + initialVelocity = trackVelocity; + if (state() != QAbstractAnimation::Running) + start(); + else + init(); +} + +void QSmoothedAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/) +{ + if (newState == QAbstractAnimation::Running) + init(); +} + +void QSmoothedAnimation::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == delayedStopTimer.timerId()) { + delayedStopTimer.stop(); + stop(); + } else { + QAbstractAnimation::timerEvent(event); + } +} + +void QSmoothedAnimation::delayedStop() +{ + if (!delayedStopTimer.isActive()) + delayedStopTimer.start(DELAY_STOP_TIMER_INTERVAL, this); +} + +int QSmoothedAnimation::duration() const +{ + return -1; +} + +bool QSmoothedAnimation::recalc() +{ + s = to - initialValue; + vi = initialVelocity; + + s = (invert? -1.0: 1.0) * s; + + if (userDuration > 0 && velocity > 0) { + tf = s / velocity; + if (tf > (userDuration / 1000.)) tf = (userDuration / 1000.); + } else if (userDuration > 0) { + tf = userDuration / 1000.; + } else if (velocity > 0) { + tf = s / velocity; + } else { + return false; + } + + finalDuration = ceil(tf * 1000.0); + + if (maximumEasingTime == 0) { + a = 0; + d = 0; + tp = 0; + td = tf; + vp = velocity; + sp = 0; + sd = s; + } else if (maximumEasingTime != -1 && tf > (maximumEasingTime / 1000.)) { + qreal met = maximumEasingTime / 1000.; + /* tp| |td + * vp_ _______ + * / \ + * vi_ / \ + * \ + * \ _ 0 + * |ta| |ta| + */ + qreal ta = met / 2.; + a = (s - (vi * tf - 0.5 * vi * ta)) / (tf * ta - ta * ta); + + vp = vi + a * ta; + d = vp / ta; + tp = ta; + sp = vi * ta + 0.5 * a * tp * tp; + sd = sp + vp * (tf - 2 * ta); + td = tf - ta; + } else { + qreal c1 = 0.25 * tf * tf; + qreal c2 = 0.5 * vi * tf - s; + qreal c3 = -0.25 * vi * vi; + + qreal a1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1); + + qreal tp1 = 0.5 * tf - 0.5 * vi / a1; + qreal vp1 = a1 * tp1 + vi; + + qreal sp1 = 0.5 * a1 * tp1 * tp1 + vi * tp1; + + a = a1; + d = a1; + tp = tp1; + td = tp1; + vp = vp1; + sp = sp1; + sd = sp1; + } + return true; +} + +qreal QSmoothedAnimation::easeFollow(qreal time_seconds) +{ + qreal value; + if (time_seconds < tp) { + trackVelocity = vi + time_seconds * a; + value = 0.5 * a * time_seconds * time_seconds + vi * time_seconds; + } else if (time_seconds < td) { + time_seconds -= tp; + trackVelocity = vp; + value = sp + time_seconds * vp; + } else if (time_seconds < tf) { + time_seconds -= td; + trackVelocity = vp - time_seconds * a; + value = sd - 0.5 * d * time_seconds * time_seconds + vp * time_seconds; + } else { + trackVelocity = 0; + value = s; + delayedStop(); + } + + // to normalize 's' between [0..1], divide 'value' by 's' + return value; +} + +void QSmoothedAnimation::updateCurrentTime(int t) +{ + qreal time_seconds = qreal(t - lastTime) / 1000.; + + qreal value = easeFollow(time_seconds); + value *= (invert? -1.0: 1.0); + QDeclarativePropertyPrivate::write(target, initialValue + value, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); +} + +void QSmoothedAnimation::init() +{ + if (velocity == 0) { + stop(); + return; + } + + if (delayedStopTimer.isActive()) + delayedStopTimer.stop(); + + initialValue = target.read().toReal(); + lastTime = this->currentTime(); + + if (to == initialValue) { + stop(); + return; + } + + bool hasReversed = trackVelocity != 0. && + ((!invert) == ((initialValue - to) > 0)); + + if (hasReversed) { + switch (reversingMode) { + default: + case QDeclarativeSmoothedAnimation::Eased: + initialVelocity = -trackVelocity; + break; + case QDeclarativeSmoothedAnimation::Sync: + QDeclarativePropertyPrivate::write(target, to, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); + trackVelocity = 0; + stop(); + return; + case QDeclarativeSmoothedAnimation::Immediate: + initialVelocity = 0; + break; + } + } + + trackVelocity = initialVelocity; + + invert = (to < initialValue); + + if (!recalc()) { + QDeclarativePropertyPrivate::write(target, to, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); + stop(); + return; + } +} + +/*! + \qmlclass SmoothedAnimation QDeclarativeSmoothedAnimation + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \inherits NumberAnimation + \brief The SmoothedAnimation element allows a property to smoothly track a value. + + A SmoothedAnimation animates a property's value to a set target value + using an ease in/out quad easing curve. When the target value changes, + the easing curves used to animate between the old and new target values + are smoothly spliced together to create a smooth movement to the new + target value that maintains the current velocity. + + The follow example shows one \l Rectangle tracking the position of another + using SmoothedAnimation. The green rectangle's \c x and \c y values are + bound to those of the red rectangle. Whenever these values change, the + green rectangle smoothly animates to its new position: + + \snippet doc/src/snippets/declarative/smoothedanimation.qml 0 + + A SmoothedAnimation can be configured by setting the \l velocity at which the + animation should occur, or the \l duration that the animation should take. + If both the \l velocity and \l duration are specified, the one that results in + the quickest animation is chosen for each change in the target value. + + For example, animating from 0 to 800 will take 4 seconds if a velocity + of 200 is set, will take 8 seconds with a duration of 8000 set, and will + take 4 seconds with both a velocity of 200 and a duration of 8000 set. + Animating from 0 to 20000 will take 10 seconds if a velocity of 200 is set, + will take 8 seconds with a duration of 8000 set, and will take 8 seconds + with both a velocity of 200 and a duration of 8000 set. + + The default velocity of SmoothedAnimation is 200 units/second. Note that if the range of the + value being animated is small, then the velocity will need to be adjusted + appropriately. For example, the opacity of an item ranges from 0 - 1.0. + To enable a smooth animation in this range the velocity will need to be + set to a value such as 0.5 units/second. Animating from 0 to 1.0 with a velocity + of 0.5 will take 2000 ms to complete. + + Like any other animation element, a SmoothedAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa SpringAnimation, NumberAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ + +QDeclarativeSmoothedAnimation::QDeclarativeSmoothedAnimation(QObject *parent) +: QDeclarativeNumberAnimation(*(new QDeclarativeSmoothedAnimationPrivate), parent) +{ +} + +QDeclarativeSmoothedAnimation::~QDeclarativeSmoothedAnimation() +{ +} + +QDeclarativeSmoothedAnimationPrivate::QDeclarativeSmoothedAnimationPrivate() + : wrapperGroup(new QParallelAnimationGroup), anim(new QSmoothedAnimation) +{ + Q_Q(QDeclarativeSmoothedAnimation); + QDeclarative_setParent_noEvent(wrapperGroup, q); + QDeclarative_setParent_noEvent(anim, q); +} + +void QDeclarativeSmoothedAnimationPrivate::updateRunningAnimations() +{ + foreach(QSmoothedAnimation* ease, activeAnimations.values()){ + ease->maximumEasingTime = anim->maximumEasingTime; + ease->reversingMode = anim->reversingMode; + ease->velocity = anim->velocity; + ease->userDuration = anim->userDuration; + ease->init(); + } +} + +QAbstractAnimation* QDeclarativeSmoothedAnimation::qtAnimation() +{ + Q_D(QDeclarativeSmoothedAnimation); + return d->wrapperGroup; +} + +void QDeclarativeSmoothedAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeSmoothedAnimation); + QDeclarativeNumberAnimation::transition(actions, modified, direction); + + if (!d->actions) + return; + + QSet<QAbstractAnimation*> anims; + for (int i = 0; i < d->actions->size(); i++) { + QSmoothedAnimation *ease; + bool needsRestart; + if (!d->activeAnimations.contains((*d->actions)[i].property)) { + ease = new QSmoothedAnimation(); + d->wrapperGroup->addAnimation(ease); + d->activeAnimations.insert((*d->actions)[i].property, ease); + needsRestart = false; + } else { + ease = d->activeAnimations.value((*d->actions)[i].property); + needsRestart = true; + } + ease->target = (*d->actions)[i].property; + ease->to = (*d->actions)[i].toValue.toReal(); + + // copying public members from main value holder animation + ease->maximumEasingTime = d->anim->maximumEasingTime; + ease->reversingMode = d->anim->reversingMode; + ease->velocity = d->anim->velocity; + ease->userDuration = d->anim->userDuration; + + ease->initialVelocity = ease->trackVelocity; + + if (needsRestart) + ease->init(); + anims.insert(ease); + } + + for (int i = d->wrapperGroup->animationCount() - 1; i >= 0 ; --i) { + if (!anims.contains(d->wrapperGroup->animationAt(i))) { + QSmoothedAnimation *ease = static_cast<QSmoothedAnimation*>(d->wrapperGroup->animationAt(i)); + d->activeAnimations.remove(ease->target); + d->wrapperGroup->takeAnimation(i); + delete ease; + } + } +} + +/*! + \qmlproperty enumeration QtQuick2::SmoothedAnimation::reversingMode + + Sets how the SmoothedAnimation behaves if an animation direction is reversed. + + Possible values are: + + \list + \o SmoothedAnimation.Eased (default) - the animation will smoothly decelerate, and then reverse direction + \o SmoothedAnimation.Immediate - the animation will immediately begin accelerating in the reverse direction, beginning with a velocity of 0 + \o SmoothedAnimation.Sync - the property is immediately set to the target value + \endlist +*/ +QDeclarativeSmoothedAnimation::ReversingMode QDeclarativeSmoothedAnimation::reversingMode() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return (QDeclarativeSmoothedAnimation::ReversingMode) d->anim->reversingMode; +} + +void QDeclarativeSmoothedAnimation::setReversingMode(ReversingMode m) +{ + Q_D(QDeclarativeSmoothedAnimation); + if (d->anim->reversingMode == m) + return; + + d->anim->reversingMode = m; + emit reversingModeChanged(); + d->updateRunningAnimations(); +} + +/*! + \qmlproperty int QtQuick2::SmoothedAnimation::duration + + This property holds the animation duration, in msecs, used when tracking the source. + + Setting this to -1 (the default) disables the duration value. + + If the velocity value and the duration value are both enabled, then the animation will + use whichever gives the shorter duration. +*/ +int QDeclarativeSmoothedAnimation::duration() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return d->anim->userDuration; +} + +void QDeclarativeSmoothedAnimation::setDuration(int duration) +{ + Q_D(QDeclarativeSmoothedAnimation); + if (duration != -1) + QDeclarativeNumberAnimation::setDuration(duration); + if(duration == d->anim->userDuration) + return; + d->anim->userDuration = duration; + d->updateRunningAnimations(); +} + +qreal QDeclarativeSmoothedAnimation::velocity() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return d->anim->velocity; +} + +/*! + \qmlproperty real QtQuick2::SmoothedAnimation::velocity + + This property holds the average velocity allowed when tracking the 'to' value. + + The default velocity of SmoothedAnimation is 200 units/second. + + Setting this to -1 disables the velocity value. + + If the velocity value and the duration value are both enabled, then the animation will + use whichever gives the shorter duration. +*/ +void QDeclarativeSmoothedAnimation::setVelocity(qreal v) +{ + Q_D(QDeclarativeSmoothedAnimation); + if (d->anim->velocity == v) + return; + + d->anim->velocity = v; + emit velocityChanged(); + d->updateRunningAnimations(); +} + +/*! + \qmlproperty int QtQuick2::SmoothedAnimation::maximumEasingTime + + This property specifies the maximum time, in msecs, any "eases" during the follow should take. + Setting this property causes the velocity to "level out" after at a time. Setting + a negative value reverts to the normal mode of easing over the entire animation + duration. + + The default value is -1. +*/ +int QDeclarativeSmoothedAnimation::maximumEasingTime() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return d->anim->maximumEasingTime; +} + +void QDeclarativeSmoothedAnimation::setMaximumEasingTime(int v) +{ + Q_D(QDeclarativeSmoothedAnimation); + if(v == d->anim->maximumEasingTime) + return; + d->anim->maximumEasingTime = v; + emit maximumEasingTimeChanged(); + d->updateRunningAnimations(); +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativesmoothedanimation_p.h b/src/quick/util/qdeclarativesmoothedanimation_p.h new file mode 100644 index 0000000000..d4c7716540 --- /dev/null +++ b/src/quick/util/qdeclarativesmoothedanimation_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESMOOTHEDANIMATION_H +#define QDECLARATIVESMOOTHEDANIMATION_H + +#include <qdeclarative.h> +#include "qdeclarativeanimation_p.h" + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeProperty; +class QDeclarativeSmoothedAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeSmoothedAnimation : public QDeclarativeNumberAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeSmoothedAnimation) + Q_ENUMS(ReversingMode) + + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity NOTIFY velocityChanged) + Q_PROPERTY(ReversingMode reversingMode READ reversingMode WRITE setReversingMode NOTIFY reversingModeChanged) + Q_PROPERTY(qreal maximumEasingTime READ maximumEasingTime WRITE setMaximumEasingTime NOTIFY maximumEasingTimeChanged) + +public: + enum ReversingMode { Eased, Immediate, Sync }; + + QDeclarativeSmoothedAnimation(QObject *parent = 0); + ~QDeclarativeSmoothedAnimation(); + + ReversingMode reversingMode() const; + void setReversingMode(ReversingMode); + + virtual int duration() const; + virtual void setDuration(int); + + qreal velocity() const; + void setVelocity(qreal); + + int maximumEasingTime() const; + void setMaximumEasingTime(int); + + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + QAbstractAnimation* qtAnimation(); + +Q_SIGNALS: + void velocityChanged(); + void reversingModeChanged(); + void maximumEasingTimeChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSmoothedAnimation) + +QT_END_HEADER + +#endif // QDECLARATIVESMOOTHEDANIMATION_H diff --git a/src/quick/util/qdeclarativesmoothedanimation_p_p.h b/src/quick/util/qdeclarativesmoothedanimation_p_p.h new file mode 100644 index 0000000000..08b1450342 --- /dev/null +++ b/src/quick/util/qdeclarativesmoothedanimation_p_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESMOOTHEDANIMATION_P_H +#define QDECLARATIVESMOOTHEDANIMATION_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 "qdeclarativesmoothedanimation_p.h" +#include "qdeclarativeanimation_p.h" + +#include "qdeclarativeanimation_p_p.h" + +#include <qparallelanimationgroup.h> + +#include <private/qobject_p.h> +#include <QBasicTimer> + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QSmoothedAnimation : public QAbstractAnimation +{ +public: + QSmoothedAnimation(QObject *parent=0); + + qreal to; + qreal velocity; + int userDuration; + + int maximumEasingTime; + QDeclarativeSmoothedAnimation::ReversingMode reversingMode; + + qreal initialVelocity; + qreal trackVelocity; + + QDeclarativeProperty target; + + int duration() const; + void restart(); + void init(); + +protected: + virtual void updateCurrentTime(int); + virtual void updateState(QAbstractAnimation::State, QAbstractAnimation::State); + virtual void timerEvent(QTimerEvent *); + +private: + qreal easeFollow(qreal); + qreal initialValue; + + bool invert; + + int finalDuration; + + // Parameters for use in updateCurrentTime() + qreal a; // Acceleration + qreal d; // Deceleration + qreal tf; // Total time + qreal tp; // Time at which peak velocity occurs + qreal td; // Time at which deceleration begins + qreal vp; // Velocity at tp + qreal sp; // Displacement at tp + qreal sd; // Displacement at td + qreal vi; // "Normalized" initialvelocity + qreal s; // Total s + + int lastTime; + + bool recalc(); + void delayedStop(); + + QBasicTimer delayedStopTimer; +}; + +class QDeclarativeSmoothedAnimationPrivate : public QDeclarativePropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeSmoothedAnimation) +public: + QDeclarativeSmoothedAnimationPrivate(); + void updateRunningAnimations(); + + QParallelAnimationGroup *wrapperGroup; + QSmoothedAnimation *anim; + QHash<QDeclarativeProperty, QSmoothedAnimation*> activeAnimations; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESMOOTHEDANIMATION_P_H diff --git a/src/quick/util/qdeclarativespringanimation.cpp b/src/quick/util/qdeclarativespringanimation.cpp new file mode 100644 index 0000000000..b0b12c7ba4 --- /dev/null +++ b/src/quick/util/qdeclarativespringanimation.cpp @@ -0,0 +1,462 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativespringanimation_p.h" + +#include "qdeclarativeanimation_p_p.h" +#include <private/qdeclarativeproperty_p.h> + +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +#include <limits.h> +#include <math.h> + +QT_BEGIN_NAMESPACE + + +class QDeclarativeSpringAnimationPrivate : public QDeclarativePropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation) +public: + + + struct SpringAnimation { + SpringAnimation() + : currentValue(0), to(0), velocity(0), start(0), duration(0) {} + qreal currentValue; + qreal to; + qreal velocity; + int start; + int duration; + }; + QHash<QDeclarativeProperty, SpringAnimation> activeAnimations; + + qreal maxVelocity; + qreal velocityms; + int lastTime; + qreal mass; + qreal spring; + qreal damping; + qreal epsilon; + qreal modulus; + + bool useMass : 1; + bool haveModulus : 1; + + enum Mode { + Track, + Velocity, + Spring + }; + Mode mode; + + QDeclarativeSpringAnimationPrivate() + : maxVelocity(0), velocityms(0), lastTime(0) + , mass(1.0), spring(0.), damping(0.), epsilon(0.01) + , modulus(0.0), useMass(false), haveModulus(false) + , mode(Track), clock(0) + { } + + void tick(int time); + bool animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed); + void updateMode(); + + typedef QTickAnimationProxy<QDeclarativeSpringAnimationPrivate, &QDeclarativeSpringAnimationPrivate::tick> Clock; + Clock *clock; +}; + +void QDeclarativeSpringAnimationPrivate::tick(int time) +{ + if (mode == Track) { + clock->stop(); + return; + } + int elapsed = time - lastTime; + if (!elapsed) + return; + + if (mode == Spring) { + if (elapsed < 16) // capped at 62fps. + return; + int count = elapsed / 16; + lastTime = time - (elapsed - count * 16); + } else { + lastTime = time; + } + + QMutableHashIterator<QDeclarativeProperty, SpringAnimation> it(activeAnimations); + while (it.hasNext()) { + it.next(); + if (animate(it.key(), it.value(), elapsed)) + it.remove(); + } + + if (activeAnimations.isEmpty()) + clock->stop(); +} + +bool QDeclarativeSpringAnimationPrivate::animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed) +{ + qreal srcVal = animation.to; + + bool stop = false; + + if (haveModulus) { + animation.currentValue = fmod(animation.currentValue, modulus); + srcVal = fmod(srcVal, modulus); + } + if (mode == Spring) { + // Real men solve the spring DEs using RK4. + // We'll do something much simpler which gives a result that looks fine. + int count = elapsed / 16; + for (int i = 0; i < count; ++i) { + qreal diff = srcVal - animation.currentValue; + if (haveModulus && qAbs(diff) > modulus / 2) { + if (diff < 0) + diff += modulus; + else + diff -= modulus; + } + if (useMass) + animation.velocity = animation.velocity + (spring * diff - damping * animation.velocity) / mass; + else + animation.velocity = animation.velocity + spring * diff - damping * animation.velocity; + if (maxVelocity > 0.) { + // limit velocity + if (animation.velocity > maxVelocity) + animation.velocity = maxVelocity; + else if (animation.velocity < -maxVelocity) + animation.velocity = -maxVelocity; + } + animation.currentValue += animation.velocity * 16.0 / 1000.0; + if (haveModulus) { + animation.currentValue = fmod(animation.currentValue, modulus); + if (animation.currentValue < 0.0) + animation.currentValue += modulus; + } + } + if (qAbs(animation.velocity) < epsilon && qAbs(srcVal - animation.currentValue) < epsilon) { + animation.velocity = 0.0; + animation.currentValue = srcVal; + stop = true; + } + } else { + qreal moveBy = elapsed * velocityms; + qreal diff = srcVal - animation.currentValue; + if (haveModulus && qAbs(diff) > modulus / 2) { + if (diff < 0) + diff += modulus; + else + diff -= modulus; + } + if (diff > 0) { + animation.currentValue += moveBy; + if (haveModulus) + animation.currentValue = fmod(animation.currentValue, modulus); + } else { + animation.currentValue -= moveBy; + if (haveModulus && animation.currentValue < 0.0) + animation.currentValue = fmod(animation.currentValue, modulus) + modulus; + } + if (lastTime - animation.start >= animation.duration) { + animation.currentValue = animation.to; + stop = true; + } + } + + qreal old_to = animation.to; + + QDeclarativePropertyPrivate::write(property, animation.currentValue, + QDeclarativePropertyPrivate::BypassInterceptor | + QDeclarativePropertyPrivate::DontRemoveBinding); + + return (stop && old_to == animation.to); // do not stop if we got restarted +} + +void QDeclarativeSpringAnimationPrivate::updateMode() +{ + if (spring == 0. && maxVelocity == 0.) + mode = Track; + else if (spring > 0.) + mode = Spring; + else { + mode = Velocity; + QHash<QDeclarativeProperty, SpringAnimation>::iterator it; + for (it = activeAnimations.begin(); it != activeAnimations.end(); ++it) { + SpringAnimation &animation = *it; + animation.start = lastTime; + qreal dist = qAbs(animation.currentValue - animation.to); + if (haveModulus && dist > modulus / 2) + dist = modulus - fmod(dist, modulus); + animation.duration = dist / velocityms; + } + } +} + +/*! + \qmlclass SpringAnimation QDeclarativeSpringAnimation + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \inherits NumberAnimation + + \brief The SpringAnimation element allows a property to track a value in a spring-like motion. + + SpringAnimation mimics the oscillatory behavior of a spring, with the appropriate \l spring constant to + control the acceleration and the \l damping to control how quickly the effect dies away. + + You can also limit the maximum \l velocity of the animation. + + The following \l Rectangle moves to the position of the mouse using a + SpringAnimation when the mouse is clicked. The use of the \l Behavior + on the \c x and \c y values indicates that whenever these values are + changed, a SpringAnimation should be applied. + + \snippet doc/src/snippets/declarative/springanimation.qml 0 + + Like any other animation element, a SpringAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa SmoothedAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}, {declarative/toys/clocks}{Clocks example} +*/ + +QDeclarativeSpringAnimation::QDeclarativeSpringAnimation(QObject *parent) +: QDeclarativeNumberAnimation(*(new QDeclarativeSpringAnimationPrivate),parent) +{ + Q_D(QDeclarativeSpringAnimation); + d->clock = new QDeclarativeSpringAnimationPrivate::Clock(d, this); +} + +QDeclarativeSpringAnimation::~QDeclarativeSpringAnimation() +{ +} + +/*! + \qmlproperty real QtQuick2::SpringAnimation::velocity + + This property holds the maximum velocity allowed when tracking the source. + + The default value is 0 (no maximum velocity). +*/ + +qreal QDeclarativeSpringAnimation::velocity() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->maxVelocity; +} + +void QDeclarativeSpringAnimation::setVelocity(qreal velocity) +{ + Q_D(QDeclarativeSpringAnimation); + d->maxVelocity = velocity; + d->velocityms = velocity / 1000.0; + d->updateMode(); +} + +/*! + \qmlproperty real QtQuick2::SpringAnimation::spring + + This property describes how strongly the target is pulled towards the + source. The default value is 0 (that is, the spring-like motion is disabled). + + The useful value range is 0 - 5.0. + + When this property is set and the \l velocity value is greater than 0, + the \l velocity limits the maximum speed. +*/ +qreal QDeclarativeSpringAnimation::spring() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->spring; +} + +void QDeclarativeSpringAnimation::setSpring(qreal spring) +{ + Q_D(QDeclarativeSpringAnimation); + d->spring = spring; + d->updateMode(); +} + +/*! + \qmlproperty real QtQuick2::SpringAnimation::damping + This property holds the spring damping value. + + This value describes how quickly the spring-like motion comes to rest. + The default value is 0. + + The useful value range is 0 - 1.0. The lower the value, the faster it + comes to rest. +*/ +qreal QDeclarativeSpringAnimation::damping() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->damping; +} + +void QDeclarativeSpringAnimation::setDamping(qreal damping) +{ + Q_D(QDeclarativeSpringAnimation); + if (damping > 1.) + damping = 1.; + + d->damping = damping; +} + + +/*! + \qmlproperty real QtQuick2::SpringAnimation::epsilon + This property holds the spring epsilon. + + The epsilon is the rate and amount of change in the value which is close enough + to 0 to be considered equal to zero. This will depend on the usage of the value. + For pixel positions, 0.25 would suffice. For scale, 0.005 will suffice. + + The default is 0.01. Tuning this value can provide small performance improvements. +*/ +qreal QDeclarativeSpringAnimation::epsilon() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->epsilon; +} + +void QDeclarativeSpringAnimation::setEpsilon(qreal epsilon) +{ + Q_D(QDeclarativeSpringAnimation); + d->epsilon = epsilon; +} + +/*! + \qmlproperty real QtQuick2::SpringAnimation::modulus + This property holds the modulus value. The default value is 0. + + Setting a \a modulus forces the target value to "wrap around" at the modulus. + For example, setting the modulus to 360 will cause a value of 370 to wrap around to 10. +*/ +qreal QDeclarativeSpringAnimation::modulus() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->modulus; +} + +void QDeclarativeSpringAnimation::setModulus(qreal modulus) +{ + Q_D(QDeclarativeSpringAnimation); + if (d->modulus != modulus) { + d->haveModulus = modulus != 0.0; + d->modulus = modulus; + d->updateMode(); + emit modulusChanged(); + } +} + +/*! + \qmlproperty real QtQuick2::SpringAnimation::mass + This property holds the "mass" of the property being moved. + + The value is 1.0 by default. + + A greater mass causes slower movement and a greater spring-like + motion when an item comes to rest. +*/ +qreal QDeclarativeSpringAnimation::mass() const +{ + Q_D(const QDeclarativeSpringAnimation); + return d->mass; +} + +void QDeclarativeSpringAnimation::setMass(qreal mass) +{ + Q_D(QDeclarativeSpringAnimation); + if (d->mass != mass && mass > 0.0) { + d->useMass = mass != 1.0; + d->mass = mass; + emit massChanged(); + } +} + +void QDeclarativeSpringAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeSpringAnimation); + Q_UNUSED(direction); + + if (d->clock->state() != QAbstractAnimation::Running) { + d->lastTime = 0; + } + + QDeclarativeNumberAnimation::transition(actions, modified, direction); + + if (!d->actions) + return; + + if (!d->actions->isEmpty()) { + for (int i = 0; i < d->actions->size(); ++i) { + const QDeclarativeProperty &property = d->actions->at(i).property; + QDeclarativeSpringAnimationPrivate::SpringAnimation &animation + = d->activeAnimations[property]; + animation.to = d->actions->at(i).toValue.toReal(); + animation.start = d->lastTime; + if (d->fromIsDefined) + animation.currentValue = d->actions->at(i).fromValue.toReal(); + else + animation.currentValue = property.read().toReal(); + if (d->mode == QDeclarativeSpringAnimationPrivate::Velocity) { + qreal dist = qAbs(animation.currentValue - animation.to); + if (d->haveModulus && dist > d->modulus / 2) + dist = d->modulus - fmod(dist, d->modulus); + animation.duration = dist / d->velocityms; + } + } + } +} + + +QAbstractAnimation *QDeclarativeSpringAnimation::qtAnimation() +{ + Q_D(QDeclarativeSpringAnimation); + return d->clock; +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativespringanimation_p.h b/src/quick/util/qdeclarativespringanimation_p.h new file mode 100644 index 0000000000..13e983b9a5 --- /dev/null +++ b/src/quick/util/qdeclarativespringanimation_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESPRINGANIMATION_H +#define QDECLARATIVESPRINGANIMATION_H + +#include <qdeclarative.h> +#include "qdeclarativeanimation_p.h" + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeSpringAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeSpringAnimation : public QDeclarativeNumberAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeSpringAnimation) + Q_INTERFACES(QDeclarativePropertyValueSource) + + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity) + Q_PROPERTY(qreal spring READ spring WRITE setSpring) + Q_PROPERTY(qreal damping READ damping WRITE setDamping) + Q_PROPERTY(qreal epsilon READ epsilon WRITE setEpsilon) + Q_PROPERTY(qreal modulus READ modulus WRITE setModulus NOTIFY modulusChanged) + Q_PROPERTY(qreal mass READ mass WRITE setMass NOTIFY massChanged) + +public: + QDeclarativeSpringAnimation(QObject *parent=0); + ~QDeclarativeSpringAnimation(); + + qreal velocity() const; + void setVelocity(qreal velocity); + + qreal spring() const; + void setSpring(qreal spring); + + qreal damping() const; + void setDamping(qreal damping); + + qreal epsilon() const; + void setEpsilon(qreal epsilon); + + qreal mass() const; + void setMass(qreal modulus); + + qreal modulus() const; + void setModulus(qreal modulus); + + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + +protected: + virtual QAbstractAnimation *qtAnimation(); + +Q_SIGNALS: + void modulusChanged(); + void massChanged(); + void syncChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSpringAnimation) + +QT_END_HEADER + +#endif // QDECLARATIVESPRINGANIMATION_H diff --git a/src/quick/util/qdeclarativestate.cpp b/src/quick/util/qdeclarativestate.cpp new file mode 100644 index 0000000000..1250c0c2b5 --- /dev/null +++ b/src/quick/util/qdeclarativestate.cpp @@ -0,0 +1,734 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativestate_p_p.h" +#include "qdeclarativestate_p.h" + +#include "qdeclarativestategroup_p.h" +#include "qdeclarativestateoperations_p.h" + +#include <private/qdeclarativeglobal_p.h> + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +QDeclarativeAction::QDeclarativeAction() +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), fromBinding(0), event(0), + specifiedObject(0) +{ +} + +QDeclarativeAction::QDeclarativeAction(QObject *target, const QString &propertyName, + const QVariant &value) +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), + property(target, propertyName, qmlEngine(target)), toValue(value), + fromBinding(0), event(0), + specifiedObject(target), specifiedProperty(propertyName) +{ + if (property.isValid()) + fromValue = property.read(); +} + +QDeclarativeAction::QDeclarativeAction(QObject *target, const QString &propertyName, + QDeclarativeContext *context, const QVariant &value) +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), + property(target, propertyName, context), toValue(value), + fromBinding(0), event(0), + specifiedObject(target), specifiedProperty(propertyName) +{ + if (property.isValid()) + fromValue = property.read(); +} + + +QDeclarativeActionEvent::~QDeclarativeActionEvent() +{ +} + +QString QDeclarativeActionEvent::typeName() const +{ + return QString(); +} + +void QDeclarativeActionEvent::execute(Reason) +{ +} + +bool QDeclarativeActionEvent::isReversable() +{ + return false; +} + +void QDeclarativeActionEvent::reverse(Reason) +{ +} + +bool QDeclarativeActionEvent::changesBindings() +{ + return false; +} + +void QDeclarativeActionEvent::clearBindings() +{ +} + +bool QDeclarativeActionEvent::override(QDeclarativeActionEvent *other) +{ + Q_UNUSED(other); + return false; +} + +QDeclarativeStateOperation::QDeclarativeStateOperation(QObjectPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + \qmlclass State QDeclarativeState + \inqmlmodule QtQuick 2 + \ingroup qml-state-elements + \brief The State element defines configurations of objects and properties. + + A \e state is a set of batched changes from the default configuration. + + All items have a default state that defines the default configuration of objects + and property values. New states can be defined by adding State items to the \l {Item::states}{states} property to + allow items to switch between different configurations. These configurations + can, for example, be used to apply different sets of property values or execute + different scripts. + + The following example displays a single \l Rectangle. In the default state, the rectangle + is colored black. In the "clicked" state, a PropertyChanges element changes the + rectangle's color to red. Clicking within the MouseArea toggles the rectangle's state + between the default state and the "clicked" state, thus toggling the color of the + rectangle between black and red. + + \snippet doc/src/snippets/declarative/state.qml 0 + + Notice the default state is referred to using an empty string (""). + + States are commonly used together with \l{QML Animation and Transitions}{Transitions} to provide + animations when state changes occur. + + \note Setting the state of an object from within another state of the same object is + not allowed. + + \sa {declarative/animation/states}{states example}, {qmlstates}{States}, + {QML Animation and Transitions}{Transitions}, QtDeclarative +*/ +QDeclarativeState::QDeclarativeState(QObject *parent) +: QObject(*(new QDeclarativeStatePrivate), parent) +{ + Q_D(QDeclarativeState); + d->transitionManager.setState(this); +} + +QDeclarativeState::~QDeclarativeState() +{ + Q_D(QDeclarativeState); + if (d->group) + d->group->removeState(this); +} + +/*! + \qmlproperty string QtQuick2::State::name + This property holds the name of the state. + + Each state should have a unique name within its item. +*/ +QString QDeclarativeState::name() const +{ + Q_D(const QDeclarativeState); + return d->name; +} + +void QDeclarativeState::setName(const QString &n) +{ + Q_D(QDeclarativeState); + d->name = n; + d->named = true; +} + +bool QDeclarativeState::isNamed() const +{ + Q_D(const QDeclarativeState); + return d->named; +} + +bool QDeclarativeState::isWhenKnown() const +{ + Q_D(const QDeclarativeState); + return d->when != 0; +} + +/*! + \qmlproperty bool QtQuick2::State::when + This property holds when the state should be applied. + + This should be set to an expression that evaluates to \c true when you want the state to + be applied. For example, the following \l Rectangle changes in and out of the "hidden" + state when the \l MouseArea is pressed: + + \snippet doc/src/snippets/declarative/state-when.qml 0 + + If multiple states in a group have \c when clauses that evaluate to \c true + at the same time, the first matching state will be applied. For example, in + the following snippet \c state1 will always be selected rather than + \c state2 when sharedCondition becomes \c true. + \qml + Item { + states: [ + State { name: "state1"; when: sharedCondition }, + State { name: "state2"; when: sharedCondition } + ] + // ... + } + \endqml +*/ +QDeclarativeBinding *QDeclarativeState::when() const +{ + Q_D(const QDeclarativeState); + return d->when; +} + +void QDeclarativeState::setWhen(QDeclarativeBinding *when) +{ + Q_D(QDeclarativeState); + d->when = when; + if (d->group) + d->group->updateAutoState(); +} + +/*! + \qmlproperty string QtQuick2::State::extend + This property holds the state that this state extends. + + When a state extends another state, it inherits all the changes of that state. + + The state being extended is treated as the base state in regards to + the changes specified by the extending state. +*/ +QString QDeclarativeState::extends() const +{ + Q_D(const QDeclarativeState); + return d->extends; +} + +void QDeclarativeState::setExtends(const QString &extends) +{ + Q_D(QDeclarativeState); + d->extends = extends; +} + +/*! + \qmlproperty list<Change> QtQuick2::State::changes + This property holds the changes to apply for this state + \default + + By default these changes are applied against the default state. If the state + extends another state, then the changes are applied against the state being + extended. +*/ +QDeclarativeListProperty<QDeclarativeStateOperation> QDeclarativeState::changes() +{ + Q_D(QDeclarativeState); + return QDeclarativeListProperty<QDeclarativeStateOperation>(this, &d->operations, QDeclarativeStatePrivate::operations_append, + QDeclarativeStatePrivate::operations_count, QDeclarativeStatePrivate::operations_at, + QDeclarativeStatePrivate::operations_clear); +} + +int QDeclarativeState::operationCount() const +{ + Q_D(const QDeclarativeState); + return d->operations.count(); +} + +QDeclarativeStateOperation *QDeclarativeState::operationAt(int index) const +{ + Q_D(const QDeclarativeState); + return d->operations.at(index); +} + +QDeclarativeState &QDeclarativeState::operator<<(QDeclarativeStateOperation *op) +{ + Q_D(QDeclarativeState); + d->operations.append(QDeclarativeStatePrivate::OperationGuard(op, &d->operations)); + return *this; +} + +void QDeclarativeStatePrivate::complete() +{ + Q_Q(QDeclarativeState); + + for (int ii = 0; ii < reverting.count(); ++ii) { + for (int jj = 0; jj < revertList.count(); ++jj) { + const QDeclarativeRevertAction &revert = reverting.at(ii); + const QDeclarativeSimpleAction &simple = revertList.at(jj); + if ((revert.event && simple.event() == revert.event) || + simple.property() == revert.property) { + revertList.removeAt(jj); + break; + } + } + } + reverting.clear(); + + if (group) + group->stateAboutToComplete(); + emit q->completed(); +} + +// Generate a list of actions for this state. This includes coelescing state +// actions that this state "extends" +QDeclarativeStateOperation::ActionList +QDeclarativeStatePrivate::generateActionList() const +{ + QDeclarativeStateOperation::ActionList applyList; + if (inState) + return applyList; + + // Prevent "extends" recursion + inState = true; + + if (!extends.isEmpty()) { + QList<QDeclarativeState *> states = group ? group->states() : QList<QDeclarativeState *>(); + for (int ii = 0; ii < states.count(); ++ii) + if (states.at(ii)->name() == extends) { + qmlExecuteDeferred(states.at(ii)); + applyList = static_cast<QDeclarativeStatePrivate*>(states.at(ii)->d_func())->generateActionList(); + } + } + + foreach(QDeclarativeStateOperation *op, operations) + applyList << op->actions(); + + inState = false; + return applyList; +} + +QDeclarativeStateGroup *QDeclarativeState::stateGroup() const +{ + Q_D(const QDeclarativeState); + return d->group; +} + +void QDeclarativeState::setStateGroup(QDeclarativeStateGroup *group) +{ + Q_D(QDeclarativeState); + d->group = group; +} + +void QDeclarativeState::cancel() +{ + Q_D(QDeclarativeState); + d->transitionManager.cancel(); +} + +void QDeclarativeAction::deleteFromBinding() +{ + if (fromBinding) { + QDeclarativePropertyPrivate::setBinding(property, 0); + fromBinding->destroy(); + fromBinding = 0; + } +} + +bool QDeclarativeState::containsPropertyInRevertList(QObject *target, const QString &name) const +{ + Q_D(const QDeclarativeState); + + if (isStateActive()) { + QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) + return true; + } + } + + return false; +} + +bool QDeclarativeState::changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) { + simpleAction.setValue(revertValue); + return true; + } + } + } + + return false; +} + +bool QDeclarativeState::changeBindingInRevertList(QObject *target, const QString &name, QDeclarativeAbstractBinding *binding) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) { + if (simpleAction.binding()) + simpleAction.binding()->destroy(); + + simpleAction.setBinding(binding); + return true; + } + } + } + + return false; +} + +bool QDeclarativeState::removeEntryFromRevertList(QObject *target, const QString &name) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.property().object() == target && simpleAction.property().name() == name) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + oldBinding->destroy(); + } + + simpleAction.property().write(simpleAction.value()); + if (simpleAction.binding()) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding()); + + revertListIterator.remove(); + return true; + } + } + } + + return false; +} + +void QDeclarativeState::addEntryToRevertList(const QDeclarativeAction &action) +{ + Q_D(QDeclarativeState); + + QDeclarativeSimpleAction simpleAction(action); + + d->revertList.append(simpleAction); +} + +void QDeclarativeState::removeAllEntriesFromRevertList(QObject *target) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.property().object() == target) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + oldBinding->destroy(); + } + + simpleAction.property().write(simpleAction.value()); + if (simpleAction.binding()) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding()); + + revertListIterator.remove(); + } + } + } +} + +void QDeclarativeState::addEntriesToRevertList(const QList<QDeclarativeAction> &actionList) +{ + Q_D(QDeclarativeState); + if (isStateActive()) { + QList<QDeclarativeSimpleAction> simpleActionList; + + QListIterator<QDeclarativeAction> actionListIterator(actionList); + while(actionListIterator.hasNext()) { + const QDeclarativeAction &action = actionListIterator.next(); + QDeclarativeSimpleAction simpleAction(action); + action.property.write(action.toValue); + if (!action.toBinding.isNull()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QDeclarativePropertyPrivate::DontRemoveBinding); + } + + simpleActionList.append(simpleAction); + } + + d->revertList.append(simpleActionList); + } +} + +QVariant QDeclarativeState::valueInRevertList(QObject *target, const QString &name) const +{ + Q_D(const QDeclarativeState); + + if (isStateActive()) { + QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) + return simpleAction.value(); + } + } + + return QVariant(); +} + +QDeclarativeAbstractBinding *QDeclarativeState::bindingInRevertList(QObject *target, const QString &name) const +{ + Q_D(const QDeclarativeState); + + if (isStateActive()) { + QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) + return simpleAction.binding(); + } + } + + return 0; +} + +bool QDeclarativeState::isStateActive() const +{ + return stateGroup() && stateGroup()->state() == name(); +} + +void QDeclarativeState::apply(QDeclarativeTransition *trans, QDeclarativeState *revert) +{ + Q_D(QDeclarativeState); + + qmlExecuteDeferred(this); + + cancel(); + if (revert) + revert->cancel(); + d->revertList.clear(); + d->reverting.clear(); + + if (revert) { + QDeclarativeStatePrivate *revertPrivate = + static_cast<QDeclarativeStatePrivate*>(revert->d_func()); + d->revertList = revertPrivate->revertList; + revertPrivate->revertList.clear(); + } + + // List of actions caused by this state + QDeclarativeStateOperation::ActionList applyList = d->generateActionList(); + + // List of actions that need to be reverted to roll back (just) this state + QDeclarativeStatePrivate::SimpleActionList additionalReverts; + // First add the reverse of all the applyList actions + for (int ii = 0; ii < applyList.count(); ++ii) { + QDeclarativeAction &action = applyList[ii]; + + if (action.event) { + if (!action.event->isReversable()) + continue; + bool found = false; + for (int jj = 0; jj < d->revertList.count(); ++jj) { + QDeclarativeActionEvent *event = d->revertList.at(jj).event(); + if (event && event->typeName() == action.event->typeName()) { + if (action.event->override(event)) { + found = true; + + if (action.event != d->revertList.at(jj).event() && action.event->needsCopy()) { + action.event->copyOriginals(d->revertList.at(jj).event()); + + QDeclarativeSimpleAction r(action); + additionalReverts << r; + d->revertList.removeAt(jj); + --jj; + } else if (action.event->isRewindable()) //###why needed? + action.event->saveCurrentValues(); + + break; + } + } + } + if (!found) { + action.event->saveOriginals(); + // Only need to revert the applyList action if the previous + // state doesn't have a higher priority revert already + QDeclarativeSimpleAction r(action); + additionalReverts << r; + } + } else { + bool found = false; + action.fromBinding = QDeclarativePropertyPrivate::binding(action.property); + + for (int jj = 0; jj < d->revertList.count(); ++jj) { + if (d->revertList.at(jj).property() == action.property) { + found = true; + if (d->revertList.at(jj).binding() != action.fromBinding) { + action.deleteFromBinding(); + } + break; + } + } + + if (!found) { + if (!action.restore) { + action.deleteFromBinding();; + } else { + // Only need to revert the applyList action if the previous + // state doesn't have a higher priority revert already + QDeclarativeSimpleAction r(action); + additionalReverts << r; + } + } + } + } + + // Any reverts from a previous state that aren't carried forth + // into this state need to be translated into apply actions + for (int ii = 0; ii < d->revertList.count(); ++ii) { + bool found = false; + if (d->revertList.at(ii).event()) { + QDeclarativeActionEvent *event = d->revertList.at(ii).event(); + if (!event->isReversable()) + continue; + for (int jj = 0; !found && jj < applyList.count(); ++jj) { + const QDeclarativeAction &action = applyList.at(jj); + if (action.event && action.event->typeName() == event->typeName()) { + if (action.event->override(event)) + found = true; + } + } + } else { + for (int jj = 0; !found && jj < applyList.count(); ++jj) { + const QDeclarativeAction &action = applyList.at(jj); + if (action.property == d->revertList.at(ii).property()) + found = true; + } + } + if (!found) { + QVariant cur = d->revertList.at(ii).property().read(); + QDeclarativeAbstractBinding *delBinding = + QDeclarativePropertyPrivate::setBinding(d->revertList.at(ii).property(), 0); + if (delBinding) + delBinding->destroy(); + + QDeclarativeAction a; + a.property = d->revertList.at(ii).property(); + a.fromValue = cur; + a.toValue = d->revertList.at(ii).value(); + a.toBinding = QDeclarativeAbstractBinding::getPointer(d->revertList.at(ii).binding()); + a.specifiedObject = d->revertList.at(ii).specifiedObject(); + a.specifiedProperty = d->revertList.at(ii).specifiedProperty(); + a.event = d->revertList.at(ii).event(); + a.reverseEvent = d->revertList.at(ii).reverseEvent(); + if (a.event && a.event->isRewindable()) + a.event->saveCurrentValues(); + applyList << a; + // Store these special reverts in the reverting list + if (a.event) + d->reverting << a.event; + else + d->reverting << a.property; + } + } + // All the local reverts now become part of the ongoing revertList + d->revertList << additionalReverts; + +#ifndef QT_NO_DEBUG_STREAM + // Output for debugging + if (stateChangeDebug()) { + foreach(const QDeclarativeAction &action, applyList) { + if (action.event) + qWarning() << " QDeclarativeAction event:" << action.event->typeName(); + else + qWarning() << " QDeclarativeAction:" << action.property.object() + << action.property.name() << "From:" << action.fromValue + << "To:" << action.toValue; + } + } +#endif + + d->transitionManager.transition(applyList, trans); +} + +QDeclarativeStateOperation::ActionList QDeclarativeStateOperation::actions() +{ + return ActionList(); +} + +QDeclarativeState *QDeclarativeStateOperation::state() const +{ + Q_D(const QDeclarativeStateOperation); + return d->m_state; +} + +void QDeclarativeStateOperation::setState(QDeclarativeState *state) +{ + Q_D(QDeclarativeStateOperation); + d->m_state = state; +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativestate_p.h b/src/quick/util/qdeclarativestate_p.h new file mode 100644 index 0000000000..79038e50ea --- /dev/null +++ b/src/quick/util/qdeclarativestate_p.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATE_H +#define QDECLARATIVESTATE_H + +#include <qdeclarative.h> +#include <qdeclarativeproperty.h> +#include <QtCore/qobject.h> +#include <QtCore/qsharedpointer.h> +#include <private/qtquickglobal_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeActionEvent; +class QDeclarativeAbstractBinding; +class QDeclarativeBinding; +class QDeclarativeExpression; +class Q_QUICK_PRIVATE_EXPORT QDeclarativeAction +{ +public: + QDeclarativeAction(); + QDeclarativeAction(QObject *, const QString &, const QVariant &); + QDeclarativeAction(QObject *, const QString &, + QDeclarativeContext *, const QVariant &); + + bool restore:1; + bool actionDone:1; + bool reverseEvent:1; + bool deletableToBinding:1; + + QDeclarativeProperty property; + QVariant fromValue; + QVariant toValue; + + QDeclarativeAbstractBinding *fromBinding; + QWeakPointer<QDeclarativeAbstractBinding> toBinding; + QDeclarativeActionEvent *event; + + //strictly for matching + QObject *specifiedObject; + QString specifiedProperty; + + void deleteFromBinding(); +}; + +class Q_AUTOTEST_EXPORT QDeclarativeActionEvent +{ +public: + virtual ~QDeclarativeActionEvent(); + virtual QString typeName() const; + + enum Reason { ActualChange, FastForward }; + + virtual void execute(Reason reason = ActualChange); + virtual bool isReversable(); + virtual void reverse(Reason reason = ActualChange); + virtual void saveOriginals() {} + virtual bool needsCopy() { return false; } + virtual void copyOriginals(QDeclarativeActionEvent *) {} + + virtual bool isRewindable() { return isReversable(); } + virtual void rewind() {} + virtual void saveCurrentValues() {} + virtual void saveTargetValues() {} + + virtual bool changesBindings(); + virtual void clearBindings(); + virtual bool override(QDeclarativeActionEvent*other); +}; + +//### rename to QDeclarativeStateChange? +class QDeclarativeStateGroup; +class QDeclarativeState; +class QDeclarativeStateOperationPrivate; +class Q_QUICK_EXPORT QDeclarativeStateOperation : public QObject +{ + Q_OBJECT +public: + QDeclarativeStateOperation(QObject *parent = 0) + : QObject(parent) {} + typedef QList<QDeclarativeAction> ActionList; + + virtual ActionList actions(); + + QDeclarativeState *state() const; + void setState(QDeclarativeState *state); + +protected: + QDeclarativeStateOperation(QObjectPrivate &dd, QObject *parent = 0); + +private: + Q_DECLARE_PRIVATE(QDeclarativeStateOperation) + Q_DISABLE_COPY(QDeclarativeStateOperation) +}; + +typedef QDeclarativeStateOperation::ActionList QDeclarativeStateActions; + +class QDeclarativeTransition; +class QDeclarativeStatePrivate; +class Q_QUICK_EXPORT QDeclarativeState : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(QDeclarativeBinding *when READ when WRITE setWhen) + Q_PROPERTY(QString extend READ extends WRITE setExtends) + Q_PROPERTY(QDeclarativeListProperty<QDeclarativeStateOperation> changes READ changes) + Q_CLASSINFO("DefaultProperty", "changes") + Q_CLASSINFO("DeferredPropertyNames", "changes") + +public: + QDeclarativeState(QObject *parent=0); + virtual ~QDeclarativeState(); + + QString name() const; + void setName(const QString &); + bool isNamed() const; + + /*'when' is a QDeclarativeBinding to limit state changes oscillation + due to the unpredictable order of evaluation of bound expressions*/ + bool isWhenKnown() const; + QDeclarativeBinding *when() const; + void setWhen(QDeclarativeBinding *); + + QString extends() const; + void setExtends(const QString &); + + QDeclarativeListProperty<QDeclarativeStateOperation> changes(); + int operationCount() const; + QDeclarativeStateOperation *operationAt(int) const; + + QDeclarativeState &operator<<(QDeclarativeStateOperation *); + + void apply(QDeclarativeTransition *, QDeclarativeState *revert); + void cancel(); + + QDeclarativeStateGroup *stateGroup() const; + void setStateGroup(QDeclarativeStateGroup *); + + bool containsPropertyInRevertList(QObject *target, const QString &name) const; + bool changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue); + bool changeBindingInRevertList(QObject *target, const QString &name, QDeclarativeAbstractBinding *binding); + bool removeEntryFromRevertList(QObject *target, const QString &name); + void addEntryToRevertList(const QDeclarativeAction &action); + void removeAllEntriesFromRevertList(QObject *target); + void addEntriesToRevertList(const QList<QDeclarativeAction> &actions); + QVariant valueInRevertList(QObject *target, const QString &name) const; + QDeclarativeAbstractBinding *bindingInRevertList(QObject *target, const QString &name) const; + + bool isStateActive() const; + +Q_SIGNALS: + void completed(); + +private: + Q_DECLARE_PRIVATE(QDeclarativeState) + Q_DISABLE_COPY(QDeclarativeState) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeStateOperation) +QML_DECLARE_TYPE(QDeclarativeState) + +QT_END_HEADER + +#endif // QDECLARATIVESTATE_H diff --git a/src/quick/util/qdeclarativestate_p_p.h b/src/quick/util/qdeclarativestate_p_p.h new file mode 100644 index 0000000000..55aeecde11 --- /dev/null +++ b/src/quick/util/qdeclarativestate_p_p.h @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATE_P_H +#define QDECLARATIVESTATE_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 "qdeclarativestate_p.h" + +#include "qdeclarativetransitionmanager_p_p.h" + +#include <private/qdeclarativeproperty_p.h> +#include <private/qdeclarativeguard_p.h> + +#include <private/qdeclarativebinding_p.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativeSimpleAction +{ +public: + enum State { StartState, EndState }; + QDeclarativeSimpleAction(const QDeclarativeAction &a, State state = StartState) + { + m_property = a.property; + m_specifiedObject = a.specifiedObject; + m_specifiedProperty = a.specifiedProperty; + m_event = a.event; + if (state == StartState) { + m_value = a.fromValue; + if (QDeclarativePropertyPrivate::binding(m_property)) { + m_binding = QDeclarativeAbstractBinding::getPointer(QDeclarativePropertyPrivate::binding(m_property)); + } + m_reverseEvent = true; + } else { + m_value = a.toValue; + m_binding = a.toBinding; + m_reverseEvent = false; + } + } + + ~QDeclarativeSimpleAction() + { + } + + QDeclarativeSimpleAction(const QDeclarativeSimpleAction &other) + : m_property(other.m_property), + m_value(other.m_value), + m_binding(QDeclarativeAbstractBinding::getPointer(other.binding())), + m_specifiedObject(other.m_specifiedObject), + m_specifiedProperty(other.m_specifiedProperty), + m_event(other.m_event), + m_reverseEvent(other.m_reverseEvent) + { + } + + QDeclarativeSimpleAction &operator =(const QDeclarativeSimpleAction &other) + { + m_property = other.m_property; + m_value = other.m_value; + m_binding = QDeclarativeAbstractBinding::getPointer(other.binding()); + m_specifiedObject = other.m_specifiedObject; + m_specifiedProperty = other.m_specifiedProperty; + m_event = other.m_event; + m_reverseEvent = other.m_reverseEvent; + + return *this; + } + + void setProperty(const QDeclarativeProperty &property) + { + m_property = property; + } + + const QDeclarativeProperty &property() const + { + return m_property; + } + + void setValue(const QVariant &value) + { + m_value = value; + } + + const QVariant &value() const + { + return m_value; + } + + void setBinding(QDeclarativeAbstractBinding *binding) + { + m_binding = QDeclarativeAbstractBinding::getPointer(binding); + } + + QDeclarativeAbstractBinding *binding() const + { + return m_binding.data(); + } + + QObject *specifiedObject() const + { + return m_specifiedObject; + } + + const QString &specifiedProperty() const + { + return m_specifiedProperty; + } + + QDeclarativeActionEvent *event() const + { + return m_event; + } + + bool reverseEvent() const + { + return m_reverseEvent; + } + +private: + QDeclarativeProperty m_property; + QVariant m_value; + QDeclarativeAbstractBinding::Pointer m_binding; + QObject *m_specifiedObject; + QString m_specifiedProperty; + QDeclarativeActionEvent *m_event; + bool m_reverseEvent; +}; + +class QDeclarativeRevertAction +{ +public: + QDeclarativeRevertAction() : event(0) {} + QDeclarativeRevertAction(const QDeclarativeProperty &prop) : property(prop), event(0) {} + QDeclarativeRevertAction(QDeclarativeActionEvent *e) : event(e) {} + QDeclarativeProperty property; + QDeclarativeActionEvent *event; +}; + +class QDeclarativeStateOperationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeStateOperation) + +public: + + QDeclarativeStateOperationPrivate() + : m_state(0) {} + + QDeclarativeState *m_state; +}; + +class QDeclarativeStatePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeState) + +public: + QDeclarativeStatePrivate() + : when(0), named(false), inState(false), group(0) {} + + typedef QList<QDeclarativeSimpleAction> SimpleActionList; + + QString name; + QDeclarativeBinding *when; + bool named; + + struct OperationGuard : public QDeclarativeGuard<QDeclarativeStateOperation> + { + OperationGuard(QObject *obj, QList<OperationGuard> *l) : list(l) { + setObject(static_cast<QDeclarativeStateOperation *>(obj)); + } + QList<OperationGuard> *list; + void objectDestroyed(QDeclarativeStateOperation *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + QList<OperationGuard> operations; + + static void operations_append(QDeclarativeListProperty<QDeclarativeStateOperation> *prop, QDeclarativeStateOperation *op) { + QList<OperationGuard> *list = static_cast<QList<OperationGuard> *>(prop->data); + op->setState(qobject_cast<QDeclarativeState*>(prop->object)); + list->append(OperationGuard(op, list)); + } + static void operations_clear(QDeclarativeListProperty<QDeclarativeStateOperation> *prop) { + QList<OperationGuard> *list = static_cast<QList<OperationGuard> *>(prop->data); + QMutableListIterator<OperationGuard> listIterator(*list); + while(listIterator.hasNext()) + listIterator.next()->setState(0); + list->clear(); + } + static int operations_count(QDeclarativeListProperty<QDeclarativeStateOperation> *prop) { + QList<OperationGuard> *list = static_cast<QList<OperationGuard> *>(prop->data); + return list->count(); + } + static QDeclarativeStateOperation *operations_at(QDeclarativeListProperty<QDeclarativeStateOperation> *prop, int index) { + QList<OperationGuard> *list = static_cast<QList<OperationGuard> *>(prop->data); + return list->at(index); + } + + QDeclarativeTransitionManager transitionManager; + + SimpleActionList revertList; + QList<QDeclarativeRevertAction> reverting; + QString extends; + mutable bool inState; + QDeclarativeStateGroup *group; + + QDeclarativeStateOperation::ActionList generateActionList() const; + void complete(); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESTATE_P_H diff --git a/src/quick/util/qdeclarativestategroup.cpp b/src/quick/util/qdeclarativestategroup.cpp new file mode 100644 index 0000000000..51e27e4bf0 --- /dev/null +++ b/src/quick/util/qdeclarativestategroup.cpp @@ -0,0 +1,516 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativestategroup_p.h" + +#include "qdeclarativetransition_p.h" + +#include <private/qdeclarativebinding_p.h> +#include <private/qdeclarativeglobal_p.h> + +#include <QtCore/qstringbuilder.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> +#include <qdeclarativeinfo.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +class QDeclarativeStateGroupPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeStateGroup) +public: + QDeclarativeStateGroupPrivate() + : nullState(0), componentComplete(true), + ignoreTrans(false), applyingState(false), unnamedCount(0) {} + + QString currentState; + QDeclarativeState *nullState; + + static void append_state(QDeclarativeListProperty<QDeclarativeState> *list, QDeclarativeState *state); + static int count_state(QDeclarativeListProperty<QDeclarativeState> *list); + static QDeclarativeState *at_state(QDeclarativeListProperty<QDeclarativeState> *list, int index); + static void clear_states(QDeclarativeListProperty<QDeclarativeState> *list); + + static void append_transition(QDeclarativeListProperty<QDeclarativeTransition> *list, QDeclarativeTransition *state); + static int count_transitions(QDeclarativeListProperty<QDeclarativeTransition> *list); + static QDeclarativeTransition *at_transition(QDeclarativeListProperty<QDeclarativeTransition> *list, int index); + static void clear_transitions(QDeclarativeListProperty<QDeclarativeTransition> *list); + + QList<QDeclarativeState *> states; + QList<QDeclarativeTransition *> transitions; + + bool componentComplete; + bool ignoreTrans; + bool applyingState; + int unnamedCount; + + QDeclarativeTransition *findTransition(const QString &from, const QString &to); + void setCurrentStateInternal(const QString &state, bool = false); + bool updateAutoState(); +}; + +/*! + \qmlclass StateGroup QDeclarativeStateGroup + \inqmlmodule QtQuick 2 + \ingroup qml-state-elements + \brief The StateGroup element provides state support for non-Item elements. + + Item (and all derived elements) provides built in support for states and transitions + via its \l{Item::state}{state}, \l{Item::states}{states} and \l{Item::transitions}{transitions} properties. StateGroup provides an easy way to + use this support in other (non-Item-derived) elements. + + \qml + MyCustomObject { + StateGroup { + id: myStateGroup + states: State { + name: "state1" + // ... + } + transitions: Transition { + // ... + } + } + + onSomethingHappened: myStateGroup.state = "state1"; + } + \endqml + + \sa {qmlstate}{States} {QML Animation and Transitions}{Transitions}, {QtDeclarative} +*/ + +QDeclarativeStateGroup::QDeclarativeStateGroup(QObject *parent) + : QObject(*(new QDeclarativeStateGroupPrivate), parent) +{ +} + +QDeclarativeStateGroup::~QDeclarativeStateGroup() +{ + Q_D(const QDeclarativeStateGroup); + for (int i = 0; i < d->states.count(); ++i) + d->states.at(i)->setStateGroup(0); +} + +QList<QDeclarativeState *> QDeclarativeStateGroup::states() const +{ + Q_D(const QDeclarativeStateGroup); + return d->states; +} + +/*! + \qmlproperty list<State> QtQuick2::StateGroup::states + This property holds a list of states defined by the state group. + + \qml + StateGroup { + states: [ + State { + // State definition... + }, + State { + // ... + } + // Other states... + ] + } + \endqml + + \sa {qmlstate}{States} +*/ +QDeclarativeListProperty<QDeclarativeState> QDeclarativeStateGroup::statesProperty() +{ + Q_D(QDeclarativeStateGroup); + return QDeclarativeListProperty<QDeclarativeState>(this, &d->states, &QDeclarativeStateGroupPrivate::append_state, + &QDeclarativeStateGroupPrivate::count_state, + &QDeclarativeStateGroupPrivate::at_state, + &QDeclarativeStateGroupPrivate::clear_states); +} + +void QDeclarativeStateGroupPrivate::append_state(QDeclarativeListProperty<QDeclarativeState> *list, QDeclarativeState *state) +{ + QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object); + if (state) { + _this->d_func()->states.append(state); + state->setStateGroup(_this); + } + +} + +int QDeclarativeStateGroupPrivate::count_state(QDeclarativeListProperty<QDeclarativeState> *list) +{ + QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object); + return _this->d_func()->states.count(); +} + +QDeclarativeState *QDeclarativeStateGroupPrivate::at_state(QDeclarativeListProperty<QDeclarativeState> *list, int index) +{ + QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object); + return _this->d_func()->states.at(index); +} + +void QDeclarativeStateGroupPrivate::clear_states(QDeclarativeListProperty<QDeclarativeState> *list) +{ + QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object); + _this->d_func()->setCurrentStateInternal(QString(), true); + for (int i = 0; i < _this->d_func()->states.count(); ++i) { + _this->d_func()->states.at(i)->setStateGroup(0); + } + _this->d_func()->states.clear(); +} + +/*! + \qmlproperty list<Transition> QtQuick2::StateGroup::transitions + This property holds a list of transitions defined by the state group. + + \qml + StateGroup { + transitions: [ + Transition { + // ... + }, + Transition { + // ... + } + // ... + ] + } + \endqml + + \sa {QML Animation and Transitions}{Transitions} +*/ +QDeclarativeListProperty<QDeclarativeTransition> QDeclarativeStateGroup::transitionsProperty() +{ + Q_D(QDeclarativeStateGroup); + return QDeclarativeListProperty<QDeclarativeTransition>(this, &d->transitions, &QDeclarativeStateGroupPrivate::append_transition, + &QDeclarativeStateGroupPrivate::count_transitions, + &QDeclarativeStateGroupPrivate::at_transition, + &QDeclarativeStateGroupPrivate::clear_transitions); +} + +void QDeclarativeStateGroupPrivate::append_transition(QDeclarativeListProperty<QDeclarativeTransition> *list, QDeclarativeTransition *trans) +{ + QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object); + if (trans) + _this->d_func()->transitions.append(trans); +} + +int QDeclarativeStateGroupPrivate::count_transitions(QDeclarativeListProperty<QDeclarativeTransition> *list) +{ + QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object); + return _this->d_func()->transitions.count(); +} + +QDeclarativeTransition *QDeclarativeStateGroupPrivate::at_transition(QDeclarativeListProperty<QDeclarativeTransition> *list, int index) +{ + QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object); + return _this->d_func()->transitions.at(index); +} + +void QDeclarativeStateGroupPrivate::clear_transitions(QDeclarativeListProperty<QDeclarativeTransition> *list) +{ + QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object); + _this->d_func()->transitions.clear(); +} + +/*! + \qmlproperty string QtQuick2::StateGroup::state + + This property holds the name of the current state of the state group. + + This property is often used in scripts to change between states. For + example: + + \js + function toggle() { + if (button.state == 'On') + button.state = 'Off'; + else + button.state = 'On'; + } + \endjs + + If the state group is in its base state (i.e. no explicit state has been + set), \c state will be a blank string. Likewise, you can return a + state group to its base state by setting its current state to \c ''. + + \sa {qmlstates}{States} +*/ +QString QDeclarativeStateGroup::state() const +{ + Q_D(const QDeclarativeStateGroup); + return d->currentState; +} + +void QDeclarativeStateGroup::setState(const QString &state) +{ + Q_D(QDeclarativeStateGroup); + if (d->currentState == state) + return; + + d->setCurrentStateInternal(state); +} + +void QDeclarativeStateGroup::classBegin() +{ + Q_D(QDeclarativeStateGroup); + d->componentComplete = false; +} + +void QDeclarativeStateGroup::componentComplete() +{ + Q_D(QDeclarativeStateGroup); + d->componentComplete = true; + + for (int ii = 0; ii < d->states.count(); ++ii) { + QDeclarativeState *state = d->states.at(ii); + if (!state->isNamed()) + state->setName(QLatin1String("anonymousState") % QString::number(++d->unnamedCount)); + } + + if (d->updateAutoState()) { + return; + } else if (!d->currentState.isEmpty()) { + QString cs = d->currentState; + d->currentState.clear(); + d->setCurrentStateInternal(cs, true); + } +} + +/*! + Returns true if the state was changed, otherwise false. +*/ +bool QDeclarativeStateGroup::updateAutoState() +{ + Q_D(QDeclarativeStateGroup); + return d->updateAutoState(); +} + +bool QDeclarativeStateGroupPrivate::updateAutoState() +{ + Q_Q(QDeclarativeStateGroup); + if (!componentComplete) + return false; + + bool revert = false; + for (int ii = 0; ii < states.count(); ++ii) { + QDeclarativeState *state = states.at(ii); + if (state->isWhenKnown()) { + if (state->isNamed()) { + if (state->when() && state->when()->evaluate().toBool()) { + if (stateChangeDebug()) + qWarning() << "Setting auto state due to:" + << state->when()->expression(); + if (currentState != state->name()) { + q->setState(state->name()); + return true; + } else { + return false; + } + } else if (state->name() == currentState) { + revert = true; + } + } + } + } + if (revert) { + bool rv = !currentState.isEmpty(); + q->setState(QString()); + return rv; + } else { + return false; + } +} + +QDeclarativeTransition *QDeclarativeStateGroupPrivate::findTransition(const QString &from, const QString &to) +{ + QDeclarativeTransition *highest = 0; + int score = 0; + bool reversed = false; + bool done = false; + + for (int ii = 0; !done && ii < transitions.count(); ++ii) { + QDeclarativeTransition *t = transitions.at(ii); + if (!t->enabled()) + continue; + for (int ii = 0; ii < 2; ++ii) + { + if (ii && (!t->reversible() || + (t->fromState() == QLatin1String("*") && + t->toState() == QLatin1String("*")))) + break; + QStringList fromState; + QStringList toState; + + fromState = t->fromState().split(QLatin1Char(',')); + for (int jj = 0; jj < fromState.count(); ++jj) + fromState[jj] = fromState.at(jj).trimmed(); + toState = t->toState().split(QLatin1Char(',')); + for (int jj = 0; jj < toState.count(); ++jj) + toState[jj] = toState.at(jj).trimmed(); + if (ii == 1) + qSwap(fromState, toState); + int tScore = 0; + if (fromState.contains(from)) + tScore += 2; + else if (fromState.contains(QLatin1String("*"))) + tScore += 1; + else + continue; + + if (toState.contains(to)) + tScore += 2; + else if (toState.contains(QLatin1String("*"))) + tScore += 1; + else + continue; + + if (ii == 1) + reversed = true; + else + reversed = false; + + if (tScore == 4) { + highest = t; + done = true; + break; + } else if (tScore > score) { + score = tScore; + highest = t; + } + } + } + + if (highest) + highest->setReversed(reversed); + + return highest; +} + +void QDeclarativeStateGroupPrivate::setCurrentStateInternal(const QString &state, + bool ignoreTrans) +{ + Q_Q(QDeclarativeStateGroup); + if (!componentComplete) { + currentState = state; + return; + } + + if (applyingState) { + qmlInfo(q) << "Can't apply a state change as part of a state definition."; + return; + } + + applyingState = true; + + QDeclarativeTransition *transition = ignoreTrans ? 0 : findTransition(currentState, state); + if (stateChangeDebug()) { + qWarning() << this << "Changing state. From" << currentState << ". To" << state; + if (transition) + qWarning() << " using transition" << transition->fromState() + << transition->toState(); + } + + QDeclarativeState *oldState = 0; + if (!currentState.isEmpty()) { + for (int ii = 0; ii < states.count(); ++ii) { + if (states.at(ii)->name() == currentState) { + oldState = states.at(ii); + break; + } + } + } + + currentState = state; + emit q->stateChanged(currentState); + + QDeclarativeState *newState = 0; + for (int ii = 0; ii < states.count(); ++ii) { + if (states.at(ii)->name() == currentState) { + newState = states.at(ii); + break; + } + } + + if (oldState == 0 || newState == 0) { + if (!nullState) { + nullState = new QDeclarativeState; + QDeclarative_setParent_noEvent(nullState, q); + nullState->setStateGroup(q); + } + if (!oldState) oldState = nullState; + if (!newState) newState = nullState; + } + + newState->apply(transition, oldState); + applyingState = false; //### consider removing this (don't allow state changes in transition) +} + +QDeclarativeState *QDeclarativeStateGroup::findState(const QString &name) const +{ + Q_D(const QDeclarativeStateGroup); + for (int i = 0; i < d->states.count(); ++i) { + QDeclarativeState *state = d->states.at(i); + if (state->name() == name) + return state; + } + + return 0; +} + +void QDeclarativeStateGroup::removeState(QDeclarativeState *state) +{ + Q_D(QDeclarativeStateGroup); + d->states.removeOne(state); +} + +void QDeclarativeStateGroup::stateAboutToComplete() +{ + Q_D(QDeclarativeStateGroup); + d->applyingState = false; +} + +QT_END_NAMESPACE + + diff --git a/src/quick/util/qdeclarativestategroup_p.h b/src/quick/util/qdeclarativestategroup_p.h new file mode 100644 index 0000000000..0ddc3db312 --- /dev/null +++ b/src/quick/util/qdeclarativestategroup_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATEGROUP_H +#define QDECLARATIVESTATEGROUP_H + +#include "qdeclarativestate_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeStateGroupPrivate; +class Q_QUICK_EXPORT QDeclarativeStateGroup : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + Q_DECLARE_PRIVATE(QDeclarativeStateGroup) + + Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) + Q_PROPERTY(QDeclarativeListProperty<QDeclarativeState> states READ statesProperty DESIGNABLE false) + Q_PROPERTY(QDeclarativeListProperty<QDeclarativeTransition> transitions READ transitionsProperty DESIGNABLE false) + +public: + QDeclarativeStateGroup(QObject * = 0); + virtual ~QDeclarativeStateGroup(); + + QString state() const; + void setState(const QString &); + + QDeclarativeListProperty<QDeclarativeState> statesProperty(); + QList<QDeclarativeState *> states() const; + + QDeclarativeListProperty<QDeclarativeTransition> transitionsProperty(); + + QDeclarativeState *findState(const QString &name) const; + void removeState(QDeclarativeState *state); + + virtual void classBegin(); + virtual void componentComplete(); +Q_SIGNALS: + void stateChanged(const QString &); + +private: + friend class QDeclarativeState; + friend class QDeclarativeStatePrivate; + bool updateAutoState(); + void stateAboutToComplete(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeStateGroup) + +QT_END_HEADER + +#endif // QDECLARATIVESTATEGROUP_H diff --git a/src/quick/util/qdeclarativestateoperations.cpp b/src/quick/util/qdeclarativestateoperations.cpp new file mode 100644 index 0000000000..d8a5369568 --- /dev/null +++ b/src/quick/util/qdeclarativestateoperations.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativestateoperations_p.h" + +#include <qdeclarative.h> +#include <qdeclarativecontext.h> +#include <qdeclarativeexpression.h> +#include <qdeclarativeinfo.h> +#include <private/qdeclarativeguard_p.h> +#include <private/qdeclarativecontext_p.h> +#include <private/qdeclarativeproperty_p.h> +#include <private/qdeclarativebinding_p.h> +#include "qdeclarativestate_p_p.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qmath.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativeStateChangeScriptPrivate : public QDeclarativeStateOperationPrivate +{ +public: + QDeclarativeStateChangeScriptPrivate() {} + + QDeclarativeScriptString script; + QString name; +}; + +/*! + \qmlclass StateChangeScript QDeclarativeStateChangeScript + \inqmlmodule QtQuick 2 + \ingroup qml-state-elements + \brief The StateChangeScript element allows you to run a script in a state. + + A StateChangeScript is run upon entering a state. You can optionally use + ScriptAction to specify the point in the transition at which + the StateChangeScript should to be run. + + \snippet snippets/declarative/states/statechangescript.qml state and transition + + \sa ScriptAction +*/ + +QDeclarativeStateChangeScript::QDeclarativeStateChangeScript(QObject *parent) +: QDeclarativeStateOperation(*(new QDeclarativeStateChangeScriptPrivate), parent) +{ +} + +QDeclarativeStateChangeScript::~QDeclarativeStateChangeScript() +{ +} + +/*! + \qmlproperty script QtQuick2::StateChangeScript::script + This property holds the script to run when the state is current. +*/ +QDeclarativeScriptString QDeclarativeStateChangeScript::script() const +{ + Q_D(const QDeclarativeStateChangeScript); + return d->script; +} + +void QDeclarativeStateChangeScript::setScript(const QDeclarativeScriptString &s) +{ + Q_D(QDeclarativeStateChangeScript); + d->script = s; +} + +/*! + \qmlproperty string QtQuick2::StateChangeScript::name + This property holds the name of the script. This name can be used by a + ScriptAction to target a specific script. + + \sa ScriptAction::scriptName +*/ +QString QDeclarativeStateChangeScript::name() const +{ + Q_D(const QDeclarativeStateChangeScript); + return d->name; +} + +void QDeclarativeStateChangeScript::setName(const QString &n) +{ + Q_D(QDeclarativeStateChangeScript); + d->name = n; +} + +void QDeclarativeStateChangeScript::execute(Reason) +{ + Q_D(QDeclarativeStateChangeScript); + if (!d->script.script().isEmpty()) { + QDeclarativeExpression expr(d->script); + expr.evaluate(); + if (expr.hasError()) + qmlInfo(this, expr.error()); + } +} + +QDeclarativeStateChangeScript::ActionList QDeclarativeStateChangeScript::actions() +{ + ActionList rv; + QDeclarativeAction a; + a.event = this; + rv << a; + return rv; +} + +QString QDeclarativeStateChangeScript::typeName() const +{ + return QLatin1String("StateChangeScript"); +} + + +#include <moc_qdeclarativestateoperations_p.cpp> + +QT_END_NAMESPACE + diff --git a/src/quick/util/qdeclarativestateoperations_p.h b/src/quick/util/qdeclarativestateoperations_p.h new file mode 100644 index 0000000000..872a296025 --- /dev/null +++ b/src/quick/util/qdeclarativestateoperations_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATEOPERATIONS_H +#define QDECLARATIVESTATEOPERATIONS_H + +#include "qdeclarativestate_p.h" +#include <qdeclarativescriptstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeStateChangeScriptPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeStateChangeScript : public QDeclarativeStateOperation, public QDeclarativeActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeStateChangeScript) + + Q_PROPERTY(QDeclarativeScriptString script READ script WRITE setScript) + Q_PROPERTY(QString name READ name WRITE setName) + +public: + QDeclarativeStateChangeScript(QObject *parent=0); + ~QDeclarativeStateChangeScript(); + + virtual ActionList actions(); + + virtual QString typeName() const; + + QDeclarativeScriptString script() const; + void setScript(const QDeclarativeScriptString &); + + QString name() const; + void setName(const QString &); + + virtual void execute(Reason reason = ActualChange); +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeStateChangeScript) + +QT_END_HEADER + +#endif // QDECLARATIVESTATEOPERATIONS_H diff --git a/src/quick/util/qdeclarativestyledtext.cpp b/src/quick/util/qdeclarativestyledtext.cpp new file mode 100644 index 0000000000..1c7ca5fbfd --- /dev/null +++ b/src/quick/util/qdeclarativestyledtext.cpp @@ -0,0 +1,624 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QStack> +#include <QVector> +#include <QPainter> +#include <QTextLayout> +#include <QDebug> +#include <qmath.h> +#include "qdeclarativestyledtext_p.h" + +/* + QDeclarativeStyledText supports few tags: + + <b></b> - bold + <i></i> - italic + <br> - new line + <p> - paragraph + <u> - underlined text + <font color="color_name" size="1-7"></font> + <h1> to <h6> - headers + <a href=""> - anchor + <ol type="">, <ul type=""> and <li> - ordered and unordered lists + + The opening and closing tags must be correctly nested. +*/ + +QT_BEGIN_NAMESPACE + +class QDeclarativeStyledTextPrivate +{ +public: + enum ListType { Ordered, Unordered }; + enum ListFormat { Bullet, Disc, Square, Decimal, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman }; + + struct List { + int level; + ListType type; + ListFormat format; + }; + + QDeclarativeStyledTextPrivate(const QString &t, QTextLayout &l) + : text(t), layout(l), baseFont(layout.font()), hasNewLine(false) + { + } + + void parse(); + bool parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format); + bool parseCloseTag(const QChar *&ch, const QString &textIn, QString &textOut); + void parseEntity(const QChar *&ch, const QString &textIn, QString &textOut); + bool parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format); + bool parseOrderedListAttributes(const QChar *&ch, const QString &textIn); + bool parseUnorderedListAttributes(const QChar *&ch, const QString &textIn); + bool parseAnchorAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format); + QPair<QStringRef,QStringRef> parseAttribute(const QChar *&ch, const QString &textIn); + QStringRef parseValue(const QChar *&ch, const QString &textIn); + + inline void skipSpace(const QChar *&ch) { + while (ch->isSpace() && !ch->isNull()) + ++ch; + } + + static QString toAlpha(int value, bool upper); + static QString toRoman(int value, bool upper); + + QString text; + QTextLayout &layout; + QFont baseFont; + QStack<List> listStack; + bool hasNewLine; + + static const QChar lessThan; + static const QChar greaterThan; + static const QChar equals; + static const QChar singleQuote; + static const QChar doubleQuote; + static const QChar slash; + static const QChar ampersand; + static const QChar bullet; + static const QChar disc; + static const QChar square; + static const int tabsize = 6; +}; + +const QChar QDeclarativeStyledTextPrivate::lessThan(QLatin1Char('<')); +const QChar QDeclarativeStyledTextPrivate::greaterThan(QLatin1Char('>')); +const QChar QDeclarativeStyledTextPrivate::equals(QLatin1Char('=')); +const QChar QDeclarativeStyledTextPrivate::singleQuote(QLatin1Char('\'')); +const QChar QDeclarativeStyledTextPrivate::doubleQuote(QLatin1Char('\"')); +const QChar QDeclarativeStyledTextPrivate::slash(QLatin1Char('/')); +const QChar QDeclarativeStyledTextPrivate::ampersand(QLatin1Char('&')); +const QChar QDeclarativeStyledTextPrivate::bullet(0x2022); +const QChar QDeclarativeStyledTextPrivate::disc(0x25e6); +const QChar QDeclarativeStyledTextPrivate::square(0x25a1); + +QDeclarativeStyledText::QDeclarativeStyledText(const QString &string, QTextLayout &layout) +: d(new QDeclarativeStyledTextPrivate(string, layout)) +{ +} + +QDeclarativeStyledText::~QDeclarativeStyledText() +{ + delete d; +} + +void QDeclarativeStyledText::parse(const QString &string, QTextLayout &layout) +{ + if (string.isEmpty()) + return; + QDeclarativeStyledText styledText(string, layout); + styledText.d->parse(); +} + +void QDeclarativeStyledTextPrivate::parse() +{ + QList<QTextLayout::FormatRange> ranges; + QStack<QTextCharFormat> formatStack; + + QString drawText; + drawText.reserve(text.count()); + + int textStart = 0; + int textLength = 0; + int rangeStart = 0; + const QChar *ch = text.constData(); + while (!ch->isNull()) { + if (*ch == lessThan) { + if (textLength) { + QStringRef ref = QStringRef(&text, textStart, textLength); + const QChar *c = ref.constData(); + bool isWhiteSpace = true; + for (int i = 0; isWhiteSpace && (i < textLength); ++c, ++i) { + if (!c->isSpace()) + isWhiteSpace = false; + } + if (!isWhiteSpace) { + drawText.append(ref); + hasNewLine = false; + } + } + if (rangeStart != drawText.length() && formatStack.count()) { + QTextLayout::FormatRange formatRange; + formatRange.format = formatStack.top(); + formatRange.start = rangeStart; + formatRange.length = drawText.length() - rangeStart; + ranges.append(formatRange); + } + rangeStart = drawText.length(); + ++ch; + if (*ch == slash) { + ++ch; + if (parseCloseTag(ch, text, drawText)) { + if (formatStack.count()) + formatStack.pop(); + } + } else { + QTextCharFormat format; + if (formatStack.count()) + format = formatStack.top(); + if (parseTag(ch, text, drawText, format)) + formatStack.push(format); + } + textStart = ch - text.constData() + 1; + textLength = 0; + } else if (*ch == ampersand) { + ++ch; + drawText.append(QStringRef(&text, textStart, textLength)); + parseEntity(ch, text, drawText); + textStart = ch - text.constData() + 1; + textLength = 0; + } else { + ++textLength; + } + if (!ch->isNull()) + ++ch; + } + if (textLength) + drawText.append(QStringRef(&text, textStart, textLength)); + if (rangeStart != drawText.length() && formatStack.count()) { + QTextLayout::FormatRange formatRange; + formatRange.format = formatStack.top(); + formatRange.start = rangeStart; + formatRange.length = drawText.length() - rangeStart; + ranges.append(formatRange); + } + + layout.setText(drawText); + layout.setAdditionalFormats(ranges); +} + +bool QDeclarativeStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format) +{ + skipSpace(ch); + + int tagStart = ch - textIn.constData(); + int tagLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + if (tagLength == 0) + return false; + QStringRef tag(&textIn, tagStart, tagLength); + const QChar char0 = tag.at(0); + if (char0 == QLatin1Char('b')) { + if (tagLength == 1) + format.setFontWeight(QFont::Bold); + else if (tagLength == 2 && tag.at(1) == QLatin1Char('r')) { + textOut.append(QChar(QChar::LineSeparator)); + return false; + } + } else if (char0 == QLatin1Char('i')) { + if (tagLength == 1) + format.setFontItalic(true); + } else if (char0 == QLatin1Char('p')) { + if (tagLength == 1) { + if (!hasNewLine) + textOut.append(QChar::LineSeparator); + } + } else if (char0 == QLatin1Char('u')) { + if (tagLength == 1) + format.setFontUnderline(true); + else if (tag == QLatin1String("ul")) { + List listItem; + listItem.level = 0; + listItem.type = Unordered; + listItem.format = Bullet; + listStack.push(listItem); + } + } else if (char0 == QLatin1Char('h') && tagLength == 2) { + int level = tag.at(1).digitValue(); + if (level >= 1 && level <= 6) { + static const qreal scaling[] = { 2.0, 1.5, 1.2, 1.0, 0.8, 0.7 }; + if (!hasNewLine) + textOut.append(QChar::LineSeparator); + format.setFontPointSize(baseFont.pointSize() * scaling[level - 1]); + format.setFontWeight(QFont::Bold); + } + } else if (tag == QLatin1String("ol")) { + List listItem; + listItem.level = 0; + listItem.type = Ordered; + listItem.format = Decimal; + listStack.push(listItem); + } else if (tag == QLatin1String("li")) { + if (!hasNewLine) + textOut.append(QChar(QChar::LineSeparator)); + if (!listStack.isEmpty()) { + int count = ++listStack.top().level; + for (int i = 0; i < listStack.size(); ++i) + textOut += QString(tabsize, QChar::Nbsp); + switch (listStack.top().format) { + case Decimal: + textOut += QString::number(count) % QLatin1Char('.'); + break; + case LowerAlpha: + textOut += toAlpha(count, false) % QLatin1Char('.'); + break; + case UpperAlpha: + textOut += toAlpha(count, true) % QLatin1Char('.'); + break; + case LowerRoman: + textOut += toRoman(count, false) % QLatin1Char('.'); + break; + case UpperRoman: + textOut += toRoman(count, true) % QLatin1Char('.'); + break; + case Bullet: + textOut += bullet; + break; + case Disc: + textOut += disc; + break; + case Square: + textOut += square; + break; + } + textOut += QString(2, QChar::Nbsp); + } + } + return true; + } else if (ch->isSpace()) { + // may have params. + QStringRef tag(&textIn, tagStart, tagLength); + if (tag == QLatin1String("font")) + return parseFontAttributes(ch, textIn, format); + if (tag == QLatin1String("ol")) + return parseOrderedListAttributes(ch, textIn); + if (tag == QLatin1String("ul")) + return parseUnorderedListAttributes(ch, textIn); + if (tag == QLatin1String("a")) { + return parseAnchorAttributes(ch, textIn, format); + } + if (*ch == greaterThan || ch->isNull()) + continue; + } else if (*ch != slash) { + tagLength++; + } + ++ch; + } + return false; +} + +bool QDeclarativeStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &textIn, QString &textOut) +{ + skipSpace(ch); + + int tagStart = ch - textIn.constData(); + int tagLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + if (tagLength == 0) + return false; + QStringRef tag(&textIn, tagStart, tagLength); + const QChar char0 = tag.at(0); + hasNewLine = false; + if (char0 == QLatin1Char('b')) { + if (tagLength == 1) + return true; + else if (tag.at(1) == QLatin1Char('r') && tagLength == 2) + return true; + } else if (char0 == QLatin1Char('i')) { + if (tagLength == 1) + return true; + } else if (char0 == QLatin1Char('a')) { + if (tagLength == 1) + return true; + } else if (char0 == QLatin1Char('p')) { + if (tagLength == 1) { + textOut.append(QChar::LineSeparator); + hasNewLine = true; + return true; + } + } else if (char0 == QLatin1Char('u')) { + if (tagLength == 1) + return true; + else if (tag == QLatin1String("ul")) { + if (!listStack.isEmpty()) { + listStack.pop(); + if (!listStack.count()) + textOut.append(QChar::LineSeparator); + } + return true; + } + } else if (char0 == QLatin1Char('h') && tagLength == 2) { + textOut.append(QChar::LineSeparator); + hasNewLine = true; + return true; + } else if (tag == QLatin1String("font")) { + return true; + } else if (tag == QLatin1String("ol")) { + if (!listStack.isEmpty()) { + listStack.pop(); + if (!listStack.count()) + textOut.append(QChar::LineSeparator); + } + return true; + } else if (tag == QLatin1String("li")) { + return true; + } + return false; + } else if (!ch->isSpace()){ + tagLength++; + } + ++ch; + } + + return false; +} + +void QDeclarativeStyledTextPrivate::parseEntity(const QChar *&ch, const QString &textIn, QString &textOut) +{ + int entityStart = ch - textIn.constData(); + int entityLength = 0; + while (!ch->isNull()) { + if (*ch == QLatin1Char(';')) { + QStringRef entity(&textIn, entityStart, entityLength); + if (entity == QLatin1String("gt")) + textOut += QChar(62); + else if (entity == QLatin1String("lt")) + textOut += QChar(60); + else if (entity == QLatin1String("amp")) + textOut += QChar(38); + return; + } + ++entityLength; + ++ch; + } +} + +bool QDeclarativeStyledTextPrivate::parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format) +{ + bool valid = false; + QPair<QStringRef,QStringRef> attr; + do { + attr = parseAttribute(ch, textIn); + if (attr.first == QLatin1String("color")) { + valid = true; + format.setForeground(QColor(attr.second.toString())); + } else if (attr.first == QLatin1String("size")) { + valid = true; + int size = attr.second.toString().toInt(); + if (attr.second.at(0) == QLatin1Char('-') || attr.second.at(0) == QLatin1Char('+')) + size += 3; + if (size >= 1 && size <= 7) { + static const qreal scaling[] = { 0.7, 0.8, 1.0, 1.2, 1.5, 2.0, 2.4 }; + format.setFontPointSize(baseFont.pointSize() * scaling[size-1]); + } + } + } while (!ch->isNull() && !attr.first.isEmpty()); + + return valid; +} + +bool QDeclarativeStyledTextPrivate::parseOrderedListAttributes(const QChar *&ch, const QString &textIn) +{ + bool valid = false; + + List listItem; + listItem.level = 0; + listItem.type = Ordered; + listItem.format = Decimal; + + QPair<QStringRef,QStringRef> attr; + do { + attr = parseAttribute(ch, textIn); + if (attr.first == QLatin1String("type")) { + valid = true; + if (attr.second == QLatin1String("a")) + listItem.format = LowerAlpha; + else if (attr.second == QLatin1String("A")) + listItem.format = UpperAlpha; + else if (attr.second == QLatin1String("i")) + listItem.format = LowerRoman; + else if (attr.second == QLatin1String("I")) + listItem.format = UpperRoman; + } + } while (!ch->isNull() && !attr.first.isEmpty()); + + listStack.push(listItem); + return valid; +} + +bool QDeclarativeStyledTextPrivate::parseUnorderedListAttributes(const QChar *&ch, const QString &textIn) +{ + bool valid = false; + + List listItem; + listItem.level = 0; + listItem.type = Unordered; + listItem.format = Bullet; + + QPair<QStringRef,QStringRef> attr; + do { + attr = parseAttribute(ch, textIn); + if (attr.first == QLatin1String("type")) { + valid = true; + if (attr.second == QLatin1String("disc")) + listItem.format = Disc; + else if (attr.second == QLatin1String("square")) + listItem.format = Square; + } + } while (!ch->isNull() && !attr.first.isEmpty()); + + listStack.push(listItem); + return valid; +} + +bool QDeclarativeStyledTextPrivate::parseAnchorAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format) +{ + bool valid = false; + + QPair<QStringRef,QStringRef> attr; + do { + attr = parseAttribute(ch, textIn); + if (attr.first == QLatin1String("href")) { + format.setAnchorHref(attr.second.toString()); + format.setAnchor(true); + format.setFontUnderline(true); + format.setForeground(QColor("blue")); + valid = true; + } + } while (!ch->isNull() && !attr.first.isEmpty()); + + return valid; +} + +QPair<QStringRef,QStringRef> QDeclarativeStyledTextPrivate::parseAttribute(const QChar *&ch, const QString &textIn) +{ + skipSpace(ch); + + int attrStart = ch - textIn.constData(); + int attrLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + break; + } else if (*ch == equals) { + ++ch; + if (*ch != singleQuote && *ch != doubleQuote) { + while (*ch != greaterThan && !ch->isNull()) + ++ch; + break; + } + ++ch; + if (!attrLength) + break; + QStringRef attr(&textIn, attrStart, attrLength); + QStringRef val = parseValue(ch, textIn); + if (!val.isEmpty()) + return QPair<QStringRef,QStringRef>(attr,val); + break; + } else { + ++attrLength; + } + ++ch; + } + + return QPair<QStringRef,QStringRef>(); +} + +QStringRef QDeclarativeStyledTextPrivate::parseValue(const QChar *&ch, const QString &textIn) +{ + int valStart = ch - textIn.constData(); + int valLength = 0; + while (*ch != singleQuote && *ch != doubleQuote && !ch->isNull()) { + ++valLength; + ++ch; + } + if (ch->isNull()) + return QStringRef(); + ++ch; // skip quote + + return QStringRef(&textIn, valStart, valLength); +} + +QString QDeclarativeStyledTextPrivate::toAlpha(int value, bool upper) +{ + const char baseChar = upper ? 'A' : 'a'; + + QString result; + int c = value; + while (c > 0) { + c--; + result.prepend(QChar(baseChar + (c % 26))); + c /= 26; + } + return result; +} + +QString QDeclarativeStyledTextPrivate::toRoman(int value, bool upper) +{ + QString result = QLatin1String("?"); + // works for up to 4999 items + if (value < 5000) { + QByteArray romanNumeral; + + static const char romanSymbolsLower[] = "iiivixxxlxcccdcmmmm"; + static const char romanSymbolsUpper[] = "IIIVIXXXLXCCCDCMMMM"; + QByteArray romanSymbols; + if (!upper) + romanSymbols = QByteArray::fromRawData(romanSymbolsLower, sizeof(romanSymbolsLower)); + else + romanSymbols = QByteArray::fromRawData(romanSymbolsUpper, sizeof(romanSymbolsUpper)); + + int c[] = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 }; + int n = value; + for (int i = 12; i >= 0; n %= c[i], i--) { + int q = n / c[i]; + if (q > 0) { + int startDigit = i + (i + 3) / 4; + int numDigits; + if (i % 4) { + if ((i - 2) % 4) + numDigits = 2; + else + numDigits = 1; + } + else + numDigits = q; + romanNumeral.append(romanSymbols.mid(startDigit, numDigits)); + } + } + result = QString::fromLatin1(romanNumeral); + } + return result; +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativestyledtext_p.h b/src/quick/util/qdeclarativestyledtext_p.h new file mode 100644 index 0000000000..beffb4d22a --- /dev/null +++ b/src/quick/util/qdeclarativestyledtext_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTYLEDTEXT_H +#define QDECLARATIVESTYLEDTEXT_H + +#include <QSizeF> + +QT_BEGIN_NAMESPACE + +class QPainter; +class QPointF; +class QString; +class QDeclarativeStyledTextPrivate; +class QTextLayout; + +class Q_AUTOTEST_EXPORT QDeclarativeStyledText +{ +public: + static void parse(const QString &string, QTextLayout &layout); + +private: + QDeclarativeStyledText(const QString &string, QTextLayout &layout); + ~QDeclarativeStyledText(); + + QDeclarativeStyledTextPrivate *d; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/util/qdeclarativesvgparser.cpp b/src/quick/util/qdeclarativesvgparser.cpp new file mode 100644 index 0000000000..e8ceb2116c --- /dev/null +++ b/src/quick/util/qdeclarativesvgparser.cpp @@ -0,0 +1,614 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclaractive module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativesvgparser_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qstring.h> + +QT_BEGIN_NAMESPACE + +static const double Q_PI = 3.14159265358979323846; // pi + +//copied from QtSvg (qsvghandler.cpp). +Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); +// '0' is 0x30 and '9' is 0x39 +static inline bool isDigit(ushort ch) +{ + static quint16 magic = 0x3ff; + return ((ch >> 4) == 3) && (magic >> (ch & 15)); +} + +static qreal toDouble(const QChar *&str) +{ + const int maxLen = 255;//technically doubles can go til 308+ but whatever + char temp[maxLen+1]; + int pos = 0; + + if (*str == QLatin1Char('-')) { + temp[pos++] = '-'; + ++str; + } else if (*str == QLatin1Char('+')) { + ++str; + } + while (isDigit(str->unicode()) && pos < maxLen) { + temp[pos++] = str->toLatin1(); + ++str; + } + if (*str == QLatin1Char('.') && pos < maxLen) { + temp[pos++] = '.'; + ++str; + } + while (isDigit(str->unicode()) && pos < maxLen) { + temp[pos++] = str->toLatin1(); + ++str; + } + bool exponent = false; + if ((*str == QLatin1Char('e') || *str == QLatin1Char('E')) && pos < maxLen) { + exponent = true; + temp[pos++] = 'e'; + ++str; + if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) { + temp[pos++] = str->toLatin1(); + ++str; + } + while (isDigit(str->unicode()) && pos < maxLen) { + temp[pos++] = str->toLatin1(); + ++str; + } + } + + temp[pos] = '\0'; + + qreal val; + if (!exponent && pos < 10) { + int ival = 0; + const char *t = temp; + bool neg = false; + if(*t == '-') { + neg = true; + ++t; + } + while(*t && *t != '.') { + ival *= 10; + ival += (*t) - '0'; + ++t; + } + if(*t == '.') { + ++t; + int div = 1; + while(*t) { + ival *= 10; + ival += (*t) - '0'; + div *= 10; + ++t; + } + val = ((qreal)ival)/((qreal)div); + } else { + val = ival; + } + if (neg) + val = -val; + } else { +#if defined(Q_WS_QWS) && !defined(Q_OS_VXWORKS) + if(sizeof(qreal) == sizeof(float)) + val = strtof(temp, 0); + else +#endif + { + bool ok = false; + val = qstrtod(temp, 0, &ok); + } + } + return val; + +} +static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points) +{ + while (str->isSpace()) + ++str; + while (isDigit(str->unicode()) || + *str == QLatin1Char('-') || *str == QLatin1Char('+') || + *str == QLatin1Char('.')) { + + points.append(toDouble(str)); + + while (str->isSpace()) + ++str; + if (*str == QLatin1Char(',')) + ++str; + + //eat the rest of space + while (str->isSpace()) + ++str; + } +} + +static void pathArcSegment(QPainterPath &path, + qreal xc, qreal yc, + qreal th0, qreal th1, + qreal rx, qreal ry, qreal xAxisRotation) +{ + qreal sinTh, cosTh; + qreal a00, a01, a10, a11; + qreal x1, y1, x2, y2, x3, y3; + qreal t; + qreal thHalf; + + sinTh = qSin(xAxisRotation * (Q_PI / 180.0)); + cosTh = qCos(xAxisRotation * (Q_PI / 180.0)); + + a00 = cosTh * rx; + a01 = -sinTh * ry; + a10 = sinTh * rx; + a11 = cosTh * ry; + + thHalf = 0.5 * (th1 - th0); + t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf); + x1 = xc + qCos(th0) - t * qSin(th0); + y1 = yc + qSin(th0) + t * qCos(th0); + x3 = xc + qCos(th1); + y3 = yc + qSin(th1); + x2 = x3 + t * qSin(th1); + y2 = y3 - t * qCos(th1); + + path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1, + a00 * x2 + a01 * y2, a10 * x2 + a11 * y2, + a00 * x3 + a01 * y3, a10 * x3 + a11 * y3); +} + +void QDeclarativeSvgParser::pathArc(QPainterPath &path, + qreal rx, + qreal ry, + qreal x_axis_rotation, + int large_arc_flag, + int sweep_flag, + qreal x, + qreal y, + qreal curx, qreal cury) +{ + qreal sin_th, cos_th; + qreal a00, a01, a10, a11; + qreal x0, y0, x1, y1, xc, yc; + qreal d, sfactor, sfactor_sq; + qreal th0, th1, th_arc; + int i, n_segs; + qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check; + + rx = qAbs(rx); + ry = qAbs(ry); + + sin_th = qSin(x_axis_rotation * (Q_PI / 180.0)); + cos_th = qCos(x_axis_rotation * (Q_PI / 180.0)); + + dx = (curx - x) / 2.0; + dy = (cury - y) / 2.0; + dx1 = cos_th * dx + sin_th * dy; + dy1 = -sin_th * dx + cos_th * dy; + Pr1 = rx * rx; + Pr2 = ry * ry; + Px = dx1 * dx1; + Py = dy1 * dy1; + /* Spec : check if radii are large enough */ + check = Px / Pr1 + Py / Pr2; + if (check > 1) { + rx = rx * qSqrt(check); + ry = ry * qSqrt(check); + } + + a00 = cos_th / rx; + a01 = sin_th / rx; + a10 = -sin_th / ry; + a11 = cos_th / ry; + x0 = a00 * curx + a01 * cury; + y0 = a10 * curx + a11 * cury; + x1 = a00 * x + a01 * y; + y1 = a10 * x + a11 * y; + /* (x0, y0) is current point in transformed coordinate space. + (x1, y1) is new point in transformed coordinate space. + + The arc fits a unit-radius circle in this space. + */ + d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0); + sfactor_sq = 1.0 / d - 0.25; + if (sfactor_sq < 0) sfactor_sq = 0; + sfactor = qSqrt(sfactor_sq); + if (sweep_flag == large_arc_flag) sfactor = -sfactor; + xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0); + yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0); + /* (xc, yc) is center of the circle. */ + + th0 = qAtan2(y0 - yc, x0 - xc); + th1 = qAtan2(y1 - yc, x1 - xc); + + th_arc = th1 - th0; + if (th_arc < 0 && sweep_flag) + th_arc += 2 * Q_PI; + else if (th_arc > 0 && !sweep_flag) + th_arc -= 2 * Q_PI; + + n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001))); + + for (i = 0; i < n_segs; i++) { + pathArcSegment(path, xc, yc, + th0 + i * th_arc / n_segs, + th0 + (i + 1) * th_arc / n_segs, + rx, ry, x_axis_rotation); + } +} + + +bool QDeclarativeSvgParser::parsePathDataFast(const QString &dataStr, QPainterPath &path) +{ + qreal x0 = 0, y0 = 0; // starting point + qreal x = 0, y = 0; // current point + char lastMode = 0; + QPointF ctrlPt; + const QChar *str = dataStr.constData(); + const QChar *end = str + dataStr.size(); + + while (str != end) { + while (str->isSpace()) + ++str; + QChar pathElem = *str; + ++str; + QChar endc = *end; + *const_cast<QChar *>(end) = 0; // parseNumbersArray requires 0-termination that QStringRef cannot guarantee + QVarLengthArray<qreal, 8> arg; + parseNumbersArray(str, arg); + *const_cast<QChar *>(end) = endc; + if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z')) + arg.append(0);//dummy + const qreal *num = arg.constData(); + int count = arg.count(); + while (count > 0) { + qreal offsetX = x; // correction offsets + qreal offsetY = y; // for relative commands + switch (pathElem.unicode()) { + case 'm': { + if (count < 2) { + num++; + count--; + break; + } + x = x0 = num[0] + offsetX; + y = y0 = num[1] + offsetY; + num += 2; + count -= 2; + path.moveTo(x0, y0); + + // As per 1.2 spec 8.3.2 The "moveto" commands + // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands, + // the subsequent pairs shall be treated as implicit 'lineto' commands. + pathElem = QLatin1Char('l'); + } + break; + case 'M': { + if (count < 2) { + num++; + count--; + break; + } + x = x0 = num[0]; + y = y0 = num[1]; + num += 2; + count -= 2; + path.moveTo(x0, y0); + + // As per 1.2 spec 8.3.2 The "moveto" commands + // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands, + // the subsequent pairs shall be treated as implicit 'lineto' commands. + pathElem = QLatin1Char('L'); + } + break; + case 'z': + case 'Z': { + x = x0; + y = y0; + count--; // skip dummy + num++; + path.closeSubpath(); + } + break; + case 'l': { + if (count < 2) { + num++; + count--; + break; + } + x = num[0] + offsetX; + y = num[1] + offsetY; + num += 2; + count -= 2; + path.lineTo(x, y); + + } + break; + case 'L': { + if (count < 2) { + num++; + count--; + break; + } + x = num[0]; + y = num[1]; + num += 2; + count -= 2; + path.lineTo(x, y); + } + break; + case 'h': { + x = num[0] + offsetX; + num++; + count--; + path.lineTo(x, y); + } + break; + case 'H': { + x = num[0]; + num++; + count--; + path.lineTo(x, y); + } + break; + case 'v': { + y = num[0] + offsetY; + num++; + count--; + path.lineTo(x, y); + } + break; + case 'V': { + y = num[0]; + num++; + count--; + path.lineTo(x, y); + } + break; + case 'c': { + if (count < 6) { + num += count; + count = 0; + break; + } + QPointF c1(num[0] + offsetX, num[1] + offsetY); + QPointF c2(num[2] + offsetX, num[3] + offsetY); + QPointF e(num[4] + offsetX, num[5] + offsetY); + num += 6; + count -= 6; + path.cubicTo(c1, c2, e); + ctrlPt = c2; + x = e.x(); + y = e.y(); + break; + } + case 'C': { + if (count < 6) { + num += count; + count = 0; + break; + } + QPointF c1(num[0], num[1]); + QPointF c2(num[2], num[3]); + QPointF e(num[4], num[5]); + num += 6; + count -= 6; + path.cubicTo(c1, c2, e); + ctrlPt = c2; + x = e.x(); + y = e.y(); + break; + } + case 's': { + if (count < 4) { + num += count; + count = 0; + break; + } + QPointF c1; + if (lastMode == 'c' || lastMode == 'C' || + lastMode == 's' || lastMode == 'S') + c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y()); + else + c1 = QPointF(x, y); + QPointF c2(num[0] + offsetX, num[1] + offsetY); + QPointF e(num[2] + offsetX, num[3] + offsetY); + num += 4; + count -= 4; + path.cubicTo(c1, c2, e); + ctrlPt = c2; + x = e.x(); + y = e.y(); + break; + } + case 'S': { + if (count < 4) { + num += count; + count = 0; + break; + } + QPointF c1; + if (lastMode == 'c' || lastMode == 'C' || + lastMode == 's' || lastMode == 'S') + c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y()); + else + c1 = QPointF(x, y); + QPointF c2(num[0], num[1]); + QPointF e(num[2], num[3]); + num += 4; + count -= 4; + path.cubicTo(c1, c2, e); + ctrlPt = c2; + x = e.x(); + y = e.y(); + break; + } + case 'q': { + if (count < 4) { + num += count; + count = 0; + break; + } + QPointF c(num[0] + offsetX, num[1] + offsetY); + QPointF e(num[2] + offsetX, num[3] + offsetY); + num += 4; + count -= 4; + path.quadTo(c, e); + ctrlPt = c; + x = e.x(); + y = e.y(); + break; + } + case 'Q': { + if (count < 4) { + num += count; + count = 0; + break; + } + QPointF c(num[0], num[1]); + QPointF e(num[2], num[3]); + num += 4; + count -= 4; + path.quadTo(c, e); + ctrlPt = c; + x = e.x(); + y = e.y(); + break; + } + case 't': { + if (count < 2) { + num += count; + count = 0; + break; + } + QPointF e(num[0] + offsetX, num[1] + offsetY); + num += 2; + count -= 2; + QPointF c; + if (lastMode == 'q' || lastMode == 'Q' || + lastMode == 't' || lastMode == 'T') + c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y()); + else + c = QPointF(x, y); + path.quadTo(c, e); + ctrlPt = c; + x = e.x(); + y = e.y(); + break; + } + case 'T': { + if (count < 2) { + num += count; + count = 0; + break; + } + QPointF e(num[0], num[1]); + num += 2; + count -= 2; + QPointF c; + if (lastMode == 'q' || lastMode == 'Q' || + lastMode == 't' || lastMode == 'T') + c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y()); + else + c = QPointF(x, y); + path.quadTo(c, e); + ctrlPt = c; + x = e.x(); + y = e.y(); + break; + } + case 'a': { + if (count < 7) { + num += count; + count = 0; + break; + } + qreal rx = (*num++); + qreal ry = (*num++); + qreal xAxisRotation = (*num++); + qreal largeArcFlag = (*num++); + qreal sweepFlag = (*num++); + qreal ex = (*num++) + offsetX; + qreal ey = (*num++) + offsetY; + count -= 7; + qreal curx = x; + qreal cury = y; + pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag), + int(sweepFlag), ex, ey, curx, cury); + + x = ex; + y = ey; + } + break; + case 'A': { + if (count < 7) { + num += count; + count = 0; + break; + } + qreal rx = (*num++); + qreal ry = (*num++); + qreal xAxisRotation = (*num++); + qreal largeArcFlag = (*num++); + qreal sweepFlag = (*num++); + qreal ex = (*num++); + qreal ey = (*num++); + count -= 7; + qreal curx = x; + qreal cury = y; + pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag), + int(sweepFlag), ex, ey, curx, cury); + + x = ex; + y = ey; + } + break; + default: + return false; + } + lastMode = pathElem.toLatin1(); + } + } + return true; +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativesvgparser_p.h b/src/quick/util/qdeclarativesvgparser_p.h new file mode 100644 index 0000000000..0d7be10761 --- /dev/null +++ b/src/quick/util/qdeclarativesvgparser_p.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclaractive module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESVGPARSER_P_H +#define QDECLARATIVESVGPARSER_P_H + +#include <QtCore/qstring.h> +#include <QtGui/qpainterpath.h> + +QT_BEGIN_NAMESPACE + +namespace QDeclarativeSvgParser +{ + bool parsePathDataFast(const QString &dataStr, QPainterPath &path); + void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation, + int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx, + qreal cury); +} + +QT_END_NAMESPACE + +#endif // QDECLARATIVESVGPARSER_P_H diff --git a/src/quick/util/qdeclarativesystempalette.cpp b/src/quick/util/qdeclarativesystempalette.cpp new file mode 100644 index 0000000000..61d8406141 --- /dev/null +++ b/src/quick/util/qdeclarativesystempalette.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativesystempalette_p.h" + +#include <QGuiApplication> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativeSystemPalettePrivate : public QObjectPrivate +{ +public: + QPalette palette; + QPalette::ColorGroup group; +}; + + + +/*! + \qmlclass SystemPalette QDeclarativeSystemPalette + \inqmlmodule QtQuick 2 + \ingroup qml-utility-elements + \brief The SystemPalette element provides access to the Qt palettes. + + The SystemPalette element provides access to the Qt application + palettes. This provides information about the standard colors used + for application windows, buttons and other features. These colors + are grouped into three \e {color groups}: \c Active, \c Inactive, + and \c Disabled. See the QPalette documentation for details about + color groups and the properties provided by SystemPalette. + + This can be used to color items in a way that provides a more + native look and feel. + + The following example creates a palette from the \c Active color + group and uses this to color the window and text items + appropriately: + + \snippet doc/src/snippets/declarative/systempalette.qml 0 + + \sa QPalette +*/ +QDeclarativeSystemPalette::QDeclarativeSystemPalette(QObject *parent) + : QObject(*(new QDeclarativeSystemPalettePrivate), parent) +{ + Q_D(QDeclarativeSystemPalette); + d->palette = QGuiApplication::palette(); + d->group = QPalette::Active; + qApp->installEventFilter(this); +} + +QDeclarativeSystemPalette::~QDeclarativeSystemPalette() +{ +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::window + The window (general background) color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::window() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Window); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::windowText + The window text (general foreground) color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::windowText() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::WindowText); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::base + The base color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::base() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Base); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::text + The text color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::text() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Text); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::alternateBase + The alternate base color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::alternateBase() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::AlternateBase); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::button + The button color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::button() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Button); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::buttonText + The button text foreground color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::buttonText() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::ButtonText); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::light + The light color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::light() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Light); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::midlight + The midlight color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::midlight() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Midlight); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::dark + The dark color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::dark() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Dark); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::mid + The mid color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::mid() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Mid); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::shadow + The shadow color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::shadow() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Shadow); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::highlight + The highlight color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::highlight() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::Highlight); +} + +/*! + \qmlproperty color QtQuick2::SystemPalette::highlightedText + The highlighted text color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarativeSystemPalette::highlightedText() const +{ + Q_D(const QDeclarativeSystemPalette); + return d->palette.color(d->group, QPalette::HighlightedText); +} + +/*! + \qmlproperty enumeration QtQuick2::SystemPalette::colorGroup + + The color group of the palette. This can be one of: + + \list + \o SystemPalette.Active (default) + \o SystemPalette.Inactive + \o SystemPalette.Disabled + \endlist + + \sa QPalette::ColorGroup +*/ +QDeclarativeSystemPalette::ColorGroup QDeclarativeSystemPalette::colorGroup() const +{ + Q_D(const QDeclarativeSystemPalette); + return (QDeclarativeSystemPalette::ColorGroup)d->group; +} + +void QDeclarativeSystemPalette::setColorGroup(QDeclarativeSystemPalette::ColorGroup colorGroup) +{ + Q_D(QDeclarativeSystemPalette); + d->group = (QPalette::ColorGroup)colorGroup; + emit paletteChanged(); +} + +bool QDeclarativeSystemPalette::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == qApp) { + if (event->type() == QEvent::ApplicationPaletteChange) { + QGuiApplication::postEvent(this, new QEvent(QEvent::ApplicationPaletteChange)); + return false; + } + } + return QObject::eventFilter(watched, event); +} + +bool QDeclarativeSystemPalette::event(QEvent *event) +{ + Q_D(QDeclarativeSystemPalette); + if (event->type() == QEvent::ApplicationPaletteChange) { + d->palette = QGuiApplication::palette(); + emit paletteChanged(); + return true; + } + return QObject::event(event); +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativesystempalette_p.h b/src/quick/util/qdeclarativesystempalette_p.h new file mode 100644 index 0000000000..25224fd4d1 --- /dev/null +++ b/src/quick/util/qdeclarativesystempalette_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESYSTEMPALETTE_H +#define QDECLARATIVESYSTEMPALETTE_H + +#include <qdeclarative.h> + +#include <QtCore/qobject.h> +#include <QPalette> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeSystemPalettePrivate; +class Q_AUTOTEST_EXPORT QDeclarativeSystemPalette : public QObject +{ + Q_OBJECT + Q_ENUMS(ColorGroup) + Q_DECLARE_PRIVATE(QDeclarativeSystemPalette) + + Q_PROPERTY(QDeclarativeSystemPalette::ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY paletteChanged) + Q_PROPERTY(QColor window READ window NOTIFY paletteChanged) + Q_PROPERTY(QColor windowText READ windowText NOTIFY paletteChanged) + Q_PROPERTY(QColor base READ base NOTIFY paletteChanged) + Q_PROPERTY(QColor text READ text NOTIFY paletteChanged) + Q_PROPERTY(QColor alternateBase READ alternateBase NOTIFY paletteChanged) + Q_PROPERTY(QColor button READ button NOTIFY paletteChanged) + Q_PROPERTY(QColor buttonText READ buttonText NOTIFY paletteChanged) + Q_PROPERTY(QColor light READ light NOTIFY paletteChanged) + Q_PROPERTY(QColor midlight READ midlight NOTIFY paletteChanged) + Q_PROPERTY(QColor dark READ dark NOTIFY paletteChanged) + Q_PROPERTY(QColor mid READ mid NOTIFY paletteChanged) + Q_PROPERTY(QColor shadow READ shadow NOTIFY paletteChanged) + Q_PROPERTY(QColor highlight READ highlight NOTIFY paletteChanged) + Q_PROPERTY(QColor highlightedText READ highlightedText NOTIFY paletteChanged) + +public: + QDeclarativeSystemPalette(QObject *parent=0); + ~QDeclarativeSystemPalette(); + + enum ColorGroup { Active = QPalette::Active, Inactive = QPalette::Inactive, Disabled = QPalette::Disabled }; + + QColor window() const; + QColor windowText() const; + + QColor base() const; + QColor text() const; + QColor alternateBase() const; + + QColor button() const; + QColor buttonText() const; + + QColor light() const; + QColor midlight() const; + QColor dark() const; + QColor mid() const; + QColor shadow() const; + + QColor highlight() const; + QColor highlightedText() const; + + QDeclarativeSystemPalette::ColorGroup colorGroup() const; + void setColorGroup(QDeclarativeSystemPalette::ColorGroup); + +Q_SIGNALS: + void paletteChanged(); + +private: + bool eventFilter(QObject *watched, QEvent *event); + bool event(QEvent *event); + +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSystemPalette) + +QT_END_HEADER + +#endif // QDECLARATIVESYSTEMPALETTE_H diff --git a/src/quick/util/qdeclarativetimeline.cpp b/src/quick/util/qdeclarativetimeline.cpp new file mode 100644 index 0000000000..7066d3d062 --- /dev/null +++ b/src/quick/util/qdeclarativetimeline.cpp @@ -0,0 +1,947 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativetimeline_p_p.h" + +#include <QDebug> +#include <QMutex> +#include <QThread> +#include <QWaitCondition> +#include <QEvent> +#include <QCoreApplication> +#include <QEasingCurve> +#include <QTime> + +QT_BEGIN_NAMESPACE + +struct Update { + Update(QDeclarativeTimeLineValue *_g, qreal _v) + : g(_g), v(_v) {} + Update(const QDeclarativeTimeLineCallback &_e) + : g(0), v(0), e(_e) {} + + QDeclarativeTimeLineValue *g; + qreal v; + QDeclarativeTimeLineCallback e; +}; + +struct QDeclarativeTimeLinePrivate +{ + QDeclarativeTimeLinePrivate(QDeclarativeTimeLine *); + + struct Op { + enum Type { + Pause, + Set, + Move, + MoveBy, + Accel, + AccelDistance, + Execute + }; + Op() {} + Op(Type t, int l, qreal v, qreal v2, int o, + const QDeclarativeTimeLineCallback &ev = QDeclarativeTimeLineCallback(), const QEasingCurve &es = QEasingCurve()) + : type(t), length(l), value(v), value2(v2), order(o), event(ev), + easing(es) {} + Op(const Op &o) + : type(o.type), length(o.length), value(o.value), value2(o.value2), + order(o.order), event(o.event), easing(o.easing) {} + Op &operator=(const Op &o) { + type = o.type; length = o.length; value = o.value; + value2 = o.value2; order = o.order; event = o.event; + easing = o.easing; + return *this; + } + + Type type; + int length; + qreal value; + qreal value2; + + int order; + QDeclarativeTimeLineCallback event; + QEasingCurve easing; + }; + struct TimeLine + { + TimeLine() : length(0), consumedOpLength(0), base(0.) {} + QList<Op> ops; + int length; + int consumedOpLength; + qreal base; + }; + + int length; + int syncPoint; + typedef QHash<QDeclarativeTimeLineObject *, TimeLine> Ops; + Ops ops; + QDeclarativeTimeLine *q; + + void add(QDeclarativeTimeLineObject &, const Op &); + qreal value(const Op &op, int time, qreal base, bool *) const; + + int advance(int); + + bool clockRunning; + int prevTime; + + int order; + + QDeclarativeTimeLine::SyncMode syncMode; + int syncAdj; + QList<QPair<int, Update> > *updateQueue; +}; + +QDeclarativeTimeLinePrivate::QDeclarativeTimeLinePrivate(QDeclarativeTimeLine *parent) +: length(0), syncPoint(0), q(parent), clockRunning(false), prevTime(0), order(0), syncMode(QDeclarativeTimeLine::LocalSync), syncAdj(0), updateQueue(0) +{ +} + +void QDeclarativeTimeLinePrivate::add(QDeclarativeTimeLineObject &g, const Op &o) +{ + if (g._t && g._t != q) { + qWarning() << "QDeclarativeTimeLine: Cannot modify a QDeclarativeTimeLineValue owned by" + << "another timeline."; + return; + } + g._t = q; + + Ops::Iterator iter = ops.find(&g); + if (iter == ops.end()) { + iter = ops.insert(&g, TimeLine()); + if (syncPoint > 0) + q->pause(g, syncPoint); + } + if (!iter->ops.isEmpty() && + o.type == Op::Pause && + iter->ops.last().type == Op::Pause) { + iter->ops.last().length += o.length; + iter->length += o.length; + } else { + iter->ops.append(o); + iter->length += o.length; + } + + if (iter->length > length) + length = iter->length; + + if (!clockRunning) { + q->stop(); + prevTime = 0; + clockRunning = true; + + if (syncMode == QDeclarativeTimeLine::LocalSync) { + syncAdj = -1; + } else { + syncAdj = 0; + } + q->start(); +/* q->tick(0); + if (syncMode == QDeclarativeTimeLine::LocalSync) { + syncAdj = -1; + } else { + syncAdj = 0; + } + */ + } +} + +qreal QDeclarativeTimeLinePrivate::value(const Op &op, int time, qreal base, bool *changed) const +{ + Q_ASSERT(time >= 0); + Q_ASSERT(time <= op.length); + *changed = true; + + switch(op.type) { + case Op::Pause: + *changed = false; + return base; + case Op::Set: + return op.value; + case Op::Move: + if (time == 0) { + return base; + } else if (time == (op.length)) { + return op.value; + } else { + qreal delta = op.value - base; + qreal pTime = (qreal)(time) / (qreal)op.length; + if (op.easing.type() == QEasingCurve::Linear) + return base + delta * pTime; + else + return base + delta * op.easing.valueForProgress(pTime); + } + case Op::MoveBy: + if (time == 0) { + return base; + } else if (time == (op.length)) { + return base + op.value; + } else { + qreal delta = op.value; + qreal pTime = (qreal)(time) / (qreal)op.length; + if (op.easing.type() == QEasingCurve::Linear) + return base + delta * pTime; + else + return base + delta * op.easing.valueForProgress(pTime); + } + case Op::Accel: + if (time == 0) { + return base; + } else { + qreal t = (qreal)(time) / 1000.0f; + qreal delta = op.value * t + 0.5f * op.value2 * t * t; + return base + delta; + } + case Op::AccelDistance: + if (time == 0) { + return base; + } else if (time == (op.length)) { + return base + op.value2; + } else { + qreal t = (qreal)(time) / 1000.0f; + qreal accel = -1.0f * 1000.0f * op.value / (qreal)op.length; + qreal delta = op.value * t + 0.5f * accel * t * t; + return base + delta; + + } + case Op::Execute: + op.event.d0(op.event.d1); + *changed = false; + return -1; + } + + return base; +} + +/*! + \internal + \class QDeclarativeTimeLine + \brief The QDeclarativeTimeLine class provides a timeline for controlling animations. + + QDeclarativeTimeLine is similar to QTimeLine except: + \list + \i It updates QDeclarativeTimeLineValue instances directly, rather than maintaining a single + current value. + + For example, the following animates a simple value over 200 milliseconds: + \code + QDeclarativeTimeLineValue v(<starting value>); + QDeclarativeTimeLine tl; + tl.move(v, 100., 200); + tl.start() + \endcode + + If your program needs to know when values are changed, it can either + connect to the QDeclarativeTimeLine's updated() signal, or inherit from QDeclarativeTimeLineValue + and reimplement the QDeclarativeTimeLineValue::setValue() method. + + \i Supports multiple QDeclarativeTimeLineValue, arbitrary start and end values and allows + animations to be strung together for more complex effects. + + For example, the following animation moves the x and y coordinates of + an object from wherever they are to the position (100, 100) in 50 + milliseconds and then further animates them to (100, 200) in 50 + milliseconds: + + \code + QDeclarativeTimeLineValue x(<starting value>); + QDeclarativeTimeLineValue y(<starting value>); + + QDeclarativeTimeLine tl; + tl.start(); + + tl.move(x, 100., 50); + tl.move(y, 100., 50); + tl.move(y, 200., 50); + \endcode + + \i All QDeclarativeTimeLine instances share a single, synchronized clock. + + Actions scheduled within the same event loop tick are scheduled + synchronously against each other, regardless of the wall time between the + scheduling. Synchronized scheduling applies both to within the same + QDeclarativeTimeLine and across separate QDeclarativeTimeLine's within the same process. + + \endlist + + Currently easing functions are not supported. +*/ + + +/*! + Construct a new QDeclarativeTimeLine with the specified \a parent. +*/ +QDeclarativeTimeLine::QDeclarativeTimeLine(QObject *parent) +: QAbstractAnimation(parent) +{ + d = new QDeclarativeTimeLinePrivate(this); +} + +/*! + Destroys the time line. Any inprogress animations are canceled, but not + completed. +*/ +QDeclarativeTimeLine::~QDeclarativeTimeLine() +{ + for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) + iter.key()->_t = 0; + + delete d; d = 0; +} + +/*! + \enum QDeclarativeTimeLine::SyncMode + */ + +/*! + Return the timeline's synchronization mode. + */ +QDeclarativeTimeLine::SyncMode QDeclarativeTimeLine::syncMode() const +{ + return d->syncMode; +} + +/*! + Set the timeline's synchronization mode to \a syncMode. + */ +void QDeclarativeTimeLine::setSyncMode(SyncMode syncMode) +{ + d->syncMode = syncMode; +} + +/*! + Pause \a obj for \a time milliseconds. +*/ +void QDeclarativeTimeLine::pause(QDeclarativeTimeLineObject &obj, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Pause, time, 0., 0., d->order++); + d->add(obj, op); +} + +/*! + Execute the \a event. + */ +void QDeclarativeTimeLine::callback(const QDeclarativeTimeLineCallback &callback) +{ + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Execute, 0, 0, 0., d->order++, callback); + d->add(*callback.callbackObject(), op); +} + +/*! + Set the \a value of \a timeLineValue. +*/ +void QDeclarativeTimeLine::set(QDeclarativeTimeLineValue &timeLineValue, qreal value) +{ + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Set, 0, value, 0., d->order++); + d->add(timeLineValue, op); +} + +/*! + Decelerate \a timeLineValue from the starting \a velocity to zero at the + given \a acceleration rate. Although the \a acceleration is technically + a deceleration, it should always be positive. The QDeclarativeTimeLine will ensure + that the deceleration is in the opposite direction to the initial velocity. +*/ +int QDeclarativeTimeLine::accel(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal acceleration) +{ + if (acceleration == 0.0f) + return -1; + + if ((velocity > 0.0f) == (acceleration > 0.0f)) + acceleration = acceleration * -1.0f; + + int time = static_cast<int>(-1000 * velocity / acceleration); + + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++); + d->add(timeLineValue, op); + + return time; +} + +/*! + \overload + + Decelerate \a timeLineValue from the starting \a velocity to zero at the + given \a acceleration rate over a maximum distance of maxDistance. + + If necessary, QDeclarativeTimeLine will reduce the acceleration to ensure that the + entire operation does not require a move of more than \a maxDistance. + \a maxDistance should always be positive. +*/ +int QDeclarativeTimeLine::accel(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal acceleration, qreal maxDistance) +{ + if (maxDistance == 0.0f || acceleration == 0.0f) + return -1; + + Q_ASSERT(acceleration > 0.0f && maxDistance > 0.0f); + + qreal maxAccel = (velocity * velocity) / (2.0f * maxDistance); + if (maxAccel > acceleration) + acceleration = maxAccel; + + if ((velocity > 0.0f) == (acceleration > 0.0f)) + acceleration = acceleration * -1.0f; + + int time = static_cast<int>(-1000 * velocity / acceleration); + + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++); + d->add(timeLineValue, op); + + return time; +} + +/*! + Decelerate \a timeLineValue from the starting \a velocity to zero over the given + \a distance. This is like accel(), but the QDeclarativeTimeLine calculates the exact + deceleration to use. + + \a distance should be positive. +*/ +int QDeclarativeTimeLine::accelDistance(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal distance) +{ + if (distance == 0.0f || velocity == 0.0f) + return -1; + + Q_ASSERT((distance >= 0.0f) == (velocity >= 0.0f)); + + int time = static_cast<int>(1000 * (2.0f * distance) / velocity); + + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::AccelDistance, time, velocity, distance, d->order++); + d->add(timeLineValue, op); + + return time; +} + +/*! + Linearly change the \a timeLineValue from its current value to the given + \a destination value over \a time milliseconds. +*/ +void QDeclarativeTimeLine::move(QDeclarativeTimeLineValue &timeLineValue, qreal destination, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++); + d->add(timeLineValue, op); +} + +/*! + Change the \a timeLineValue from its current value to the given \a destination + value over \a time milliseconds using the \a easing curve. + */ +void QDeclarativeTimeLine::move(QDeclarativeTimeLineValue &timeLineValue, qreal destination, const QEasingCurve &easing, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++, QDeclarativeTimeLineCallback(), easing); + d->add(timeLineValue, op); +} + +/*! + Linearly change the \a timeLineValue from its current value by the \a change amount + over \a time milliseconds. +*/ +void QDeclarativeTimeLine::moveBy(QDeclarativeTimeLineValue &timeLineValue, qreal change, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++); + d->add(timeLineValue, op); +} + +/*! + Change the \a timeLineValue from its current value by the \a change amount over + \a time milliseconds using the \a easing curve. + */ +void QDeclarativeTimeLine::moveBy(QDeclarativeTimeLineValue &timeLineValue, qreal change, const QEasingCurve &easing, int time) +{ + if (time <= 0) return; + QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++, QDeclarativeTimeLineCallback(), easing); + d->add(timeLineValue, op); +} + +/*! + Cancel (but don't complete) all scheduled actions for \a timeLineValue. +*/ +void QDeclarativeTimeLine::reset(QDeclarativeTimeLineValue &timeLineValue) +{ + if (!timeLineValue._t) + return; + if (timeLineValue._t != this) { + qWarning() << "QDeclarativeTimeLine: Cannot reset a QDeclarativeTimeLineValue owned by another timeline."; + return; + } + remove(&timeLineValue); + timeLineValue._t = 0; +} + +int QDeclarativeTimeLine::duration() const +{ + return -1; +} + +/*! + Synchronize the end point of \a timeLineValue to the endpoint of \a syncTo + within this timeline. + + Following operations on \a timeLineValue in this timeline will be scheduled after + all the currently scheduled actions on \a syncTo are complete. In + pseudo-code this is equivalent to: + \code + QDeclarativeTimeLine::pause(timeLineValue, min(0, length_of(syncTo) - length_of(timeLineValue))) + \endcode +*/ +void QDeclarativeTimeLine::sync(QDeclarativeTimeLineValue &timeLineValue, QDeclarativeTimeLineValue &syncTo) +{ + QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.find(&syncTo); + if (iter == d->ops.end()) + return; + int length = iter->length; + + iter = d->ops.find(&timeLineValue); + if (iter == d->ops.end()) { + pause(timeLineValue, length); + } else { + int glength = iter->length; + pause(timeLineValue, length - glength); + } +} + +/*! + Synchronize the end point of \a timeLineValue to the endpoint of the longest + action cursrently scheduled in the timeline. + + In pseudo-code, this is equivalent to: + \code + QDeclarativeTimeLine::pause(timeLineValue, length_of(timeline) - length_of(timeLineValue)) + \endcode +*/ +void QDeclarativeTimeLine::sync(QDeclarativeTimeLineValue &timeLineValue) +{ + QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.find(&timeLineValue); + if (iter == d->ops.end()) { + pause(timeLineValue, d->length); + } else { + pause(timeLineValue, d->length - iter->length); + } +} + +/* + Synchronize all currently and future scheduled values in this timeline to + the longest action currently scheduled. + + For example: + \code + value1->setValue(0.); + value2->setValue(0.); + value3->setValue(0.); + QDeclarativeTimeLine tl; + ... + tl.move(value1, 10, 200); + tl.move(value2, 10, 100); + tl.sync(); + tl.move(value2, 20, 100); + tl.move(value3, 20, 100); + \endcode + + will result in: + + \table + \header \o \o 0ms \o 50ms \o 100ms \o 150ms \o 200ms \o 250ms \o 300ms + \row \o value1 \o 0 \o 2.5 \o 5.0 \o 7.5 \o 10 \o 10 \o 10 + \row \o value2 \o 0 \o 5.0 \o 10.0 \o 10.0 \o 10.0 \o 15.0 \o 20.0 + \row \o value2 \o 0 \o 0 \o 0 \o 0 \o 0 \o 10.0 \o 20.0 + \endtable +*/ + +/*void QDeclarativeTimeLine::sync() +{ + for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) + pause(*iter.key(), d->length - iter->length); + d->syncPoint = d->length; +}*/ + +/*! + \internal + + Temporary hack. + */ +void QDeclarativeTimeLine::setSyncPoint(int sp) +{ + d->syncPoint = sp; +} + +/*! + \internal + + Temporary hack. + */ +int QDeclarativeTimeLine::syncPoint() const +{ + return d->syncPoint; +} + +/*! + Returns true if the timeline is active. An active timeline is one where + QDeclarativeTimeLineValue actions are still pending. +*/ +bool QDeclarativeTimeLine::isActive() const +{ + return !d->ops.isEmpty(); +} + +/*! + Completes the timeline. All queued actions are played to completion, and then discarded. For example, + \code + QDeclarativeTimeLineValue v(0.); + QDeclarativeTimeLine tl; + tl.move(v, 100., 1000.); + // 500 ms passes + // v.value() == 50. + tl.complete(); + // v.value() == 100. + \endcode +*/ +void QDeclarativeTimeLine::complete() +{ + d->advance(d->length); +} + +/*! + Resets the timeline. All queued actions are discarded and QDeclarativeTimeLineValue's retain their current value. For example, + \code + QDeclarativeTimeLineValue v(0.); + QDeclarativeTimeLine tl; + tl.move(v, 100., 1000.); + // 500 ms passes + // v.value() == 50. + tl.clear(); + // v.value() == 50. + \endcode +*/ +void QDeclarativeTimeLine::clear() +{ + for (QDeclarativeTimeLinePrivate::Ops::ConstIterator iter = d->ops.begin(); iter != d->ops.end(); ++iter) + iter.key()->_t = 0; + d->ops.clear(); + d->length = 0; + d->syncPoint = 0; + //XXX need stop here? +} + +int QDeclarativeTimeLine::time() const +{ + return d->prevTime; +} + +/*! + \fn void QDeclarativeTimeLine::updated() + + Emitted each time the timeline modifies QDeclarativeTimeLineValues. Even if multiple + QDeclarativeTimeLineValues are changed, this signal is only emitted once for each clock tick. +*/ + +void QDeclarativeTimeLine::updateCurrentTime(int v) +{ + if (d->syncAdj == -1) + d->syncAdj = v; + v -= d->syncAdj; + + int timeChanged = v - d->prevTime; +#if 0 + if (!timeChanged) + return; +#endif + d->prevTime = v; + d->advance(timeChanged); + emit updated(); + + // Do we need to stop the clock? + if (d->ops.isEmpty()) { + stop(); + d->prevTime = 0; + d->clockRunning = false; + emit completed(); + } /*else if (pauseTime > 0) { + GfxClock::cancelClock(); + d->prevTime = 0; + GfxClock::pauseFor(pauseTime); + d->syncAdj = 0; + d->clockRunning = false; + }*/ else if (/*!GfxClock::isActive()*/ state() != Running) { + stop(); + d->prevTime = 0; + d->clockRunning = true; + d->syncAdj = 0; + start(); + } +} + +bool operator<(const QPair<int, Update> &lhs, + const QPair<int, Update> &rhs) +{ + return lhs.first < rhs.first; +} + +int QDeclarativeTimeLinePrivate::advance(int t) +{ + int pauseTime = -1; + + // XXX - surely there is a more efficient way? + do { + pauseTime = -1; + // Minimal advance time + int advanceTime = t; + for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ++iter) { + TimeLine &tl = *iter; + Op &op = tl.ops.first(); + int length = op.length - tl.consumedOpLength; + + if (length < advanceTime) { + advanceTime = length; + if (advanceTime == 0) + break; + } + } + t -= advanceTime; + + // Process until then. A zero length advance time will only process + // sets. + QList<QPair<int, Update> > updates; + + for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ) { + QDeclarativeTimeLineValue *v = static_cast<QDeclarativeTimeLineValue *>(iter.key()); + TimeLine &tl = *iter; + Q_ASSERT(!tl.ops.isEmpty()); + + do { + Op &op = tl.ops.first(); + if (advanceTime == 0 && op.length != 0) + continue; + + if (tl.consumedOpLength == 0 && + op.type != Op::Pause && + op.type != Op::Execute) + tl.base = v->value(); + + if ((tl.consumedOpLength + advanceTime) == op.length) { + if (op.type == Op::Execute) { + updates << qMakePair(op.order, Update(op.event)); + } else { + bool changed = false; + qreal val = value(op, op.length, tl.base, &changed); + if (changed) + updates << qMakePair(op.order, Update(v, val)); + } + tl.length -= qMin(advanceTime, tl.length); + tl.consumedOpLength = 0; + tl.ops.removeFirst(); + } else { + tl.consumedOpLength += advanceTime; + bool changed = false; + qreal val = value(op, tl.consumedOpLength, tl.base, &changed); + if (changed) + updates << qMakePair(op.order, Update(v, val)); + tl.length -= qMin(advanceTime, tl.length); + break; + } + + } while(!tl.ops.isEmpty() && advanceTime == 0 && tl.ops.first().length == 0); + + + if (tl.ops.isEmpty()) { + iter = ops.erase(iter); + v->_t = 0; + } else { + if (tl.ops.first().type == Op::Pause && pauseTime != 0) { + int opPauseTime = tl.ops.first().length - tl.consumedOpLength; + if (pauseTime == -1 || opPauseTime < pauseTime) + pauseTime = opPauseTime; + } else { + pauseTime = 0; + } + ++iter; + } + } + + length -= qMin(length, advanceTime); + syncPoint -= advanceTime; + + qSort(updates.begin(), updates.end()); + updateQueue = &updates; + for (int ii = 0; ii < updates.count(); ++ii) { + const Update &v = updates.at(ii).second; + if (v.g) { + v.g->setValue(v.v); + } else { + v.e.d0(v.e.d1); + } + } + updateQueue = 0; + } while(t); + + return pauseTime; +} + +void QDeclarativeTimeLine::remove(QDeclarativeTimeLineObject *v) +{ + QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.find(v); + Q_ASSERT(iter != d->ops.end()); + + int len = iter->length; + d->ops.erase(iter); + if (len == d->length) { + // We need to recalculate the length + d->length = 0; + for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) { + + if (iter->length > d->length) + d->length = iter->length; + + } + } + if (d->ops.isEmpty()) { + stop(); + d->clockRunning = false; + } else if (/*!GfxClock::isActive()*/ state() != Running) { + stop(); + d->prevTime = 0; + d->clockRunning = true; + + if (d->syncMode == QDeclarativeTimeLine::LocalSync) { + d->syncAdj = -1; + } else { + d->syncAdj = 0; + } + start(); + } + + if (d->updateQueue) { + for (int ii = 0; ii < d->updateQueue->count(); ++ii) { + if (d->updateQueue->at(ii).second.g == v || + d->updateQueue->at(ii).second.e.callbackObject() == v) { + d->updateQueue->removeAt(ii); + --ii; + } + } + } + + +} + +/*! + \internal + \class QDeclarativeTimeLineValue + \brief The QDeclarativeTimeLineValue class provides a value that can be modified by QDeclarativeTimeLine. +*/ + +/*! + \fn QDeclarativeTimeLineValue::QDeclarativeTimeLineValue(qreal value = 0) + + Construct a new QDeclarativeTimeLineValue with an initial \a value. +*/ + +/*! + \fn qreal QDeclarativeTimeLineValue::value() const + + Return the current value. +*/ + +/*! + \fn void QDeclarativeTimeLineValue::setValue(qreal value) + + Set the current \a value. +*/ + +/*! + \fn QDeclarativeTimeLine *QDeclarativeTimeLineValue::timeLine() const + + If a QDeclarativeTimeLine is operating on this value, return a pointer to it, + otherwise return null. +*/ + + +QDeclarativeTimeLineObject::QDeclarativeTimeLineObject() +: _t(0) +{ +} + +QDeclarativeTimeLineObject::~QDeclarativeTimeLineObject() +{ + if (_t) { + _t->remove(this); + _t = 0; + } +} + +QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback() +: d0(0), d1(0), d2(0) +{ +} + +QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback(QDeclarativeTimeLineObject *b, Callback f, void *d) +: d0(f), d1(d), d2(b) +{ +} + +QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback(const QDeclarativeTimeLineCallback &o) +: d0(o.d0), d1(o.d1), d2(o.d2) +{ +} + +QDeclarativeTimeLineCallback &QDeclarativeTimeLineCallback::operator=(const QDeclarativeTimeLineCallback &o) +{ + d0 = o.d0; + d1 = o.d1; + d2 = o.d2; + return *this; +} + +QDeclarativeTimeLineObject *QDeclarativeTimeLineCallback::callbackObject() const +{ + return d2; +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativetimeline_p_p.h b/src/quick/util/qdeclarativetimeline_p_p.h new file mode 100644 index 0000000000..d84053903f --- /dev/null +++ b/src/quick/util/qdeclarativetimeline_p_p.h @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETIMELINE_H +#define QDECLARATIVETIMELINE_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 <QtCore/QObject> +#include <QtCore/QAbstractAnimation> + +QT_BEGIN_NAMESPACE + +class QEasingCurve; +class QDeclarativeTimeLineValue; +class QDeclarativeTimeLineCallback; +struct QDeclarativeTimeLinePrivate; +class QDeclarativeTimeLineObject; +class Q_AUTOTEST_EXPORT QDeclarativeTimeLine : public QAbstractAnimation +{ +Q_OBJECT +public: + QDeclarativeTimeLine(QObject *parent = 0); + ~QDeclarativeTimeLine(); + + enum SyncMode { LocalSync, GlobalSync }; + SyncMode syncMode() const; + void setSyncMode(SyncMode); + + void pause(QDeclarativeTimeLineObject &, int); + void callback(const QDeclarativeTimeLineCallback &); + void set(QDeclarativeTimeLineValue &, qreal); + + int accel(QDeclarativeTimeLineValue &, qreal velocity, qreal accel); + int accel(QDeclarativeTimeLineValue &, qreal velocity, qreal accel, qreal maxDistance); + int accelDistance(QDeclarativeTimeLineValue &, qreal velocity, qreal distance); + + void move(QDeclarativeTimeLineValue &, qreal destination, int time = 500); + void move(QDeclarativeTimeLineValue &, qreal destination, const QEasingCurve &, int time = 500); + void moveBy(QDeclarativeTimeLineValue &, qreal change, int time = 500); + void moveBy(QDeclarativeTimeLineValue &, qreal change, const QEasingCurve &, int time = 500); + + void sync(); + void setSyncPoint(int); + int syncPoint() const; + + void sync(QDeclarativeTimeLineValue &); + void sync(QDeclarativeTimeLineValue &, QDeclarativeTimeLineValue &); + + void reset(QDeclarativeTimeLineValue &); + + void complete(); + void clear(); + bool isActive() const; + + int time() const; + + virtual int duration() const; +Q_SIGNALS: + void updated(); + void completed(); + +protected: + virtual void updateCurrentTime(int); + +private: + void remove(QDeclarativeTimeLineObject *); + friend class QDeclarativeTimeLineObject; + friend struct QDeclarativeTimeLinePrivate; + QDeclarativeTimeLinePrivate *d; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeTimeLineObject +{ +public: + QDeclarativeTimeLineObject(); + virtual ~QDeclarativeTimeLineObject(); + +protected: + friend class QDeclarativeTimeLine; + friend struct QDeclarativeTimeLinePrivate; + QDeclarativeTimeLine *_t; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeTimeLineValue : public QDeclarativeTimeLineObject +{ +public: + QDeclarativeTimeLineValue(qreal v = 0.) : _v(v) {} + + virtual qreal value() const { return _v; } + virtual void setValue(qreal v) { _v = v; } + + QDeclarativeTimeLine *timeLine() const { return _t; } + + operator qreal() const { return _v; } + QDeclarativeTimeLineValue &operator=(qreal v) { setValue(v); return *this; } +private: + friend class QDeclarativeTimeLine; + friend struct QDeclarativeTimeLinePrivate; + qreal _v; +}; + +class Q_AUTOTEST_EXPORT QDeclarativeTimeLineCallback +{ +public: + typedef void (*Callback)(void *); + + QDeclarativeTimeLineCallback(); + QDeclarativeTimeLineCallback(QDeclarativeTimeLineObject *b, Callback, void * = 0); + QDeclarativeTimeLineCallback(const QDeclarativeTimeLineCallback &o); + + QDeclarativeTimeLineCallback &operator=(const QDeclarativeTimeLineCallback &o); + QDeclarativeTimeLineObject *callbackObject() const; + +private: + friend struct QDeclarativeTimeLinePrivate; + Callback d0; + void *d1; + QDeclarativeTimeLineObject *d2; +}; + +template<class T> +class QDeclarativeTimeLineValueProxy : public QDeclarativeTimeLineValue +{ +public: + QDeclarativeTimeLineValueProxy(T *cls, void (T::*func)(qreal), qreal v = 0.) + : QDeclarativeTimeLineValue(v), _class(cls), _setFunctionReal(func), _setFunctionInt(0) + { + Q_ASSERT(_class); + } + + QDeclarativeTimeLineValueProxy(T *cls, void (T::*func)(int), qreal v = 0.) + : QDeclarativeTimeLineValue(v), _class(cls), _setFunctionReal(0), _setFunctionInt(func) + { + Q_ASSERT(_class); + } + + virtual void setValue(qreal v) + { + QDeclarativeTimeLineValue::setValue(v); + if (_setFunctionReal) (_class->*_setFunctionReal)(v); + else if (_setFunctionInt) (_class->*_setFunctionInt)((int)v); + } + +private: + T *_class; + void (T::*_setFunctionReal)(qreal); + void (T::*_setFunctionInt)(int); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/util/qdeclarativetimer.cpp b/src/quick/util/qdeclarativetimer.cpp new file mode 100644 index 0000000000..a4a2362680 --- /dev/null +++ b/src/quick/util/qdeclarativetimer.cpp @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativetimer_p.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qpauseanimation.h> +#include <qdebug.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + + + +class QDeclarativeTimerPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeTimer) +public: + QDeclarativeTimerPrivate() + : interval(1000), running(false), repeating(false), triggeredOnStart(false) + , classBegun(false), componentComplete(false), firstTick(true) {} + int interval; + QPauseAnimation pause; + bool running : 1; + bool repeating : 1; + bool triggeredOnStart : 1; + bool classBegun : 1; + bool componentComplete : 1; + bool firstTick : 1; +}; + +/*! + \qmlclass Timer QDeclarativeTimer + \inqmlmodule QtQuick 2 + \ingroup qml-utility-elements + \brief The Timer item triggers a handler at a specified interval. + + A Timer can be used to trigger an action either once, or repeatedly + at a given interval. + + Here is a Timer that shows the current date and time, and updates + the text every 500 milliseconds. It uses the JavaScript \c Date + object to access the current time. + + \qml + import QtQuick 1.0 + + Item { + Timer { + interval: 500; running: true; repeat: true + onTriggered: time.text = Date().toString() + } + + Text { id: time } + } + \endqml + + The Timer element is synchronized with the animation timer. Since the animation + timer is usually set to 60fps, the resolution of Timer will be + at best 16ms. + + If the Timer is running and one of its properties is changed, the + elapsed time will be reset. For example, if a Timer with interval of + 1000ms has its \e repeat property changed 500ms after starting, the + elapsed time will be reset to 0, and the Timer will be triggered + 1000ms later. + + \sa {declarative/toys/clocks}{Clocks example} +*/ + +QDeclarativeTimer::QDeclarativeTimer(QObject *parent) + : QObject(*(new QDeclarativeTimerPrivate), parent) +{ + Q_D(QDeclarativeTimer); + connect(&d->pause, SIGNAL(currentLoopChanged(int)), this, SLOT(ticked())); + connect(&d->pause, SIGNAL(finished()), this, SLOT(finished())); + d->pause.setLoopCount(1); + d->pause.setDuration(d->interval); +} + +/*! + \qmlproperty int QtQuick2::Timer::interval + + Sets the \a interval between triggers, in milliseconds. + + The default interval is 1000 milliseconds. +*/ +void QDeclarativeTimer::setInterval(int interval) +{ + Q_D(QDeclarativeTimer); + if (interval != d->interval) { + d->interval = interval; + update(); + emit intervalChanged(); + } +} + +int QDeclarativeTimer::interval() const +{ + Q_D(const QDeclarativeTimer); + return d->interval; +} + +/*! + \qmlproperty bool QtQuick2::Timer::running + + If set to true, starts the timer; otherwise stops the timer. + For a non-repeating timer, \a running is set to false after the + timer has been triggered. + + \a running defaults to false. + + \sa repeat +*/ +bool QDeclarativeTimer::isRunning() const +{ + Q_D(const QDeclarativeTimer); + return d->running; +} + +void QDeclarativeTimer::setRunning(bool running) +{ + Q_D(QDeclarativeTimer); + if (d->running != running) { + d->running = running; + d->firstTick = true; + emit runningChanged(); + update(); + } +} + +/*! + \qmlproperty bool QtQuick2::Timer::repeat + + If \a repeat is true the timer is triggered repeatedly at the + specified interval; otherwise, the timer will trigger once at the + specified interval and then stop (i.e. running will be set to false). + + \a repeat defaults to false. + + \sa running +*/ +bool QDeclarativeTimer::isRepeating() const +{ + Q_D(const QDeclarativeTimer); + return d->repeating; +} + +void QDeclarativeTimer::setRepeating(bool repeating) +{ + Q_D(QDeclarativeTimer); + if (repeating != d->repeating) { + d->repeating = repeating; + update(); + emit repeatChanged(); + } +} + +/*! + \qmlproperty bool QtQuick2::Timer::triggeredOnStart + + When a timer is started, the first trigger is usually after the specified + interval has elapsed. It is sometimes desirable to trigger immediately + when the timer is started; for example, to establish an initial + state. + + If \a triggeredOnStart is true, the timer is triggered immediately + when started, and subsequently at the specified interval. Note that if + \e repeat is set to false, the timer is triggered twice; once on start, + and again at the interval. + + \a triggeredOnStart defaults to false. + + \sa running +*/ +bool QDeclarativeTimer::triggeredOnStart() const +{ + Q_D(const QDeclarativeTimer); + return d->triggeredOnStart; +} + +void QDeclarativeTimer::setTriggeredOnStart(bool triggeredOnStart) +{ + Q_D(QDeclarativeTimer); + if (d->triggeredOnStart != triggeredOnStart) { + d->triggeredOnStart = triggeredOnStart; + update(); + emit triggeredOnStartChanged(); + } +} + +/*! + \qmlmethod QtQuick2::Timer::start() + \brief Starts the timer. + + If the timer is already running, calling this method has no effect. The + \c running property will be true following a call to \c start(). +*/ +void QDeclarativeTimer::start() +{ + setRunning(true); +} + +/*! + \qmlmethod QtQuick2::Timer::stop() + \brief Stops the timer. + + If the timer is not running, calling this method has no effect. The + \c running property will be false following a call to \c stop(). +*/ +void QDeclarativeTimer::stop() +{ + setRunning(false); +} + +/*! + \qmlmethod QtQuick2::Timer::restart() + \brief Restarts the timer. + + If the Timer is not running it will be started, otherwise it will be + stopped, reset to initial state and started. The \c running property + will be true following a call to \c restart(). +*/ +void QDeclarativeTimer::restart() +{ + setRunning(false); + setRunning(true); +} + +void QDeclarativeTimer::update() +{ + Q_D(QDeclarativeTimer); + if (d->classBegun && !d->componentComplete) + return; + d->pause.stop(); + if (d->running) { + d->pause.setCurrentTime(0); + d->pause.setLoopCount(d->repeating ? -1 : 1); + d->pause.setDuration(d->interval); + d->pause.start(); + if (d->triggeredOnStart && d->firstTick) { + QCoreApplication::removePostedEvents(this, QEvent::MetaCall); + QMetaObject::invokeMethod(this, "ticked", Qt::QueuedConnection); + } + } +} + +void QDeclarativeTimer::classBegin() +{ + Q_D(QDeclarativeTimer); + d->classBegun = true; +} + +void QDeclarativeTimer::componentComplete() +{ + Q_D(QDeclarativeTimer); + d->componentComplete = true; + update(); +} + +/*! + \qmlsignal QtQuick2::Timer::onTriggered() + + This handler is called when the Timer is triggered. +*/ +void QDeclarativeTimer::ticked() +{ + Q_D(QDeclarativeTimer); + if (d->running && (d->pause.currentTime() > 0 || (d->triggeredOnStart && d->firstTick))) + emit triggered(); + d->firstTick = false; +} + +void QDeclarativeTimer::finished() +{ + Q_D(QDeclarativeTimer); + if (d->repeating || !d->running) + return; + emit triggered(); + d->running = false; + d->firstTick = false; + emit runningChanged(); +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativetimer_p.h b/src/quick/util/qdeclarativetimer_p.h new file mode 100644 index 0000000000..acf728236d --- /dev/null +++ b/src/quick/util/qdeclarativetimer_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETIMER_H +#define QDECLARATIVETIMER_H + +#include <qdeclarative.h> + +#include <QtCore/qobject.h> +#include <QtCore/qabstractanimation.h> + +#include <private/qtquickglobal_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeTimerPrivate; +class Q_QUICK_PRIVATE_EXPORT QDeclarativeTimer : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeTimer) + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged) + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool repeat READ isRepeating WRITE setRepeating NOTIFY repeatChanged) + Q_PROPERTY(bool triggeredOnStart READ triggeredOnStart WRITE setTriggeredOnStart NOTIFY triggeredOnStartChanged) + Q_PROPERTY(QObject *parent READ parent CONSTANT) + +public: + QDeclarativeTimer(QObject *parent=0); + + void setInterval(int interval); + int interval() const; + + bool isRunning() const; + void setRunning(bool running); + + bool isRepeating() const; + void setRepeating(bool repeating); + + bool triggeredOnStart() const; + void setTriggeredOnStart(bool triggeredOnStart); + +protected: + void classBegin(); + void componentComplete(); + +public Q_SLOTS: + void start(); + void stop(); + void restart(); + +Q_SIGNALS: + void triggered(); + void runningChanged(); + void intervalChanged(); + void repeatChanged(); + void triggeredOnStartChanged(); + +private: + void update(); + +private Q_SLOTS: + void ticked(); + void finished(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeTimer) + +QT_END_HEADER + +#endif diff --git a/src/quick/util/qdeclarativetransition.cpp b/src/quick/util/qdeclarativetransition.cpp new file mode 100644 index 0000000000..916e599cc6 --- /dev/null +++ b/src/quick/util/qdeclarativetransition.cpp @@ -0,0 +1,392 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativetransition_p.h" + +#include "qdeclarativestate_p.h" +#include "qdeclarativestate_p_p.h" +#include "qdeclarativestateoperations_p.h" +#include "qdeclarativeanimation_p.h" +#include "qdeclarativeanimation_p_p.h" +#include "qdeclarativetransitionmanager_p_p.h" + +#include <QParallelAnimationGroup> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Transition QDeclarativeTransition + \inqmlmodule QtQuick 2 + \ingroup qml-animation-transition + \brief The Transition element defines animated transitions that occur on state changes. + + A Transition defines the animations to be applied when a \l State change occurs. + + For example, the following \l Rectangle has two states: the default state, and + an added "moved" state. In the "moved state, the rectangle's position changes + to (50, 50). The added Transition specifies that when the rectangle + changes between the default and the "moved" state, any changes + to the \c x and \c y properties should be animated, using an \c Easing.InOutQuad. + + \snippet doc/src/snippets/declarative/transition.qml 0 + + Notice the example does not require \l{PropertyAnimation::}{to} and + \l{PropertyAnimation::}{from} values for the NumberAnimation. As a convenience, + these properties are automatically set to the values of \c x and \c y before + and after the state change; the \c from values are provided by + the current values of \c x and \c y, and the \c to values are provided by + the PropertyChanges object. If you wish, you can provide \l{PropertyAnimation::}{to} and + \l{PropertyAnimation::}{from} values anyway to override the default values. + + By default, a Transition's animations are applied for any state change in the + parent item. The Transition \l {Transition::}{from} and \l {Transition::}{to} + values can be set to restrict the animations to only be applied when changing + from one particular state to another. + + To define multiple transitions, specify \l Item::transitions as a list: + + \snippet doc/src/snippets/declarative/transitions-list.qml list of transitions + + If multiple Transitions are specified, only a single (best-matching) Transition will be applied for any particular + state change. In the example above, when changing to \c state1, the first transition will be used, rather + than the more generic second transition. + + If a state change has a Transition that matches the same property as a + \l Behavior, the Transition animation overrides the \l Behavior for that + state change. + + \sa {QML Animation and Transitions}, {declarative/animation/states}{states example}, {qmlstates}{States}, {QtDeclarative} +*/ + +//ParallelAnimationWrapper allows us to do a "callback" when the animation finishes, rather than connecting +//and disconnecting signals and slots frequently +class ParallelAnimationWrapper : public QParallelAnimationGroup +{ + Q_OBJECT +public: + ParallelAnimationWrapper(QObject *parent = 0) : QParallelAnimationGroup(parent) {} + QDeclarativeTransitionPrivate *trans; +protected: + virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState); +}; + +class QDeclarativeTransitionPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeTransition) +public: + QDeclarativeTransitionPrivate() + : fromState(QLatin1String("*")), toState(QLatin1String("*")), + reversed(false), reversible(false), enabled(true), manager(0) + { + group.trans = this; + } + + QString fromState; + QString toState; + bool reversed; + bool reversible; + bool enabled; + ParallelAnimationWrapper group; + QDeclarativeTransitionManager *manager; + + void complete() + { + manager->complete(); + } + static void append_animation(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list, QDeclarativeAbstractAnimation *a); + static int animation_count(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list); + static QDeclarativeAbstractAnimation* animation_at(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list, int pos); + static void clear_animations(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list); + QList<QDeclarativeAbstractAnimation *> animations; +}; + +void QDeclarativeTransitionPrivate::append_animation(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list, QDeclarativeAbstractAnimation *a) +{ + QDeclarativeTransition *q = static_cast<QDeclarativeTransition *>(list->object); + q->d_func()->animations.append(a); + q->d_func()->group.addAnimation(a->qtAnimation()); + a->setDisableUserControl(); +} + +int QDeclarativeTransitionPrivate::animation_count(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list) +{ + QDeclarativeTransition *q = static_cast<QDeclarativeTransition *>(list->object); + return q->d_func()->animations.count(); +} + +QDeclarativeAbstractAnimation* QDeclarativeTransitionPrivate::animation_at(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list, int pos) +{ + QDeclarativeTransition *q = static_cast<QDeclarativeTransition *>(list->object); + return q->d_func()->animations.at(pos); +} + +void QDeclarativeTransitionPrivate::clear_animations(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list) +{ + QDeclarativeTransition *q = static_cast<QDeclarativeTransition *>(list->object); + while (q->d_func()->animations.count()) { + QDeclarativeAbstractAnimation *firstAnim = q->d_func()->animations.at(0); + q->d_func()->group.removeAnimation(firstAnim->qtAnimation()); + q->d_func()->animations.removeAll(firstAnim); + } +} + +void ParallelAnimationWrapper::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) +{ + QParallelAnimationGroup::updateState(newState, oldState); + if (newState == Stopped && (duration() == -1 + || (direction() == QAbstractAnimation::Forward && currentLoopTime() == duration()) + || (direction() == QAbstractAnimation::Backward && currentLoopTime() == 0))) + { + trans->complete(); + } +} + + + +QDeclarativeTransition::QDeclarativeTransition(QObject *parent) + : QObject(*(new QDeclarativeTransitionPrivate), parent) +{ +} + +QDeclarativeTransition::~QDeclarativeTransition() +{ +} + +void QDeclarativeTransition::stop() +{ + Q_D(QDeclarativeTransition); + d->group.stop(); +} + +void QDeclarativeTransition::setReversed(bool r) +{ + Q_D(QDeclarativeTransition); + d->reversed = r; +} + +void QDeclarativeTransition::prepare(QDeclarativeStateOperation::ActionList &actions, + QList<QDeclarativeProperty> &after, + QDeclarativeTransitionManager *manager) +{ + Q_D(QDeclarativeTransition); + + qmlExecuteDeferred(this); + + if (d->reversed) { + for (int ii = d->animations.count() - 1; ii >= 0; --ii) { + d->animations.at(ii)->transition(actions, after, QDeclarativeAbstractAnimation::Backward); + } + } else { + for (int ii = 0; ii < d->animations.count(); ++ii) { + d->animations.at(ii)->transition(actions, after, QDeclarativeAbstractAnimation::Forward); + } + } + + d->manager = manager; + d->group.setDirection(d->reversed ? QAbstractAnimation::Backward : QAbstractAnimation::Forward); + d->group.start(); +} + +/*! + \qmlproperty string QtQuick2::Transition::from + \qmlproperty string QtQuick2::Transition::to + + These properties indicate the state changes that trigger the transition. + + The default values for these properties is "*" (that is, any state). + + For example, the following transition has not set the \c to and \c from + properties, so the animation is always applied when changing between + the two states (i.e. when the mouse is pressed and released). + + \snippet doc/src/snippets/declarative/transition-from-to.qml 0 + + If the transition was changed to this: + + \snippet doc/src/snippets/declarative/transition-from-to-modified.qml modified transition + + The animation would only be applied when changing from the default state to + the "brighter" state (i.e. when the mouse is pressed, but not on release). + + Multiple \c to and \from values can be set by using a comma-separated string. + + \sa reversible +*/ +QString QDeclarativeTransition::fromState() const +{ + Q_D(const QDeclarativeTransition); + return d->fromState; +} + +void QDeclarativeTransition::setFromState(const QString &f) +{ + Q_D(QDeclarativeTransition); + if (f == d->fromState) + return; + + d->fromState = f; + emit fromChanged(); +} + +/*! + \qmlproperty bool QtQuick2::Transition::reversible + This property holds whether the transition should be automatically reversed when the conditions that triggered this transition are reversed. + + The default value is false. + + By default, transitions run in parallel and are applied to all state + changes if the \l from and \l to states have not been set. In this + situation, the transition is automatically applied when a state change + is reversed, and it is not necessary to set this property to reverse + the transition. + + However, if a SequentialAnimation is used, or if the \l from or \l to + properties have been set, this property will need to be set to reverse + a transition when a state change is reverted. For example, the following + transition applies a sequential animation when the mouse is pressed, + and reverses the sequence of the animation when the mouse is released: + + \snippet doc/src/snippets/declarative/transition-reversible.qml 0 + + If the transition did not set the \c to and \c reversible values, then + on the mouse release, the transition would play the PropertyAnimation + before the ColorAnimation instead of reversing the sequence. +*/ +bool QDeclarativeTransition::reversible() const +{ + Q_D(const QDeclarativeTransition); + return d->reversible; +} + +void QDeclarativeTransition::setReversible(bool r) +{ + Q_D(QDeclarativeTransition); + if (r == d->reversible) + return; + + d->reversible = r; + emit reversibleChanged(); +} + +QString QDeclarativeTransition::toState() const +{ + Q_D(const QDeclarativeTransition); + return d->toState; +} + +void QDeclarativeTransition::setToState(const QString &t) +{ + Q_D(QDeclarativeTransition); + if (t == d->toState) + return; + + d->toState = t; + emit toChanged(); +} + +/*! + \qmlproperty bool QtQuick2::Transition::enabled + + This property holds whether the Transition will be run when moving + from the \c from state to the \c to state. + + By default a Transition is enabled. + + Note that in some circumstances disabling a Transition may cause an + alternative Transition to be used in its place. In the following + example, the generic Transition will be used to animate the change + from \c state1 to \c state2, as the more specific Transition has + been disabled. + + \qml + Item { + states: [ + State { name: "state1" ... } + State { name: "state2" ... } + ] + transitions: [ + Transition { from: "state1"; to: "state2"; enabled: false ... } + Transition { ... } + ] + } + \endqml +*/ + +bool QDeclarativeTransition::enabled() const +{ + Q_D(const QDeclarativeTransition); + return d->enabled; +} + +void QDeclarativeTransition::setEnabled(bool enabled) +{ + Q_D(QDeclarativeTransition); + if (d->enabled == enabled) + return; + d->enabled = enabled; + emit enabledChanged(); +} + +/*! + \qmlproperty list<Animation> QtQuick2::Transition::animations + \default + + This property holds a list of the animations to be run for this transition. + + \snippet examples/declarative/toys/dynamicscene/dynamicscene.qml top-level transitions + + The top-level animations are run in parallel. To run them sequentially, + define them within a SequentialAnimation: + + \snippet doc/src/snippets/declarative/transition-reversible.qml sequential animations +*/ +QDeclarativeListProperty<QDeclarativeAbstractAnimation> QDeclarativeTransition::animations() +{ + Q_D(QDeclarativeTransition); + return QDeclarativeListProperty<QDeclarativeAbstractAnimation>(this, &d->animations, QDeclarativeTransitionPrivate::append_animation, + QDeclarativeTransitionPrivate::animation_count, + QDeclarativeTransitionPrivate::animation_at, + QDeclarativeTransitionPrivate::clear_animations); +} + +QT_END_NAMESPACE + +#include <qdeclarativetransition.moc> diff --git a/src/quick/util/qdeclarativetransition_p.h b/src/quick/util/qdeclarativetransition_p.h new file mode 100644 index 0000000000..439458b6af --- /dev/null +++ b/src/quick/util/qdeclarativetransition_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETRANSITION_H +#define QDECLARATIVETRANSITION_H + +#include "qdeclarativestate_p.h" + +#include <qdeclarative.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeAbstractAnimation; +class QDeclarativeTransitionPrivate; +class QDeclarativeTransitionManager; +class Q_QUICK_EXPORT QDeclarativeTransition : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeTransition) + + Q_PROPERTY(QString from READ fromState WRITE setFromState NOTIFY fromChanged) + Q_PROPERTY(QString to READ toState WRITE setToState NOTIFY toChanged) + Q_PROPERTY(bool reversible READ reversible WRITE setReversible NOTIFY reversibleChanged) + Q_PROPERTY(QDeclarativeListProperty<QDeclarativeAbstractAnimation> animations READ animations) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_CLASSINFO("DefaultProperty", "animations") + Q_CLASSINFO("DeferredPropertyNames", "animations") + +public: + QDeclarativeTransition(QObject *parent=0); + ~QDeclarativeTransition(); + + QString fromState() const; + void setFromState(const QString &); + + QString toState() const; + void setToState(const QString &); + + bool reversible() const; + void setReversible(bool); + + bool enabled() const; + void setEnabled(bool enabled); + + QDeclarativeListProperty<QDeclarativeAbstractAnimation> animations(); + + void prepare(QDeclarativeStateOperation::ActionList &actions, + QList<QDeclarativeProperty> &after, + QDeclarativeTransitionManager *end); + + void setReversed(bool r); + void stop(); + +Q_SIGNALS: + void fromChanged(); + void toChanged(); + void reversibleChanged(); + void enabledChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeTransition) + +QT_END_HEADER + +#endif // QDECLARATIVETRANSITION_H diff --git a/src/quick/util/qdeclarativetransitionmanager.cpp b/src/quick/util/qdeclarativetransitionmanager.cpp new file mode 100644 index 0000000000..2dd1da70ed --- /dev/null +++ b/src/quick/util/qdeclarativetransitionmanager.cpp @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativetransitionmanager_p_p.h" + +#include "qdeclarativetransition_p.h" +#include "qdeclarativestate_p_p.h" +#include "qdeclarativestate_p.h" + +#include <private/qdeclarativebinding_p.h> +#include <private/qdeclarativeglobal_p.h> +#include <private/qdeclarativeproperty_p.h> + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +class QDeclarativeTransitionManagerPrivate +{ +public: + QDeclarativeTransitionManagerPrivate() + : state(0) {} + + void applyBindings(); + typedef QList<QDeclarativeSimpleAction> SimpleActionList; + QDeclarativeState *state; + QDeclarativeGuard<QDeclarativeTransition> transition; + QDeclarativeStateOperation::ActionList bindingsList; + SimpleActionList completeList; +}; + +QDeclarativeTransitionManager::QDeclarativeTransitionManager() +: d(new QDeclarativeTransitionManagerPrivate) +{ +} + +void QDeclarativeTransitionManager::setState(QDeclarativeState *s) +{ + d->state = s; +} + +QDeclarativeTransitionManager::~QDeclarativeTransitionManager() +{ + delete d; d = 0; +} + +void QDeclarativeTransitionManager::complete() +{ + d->applyBindings(); + + for (int ii = 0; ii < d->completeList.count(); ++ii) { + const QDeclarativeProperty &prop = d->completeList.at(ii).property(); + prop.write(d->completeList.at(ii).value()); + } + + d->completeList.clear(); + + if (d->state) + static_cast<QDeclarativeStatePrivate*>(QObjectPrivate::get(d->state))->complete(); +} + +void QDeclarativeTransitionManagerPrivate::applyBindings() +{ + foreach(const QDeclarativeAction &action, bindingsList) { + if (!action.toBinding.isNull()) { + QDeclarativePropertyPrivate::setBinding(action.property, action.toBinding.data()); + } else if (action.event) { + if (action.reverseEvent) + action.event->reverse(); + else + action.event->execute(); + } + + } + + bindingsList.clear(); +} + +void QDeclarativeTransitionManager::transition(const QList<QDeclarativeAction> &list, + QDeclarativeTransition *transition) +{ + cancel(); + + QDeclarativeStateOperation::ActionList applyList = list; + // Determine which actions are binding changes and disable any current bindings + foreach(const QDeclarativeAction &action, applyList) { + if (action.toBinding) + d->bindingsList << action; + if (action.fromBinding) + QDeclarativePropertyPrivate::setBinding(action.property, 0); // Disable current binding + if (action.event && action.event->changesBindings()) { //### assume isReversable()? + d->bindingsList << action; + action.event->clearBindings(); + } + } + + // Animated transitions need both the start and the end value for + // each property change. In the presence of bindings, the end values + // are non-trivial to calculate. As a "best effort" attempt, we first + // apply all the property and binding changes, then read all the actual + // final values, then roll back the changes and proceed as normal. + // + // This doesn't catch everything, and it might be a little fragile in + // some cases - but whatcha going to do? + // + // Note that we only fast forward if both a transition and bindings are + // present, as it is unneccessary (and potentially expensive) otherwise. + + if (transition && !d->bindingsList.isEmpty()) { + + // Apply all the property and binding changes + for (int ii = 0; ii < applyList.size(); ++ii) { + const QDeclarativeAction &action = applyList.at(ii); + if (!action.toBinding.isNull()) { + QDeclarativePropertyPrivate::setBinding(action.property, action.toBinding.data(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } else if (!action.event) { + QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } else if (action.event->isReversable()) { + if (action.reverseEvent) + action.event->reverse(QDeclarativeActionEvent::FastForward); + else + action.event->execute(QDeclarativeActionEvent::FastForward); + } + } + + // Read all the end values for binding changes + for (int ii = 0; ii < applyList.size(); ++ii) { + QDeclarativeAction *action = &applyList[ii]; + if (action->event) { + action->event->saveTargetValues(); + continue; + } + const QDeclarativeProperty &prop = action->property; + if (!action->toBinding.isNull() || !action->toValue.isValid()) { + action->toValue = prop.read(); + } + } + + // Revert back to the original values + foreach(const QDeclarativeAction &action, applyList) { + if (action.event) { + if (action.event->isReversable()) { + action.event->clearBindings(); + action.event->rewind(); + action.event->clearBindings(); //### shouldn't be needed + } + continue; + } + + if (action.toBinding) + QDeclarativePropertyPrivate::setBinding(action.property, 0); // Make sure this is disabled during the transition + + QDeclarativePropertyPrivate::write(action.property, action.fromValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } + } + + if (transition) { + QList<QDeclarativeProperty> touched; + d->transition = transition; + d->transition->prepare(applyList, touched, this); + + // Modify the action list to remove actions handled in the transition + for (int ii = 0; ii < applyList.count(); ++ii) { + const QDeclarativeAction &action = applyList.at(ii); + + if (action.event) { + + if (action.actionDone) { + applyList.removeAt(ii); + --ii; + } + + } else { + + if (touched.contains(action.property)) { + if (action.toValue != action.fromValue) + d->completeList << + QDeclarativeSimpleAction(action, QDeclarativeSimpleAction::EndState); + + applyList.removeAt(ii); + --ii; + } + + } + } + } + + // Any actions remaining have not been handled by the transition and should + // be applied immediately. We skip applying bindings, as they are all + // applied at the end in applyBindings() to avoid any nastiness mid + // transition + foreach(const QDeclarativeAction &action, applyList) { + if (action.event && !action.event->changesBindings()) { + if (action.event->isReversable() && action.reverseEvent) + action.event->reverse(); + else + action.event->execute(); + } else if (!action.event && !action.toBinding) { + action.property.write(action.toValue); + } + } +#ifndef QT_NO_DEBUG_STREAM + if (stateChangeDebug()) { + foreach(const QDeclarativeAction &action, applyList) { + if (action.event) + qWarning() << " No transition for event:" << action.event->typeName(); + else + qWarning() << " No transition for:" << action.property.object() + << action.property.name() << "From:" << action.fromValue + << "To:" << action.toValue; + } + } +#endif + if (!transition) + complete(); +} + +void QDeclarativeTransitionManager::cancel() +{ + if (d->transition) { + // ### this could potentially trigger a complete in rare circumstances + d->transition->stop(); + d->transition = 0; + } + + for(int i = 0; i < d->bindingsList.count(); ++i) { + QDeclarativeAction action = d->bindingsList[i]; + if (!action.toBinding.isNull() && action.deletableToBinding) { + QDeclarativePropertyPrivate::setBinding(action.property, 0); + action.toBinding.data()->destroy(); + action.toBinding.clear(); + action.deletableToBinding = false; + } else if (action.event) { + //### what do we do here? + } + + } + d->bindingsList.clear(); + d->completeList.clear(); +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativetransitionmanager_p_p.h b/src/quick/util/qdeclarativetransitionmanager_p_p.h new file mode 100644 index 0000000000..94a2dd1539 --- /dev/null +++ b/src/quick/util/qdeclarativetransitionmanager_p_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETRANSITIONMANAGER_P_H +#define QDECLARATIVETRANSITIONMANAGER_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 "qdeclarativestateoperations_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeStatePrivate; +class QDeclarativeTransitionManagerPrivate; +class Q_AUTOTEST_EXPORT QDeclarativeTransitionManager +{ +public: + QDeclarativeTransitionManager(); + ~QDeclarativeTransitionManager(); + + void transition(const QList<QDeclarativeAction> &, QDeclarativeTransition *transition); + + void cancel(); + +private: + Q_DISABLE_COPY(QDeclarativeTransitionManager) + QDeclarativeTransitionManagerPrivate *d; + + void complete(); + void setState(QDeclarativeState *); + + friend class QDeclarativeState; + friend class QDeclarativeTransitionPrivate; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVETRANSITIONMANAGER_P_H diff --git a/src/quick/util/qdeclarativeutilmodule.cpp b/src/quick/util/qdeclarativeutilmodule.cpp new file mode 100644 index 0000000000..2e82364a42 --- /dev/null +++ b/src/quick/util/qdeclarativeutilmodule.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeutilmodule_p.h" +#include "qdeclarativeanimation_p.h" +#include "qdeclarativeanimation_p_p.h" +#include "qdeclarativebehavior_p.h" +#include "qdeclarativebind_p.h" +#include "qdeclarativeconnections_p.h" +#include "qdeclarativesmoothedanimation_p.h" +#include "qdeclarativefontloader_p.h" +#include "qdeclarativepackage_p.h" +#include "qdeclarativepropertychanges_p.h" +#include "qdeclarativespringanimation_p.h" +#include "qdeclarativestategroup_p.h" +#include "qdeclarativestateoperations_p.h" +#include "qdeclarativestate_p.h" +#include "qdeclarativestate_p_p.h" +#include "qdeclarativesystempalette_p.h" +#include "qdeclarativetimer_p.h" +#include "qdeclarativetransition_p.h" +#include <qdeclarativeinfo.h> +#include <private/qdeclarativetypenotavailable_p.h> +#include <QtCore/qcoreapplication.h> +#include <QtGui/QInputPanel> + +void QDeclarativeUtilModule::defineModule() +{ + qmlRegisterUncreatableType<QInputPanel>("QtQuick",2,0,"InputPanel", QInputPanel::tr("InputPanel is an abstract class")); + qmlRegisterUncreatableType<QDeclarativeAbstractAnimation>("QtQuick",2,0,"Animation",QDeclarativeAbstractAnimation::tr("Animation is an abstract class")); + + qmlRegisterType<QDeclarativeBehavior>("QtQuick",2,0,"Behavior"); + qmlRegisterType<QDeclarativeBind>("QtQuick",2,0,"Binding"); + qmlRegisterType<QDeclarativeColorAnimation>("QtQuick",2,0,"ColorAnimation"); + qmlRegisterType<QDeclarativeConnections>("QtQuick",2,0,"Connections"); + qmlRegisterType<QDeclarativeSmoothedAnimation>("QtQuick",2,0,"SmoothedAnimation"); + qmlRegisterType<QDeclarativeFontLoader>("QtQuick",2,0,"FontLoader"); + qmlRegisterType<QDeclarativeNumberAnimation>("QtQuick",2,0,"NumberAnimation"); + qmlRegisterType<QDeclarativePackage>("QtQuick",2,0,"Package"); + qmlRegisterType<QDeclarativeParallelAnimation>("QtQuick",2,0,"ParallelAnimation"); + qmlRegisterType<QDeclarativePauseAnimation>("QtQuick",2,0,"PauseAnimation"); + qmlRegisterType<QDeclarativePropertyAction>("QtQuick",2,0,"PropertyAction"); + qmlRegisterType<QDeclarativePropertyAnimation>("QtQuick",2,0,"PropertyAnimation"); + qmlRegisterType<QDeclarativeRotationAnimation>("QtQuick",2,0,"RotationAnimation"); + qmlRegisterType<QDeclarativeScriptAction>("QtQuick",2,0,"ScriptAction"); + qmlRegisterType<QDeclarativeSequentialAnimation>("QtQuick",2,0,"SequentialAnimation"); + qmlRegisterType<QDeclarativeSpringAnimation>("QtQuick",2,0,"SpringAnimation"); + qmlRegisterType<QDeclarativeStateChangeScript>("QtQuick",2,0,"StateChangeScript"); + qmlRegisterType<QDeclarativeStateGroup>("QtQuick",2,0,"StateGroup"); + qmlRegisterType<QDeclarativeState>("QtQuick",2,0,"State"); + qmlRegisterType<QDeclarativeSystemPalette>("QtQuick",2,0,"SystemPalette"); + qmlRegisterType<QDeclarativeTimer>("QtQuick",2,0,"Timer"); + qmlRegisterType<QDeclarativeTransition>("QtQuick",2,0,"Transition"); + qmlRegisterType<QDeclarativeVector3dAnimation>("QtQuick",2,0,"Vector3dAnimation"); + + qmlRegisterType<QDeclarativeStateOperation>(); + + qmlRegisterCustomType<QDeclarativePropertyChanges>("QtQuick",2,0,"PropertyChanges", new QDeclarativePropertyChangesParser); + qmlRegisterCustomType<QDeclarativeConnections>("QtQuick",2,0,"Connections", new QDeclarativeConnectionsParser); +} diff --git a/src/quick/util/qdeclarativeutilmodule_p.h b/src/quick/util/qdeclarativeutilmodule_p.h new file mode 100644 index 0000000000..13739c8170 --- /dev/null +++ b/src/quick/util/qdeclarativeutilmodule_p.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEUTILMODULE_H +#define QDECLARATIVEUTILMODULE_H + +#include <qdeclarative.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeUtilModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEUTILMODULE_H diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri new file mode 100644 index 0000000000..9c8964bfef --- /dev/null +++ b/src/quick/util/util.pri @@ -0,0 +1,59 @@ +SOURCES += \ + $$PWD/qdeclarativeutilmodule.cpp\ + $$PWD/qdeclarativeconnections.cpp \ + $$PWD/qdeclarativepackage.cpp \ + $$PWD/qdeclarativeanimation.cpp \ + $$PWD/qdeclarativesystempalette.cpp \ + $$PWD/qdeclarativespringanimation.cpp \ + $$PWD/qdeclarativesmoothedanimation.cpp \ + $$PWD/qdeclarativestate.cpp\ + $$PWD/qdeclarativetransitionmanager.cpp \ + $$PWD/qdeclarativestateoperations.cpp \ + $$PWD/qdeclarativepropertychanges.cpp \ + $$PWD/qdeclarativestategroup.cpp \ + $$PWD/qdeclarativetransition.cpp \ + $$PWD/qdeclarativelistaccessor.cpp \ + $$PWD/qdeclarativetimeline.cpp \ + $$PWD/qdeclarativetimer.cpp \ + $$PWD/qdeclarativebind.cpp \ + $$PWD/qdeclarativepixmapcache.cpp \ + $$PWD/qdeclarativebehavior.cpp \ + $$PWD/qdeclarativefontloader.cpp \ + $$PWD/qdeclarativestyledtext.cpp \ + $$PWD/qdeclarativepath.cpp \ + $$PWD/qdeclarativechangeset.cpp \ + $$PWD/qdeclarativelistcompositor.cpp \ + $$PWD/qdeclarativepathinterpolator.cpp \ + $$PWD/qdeclarativesvgparser.cpp + +HEADERS += \ + $$PWD/qdeclarativeutilmodule_p.h\ + $$PWD/qdeclarativeconnections_p.h \ + $$PWD/qdeclarativepackage_p.h \ + $$PWD/qdeclarativeanimation_p.h \ + $$PWD/qdeclarativeanimation_p_p.h \ + $$PWD/qdeclarativesystempalette_p.h \ + $$PWD/qdeclarativespringanimation_p.h \ + $$PWD/qdeclarativesmoothedanimation_p.h \ + $$PWD/qdeclarativesmoothedanimation_p_p.h \ + $$PWD/qdeclarativestate_p.h\ + $$PWD/qdeclarativestateoperations_p.h \ + $$PWD/qdeclarativepropertychanges_p.h \ + $$PWD/qdeclarativestate_p_p.h\ + $$PWD/qdeclarativetransitionmanager_p_p.h \ + $$PWD/qdeclarativestategroup_p.h \ + $$PWD/qdeclarativetransition_p.h \ + $$PWD/qdeclarativelistaccessor_p.h \ + $$PWD/qdeclarativetimeline_p_p.h \ + $$PWD/qdeclarativetimer_p.h \ + $$PWD/qdeclarativebind_p.h \ + $$PWD/qdeclarativepixmapcache_p.h \ + $$PWD/qdeclarativebehavior_p.h \ + $$PWD/qdeclarativefontloader_p.h \ + $$PWD/qdeclarativestyledtext_p.h \ + $$PWD/qdeclarativepath_p.h \ + $$PWD/qdeclarativepath_p_p.h \ + $$PWD/qdeclarativechangeset_p.h \ + $$PWD/qdeclarativelistcompositor_p.h \ + $$PWD/qdeclarativepathinterpolator_p.h \ + $$PWD/qdeclarativesvgparser_p.h |