From 0521b7e1effdad551f5c1d31495d44b4c13cff92 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 19 Sep 2017 14:10:43 +0200 Subject: [PATCH] QmlDesigner: Implement model support for timeline This patch implements the basic support for the timelien in the model. The type names will most likely change. Change-Id: Ib3161d480468cf88e9f155130f7cba70451a8c7e Reviewed-by: Tim Jenssen --- .../designercore/designercore-lib.pri | 8 +- .../designercore/include/abstractview.h | 7 + .../designercore/include/qmlobjectnode.h | 3 + .../designercore/include/qmlstate.h | 1 - .../include/qmltimelinekeyframes.h | 65 +++++++ .../designercore/include/qmltimelinemutator.h | 62 +++++++ .../instances/nodeinstanceview.cpp | 17 ++ .../designercore/model/abstractview.cpp | 31 ++++ .../qmldesigner/designercore/model/model.cpp | 33 ++++ .../qmldesigner/designercore/model/model_p.h | 3 + .../designercore/model/qmlobjectnode.cpp | 45 +++++ .../model/qmltimelinekeyframes.cpp | 147 +++++++++++++++ .../designercore/model/qmltimelinemutator.cpp | 167 ++++++++++++++++++ src/plugins/qmldesigner/qmldesignerplugin.qbs | 4 + 14 files changed, 590 insertions(+), 3 deletions(-) create mode 100644 src/plugins/qmldesigner/designercore/include/qmltimelinekeyframes.h create mode 100644 src/plugins/qmldesigner/designercore/include/qmltimelinemutator.h create mode 100644 src/plugins/qmldesigner/designercore/model/qmltimelinekeyframes.cpp create mode 100644 src/plugins/qmldesigner/designercore/model/qmltimelinemutator.cpp diff --git a/src/plugins/qmldesigner/designercore/designercore-lib.pri b/src/plugins/qmldesigner/designercore/designercore-lib.pri index 2acf4922057..b7f36ac47c9 100644 --- a/src/plugins/qmldesigner/designercore/designercore-lib.pri +++ b/src/plugins/qmldesigner/designercore/designercore-lib.pri @@ -78,7 +78,9 @@ SOURCES += $$PWD/model/abstractview.cpp \ $$PWD/model/signalhandlerproperty.cpp \ $$PWD/model/internalsignalhandlerproperty.cpp \ $$PWD/model/anchorline.cpp \ - $$PWD/instances/puppetdialog.cpp + $$PWD/instances/puppetdialog.cpp \ + $$PWD/model/qmltimelinemutator.cpp \ + $$PWD/model/qmltimelinekeyframes.cpp HEADERS += $$PWD/include/qmldesignercorelib_global.h \ $$PWD/include/abstractview.h \ @@ -152,7 +154,9 @@ HEADERS += $$PWD/include/qmldesignercorelib_global.h \ $$PWD/include/signalhandlerproperty.h \ $$PWD/model/internalsignalhandlerproperty.h \ $$PWD/include/anchorline.h \ - $$PWD/instances/puppetdialog.h + $$PWD/instances/puppetdialog.h \ + $$PWD/include/qmltimelinemutator.h \ + $$PWD/include/qmltimelinekeyframes.h \ FORMS += \ $$PWD/instances/puppetdialog.ui diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 6987d747df4..8b085f91371 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -55,6 +55,7 @@ namespace QmlDesigner { class NodeInstanceView; class RewriterView; class QmlModelState; +class QmlTimelineMutator; enum DesignerWidgetFlags { DisableOnError, @@ -232,6 +233,8 @@ public: virtual void documentMessagesChanged(const QList &errors, const QList &warnings); + virtual void currentTimelineChanged(const ModelNode &node); + void changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion); NodeInstanceView *nodeInstanceView() const; @@ -240,6 +243,7 @@ public: void setCurrentStateNode(const ModelNode &node); ModelNode currentStateNode() const; QmlModelState currentState() const; + QmlTimelineMutator currentTimeline() const; int majorQtQuickVersion() const; int minorQtQuickVersion() const; @@ -253,6 +257,9 @@ public: virtual QString contextHelpId() const; + void activateTimelineRecording(const ModelNode &mutator); + void deactivateTimelineRecording(); + protected: void setModel(Model * model); void removeModel(); diff --git a/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h b/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h index 3e9d5ff7e4e..e5d58910767 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h @@ -28,6 +28,7 @@ #include #include "qmlmodelnodefacade.h" #include "qmlstate.h" +#include "qmltimelinemutator.h" #include "qmlchangeset.h" #include @@ -63,6 +64,7 @@ public: QString validId(); QmlModelState currentState() const; + QmlTimelineMutator currentTimeline() const; void setVariantProperty(const PropertyName &name, const QVariant &value); void setBindingProperty(const PropertyName &name, const QString &expression); NodeAbstractProperty nodeAbstractProperty(const PropertyName &name) const; @@ -83,6 +85,7 @@ public: QString stripedTranslatableText(const PropertyName &name) const; QString expression(const PropertyName &name) const; bool isInBaseState() const; + bool timelineIsActive() const; QmlPropertyChanges propertyChangeForCurrentState() const; virtual bool instanceCanReparent() const; diff --git a/src/plugins/qmldesigner/designercore/include/qmlstate.h b/src/plugins/qmldesigner/designercore/include/qmlstate.h index 4ee216f84f2..ac6dd39b577 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlstate.h +++ b/src/plugins/qmldesigner/designercore/include/qmlstate.h @@ -72,7 +72,6 @@ public: protected: void addChangeSetIfNotExists(const ModelNode &node); static QmlModelState createBaseState(const AbstractView *view); - }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframes.h b/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframes.h new file mode 100644 index 00000000000..2969d4dd9a9 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframes.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include "qmlmodelnodefacade.h" +#include "qmlchangeset.h" + +namespace QmlDesigner { + +class AbstractViewAbstractVieweGroup; +class QmlObjectNode; + +class QMLDESIGNERCORE_EXPORT QmlTimelineFrames : public QmlModelNodeFacade +{ + +public: + QmlTimelineFrames(); + QmlTimelineFrames(const ModelNode &modelNode); + + bool isValid() const; + static bool isValidQmlTimelineFrames(const ModelNode &modelNode); + void destroy(); + + ModelNode target() const; + void setTarget(const ModelNode &target); + + PropertyName propertyName() const; + void setPropertyName(const PropertyName &propertyName); + + void setValue(const QVariant &value, qreal frame); + QVariant value(qreal frame) const; + + qreal currentFrame() const; + + bool hasKeyframe(qreal frame); + + static bool isValidKeyframe(const ModelNode &node); + static QmlTimelineFrames keyframesForKeyframe(const ModelNode &node); +}; + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/qmltimelinemutator.h b/src/plugins/qmldesigner/designercore/include/qmltimelinemutator.h new file mode 100644 index 00000000000..44327fcb25c --- /dev/null +++ b/src/plugins/qmldesigner/designercore/include/qmltimelinemutator.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include "qmlmodelnodefacade.h" + +namespace QmlDesigner { + +class AbstractViewAbstractVieweGroup; +class QmlObjectNode; +class QmlModelStateGroup; +class QmlTimelineFrames; + +class QMLDESIGNERCORE_EXPORT QmlTimelineMutator : public QmlModelNodeFacade +{ + +public: + QmlTimelineMutator(); + QmlTimelineMutator(const ModelNode &modelNode); + + bool isValid() const; + static bool isValidQmlTimelineMutator(const ModelNode &modelNode); + void destroy(); + + QmlTimelineFrames timelineFrames(const ModelNode &modelNode, const PropertyName &propertyName); + bool hasTimeline(const ModelNode &modelNode, const PropertyName &propertyName); + + qreal startFrame() const; + qreal endFrame() const; + qreal currentFrame() const; + +private: + void addFramesIfNotExists(const ModelNode &node, const PropertyName &propertyName); + bool hasFrames(const ModelNode &node, const PropertyName &propertyName) const; + QList frames() const; +}; + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index caad75eaea1..bbb34e906a6 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -43,6 +43,8 @@ #include "nodelistproperty.h" #include "nodeproperty.h" #include "qmlchangeset.h" +#include "qmltimelinemutator.h" +#include "qmltimelinekeyframes.h" #include "createscenecommand.h" #include "createinstancescommand.h" @@ -690,6 +692,21 @@ void NodeInstanceView::updatePosition(const QList &propertyList NodeInstance instance = instanceForModelNode(modelNode); setYValue(instance, variantProperty, informationChangeHash); } + } else if (currentTimeline().isValid() + && variantProperty.name() == "value" + && QmlTimelineFrames::isValidKeyframe(variantProperty.parentModelNode())) { + + QmlTimelineFrames frames = QmlTimelineFrames::keyframesForKeyframe(variantProperty.parentModelNode()); + + if (frames.isValid() && frames.propertyName() == "x" && frames.target().isValid()) { + + NodeInstance instance = instanceForModelNode(frames.target()); + setXValue(instance, variantProperty, informationChangeHash); + } else if (frames.isValid() && frames.propertyName() == "y" && frames.target().isValid()) { + NodeInstance instance = instanceForModelNode(frames.target()); + setYValue(instance, variantProperty, informationChangeHash); + } + } } diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index f8092b9c7fa..57ce5f8ddce 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -30,6 +30,7 @@ #include "internalnode_p.h" #include "nodeinstanceview.h" #include +#include #ifndef QMLDESIGNER_TEST #include @@ -359,6 +360,11 @@ void AbstractView::documentMessagesChanged(const QList &/*error { } +void AbstractView::currentTimelineChanged(const ModelNode & /*node*/) +{ + +} + QList AbstractView::toModelNodeList(const QList &nodeList) const { return QmlDesigner::toModelNodeList(nodeList, const_cast(this)); @@ -565,6 +571,21 @@ QString AbstractView::contextHelpId() const return helpId; } +void AbstractView::activateTimelineRecording(const ModelNode &mutator) +{ + Internal::WriteLocker locker(m_model.data()); + if (model()) + model()->d->notifyCurrentTimelineChanged(mutator); + +} + +void AbstractView::deactivateTimelineRecording() +{ + Internal::WriteLocker locker(m_model.data()); + if (model()) + model()->d->notifyCurrentTimelineChanged(ModelNode()); +} + QList AbstractView::allModelNodes() const { return toModelNodeList(model()->d->allNodes()); @@ -688,6 +709,16 @@ QmlModelState AbstractView::currentState() const return QmlModelState(currentStateNode()); } +QmlTimelineMutator AbstractView::currentTimeline() const +{ + if (model()) + return QmlTimelineMutator(ModelNode(m_model.data()->d->currentTimelineNode(), + m_model.data(), + const_cast(this))); + + return QmlTimelineMutator(); +} + static int getMinorVersionFromImport(const Model *model) { foreach (const Import &import, model->imports()) { diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 011a5271d3d..561910006ca 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -85,6 +85,7 @@ ModelPrivate::ModelPrivate(Model *model) : { m_rootInternalNode = createNode("QtQuick.Item", 1, 0, PropertyListType(), PropertyListType(), QString(), ModelNode::NodeWithoutSource,true); m_currentStateNode = m_rootInternalNode; + m_currentTimelineMutatorNode = m_rootInternalNode; } ModelPrivate::~ModelPrivate() @@ -641,6 +642,33 @@ void ModelPrivate::notifyCurrentStateChanged(const ModelNode &node) resetModelByRewriter(description); } +void ModelPrivate::notifyCurrentTimelineChanged(const ModelNode &node) +{ + bool resetModel = false; + QString description; + + m_currentTimelineMutatorNode = node.internalNode(); + + try { + if (rewriterView()) + rewriterView()->currentTimelineChanged(ModelNode(node.internalNode(), model(), rewriterView())); + } catch (const RewritingException &e) { + description = e.description(); + resetModel = true; + } + + for (const QPointer &view : m_viewList) { + Q_ASSERT(view != 0); + view->currentTimelineChanged(ModelNode(node.internalNode(), model(), view.data())); + } + + if (nodeInstanceView()) + nodeInstanceView()->currentTimelineChanged(ModelNode(node.internalNode(), model(), nodeInstanceView())); + + if (resetModel) + resetModelByRewriter(description); +} + void ModelPrivate::notifyRewriterBeginTransaction() { bool resetModel = false; @@ -1707,6 +1735,11 @@ NodeInstanceView *ModelPrivate::nodeInstanceView() const return m_nodeInstanceView.data(); } +InternalNodePointer ModelPrivate::currentTimelineNode() const +{ + return m_currentTimelineMutatorNode; +} + InternalNodePointer ModelPrivate::nodeForId(const QString &id) const { return m_idNodeHash.value(id); diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 882ea0599d9..f67e59aed1b 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -157,6 +157,7 @@ public: void notifyInstanceToken(const QString &token, int number, const QVector &nodeVector); void notifyCurrentStateChanged(const ModelNode &node); + void notifyCurrentTimelineChanged(const ModelNode &node); void setDocumentMessages(const QList &errors, const QList &warnings); @@ -226,6 +227,7 @@ public: NodeInstanceView *nodeInstanceView() const; InternalNodePointer currentStateNode() const; + InternalNodePointer currentTimelineNode() const; private: //functions void removePropertyWithoutNotification(const InternalPropertyPointer &property); @@ -249,6 +251,7 @@ private: QSet m_nodeSet; InternalNodePointer m_currentStateNode; InternalNodePointer m_rootInternalNode; + InternalNodePointer m_currentTimelineMutatorNode; QUrl m_fileUrl; QPointer m_rewriterView; QPointer m_nodeInstanceView; diff --git a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp index 4c58c15dc1f..218601d4099 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp @@ -26,6 +26,7 @@ #include "qmlobjectnode.h" #include "qmlitemnode.h" #include "qmlstate.h" +#include "qmltimelinekeyframes.h" #include "variantproperty.h" #include "nodeproperty.h" #include @@ -48,6 +49,19 @@ void QmlObjectNode::setVariantProperty(const PropertyName &name, const QVariant if (!isValid()) throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + if (timelineIsActive()) { + modelNode().validId(); + + QmlTimelineFrames timelineFrames(currentTimeline().timelineFrames(modelNode(), name)); + + Q_ASSERT(timelineFrames.isValid()); + + qreal frame = currentTimeline().modelNode().auxiliaryData("currentFrame@NodeInstance").toReal(); + timelineFrames.setValue(value, frame); + + return; + } + if (isInBaseState()) { modelNode().variantProperty(name).setValue(value); //basestate } else { @@ -83,6 +97,14 @@ QmlModelState QmlObjectNode::currentState() const return QmlModelState(); } +QmlTimelineMutator QmlObjectNode::currentTimeline() const +{ + if (isValid()) + return view()->currentTimeline(); + else + return QmlTimelineMutator(); +} + bool QmlObjectNode::isRootModelNode() const { return modelNode().isRootNode(); @@ -161,6 +183,9 @@ bool QmlObjectNode::propertyAffectedByCurrentState(const PropertyName &name) con if (currentState().isBaseState()) return modelNode().hasProperty(name); + if (timelineIsActive() && currentTimeline().hasTimeline(modelNode(), name)) + return true; + if (!currentState().hasPropertyChanges(modelNode())) return false; @@ -172,6 +197,21 @@ QVariant QmlObjectNode::modelValue(const PropertyName &name) const if (!isValid()) throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + if (timelineIsActive() && currentTimeline().hasTimeline(modelNode(), name)) { + QmlTimelineFrames timelineFrames(currentTimeline().timelineFrames(modelNode(), name)); + + Q_ASSERT(timelineFrames.isValid()); + + qreal frame = currentTimeline().modelNode().auxiliaryData("currentFrame@NodeInstance").toReal(); + + QVariant value = timelineFrames.value(frame); + + if (!value.isValid()) //interpolation is not done in the model + value = instanceValue(name); + + return value; + } + if (currentState().isBaseState()) return modelNode().variantProperty(name).value(); @@ -239,6 +279,11 @@ bool QmlObjectNode::isInBaseState() const return currentState().isBaseState(); } +bool QmlObjectNode::timelineIsActive() const +{ + return currentTimeline().isValid(); +} + bool QmlObjectNode::instanceCanReparent() const { return isInBaseState(); diff --git a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframes.cpp b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframes.cpp new file mode 100644 index 00000000000..959f2ea3f66 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframes.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "qmltimelinekeyframes.h" +#include "abstractview.h" +#include +#include +#include +#include +#include "bindingproperty.h" +#include "qmlitemnode.h" + +#include + +namespace QmlDesigner { + +QmlTimelineFrames::QmlTimelineFrames() +{ + +} + +QmlTimelineFrames::QmlTimelineFrames(const ModelNode &modelNode) : QmlModelNodeFacade(modelNode) +{ + +} + +bool QmlTimelineFrames::isValid() const +{ + return isValidQmlTimelineFrames(modelNode()); +} + +bool QmlTimelineFrames::isValidQmlTimelineFrames(const ModelNode &modelNode) +{ + return isValidQmlModelNodeFacade(modelNode) + && modelNode.metaInfo().isValid() + && modelNode.metaInfo().isSubclassOf("QtQuick.Timeline.Keyframes"); +} + +void QmlTimelineFrames::destroy() +{ + Q_ASSERT(isValid()); + modelNode().destroy(); +} + +ModelNode QmlTimelineFrames::target() const +{ + if (modelNode().property("target").isBindingProperty()) + return modelNode().bindingProperty("target").resolveToModelNode(); + else + return ModelNode(); //exception? +} + +void QmlTimelineFrames::setTarget(const ModelNode &target) +{ + modelNode().bindingProperty("target").setExpression(target.id()); +} + + +PropertyName QmlTimelineFrames::propertyName() const +{ + return modelNode().variantProperty("property").value().toString().toUtf8(); +} + +void QmlTimelineFrames::setPropertyName(const PropertyName &propertyName) +{ + modelNode().variantProperty("property").setValue(QString::fromUtf8(propertyName)); +} + +void QmlTimelineFrames::setValue(const QVariant &value, qreal currentFrame) +{ + + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + if (qFuzzyCompare(childNode.variantProperty("frame").value().toReal(), currentFrame)) { + childNode.variantProperty("value").setValue(value); + return; + } + } + + const QList > propertyPairList{{PropertyName("frame"), QVariant(currentFrame)}, + {PropertyName("value"), value}}; + + ModelNode frame = modelNode().view()->createModelNode("QtQuick.Timeline.Keyframe", 1, 0, propertyPairList); + modelNode().defaultNodeListProperty().reparentHere(frame); +} + +QVariant QmlTimelineFrames::value(qreal frame) const +{ + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + if (qFuzzyCompare(childNode.variantProperty("frame").value().toReal(), frame)) { + return childNode.variantProperty("value").value(); + } + } + + return QVariant(); +} + +bool QmlTimelineFrames::hasKeyframe(qreal frame) +{ + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + if (qFuzzyCompare(childNode.variantProperty("frame").value().toReal(), frame)) + return true; + } + + return false; +} + +bool QmlTimelineFrames::isValidKeyframe(const ModelNode &node) +{ + return isValidQmlModelNodeFacade(node) + && node.metaInfo().isValid() + && node.metaInfo().isSubclassOf("QtQuick.Timeline.Keyframe"); +} + +QmlTimelineFrames QmlTimelineFrames::keyframesForKeyframe(const ModelNode &node) +{ + if (isValidKeyframe(node) && node.hasParentProperty()) { + const QmlTimelineFrames timeline(node.parentProperty().parentModelNode()); + if (timeline.isValid()) + return timeline; + } + + return QmlTimelineFrames(); +} + +} // QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/qmltimelinemutator.cpp b/src/plugins/qmldesigner/designercore/model/qmltimelinemutator.cpp new file mode 100644 index 00000000000..7e22a615650 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/qmltimelinemutator.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "qmltimelinemutator.h" +#include "qmltimelinekeyframes.h" +#include "abstractview.h" +#include +#include +#include +#include +#include +#include "bindingproperty.h" +#include "qmlitemnode.h" + +#include + +namespace QmlDesigner { + +QmlTimelineMutator::QmlTimelineMutator() +{ + +} + +QmlTimelineMutator::QmlTimelineMutator(const ModelNode &modelNode) : QmlModelNodeFacade(modelNode) +{ + +} + +bool QmlTimelineMutator::isValid() const +{ + return isValidQmlTimelineMutator(modelNode()); +} + +bool QmlTimelineMutator::isValidQmlTimelineMutator(const ModelNode &modelNode) +{ + return isValidQmlModelNodeFacade(modelNode) + && modelNode.metaInfo().isValid() + && modelNode.metaInfo().isSubclassOf("QtQuick.Timeline.KeyframeMutator"); +} + +void QmlTimelineMutator::destroy() +{ + Q_ASSERT(isValid()); + modelNode().destroy(); +} + +QmlTimelineFrames QmlTimelineMutator::timelineFrames(const ModelNode &node, const PropertyName &propertyName) +{ + if (isValid()) { + addFramesIfNotExists(node, propertyName); + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + if (QmlTimelineFrames::isValidQmlTimelineFrames(childNode)) { + const QmlTimelineFrames frames(childNode); + + if (frames.target().isValid() + && frames.target() == node + && frames.propertyName() == propertyName) + return frames; + } + } + } + + return QmlTimelineFrames(); //not found +} + +bool QmlTimelineMutator::hasTimeline(const ModelNode &node, const PropertyName &propertyName) +{ + if (isValid()) { + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + if (QmlTimelineFrames::isValidQmlTimelineFrames(childNode)) { + const QmlTimelineFrames frames(childNode); + + if (frames.target().isValid() + && frames.target() == node + && frames.propertyName() == propertyName) + return true; + } + } + } + return false; +} + +qreal QmlTimelineMutator::startFrame() const +{ + if (isValid()) + return QmlObjectNode(modelNode()).instanceValue("startFrame").toReal(); + return 0; +} + +qreal QmlTimelineMutator::endFrame() const +{ + if (isValid()) + return QmlObjectNode(modelNode()).instanceValue("endFrame").toReal(); + return 0; +} + +qreal QmlTimelineMutator::currentFrame() const +{ + if (isValid()) + return QmlObjectNode(modelNode()).instanceValue("currentFrame").toReal(); + return 0; +} + +void QmlTimelineMutator::addFramesIfNotExists(const ModelNode &node, const PropertyName &propertyName) +{ + if (!isValid()) + throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + if (!hasFrames(node, propertyName)) { + ModelNode frames = modelNode().view()->createModelNode("QtQuick.Timeline.Keyframes", 1, 0); + modelNode().defaultNodeListProperty().reparentHere(frames); + + QmlTimelineFrames(frames).setTarget(node); + QmlTimelineFrames(frames).setPropertyName(propertyName); + + Q_ASSERT(QmlTimelineFrames::isValidQmlTimelineFrames(frames)); + } +} + + +bool QmlTimelineMutator::hasFrames(const ModelNode &node, const PropertyName &propertyName) const +{ + for (const QmlTimelineFrames &frames : frames()) { + if (frames.target().isValid() + && frames.target() == node + && frames.propertyName() == propertyName) + return true; + } + + return false; +} + +QList QmlTimelineMutator::frames() const +{ + QList returnList; + + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + if (QmlTimelineFrames::isValidQmlTimelineFrames(childNode)) + returnList.append(QmlTimelineFrames(childNode)); + } + + return returnList; +} + +} // QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 6e88e6f101e..50ad382b1ab 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -280,6 +280,8 @@ Project { "include/subcomponentmanager.h", "include/textmodifier.h", "include/variantproperty.h", + "include/qmltimelinekeyframes.h", + "include/qmltimelinemutator.h", "instances/nodeinstance.cpp", "instances/nodeinstanceserverproxy.cpp", "instances/nodeinstanceserverproxy.h", @@ -357,6 +359,8 @@ Project { "model/texttomodelmerger.h", "model/variantproperty.cpp", "model/viewmanager.cpp", + "model/qmltimelinekeyframes.cpp", + "model/qmltimelinemutator.cpp", "pluginmanager/widgetpluginmanager.cpp", "pluginmanager/widgetpluginmanager.h", "pluginmanager/widgetpluginpath.cpp",