diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 38d4345bf0a..6a8ac1924ca 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -649,8 +649,6 @@ extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/timelineeditor SOURCES - animationcurvedialog.cpp animationcurvedialog.h - animationcurveeditormodel.cpp animationcurveeditormodel.h canvas.cpp canvas.h canvasstyledialog.cpp canvasstyledialog.h easingcurve.cpp easingcurve.h @@ -707,6 +705,7 @@ extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/curveeditor SOURCES curveeditor.qrc + curveeditorview.cpp curveeditorview.h animationcurve.cpp animationcurve.h curveeditor.cpp curveeditor.h curveeditormodel.cpp curveeditormodel.h diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp index 33618dcc8d1..2db3509b184 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp @@ -62,6 +62,11 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent) &GraphicsView::updateSelection); } +bool CurveEditor::dragging() const +{ + return m_view->dragging(); +} + void CurveEditor::zoomX(double zoom) { m_view->setZoomX(zoom); @@ -82,18 +87,21 @@ QToolBar *CurveEditor::createToolBar(CurveEditorModel *model) auto *bar = new QToolBar; bar->setFloatable(false); - QAction *tangentLinearAction = bar->addAction(QIcon(":/curveeditor/images/tangetToolsLinearIcon.png"), "Linear"); - QAction *tangentStepAction = bar->addAction(QIcon(":/curveeditor/images/tangetToolsStepIcon.png"), "Step"); - QAction *tangentSplineAction = bar->addAction(QIcon(":/curveeditor/images/tangetToolsSplineIcon.png"), "Spline"); - QAction *tangentDefaultAction = bar->addAction("Set Default"); - QAction *tangentUnifyAction = bar->addAction("Unify"); + QAction *tangentLinearAction = bar->addAction( + QIcon(":/curveeditor/images/tangetToolsLinearIcon.png"), "Linear"); + QAction *tangentStepAction = bar->addAction(QIcon( + ":/curveeditor/images/tangetToolsStepIcon.png"), + "Step"); + QAction *tangentSplineAction = bar->addAction( + QIcon(":/curveeditor/images/tangetToolsSplineIcon.png"), "Spline"); + + QAction *tangentDefaultAction = bar->addAction(tr("Set Default")); + QAction *tangentUnifyAction = bar->addAction(tr("Unify")); auto setLinearInterpolation = [this]() { m_view->setInterpolation(Keyframe::Interpolation::Linear); }; - auto setStepInterpolation = [this]() { - m_view->setInterpolation(Keyframe::Interpolation::Step); - }; + auto setStepInterpolation = [this]() { m_view->setInterpolation(Keyframe::Interpolation::Step); }; auto setSplineInterpolation = [this]() { m_view->setInterpolation(Keyframe::Interpolation::Bezier); }; @@ -118,7 +126,7 @@ QToolBar *CurveEditor::createToolBar(CurveEditorModel *model) startSpin->setValue(model->minimumTime()); auto updateStartFrame = [this, model](int frame) { - model->setMinimumTime(frame, false); + model->setMinimumTime(frame); m_view->viewport()->update(); }; connect(startSpin, QOverload::of(&QSpinBox::valueChanged), updateStartFrame); @@ -127,16 +135,16 @@ QToolBar *CurveEditor::createToolBar(CurveEditorModel *model) endSpin->setValue(model->maximumTime()); auto updateEndFrame = [this, model](int frame) { - model->setMaximumTime(frame, false); + model->setMaximumTime(frame); m_view->viewport()->update(); }; connect(endSpin, QOverload::of(&QSpinBox::valueChanged), updateEndFrame); auto setStartSlot = [startSpin](int frame) { startSpin->setValue(frame); }; - connect(model, &CurveEditorModel::updateStartFrame, setStartSlot); + connect(model, &CurveEditorModel::commitStartFrame, setStartSlot); auto setEndSlot = [endSpin](int frame) { endSpin->setValue(frame); }; - connect(model, &CurveEditorModel::updateEndFrame, setEndSlot); + connect(model, &CurveEditorModel::commitEndFrame, setEndSlot); durationBox->addWidget(new QLabel(tr("Start Frame"))); durationBox->addWidget(startSpin); @@ -152,10 +160,14 @@ QToolBar *CurveEditor::createToolBar(CurveEditorModel *model) cfspin->setMaximum(std::numeric_limits::max()); auto intSignal = static_cast(&QSpinBox::valueChanged); - connect(cfspin, intSignal, [this](int val) { m_view->setCurrentFrame(val, false); }); - connect(m_view, &GraphicsView::notifyFrameChanged, [cfspin](int val) { - QSignalBlocker blocker(cfspin); - cfspin->setValue(val); + connect(cfspin, intSignal, [this, model](int val) { model->commitCurrentFrame(val); }); + connect(m_view, &GraphicsView::currentFrameChanged, [cfspin](int val, bool notify) { + if (notify) { + cfspin->setValue(val); + } else { + const QSignalBlocker blocker(cfspin); + cfspin->setValue(val); + } }); auto *positionBox = new QHBoxLayout; diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.h b/src/plugins/qmldesigner/components/curveeditor/curveeditor.h index b6d9cfc0138..86f6c592578 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.h +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.h @@ -41,6 +41,8 @@ class CurveEditor : public QWidget public: CurveEditor(CurveEditorModel *model, QWidget *parent = nullptr); + bool dragging() const; + void zoomX(double zoom); void zoomY(double zoom); diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.pri b/src/plugins/qmldesigner/components/curveeditor/curveeditor.pri index d42544f3fad..2cef92b01b1 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.pri +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.pri @@ -4,6 +4,7 @@ HEADERS += \ $$PWD/animationcurve.h \ $$PWD/curveeditor.h \ $$PWD/curveeditormodel.h \ + $$PWD/curveeditorview.h \ $$PWD/curvesegment.h \ $$PWD/detail/colorcontrol.h \ $$PWD/detail/curveeditorstyledialog.h \ @@ -28,6 +29,7 @@ SOURCES += \ $$PWD/curvesegment.cpp \ $$PWD/curveeditor.cpp \ $$PWD/curveeditormodel.cpp \ + $$PWD/curveeditorview.cpp \ $$PWD/detail/colorcontrol.cpp \ $$PWD/detail/curveeditorstyledialog.cpp \ $$PWD/detail/curveitem.cpp \ diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.cpp index 5c2796016df..db58ce4dbb2 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.cpp @@ -24,11 +24,19 @@ ****************************************************************************/ #include "curveeditormodel.h" +#include "curveeditorstyle.h" #include "treeitem.h" #include "detail/graphicsview.h" #include "detail/selectionmodel.h" +#include "easingcurve.h" +#include "qmltimeline.h" + +#include +#include +#include + namespace DesignTools { CurveEditorModel::CurveEditorModel(double minTime, double maxTime, QObject *parent) @@ -39,28 +47,82 @@ CurveEditorModel::CurveEditorModel(double minTime, double maxTime, QObject *pare CurveEditorModel::~CurveEditorModel() {} +double CurveEditorModel::minimumTime() const +{ + return m_minTime; +} + +double CurveEditorModel::maximumTime() const +{ + return m_maxTime; +} + +DesignTools::CurveEditorStyle CurveEditorModel::style() const +{ + // Pseudo auto generated. See: CurveEditorStyleDialog + DesignTools::CurveEditorStyle out; + out.backgroundBrush = QBrush(QColor(21, 21, 21)); + out.backgroundAlternateBrush = QBrush(QColor(32, 32, 32)); + out.fontColor = QColor(255, 255, 255); + out.gridColor = QColor(114, 116, 118); + out.canvasMargin = 15; + out.zoomInWidth = 99; + out.zoomInHeight = 99; + out.timeAxisHeight = 60; + out.timeOffsetLeft = 10; + out.timeOffsetRight = 10; + out.rangeBarColor = QmlDesigner::Theme::instance()->qmlDesignerBackgroundColorDarkAlternate(); + out.rangeBarCapsColor = QmlDesigner::Theme::getColor( + QmlDesigner::Theme::QmlDesigner_HighlightColor); + out.valueAxisWidth = 60; + out.valueOffsetTop = 10; + out.valueOffsetBottom = 10; + out.handleStyle.size = 10; + out.handleStyle.lineWidth = 1; + out.handleStyle.color = QColor(255, 255, 255); + out.handleStyle.selectionColor = QColor(255, 255, 255); + out.keyframeStyle.size = 14; + out.keyframeStyle.color = QColor(172, 210, 255); + out.keyframeStyle.selectionColor = QColor(255, 255, 255); + out.curveStyle.width = 2; + out.curveStyle.color = QColor(0, 200, 0); + out.curveStyle.selectionColor = QColor(255, 255, 255); + out.treeItemStyle.margins = 0; + out.playhead.width = 20; + out.playhead.radius = 4; + out.playhead.color = QColor(200, 200, 0); + return out; +} + +void CurveEditorModel::setTimeline(const QmlDesigner::QmlTimeline &timeline) +{ + m_minTime = timeline.startKeyframe(); + m_maxTime = timeline.endKeyframe(); + std::vector items; + for (auto &&target : timeline.allTargets()) { + if (DesignTools::TreeItem *item = createTopLevelItem(timeline, target)) + items.push_back(item); + } + + reset(items); +} + void CurveEditorModel::setCurrentFrame(int frame) { if (graphicsView()) - graphicsView()->setCurrentFrame(frame); + graphicsView()->setCurrentFrame(frame, false); } -void CurveEditorModel::setMinimumTime(double time, bool internal) +void CurveEditorModel::setMinimumTime(double time) { m_minTime = time; - if (internal) - emit updateStartFrame(m_minTime); - else - emit startFrameChanged(m_minTime); + emit commitStartFrame(static_cast(m_minTime)); } -void CurveEditorModel::setMaximumTime(double time, bool internal) +void CurveEditorModel::setMaximumTime(double time) { m_maxTime = time; - if (internal) - emit updateEndFrame(m_maxTime); - else - emit endFrameChanged(m_maxTime); + emit commitEndFrame(static_cast(m_maxTime)); } void CurveEditorModel::setCurve(unsigned int id, const AnimationCurve &curve) @@ -114,4 +176,152 @@ void CurveEditorModel::reset(const std::vector &items) sm->selectPaths(sel); } +DesignTools::ValueType typeFrom(const QmlDesigner::QmlTimelineKeyframeGroup &group) +{ + if (group.valueType() == QmlDesigner::TypeName("double") + || group.valueType() == QmlDesigner::TypeName("real") + || group.valueType() == QmlDesigner::TypeName("float")) + return DesignTools::ValueType::Double; + + if (group.valueType() == QmlDesigner::TypeName("boolean") + || group.valueType() == QmlDesigner::TypeName("bool")) + return DesignTools::ValueType::Bool; + + if (group.valueType() == QmlDesigner::TypeName("integer") + || group.valueType() == QmlDesigner::TypeName("int")) + return DesignTools::ValueType::Integer; + + // Ignoring: QColor / HAlignment / VAlignment + return DesignTools::ValueType::Undefined; +} + +DesignTools::TreeItem *CurveEditorModel::createTopLevelItem(const QmlDesigner::QmlTimeline &timeline, + const QmlDesigner::ModelNode &node) +{ + if (!node.isValid()) + return nullptr; + + auto *nodeItem = new DesignTools::NodeTreeItem(node.id(), QIcon(":/ICON_INSTANCE")); + for (auto &&grp : timeline.keyframeGroupsForTarget(node)) { + if (grp.isValid()) { + DesignTools::AnimationCurve curve = createAnimationCurve(grp); + if (curve.isValid()) { + QString name = QString::fromUtf8(grp.propertyName()); + auto propertyItem = new DesignTools::PropertyTreeItem(name, curve, typeFrom(grp)); + + QmlDesigner::ModelNode target = grp.modelNode(); + if (target.hasAuxiliaryData("locked")) + propertyItem->setLocked(true); + + if (target.hasAuxiliaryData("pinned")) + propertyItem->setPinned(true); + + nodeItem->addChild(propertyItem); + } + } + } + + if (!nodeItem->hasChildren()) { + delete nodeItem; + nodeItem = nullptr; + } + + return nodeItem; +} + +DesignTools::AnimationCurve CurveEditorModel::createAnimationCurve( + const QmlDesigner::QmlTimelineKeyframeGroup &group) +{ + switch (typeFrom(group)) { + case DesignTools::ValueType::Bool: + return createDoubleCurve(group); + + case DesignTools::ValueType::Integer: + return createDoubleCurve(group); + + case DesignTools::ValueType::Double: + return createDoubleCurve(group); + + default: + return DesignTools::AnimationCurve(); + } +} + +std::vector createKeyframes(QList nodes) +{ + auto byTime = [](const auto &a, const auto &b) { + return a.variantProperty("frame").value().toDouble() + < b.variantProperty("frame").value().toDouble(); + }; + std::sort(nodes.begin(), nodes.end(), byTime); + + std::vector frames; + for (auto &&node : nodes) { + QVariant timeVariant = node.variantProperty("frame").value(); + QVariant valueVariant = node.variantProperty("value").value(); + if (!timeVariant.isValid() || !valueVariant.isValid()) + continue; + + QPointF position(timeVariant.toDouble(), valueVariant.toDouble()); + + auto keyframe = DesignTools::Keyframe(position); + + if (node.hasBindingProperty("easing.bezierCurve")) { + QmlDesigner::EasingCurve ecurve; + ecurve.fromString(node.bindingProperty("easing.bezierCurve").expression()); + keyframe.setData(static_cast(ecurve)); + } + frames.push_back(keyframe); + } + return frames; +} + +std::vector resolveSmallCurves(const std::vector &frames) +{ + std::vector out; + for (auto &&frame : frames) { + if (frame.hasData() && !out.empty()) { + QEasingCurve curve = frame.data().toEasingCurve(); + // One-segment-curve: Since (0,0) is implicit => 3 + if (curve.toCubicSpline().count() == 3) { + DesignTools::Keyframe &previous = out.back(); +#if 0 + // Do not resolve when two adjacent keyframes have the same value. + if (qFuzzyCompare(previous.position().y(), frame.position().y())) { + out.push_back(frame); + continue; + } +#endif + DesignTools::AnimationCurve acurve(curve, previous.position(), frame.position()); + previous.setRightHandle(acurve.keyframeAt(0).rightHandle()); + out.push_back(acurve.keyframeAt(1)); + continue; + } + } + out.push_back(frame); + } + return out; +} + +DesignTools::AnimationCurve CurveEditorModel::createDoubleCurve( + const QmlDesigner::QmlTimelineKeyframeGroup &group) +{ + std::vector keyframes = createKeyframes(group.keyframePositions()); + keyframes = resolveSmallCurves(keyframes); + + QString str; + QmlDesigner::ModelNode target = group.modelNode(); + if (target.hasAuxiliaryData("unified")) + str = target.auxiliaryData("unified").toString(); + + if (str.size() == static_cast(keyframes.size())) { + for (int i = 0; i < str.size(); ++i) { + if (str.at(i) == '1') + keyframes[static_cast(i)].setUnified(true); + } + } + + return DesignTools::AnimationCurve(keyframes); +} + } // End namespace DesignTools. diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.h b/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.h index 48d05586a2a..952dc46c639 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.h +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.h @@ -27,6 +27,8 @@ #include "detail/treemodel.h" +#include + #include QT_BEGIN_NAMESPACE @@ -46,41 +48,46 @@ class CurveEditorModel : public TreeModel Q_OBJECT signals: - void currentFrameChanged(int frame); + void commitCurrentFrame(int frame); - void startFrameChanged(int frame); + void commitStartFrame(int frame); - void endFrameChanged(int frame); - - void updateStartFrame(int frame); - - void updateEndFrame(int frame); + void commitEndFrame(int frame); void curveChanged(PropertyTreeItem *item); -public: - virtual double minimumTime() const = 0; - - virtual double maximumTime() const = 0; - - virtual CurveEditorStyle style() const = 0; - public: CurveEditorModel(double minTime, double maxTime, QObject *parent = nullptr); ~CurveEditorModel() override; + double minimumTime() const; + + double maximumTime() const; + + DesignTools::CurveEditorStyle style() const; + +public: + void setTimeline(const QmlDesigner::QmlTimeline &timeline); + void setCurrentFrame(int frame); - void setMinimumTime(double time, bool internal); + void setMinimumTime(double time); - void setMaximumTime(double time, bool internal); + void setMaximumTime(double time); void setCurve(unsigned int id, const AnimationCurve &curve); void reset(const std::vector &items); -protected: +private: + DesignTools::TreeItem *createTopLevelItem(const QmlDesigner::QmlTimeline &timeline, + const QmlDesigner::ModelNode &node); + + DesignTools::AnimationCurve createAnimationCurve(const QmlDesigner::QmlTimelineKeyframeGroup &group); + + DesignTools::AnimationCurve createDoubleCurve(const QmlDesigner::QmlTimelineKeyframeGroup &group); + double m_minTime = 0.; double m_maxTime = 0.; diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp new file mode 100644 index 00000000000..9d858d684c3 --- /dev/null +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp @@ -0,0 +1,375 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** 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 "curveeditorview.h" +#include "curveeditor.h" +#include "curveeditormodel.h" +#include "curvesegment.h" + +#include +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { + +CurveEditorView::CurveEditorView(QObject *parent) + : AbstractView(parent) + , m_block(false) + , m_model(new DesignTools::CurveEditorModel(0., 500.)) + , m_editor(new DesignTools::CurveEditor(m_model)) +{ + Q_UNUSED(parent); + connect(m_model, + &DesignTools::CurveEditorModel::commitCurrentFrame, + this, + &CurveEditorView::commitCurrentFrame); + + connect(m_model, + &DesignTools::CurveEditorModel::commitStartFrame, + this, + &CurveEditorView::commitStartFrame); + + connect(m_model, + &DesignTools::CurveEditorModel::commitEndFrame, + this, + &CurveEditorView::commitEndFrame); + + connect(m_model, + &DesignTools::CurveEditorModel::curveChanged, + this, + &CurveEditorView::commitKeyframes); +} + +CurveEditorView::~CurveEditorView() {} + +bool CurveEditorView::hasWidget() const +{ + return true; +} + +WidgetInfo CurveEditorView::widgetInfo() +{ + return createWidgetInfo( + m_editor, nullptr, "CurveEditorId", WidgetInfo::BottomPane, 0, tr("CurveEditor")); +} + +void CurveEditorView::modelAttached(Model *model) +{ + AbstractView::modelAttached(model); + + QmlTimeline timeline = activeTimeline(); + if (timeline.isValid()) { + m_model->setTimeline(timeline); + } +} + +void CurveEditorView::modelAboutToBeDetached(Model *model) +{ + AbstractView::modelAboutToBeDetached(model); + m_model->reset({}); +} + +bool dirtyfiesView(const ModelNode &node) +{ + return QmlTimeline::isValidQmlTimeline(node) + || QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(node); +} + +void CurveEditorView::nodeRemoved(const ModelNode &removedNode, + const NodeAbstractProperty &parentProperty, + PropertyChangeFlags propertyChange) +{ + Q_UNUSED(removedNode); + Q_UNUSED(propertyChange); + + if (!parentProperty.isValid()) + return; + + ModelNode parent = parentProperty.parentModelNode(); + if (dirtyfiesView(parent)) + updateKeyframes(); +} + +void CurveEditorView::nodeReparented(const ModelNode &node, + const NodeAbstractProperty &newPropertyParent, + const NodeAbstractProperty &oldPropertyParent, + PropertyChangeFlags propertyChange) +{ + Q_UNUSED(node); + Q_UNUSED(newPropertyParent); + Q_UNUSED(oldPropertyParent); + Q_UNUSED(propertyChange); + + ModelNode parent = newPropertyParent.parentModelNode(); + if (newPropertyParent.isValid() && dirtyfiesView(parent)) + updateKeyframes(); + else if (QmlTimelineKeyframeGroup::checkKeyframesType(node)) + updateKeyframes(); +} + +void CurveEditorView::instancePropertyChanged(const QList> &propertyList) +{ + Q_UNUSED(propertyList); + + for (const auto &pair : propertyList) { + if (!QmlTimeline::isValidQmlTimeline(pair.first)) + continue; + + if (pair.second == "startFrame") + updateStartFrame(pair.first); + else if (pair.second == "endFrame") + updateEndFrame(pair.first); + else if (pair.second == "currentFrame") + updateCurrentFrame(pair.first); + } +} + +void CurveEditorView::variantPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) +{ + Q_UNUSED(propertyList); + Q_UNUSED(propertyChange); + + for (const auto &property : propertyList) { + if ((property.name() == "frame" || property.name() == "value") + && property.parentModelNode().type() == "QtQuick.Timeline.Keyframe" + && property.parentModelNode().isValid() + && property.parentModelNode().hasParentProperty()) { + const ModelNode framesNode = property.parentModelNode().parentProperty().parentModelNode(); + if (QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(framesNode)) + updateKeyframes(); + } + } +} + +void CurveEditorView::bindingPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) +{ + Q_UNUSED(propertyList); + Q_UNUSED(propertyChange); + + for (const auto &property : propertyList) { + if (property.name() == "easing.bezierCurve") { + updateKeyframes(); + } + } +} + +void CurveEditorView::propertiesRemoved(const QList &propertyList) +{ + Q_UNUSED(propertyList); + + for (const auto &property : propertyList) { + if (property.name() == "keyframes" && property.parentModelNode().isValid()) { + ModelNode parent = property.parentModelNode(); + if (dirtyfiesView(parent)) + updateKeyframes(); + } + } +} + +QmlTimeline CurveEditorView::activeTimeline() const +{ + QmlModelState state = currentState(); + if (state.isBaseState()) { + for (const ModelNode &node : allModelNodesOfType("QtQuick.Timeline.Timeline")) { + if (QmlTimeline::isValidQmlTimeline(node)) { + if (node.hasVariantProperty("enabled") + && node.variantProperty("enabled").value().toBool()) + return QmlTimeline(node); + + return {}; + } + } + } + + for (const ModelNode &node : allModelNodesOfType("QtQuick.Timeline.Timeline")) { + if (QmlTimeline::isValidQmlTimeline(node) && state.affectsModelNode(node)) { + QmlPropertyChanges propertyChanges(state.propertyChanges(node)); + if (!propertyChanges.isValid()) + continue; + + if (node.hasVariantProperty("enabled") && node.variantProperty("enabled").value().toBool()) + return QmlTimeline(node); + } + } + return {}; +} + +void CurveEditorView::updateKeyframes() +{ + if (m_block) + return; + + QmlTimeline timeline = activeTimeline(); + if (timeline.isValid()) + m_model->setTimeline(timeline); + else + m_model->reset({}); +} + +void CurveEditorView::updateCurrentFrame(const ModelNode &node) +{ + if (m_editor->dragging()) + return; + + QmlTimeline timeline(node); + if (timeline.isValid()) + m_model->setCurrentFrame(static_cast(std::round(timeline.currentKeyframe()))); + else + m_model->setCurrentFrame(0); +} + +void CurveEditorView::updateStartFrame(const ModelNode &node) +{ + QmlTimeline timeline(node); + if (timeline.isValid()) + m_model->setMinimumTime(static_cast(std::round(timeline.startKeyframe()))); +} + +void CurveEditorView::updateEndFrame(const ModelNode &node) +{ + QmlTimeline timeline(node); + if (timeline.isValid()) + m_model->setMaximumTime(static_cast(std::round(timeline.endKeyframe()))); +} + +ModelNode getTargetNode1(DesignTools::PropertyTreeItem *item, const QmlTimeline &timeline) +{ + if (const DesignTools::NodeTreeItem *nodeItem = item->parentNodeTreeItem()) { + QString targetId = nodeItem->name(); + if (timeline.isValid()) { + for (auto &&target : timeline.allTargets()) { + if (target.displayName() == targetId) + return target; + } + } + } + return ModelNode(); +} + +QmlTimelineKeyframeGroup timelineKeyframeGroup1(QmlTimeline &timeline, + DesignTools::PropertyTreeItem *item) +{ + ModelNode node = getTargetNode1(item, timeline); + if (node.isValid()) + return timeline.keyframeGroup(node, item->name().toLatin1()); + + return QmlTimelineKeyframeGroup(); +} + +void attachEasingCurve1(double frame, const QEasingCurve &curve, const QmlTimelineKeyframeGroup &group) +{ + ModelNode frameNode = group.keyframe(frame); + if (frameNode.isValid()) { + auto expression = EasingCurve(curve).toString(); + frameNode.bindingProperty("easing.bezierCurve").setExpression(expression); + } +} + +void CurveEditorView::commitKeyframes(DesignTools::PropertyTreeItem *item) +{ + QmlTimeline currentTimeline = activeTimeline(); + QmlTimelineKeyframeGroup group = timelineKeyframeGroup1(currentTimeline, item); + + if (group.isValid()) { + ModelNode groupNode = group.modelNode(); + + if (groupNode.isValid()) { + if (item->locked()) + groupNode.setAuxiliaryData("locked", true); + else + groupNode.removeAuxiliaryData("locked"); + + if (item->pinned()) + groupNode.setAuxiliaryData("pinned", true); + else + groupNode.removeAuxiliaryData("pinned"); + + if (item->hasUnified()) + groupNode.setAuxiliaryData("unified", item->unifyString()); + else + groupNode.removeAuxiliaryData("unified"); + } + + auto replaceKeyframes = [&group, item, this]() { + m_block = true; + for (auto frame : group.keyframes()) + frame.destroy(); + + DesignTools::Keyframe previous; + for (auto &&frame : item->curve().keyframes()) { + QPointF pos = frame.position(); + group.setValue(QVariant(pos.y()), pos.x()); + + if (previous.isValid()) { + if (frame.interpolation() == DesignTools::Keyframe::Interpolation::Bezier) { + DesignTools::CurveSegment segment(previous, frame); + if (segment.isValid()) + attachEasingCurve1(pos.x(), segment.easingCurve(), group); + } else if (frame.interpolation() == DesignTools::Keyframe::Interpolation::Easing) { + QVariant data = frame.data(); + if (data.type() == static_cast(QMetaType::QEasingCurve)) + attachEasingCurve1(pos.x(), data.value(), group); + } else if (frame.interpolation() == DesignTools::Keyframe::Interpolation::Step) { + // Warning: Keyframe::Interpolation::Step not yet implemented + } + } + + previous = frame; + } + m_block = false; + }; + + executeInTransaction("CurveEditor::commitKeyframes", replaceKeyframes); + } +} + +void CurveEditorView::commitCurrentFrame(int frame) +{ + QmlTimeline timeline = activeTimeline(); + timeline.modelNode().setAuxiliaryData("currentFrame@NodeInstance", frame); +} + +void CurveEditorView::commitStartFrame(int frame) +{ + QmlTimeline timeline = activeTimeline(); + if (timeline.isValid()) + timeline.modelNode().variantProperty("startFrame").setValue(frame); +} + +void CurveEditorView::commitEndFrame(int frame) +{ + QmlTimeline timeline = activeTimeline(); + if (timeline.isValid()) + timeline.modelNode().variantProperty("endFrame").setValue(frame); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditorview.h b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.h new file mode 100644 index 00000000000..5da7c4514b9 --- /dev/null +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** 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 "curveeditor.h" +#include "curveeditormodel.h" +#include + +namespace QmlDesigner { + +class TimelineWidget; + +class CurveEditorView : public AbstractView +{ + Q_OBJECT + +public: + explicit CurveEditorView(QObject *parent = nullptr); + ~CurveEditorView() override; + +public: + bool hasWidget() const override; + + WidgetInfo widgetInfo() override; + + void modelAttached(Model *model) override; + + void modelAboutToBeDetached(Model *model) override; + + void nodeRemoved(const ModelNode &removedNode, + const NodeAbstractProperty &parentProperty, + PropertyChangeFlags propertyChange) override; + + void nodeReparented(const ModelNode &node, + const NodeAbstractProperty &newPropertyParent, + const NodeAbstractProperty &oldPropertyParent, + PropertyChangeFlags propertyChange) override; + + void instancePropertyChanged(const QList> &propertyList) override; + + void variantPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override; + + void bindingPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override; + + void propertiesRemoved(const QList &propertyList) override; + +private: + QmlTimeline activeTimeline() const; + + void updateKeyframes(); + void updateCurrentFrame(const ModelNode &node); + void updateStartFrame(const ModelNode &node); + void updateEndFrame(const ModelNode &node); + + void commitKeyframes(DesignTools::PropertyTreeItem *item); + void commitCurrentFrame(int frame); + void commitStartFrame(int frame); + void commitEndFrame(int frame); + +private: + bool m_block; + DesignTools::CurveEditorModel *m_model; + DesignTools::CurveEditor *m_editor; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp index 60aa7060426..95ba22831f0 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp @@ -43,6 +43,7 @@ namespace DesignTools { GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent) : QGraphicsView(parent) + , m_dragging(false) , m_zoomX(0.0) , m_zoomY(0.0) , m_transform() @@ -98,6 +99,11 @@ CurveEditorStyle GraphicsView::editorStyle() const return m_style; } +bool GraphicsView::dragging() const +{ + return m_dragging; +} + double GraphicsView::minimumTime() const { bool check = m_model->minimumTime() < m_scene->minimumTime(); @@ -152,9 +158,8 @@ QRectF GraphicsView::timeScaleRect() const QRectF GraphicsView::valueScaleRect() const { QRect vp(viewport()->rect()); - QPoint tl = vp.topLeft() + QPoint(0, m_style.timeAxisHeight); QPoint br = vp.bottomLeft() + QPoint(m_style.valueAxisWidth, 0); - return mapToScene(QRect(tl, br)).boundingRect(); + return mapToScene(QRect(vp.topLeft(), br)).boundingRect(); } QRectF GraphicsView::defaultRasterRect() const @@ -204,11 +209,11 @@ void GraphicsView::setZoomY(double zoom, const QPoint &pivot) void GraphicsView::setCurrentFrame(int frame, bool notify) { int clampedFrame = clamp(frame, m_model->minimumTime(), m_model->maximumTime()); + m_playhead.moveToFrame(clampedFrame, this); viewport()->update(); - if (notify) - notifyFrameChanged(frame); + currentFrameChanged(clampedFrame, notify); } void GraphicsView::scrollContent(double x, double y) @@ -276,8 +281,10 @@ void GraphicsView::keyPressEvent(QKeyEvent *event) void GraphicsView::mousePressEvent(QMouseEvent *event) { - if (m_playhead.mousePress(globalToScene(event->globalPos()))) + if (m_playhead.mousePress(globalToScene(event->globalPos()))) { + m_dragging = true; return; + } Shortcut shortcut(event); if (shortcut == m_style.shortcuts.insertKeyframe) { @@ -288,6 +295,7 @@ void GraphicsView::mousePressEvent(QMouseEvent *event) if (shortcut == Shortcut(Qt::LeftButton)) { QPointF pos = mapToScene(event->pos()); if (timeScaleRect().contains(pos)) { + m_dragging = true; setCurrentFrame(std::round(mapXtoTime(pos.x()))); m_playhead.setMoving(true); event->accept(); @@ -317,6 +325,7 @@ void GraphicsView::mouseReleaseEvent(QMouseEvent *event) m_playhead.mouseRelease(this); m_selector.mouseRelease(event, m_scene); this->viewport()->update(); + m_dragging = false; } void GraphicsView::wheelEvent(QWheelEvent *event) @@ -364,15 +373,14 @@ void GraphicsView::drawForeground(QPainter *painter, const QRectF &rect) if (abscissa.isValid()) drawTimeScale(painter, abscissa); - painter->fillRect(QRectF(rect.topLeft(), abscissa.bottomLeft()), - m_style.backgroundAlternateBrush); + painter->fillRect(QRectF(rect.topLeft(), abscissa.bottomLeft()), m_style.backgroundAlternateBrush); + + m_playhead.paint(painter, this); auto ordinate = valueScaleRect(); if (ordinate.isValid()) drawValueScale(painter, ordinate); - m_playhead.paint(painter, this); - m_selector.paint(painter); } @@ -463,11 +471,7 @@ void GraphicsView::applyZoom(double x, double y, const QPoint &pivot) void GraphicsView::drawGrid(QPainter *painter, const QRectF &rect) { - QRectF gridRect = rect.adjusted( - m_style.valueAxisWidth + m_style.canvasMargin, - m_style.timeAxisHeight + m_style.canvasMargin, - -m_style.canvasMargin, - -m_style.canvasMargin); + QRectF gridRect = scene()->sceneRect(); if (!gridRect.isValid()) return; @@ -577,8 +581,6 @@ void GraphicsView::drawTimeScale(QPainter *painter, const QRectF &rect) for (double i = minimumTime(); i <= maximumTime(); i += timeIncrement) paintLabeledTick(i); - drawRangeBar(painter, rect); - painter->restore(); } diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h index ca062416dd3..374109feb94 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h +++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h @@ -47,7 +47,7 @@ class GraphicsView : public QGraphicsView friend class Playhead; signals: - void notifyFrameChanged(int frame); + void currentFrameChanged(int frame, bool notify); public: GraphicsView(CurveEditorModel *model, QWidget *parent = nullptr); @@ -58,6 +58,8 @@ public: CurveEditorStyle editorStyle() const; + bool dragging() const; + int mapTimeToX(double time) const; int mapValueToY(double value) const; @@ -153,6 +155,8 @@ private: QRectF rangeMaxHandle(const QRectF &rect); private: + bool m_dragging; + double m_zoomX; double m_zoomY; diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/playhead.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/playhead.cpp index e54f4bc26ad..e59c9f012e8 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/playhead.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/playhead.cpp @@ -102,9 +102,10 @@ bool Playhead::mouseMove(const QPointF &pos, GraphicsView *view) QRectF canvas = view->canvasRect().adjusted(0.0, -style.timeAxisHeight, 0.0, 0.0); - if (canvas.contains(pos)) - view->setCurrentFrame(std::round(view->mapXtoTime(pos.x()))); - else if (!m_timer.isActive()) + if (canvas.contains(pos)) { + int frame = std::round(view->mapXtoTime(pos.x())); + view->setCurrentFrame(frame); + } else if (!m_timer.isActive()) m_timer.start(); } @@ -148,9 +149,6 @@ void Playhead::mouseMoveOutOfBounds(GraphicsView *view) void Playhead::mouseRelease(GraphicsView *view) { - if (m_moving) - emit view->model()->currentFrameChanged(m_frame); - m_moving = false; } diff --git a/src/plugins/qmldesigner/components/timelineeditor/animationcurvedialog.cpp b/src/plugins/qmldesigner/components/timelineeditor/animationcurvedialog.cpp deleted file mode 100644 index 71fcb1a260e..00000000000 --- a/src/plugins/qmldesigner/components/timelineeditor/animationcurvedialog.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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 "animationcurvedialog.h" - -#include - -namespace QmlDesigner { - -AnimationCurveDialog::AnimationCurveDialog(QWidget *parent) - : QDialog(parent) - , m_editor(nullptr) -{ -} - -AnimationCurveDialog::AnimationCurveDialog(DesignTools::CurveEditorModel *model, QWidget *parent) - : QDialog(parent) - , m_editor(nullptr) -{ - setModel(model); -} - -void AnimationCurveDialog::setModel(DesignTools::CurveEditorModel *model) -{ - if (m_editor) - return; - - m_editor = new DesignTools::CurveEditor(model); - - auto *layout = new QVBoxLayout; - layout->addWidget(m_editor); - setLayout(layout); -} - -void AnimationCurveDialog::refresh() -{ - if (m_editor) - m_editor->clearCanvas(); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/animationcurvedialog.h b/src/plugins/qmldesigner/components/timelineeditor/animationcurvedialog.h deleted file mode 100644 index 1bb20a31744..00000000000 --- a/src/plugins/qmldesigner/components/timelineeditor/animationcurvedialog.h +++ /dev/null @@ -1,51 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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 "curveeditor/curveeditor.h" - -#include - -namespace QmlDesigner { - -class AnimationCurveDialog : public QDialog -{ - Q_OBJECT - -public: - AnimationCurveDialog(QWidget *parent = nullptr); - - AnimationCurveDialog(DesignTools::CurveEditorModel *model, QWidget *parent = nullptr); - - void setModel(DesignTools::CurveEditorModel *model); - - void refresh(); - -private: - DesignTools::CurveEditor *m_editor; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/animationcurveeditormodel.cpp b/src/plugins/qmldesigner/components/timelineeditor/animationcurveeditormodel.cpp deleted file mode 100644 index 077b467c0e2..00000000000 --- a/src/plugins/qmldesigner/components/timelineeditor/animationcurveeditormodel.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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 "animationcurveeditormodel.h" -#include "curveeditor/curveeditorstyle.h" -#include "curveeditor/treeitem.h" -#include "easingcurve.h" -#include "qmltimeline.h" - -#include -#include -#include - -namespace QmlDesigner { - -AnimationCurveEditorModel::AnimationCurveEditorModel(double minTime, double maxTime) - : CurveEditorModel(minTime, maxTime) -{} - -AnimationCurveEditorModel::~AnimationCurveEditorModel() {} - -double AnimationCurveEditorModel::minimumTime() const -{ - return m_minTime; -} - -double AnimationCurveEditorModel::maximumTime() const -{ - return m_maxTime; -} - -DesignTools::CurveEditorStyle AnimationCurveEditorModel::style() const -{ - // Pseudo auto generated. See: CurveEditorStyleDialog - DesignTools::CurveEditorStyle out; - out.backgroundBrush = QBrush(QColor(21, 21, 21)); - out.backgroundAlternateBrush = QBrush(QColor(32, 32, 32)); - out.fontColor = QColor(255, 255, 255); - out.gridColor = QColor(114, 116, 118); - out.canvasMargin = 15; - out.zoomInWidth = 99; - out.zoomInHeight = 99; - out.timeAxisHeight = 60; - out.timeOffsetLeft = 10; - out.timeOffsetRight = 10; - out.rangeBarColor = Theme::instance()->qmlDesignerBackgroundColorDarkAlternate(); - out.rangeBarCapsColor = Theme::getColor(Theme::QmlDesigner_HighlightColor); - out.valueAxisWidth = 60; - out.valueOffsetTop = 10; - out.valueOffsetBottom = 10; - out.handleStyle.size = 10; - out.handleStyle.lineWidth = 1; - out.handleStyle.color = QColor(255, 255, 255); - out.handleStyle.selectionColor = QColor(255, 255, 255); - out.keyframeStyle.size = 14; - out.keyframeStyle.color = QColor(172, 210, 255); - out.keyframeStyle.selectionColor = QColor(255, 255, 255); - out.curveStyle.width = 2; - out.curveStyle.color = QColor(0, 200, 0); - out.curveStyle.selectionColor = QColor(255, 255, 255); - out.treeItemStyle.margins = 0; - out.playhead.width = 20; - out.playhead.radius = 4; - out.playhead.color = QColor(200, 200, 0); - return out; -} - -void AnimationCurveEditorModel::setTimeline(const QmlTimeline &timeline) -{ - m_minTime = timeline.startKeyframe(); - m_maxTime = timeline.endKeyframe(); - - std::vector items; - for (auto &&target : timeline.allTargets()) { - if (DesignTools::TreeItem *item = createTopLevelItem(timeline, target)) - items.push_back(item); - } - - reset(items); -} - -DesignTools::ValueType typeFrom(const QmlTimelineKeyframeGroup &group) -{ - if (group.valueType() == TypeName("double") || group.valueType() == TypeName("real") - || group.valueType() == TypeName("float")) - return DesignTools::ValueType::Double; - - if (group.valueType() == TypeName("boolean") || group.valueType() == TypeName("bool")) - return DesignTools::ValueType::Bool; - - if (group.valueType() == TypeName("integer") || group.valueType() == TypeName("int")) - return DesignTools::ValueType::Integer; - - // Ignoring: QColor / HAlignment / VAlignment - return DesignTools::ValueType::Undefined; -} - -DesignTools::TreeItem *AnimationCurveEditorModel::createTopLevelItem(const QmlTimeline &timeline, - const ModelNode &node) -{ - if (!node.isValid()) - return nullptr; - - auto *nodeItem = new DesignTools::NodeTreeItem(node.id(), QIcon(":/ICON_INSTANCE")); - for (auto &&grp : timeline.keyframeGroupsForTarget(node)) { - if (grp.isValid()) { - DesignTools::AnimationCurve curve = createAnimationCurve(grp); - if (curve.isValid()) { - QString name = QString::fromUtf8(grp.propertyName()); - auto propertyItem = new DesignTools::PropertyTreeItem(name, curve, typeFrom(grp)); - - ModelNode target = grp.modelNode(); - if (target.hasAuxiliaryData("locked")) - propertyItem->setLocked(true); - - if (target.hasAuxiliaryData("pinned")) - propertyItem->setPinned(true); - - nodeItem->addChild(propertyItem); - } - } - } - - if (!nodeItem->hasChildren()) { - delete nodeItem; - nodeItem = nullptr; - } - - return nodeItem; -} - -DesignTools::AnimationCurve AnimationCurveEditorModel::createAnimationCurve( - const QmlTimelineKeyframeGroup &group) -{ - switch (typeFrom(group)) { - case DesignTools::ValueType::Bool: - return createDoubleCurve(group); - - case DesignTools::ValueType::Integer: - return createDoubleCurve(group); - - case DesignTools::ValueType::Double: - return createDoubleCurve(group); - default: - return DesignTools::AnimationCurve(); - } -} - -std::vector createKeyframes(QList nodes) -{ - auto byTime = [](const auto &a, const auto &b) { - return a.variantProperty("frame").value().toDouble() - < b.variantProperty("frame").value().toDouble(); - }; - std::sort(nodes.begin(), nodes.end(), byTime); - - std::vector frames; - for (auto &&node : nodes) { - QVariant timeVariant = node.variantProperty("frame").value(); - QVariant valueVariant = node.variantProperty("value").value(); - if (!timeVariant.isValid() || !valueVariant.isValid()) - continue; - - QPointF position(timeVariant.toDouble(), valueVariant.toDouble()); - - auto keyframe = DesignTools::Keyframe(position); - - if (node.hasBindingProperty("easing.bezierCurve")) { - EasingCurve ecurve; - ecurve.fromString(node.bindingProperty("easing.bezierCurve").expression()); - keyframe.setData(static_cast(ecurve)); - } - frames.push_back(keyframe); - } - return frames; -} - -std::vector resolveSmallCurves( - const std::vector &frames) -{ - std::vector out; - for (auto &&frame : frames) { - if (frame.hasData() && !out.empty()) { - QEasingCurve curve = frame.data().toEasingCurve(); - // One-segment-curve: Since (0,0) is implicit => 3 - if (curve.toCubicSpline().count() == 3) { - DesignTools::Keyframe &previous = out.back(); -#if 0 - // Do not resolve when two adjacent keyframes have the same value. - if (qFuzzyCompare(previous.position().y(), frame.position().y())) { - out.push_back(frame); - continue; - } -#endif - DesignTools::AnimationCurve acurve(curve, previous.position(), frame.position()); - previous.setRightHandle(acurve.keyframeAt(0).rightHandle()); - out.push_back(acurve.keyframeAt(1)); - continue; - } - } - out.push_back(frame); - } - return out; -} - -DesignTools::AnimationCurve AnimationCurveEditorModel::createDoubleCurve( - const QmlTimelineKeyframeGroup &group) -{ - std::vector keyframes = createKeyframes(group.keyframePositions()); - keyframes = resolveSmallCurves(keyframes); - - QString str; - ModelNode target = group.modelNode(); - if (target.hasAuxiliaryData("unified")) - str = target.auxiliaryData("unified").toString(); - - if (str.size() == static_cast(keyframes.size())) { - for (int i = 0; i < str.size(); ++i) { - if (str.at(i) == '1') - keyframes[i].setUnified(true); - } - } - - return DesignTools::AnimationCurve(keyframes); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/animationcurveeditormodel.h b/src/plugins/qmldesigner/components/timelineeditor/animationcurveeditormodel.h deleted file mode 100644 index 4da0be62b0b..00000000000 --- a/src/plugins/qmldesigner/components/timelineeditor/animationcurveeditormodel.h +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Design Tooling -** -** 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 "curveeditor/curveeditormodel.h" -#include "curveeditor/treeitem.h" - -#include - -namespace QmlDesigner { - -class AnimationCurveEditorModel : public DesignTools::CurveEditorModel -{ - Q_OBJECT - -public: - AnimationCurveEditorModel(double minTime, double maxTime); - - ~AnimationCurveEditorModel() override; - - double minimumTime() const override; - - double maximumTime() const override; - - DesignTools::CurveEditorStyle style() const override; - - void setTimeline(const QmlTimeline &timeline); - -private: - DesignTools::TreeItem *createTopLevelItem(const QmlTimeline &timeline, const ModelNode &node); - - DesignTools::AnimationCurve createAnimationCurve(const QmlTimelineKeyframeGroup &group); - - DesignTools::AnimationCurve createDoubleCurve(const QmlTimelineKeyframeGroup &group); -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineeditor.pri b/src/plugins/qmldesigner/components/timelineeditor/timelineeditor.pri index 184f61e4264..b4aae64da60 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineeditor.pri +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineeditor.pri @@ -36,9 +36,7 @@ SOURCES += \ easingcurve.cpp \ timelinesettingsmodel.cpp \ timelinetooldelegate.cpp \ - timelinecontrols.cpp \ - animationcurveeditormodel.cpp \ - animationcurvedialog.cpp + timelinecontrols.cpp HEADERS += \ timelineview.h \ diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp index 60ca96e93e8..0dc58508448 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp @@ -103,36 +103,7 @@ static QAction *createAction(const Utils::Id &id, TimelineToolBar::TimelineToolBar(QWidget *parent) : QToolBar(parent) , m_grp() - , m_dialog(new AnimationCurveDialog(this)) - , m_curveModel(new AnimationCurveEditorModel(0., 500.)) { - m_dialog->setModel(m_curveModel); - connect(m_curveModel, - &AnimationCurveEditorModel::currentFrameChanged, - this, - &TimelineToolBar::currentFrameChanged); - - auto setStartFrameValue = [this](int val) { - if (m_firstFrame) { - m_firstFrame->setText(QString::number(val, 'f', 0)); - emit startFrameChanged(val); - } - }; - connect(m_curveModel, &AnimationCurveEditorModel::startFrameChanged, setStartFrameValue); - - auto setEndFrameValue = [this](int val) { - if (m_lastFrame) { - m_lastFrame->setText(QString::number(val, 'f', 0)); - emit endFrameChanged(val); - } - }; - connect(m_curveModel, &AnimationCurveEditorModel::endFrameChanged, setEndFrameValue); - - connect(m_curveModel, - &AnimationCurveEditorModel::curveChanged, - this, - &TimelineToolBar::curveChanged); - setContentsMargins(0, 0, 0, 0); createLeftControls(); createCenterControls(); @@ -143,8 +114,6 @@ void TimelineToolBar::reset() { if (recording()) m_recording->setChecked(false); - - m_curveModel->reset({}); } bool TimelineToolBar::recording() const @@ -189,7 +158,6 @@ void TimelineToolBar::setCurrentTimeline(const QmlTimeline &timeline) setStartFrame(timeline.startKeyframe()); setEndFrame(timeline.endKeyframe()); m_timelineLabel->setText(timeline.modelNode().id()); - m_curveModel->setTimeline(timeline); } else { m_timelineLabel->setText(""); } @@ -197,8 +165,6 @@ void TimelineToolBar::setCurrentTimeline(const QmlTimeline &timeline) void TimelineToolBar::setStartFrame(qreal frame) { - m_curveModel->setMinimumTime(frame, true); - auto text = QString::number(frame, 'f', 0); m_firstFrame->setText(text); setupCurrentFrameValidator(); @@ -206,16 +172,12 @@ void TimelineToolBar::setStartFrame(qreal frame) void TimelineToolBar::setCurrentFrame(qreal frame) { - m_curveModel->setCurrentFrame(std::round(frame)); - auto text = QString::number(frame, 'f', 0); m_currentFrame->setText(text); } void TimelineToolBar::setEndFrame(qreal frame) { - m_curveModel->setMaximumTime(frame, true); - auto text = QString::number(frame, 'f', 0); m_lastFrame->setText(text); setupCurrentFrameValidator(); @@ -240,19 +202,6 @@ void TimelineToolBar::removeTimeline(const QmlTimeline &timeline) setCurrentTimeline(QmlTimeline()); } -void TimelineToolBar::openAnimationCurveEditor() -{ - QmlTimeline timeline; - if (auto *tlw = qobject_cast(parent())) { - if (auto *tlv = tlw->timelineView()) - timeline = tlv->timelineForState(tlv->currentState()); - } - - m_dialog->refresh(); - m_curveModel->setTimeline(timeline); - m_dialog->show(); -} - void TimelineToolBar::createLeftControls() { auto addActionToGroup = [&](QAction *action) { @@ -282,17 +231,6 @@ void TimelineToolBar::createLeftControls() connect(settingsAction, &QAction::triggered, this, &TimelineToolBar::settingDialogClicked); addActionToGroup(settingsAction); - auto *curveEditorAction = createAction(TimelineConstants::C_CURVE_EDITOR, - TimelineIcons::CURVE_EDITORDIALOG.icon(), - tr("Animation Curve Editor"), - QKeySequence(Qt::Key_C)); - - connect(curveEditorAction, - &QAction::triggered, - this, - &TimelineToolBar::openAnimationCurveEditor); - addActionToGroup(curveEditorAction); - addWidgetToGroup(createSpacer()); m_timelineLabel = new QLabel(this); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.h b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.h index 1bb222b424b..87195349921 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.h @@ -25,9 +25,6 @@ #pragma once -#include "animationcurvedialog.h" -#include "animationcurveeditormodel.h" - #include QT_FORWARD_DECLARE_CLASS(QLabel) @@ -67,8 +64,6 @@ signals: void currentFrameChanged(int value); void endFrameChanged(int value); - void curveChanged(DesignTools::PropertyTreeItem *item); - public: explicit TimelineToolBar(QWidget *parent = nullptr); @@ -89,8 +84,6 @@ public: void setActionEnabled(const QString &name, bool enabled); void removeTimeline(const QmlTimeline &timeline); - void openAnimationCurveEditor(); - protected: void resizeEvent(QResizeEvent *event) override; @@ -103,10 +96,6 @@ private: QList m_grp; - AnimationCurveDialog *m_dialog = nullptr; - - AnimationCurveEditorModel *m_curveModel = nullptr; - QLabel *m_timelineLabel = nullptr; QLabel *m_stateLabel = nullptr; QSlider *m_scale = nullptr; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.h b/src/plugins/qmldesigner/components/timelineeditor/timelineview.h index ec85e08ccf4..fe3f5903ff3 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.h @@ -25,8 +25,6 @@ #pragma once -#include "animationcurvedialog.h" -#include "animationcurveeditormodel.h" #include "treeitem.h" #include diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp index 2360f01b7ae..a618d22a614 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp @@ -258,8 +258,6 @@ void TimelineWidget::connectToolbar() connect(graphicsScene(), &TimelineGraphicsScene::scroll, this, &TimelineWidget::scroll); - connect(m_toolbar, &TimelineToolBar::curveChanged, this, &TimelineWidget::updateAnimationCurve); - auto setRulerScaling = [this](int val) { m_graphicsScene->setRulerScaling(val); }; connect(m_toolbar, &TimelineToolBar::scaleFactorChanged, setRulerScaling); @@ -372,66 +370,6 @@ void attachEasingCurve(double frame, } } -void TimelineWidget::updateAnimationCurve(DesignTools::PropertyTreeItem *item) -{ - QmlTimeline currentTimeline = graphicsScene()->currentTimeline(); - QmlTimelineKeyframeGroup group = timelineKeyframeGroup(currentTimeline, item); - - if (group.isValid()) { - ModelNode groupNode = group.modelNode(); - - if (groupNode.isValid()) { - if (item->locked()) - groupNode.setAuxiliaryData("locked", true); - else - groupNode.removeAuxiliaryData("locked"); - - if (item->pinned()) - groupNode.setAuxiliaryData("pinned", true); - else - groupNode.removeAuxiliaryData("pinned"); - - if (item->hasUnified()) - groupNode.setAuxiliaryData("unified", item->unifyString()); - else - groupNode.removeAuxiliaryData("unified"); - } - - auto replaceKeyframes = [&group, item, this]() { - m_toolbar->setBlockReflection(true); - for (auto frame : group.keyframes()) - frame.destroy(); - - DesignTools::Keyframe previous; - for (auto &&frame : item->curve().keyframes()) { - QPointF pos = frame.position(); - group.setValue(QVariant(pos.y()), pos.x()); - - if (previous.isValid()) { - if (frame.interpolation() == DesignTools::Keyframe::Interpolation::Bezier) { - DesignTools::CurveSegment segment(previous, frame); - if (segment.isValid()) - attachEasingCurve(pos.x(), segment.easingCurve(), group); - } else if (frame.interpolation() - == DesignTools::Keyframe::Interpolation::Easing) { - QVariant data = frame.data(); - if (data.type() == static_cast(QMetaType::QEasingCurve)) - attachEasingCurve(pos.x(), data.value(), group); - } else if (frame.interpolation() == DesignTools::Keyframe::Interpolation::Step) { - // Warning: Keyframe::Interpolation::Step not yet implemented - } - } - - previous = frame; - } - m_toolbar->setBlockReflection(false); - }; - - timelineView()->executeInTransaction("TimelineWidget::handleKeyframeReplacement", - replaceKeyframes); - } -} - void TimelineWidget::selectionChanged() { if (graphicsScene()->hasSelection()) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h index 23cd338d61a..f50abbebc71 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h @@ -25,7 +25,6 @@ #pragma once -#include "animationcurveeditormodel.h" #include "timelineutils.h" #include @@ -79,8 +78,6 @@ public slots: void changeScaleFactor(int factor); void scroll(const TimelineUtils::Side &side); - void updateAnimationCurve(DesignTools::PropertyTreeItem *item); - protected: void showEvent(QShowEvent *event) override; void resizeEvent(QResizeEvent *event) override; diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp index 726472d149f..2b834cea735 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp @@ -125,11 +125,6 @@ QString TransitionEditorToolBar::currentTransitionId() const return m_transitionComboBox->currentText(); } -void TransitionEditorToolBar::setBlockReflection(bool block) -{ - m_blockReflection = block; -} - void TransitionEditorToolBar::updateComboBox(const ModelNode &root) { if (root.isValid() && root.hasProperty("transitions")) { @@ -142,9 +137,6 @@ void TransitionEditorToolBar::updateComboBox(const ModelNode &root) void TransitionEditorToolBar::setCurrentTransition(const ModelNode &transition) { - if (m_blockReflection) - return; - if (transition.isValid()) { m_transitionComboBox->clear(); const ModelNode root = transition.view()->rootModelNode(); diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.h index eba74021ca4..e16e0718cbe 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.h +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.h @@ -25,9 +25,7 @@ #pragma once -#include "animationcurvedialog.h" -#include "animationcurveeditormodel.h" - +#include #include QT_FORWARD_DECLARE_CLASS(QComboBox) @@ -85,8 +83,6 @@ private: QComboBox *m_transitionComboBox = nullptr; QSlider *m_scale = nullptr; QLineEdit *m_duration = nullptr; - - bool m_blockReflection = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h index faa383c6215..1476a07353d 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h @@ -25,10 +25,6 @@ #pragma once -#include "animationcurvedialog.h" -#include "animationcurveeditormodel.h" -#include "treeitem.h" - #include #include diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index a80009f3da3..ed09082bad8 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -238,6 +239,9 @@ bool QmlDesignerPlugin::delayedInitialize() auto timelineView = new QmlDesigner::TimelineView; d->viewManager.registerViewTakingOwnership(timelineView); timelineView->registerActions(); + + auto curveEditorView = new QmlDesigner::CurveEditorView; + d->viewManager.registerViewTakingOwnership(curveEditorView); } auto transitionEditorView = new QmlDesigner::TransitionEditorView; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 8a711f25d32..9549075f11b 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -721,6 +721,8 @@ Project { "connectioneditor/dynamicpropertiesmodel.cpp", "connectioneditor/dynamicpropertiesmodel.h", "connectioneditor/stylesheet.css", + "curveeditor/curveeditorview.cpp", + "curveeditor/curveeditorview.h", "curveeditor/animationcurve.cpp", "curveeditor/animationcurve.h", "curveeditor/curveeditor.cpp", @@ -796,10 +798,6 @@ Project { "texttool/textedititemwidget.h", "texttool/texttool.cpp", "texttool/texttool.h", - "timelineeditor/animationcurvedialog.cpp", - "timelineeditor/animationcurvedialog.h", - "timelineeditor/animationcurveeditormodel.cpp", - "timelineeditor/animationcurveeditormodel.h", "timelineeditor/canvas.cpp", "timelineeditor/canvas.h", "timelineeditor/canvasstyledialog.cpp",