Implement keyframes snapping

When shift is down while dragging keyframe(s), they snap to ruler ticks,
keyframes, and the playhead.

Task-number: QDS-1068
Change-Id: Iea5fec9e578d3f1db51c429cbd565ad145a90fe8
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Mahmoud Badri
2019-10-03 11:51:13 +03:00
parent 69719e7b3e
commit 2c2ffa8273
3 changed files with 31 additions and 15 deletions

View File

@@ -181,10 +181,10 @@ void TimelineGraphicsScene::updateKeyframePositionsCache()
}
// snap a frame to nearest keyframe or ruler tick
qreal TimelineGraphicsScene::snap(qreal frame)
qreal TimelineGraphicsScene::snap(qreal frame, bool snapToPlayhead)
{
qreal frameTick = m_layout->ruler()->getFrameTick();
qreal rulerTicksSnapframe = qRound(frame / frameTick) * frameTick;
qreal rulerFrameTick = m_layout->ruler()->getFrameTick();
qreal nearestRulerTickFrame = qRound(frame / rulerFrameTick) * rulerFrameTick;
// get nearest keyframe to the input frame
bool nearestKeyframeFound = false;
qreal nearestKeyframe = 0;
@@ -202,23 +202,30 @@ qreal TimelineGraphicsScene::snap(qreal frame)
}
}
if (!nearestKeyframeFound && !m_keyframePositionsCache.empty()) {
// playhead past last keyframe case
// playhead past last keyframe case
if (!nearestKeyframeFound && !m_keyframePositionsCache.empty())
nearestKeyframe = m_keyframePositionsCache.last();
nearestKeyframeFound = true;
}
// return nearest snappable keyframe or ruler tick
return nearestKeyframeFound && qAbs(nearestKeyframe - frame)
< qAbs(rulerTicksSnapframe - frame) ? nearestKeyframe
: rulerTicksSnapframe;
qreal playheadFrame = m_currentFrameIndicator->position();
qreal dKeyframe = qAbs(nearestKeyframe - frame);
qreal dPlayhead = snapToPlayhead ? qAbs(playheadFrame - frame) : 99999.;
qreal dRulerTick = qAbs(nearestRulerTickFrame - frame);
if (dKeyframe <= qMin(dPlayhead, dRulerTick))
return nearestKeyframe;
if (dRulerTick <= dPlayhead)
return nearestRulerTickFrame;
return playheadFrame;
}
void TimelineGraphicsScene::setCurrenFrame(const QmlTimeline &timeline, qreal frame)
{
if (timeline.isValid()) {
if (QApplication::keyboardModifiers() & Qt::ShiftModifier) // playhead snapping
frame = snap(frame);
frame = snap(frame, false);
m_currentFrameIndicator->setPosition(frame);
} else {
m_currentFrameIndicator->setPosition(0);

View File

@@ -97,6 +97,8 @@ public:
QVector<qreal> keyframePositions() const;
QVector<qreal> keyframePositions(const QmlTimelineKeyframeGroup &frames) const;
qreal snap(qreal frame, bool snapToPlayhead = true);
void setRulerScaling(int scaling);
void commitCurrentFrame(qreal frame);
@@ -167,7 +169,6 @@ private:
QList<QGraphicsItem *> itemsAt(const QPointF &pos);
private:
qreal snap(qreal frame);
TimelineWidget *m_parent = nullptr;

View File

@@ -90,7 +90,7 @@ void TimelineMoveTool::mouseMoveEvent(TimelineMovableAbstractItem *item,
return;
const qreal sourceFrame = qRound(current->mapFromSceneToFrame(current->rect().center().x()));
const qreal targetFrame = qRound(current->mapFromSceneToFrame(event->scenePos().x()));
qreal targetFrame = qRound(current->mapFromSceneToFrame(event->scenePos().x()));
qreal deltaFrame = targetFrame - sourceFrame - m_pressKeyframeDelta;
const qreal minFrame = scene()->startFrame();
@@ -106,8 +106,16 @@ void TimelineMoveTool::mouseMoveEvent(TimelineMovableAbstractItem *item,
else if (firstFrame + deltaFrame < minFrame)
deltaFrame = minFrame - firstFrame;
targetFrame = sourceFrame + deltaFrame;
if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // keyframe snapping
qreal snappedTargetFrame = scene()->snap(targetFrame);
deltaFrame += snappedTargetFrame - targetFrame;
targetFrame = snappedTargetFrame;
}
scene()->statusBarMessageChanged(tr(TimelineConstants::statusBarKeyframe)
.arg(sourceFrame + deltaFrame));
.arg(targetFrame));
const QList<TimelineKeyframeItem *> selectedKeyframes = scene()->selectedKeyframes();
for (auto *keyframe : selectedKeyframes) {