From e6d4fe43de67dc46ec6d491b675438471c19103f Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 2 May 2022 00:01:49 -0700 Subject: [PATCH] reworked and vastly improved snapping subsystem --- app/dialog/export/export.cpp | 1 + app/node/keyframe.h | 9 + app/timeline/timelinemarker.h | 9 +- app/widget/curvewidget/curvewidget.cpp | 1 + app/widget/keyframeview/keyframeview.h | 5 + .../keyframeviewinputconnection.h | 2 +- app/widget/nodeparamview/nodeparamview.cpp | 2 + app/widget/nodeparamview/nodeparamview.h | 10 ++ app/widget/snapservice/CMakeLists.txt | 22 --- app/widget/snapservice/snapservice.cpp | 1 - app/widget/snapservice/snapservice.h | 31 ---- app/widget/timebased/timebasedview.cpp | 10 +- app/widget/timebased/timebasedview.h | 14 +- .../timebasedviewselectionmanager.cpp | 1 - .../timebased/timebasedviewselectionmanager.h | 36 +++- app/widget/timebased/timebasedwidget.cpp | 154 ++++++++++++++++++ app/widget/timebased/timebasedwidget.h | 26 ++- app/widget/timebased/timescaledobject.h | 1 + app/widget/timelinewidget/timelinewidget.cpp | 119 +------------- app/widget/timelinewidget/timelinewidget.h | 11 +- app/widget/timelinewidget/tool/add.cpp | 2 +- app/widget/timelinewidget/tool/import.cpp | 4 +- app/widget/timelinewidget/tool/pointer.cpp | 9 +- app/widget/timelinewidget/tool/tool.h | 2 +- app/widget/timelinewidget/tool/transition.cpp | 2 +- app/widget/timeruler/seekablewidget.cpp | 23 ++- app/widget/timeruler/seekablewidget.h | 4 +- app/widget/timeruler/timeruler.cpp | 2 +- app/widget/viewer/viewer.cpp | 1 + 29 files changed, 289 insertions(+), 225 deletions(-) delete mode 100644 app/widget/snapservice/CMakeLists.txt delete mode 100644 app/widget/snapservice/snapservice.cpp delete mode 100644 app/widget/snapservice/snapservice.h diff --git a/app/dialog/export/export.cpp b/app/dialog/export/export.cpp index 83f8a0d8d..c9aa7249c 100644 --- a/app/dialog/export/export.cpp +++ b/app/dialog/export/export.cpp @@ -38,6 +38,7 @@ #include "node/project/sequence/sequence.h" #include "task/taskmanager.h" #include "ui/icons/icons.h" +#include "widget/timeruler/timeruler.h" namespace olive { diff --git a/app/node/keyframe.h b/app/node/keyframe.h index b4c7f08e6..a6163039a 100644 --- a/app/node/keyframe.h +++ b/app/node/keyframe.h @@ -26,6 +26,7 @@ #include #include "common/rational.h" +#include "common/timerange.h" #include "node/param.h" namespace olive { @@ -86,6 +87,14 @@ public: const rational& time() const; void set_time(const rational& time); + /** + * @brief Dummy function for TimeBasedViewSelectionManager compatibility + * + * FIXME: Once we upgrade to C++17, we won't need this because we'll be able to check types in + * TimeBasedViewSelectionManager's template functions + */ + TimeRange time_range() const { return TimeRange(time_, time_); } + /** * @brief The value of this keyframe (i.e. the value to use at this keyframe's time) */ diff --git a/app/timeline/timelinemarker.h b/app/timeline/timelinemarker.h index 1389da26e..cc1fd110c 100644 --- a/app/timeline/timelinemarker.h +++ b/app/timeline/timelinemarker.h @@ -38,10 +38,17 @@ public: TimelineMarker(QObject* parent = nullptr); TimelineMarker(int color, const TimeRange& time, const QString& name = QString(), QObject* parent = nullptr); + /** + * @brief Dummy function for TimeBasedViewSelectionManager compatibility + * + * FIXME: Once we upgrade to C++17, we won't need this because we'll be able to check types in + * TimeBasedViewSelectionManager's template functions + */ const rational &time() const { return time_.in(); } + void set_time(const rational& time); + const TimeRange &time_range() const { return time_; } void set_time(const TimeRange& time); - void set_time(const rational& time); bool has_sibling_at_time(const rational &t) const; diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index e75d6746b..202e90a8c 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -31,6 +31,7 @@ #include "common/timecodefunctions.h" #include "node/node.h" #include "widget/keyframeview/keyframeviewundo.h" +#include "widget/timeruler/timeruler.h" namespace olive { diff --git a/app/widget/keyframeview/keyframeview.h b/app/widget/keyframeview/keyframeview.h index e7a2102f5..096be2f9f 100644 --- a/app/widget/keyframeview/keyframeview.h +++ b/app/widget/keyframeview/keyframeview.h @@ -63,6 +63,11 @@ public: return selection_manager_.GetSelectedObjects(); } + const QVector &GetKeyframeTracks() const + { + return tracks_; + } + virtual void SelectionManagerSelectEvent(void *obj) override; virtual void SelectionManagerDeselectEvent(void *obj) override; diff --git a/app/widget/keyframeview/keyframeviewinputconnection.h b/app/widget/keyframeview/keyframeviewinputconnection.h index b6b0c1da5..5b3275fc0 100644 --- a/app/widget/keyframeview/keyframeviewinputconnection.h +++ b/app/widget/keyframeview/keyframeviewinputconnection.h @@ -50,7 +50,7 @@ public: void SetYBehavior(YBehavior e); - const QVector GetKeyframes() const + const QVector &GetKeyframes() const { return input_.input().node()->GetKeyframeTracks(input_.input()).at(input_.track()); } diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index 8af158346..8e3beb82e 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -29,6 +29,7 @@ #include "common/timecodefunctions.h" #include "node/output/viewer/viewer.h" #include "widget/nodeview/nodeviewundo.h" +#include "widget/timeruler/timeruler.h" namespace olive { @@ -117,6 +118,7 @@ NodeParamView::NodeParamView(bool create_keyframe_view, QWidget *parent) : // Create keyframe view keyframe_view_ = new KeyframeView(); keyframe_view_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + keyframe_view_->SetSnapService(this); ConnectTimelineView(keyframe_view_); keyframe_area_layout->addWidget(keyframe_view_); diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index 8061d5b1a..c0078b83e 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -88,6 +88,16 @@ protected: virtual void ConnectedNodeChangeEvent(ViewerOutput* n) override; + virtual const QVector *GetSnapKeyframes() const override + { + return keyframe_view_ ? &keyframe_view_->GetKeyframeTracks() : nullptr; + } + + virtual const std::vector *GetSnapIgnoreKeyframes() const override + { + return keyframe_view_ ? &keyframe_view_->GetSelectedKeyframes() : nullptr; + } + private: void UpdateItemTime(const rational &time); diff --git a/app/widget/snapservice/CMakeLists.txt b/app/widget/snapservice/CMakeLists.txt deleted file mode 100644 index 6224fa753..000000000 --- a/app/widget/snapservice/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Olive - Non-Linear Video Editor -# Copyright (C) 2021 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 . - -set(OLIVE_SOURCES - ${OLIVE_SOURCES} - widget/snapservice/snapservice.cpp - widget/snapservice/snapservice.h - PARENT_SCOPE -) diff --git a/app/widget/snapservice/snapservice.cpp b/app/widget/snapservice/snapservice.cpp deleted file mode 100644 index b3a853f1e..000000000 --- a/app/widget/snapservice/snapservice.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "snapservice.h" diff --git a/app/widget/snapservice/snapservice.h b/app/widget/snapservice/snapservice.h deleted file mode 100644 index ad83fb3c9..000000000 --- a/app/widget/snapservice/snapservice.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef SNAPSERVICE_H -#define SNAPSERVICE_H - -#include "common/rational.h" - -namespace olive { - -class SnapService -{ -public: - SnapService() = default; - - enum SnapPoints { - kSnapToClips = 0x1, - kSnapToPlayhead = 0x2, - kSnapToMarkers = 0x4, - kSnapAll = 0xFF - }; - - /** - * @brief Snaps point `start_point` that is moving by `movement` to currently existing clips - */ - virtual bool SnapPoint(QVector start_times, rational *movement, int snap_points = kSnapAll) = 0; - - virtual void HideSnaps() = 0; - -}; - -} - -#endif // SNAPSERVICE_H diff --git a/app/widget/timebased/timebasedview.cpp b/app/widget/timebased/timebasedview.cpp index 05a749976..dc5e68cd8 100644 --- a/app/widget/timebased/timebasedview.cpp +++ b/app/widget/timebased/timebasedview.cpp @@ -26,6 +26,7 @@ #include #include "common/timecodefunctions.h" +#include "widget/timebased/timebasedwidget.h" namespace olive { @@ -63,7 +64,7 @@ void TimeBasedView::TimebaseChangedEvent(const rational &) viewport()->update(); } -void TimeBasedView::EnableSnap(const QVector &points) +void TimeBasedView::EnableSnap(const std::vector &points) { snapped_ = true; snap_time_ = points; @@ -78,11 +79,6 @@ void TimeBasedView::DisableSnap() viewport()->update(); } -void TimeBasedView::SetSnapService(SnapService *service) -{ - snap_service_ = service; -} - const double &TimeBasedView::GetYScale() const { return y_scale_; @@ -211,7 +207,7 @@ bool TimeBasedView::PlayheadMove(QMouseEvent *event) if (Core::instance()->snapping() && snap_service_) { rational movement; - snap_service_->SnapPoint({mouse_time}, &movement, SnapService::kSnapAll & ~SnapService::kSnapToPlayhead); + snap_service_->SnapPoint({mouse_time}, &movement, TimeBasedWidget::kSnapAll & ~TimeBasedWidget::kSnapToPlayhead); mouse_time += movement; } diff --git a/app/widget/timebased/timebasedview.h b/app/widget/timebased/timebasedview.h index 4bca7f9da..8771f6c01 100644 --- a/app/widget/timebased/timebasedview.h +++ b/app/widget/timebased/timebasedview.h @@ -22,21 +22,23 @@ #define TIMELINEVIEWBASE_H #include +#include #include "core.h" #include "timescaledobject.h" #include "widget/handmovableview/handmovableview.h" -#include "widget/snapservice/snapservice.h" namespace olive { +class TimeBasedWidget; + class TimeBasedView : public HandMovableView, public TimeScaledObject { Q_OBJECT public: TimeBasedView(QWidget* parent = nullptr); - void EnableSnap(const QVector &points); + void EnableSnap(const std::vector &points); void DisableSnap(); bool IsSnapped() const { @@ -45,8 +47,8 @@ public: const rational &GetTime() const { return playhead_; } - SnapService *GetSnapService() const { return snap_service_; } - void SetSnapService(SnapService* service); + TimeBasedWidget *GetSnapService() const { return snap_service_; } + void SetSnapService(TimeBasedWidget* service) { snap_service_ = service; } const double& GetYScale() const; void SetYScale(const double& y_scale); @@ -117,11 +119,11 @@ private: QGraphicsScene scene_; bool snapped_; - QVector snap_time_; + std::vector snap_time_; rational end_time_; - SnapService* snap_service_; + TimeBasedWidget* snap_service_; bool y_axis_enabled_; diff --git a/app/widget/timebased/timebasedviewselectionmanager.cpp b/app/widget/timebased/timebasedviewselectionmanager.cpp index dd44a172e..577046aff 100644 --- a/app/widget/timebased/timebasedviewselectionmanager.cpp +++ b/app/widget/timebased/timebasedviewselectionmanager.cpp @@ -22,5 +22,4 @@ namespace olive { - } diff --git a/app/widget/timebased/timebasedviewselectionmanager.h b/app/widget/timebased/timebasedviewselectionmanager.h index d13d4754e..106eef182 100644 --- a/app/widget/timebased/timebasedviewselectionmanager.h +++ b/app/widget/timebased/timebasedviewselectionmanager.h @@ -29,6 +29,7 @@ #include "common/rational.h" #include "common/timecodefunctions.h" #include "timebasedview.h" +#include "timebasedwidget.h" namespace olive { @@ -38,9 +39,15 @@ class TimeBasedViewSelectionManager public: TimeBasedViewSelectionManager(TimeBasedView *view) : view_(view), - rubberband_(nullptr) + rubberband_(nullptr), + snap_mask_(TimeBasedWidget::kSnapAll) {} + void SetSnapMask(TimeBasedWidget::SnapMask e) + { + snap_mask_ = e; + } + void ClearDrawnObjects() { drawn_objects_.clear(); @@ -156,19 +163,40 @@ public: initial_drag_item_ = initial_item; dragging_.resize(selected_.size()); + snap_points_.resize(selected_.size()*2); for (size_t i=0; itime(); + + snap_points_[i] = obj->time(); + snap_points_[i+selected_.size()] = obj->time_range().out(); } drag_mouse_start_ = view_->mapToScene(event->pos()); } + void SnapPoints(rational *movement) + { + if (Core::instance()->snapping() && view_->GetSnapService()) { + view_->GetSnapService()->SnapPoint(snap_points_, movement, snap_mask_); + } + } + + void Unsnap() + { + if (view_->GetSnapService()) { + view_->GetSnapService()->HideSnaps(); + } + } + void DragMove(QMouseEvent *event, const QString &tip_format = QString()) { rational time_diff = view_->SceneToTimeNoGrid(view_->mapToScene(event->pos()).x() - drag_mouse_start_.x()); + // Snap points + SnapPoints(&time_diff); + // Validate movement for (size_t i=0; ihas_sibling_at_time(proposed_time)) { proposed_time += adj; + Unsnap(); } if (proposed_time < 0) { // Prevent any object from going below zero proposed_time = 0; + Unsnap(); // Setting our proposed time to zero may (re)introduce a conflict that we just avoided // with the sibling check above, so we request it to happen again. To avoid a negative @@ -230,6 +260,7 @@ public: } dragging_.clear(); + Unsnap(); } void RubberBandStart(QMouseEvent *event) @@ -325,6 +356,7 @@ private: std::vector selected_; std::vector dragging_; + std::vector snap_points_; T *initial_drag_item_; @@ -336,6 +368,8 @@ private: QPoint rubberband_start_; std::vector rubberband_preselected_; + TimeBasedWidget::SnapMask snap_mask_; + }; } diff --git a/app/widget/timebased/timebasedwidget.cpp b/app/widget/timebased/timebasedwidget.cpp index 81197e616..379dd8102 100644 --- a/app/widget/timebased/timebasedwidget.cpp +++ b/app/widget/timebased/timebasedwidget.cpp @@ -23,11 +23,13 @@ #include #include "common/autoscroll.h" +#include "common/range.h" #include "common/timecodefunctions.h" #include "config/config.h" #include "core.h" #include "dialog/markerproperties/markerpropertiesdialog.h" #include "node/project/sequence/sequence.h" +#include "widget/timeruler/timeruler.h" #include "widget/timelinewidget/undo/timelineundoworkarea.h" namespace olive { @@ -41,6 +43,7 @@ TimeBasedWidget::TimeBasedWidget(bool ruler_text_visible, bool ruler_cache_statu { ruler_ = new TimeRuler(ruler_text_visible, ruler_cache_status_visible, this); ConnectTimelineView(ruler_, true); + ruler()->SetSnapService(this); scrollbar_ = new ResizableTimelineScrollBar(Qt::Horizontal, this); connect(scrollbar_, &ResizableScrollBar::ResizeBegan, this, &TimeBasedWidget::ScrollBarResizeBegan); @@ -693,4 +696,155 @@ bool TimeBasedWidget::eventFilter(QObject *object, QEvent *event) return false; } +struct SnapData { + rational time; + rational movement; +}; + +void AttemptSnap(std::vector &snap_data, + const std::vector& screen_pt, + double compare_pt, + const std::vector& start_times, + const rational& compare_time) +{ + const qreal kSnapRange = 10; // FIXME: Hardcoded number + + for (size_t i=0;i &start_times, rational *movement, SnapMask snap_points) +{ + std::vector screen_pt(start_times.size()); + + for (size_t i=0; i potential_snaps; + + if (snap_points & kSnapToPlayhead) { + rational playhead_abs_time = GetTime(); + qreal playhead_pos = TimeToScene(playhead_abs_time); + AttemptSnap(potential_snaps, screen_pt, playhead_pos, start_times, playhead_abs_time); + } + + if ((snap_points & kSnapToClips) && GetSnapBlocks()) { + for (auto it=GetSnapBlocks()->cbegin(); it!=GetSnapBlocks()->cend(); it++) { + Block *b = *it; + + qreal rect_left = TimeToScene(b->in()); + qreal rect_right = TimeToScene(b->out()); + + // Attempt snapping to clip in point + AttemptSnap(potential_snaps, screen_pt, rect_left, start_times, b->in()); + + // Attempt snapping to clip out point + AttemptSnap(potential_snaps, screen_pt, rect_right, start_times, b->out()); + + if (snap_points & kSnapToMarkers) { + // Snap to clip markers too + if (ClipBlock *clip = dynamic_cast(b)) { + if (clip->connected_viewer()) { + TimelineMarkerList *markers = clip->connected_viewer()->GetTimelinePoints()->markers(); + for (auto jt=markers->cbegin(); jt!=markers->cend(); jt++) { + TimelineMarker *marker = *jt; + + TimeRange marker_range = marker->time_range() + clip->in() - clip->media_in(); + + qreal marker_in_screen = TimeToScene(marker_range.in()); + qreal marker_out_screen = TimeToScene(marker_range.out()); + + AttemptSnap(potential_snaps, screen_pt, marker_in_screen, start_times, marker_range.in()); + AttemptSnap(potential_snaps, screen_pt, marker_out_screen, start_times, marker_range.out()); + } + } + } + } + } + } + + if ((snap_points & kSnapToMarkers) && ruler()->GetTimelinePoints()) { + for (auto it=ruler()->GetTimelinePoints()->markers()->cbegin(); it!=ruler()->GetTimelinePoints()->markers()->cend(); it++) { + TimelineMarker* m = *it; + + qreal marker_pos = TimeToScene(m->time_range().in()); + AttemptSnap(potential_snaps, screen_pt, marker_pos, start_times, m->time_range().in()); + + if (m->time_range().in() != m->time_range().out()) { + marker_pos = TimeToScene(m->time_range().out()); + AttemptSnap(potential_snaps, screen_pt, marker_pos, start_times, m->time_range().out()); + } + } + } + + if ((snap_points & kSnapToKeyframes) && GetSnapKeyframes()) { + for (auto it=GetSnapKeyframes()->cbegin(); it!=GetSnapKeyframes()->cend(); it++) { + const QVector &keys = (*it)->GetKeyframes(); + for (auto jt=keys.cbegin(); jt!=keys.cend(); jt++) { + NodeKeyframe *key = *jt; + + auto ignore = GetSnapIgnoreKeyframes(); + if (ignore && std::find(ignore->cbegin(), ignore->cend(), key) != ignore->cend()) { + continue; + } + + qreal key_scene_pt = TimeToScene(key->time()); + + AttemptSnap(potential_snaps, screen_pt, key_scene_pt, start_times, key->time()); + } + } + } + + if (potential_snaps.empty()) { + 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 (size_t i=1; i snap_times; + foreach (const SnapData& d, potential_snaps) { + if (d.movement == *movement) { + snap_times.push_back(d.time); + } + } + + ShowSnaps(snap_times); + + return true; +} + +void TimeBasedWidget::ShowSnaps(const std::vector ×) +{ + foreach (TimeBasedView* view, timeline_views_) { + view->EnableSnap(times); + } +} + +void TimeBasedWidget::HideSnaps() +{ + foreach (TimeBasedView* view, timeline_views_) { + view->DisableSnap(); + } +} + } diff --git a/app/widget/timebased/timebasedwidget.h b/app/widget/timebased/timebasedwidget.h index e8b6bee76..3dce38e5a 100644 --- a/app/widget/timebased/timebasedwidget.h +++ b/app/widget/timebased/timebasedwidget.h @@ -25,13 +25,15 @@ #include "node/output/viewer/viewer.h" #include "timeline/timelinecommon.h" +#include "widget/keyframeview/keyframeviewinputconnection.h" #include "widget/resizablescrollbar/resizabletimelinescrollbar.h" #include "widget/timebased/timescaledobject.h" #include "widget/timelinewidget/view/timelineview.h" -#include "widget/timeruler/timeruler.h" namespace olive { +class TimeRuler; + class TimeBasedWidget : public TimelineScaledWidget { Q_OBJECT @@ -54,6 +56,22 @@ public: virtual bool eventFilter(QObject* object, QEvent* event) override; + using SnapMask = uint32_t; + enum SnapPoints { + kSnapToClips = 0x1, + kSnapToPlayhead = 0x2, + kSnapToMarkers = 0x4, + kSnapToKeyframes = 0x8, + kSnapAll = UINT32_MAX + }; + + /** + * @brief Snaps point `start_point` that is moving by `movement` to currently existing clips + */ + bool SnapPoint(const std::vector &start_times, rational *movement, SnapMask snap_points = kSnapAll); + void ShowSnaps(const std::vector ×); + void HideSnaps(); + public slots: void SetTime(const rational &time); @@ -119,6 +137,10 @@ protected: void PassWheelEventsToScrollBar(QObject* object); + virtual const QVector *GetSnapBlocks() const { return nullptr; } + virtual const QVector *GetSnapKeyframes() const { return nullptr; } + virtual const std::vector *GetSnapIgnoreKeyframes() const { return nullptr; } + protected slots: /** * @brief Slot to center the horizontal scroll bar on the playhead's current position @@ -141,7 +163,7 @@ signals: void ConnectedNodeChanged(ViewerOutput* old, ViewerOutput* now); private: - + /** * @brief Set either in or out point to the current playhead diff --git a/app/widget/timebased/timescaledobject.h b/app/widget/timebased/timescaledobject.h index acdc44acb..d5cd102e2 100644 --- a/app/widget/timebased/timescaledobject.h +++ b/app/widget/timebased/timescaledobject.h @@ -24,6 +24,7 @@ #include #include "common/rational.h" +#include "node/block/block.h" namespace olive { diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index f62938346..8766cd2fb 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -54,6 +54,7 @@ #include "widget/menu/menu.h" #include "widget/menu/menushared.h" #include "widget/nodeview/nodeviewundo.h" +#include "widget/timeruler/timeruler.h" namespace olive { @@ -82,7 +83,6 @@ 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 @@ -1515,13 +1515,6 @@ void TimelineWidget::EditTo(Timeline::MovementMode mode) Core::instance()->undo_stack()->pushIfHasChildren(command); } -void TimelineWidget::ShowSnap(const QVector ×) -{ - foreach (TimelineAndTrackView* tview, views_) { - tview->view()->EnableSnap(times); - } -} - void TimelineWidget::UpdateViewports(const Track::Type &type) { if (type == Track::kNone) { @@ -1573,13 +1566,6 @@ QVector TimelineWidget::GetBlocksInGlobalRect(const QPoint &p1, const Q return blocks_in_rect; } -void TimelineWidget::HideSnaps() -{ - foreach (TimelineAndTrackView* tview, views_) { - tview->view()->DisableSnap(); - } -} - QByteArray TimelineWidget::SaveSplitterState() const { return view_splitter_->saveState(); @@ -1734,109 +1720,6 @@ Block *TimelineWidget::GetItemAtScenePos(const TimelineCoordinate& coord) return views_.at(coord.GetTrack().type())->view()->GetItemAtScenePos(coord.GetFrame(), coord.GetTrack().index()); } -struct SnapData { - rational time; - rational movement; -}; - -QVector AttemptSnap(const QVector& screen_pt, - double compare_pt, - const QVector& start_times, - const rational& compare_time) { - const qreal kSnapRange = 10; // FIXME: Hardcoded number - - QVector snap_data; - - for (int i=0;i start_times, rational* movement, int snap_points) -{ - if (!GetConnectedNode()) { - return false; - } - - QVector screen_pt; - - foreach (const rational& s, start_times) { - screen_pt.append(TimeToScene(s + *movement)); - } - - QVector 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) { - foreach (Block* b, added_blocks_) { - qreal rect_left = TimeToScene(b->in()); - qreal rect_right = TimeToScene(b->out()); - - // Attempt snapping to clip in point - potential_snaps.append(AttemptSnap(screen_pt, rect_left, start_times, b->in())); - - // Attempt snapping to clip out point - potential_snaps.append(AttemptSnap(screen_pt, rect_right, start_times, b->out())); - } - } - - if ((snap_points & kSnapToMarkers)) { - for (auto it=GetConnectedNode()->GetTimelinePoints()->markers()->cbegin(); it!=GetConnectedNode()->GetTimelinePoints()->markers()->cend(); it++) { - TimelineMarker* m = *it; - - qreal marker_pos = TimeToScene(m->time_range().in()); - potential_snaps.append(AttemptSnap(screen_pt, marker_pos, start_times, m->time_range().in())); - - if (m->time_range().in() != m->time_range().out()) { - marker_pos = TimeToScene(m->time_range().out()); - potential_snaps.append(AttemptSnap(screen_pt, marker_pos, start_times, m->time_range().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 snap_times; - foreach (const SnapData& d, potential_snaps) { - if (d.movement == *movement) { - snap_times.append(d.time); - } - } - - ShowSnap(snap_times); - - return true; -} - void TimelineWidget::SetSplitterSizesCommand::redo() { old_sizes_ = splitter_->sizes(); diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index b19159403..841b793ee 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -31,7 +31,6 @@ #include "timeline/timelinecommon.h" #include "timelineandtrackview.h" #include "widget/slider/rationalslider.h" -#include "widget/snapservice/snapservice.h" #include "widget/timebased/timebasedwidget.h" #include "widget/timelinewidget/timelinewidgetselections.h" #include "widget/timelinewidget/tool/import.h" @@ -44,7 +43,7 @@ namespace olive { * * Encapsulates TimelineViews, TimeRulers, and scrollbars for a complete widget to manipulate Timelines */ -class TimelineWidget : public TimeBasedWidget, public SnapService +class TimelineWidget : public TimeBasedWidget { Q_OBJECT public: @@ -113,10 +112,6 @@ public: return selected_blocks_; } - virtual bool SnapPoint(QVector start_times, rational *movement, int snap_points = kSnapAll) override; - - virtual void HideSnaps() override; - QByteArray SaveSplitterState() const; void RestoreSplitterState(const QByteArray& state); @@ -276,6 +271,8 @@ protected: virtual void ConnectNodeEvent(ViewerOutput* n) override; virtual void DisconnectNodeEvent(ViewerOutput* n) override; + virtual const QVector *GetSnapBlocks() const override { return &added_blocks_; } + private: QVector GetEditToInfo(const rational &playhead_time, Timeline::MovementMode mode); @@ -283,8 +280,6 @@ private: void EditTo(Timeline::MovementMode mode); - void ShowSnap(const QVector& times); - void UpdateViewports(const Track::Type& type = Track::kNone); QVector GetBlocksInGlobalRect(const QPoint &p1, const QPoint &p2); diff --git a/app/widget/timelinewidget/tool/add.cpp b/app/widget/timelinewidget/tool/add.cpp index a8f778b5f..2dd7455c5 100644 --- a/app/widget/timelinewidget/tool/add.cpp +++ b/app/widget/timelinewidget/tool/add.cpp @@ -79,7 +79,7 @@ void AddTool::MousePress(TimelineViewMouseEvent *event) ghost_->SetTrack(track); parent()->AddGhost(ghost_); - snap_points_.append(drag_start_point_); + snap_points_.push_back(drag_start_point_); } } diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index f9ab4b6f8..de84a94b5 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -243,8 +243,8 @@ void ImportTool::FootageToGhosts(rational ghost_start, const DraggedFootageData ghost->SetMediaIn(ghost_in); ghost->SetTrack(Track::Reference(track_type, track_offsets.at(track_type))); - snap_points_.append(ghost->GetIn()); - snap_points_.append(ghost->GetOut()); + snap_points_.push_back(ghost->GetIn()); + snap_points_.push_back(ghost->GetOut()); // Increment track count for this track type track_offsets[track_type]++; diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 4816d4bc6..ad76ff6a6 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -35,6 +35,7 @@ #include "pointer.h" #include "widget/nodeview/nodeviewundo.h" #include "widget/timelinewidget/undo/timelineundopointer.h" +#include "widget/timeruler/timeruler.h" namespace olive { @@ -793,14 +794,14 @@ void PointerTool::AddGhostInternal(TimelineViewGhostItem* ghost, Timeline::Movem // Prepare snap points (optimizes snapping for later) switch (mode) { case Timeline::kMove: - snap_points_.append(ghost->GetIn()); - snap_points_.append(ghost->GetOut()); + snap_points_.push_back(ghost->GetIn()); + snap_points_.push_back(ghost->GetOut()); break; case Timeline::kTrimIn: - snap_points_.append(ghost->GetIn()); + snap_points_.push_back(ghost->GetIn()); break; case Timeline::kTrimOut: - snap_points_.append(ghost->GetOut()); + snap_points_.push_back(ghost->GetOut()); break; default: break; diff --git a/app/widget/timelinewidget/tool/tool.h b/app/widget/timelinewidget/tool/tool.h index c627f542e..47fafbd90 100644 --- a/app/widget/timelinewidget/tool/tool.h +++ b/app/widget/timelinewidget/tool/tool.h @@ -78,7 +78,7 @@ protected: void InsertGapsAtGhostDestination(MultiUndoCommand* command); - QVector snap_points_; + std::vector snap_points_; bool dragging_; diff --git a/app/widget/timelinewidget/tool/transition.cpp b/app/widget/timelinewidget/tool/transition.cpp index 594659ccb..bd95a429a 100644 --- a/app/widget/timelinewidget/tool/transition.cpp +++ b/app/widget/timelinewidget/tool/transition.cpp @@ -73,7 +73,7 @@ void TransitionTool::MousePress(TimelineViewMouseEvent *event) parent()->AddGhost(ghost_); - snap_points_.append(transition_start_point); + snap_points_.push_back(transition_start_point); // Set the drag start point drag_start_point_ = event->GetFrame(); diff --git a/app/widget/timeruler/seekablewidget.cpp b/app/widget/timeruler/seekablewidget.cpp index c23a010c5..b5334fe83 100644 --- a/app/widget/timeruler/seekablewidget.cpp +++ b/app/widget/timeruler/seekablewidget.cpp @@ -53,6 +53,8 @@ SeekableWidget::SeekableWidget(QWidget* parent) : setContextMenuPolicy(Qt::CustomContextMenu); setFocusPolicy(Qt::ClickFocus); + + selection_manager_.SetSnapMask(TimeBasedWidget::kSnapAll & ~TimeBasedWidget::kSnapToMarkers); } void SeekableWidget::ConnectTimelinePoints(TimelinePoints *points) @@ -212,11 +214,6 @@ void SeekableWidget::focusOutEvent(QFocusEvent *event) } } -TimelinePoints *SeekableWidget::timeline_points() const -{ - return timeline_points_; -} - void SeekableWidget::DeselectAllMarkers() { selection_manager_.ClearSelection(); @@ -262,7 +259,7 @@ void SeekableWidget::SeekToScenePoint(qreal scene) GetSnapService()->SnapPoint({playhead_time}, &movement, - SnapService::kSnapAll & ~SnapService::kSnapToPlayhead); + TimeBasedWidget::kSnapAll & ~TimeBasedWidget::kSnapToPlayhead); playhead_time += movement; } @@ -290,7 +287,7 @@ void SeekableWidget::SelectionManagerDeselectEvent(void *obj) void SeekableWidget::DrawTimelinePoints(QPainter* p, int marker_bottom) { - if (!timeline_points()) { + if (!GetTimelinePoints()) { return; } @@ -300,22 +297,22 @@ void SeekableWidget::DrawTimelinePoints(QPainter* p, int marker_bottom) selection_manager_.ClearDrawnObjects(); // Draw in/out workarea - if (timeline_points()->workarea()->enabled()) { - int workarea_left = qMax(qreal(lim_left), TimeToScene(timeline_points()->workarea()->in())); + if (GetTimelinePoints()->workarea()->enabled()) { + int workarea_left = qMax(qreal(lim_left), TimeToScene(GetTimelinePoints()->workarea()->in())); int workarea_right; - if (timeline_points()->workarea()->out() == TimelineWorkArea::kResetOut) { + if (GetTimelinePoints()->workarea()->out() == TimelineWorkArea::kResetOut) { workarea_right = lim_right; } else { - workarea_right = qMin(qreal(lim_right), TimeToScene(timeline_points()->workarea()->out())); + workarea_right = qMin(qreal(lim_right), TimeToScene(GetTimelinePoints()->workarea()->out())); } p->fillRect(workarea_left, 0, workarea_right - workarea_left, height(), palette().highlight()); } // Draw markers - if (marker_bottom > 0 && !timeline_points()->markers()->empty()) { - for (auto it=timeline_points()->markers()->cbegin(); it!=timeline_points()->markers()->cend(); it++) { + if (marker_bottom > 0 && !GetTimelinePoints()->markers()->empty()) { + for (auto it=GetTimelinePoints()->markers()->cbegin(); it!=GetTimelinePoints()->markers()->cend(); it++) { TimelineMarker* marker = *it; int marker_right = TimeToScene(marker->time_range().out()); diff --git a/app/widget/timeruler/seekablewidget.h b/app/widget/timeruler/seekablewidget.h index 37d87903c..1bc8e61de 100644 --- a/app/widget/timeruler/seekablewidget.h +++ b/app/widget/timeruler/seekablewidget.h @@ -27,7 +27,6 @@ #include "common/rational.h" #include "timeline/timelinepoints.h" #include "widget/menu/menu.h" -#include "widget/snapservice/snapservice.h" #include "widget/timebased/timebasedviewselectionmanager.h" namespace olive { @@ -43,6 +42,7 @@ public: return horizontalScrollBar()->value(); } + TimelinePoints* GetTimelinePoints() const { return timeline_points_; } void ConnectTimelinePoints(TimelinePoints* points); bool IsDraggingPlayhead() const @@ -81,8 +81,6 @@ protected: void DrawTimelinePoints(QPainter *p, int marker_bottom = 0); - TimelinePoints* timeline_points() const; - void DrawPlayhead(QPainter* p, int x, int y); inline const int& text_height() const { diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index f50e67d36..61f95b020 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -105,7 +105,7 @@ void TimeRuler::drawForeground(QPainter *p, const QRectF &rect) // Draw timeline points if connected int marker_height = TimelineMarker::GetMarkerHeight(p->fontMetrics()); - if (timeline_points()) { + if (GetTimelinePoints()) { DrawTimelinePoints(p, marker_height); } diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index e5eae2440..996e5e5bc 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -43,6 +43,7 @@ #include "viewerpreventsleep.h" #include "widget/menu/menu.h" #include "window/mainwindow/mainwindow.h" +#include "widget/timeruler/timeruler.h" namespace olive {