diff --git a/app/widget/handmovableview/handmovableview.h b/app/widget/handmovableview/handmovableview.h index 15fd8fc380..9fdaa53956 100644 --- a/app/widget/handmovableview/handmovableview.h +++ b/app/widget/handmovableview/handmovableview.h @@ -38,6 +38,8 @@ class HandMovableView : public QGraphicsView static qreal GetScrollZoomMultiplier(QWheelEvent* event); + virtual void CatchUpScrollEvent(){} + protected: virtual void ToolChangedEvent(Tool::Item tool){Q_UNUSED(tool)} diff --git a/app/widget/keyframeview/keyframeview.cpp b/app/widget/keyframeview/keyframeview.cpp index 1705f689cc..1558aef7dd 100644 --- a/app/widget/keyframeview/keyframeview.cpp +++ b/app/widget/keyframeview/keyframeview.cpp @@ -254,6 +254,15 @@ bool KeyframeView::Paste(std::function find_node_functi return false; } +void KeyframeView::CatchUpScrollEvent() +{ + super::CatchUpScrollEvent(); + + if (this->selection_manager_.IsRubberBanding()) { + this->selection_manager_.RubberBandMove(this->viewport()->mapFromGlobal(QCursor::pos())); + } +} + void KeyframeView::mousePressEvent(QMouseEvent *event) { NodeKeyframe *key_under_cursor = selection_manager_.GetObjectAtPoint(event->pos()); @@ -289,7 +298,7 @@ void KeyframeView::mouseMoveEvent(QMouseEvent *event) KeyframeDragMove(event, tip); selection_manager_.DragMove(event, tip); } else if (selection_manager_.IsRubberBanding()) { - selection_manager_.RubberBandMove(event); + selection_manager_.RubberBandMove(event->pos()); Redraw(); } @@ -313,12 +322,13 @@ 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(); emit SelectionChanged(); } + + emit Released(); } int BinarySearchFirstKeyframeAfterOrAt(const QVector &keys, const rational &time) @@ -617,6 +627,13 @@ void KeyframeView::ShowKeyframePropertiesDialog() } } +void KeyframeView::UpdateRubberBandForScroll() +{ + if (this->selection_manager_.IsRubberBanding()) { + this->selection_manager_.RubberBandMove(this->viewport()->mapFromGlobal(QCursor::pos())); + } +} + void KeyframeView::Redraw() { viewport()->update(); diff --git a/app/widget/keyframeview/keyframeview.h b/app/widget/keyframeview/keyframeview.h index 541b815833..b7c854777f 100644 --- a/app/widget/keyframeview/keyframeview.h +++ b/app/widget/keyframeview/keyframeview.h @@ -83,6 +83,8 @@ class KeyframeView : public TimeBasedView, public TimeTargetObject bool Paste(std::function find_node_function); + virtual void CatchUpScrollEvent() override; + signals: void Dragged(int current_x, int current_y); @@ -164,6 +166,8 @@ private slots: void ShowKeyframePropertiesDialog(); + void UpdateRubberBandForScroll(); + }; } diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index 5483083382..4060b81377 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -123,8 +123,8 @@ NodeParamView::NodeParamView(bool create_keyframe_view, QWidget *parent) : keyframe_area_layout->addWidget(keyframe_view_); // Connect ruler and keyframe view together - connect(keyframe_view_, &KeyframeView::Dragged, this, &NodeParamView::KeyframeViewDragged); - connect(keyframe_view_, &KeyframeView::Released, this, &NodeParamView::KeyframeViewReleased); + connect(keyframe_view_, &KeyframeView::Dragged, this, static_cast(&NodeParamView::SetCatchUpScrollValue)); + connect(keyframe_view_, &KeyframeView::Released, this, static_cast(&NodeParamView::StopCatchUpScrollTimer)); splitter->addWidget(keyframe_area); @@ -898,18 +898,6 @@ void NodeParamView::PinNode(bool pin) } }*/ -void NodeParamView::KeyframeViewDragged(int x, int y) -{ - Q_UNUSED(y) - - SetCatchUpScrollValue(x); -} - -void NodeParamView::KeyframeViewReleased() -{ - StopCatchUpScrollTimer(); -} - void NodeParamView::UpdateElementY() { for (NodeParamViewContext *ctx : context_items_) { diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index da63e638a6..3899f4e664 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -167,9 +167,6 @@ private slots: //void FocusChanged(QWidget *old, QWidget *now); - void KeyframeViewDragged(int x, int y); - void KeyframeViewReleased(); - void NodeAddedToContext(Node *n); void NodeRemovedFromContext(Node *n); diff --git a/app/widget/timebased/timebasedview.cpp b/app/widget/timebased/timebasedview.cpp index 8f83970fbc..2929d9a616 100644 --- a/app/widget/timebased/timebasedview.cpp +++ b/app/widget/timebased/timebasedview.cpp @@ -157,6 +157,16 @@ void TimeBasedView::SetViewerNode(ViewerOutput *v) } } +QPointF TimeBasedView::ScalePoint(const QPointF &p) const +{ + return QPointF(p.x() * GetScale(), p.y() * GetYScale()); +} + +QPointF TimeBasedView::UnscalePoint(const QPointF &p) const +{ + return QPointF(p.x() / GetScale(), p.y() / GetYScale()); +} + void TimeBasedView::drawForeground(QPainter *painter, const QRectF &rect) { QGraphicsView::drawForeground(painter, rect); diff --git a/app/widget/timebased/timebasedview.h b/app/widget/timebased/timebasedview.h index cb149b4450..5329570dc4 100644 --- a/app/widget/timebased/timebasedview.h +++ b/app/widget/timebased/timebasedview.h @@ -64,6 +64,9 @@ class TimeBasedView : public HandMovableView, public TimeScaledObject void SetViewerNode(ViewerOutput *v); + QPointF ScalePoint(const QPointF &p) const; + QPointF UnscalePoint(const QPointF &p) const; + public slots: void SetEndTime(const rational& length); diff --git a/app/widget/timebased/timebasedviewselectionmanager.h b/app/widget/timebased/timebasedviewselectionmanager.h index 2169170b2a..07ff10c3c3 100644 --- a/app/widget/timebased/timebasedviewselectionmanager.h +++ b/app/widget/timebased/timebasedviewselectionmanager.h @@ -53,9 +53,10 @@ class TimeBasedViewSelectionManager drawn_objects_.clear(); } - void DeclareDrawnObject(T *object, const QRectF &pos) + void DeclareDrawnObject(T *object, const QRectF &rect) { - drawn_objects_.push_back({object, pos}); + QRectF r(view_->UnscalePoint(rect.topLeft()), view_->UnscalePoint(rect.bottomRight())); + drawn_objects_.push_back({object, r}); } bool Select(T *key) @@ -345,23 +346,24 @@ class TimeBasedViewSelectionManager void RubberBandStart(QMouseEvent *event) { if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) { - rubberband_start_ = event->pos(); + rubberband_scene_start_ = view_->UnscalePoint(view_->mapToScene(event->pos())); rubberband_ = new QRubberBand(QRubberBand::Rectangle, view_); - rubberband_->setGeometry(QRect(rubberband_start_.x(), rubberband_start_.y(), 0, 0)); + rubberband_->setGeometry(QRect(event->pos().x(), event->pos().y(), 0, 0)); rubberband_->show(); rubberband_preselected_ = selected_; } } - void RubberBandMove(QMouseEvent *event) + void RubberBandMove(const QPoint &pos) { if (IsRubberBanding()) { - QRect band_rect = QRect(rubberband_start_, event->pos()).normalized(); - rubberband_->setGeometry(band_rect); + QRectF band_rect = QRectF(view_->mapFromScene(view_->ScalePoint(rubberband_scene_start_)), pos).normalized(); + rubberband_->setGeometry(band_rect.toRect()); - QRectF scene_rect = view_->mapToScene(band_rect).boundingRect(); + QPointF current = view_->UnscalePoint(view_->mapToScene(pos)); + QRectF scene_rect = QRectF(rubberband_scene_start_, current).normalized(); selected_ = rubberband_preselected_; foreach (const DrawnObject &kp, drawn_objects_) { @@ -445,7 +447,7 @@ class TimeBasedViewSelectionManager rational timebase_; QRubberBand *rubberband_; - QPoint rubberband_start_; + QPointF rubberband_scene_start_; std::vector rubberband_preselected_; TimeBasedWidget::SnapMask snap_mask_; diff --git a/app/widget/timebased/timebasedwidget.cpp b/app/widget/timebased/timebasedwidget.cpp index 945bb34ea8..1e59890242 100644 --- a/app/widget/timebased/timebasedwidget.cpp +++ b/app/widget/timebased/timebasedwidget.cpp @@ -49,6 +49,7 @@ TimeBasedWidget::TimeBasedWidget(bool ruler_text_visible, bool ruler_cache_statu ruler_ = new TimeRuler(ruler_text_visible, ruler_cache_status_visible, this); ConnectTimelineView(ruler_); ruler()->SetSnapService(this); + connect(ruler(), &TimeRuler::DragMoved, this, static_cast(&TimeBasedWidget::SetCatchUpScrollValue)); connect(ruler(), &TimeRuler::DragReleased, this, static_cast(&TimeBasedWidget::StopCatchUpScrollTimer)); catchup_scroll_timer_ = new QTimer(this); @@ -230,6 +231,15 @@ void TimeBasedWidget::CatchUpTimerTimeout() const CatchUpScrollData &d = it.value(); PageScrollInternal(sb, d.maximum, sb->value() + d.value, false); } + + SendCatchUpScrollEvent(); +} + +void TimeBasedWidget::SendCatchUpScrollEvent() +{ + for (auto v : this->timeline_views_) { + v->CatchUpScrollEvent(); + } } void TimeBasedWidget::AutoUpdateTimebase() @@ -283,6 +293,8 @@ void TimeBasedWidget::ScaleChangedEvent(const double &scale) UpdateMaximumScroll(); + QMetaObject::invokeMethod(this, &TimeBasedWidget::SendCatchUpScrollEvent, Qt::QueuedConnection); + toggle_show_all_ = false; } diff --git a/app/widget/timebased/timebasedwidget.h b/app/widget/timebased/timebasedwidget.h index 51e35dd77c..157556c630 100644 --- a/app/widget/timebased/timebasedwidget.h +++ b/app/widget/timebased/timebasedwidget.h @@ -141,7 +141,6 @@ public slots: void ConnectTimelineView(TimeBasedView* base); void SetCatchUpScrollValue(QScrollBar *b, int v, int maximum); - void SetCatchUpScrollValue(int v); void StopCatchUpScrollTimer(QScrollBar *b); virtual const QVector *GetSnapBlocks() const { return nullptr; } @@ -169,11 +168,16 @@ protected slots: StopCatchUpScrollTimer(scrollbar_); } + void SetCatchUpScrollValue(int v); + signals: void TimebaseChanged(const rational&); void ConnectedNodeChanged(ViewerOutput* old, ViewerOutput* now); +protected slots: + virtual void SendCatchUpScrollEvent(); + private: /** * @brief Set either in or out point to the current playhead diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 0af5e74be7..450561be3c 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -274,6 +274,10 @@ void TimelineWidget::ScaleChangedEvent(const double &scale) foreach (TimelineAndTrackView* view, views_) { view->view()->SetScale(scale); } + + if (rubberband_.isVisible()) { + QMetaObject::invokeMethod(this, &TimelineWidget::ForceUpdateRubberBand, Qt::QueuedConnection); + } } void TimelineWidget::ConnectNodeEvent(ViewerOutput *n) @@ -339,6 +343,15 @@ void TimelineWidget::DisconnectNodeEvent(ViewerOutput *n) } } +void TimelineWidget::SendCatchUpScrollEvent() +{ + super::SendCatchUpScrollEvent(); + + if (rubberband_.isVisible()) { + this->ForceUpdateRubberBand(); + } +} + void TimelineWidget::SelectAll() { QVector newly_selected_blocks; @@ -1564,6 +1577,13 @@ void TimelineWidget::MulticamEnabledTriggered(bool e) Core::instance()->undo_stack()->pushIfHasChildren(command); } +void TimelineWidget::ForceUpdateRubberBand() +{ + if (rubberband_.isVisible()) { + this->MoveRubberBandSelect(rubberband_enable_selecting_, rubberband_select_links_); + } +} + void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost) { ghost_items_.append(ghost); @@ -1912,46 +1932,6 @@ void TimelineWidget::UpdateViewports(const Track::Type &type) } } -QVector TimelineWidget::GetBlocksInGlobalRect(const QPoint &p1, const QPoint& p2) -{ - QVector blocks_in_rect; - - // Determine which tracks are in the rect - for (int i=0; iview(); - - // Map global mouse coordinates to viewport - QRectF mapped_rect(view->mapToScene(view->viewport()->mapFromGlobal(p1)), - view->mapToScene(view->viewport()->mapFromGlobal(p2))); - - // Normalize - mapped_rect = mapped_rect.normalized(); - - // Get tracks - TrackList* track_list = sequence()->track_list(static_cast(i)); - - for (int j=0; jGetTrackCount(); j++) { - int track_top = view->GetTrackY(j); - int track_bottom = track_top + view->GetTrackHeight(j); - - if (!(track_bottom < mapped_rect.top() || track_top > mapped_rect.bottom())) { - // This track is in the rect, so we'll iterate through its blocks and see where they start - rational left_time = SceneToTime(mapped_rect.left()); - rational right_time = SceneToTime(mapped_rect.right(), true); - - Track* track = track_list->GetTrackAt(j); - foreach (Block* b, track->Blocks()) { - if (!(b->out() < left_time || b->in() > right_time)) { - blocks_in_rect.append(b); - } - } - } - } - } - - return blocks_in_rect; -} - bool TimelineWidget::PasteInternal(bool insert) { if (!GetConnectedNode()) { @@ -2039,11 +2019,12 @@ void TimelineWidget::RestoreSplitterState(const QByteArray &state) void TimelineWidget::StartRubberBandSelect(const QPoint &global_cursor_start) { - drag_origin_ = global_cursor_start; - - // Start rubberband at origin - QPoint local_origin = mapFromGlobal(drag_origin_); - rubberband_.setGeometry(QRect(local_origin.x(), local_origin.y(), 0, 0)); + // Store scene positions for each view + rubberband_scene_pos_.resize(views_.size()); + for (int i = 0; i < rubberband_scene_pos_.size(); i++) { + TimelineView *v = views_.at(i)->view(); + rubberband_scene_pos_[i] = v->UnscalePoint(v->mapToScene(v->mapFromGlobal(global_cursor_start))); + } rubberband_.show(); @@ -2056,14 +2037,30 @@ void TimelineWidget::MoveRubberBandSelect(bool enable_selecting, bool select_lin { QPoint rubberband_now = QCursor::pos(); - rubberband_.setGeometry(QRect(mapFromGlobal(drag_origin_), mapFromGlobal(rubberband_now)).normalized()); + TimelineView *fv = views_.first()->view(); + const QPointF &rubberband_scene_start = rubberband_scene_pos_.at(0); + QPointF rubberband_now_scaled = fv->UnscalePoint(fv->mapToScene(fv->mapFromGlobal(rubberband_now))); + + QPoint rubberband_local_start = fv->mapTo(this, fv->mapFromScene(fv->ScalePoint(rubberband_scene_start))); + QPoint rubberband_local_now = fv->mapTo(this, fv->mapFromScene(fv->ScalePoint(rubberband_now_scaled))); + + rubberband_.setGeometry(QRect(rubberband_local_start, rubberband_local_now).normalized()); + + rubberband_enable_selecting_ = enable_selecting; + rubberband_select_links_ = select_links; if (!enable_selecting) { return; } // Get current items in rubberband - QVector items_in_rubberband = GetBlocksInGlobalRect(drag_origin_, rubberband_now); + QVector items_in_rubberband; + + for (int i = 0; i < views_.size(); i++) { + TimelineView *v = views_.at(i)->view(); + QRectF r = QRectF(v->ScalePoint(rubberband_scene_pos_.at(i)), v->mapToScene(v->mapFromGlobal(rubberband_now))).normalized(); + items_in_rubberband.append(v->GetItemsAtSceneRect(r)); + } // Reset selection to whatever it was before SetSelections(rubberband_old_selections_, false); diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 4a49bcfb76..112fdc987c 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -299,6 +299,9 @@ public slots: virtual const QVector *GetSnapBlocks() const override { return &added_blocks_; } +protected slots: + virtual void SendCatchUpScrollEvent() override; + private: QVector GetEditToInfo(const rational &playhead_time, Timeline::MovementMode mode); @@ -308,19 +311,18 @@ public slots: void UpdateViewports(const Track::Type& type = Track::kNone); - QVector GetBlocksInGlobalRect(const QPoint &p1, const QPoint &p2); - bool PasteInternal(bool insert); TimelineAndTrackView *AddTimelineAndTrackView(Qt::Alignment alignment); QHash GenerateExistingPasteMap(const ProjectSerializer::Result &r); - QPoint drag_origin_; - QRubberBand rubberband_; + QVector rubberband_scene_pos_; TimelineWidgetSelections rubberband_old_selections_; QVector rubberband_now_selected_; + bool rubberband_enable_selecting_; + bool rubberband_select_links_; TimelineWidgetSelections selections_; @@ -446,6 +448,8 @@ private slots: void MulticamEnabledTriggered(bool e); + void ForceUpdateRubberBand(); + }; } diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index d5b6e77b1b..306a82f21a 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -867,6 +867,34 @@ Block *TimelineView::GetItemAtScenePos(const rational &time, int track_index) co return nullptr; } +QVector TimelineView::GetItemsAtSceneRect(const QRectF &rect) const +{ + QVector list; + + if (connected_track_list_) { + rational start = this->SceneToTime(rect.left()); + rational end = this->SceneToTime(rect.right()); + + for (int i = 0; i < connected_track_list_->GetTrackCount(); i++) { + Track *track = connected_track_list_->GetTrackAt(i); + int track_top = GetTrackY(i); + int track_bottom = track_top + GetTrackHeight(i); + + if (track) { + if (!(track_bottom < rect.top() || track_top > rect.bottom())) { + Block *b = track->NearestBlockBeforeOrAt(start); + while (b && b->in() < end) { + list.append(b); + b = b->next(); + } + } + } + } + } + + return list; +} + void TimelineView::TrackListChanged() { UpdateSceneRect(); diff --git a/app/widget/timelinewidget/view/timelineview.h b/app/widget/timelinewidget/view/timelineview.h index a1e1698a96..103850566b 100644 --- a/app/widget/timelinewidget/view/timelineview.h +++ b/app/widget/timelinewidget/view/timelineview.h @@ -73,6 +73,8 @@ class TimelineView : public TimeBasedView Block* GetItemAtScenePos(const rational& time, int track_index) const; + QVector GetItemsAtSceneRect(const QRectF &rect) const; + signals: void MousePressed(TimelineViewMouseEvent* event); void MouseMoved(TimelineViewMouseEvent* event); diff --git a/app/widget/timeruler/seekablewidget.cpp b/app/widget/timeruler/seekablewidget.cpp index 4df431c979..7f9324dc19 100644 --- a/app/widget/timeruler/seekablewidget.cpp +++ b/app/widget/timeruler/seekablewidget.cpp @@ -206,7 +206,7 @@ void SeekableWidget::mouseMoveEvent(QMouseEvent *event) if (HandMove(event)) { return; } else if (selection_manager_.IsRubberBanding()) { - selection_manager_.RubberBandMove(event); + selection_manager_.RubberBandMove(event->pos()); viewport()->update(); } else if (selection_manager_.IsDragging()) { selection_manager_.DragMove(event); @@ -228,6 +228,11 @@ void SeekableWidget::mouseMoveEvent(QMouseEvent *event) ClearResizeHandle(); } } + + if (event->buttons()) { + // Signal cursor pos in case we should scroll to catch up to it + emit DragMoved(event->pos().x(), event->pos().y()); + } } void SeekableWidget::mouseReleaseEvent(QMouseEvent *event) @@ -413,6 +418,15 @@ void SeekableWidget::SelectionManagerDeselectEvent(void *obj) viewport()->update(); } +void SeekableWidget::CatchUpScrollEvent() +{ + super::CatchUpScrollEvent(); + + if (this->selection_manager_.IsRubberBanding()) { + this->selection_manager_.RubberBandMove(this->viewport()->mapFromGlobal(QCursor::pos())); + } +} + void SeekableWidget::DrawPlayhead(QPainter *p, int x, int y) { int half_width = playhead_width_ / 2; diff --git a/app/widget/timeruler/seekablewidget.h b/app/widget/timeruler/seekablewidget.h index bf0b248a68..3d0a5d9851 100644 --- a/app/widget/timeruler/seekablewidget.h +++ b/app/widget/timeruler/seekablewidget.h @@ -77,6 +77,8 @@ class SeekableWidget : public TimeBasedView virtual void SelectionManagerSelectEvent(void *obj) override; virtual void SelectionManagerDeselectEvent(void *obj) override; + virtual void CatchUpScrollEvent() override; + public slots: void SetScroll(int i) { @@ -86,6 +88,8 @@ public slots: virtual void TimebaseChangedEvent(const rational &) override; signals: + void DragMoved(int x, int y); + void DragReleased(); protected: