--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hbcore/effects/hbeffectscale.cpp Mon Apr 19 14:02:13 2010 +0300
@@ -0,0 +1,477 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (developer.feedback@nokia.com)
+**
+** This file is part of the HbCore module of the UI Extensions for Mobile.
+**
+** 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at developer.feedback@nokia.com.
+**
+****************************************************************************/
+#include "hbeffectscale_p.h"
+#include "hbeffectanimation_p.h"
+#include "hbeffectutils_p.h"
+#include "hbeffectgroup_p.h"
+#include "hbeffectscaleanimation_p.h"
+#include "hbeffectdef_p.h"
+#include <QGraphicsItem>
+#include <QtDebug>
+#include <QVariantAnimation>
+
+HbEffectScaleAnimation::HbEffectScaleAnimation(
+ HbEffectGroup *group,
+ HbEffectScale *effect,
+ int duration ) :
+ HbEffectAnimation(),
+ mGroup(group),
+ mEffect(effect),
+ mCenter(0),
+ mCurrentScaling(1.0)
+{
+ setDuration(duration);
+}
+
+void HbEffectScaleAnimation::setCenter(qreal center)
+{
+ mCenter = center;
+}
+
+void HbEffectScaleAnimation::handleAnimationUpdate(const QVariant &value)
+{
+ if (mGroup->isRunning()) {
+ mCurrentScaling = qVariantValue<qreal>(value);
+ // Prevent negative scale factors, they crash QT.
+ if (mCurrentScaling < 0) {
+ mCurrentScaling = 0;
+ }
+ mGroup->updateItemTransform();
+ }
+}
+
+void HbEffectScaleAnimation::handleAnimationFinish()
+{
+ // This informs the effect group when both scale_x and scale_y have finished
+ mEffect->handleAnimationFinished();
+}
+
+// -----------------------------------------------------------------------------
+
+// HbEffectScale
+
+HbEffectScale::HbEffectScale(
+ const QList<HbEffectFxmlParamData> &data,
+ QGraphicsItem *item,
+ HbEffectGroup *group) :
+ HbEffectAbstract(0, item, group),
+ mAnimationX(0),
+ mAnimationY(0)
+{
+ int durationX = 0;
+ int durationY = 0;
+ QEasingCurve curveX = QEasingCurve::Linear;
+ QEasingCurve curveY = QEasingCurve::Linear;
+
+ // Default values if not defined in the data. Value 1.0f means the item's "identity" i.e. its normal size.
+ mStartXValue = 1.0f;
+ mEndXValue = 1.0f;
+ mStartYValue = 1.0f;
+ mEndYValue = 1.0f;
+ mCenterXValue = 0;
+ mCenterYValue = 0;
+
+ const HbEffectFxmlParamData* paramX = 0;
+ const HbEffectFxmlParamData* paramY = 0;
+
+ Q_FOREACH(const HbEffectFxmlParamData ¶m, data) {
+ if (param.name() == FXML_KEYWORD_SCALE_X) {
+ mKeyFrameListX = param.keyFrames();
+ // <start> for scale_x
+ mStartWidth = param.getAttribute(FXML_KEYWORD_START);
+ mStartWidthRef = param.startRef();
+ // <end> for scale_x
+ mEndWidth = param.getAttribute(FXML_KEYWORD_END);
+ mEndWidthRef = param.endRef();
+ // duration and curvepath
+ HbEffectUtils::resolveFxmlDuration(durationX, param);
+ HbEffectUtils::resolveFxmlCurveShape(curveX, param);
+
+ // Only if "start ref" and "end ref" are not used, read parameter's "ref" attribute
+ if (mStartWidthRef.isEmpty() && mEndWidthRef.isEmpty()) {
+ mParamRefX = param.getAttribute(FXML_PARAM_REF);
+ }
+
+ paramX = ¶m;
+ }
+ else if (param.name() == FXML_KEYWORD_SCALE_Y) {
+ mKeyFrameListY = param.keyFrames();
+ // <start> for scale_y
+ mStartHeight = param.getAttribute(FXML_KEYWORD_START);
+ mStartHeightRef = param.startRef();
+ // <end> for scale_y
+ mEndHeight = param.getAttribute(FXML_KEYWORD_END);
+ mEndHeightRef = param.endRef();
+ HbEffectUtils::resolveFxmlDuration(durationY, param);
+ HbEffectUtils::resolveFxmlCurveShape(curveY, param);
+
+ // Only if "start ref" and "end ref" are not used, read parameter's "ref" attribute
+ if (mStartHeightRef.isEmpty() && mEndHeightRef.isEmpty()) {
+ mParamRefY = param.getAttribute(FXML_PARAM_REF);
+ }
+
+ paramY = ¶m;
+ }
+ else if (param.name() == FXML_KEYWORD_SCALE_ORIGIN_X) {
+ mCenterX = param.getValue();
+ mCenterXRef = param.getAttribute(FXML_PARAM_REF);
+ }
+ else if (param.name() == FXML_KEYWORD_SCALE_ORIGIN_Y) {
+ mCenterY = param.getValue();
+ mCenterYRef = param.getAttribute(FXML_PARAM_REF);
+ }
+ }
+
+ // Validate references. If start and end references are used, at least one of them must be a visual reference.
+ if ((mStartWidthRef.isEmpty() || mStartWidthRef.startsWith(FXML_VISUAL) ||
+ mEndWidthRef.isEmpty() || mEndWidthRef.startsWith(FXML_VISUAL)) &&
+ (mStartHeightRef.isEmpty() || mStartHeightRef.startsWith(FXML_VISUAL) ||
+ mEndHeightRef.isEmpty() || mEndHeightRef.startsWith(FXML_VISUAL))) {
+ // references ok
+ } else {
+ // Invalid references - disable effect
+ durationX = 0;
+ durationY = 0;
+
+ mParamRefX.clear();
+ mStartWidthRef.clear();
+ mStartWidth.clear();
+ mEndWidthRef.clear();
+ mEndWidth.clear();
+
+ mParamRefY.clear();
+ mStartHeightRef.clear();
+ mStartHeight.clear();
+ mEndHeightRef.clear();
+ mEndHeight.clear();
+
+ mCenterX.clear();
+ mCenterXRef.clear();
+ mCenterY.clear();
+ mCenterYRef.clear();
+ }
+
+ if (durationX > 0) {
+ mAnimationX = new HbEffectScaleAnimation(group, this, durationX);
+ mAnimationX->addLooping(paramX, group);
+ mAnimationX->mCurve = curveX;
+ }
+ // Create at least one animation even if both durations are zero for handling animation finish easily.
+ if (durationY > 0 || !mAnimationX) {
+ mAnimationY = new HbEffectScaleAnimation(group, this, durationY);
+ mAnimationY->addLooping(paramY, group);
+ mAnimationY->mCurve = curveY;
+ }
+}
+
+
+/* deletes the effect and rewinds the animation so that next animation can star,
+ rewind is necessary as the general use case is that the effect is restarted before
+ being complete, in which case the rewind would transform the item and prepare for
+ the next effect.
+*/
+HbEffectScale::~HbEffectScale()
+{
+ if (mAnimationX) {
+ mAnimationX->stop();
+ // Applying the cancel policy so that if dynamically new effect is started HbEffect::start then this finishes
+ // and the new one starts.
+ // This will set the endstate for the object correctly when destroying effect
+ delete mAnimationX;
+ }
+ if (mAnimationY) {
+ mAnimationY->stop();
+ delete mAnimationY;
+ }
+}
+
+QString HbEffectScale::name() const
+{
+ return HB_EFFECT_NAME_SCALE;
+}
+
+void HbEffectScale::init()
+{
+ QSizeF orgSize = item()->boundingRect().size();
+
+ // Resolve Dynamic attributes at this point
+ bool valueOk;
+ qreal value;
+
+ const QRectF &extRect = group()->extRect();
+
+ // start width
+ if (!mStartWidth.isEmpty()) {
+ value = HbEffectUtils::resolveFxmlRef(
+ mStartWidthRef, mStartWidth, &valueOk, item(), HbEffectUtils::Size, extRect);
+ if (valueOk) {
+ mStartXValue = value;
+ }
+ }
+ // start height
+ if (!mStartHeight.isEmpty()) {
+ value = HbEffectUtils::resolveFxmlRef(mStartHeightRef, mStartHeight, &valueOk, item(), HbEffectUtils::Size, extRect);
+ if (valueOk) {
+ mStartYValue = value;
+ }
+ }
+
+ // end width
+ if (!mEndWidth.isEmpty()) {
+ value = HbEffectUtils::resolveFxmlRef(
+ mEndWidthRef, mEndWidth, &valueOk, item(), HbEffectUtils::Size, extRect);
+
+ if (valueOk) {
+ mEndXValue = value;
+ }
+ }
+ // end height
+ if (!mEndHeight.isEmpty()) {
+ value = HbEffectUtils::resolveFxmlRef(
+ mEndHeightRef, mEndHeight, &valueOk, item(), HbEffectUtils::Size, extRect);
+
+ if (valueOk) {
+ mEndYValue = value;
+ }
+ }
+
+ // CenterX
+ value = HbEffectUtils::resolveFxmlRef(
+ mCenterXRef, mCenterX, &valueOk, item(), HbEffectUtils::CenterMappedToTargetRect, extRect);
+
+ if (valueOk) {
+ mCenterXValue = value;
+ }
+ // CenterY
+ value = HbEffectUtils::resolveFxmlRef(
+ mCenterYRef, mCenterY, &valueOk, item(), HbEffectUtils::CenterMappedToTargetRect, extRect);
+
+ if (valueOk) {
+ mCenterYValue = value;
+ }
+
+ // QT's scale animations do not handle 0 value correctly so change it here.
+ if (HbEffectUtils::fuzzyIsNull(mStartXValue)) {
+ mStartXValue = 0.00002;
+ }
+ if (HbEffectUtils::fuzzyIsNull(mStartYValue)) {
+ mStartYValue = 0.00002;
+ }
+ if (HbEffectUtils::fuzzyIsNull(mEndXValue)) {
+ mEndXValue = 0.00002;
+ }
+ if (HbEffectUtils::fuzzyIsNull(mEndYValue)) {
+ mEndYValue = 0.00002;
+ }
+
+ if (mAnimationX) {
+ // Start and end values are relative to item's size so value 1 means normal size
+ mAnimationX->setStartValue(QVariant(mStartXValue));
+ mAnimationX->setEndValue(QVariant(mEndXValue));
+ // Center value is in pixels
+ mAnimationX->setCenter(mCenterXValue);
+
+ bool startEndRefUsed = !mStartWidthRef.isEmpty() && !mEndWidthRef.isEmpty();
+
+ qreal paramRefValueX = 0.0;
+
+ // Resolve SCALE_X parameter's "ref" value only if that's needed
+ if (!startEndRefUsed) {
+ paramRefValueX = HbEffectUtils::resolveFxmlRef(
+ mParamRefX, "1", &valueOk, item(), HbEffectUtils::Size, extRect);
+ }
+
+ // Set keyframes in animation
+ foreach(const HbKeyFrame &kf, mKeyFrameListX) {
+
+ // If start and end references are used,
+ // value at given step is (1-c)*startX + c*endX where c is the keyframe coefficient value
+ if (startEndRefUsed) {
+ value = (1 - kf.val) * mStartXValue + kf.val * mEndXValue;
+ }
+ // Otherwise c defines the value
+ else {
+ value = kf.val;
+ // Multiply keyframe's value with parameter's "ref" if it is defined
+ if (valueOk) {
+ value *= paramRefValueX;
+ }
+ }
+
+ mAnimationX->setKeyValueAt(kf.pos, QVariant(value));
+
+ // Update also stored start and end values if keyframe refers to those
+ if (HbEffectUtils::fuzzyIsNull(kf.pos)) {
+ mStartXValue = value;
+ } else if (HbEffectUtils::fuzzyIsOneOrGreater(kf.pos)) {
+ mEndXValue = value;
+ }
+ }
+ }
+ if (mAnimationY) {
+ // Start and end values are relative to item's size so value 1 means normal size
+ mAnimationY->setStartValue(QVariant(mStartYValue));
+ mAnimationY->setEndValue(QVariant(mEndYValue));
+ // Center value is in pixels
+ mAnimationY->setCenter(mCenterYValue);
+
+ bool startEndRefUsed = !mStartHeightRef.isEmpty() && !mEndHeightRef.isEmpty();
+
+ qreal paramRefValueY = 0.0;
+ // Resolve SCALE_Y parameter's "ref" value only if that's needed
+ if (!startEndRefUsed) {
+ paramRefValueY = HbEffectUtils::resolveFxmlRef(
+ mParamRefY, "1", &valueOk, item(), HbEffectUtils::Size, extRect);
+ }
+
+ // Set keyframes in animation
+ foreach(const HbKeyFrame &kf, mKeyFrameListY) {
+ // If start and end references are used,
+ // value at given step is (1-c)*startY + c*endY where c is the keyframe coefficient value
+ if (startEndRefUsed) {
+ value = (1 - kf.val) * mStartYValue + kf.val * mEndYValue;
+ }
+ // Otherwise c defines the value
+ else {
+ value = kf.val;
+ // Multiply keyframe's value with parameter's "ref" if it is defined
+ if (valueOk) {
+ value *= paramRefValueY;
+ }
+ }
+
+ mAnimationY->setKeyValueAt(kf.pos, QVariant(value));
+
+ // Update also stored start and end values if keyframe refers to those
+ if (HbEffectUtils::fuzzyIsNull(kf.pos)) {
+ mStartYValue = value;
+ } else if (HbEffectUtils::fuzzyIsOneOrGreater(kf.pos)) {
+ mEndYValue = value;
+ }
+ }
+ }
+}
+
+void HbEffectScale::setStartState(QTransform &transform)
+{
+ // Set the start state
+ updateItemTransform(transform);
+}
+
+/* Starts the scale effect animation. If the effect is already started,
+the result is undefined.
+*/
+void HbEffectScale::start()
+{
+ if (mAnimationX) {
+ mAnimationX->stop(); // rewind the animation before starting again
+ mAnimationX->setCurrentTime(0);
+ mAnimationX->mFinished = false;
+ mAnimationX->start();
+ }
+ if (mAnimationY) {
+ mAnimationY->stop(); // rewind the animation before starting again
+ mAnimationY->setCurrentTime(0);
+ mAnimationY->mFinished = false;
+ mAnimationY->start();
+ }
+}
+
+/* Cancels the effect animation and sets the animation end state immediately.
+*/
+void HbEffectScale::cancel(QTransform &transform, bool itemIsValid)
+{
+ Q_UNUSED(itemIsValid)
+
+ if (mAnimationX) {
+ mAnimationX->stop();
+ }
+ if (mAnimationY) {
+ mAnimationY->stop();
+ }
+
+ // This will set the endstate for the object correctly when cancelling effect
+ QTransform newTransform;
+ newTransform.translate(mCenterXValue, mCenterYValue);
+ newTransform.scale(mEndXValue, mEndYValue);
+ newTransform.translate(-mCenterXValue, -mCenterYValue);
+ // Combine the new transform with the existing one
+ transform *= newTransform;
+}
+
+void HbEffectScale::updateItemTransform(QTransform &transform)
+{
+ QTransform newTransform;
+ // Handle centering for scaling the matrix
+ newTransform.translate(mCenterXValue, mCenterYValue);
+
+ // Get the current scaling factor from animation or use 1.0 if animation does not exist
+ qreal currentScalingX = mAnimationX ? mAnimationX->mCurrentScaling : 1.0;
+ qreal currentScalingY = mAnimationY ? mAnimationY->mCurrentScaling : 1.0;
+
+ // Scale the transformation matrix to reach the new scaling factor.
+ newTransform.scale(currentScalingX, currentScalingY);
+ // Centering must be canceled after the scaling has been done in the matrix
+ newTransform.translate(-mCenterXValue, -mCenterYValue);
+ // Combine the new transform with the existing one
+ transform *= newTransform;
+}
+
+void HbEffectScale::handleAnimationFinished()
+{
+ bool allFinished = true;
+
+ if (mAnimationX && !mAnimationX->mFinished) {
+ allFinished = false;
+ }
+ if (mAnimationY && !mAnimationY->mFinished) {
+ allFinished = false;
+ }
+
+ // Inform the effect group that the whole scale effect has finished (both scaleX and scaleY)
+ if (allFinished) {
+ group()->effectFinished();
+ }
+}
+
+void HbEffectScale::pause()
+{
+ if (mAnimationX) {
+ mAnimationX->pause();
+ }
+ if (mAnimationY) {
+ mAnimationY->pause();
+ }
+}
+
+void HbEffectScale::resume()
+{
+ if (mAnimationX) {
+ mAnimationX->resume();
+ }
+ if (mAnimationY) {
+ mAnimationY->resume();
+ }
+}