Add the ability to modify the animation range from the curve editor

Fix a synchronization issue between timeline and curve editor when
the animation contains pinned curves

Change-Id: I89dda234063259f41d662749d696f5fc8a04f9e8
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Knud Dollereder
2020-02-21 15:28:31 +01:00
parent 92a1d1a9ba
commit 06d5817076
10 changed files with 179 additions and 34 deletions

View File

@@ -48,15 +48,18 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
splitter->setStretchFactor(1, 2); splitter->setStretchFactor(1, 2);
auto *box = new QVBoxLayout; auto *box = new QVBoxLayout;
box->addWidget(createToolBar()); box->addWidget(createToolBar(model));
box->addWidget(splitter); box->addWidget(splitter);
setLayout(box); setLayout(box);
connect(m_tree, &TreeView::treeItemLocked, m_view, &GraphicsView::setLocked);
connect(m_tree->selectionModel(), &SelectionModel::curvesSelected, m_view, &GraphicsView::reset);
connect(m_tree, &TreeView::treeItemLocked, model, &CurveEditorModel::curveChanged); connect(m_tree, &TreeView::treeItemLocked, model, &CurveEditorModel::curveChanged);
connect(m_tree, &TreeView::treeItemPinned, model, &CurveEditorModel::curveChanged); connect(m_tree, &TreeView::treeItemPinned, model, &CurveEditorModel::curveChanged);
connect(m_tree, &TreeView::treeItemLocked, m_view, &GraphicsView::setLocked);
connect(m_tree->selectionModel(),
&SelectionModel::curvesSelected,
m_view,
&GraphicsView::updateSelection);
} }
void CurveEditor::zoomX(double zoom) void CurveEditor::zoomX(double zoom)
@@ -74,7 +77,7 @@ void CurveEditor::clearCanvas()
m_view->reset({}); m_view->reset({});
} }
QToolBar *CurveEditor::createToolBar() QToolBar *CurveEditor::createToolBar(CurveEditorModel *model)
{ {
auto *bar = new QToolBar; auto *bar = new QToolBar;
bar->setFloatable(false); bar->setFloatable(false);
@@ -111,8 +114,38 @@ QToolBar *CurveEditor::createToolBar()
bar->addWidget(valueWidget); bar->addWidget(valueWidget);
auto *durationBox = new QHBoxLayout; auto *durationBox = new QHBoxLayout;
durationBox->addWidget(new QLabel(tr("Duration"))); auto *startSpin = new QSpinBox;
durationBox->addWidget(new QSpinBox); auto *endSpin = new QSpinBox;
startSpin->setRange(std::numeric_limits<int>::lowest(), std::numeric_limits<int>::max());
startSpin->setValue(model->minimumTime());
auto updateStartFrame = [this, model](int frame) {
model->setMinimumTime(frame, false);
m_view->viewport()->update();
};
connect(startSpin, QOverload<int>::of(&QSpinBox::valueChanged), updateStartFrame);
endSpin->setRange(std::numeric_limits<int>::lowest(), std::numeric_limits<int>::max());
endSpin->setValue(model->maximumTime());
auto updateEndFrame = [this, model](int frame) {
model->setMaximumTime(frame, false);
m_view->viewport()->update();
};
connect(endSpin, QOverload<int>::of(&QSpinBox::valueChanged), updateEndFrame);
auto setStartSlot = [startSpin](int frame) { startSpin->setValue(frame); };
connect(model, &CurveEditorModel::updateStartFrame, setStartSlot);
auto setEndSlot = [endSpin](int frame) { endSpin->setValue(frame); };
connect(model, &CurveEditorModel::updateEndFrame, setEndSlot);
durationBox->addWidget(new QLabel(tr("Start Frame")));
durationBox->addWidget(startSpin);
durationBox->addWidget(new QLabel(tr("End Frame")));
durationBox->addWidget(endSpin);
auto *durationWidget = new QWidget; auto *durationWidget = new QWidget;
durationWidget->setLayout(durationBox); durationWidget->setLayout(durationBox);
bar->addWidget(durationWidget); bar->addWidget(durationWidget);

View File

@@ -48,7 +48,7 @@ public:
void clearCanvas(); void clearCanvas();
private: private:
QToolBar *createToolBar(); QToolBar *createToolBar(CurveEditorModel *model);
TreeView *m_tree; TreeView *m_tree;

View File

@@ -25,13 +25,16 @@
#include "curveeditormodel.h" #include "curveeditormodel.h"
#include "treeitem.h" #include "treeitem.h"
#include "detail/graphicsview.h" #include "detail/graphicsview.h"
#include "detail/selectionmodel.h" #include "detail/selectionmodel.h"
namespace DesignTools { namespace DesignTools {
CurveEditorModel::CurveEditorModel(QObject *parent) CurveEditorModel::CurveEditorModel(double minTime, double maxTime, QObject *parent)
: TreeModel(parent) : TreeModel(parent)
, m_minTime(minTime)
, m_maxTime(maxTime)
{} {}
CurveEditorModel::~CurveEditorModel() {} CurveEditorModel::~CurveEditorModel() {}
@@ -42,6 +45,24 @@ void CurveEditorModel::setCurrentFrame(int frame)
graphicsView()->setCurrentFrame(frame); graphicsView()->setCurrentFrame(frame);
} }
void CurveEditorModel::setMinimumTime(double time, bool internal)
{
m_minTime = time;
if (internal)
emit updateStartFrame(m_minTime);
else
emit startFrameChanged(m_minTime);
}
void CurveEditorModel::setMaximumTime(double time, bool internal)
{
m_maxTime = time;
if (internal)
emit updateEndFrame(m_maxTime);
else
emit endFrameChanged(m_maxTime);
}
void CurveEditorModel::setCurve(unsigned int id, const AnimationCurve &curve) void CurveEditorModel::setCurve(unsigned int id, const AnimationCurve &curve)
{ {
if (TreeItem *item = find(id)) { if (TreeItem *item = find(id)) {

View File

@@ -48,6 +48,14 @@ class CurveEditorModel : public TreeModel
signals: signals:
void currentFrameChanged(int frame); void currentFrameChanged(int frame);
void startFrameChanged(int frame);
void endFrameChanged(int frame);
void updateStartFrame(int frame);
void updateEndFrame(int frame);
void curveChanged(PropertyTreeItem *item); void curveChanged(PropertyTreeItem *item);
public: public:
@@ -58,15 +66,24 @@ public:
virtual CurveEditorStyle style() const = 0; virtual CurveEditorStyle style() const = 0;
public: public:
CurveEditorModel(QObject *parent = nullptr); CurveEditorModel(double minTime, double maxTime, QObject *parent = nullptr);
~CurveEditorModel() override; ~CurveEditorModel() override;
void setCurrentFrame(int frame); void setCurrentFrame(int frame);
void setMinimumTime(double time, bool internal);
void setMaximumTime(double time, bool internal);
void setCurve(unsigned int id, const AnimationCurve &curve); void setCurve(unsigned int id, const AnimationCurve &curve);
void reset(const std::vector<TreeItem *> &items); void reset(const std::vector<TreeItem *> &items);
protected:
double m_minTime = 0.;
double m_maxTime = 0.;
}; };
} // End namespace DesignTools. } // End namespace DesignTools.

View File

@@ -230,6 +230,16 @@ void GraphicsView::scrollContent(double x, double y)
} }
void GraphicsView::reset(const std::vector<CurveItem *> &items) void GraphicsView::reset(const std::vector<CurveItem *> &items)
{
m_scene.clear();
for (auto *item : items)
m_scene.addCurveItem(item);
applyZoom(m_zoomX, m_zoomY);
viewport()->update();
}
void GraphicsView::updateSelection(const std::vector<CurveItem *> &items)
{ {
const std::vector<CurveItem *> pinnedItems = m_scene.takePinnedItems(); const std::vector<CurveItem *> pinnedItems = m_scene.takePinnedItems();
auto notPinned = [pinnedItems](CurveItem *item) { auto notPinned = [pinnedItems](CurveItem *item) {
@@ -541,6 +551,38 @@ void GraphicsView::drawExtremaY(QPainter *painter, const QRectF &rect)
} }
#endif #endif
void GraphicsView::drawRangeBar(QPainter *painter, const QRectF &rect)
{
QFontMetrics fm(painter->font());
QRectF labelRect = fm.boundingRect(QString("0"));
labelRect.moveCenter(rect.center());
qreal bTick = rect.bottom() - 2;
qreal tTick = labelRect.bottom() + 2;
QRectF activeRect = QRectF(QPointF(mapTimeToX(m_model->minimumTime()), tTick),
QPointF(mapTimeToX(m_model->maximumTime()), bTick));
QColor color = Qt::white;
color.setAlpha(30);
painter->fillRect(activeRect, color);
QColor handleColor(Qt::green);
painter->setBrush(handleColor);
painter->setPen(handleColor);
const qreal radius = 5.;
QRectF minHandle = rangeMinHandle(rect);
painter->drawRoundedRect(minHandle, radius, radius);
minHandle.setLeft(minHandle.center().x());
painter->fillRect(minHandle, Qt::green);
QRectF maxHandle = rangeMaxHandle(rect);
painter->drawRoundedRect(maxHandle, radius, radius);
maxHandle.setRight(maxHandle.center().x());
painter->fillRect(maxHandle, Qt::green);
}
void GraphicsView::drawTimeScale(QPainter *painter, const QRectF &rect) void GraphicsView::drawTimeScale(QPainter *painter, const QRectF &rect)
{ {
painter->save(); painter->save();
@@ -565,6 +607,8 @@ void GraphicsView::drawTimeScale(QPainter *painter, const QRectF &rect)
for (double i = minimumTime(); i <= maximumTime(); i += timeIncrement) for (double i = minimumTime(); i <= maximumTime(); i += timeIncrement)
paintLabeledTick(i); paintLabeledTick(i);
drawRangeBar(painter, rect);
painter->restore(); painter->restore();
} }
@@ -619,4 +663,28 @@ double GraphicsView::timeLabelInterval(QPainter *painter, double maxTime)
return deltaTime; return deltaTime;
} }
QRectF GraphicsView::rangeMinHandle(const QRectF &rect)
{
QRectF labelRect = fontMetrics().boundingRect(QString("0"));
labelRect.moveCenter(rect.center());
qreal top = rect.bottom() - 2;
qreal bottom = labelRect.bottom() + 2;
QSize size(10, top - bottom);
int leftHandleLeft = mapTimeToX(m_model->minimumTime()) - size.width();
return QRectF(QPointF(leftHandleLeft, bottom), size);
}
QRectF GraphicsView::rangeMaxHandle(const QRectF &rect)
{
QRectF labelRect = fontMetrics().boundingRect(QString("0"));
labelRect.moveCenter(rect.center());
qreal bottom = rect.bottom() - 2;
qreal top = labelRect.bottom() + 2;
return QRectF(QPointF(mapTimeToX(m_model->maximumTime()), bottom), QSize(10, top - bottom));
}
} // End namespace DesignTools. } // End namespace DesignTools.

View File

@@ -108,6 +108,8 @@ public:
void reset(const std::vector<CurveItem *> &items); void reset(const std::vector<CurveItem *> &items);
void updateSelection(const std::vector<CurveItem *> &items);
void setInterpolation(Keyframe::Interpolation interpol); void setInterpolation(Keyframe::Interpolation interpol);
protected: protected:
@@ -148,8 +150,14 @@ private:
void drawValueScale(QPainter *painter, const QRectF &rect); void drawValueScale(QPainter *painter, const QRectF &rect);
void drawRangeBar(QPainter *painter, const QRectF &rect);
double timeLabelInterval(QPainter *painter, double maxTime); double timeLabelInterval(QPainter *painter, double maxTime);
QRectF rangeMinHandle(const QRectF &rect);
QRectF rangeMaxHandle(const QRectF &rect);
private: private:
double m_zoomX; double m_zoomX;

View File

@@ -35,9 +35,7 @@
namespace QmlDesigner { namespace QmlDesigner {
AnimationCurveEditorModel::AnimationCurveEditorModel(double minTime, double maxTime) AnimationCurveEditorModel::AnimationCurveEditorModel(double minTime, double maxTime)
: CurveEditorModel() : CurveEditorModel(minTime, maxTime)
, m_minTime(minTime)
, m_maxTime(maxTime)
{} {}
AnimationCurveEditorModel::~AnimationCurveEditorModel() {} AnimationCurveEditorModel::~AnimationCurveEditorModel() {}
@@ -102,16 +100,6 @@ void AnimationCurveEditorModel::setTimeline(const QmlTimeline &timeline)
reset(items); reset(items);
} }
void AnimationCurveEditorModel::setMinimumTime(double time)
{
m_minTime = time;
}
void AnimationCurveEditorModel::setMaximumTime(double time)
{
m_maxTime = time;
}
DesignTools::ValueType typeFrom(const QmlTimelineKeyframeGroup &group) DesignTools::ValueType typeFrom(const QmlTimelineKeyframeGroup &group)
{ {
if (group.valueType() == TypeName("double") || group.valueType() == TypeName("real") if (group.valueType() == TypeName("double") || group.valueType() == TypeName("real")

View File

@@ -49,20 +49,12 @@ public:
void setTimeline(const QmlTimeline &timeline); void setTimeline(const QmlTimeline &timeline);
void setMinimumTime(double time);
void setMaximumTime(double time);
private: private:
DesignTools::TreeItem *createTopLevelItem(const QmlTimeline &timeline, const ModelNode &node); DesignTools::TreeItem *createTopLevelItem(const QmlTimeline &timeline, const ModelNode &node);
DesignTools::AnimationCurve createAnimationCurve(const QmlTimelineKeyframeGroup &group); DesignTools::AnimationCurve createAnimationCurve(const QmlTimelineKeyframeGroup &group);
DesignTools::AnimationCurve createDoubleCurve(const QmlTimelineKeyframeGroup &group); DesignTools::AnimationCurve createDoubleCurve(const QmlTimelineKeyframeGroup &group);
double m_minTime;
double m_maxTime;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -38,7 +38,6 @@
#include <variantproperty.h> #include <variantproperty.h>
#include <qmlstate.h> #include <qmlstate.h>
#include <qmltimeline.h> #include <qmltimeline.h>
#include <qmltimelinekeyframegroup.h>
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h> #include <coreplugin/actionmanager/command.h>
@@ -112,6 +111,23 @@ TimelineToolBar::TimelineToolBar(QWidget *parent)
&AnimationCurveEditorModel::currentFrameChanged, &AnimationCurveEditorModel::currentFrameChanged,
this, this,
&TimelineToolBar::currentFrameChanged); &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, connect(m_curveModel,
&AnimationCurveEditorModel::curveChanged, &AnimationCurveEditorModel::curveChanged,
this, this,
@@ -181,7 +197,7 @@ void TimelineToolBar::setCurrentTimeline(const QmlTimeline &timeline)
void TimelineToolBar::setStartFrame(qreal frame) void TimelineToolBar::setStartFrame(qreal frame)
{ {
m_curveModel->setMinimumTime(frame); m_curveModel->setMinimumTime(frame, true);
auto text = QString::number(frame, 'f', 0); auto text = QString::number(frame, 'f', 0);
m_firstFrame->setText(text); m_firstFrame->setText(text);
@@ -198,7 +214,7 @@ void TimelineToolBar::setCurrentFrame(qreal frame)
void TimelineToolBar::setEndFrame(qreal frame) void TimelineToolBar::setEndFrame(qreal frame)
{ {
m_curveModel->setMaximumTime(frame); m_curveModel->setMaximumTime(frame, true);
auto text = QString::number(frame, 'f', 0); auto text = QString::number(frame, 'f', 0);
m_lastFrame->setText(text); m_lastFrame->setText(text);

View File

@@ -459,6 +459,8 @@ void TimelineWidget::invalidateTimelineDuration(const QmlTimeline &timeline)
if (timelineView() && timelineView()->model()) { if (timelineView() && timelineView()->model()) {
QmlTimeline currentTimeline = graphicsScene()->currentTimeline(); QmlTimeline currentTimeline = graphicsScene()->currentTimeline();
if (currentTimeline.isValid() && currentTimeline == timeline) { if (currentTimeline.isValid() && currentTimeline == timeline) {
m_toolbar->setStartFrame(timeline.startKeyframe());
m_toolbar->setEndFrame(timeline.endKeyframe());
graphicsScene()->setTimeline(timeline); graphicsScene()->setTimeline(timeline);
qreal playHeadFrame = getcurrentFrame(timeline); qreal playHeadFrame = getcurrentFrame(timeline);