forked from qt-creator/qt-creator
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:
@@ -48,15 +48,18 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
|
||||
splitter->setStretchFactor(1, 2);
|
||||
|
||||
auto *box = new QVBoxLayout;
|
||||
box->addWidget(createToolBar());
|
||||
box->addWidget(createToolBar(model));
|
||||
box->addWidget(splitter);
|
||||
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::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)
|
||||
@@ -74,7 +77,7 @@ void CurveEditor::clearCanvas()
|
||||
m_view->reset({});
|
||||
}
|
||||
|
||||
QToolBar *CurveEditor::createToolBar()
|
||||
QToolBar *CurveEditor::createToolBar(CurveEditorModel *model)
|
||||
{
|
||||
auto *bar = new QToolBar;
|
||||
bar->setFloatable(false);
|
||||
@@ -111,8 +114,38 @@ QToolBar *CurveEditor::createToolBar()
|
||||
bar->addWidget(valueWidget);
|
||||
|
||||
auto *durationBox = new QHBoxLayout;
|
||||
durationBox->addWidget(new QLabel(tr("Duration")));
|
||||
durationBox->addWidget(new QSpinBox);
|
||||
auto *startSpin = 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;
|
||||
durationWidget->setLayout(durationBox);
|
||||
bar->addWidget(durationWidget);
|
||||
|
@@ -48,7 +48,7 @@ public:
|
||||
void clearCanvas();
|
||||
|
||||
private:
|
||||
QToolBar *createToolBar();
|
||||
QToolBar *createToolBar(CurveEditorModel *model);
|
||||
|
||||
TreeView *m_tree;
|
||||
|
||||
|
@@ -25,13 +25,16 @@
|
||||
|
||||
#include "curveeditormodel.h"
|
||||
#include "treeitem.h"
|
||||
|
||||
#include "detail/graphicsview.h"
|
||||
#include "detail/selectionmodel.h"
|
||||
|
||||
namespace DesignTools {
|
||||
|
||||
CurveEditorModel::CurveEditorModel(QObject *parent)
|
||||
CurveEditorModel::CurveEditorModel(double minTime, double maxTime, QObject *parent)
|
||||
: TreeModel(parent)
|
||||
, m_minTime(minTime)
|
||||
, m_maxTime(maxTime)
|
||||
{}
|
||||
|
||||
CurveEditorModel::~CurveEditorModel() {}
|
||||
@@ -42,6 +45,24 @@ void CurveEditorModel::setCurrentFrame(int 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)
|
||||
{
|
||||
if (TreeItem *item = find(id)) {
|
||||
|
@@ -48,6 +48,14 @@ class CurveEditorModel : public TreeModel
|
||||
signals:
|
||||
void currentFrameChanged(int frame);
|
||||
|
||||
void startFrameChanged(int frame);
|
||||
|
||||
void endFrameChanged(int frame);
|
||||
|
||||
void updateStartFrame(int frame);
|
||||
|
||||
void updateEndFrame(int frame);
|
||||
|
||||
void curveChanged(PropertyTreeItem *item);
|
||||
|
||||
public:
|
||||
@@ -58,15 +66,24 @@ public:
|
||||
virtual CurveEditorStyle style() const = 0;
|
||||
|
||||
public:
|
||||
CurveEditorModel(QObject *parent = nullptr);
|
||||
CurveEditorModel(double minTime, double maxTime, QObject *parent = nullptr);
|
||||
|
||||
~CurveEditorModel() override;
|
||||
|
||||
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 reset(const std::vector<TreeItem *> &items);
|
||||
|
||||
protected:
|
||||
double m_minTime = 0.;
|
||||
|
||||
double m_maxTime = 0.;
|
||||
};
|
||||
|
||||
} // End namespace DesignTools.
|
||||
|
@@ -230,6 +230,16 @@ void GraphicsView::scrollContent(double x, double y)
|
||||
}
|
||||
|
||||
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();
|
||||
auto notPinned = [pinnedItems](CurveItem *item) {
|
||||
@@ -541,6 +551,38 @@ void GraphicsView::drawExtremaY(QPainter *painter, const QRectF &rect)
|
||||
}
|
||||
#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)
|
||||
{
|
||||
painter->save();
|
||||
@@ -565,6 +607,8 @@ void GraphicsView::drawTimeScale(QPainter *painter, const QRectF &rect)
|
||||
for (double i = minimumTime(); i <= maximumTime(); i += timeIncrement)
|
||||
paintLabeledTick(i);
|
||||
|
||||
drawRangeBar(painter, rect);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
@@ -619,4 +663,28 @@ double GraphicsView::timeLabelInterval(QPainter *painter, double maxTime)
|
||||
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.
|
||||
|
@@ -108,6 +108,8 @@ public:
|
||||
|
||||
void reset(const std::vector<CurveItem *> &items);
|
||||
|
||||
void updateSelection(const std::vector<CurveItem *> &items);
|
||||
|
||||
void setInterpolation(Keyframe::Interpolation interpol);
|
||||
|
||||
protected:
|
||||
@@ -148,8 +150,14 @@ private:
|
||||
|
||||
void drawValueScale(QPainter *painter, const QRectF &rect);
|
||||
|
||||
void drawRangeBar(QPainter *painter, const QRectF &rect);
|
||||
|
||||
double timeLabelInterval(QPainter *painter, double maxTime);
|
||||
|
||||
QRectF rangeMinHandle(const QRectF &rect);
|
||||
|
||||
QRectF rangeMaxHandle(const QRectF &rect);
|
||||
|
||||
private:
|
||||
double m_zoomX;
|
||||
|
||||
|
@@ -35,9 +35,7 @@
|
||||
namespace QmlDesigner {
|
||||
|
||||
AnimationCurveEditorModel::AnimationCurveEditorModel(double minTime, double maxTime)
|
||||
: CurveEditorModel()
|
||||
, m_minTime(minTime)
|
||||
, m_maxTime(maxTime)
|
||||
: CurveEditorModel(minTime, maxTime)
|
||||
{}
|
||||
|
||||
AnimationCurveEditorModel::~AnimationCurveEditorModel() {}
|
||||
@@ -102,16 +100,6 @@ void AnimationCurveEditorModel::setTimeline(const QmlTimeline &timeline)
|
||||
reset(items);
|
||||
}
|
||||
|
||||
void AnimationCurveEditorModel::setMinimumTime(double time)
|
||||
{
|
||||
m_minTime = time;
|
||||
}
|
||||
|
||||
void AnimationCurveEditorModel::setMaximumTime(double time)
|
||||
{
|
||||
m_maxTime = time;
|
||||
}
|
||||
|
||||
DesignTools::ValueType typeFrom(const QmlTimelineKeyframeGroup &group)
|
||||
{
|
||||
if (group.valueType() == TypeName("double") || group.valueType() == TypeName("real")
|
||||
|
@@ -49,20 +49,12 @@ public:
|
||||
|
||||
void setTimeline(const QmlTimeline &timeline);
|
||||
|
||||
void setMinimumTime(double time);
|
||||
|
||||
void setMaximumTime(double time);
|
||||
|
||||
private:
|
||||
DesignTools::TreeItem *createTopLevelItem(const QmlTimeline &timeline, const ModelNode &node);
|
||||
|
||||
DesignTools::AnimationCurve createAnimationCurve(const QmlTimelineKeyframeGroup &group);
|
||||
|
||||
DesignTools::AnimationCurve createDoubleCurve(const QmlTimelineKeyframeGroup &group);
|
||||
|
||||
double m_minTime;
|
||||
|
||||
double m_maxTime;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -38,7 +38,6 @@
|
||||
#include <variantproperty.h>
|
||||
#include <qmlstate.h>
|
||||
#include <qmltimeline.h>
|
||||
#include <qmltimelinekeyframegroup.h>
|
||||
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/actionmanager/command.h>
|
||||
@@ -112,6 +111,23 @@ TimelineToolBar::TimelineToolBar(QWidget *parent)
|
||||
&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,
|
||||
@@ -181,7 +197,7 @@ void TimelineToolBar::setCurrentTimeline(const QmlTimeline &timeline)
|
||||
|
||||
void TimelineToolBar::setStartFrame(qreal frame)
|
||||
{
|
||||
m_curveModel->setMinimumTime(frame);
|
||||
m_curveModel->setMinimumTime(frame, true);
|
||||
|
||||
auto text = QString::number(frame, 'f', 0);
|
||||
m_firstFrame->setText(text);
|
||||
@@ -198,7 +214,7 @@ void TimelineToolBar::setCurrentFrame(qreal frame)
|
||||
|
||||
void TimelineToolBar::setEndFrame(qreal frame)
|
||||
{
|
||||
m_curveModel->setMaximumTime(frame);
|
||||
m_curveModel->setMaximumTime(frame, true);
|
||||
|
||||
auto text = QString::number(frame, 'f', 0);
|
||||
m_lastFrame->setText(text);
|
||||
|
@@ -459,6 +459,8 @@ void TimelineWidget::invalidateTimelineDuration(const QmlTimeline &timeline)
|
||||
if (timelineView() && timelineView()->model()) {
|
||||
QmlTimeline currentTimeline = graphicsScene()->currentTimeline();
|
||||
if (currentTimeline.isValid() && currentTimeline == timeline) {
|
||||
m_toolbar->setStartFrame(timeline.startKeyframe());
|
||||
m_toolbar->setEndFrame(timeline.endKeyframe());
|
||||
graphicsScene()->setTimeline(timeline);
|
||||
|
||||
qreal playHeadFrame = getcurrentFrame(timeline);
|
||||
|
Reference in New Issue
Block a user