timeline: vastly improved snapping

This commit is contained in:
itsmattkc
2020-06-06 13:59:49 +10:00
parent 5055c136b6
commit aaee2ab9ed
13 changed files with 285 additions and 99 deletions
+124 -1
View File
@@ -20,11 +20,13 @@
#include "timelinewidget.h"
#include <cfloat>
#include <QSplitter>
#include <QVBoxLayout>
#include <QtMath>
#include "core.h"
#include "common/range.h"
#include "common/timecodefunctions.h"
#include "dialog/sequence/sequence.h"
#include "dialog/speedduration/speedduration.h"
@@ -56,6 +58,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
ruler_and_time_layout->addWidget(timecode_label_);
ruler_and_time_layout->addWidget(ruler());
ruler()->SetSnapService(this);
// Create list of TimelineViews - these MUST correspond to the ViewType enum
@@ -103,6 +106,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
view->SetSnapService(this);
view_splitter->addWidget(tview);
@@ -906,6 +910,8 @@ void TimelineWidget::ClearGhosts()
ghost_items_.clear();
}
HideSnaps();
}
bool TimelineWidget::HasGhosts()
@@ -1266,7 +1272,7 @@ void TimelineWidget::SetBlockLinksSelected(Block* block, bool selected)
}
QVector<Timeline::EditToInfo> TimelineWidget::GetEditToInfo(const rational& playhead_time,
Timeline::MovementMode mode)
Timeline::MovementMode mode)
{
// Get list of unlocked tracks
QVector<TrackOutput*> tracks = GetConnectedNode()->GetUnlockedTracks();
@@ -1399,6 +1405,20 @@ void TimelineWidget::EditTo(Timeline::MovementMode mode)
Core::instance()->undo_stack()->pushIfHasChildren(command);
}
void TimelineWidget::ShowSnap(const QList<rational> &times)
{
foreach (TimelineAndTrackView* tview, views_) {
tview->view()->EnableSnap(times);
}
}
void TimelineWidget::HideSnaps()
{
foreach (TimelineAndTrackView* tview, views_) {
tview->view()->DisableSnap();
}
}
void TimelineWidget::StartRubberBandSelect(bool enable_selecting, bool select_links)
{
drag_origin_ = QCursor::pos();
@@ -1478,4 +1498,107 @@ void TimelineWidget::EndRubberBandSelect(bool enable_selecting, bool select_link
ViewSelectionChanged();
}
struct SnapData {
rational time;
rational movement;
};
QList<SnapData> AttemptSnap(const QList<double>& screen_pt,
double compare_pt,
const QList<rational>& start_times,
const rational& compare_time) {
const qreal kSnapRange = 10; // FIXME: Hardcoded number
QList<SnapData> snap_data;
for (int i=0;i<screen_pt.size();i++) {
// Attempt snapping to clip out point
if (InRange(screen_pt.at(i), compare_pt, kSnapRange)) {
snap_data.append({compare_time, compare_time - start_times.at(i)});
}
}
return snap_data;
}
bool TimelineWidget::SnapPoint(QList<rational> start_times, rational* movement, int snap_points)
{
QList<double> screen_pt;
foreach (const rational& s, start_times) {
screen_pt.append(TimeToScene(s + *movement));
}
QList<SnapData> potential_snaps;
if (snap_points & kSnapToPlayhead) {
rational playhead_abs_time = GetTime();
qreal playhead_pos = TimeToScene(playhead_abs_time);
potential_snaps.append(AttemptSnap(screen_pt, playhead_pos, start_times, playhead_abs_time));
}
if (snap_points & kSnapToClips) {
QMap<Block*, TimelineViewBlockItem*>::const_iterator i;
for (i=block_items_.constBegin(); i!=block_items_.constEnd(); i++) {
TimelineViewBlockItem* item = i.value();
if (item) {
qreal rect_left = item->x();
qreal rect_right = rect_left + item->rect().width();
// Attempt snapping to clip in point
potential_snaps.append(AttemptSnap(screen_pt, rect_left, start_times, item->block()->in()));
// Attempt snapping to clip out point
potential_snaps.append(AttemptSnap(screen_pt, rect_right, start_times, item->block()->out()));
}
}
}
if ((snap_points & kSnapToMarkers) && GetConnectedTimelinePoints()) {
foreach (TimelineMarker* m, GetConnectedTimelinePoints()->markers()->list()) {
qreal marker_pos = TimeToScene(m->time().in());
potential_snaps.append(AttemptSnap(screen_pt, marker_pos, start_times, m->time().in()));
if (m->time().in() != m->time().out()) {
marker_pos = TimeToScene(m->time().out());
potential_snaps.append(AttemptSnap(screen_pt, marker_pos, start_times, m->time().out()));
}
}
}
if (potential_snaps.isEmpty()) {
HideSnaps();
return false;
}
int closest_snap = 0;
rational closest_diff = qAbs(potential_snaps.at(0).movement - *movement);
// Determine which snap point was the closest
for (int i=1; i<potential_snaps.size(); i++) {
rational this_diff = qAbs(potential_snaps.at(i).movement - *movement);
if (this_diff < closest_diff) {
closest_snap = i;
closest_diff = this_diff;
}
}
*movement = potential_snaps.at(closest_snap).movement;
// Find all points at this movement
QList<rational> snap_times;
foreach (const SnapData& data, potential_snaps) {
if (data.movement == *movement) {
snap_times.append(data.time);
}
}
ShowSnap(snap_times);
return true;
}
OLIVE_NAMESPACE_EXIT