From 2c2ffa8273bbf5da041002f64a76dfa223705c29 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 3 Oct 2019 11:51:13 +0300 Subject: [PATCH] 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 Reviewed-by: Thomas Hartmann --- .../timelineeditor/timelinegraphicsscene.cpp | 31 ++++++++++++------- .../timelineeditor/timelinegraphicsscene.h | 3 +- .../timelineeditor/timelinemovetool.cpp | 12 +++++-- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp index f212d52916c..96d37f395a2 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp @@ -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); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h index fd2c641dd45..ba0c10bcd56 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h @@ -97,6 +97,8 @@ public: QVector keyframePositions() const; QVector 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 itemsAt(const QPointF &pos); private: - qreal snap(qreal frame); TimelineWidget *m_parent = nullptr; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp index 785b1bb2681..7c3d7a79e4d 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp @@ -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 selectedKeyframes = scene()->selectedKeyframes(); for (auto *keyframe : selectedKeyframes) {