timebasedwidget: rewrote scroll catchup code

Code is much better. Less hacky queuing, implements cooldowns to improve navigation, and introduces a framework so that subclasses can make use of catchup/cooldown code too with any scrollbar they want
This commit is contained in:
itsmattkc
2022-07-15 10:59:51 -07:00
parent 4b8febd10d
commit e4fd2d5d2c
11 changed files with 103 additions and 20 deletions
+6 -6
View File
@@ -103,6 +103,7 @@ CurveWidget::CurveWidget(QWidget *parent) :
connect(view_, &CurveView::SelectionChanged, this, &CurveWidget::SelectionChanged); connect(view_, &CurveView::SelectionChanged, this, &CurveWidget::SelectionChanged);
connect(view_, &CurveView::ScaleChanged, this, &CurveWidget::SetScale); connect(view_, &CurveView::ScaleChanged, this, &CurveWidget::SetScale);
connect(view_, &CurveView::Dragged, this, &CurveWidget::KeyframeViewDragged); 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 // TimeBasedWidget's scrollbar has extra functionality that we can take advantage of
view_->setHorizontalScrollBar(scrollbar()); view_->setHorizontalScrollBar(scrollbar());
@@ -379,15 +380,14 @@ void CurveWidget::InputSelectionChanged(const NodeKeyframeTrackReference& ref)
void CurveWidget::KeyframeViewDragged(int x, int y) void CurveWidget::KeyframeViewDragged(int x, int y)
{ {
QMetaObject::invokeMethod(this, "CatchUpScrollToPoint", Qt::QueuedConnection, SetCatchUpScrollValue(x);
Q_ARG(int, x)); SetCatchUpScrollValue(view_->verticalScrollBar(), y, view_->height());
QMetaObject::invokeMethod(this, "CatchUpYScrollToPoint", Qt::QueuedConnection,
Q_ARG(int, y));
} }
void CurveWidget::CatchUpYScrollToPoint(int point) void CurveWidget::KeyframeViewReleased()
{ {
PageScrollInternal(view_->verticalScrollBar(), view_->height(), point, false); StopCatchUpScrollTimer();
StopCatchUpScrollTimer(view_->verticalScrollBar());
} }
} }
+1 -2
View File
@@ -122,8 +122,7 @@ private slots:
void InputSelectionChanged(const NodeKeyframeTrackReference& ref); void InputSelectionChanged(const NodeKeyframeTrackReference& ref);
void KeyframeViewDragged(int x, int y); void KeyframeViewDragged(int x, int y);
void KeyframeViewReleased();
void CatchUpYScrollToPoint(int point);
}; };
+2 -2
View File
@@ -290,8 +290,7 @@ void KeyframeView::mouseMoveEvent(QMouseEvent *event)
if (event->buttons()) { if (event->buttons()) {
// Signal cursor pos in case we should scroll to catch up to it // Signal cursor pos in case we should scroll to catch up to it
QPointF scene_pos = mapToScene(event->pos()); emit Dragged(event->pos().x(), event->pos().y());
emit Dragged(scene_pos.x(), scene_pos.y());
} }
} }
@@ -309,6 +308,7 @@ void KeyframeView::mouseReleaseEvent(QMouseEvent *event)
selection_manager_.DragStop(command); selection_manager_.DragStop(command);
KeyframeDragRelease(event, command); KeyframeDragRelease(event, command);
Core::instance()->undo_stack()->push(command); Core::instance()->undo_stack()->push(command);
emit Released();
} else if (selection_manager_.IsRubberBanding()) { } else if (selection_manager_.IsRubberBanding()) {
selection_manager_.RubberBandStop(); selection_manager_.RubberBandStop();
Redraw(); Redraw();
+2
View File
@@ -88,6 +88,8 @@ signals:
void SelectionChanged(); void SelectionChanged();
void Released();
protected: protected:
virtual void mousePressEvent(QMouseEvent *event) override; virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override;
+7 -1
View File
@@ -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, ruler(), &TimeRuler::SetTime);
connect(keyframe_view_, &KeyframeView::TimeChanged, this, &NodeParamView::SetTime); connect(keyframe_view_, &KeyframeView::TimeChanged, this, &NodeParamView::SetTime);
connect(keyframe_view_, &KeyframeView::Dragged, this, &NodeParamView::KeyframeViewDragged); 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 scaling to this
connect(keyframe_view_, &KeyframeView::ScaleChanged, this, &NodeParamView::SetScale); connect(keyframe_view_, &KeyframeView::ScaleChanged, this, &NodeParamView::SetScale);
@@ -929,7 +930,12 @@ void NodeParamView::KeyframeViewDragged(int x, int y)
{ {
Q_UNUSED(y) Q_UNUSED(y)
QMetaObject::invokeMethod(this, "CatchUpScrollToPoint", Qt::QueuedConnection, Q_ARG(int, x)); SetCatchUpScrollValue(x);
}
void NodeParamView::KeyframeViewReleased()
{
StopCatchUpScrollTimer();
} }
void NodeParamView::UpdateElementY() void NodeParamView::UpdateElementY()
+1
View File
@@ -165,6 +165,7 @@ private slots:
//void FocusChanged(QWidget *old, QWidget *now); //void FocusChanged(QWidget *old, QWidget *now);
void KeyframeViewDragged(int x, int y); void KeyframeViewDragged(int x, int y);
void KeyframeViewReleased();
void NodeAddedToContext(Node *n); void NodeAddedToContext(Node *n);
+43
View File
@@ -52,6 +52,10 @@ TimeBasedWidget::TimeBasedWidget(bool ruler_text_visible, bool ruler_cache_statu
connect(scrollbar_, &ResizableScrollBar::ResizeMoved, this, &TimeBasedWidget::ScrollBarResizeMoved); connect(scrollbar_, &ResizableScrollBar::ResizeMoved, this, &TimeBasedWidget::ScrollBarResizeMoved);
PassWheelEventsToScrollBar(ruler_); 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) void TimeBasedWidget::SetScaleAndCenterOnPlayhead(const double &scale)
@@ -217,6 +221,15 @@ void TimeBasedWidget::CatchUpScrollToPoint(int point)
PageScrollInternal(point, false); 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() void TimeBasedWidget::AutoUpdateTimebase()
{ {
rational video_tb = viewer_node_->GetVideoParams().frame_rate_as_time_base(); rational video_tb = viewer_node_->GetVideoParams().frame_rate_as_time_base();
@@ -301,6 +314,36 @@ void TimeBasedWidget::PassWheelEventsToScrollBar(QObject *object)
object->installEventFilter(this); 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) void TimeBasedWidget::SetTime(const rational &time)
{ {
if (UserIsDraggingPlayhead()) { if (UserIsDraggingPlayhead()) {
+18
View File
@@ -150,6 +150,14 @@ protected:
void PassWheelEventsToScrollBar(QObject* object); 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<Block*> *GetSnapBlocks() const { return nullptr; } virtual const QVector<Block*> *GetSnapBlocks() const { return nullptr; }
virtual const QVector<KeyframeViewInputConnection*> *GetSnapKeyframes() const { return nullptr; } virtual const QVector<KeyframeViewInputConnection*> *GetSnapKeyframes() const { return nullptr; }
virtual const std::vector<NodeKeyframe*> *GetSnapIgnoreKeyframes() const { return nullptr; } virtual const std::vector<NodeKeyframe*> *GetSnapIgnoreKeyframes() const { return nullptr; }
@@ -228,6 +236,14 @@ private:
TimelineWorkArea *workarea_; TimelineWorkArea *workarea_;
TimelineMarkerList *markers_; TimelineMarkerList *markers_;
QTimer *catchup_scroll_timer_;
struct CatchUpScrollData {
qint64 last_forced = 0;
int maximum;
int value;
};
QMap<QScrollBar*, CatchUpScrollData> catchup_scroll_values_;
private slots: private slots:
void UpdateMaximumScroll(); void UpdateMaximumScroll();
@@ -247,6 +263,8 @@ private slots:
void CatchUpScrollToPoint(int point); void CatchUpScrollToPoint(int point);
void CatchUpTimerTimeout();
void AutoUpdateTimebase(); void AutoUpdateTimebase();
void ConnectedNodeRemovedFromGraph(); void ConnectedNodeRemovedFromGraph();
+9 -2
View File
@@ -906,8 +906,7 @@ void TimelineWidget::ViewMouseMoved(TimelineViewMouseEvent *event)
UpdateViewports(); UpdateViewports();
QMetaObject::invokeMethod(this, "CatchUpScrollToPoint", Qt::QueuedConnection, SetCatchUpScrollValue(event->GetScreenPos().x());
Q_ARG(int, qRound(event->GetSceneX())));
} else { } else {
// Mouse is not down, attempt a hover event // Mouse is not down, attempt a hover event
TimelineTool* hover_tool = GetActiveTool(); TimelineTool* hover_tool = GetActiveTool();
@@ -922,6 +921,8 @@ void TimelineWidget::ViewMouseMoved(TimelineViewMouseEvent *event)
void TimelineWidget::ViewMouseReleased(TimelineViewMouseEvent *event) void TimelineWidget::ViewMouseReleased(TimelineViewMouseEvent *event)
{ {
StopCatchUpScrollTimer();
if (active_tool_) { if (active_tool_) {
if (GetConnectedNode()) { if (GetConnectedNode()) {
active_tool_->MouseRelease(event); active_tool_->MouseRelease(event);
@@ -956,16 +957,22 @@ void TimelineWidget::ViewDragMoved(TimelineViewMouseEvent *event)
{ {
import_tool_->DragMove(event); import_tool_->DragMove(event);
UpdateViewports(); UpdateViewports();
SetCatchUpScrollValue(event->GetScreenPos().x());
} }
void TimelineWidget::ViewDragLeft(QDragLeaveEvent *event) void TimelineWidget::ViewDragLeft(QDragLeaveEvent *event)
{ {
StopCatchUpScrollTimer();
import_tool_->DragLeave(event); import_tool_->DragLeave(event);
UpdateViewports(); UpdateViewports();
} }
void TimelineWidget::ViewDragDropped(TimelineViewMouseEvent *event) void TimelineWidget::ViewDragDropped(TimelineViewMouseEvent *event)
{ {
StopCatchUpScrollTimer();
import_tool_->DragDrop(event); import_tool_->DragDrop(event);
UpdateViewports(); UpdateViewports();
} }
@@ -430,7 +430,8 @@ TimelineViewMouseEvent TimelineView::CreateMouseEvent(const QPoint& pos, Qt::Mou
{ {
QPointF scene_pt = mapToScene(pos); QPointF scene_pt = mapToScene(pos);
return TimelineViewMouseEvent(scene_pt.x(), return TimelineViewMouseEvent(scene_pt,
pos,
GetScale(), GetScale(),
timebase(), timebase(),
Track::Reference(ConnectedTrackType(), SceneToTrack(scene_pt.y())), Track::Reference(ConnectedTrackType(), SceneToTrack(scene_pt.y())),
@@ -34,13 +34,15 @@ namespace olive {
class TimelineViewMouseEvent class TimelineViewMouseEvent
{ {
public: public:
TimelineViewMouseEvent(const qreal& scene_x, TimelineViewMouseEvent(const QPointF& scene_pos,
const QPoint &screen_pos,
const double& scale_x, const double& scale_x,
const rational& timebase, const rational& timebase,
const Track::Reference &track, const Track::Reference &track,
const Qt::MouseButton &button, const Qt::MouseButton &button,
const Qt::KeyboardModifiers& modifiers = Qt::NoModifier) : const Qt::KeyboardModifiers& modifiers = Qt::NoModifier) :
scene_x_(scene_x), scene_pos_(scene_pos),
screen_pos_(screen_pos),
scale_x_(scale_x), scale_x_(scale_x),
timebase_(timebase), timebase_(timebase),
track_(track), track_(track),
@@ -73,7 +75,7 @@ public:
*/ */
rational GetFrame(bool round = false) const 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 const Track::Reference& GetTrack() const
@@ -96,11 +98,14 @@ public:
source_event_ = event; 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 const Qt::MouseButton& GetButton() const
{ {
return button_; return button_;
@@ -122,7 +127,8 @@ public:
void SetBypassImportBuffer(bool e) { bypass_import_buffer_ = e; } void SetBypassImportBuffer(bool e) { bypass_import_buffer_ = e; }
private: private:
qreal scene_x_; QPointF scene_pos_;
QPoint screen_pos_;
double scale_x_; double scale_x_;
rational timebase_; rational timebase_;