diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index d58bde7cd..d85e6d168 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -103,6 +103,7 @@ CurveWidget::CurveWidget(QWidget *parent) : connect(view_, &CurveView::SelectionChanged, this, &CurveWidget::SelectionChanged); connect(view_, &CurveView::ScaleChanged, this, &CurveWidget::SetScale); connect(view_, &CurveView::Dragged, this, &CurveWidget::KeyframeViewDragged); + connect(view_, &CurveView::Released, this, &CurveWidget::KeyframeViewReleased); // TimeBasedWidget's scrollbar has extra functionality that we can take advantage of view_->setHorizontalScrollBar(scrollbar()); @@ -379,15 +380,14 @@ void CurveWidget::InputSelectionChanged(const NodeKeyframeTrackReference& ref) void CurveWidget::KeyframeViewDragged(int x, int y) { - QMetaObject::invokeMethod(this, "CatchUpScrollToPoint", Qt::QueuedConnection, - Q_ARG(int, x)); - QMetaObject::invokeMethod(this, "CatchUpYScrollToPoint", Qt::QueuedConnection, - Q_ARG(int, y)); + SetCatchUpScrollValue(x); + SetCatchUpScrollValue(view_->verticalScrollBar(), y, view_->height()); } -void CurveWidget::CatchUpYScrollToPoint(int point) +void CurveWidget::KeyframeViewReleased() { - PageScrollInternal(view_->verticalScrollBar(), view_->height(), point, false); + StopCatchUpScrollTimer(); + StopCatchUpScrollTimer(view_->verticalScrollBar()); } } diff --git a/app/widget/curvewidget/curvewidget.h b/app/widget/curvewidget/curvewidget.h index 5fae1b4df..2d85c86df 100644 --- a/app/widget/curvewidget/curvewidget.h +++ b/app/widget/curvewidget/curvewidget.h @@ -122,8 +122,7 @@ private slots: void InputSelectionChanged(const NodeKeyframeTrackReference& ref); void KeyframeViewDragged(int x, int y); - - void CatchUpYScrollToPoint(int point); + void KeyframeViewReleased(); }; diff --git a/app/widget/keyframeview/keyframeview.cpp b/app/widget/keyframeview/keyframeview.cpp index df0349475..cf64fd668 100644 --- a/app/widget/keyframeview/keyframeview.cpp +++ b/app/widget/keyframeview/keyframeview.cpp @@ -290,8 +290,7 @@ void KeyframeView::mouseMoveEvent(QMouseEvent *event) if (event->buttons()) { // Signal cursor pos in case we should scroll to catch up to it - QPointF scene_pos = mapToScene(event->pos()); - emit Dragged(scene_pos.x(), scene_pos.y()); + emit Dragged(event->pos().x(), event->pos().y()); } } @@ -309,6 +308,7 @@ void KeyframeView::mouseReleaseEvent(QMouseEvent *event) selection_manager_.DragStop(command); KeyframeDragRelease(event, command); Core::instance()->undo_stack()->push(command); + emit Released(); } else if (selection_manager_.IsRubberBanding()) { selection_manager_.RubberBandStop(); Redraw(); diff --git a/app/widget/keyframeview/keyframeview.h b/app/widget/keyframeview/keyframeview.h index 2558f8cc7..0ab17f254 100644 --- a/app/widget/keyframeview/keyframeview.h +++ b/app/widget/keyframeview/keyframeview.h @@ -88,6 +88,8 @@ signals: void SelectionChanged(); + void Released(); + protected: virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index c3ab8be03..8262c2949 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -132,6 +132,7 @@ NodeParamView::NodeParamView(bool create_keyframe_view, QWidget *parent) : connect(keyframe_view_, &KeyframeView::TimeChanged, ruler(), &TimeRuler::SetTime); connect(keyframe_view_, &KeyframeView::TimeChanged, this, &NodeParamView::SetTime); connect(keyframe_view_, &KeyframeView::Dragged, this, &NodeParamView::KeyframeViewDragged); + connect(keyframe_view_, &KeyframeView::Released, this, &NodeParamView::KeyframeViewReleased); // Connect keyframe view scaling to this connect(keyframe_view_, &KeyframeView::ScaleChanged, this, &NodeParamView::SetScale); @@ -929,7 +930,12 @@ void NodeParamView::KeyframeViewDragged(int x, int y) { Q_UNUSED(y) - QMetaObject::invokeMethod(this, "CatchUpScrollToPoint", Qt::QueuedConnection, Q_ARG(int, x)); + SetCatchUpScrollValue(x); +} + +void NodeParamView::KeyframeViewReleased() +{ + StopCatchUpScrollTimer(); } void NodeParamView::UpdateElementY() diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index ba918d373..f1c3793af 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -165,6 +165,7 @@ private slots: //void FocusChanged(QWidget *old, QWidget *now); void KeyframeViewDragged(int x, int y); + void KeyframeViewReleased(); void NodeAddedToContext(Node *n); diff --git a/app/widget/timebased/timebasedwidget.cpp b/app/widget/timebased/timebasedwidget.cpp index ebd95cf9e..add053974 100644 --- a/app/widget/timebased/timebasedwidget.cpp +++ b/app/widget/timebased/timebasedwidget.cpp @@ -52,6 +52,10 @@ TimeBasedWidget::TimeBasedWidget(bool ruler_text_visible, bool ruler_cache_statu connect(scrollbar_, &ResizableScrollBar::ResizeMoved, this, &TimeBasedWidget::ScrollBarResizeMoved); PassWheelEventsToScrollBar(ruler_); + + catchup_scroll_timer_ = new QTimer(this); + catchup_scroll_timer_->setInterval(250); // Hardcoded 1/4 scroll limit value + connect(catchup_scroll_timer_, &QTimer::timeout, this, &TimeBasedWidget::CatchUpTimerTimeout); } void TimeBasedWidget::SetScaleAndCenterOnPlayhead(const double &scale) @@ -217,6 +221,15 @@ void TimeBasedWidget::CatchUpScrollToPoint(int point) PageScrollInternal(point, false); } +void TimeBasedWidget::CatchUpTimerTimeout() +{ + for (auto it=catchup_scroll_values_.cbegin(); it!=catchup_scroll_values_.cend(); it++) { + QScrollBar *sb = it.key(); + const CatchUpScrollData &d = it.value(); + PageScrollInternal(sb, d.maximum, sb->value() + d.value, false); + } +} + void TimeBasedWidget::AutoUpdateTimebase() { rational video_tb = viewer_node_->GetVideoParams().frame_rate_as_time_base(); @@ -301,6 +314,36 @@ void TimeBasedWidget::PassWheelEventsToScrollBar(QObject *object) object->installEventFilter(this); } +void TimeBasedWidget::SetCatchUpScrollValue(QScrollBar *b, int v, int maximum) +{ + CatchUpScrollData &cudata = catchup_scroll_values_[b]; + cudata.value = v; + cudata.maximum = maximum; + + static const qint64 min_cooldown = 100; // Hardcoded 1/10 sec cooldown + if (QDateTime::currentMSecsSinceEpoch() - cudata.last_forced >= min_cooldown) { + QMetaObject::invokeMethod(this, &TimeBasedWidget::CatchUpTimerTimeout, Qt::QueuedConnection); + cudata.last_forced = QDateTime::currentMSecsSinceEpoch(); + } + + if (!catchup_scroll_timer_->isActive()) { + catchup_scroll_timer_->start(); + } +} + +void TimeBasedWidget::SetCatchUpScrollValue(int v) +{ + SetCatchUpScrollValue(scrollbar_, v, ruler()->width()); +} + +void TimeBasedWidget::StopCatchUpScrollTimer(QScrollBar *b) +{ + catchup_scroll_values_.remove(b); + if (catchup_scroll_values_.empty()) { + catchup_scroll_timer_->stop(); + } +} + void TimeBasedWidget::SetTime(const rational &time) { if (UserIsDraggingPlayhead()) { diff --git a/app/widget/timebased/timebasedwidget.h b/app/widget/timebased/timebasedwidget.h index 509e8f8b9..905a22fb4 100644 --- a/app/widget/timebased/timebasedwidget.h +++ b/app/widget/timebased/timebasedwidget.h @@ -150,6 +150,14 @@ protected: void PassWheelEventsToScrollBar(QObject* object); + void SetCatchUpScrollValue(QScrollBar *b, int v, int maximum); + void SetCatchUpScrollValue(int v); + void StopCatchUpScrollTimer(QScrollBar *b); + void StopCatchUpScrollTimer() + { + StopCatchUpScrollTimer(scrollbar_); + } + virtual const QVector *GetSnapBlocks() const { return nullptr; } virtual const QVector *GetSnapKeyframes() const { return nullptr; } virtual const std::vector *GetSnapIgnoreKeyframes() const { return nullptr; } @@ -228,6 +236,14 @@ private: TimelineWorkArea *workarea_; TimelineMarkerList *markers_; + QTimer *catchup_scroll_timer_; + struct CatchUpScrollData { + qint64 last_forced = 0; + int maximum; + int value; + }; + QMap catchup_scroll_values_; + private slots: void UpdateMaximumScroll(); @@ -247,6 +263,8 @@ private slots: void CatchUpScrollToPoint(int point); + void CatchUpTimerTimeout(); + void AutoUpdateTimebase(); void ConnectedNodeRemovedFromGraph(); diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 3dc750a86..76dcc6e90 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -906,8 +906,7 @@ void TimelineWidget::ViewMouseMoved(TimelineViewMouseEvent *event) UpdateViewports(); - QMetaObject::invokeMethod(this, "CatchUpScrollToPoint", Qt::QueuedConnection, - Q_ARG(int, qRound(event->GetSceneX()))); + SetCatchUpScrollValue(event->GetScreenPos().x()); } else { // Mouse is not down, attempt a hover event TimelineTool* hover_tool = GetActiveTool(); @@ -922,6 +921,8 @@ void TimelineWidget::ViewMouseMoved(TimelineViewMouseEvent *event) void TimelineWidget::ViewMouseReleased(TimelineViewMouseEvent *event) { + StopCatchUpScrollTimer(); + if (active_tool_) { if (GetConnectedNode()) { active_tool_->MouseRelease(event); @@ -956,16 +957,22 @@ void TimelineWidget::ViewDragMoved(TimelineViewMouseEvent *event) { import_tool_->DragMove(event); UpdateViewports(); + + SetCatchUpScrollValue(event->GetScreenPos().x()); } void TimelineWidget::ViewDragLeft(QDragLeaveEvent *event) { + StopCatchUpScrollTimer(); + import_tool_->DragLeave(event); UpdateViewports(); } void TimelineWidget::ViewDragDropped(TimelineViewMouseEvent *event) { + StopCatchUpScrollTimer(); + import_tool_->DragDrop(event); UpdateViewports(); } diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index 35b40259e..71e953c5a 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -430,7 +430,8 @@ TimelineViewMouseEvent TimelineView::CreateMouseEvent(const QPoint& pos, Qt::Mou { QPointF scene_pt = mapToScene(pos); - return TimelineViewMouseEvent(scene_pt.x(), + return TimelineViewMouseEvent(scene_pt, + pos, GetScale(), timebase(), Track::Reference(ConnectedTrackType(), SceneToTrack(scene_pt.y())), diff --git a/app/widget/timelinewidget/view/timelineviewmouseevent.h b/app/widget/timelinewidget/view/timelineviewmouseevent.h index 79bd99d85..a37a5a9d8 100644 --- a/app/widget/timelinewidget/view/timelineviewmouseevent.h +++ b/app/widget/timelinewidget/view/timelineviewmouseevent.h @@ -34,13 +34,15 @@ namespace olive { class TimelineViewMouseEvent { public: - TimelineViewMouseEvent(const qreal& scene_x, + TimelineViewMouseEvent(const QPointF& scene_pos, + const QPoint &screen_pos, const double& scale_x, const rational& timebase, const Track::Reference &track, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers = Qt::NoModifier) : - scene_x_(scene_x), + scene_pos_(scene_pos), + screen_pos_(screen_pos), scale_x_(scale_x), timebase_(timebase), track_(track), @@ -73,7 +75,7 @@ public: */ rational GetFrame(bool round = false) const { - return TimeScaledObject::SceneToTime(scene_x_, scale_x_, timebase_, round); + return TimeScaledObject::SceneToTime(GetSceneX(), scale_x_, timebase_, round); } const Track::Reference& GetTrack() const @@ -96,11 +98,14 @@ public: source_event_ = event; } - const qreal& GetSceneX() const + qreal GetSceneX() const { - return scene_x_; + return scene_pos_.x(); } + const QPointF &GetScenePos() const { return scene_pos_; } + const QPoint &GetScreenPos() const { return screen_pos_; } + const Qt::MouseButton& GetButton() const { return button_; @@ -122,7 +127,8 @@ public: void SetBypassImportBuffer(bool e) { bypass_import_buffer_ = e; } private: - qreal scene_x_; + QPointF scene_pos_; + QPoint screen_pos_; double scale_x_; rational timebase_;