forked from qt-creator/qt-creator
Add the ability to unify keyframe handles
Task-number: QDS-568 Change-Id: I5b102423e8e166d41edf199c42305cee102e8b54 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -103,6 +103,15 @@ bool AnimationCurve::isFromData() const
|
||||
return m_fromData;
|
||||
}
|
||||
|
||||
bool AnimationCurve::hasUnified() const
|
||||
{
|
||||
for (auto &&frame : m_frames) {
|
||||
if (frame.isUnified())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
double AnimationCurve::minimumTime() const
|
||||
{
|
||||
if (!m_frames.empty())
|
||||
@@ -144,6 +153,18 @@ std::string AnimationCurve::string() const
|
||||
return sstream.str();
|
||||
}
|
||||
|
||||
QString AnimationCurve::unifyString() const
|
||||
{
|
||||
QString out;
|
||||
for (auto &&frame : m_frames) {
|
||||
if (frame.isUnified())
|
||||
out.append("1");
|
||||
else
|
||||
out.append("0");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
CurveSegment AnimationCurve::segment(double time) const
|
||||
{
|
||||
CurveSegment seg;
|
||||
|
@@ -50,6 +50,8 @@ public:
|
||||
|
||||
bool isFromData() const;
|
||||
|
||||
bool hasUnified() const;
|
||||
|
||||
double minimumTime() const;
|
||||
|
||||
double maximumTime() const;
|
||||
@@ -60,6 +62,8 @@ public:
|
||||
|
||||
std::string string() const;
|
||||
|
||||
QString unifyString() const;
|
||||
|
||||
CurveSegment segment(double time) const;
|
||||
|
||||
std::vector<CurveSegment> segments() const;
|
||||
|
@@ -86,6 +86,7 @@ QToolBar *CurveEditor::createToolBar(CurveEditorModel *model)
|
||||
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");
|
||||
|
||||
auto setLinearInterpolation = [this]() {
|
||||
m_view->setInterpolation(Keyframe::Interpolation::Linear);
|
||||
@@ -97,9 +98,12 @@ QToolBar *CurveEditor::createToolBar(CurveEditorModel *model)
|
||||
m_view->setInterpolation(Keyframe::Interpolation::Bezier);
|
||||
};
|
||||
|
||||
auto toggleUnifyKeyframe = [this]() { m_view->toggleUnified(); };
|
||||
|
||||
connect(tangentLinearAction, &QAction::triggered, setLinearInterpolation);
|
||||
connect(tangentStepAction, &QAction::triggered, setStepInterpolation);
|
||||
connect(tangentSplineAction, &QAction::triggered, setSplineInterpolation);
|
||||
connect(tangentUnifyAction, &QAction::triggered, toggleUnifyKeyframe);
|
||||
|
||||
Q_UNUSED(tangentLinearAction);
|
||||
Q_UNUSED(tangentSplineAction);
|
||||
|
@@ -62,6 +62,9 @@ struct KeyframeItemStyleOption
|
||||
double size = 10.0;
|
||||
QColor color = QColor(200, 200, 0);
|
||||
QColor selectionColor = QColor(200, 200, 200);
|
||||
QColor lockedColor = QColor(50, 50, 50);
|
||||
QColor unifiedColor = QColor(250, 50, 250);
|
||||
QColor splitColor = QColor(0, 250, 0);
|
||||
};
|
||||
|
||||
struct CurveItemStyleOption
|
||||
|
@@ -447,9 +447,11 @@ std::array<Keyframe, 3> CurveSegment::splitAt(double time)
|
||||
|
||||
out[0].setInterpolation(left().interpolation());
|
||||
out[0].setData(left().data());
|
||||
out[0].setUnified(left().isUnified());
|
||||
|
||||
out[2].setInterpolation(right().interpolation());
|
||||
out[2].setData(right().data());
|
||||
out[2].setUnified(right().isUnified());
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
@@ -297,6 +297,8 @@ void CurveItem::setCurve(const AnimationCurve &curve)
|
||||
item->setComponentTransform(m_transform);
|
||||
m_keyframes.push_back(item);
|
||||
QObject::connect(item, &KeyframeItem::redrawCurve, this, &CurveItem::emitCurveChanged);
|
||||
QObject::connect(item, &KeyframeItem::keyframeMoved, this, &CurveItem::keyframeMoved);
|
||||
QObject::connect(item, &KeyframeItem::handleMoved, this, &CurveItem::handleMoved);
|
||||
}
|
||||
|
||||
emitCurveChanged();
|
||||
@@ -344,14 +346,24 @@ void CurveItem::setInterpolation(Keyframe::Interpolation interpolation)
|
||||
emit curveChanged(id(), curve());
|
||||
}
|
||||
|
||||
void CurveItem::toggleUnified()
|
||||
{
|
||||
if (m_keyframes.empty())
|
||||
return;
|
||||
|
||||
for (auto *frame : m_keyframes) {
|
||||
if (frame->selected())
|
||||
frame->toggleUnified();
|
||||
}
|
||||
emit curveChanged(id(), curve());
|
||||
}
|
||||
|
||||
void CurveItem::connect(GraphicsScene *scene)
|
||||
{
|
||||
QObject::connect(this, &CurveItem::curveChanged, scene, &GraphicsScene::curveChanged);
|
||||
|
||||
for (auto *frame : m_keyframes) {
|
||||
QObject::connect(frame, &KeyframeItem::keyframeMoved, scene, &GraphicsScene::keyframeMoved);
|
||||
QObject::connect(frame, &KeyframeItem::handleMoved, scene, &GraphicsScene::handleMoved);
|
||||
}
|
||||
QObject::connect(this, &CurveItem::keyframeMoved, scene, &GraphicsScene::keyframeMoved);
|
||||
QObject::connect(this, &CurveItem::handleMoved, scene, &GraphicsScene::handleMoved);
|
||||
}
|
||||
|
||||
void CurveItem::insertKeyframeByTime(double time)
|
||||
|
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "curveeditorstyle.h"
|
||||
#include "curvesegment.h"
|
||||
#include "handleitem.h"
|
||||
#include "keyframe.h"
|
||||
#include "selectableitem.h"
|
||||
#include "treeitem.h"
|
||||
@@ -47,6 +48,10 @@ class CurveItem : public CurveEditorItem
|
||||
signals:
|
||||
void curveChanged(unsigned int id, const AnimationCurve &curve);
|
||||
|
||||
void keyframeMoved(KeyframeItem *item, const QPointF &direction);
|
||||
|
||||
void handleMoved(KeyframeItem *frame, HandleItem::Slot slot, double angle, double deltaLength);
|
||||
|
||||
public:
|
||||
CurveItem(QGraphicsItem *parent = nullptr);
|
||||
|
||||
@@ -100,6 +105,8 @@ public:
|
||||
|
||||
void setInterpolation(Keyframe::Interpolation interpolation);
|
||||
|
||||
void toggleUnified();
|
||||
|
||||
void connect(GraphicsScene *scene);
|
||||
|
||||
void insertKeyframeByTime(double time);
|
||||
|
@@ -37,6 +37,7 @@ GraphicsScene::GraphicsScene(QObject *parent)
|
||||
: QGraphicsScene(parent)
|
||||
, m_dirty(true)
|
||||
, m_limits()
|
||||
, m_doNotMoveItems(false)
|
||||
{}
|
||||
|
||||
bool GraphicsScene::empty() const
|
||||
@@ -64,6 +65,11 @@ double GraphicsScene::maximumValue() const
|
||||
return limits().top();
|
||||
}
|
||||
|
||||
void GraphicsScene::doNotMoveItems(bool val)
|
||||
{
|
||||
m_doNotMoveItems = val;
|
||||
}
|
||||
|
||||
void GraphicsScene::addCurveItem(CurveItem *item)
|
||||
{
|
||||
m_dirty = true;
|
||||
@@ -111,10 +117,9 @@ void GraphicsScene::handleUnderMouse(HandleItem *handle)
|
||||
if (item == handle)
|
||||
continue;
|
||||
|
||||
if (auto *handleItem = qgraphicsitem_cast<HandleItem *>(item)) {
|
||||
if (handleItem->selected()) {
|
||||
if (handleItem->slot() == handle->slot())
|
||||
handleItem->setActivated(handle->isUnderMouse());
|
||||
if (auto *keyItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
|
||||
if (keyItem->selected()) {
|
||||
keyItem->setActivated(handle->isUnderMouse(), handle->slot());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,14 +130,29 @@ void GraphicsScene::handleMoved(KeyframeItem *frame,
|
||||
double angle,
|
||||
double deltaLength)
|
||||
{
|
||||
if (m_doNotMoveItems)
|
||||
return;
|
||||
|
||||
auto moveUnified = [handle, angle, deltaLength](KeyframeItem *key) {
|
||||
if (key->isUnified()) {
|
||||
if (handle == HandleItem::Slot::Left)
|
||||
key->moveHandle(HandleItem::Slot::Right, angle, deltaLength);
|
||||
else
|
||||
key->moveHandle(HandleItem::Slot::Left, angle, deltaLength);
|
||||
}
|
||||
};
|
||||
|
||||
const auto itemList = items();
|
||||
for (auto *item : itemList) {
|
||||
if (item == frame)
|
||||
continue;
|
||||
|
||||
if (auto *frameItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
|
||||
if (frameItem->selected())
|
||||
if (item == frame) {
|
||||
moveUnified(frameItem);
|
||||
continue;
|
||||
}
|
||||
if (frameItem->selected()) {
|
||||
frameItem->moveHandle(handle, angle, deltaLength);
|
||||
moveUnified(frameItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -61,6 +61,8 @@ public:
|
||||
|
||||
double maximumValue() const;
|
||||
|
||||
void doNotMoveItems(bool tmp);
|
||||
|
||||
void addCurveItem(CurveItem *item);
|
||||
|
||||
void setComponentTransform(const QTransform &transform);
|
||||
@@ -90,6 +92,8 @@ private:
|
||||
mutable bool m_dirty;
|
||||
|
||||
mutable QRectF m_limits;
|
||||
|
||||
bool m_doNotMoveItems;
|
||||
};
|
||||
|
||||
} // End namespace DesignTools.
|
||||
|
@@ -275,6 +275,18 @@ void GraphicsView::setInterpolation(Keyframe::Interpolation interpol)
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
void GraphicsView::toggleUnified()
|
||||
{
|
||||
const auto itemList = items();
|
||||
for (auto *item : itemList) {
|
||||
if (auto *citem = qgraphicsitem_cast<CurveItem *>(item)) {
|
||||
if (citem->hasSelection())
|
||||
citem->toggleUnified();
|
||||
}
|
||||
}
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
void GraphicsView::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QGraphicsView::resizeEvent(event);
|
||||
@@ -433,6 +445,8 @@ QPointF GraphicsView::globalToRaster(const QPoint &point) const
|
||||
|
||||
void GraphicsView::applyZoom(double x, double y, const QPoint &pivot)
|
||||
{
|
||||
m_scene.doNotMoveItems(true);
|
||||
|
||||
QPointF pivotRaster(globalToRaster(pivot));
|
||||
|
||||
m_zoomX = clamp(x, 0.0, 1.0);
|
||||
@@ -471,6 +485,8 @@ void GraphicsView::applyZoom(double x, double y, const QPoint &pivot)
|
||||
QPointF deltaTransformed = pivotRaster - globalToRaster(pivot);
|
||||
scrollContent(mapTimeToX(deltaTransformed.x()), mapValueToY(deltaTransformed.y()));
|
||||
}
|
||||
|
||||
m_scene.doNotMoveItems(false);
|
||||
}
|
||||
|
||||
void GraphicsView::insertKeyframe(double time, bool allVisibleCurves)
|
||||
|
@@ -112,6 +112,8 @@ public:
|
||||
|
||||
void setInterpolation(Keyframe::Interpolation interpol);
|
||||
|
||||
void toggleUnified();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
|
@@ -130,7 +130,7 @@ void HandleItem::setStyle(const CurveEditorStyle &style)
|
||||
QVariant HandleItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
|
||||
{
|
||||
if (change == ItemPositionChange) {
|
||||
if (KeyframeItem *parent = qgraphicsitem_cast<KeyframeItem *>(parentItem())) {
|
||||
if (qgraphicsitem_cast<KeyframeItem *>(parentItem())) {
|
||||
QPointF pos = value.toPointF();
|
||||
if (m_slot == HandleItem::Slot::Left) {
|
||||
if (pos.x() > 0.0)
|
||||
|
@@ -64,14 +64,22 @@ void KeyframeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
|
||||
Q_UNUSED(option)
|
||||
Q_UNUSED(widget)
|
||||
|
||||
QColor mainColor = selected() ? m_style.selectionColor : m_style.color;
|
||||
QColor borderColor = isUnified() ? m_style.unifiedColor : m_style.splitColor;
|
||||
|
||||
if (locked()) {
|
||||
mainColor = m_style.lockedColor;
|
||||
borderColor = m_style.lockedColor;
|
||||
}
|
||||
|
||||
QPen pen = painter->pen();
|
||||
pen.setColor(Qt::black);
|
||||
pen.setWidthF(1.);
|
||||
pen.setColor(borderColor);
|
||||
|
||||
painter->save();
|
||||
painter->setPen(pen);
|
||||
painter->setBrush(locked() ? Qt::black : (selected() ? Qt::red : m_style.color));
|
||||
painter->setBrush(mainColor);
|
||||
painter->drawEllipse(boundingRect());
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
@@ -93,6 +101,11 @@ Keyframe KeyframeItem::keyframe() const
|
||||
return m_frame;
|
||||
}
|
||||
|
||||
bool KeyframeItem::isUnified() const
|
||||
{
|
||||
return m_frame.isUnified();
|
||||
}
|
||||
|
||||
bool KeyframeItem::hasLeftHandle() const
|
||||
{
|
||||
return m_frame.hasLeftHandle();
|
||||
@@ -108,11 +121,6 @@ QTransform KeyframeItem::transform() const
|
||||
return m_transform;
|
||||
}
|
||||
|
||||
bool KeyframeItem::contains(HandleItem *handle, const QPointF &point) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void KeyframeItem::setHandleVisibility(bool visible)
|
||||
{
|
||||
m_visibleOverride = visible;
|
||||
@@ -195,6 +203,31 @@ void KeyframeItem::setKeyframe(const Keyframe &keyframe)
|
||||
setPos(m_transform.map(m_frame.position()));
|
||||
}
|
||||
|
||||
void KeyframeItem::toggleUnified()
|
||||
{
|
||||
if (!m_left || !m_right)
|
||||
return;
|
||||
|
||||
if (m_frame.isUnified())
|
||||
m_frame.setUnified(false);
|
||||
else
|
||||
m_frame.setUnified(true);
|
||||
}
|
||||
|
||||
void KeyframeItem::setActivated(bool active, HandleItem::Slot slot)
|
||||
{
|
||||
if (isUnified() && m_left && m_right) {
|
||||
m_left->setActivated(active);
|
||||
m_right->setActivated(active);
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot == HandleItem::Slot::Left && m_left)
|
||||
m_left->setActivated(active);
|
||||
else if (slot == HandleItem::Slot::Right && m_right)
|
||||
m_right->setActivated(active);
|
||||
}
|
||||
|
||||
void KeyframeItem::setInterpolation(Keyframe::Interpolation interpolation)
|
||||
{
|
||||
m_frame.setInterpolation(interpolation);
|
||||
|
@@ -66,14 +66,14 @@ public:
|
||||
|
||||
Keyframe keyframe() const;
|
||||
|
||||
bool isUnified() const;
|
||||
|
||||
bool hasLeftHandle() const;
|
||||
|
||||
bool hasRightHandle() const;
|
||||
|
||||
QTransform transform() const;
|
||||
|
||||
bool contains(HandleItem *handle, const QPointF &point) const;
|
||||
|
||||
void setHandleVisibility(bool visible);
|
||||
|
||||
void setComponentTransform(const QTransform &transform);
|
||||
@@ -82,6 +82,10 @@ public:
|
||||
|
||||
void setKeyframe(const Keyframe &keyframe);
|
||||
|
||||
void toggleUnified();
|
||||
|
||||
void setActivated(bool active, HandleItem::Slot slot);
|
||||
|
||||
void setInterpolation(Keyframe::Interpolation interpolation);
|
||||
|
||||
void setLeftHandle(const QPointF &pos);
|
||||
|
@@ -31,6 +31,7 @@ namespace DesignTools {
|
||||
|
||||
Keyframe::Keyframe()
|
||||
: m_interpolation(Interpolation::Undefined)
|
||||
, m_unified(false)
|
||||
, m_position()
|
||||
, m_leftHandle()
|
||||
, m_rightHandle()
|
||||
@@ -39,6 +40,7 @@ Keyframe::Keyframe()
|
||||
|
||||
Keyframe::Keyframe(const QPointF &position)
|
||||
: m_interpolation(Interpolation::Linear)
|
||||
, m_unified(false)
|
||||
, m_position(position)
|
||||
, m_leftHandle()
|
||||
, m_rightHandle()
|
||||
@@ -47,6 +49,7 @@ Keyframe::Keyframe(const QPointF &position)
|
||||
|
||||
Keyframe::Keyframe(const QPointF &position, const QVariant &data)
|
||||
: m_interpolation(Interpolation::Undefined)
|
||||
, m_unified(false)
|
||||
, m_position(position)
|
||||
, m_leftHandle()
|
||||
, m_rightHandle()
|
||||
@@ -57,6 +60,7 @@ Keyframe::Keyframe(const QPointF &position, const QVariant &data)
|
||||
|
||||
Keyframe::Keyframe(const QPointF &position, const QPointF &leftHandle, const QPointF &rightHandle)
|
||||
: m_interpolation(Interpolation::Bezier)
|
||||
, m_unified(false)
|
||||
, m_position(position)
|
||||
, m_leftHandle(leftHandle)
|
||||
, m_rightHandle(rightHandle)
|
||||
@@ -73,6 +77,11 @@ bool Keyframe::hasData() const
|
||||
return m_data.isValid();
|
||||
}
|
||||
|
||||
bool Keyframe::isUnified() const
|
||||
{
|
||||
return m_unified;
|
||||
}
|
||||
|
||||
bool Keyframe::hasLeftHandle() const
|
||||
{
|
||||
return !m_leftHandle.isNull();
|
||||
@@ -143,6 +152,11 @@ void Keyframe::setPosition(const QPointF &pos)
|
||||
m_position = pos;
|
||||
}
|
||||
|
||||
void Keyframe::setUnified(bool unified)
|
||||
{
|
||||
m_unified = unified;
|
||||
}
|
||||
|
||||
void Keyframe::setLeftHandle(const QPointF &pos)
|
||||
{
|
||||
m_leftHandle = pos;
|
||||
|
@@ -47,6 +47,8 @@ public:
|
||||
|
||||
bool hasData() const;
|
||||
|
||||
bool isUnified() const;
|
||||
|
||||
bool hasLeftHandle() const;
|
||||
|
||||
bool hasRightHandle() const;
|
||||
@@ -63,6 +65,8 @@ public:
|
||||
|
||||
Interpolation interpolation() const;
|
||||
|
||||
void setUnified(bool unified);
|
||||
|
||||
void setPosition(const QPointF &pos);
|
||||
|
||||
void setLeftHandle(const QPointF &pos);
|
||||
@@ -76,6 +80,8 @@ public:
|
||||
private:
|
||||
Interpolation m_interpolation = Interpolation::Undefined;
|
||||
|
||||
bool m_unified;
|
||||
|
||||
QPointF m_position;
|
||||
|
||||
QPointF m_leftHandle;
|
||||
|
@@ -311,6 +311,16 @@ AnimationCurve PropertyTreeItem::curve() const
|
||||
return m_curve;
|
||||
}
|
||||
|
||||
bool PropertyTreeItem::hasUnified() const
|
||||
{
|
||||
return m_curve.hasUnified();
|
||||
}
|
||||
|
||||
QString PropertyTreeItem::unifyString() const
|
||||
{
|
||||
return m_curve.unifyString();
|
||||
}
|
||||
|
||||
void PropertyTreeItem::setCurve(const AnimationCurve &curve)
|
||||
{
|
||||
m_curve = curve;
|
||||
|
@@ -155,6 +155,10 @@ public:
|
||||
|
||||
AnimationCurve curve() const;
|
||||
|
||||
bool hasUnified() const;
|
||||
|
||||
QString unifyString() const;
|
||||
|
||||
void setCurve(const AnimationCurve &curve);
|
||||
|
||||
void setComponent(const Component &comp);
|
||||
|
@@ -230,6 +230,19 @@ DesignTools::AnimationCurve AnimationCurveEditorModel::createDoubleCurve(
|
||||
{
|
||||
std::vector<DesignTools::Keyframe> 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<int>(keyframes.size())) {
|
||||
for (int i = 0; i < str.size(); ++i) {
|
||||
if (str.at(i) == '1')
|
||||
keyframes[i].setUnified(true);
|
||||
}
|
||||
}
|
||||
|
||||
return DesignTools::AnimationCurve(keyframes);
|
||||
}
|
||||
|
||||
|
@@ -253,11 +253,6 @@ void TimelineToolBar::openAnimationCurveEditor()
|
||||
m_dialog->show();
|
||||
}
|
||||
|
||||
void TimelineToolBar::updateCurve(DesignTools::PropertyTreeItem *item)
|
||||
{
|
||||
DesignTools::AnimationCurve curve = item->curve();
|
||||
}
|
||||
|
||||
void TimelineToolBar::createLeftControls()
|
||||
{
|
||||
auto addActionToGroup = [&](QAction *action) {
|
||||
|
@@ -91,8 +91,6 @@ public:
|
||||
|
||||
void openAnimationCurveEditor();
|
||||
|
||||
void updateCurve(DesignTools::PropertyTreeItem *item);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
|
@@ -340,6 +340,11 @@ void TimelineWidget::updateAnimationCurve(DesignTools::PropertyTreeItem *item)
|
||||
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]() {
|
||||
|
Reference in New Issue
Block a user