diff --git a/app/common/CMakeLists.txt b/app/common/CMakeLists.txt index 9c840f53f..f3cb1ca7e 100644 --- a/app/common/CMakeLists.txt +++ b/app/common/CMakeLists.txt @@ -23,6 +23,7 @@ set(OLIVE_SOURCES common/filefunctions.h common/filefunctions.cpp common/lerp.h + common/range.h common/rational.h common/rational.cpp common/qobjectlistcast.h diff --git a/app/common/range.h b/app/common/range.h new file mode 100644 index 000000000..96de47c74 --- /dev/null +++ b/app/common/range.h @@ -0,0 +1,30 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef RANGE_H +#define RANGE_H + +template +bool InRange(T a, T b, T range) +{ + return (a >= b - range && a <= b + range); +} + +#endif // RANGE_H diff --git a/app/widget/timelineview/timelineview.h b/app/widget/timelineview/timelineview.h index 4fed1f35e..9799d43d8 100644 --- a/app/widget/timelineview/timelineview.h +++ b/app/widget/timelineview/timelineview.h @@ -142,6 +142,17 @@ private: */ rational ValidateOutTrimming(rational movement, const QVector ghosts); + enum SnapPoints { + kSnapToClips = 0x1, + kSnapToPlayhead = 0x2, + kSnapAll = 0xFF + }; + + /** + * @brief Snaps point `start_point` that is moving by `movement` to currently existing clips + */ + bool SnapPoint(rational start_point, rational *movement, int snap_points = kSnapAll); + bool dragging_; QPointF drag_start_; diff --git a/app/widget/timelineview/tool/import.cpp b/app/widget/timelineview/tool/import.cpp index 08380c10f..cdcbfa04c 100644 --- a/app/widget/timelineview/tool/import.cpp +++ b/app/widget/timelineview/tool/import.cpp @@ -24,6 +24,7 @@ #include "config/config.h" #include "common/qtversionabstraction.h" +#include "core.h" #include "node/distort/transform/transform.h" #include "node/color/opacity/opacity.h" #include "node/input/media/media.h" @@ -119,6 +120,16 @@ void TimelineView::ImportTool::DragMove(QDragMoveEvent *event) time_movement = ValidateFrameMovement(time_movement, parent()->ghost_items_); + // If snapping is enabled, check for snap points + if (olive::core.snapping()) { + foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) { + if (SnapPoint(ghost->In(), &time_movement) + || SnapPoint(ghost->Out(), &time_movement)) { + break; + } + } + } + // Move ghosts to the mouse cursor foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) { ghost->SetInAdjustment(time_movement); diff --git a/app/widget/timelineview/tool/tool.cpp b/app/widget/timelineview/tool/tool.cpp index 6bace9ae1..f8893b9fc 100644 --- a/app/widget/timelineview/tool/tool.cpp +++ b/app/widget/timelineview/tool/tool.cpp @@ -20,6 +20,10 @@ #include "widget/timelineview/timelineview.h" +#include + +#include "common/range.h" + TimelineView::Tool::Tool(TimelineView *parent) : dragging_(false), parent_(parent) @@ -131,3 +135,60 @@ rational TimelineView::Tool::ValidateOutTrimming(rational movement, const QVecto return movement; } + +void AttemptSnap(double proposed_point, + double compare_point, + rational start_time, + rational compare_time, + rational* movement, + double* diff) { + const qreal kSnapRange = 10; // FIXME: Hardcoded number + + // Attempt snapping to clip out point + if (InRange(proposed_point, compare_point, kSnapRange)) { + double this_diff = qAbs(compare_point - proposed_point); + + if (this_diff < *diff) { + *movement = compare_time - start_time; + *diff = this_diff; + } + } +} + +bool TimelineView::Tool::SnapPoint(rational start_point, rational* movement, int snap_points) +{ + QList items = parent()->scene_.items(); + + double diff = DBL_MAX; + + double proposed_point = (start_point + *movement).toDouble() * parent()->scale_; + + if (snap_points & kSnapToPlayhead) { + qreal playhead_pos = parent()->playhead_line_->x(); + + rational playhead_abs_time = rational(parent()->playhead_ * parent()->timebase_.numerator(), + parent()->timebase_.denominator()); + + AttemptSnap(proposed_point, playhead_pos, start_point, playhead_abs_time, movement, &diff); + } + + if (snap_points & kSnapToClips) { + foreach (QGraphicsItem* it, items) { + TimelineViewClipItem* timeline_rect = dynamic_cast(it); + + if (timeline_rect != nullptr) { + qreal rect_left = timeline_rect->x(); + qreal rect_right = rect_left + timeline_rect->rect().width(); + + // Attempt snapping to clip in point + AttemptSnap(proposed_point, rect_left, start_point, timeline_rect->clip()->in(), movement, &diff); + + // Attempt snapping to clip out point + AttemptSnap(proposed_point, rect_right, start_point, timeline_rect->clip()->out(), movement, &diff); + } + } + } + + + return (diff < DBL_MAX); +}