From ec4d061857fd161af1426dfd246ff342929d37a7 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 23 Jan 2021 08:19:04 +1100 Subject: [PATCH] improved resizable scrollbar functionality --- .../resizablescrollbar/resizablescrollbar.cpp | 79 +++++++++---------- .../resizablescrollbar/resizablescrollbar.h | 14 +++- app/widget/timebased/timebasedwidget.cpp | 55 +++++++++---- app/widget/timebased/timebasedwidget.h | 9 ++- 4 files changed, 94 insertions(+), 63 deletions(-) diff --git a/app/widget/resizablescrollbar/resizablescrollbar.cpp b/app/widget/resizablescrollbar/resizablescrollbar.cpp index d7cbfb8e8..0ff76a48b 100644 --- a/app/widget/resizablescrollbar/resizablescrollbar.cpp +++ b/app/widget/resizablescrollbar/resizablescrollbar.cpp @@ -48,53 +48,23 @@ void ResizableScrollBar::mousePressEvent(QMouseEvent *event) if (mouse_handle_state_ == kNotInHandle) { QScrollBar::mousePressEvent(event); } else { - mouse_dragging_ = true; + dragging_ = true; - mouse_drag_start_ = GetActiveMousePos(event); + drag_start_point_ = GetActiveMousePos(event); + + emit ResizeBegan(GetActiveBarSize(), (mouse_handle_state_ == kInTopHandle)); } } void ResizableScrollBar::mouseMoveEvent(QMouseEvent *event) { - QStyleOptionSlider opt; - initStyleOption(&opt); + QRect sr = GetScrollBarRect(); - QRect sr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, - QStyle::SC_ScrollBarSlider, this); + if (dragging_) { + // Determine how much the cursor has moved + int mouse_movement = GetActiveMousePos(event) - drag_start_point_; - if (mouse_dragging_) { - int new_drag_pos = GetActiveMousePos(event); - int mouse_movement = new_drag_pos - mouse_drag_start_; - mouse_drag_start_ = new_drag_pos; - - if (mouse_handle_state_ == kInTopHandle) { - mouse_movement = -mouse_movement; - } - - double width_adjustment = static_cast(sr.width() + mouse_movement); - - // Prevent dividing by zero or emitting a negative scale - if (width_adjustment > 0) { - double scale_multiplier = static_cast(sr.width()) / width_adjustment; - emit RequestScale(scale_multiplier); - - if (mouse_handle_state_ == kInTopHandle) { - QRect gr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, - QStyle::SC_ScrollBarGroove, this); - - int slider_min = gr.x(); - int slider_max = gr.right() - (sr.width() + mouse_movement); - int val = QStyle::sliderValueFromPosition(minimum(), - maximum(), - event->pos().x() - slider_min, - slider_max - slider_min, - opt.upsideDown); - - setValue(val); - } else { - setValue(qRound(static_cast(value()) * scale_multiplier)); - } - } + emit ResizeMoved(mouse_movement); } else { @@ -133,13 +103,27 @@ void ResizableScrollBar::mouseMoveEvent(QMouseEvent *event) void ResizableScrollBar::mouseReleaseEvent(QMouseEvent *event) { - if (mouse_dragging_) { - mouse_dragging_ = false; + if (dragging_) { + dragging_ = false; + + emit ResizeEnded(); } else { QScrollBar::mouseReleaseEvent(event); } } +QRect ResizableScrollBar::GetScrollBarRect() +{ + // Initialize "style option". I don't know what this does, I just ripped it straight from + // Qt source code + QStyleOptionSlider opt; + initStyleOption(&opt); + + // Determine rect of slider bar + return style()->subControlRect(QStyle::CC_ScrollBar, &opt, + QStyle::SC_ScrollBarSlider, this); +} + void ResizableScrollBar::Init() { setSingleStep(20); @@ -147,7 +131,7 @@ void ResizableScrollBar::Init() setMouseTracking(true); mouse_handle_state_= kNotInHandle; - mouse_dragging_ = false; + dragging_ = false; } int ResizableScrollBar::GetActiveMousePos(QMouseEvent *event) @@ -159,4 +143,15 @@ int ResizableScrollBar::GetActiveMousePos(QMouseEvent *event) } } +int ResizableScrollBar::GetActiveBarSize() +{ + QRect sr = GetScrollBarRect(); + + if (orientation() == Qt::Horizontal) { + return sr.width(); + } else { + return sr.height(); + } +} + } diff --git a/app/widget/resizablescrollbar/resizablescrollbar.h b/app/widget/resizablescrollbar/resizablescrollbar.h index 1e0d21f13..8fb887bbd 100644 --- a/app/widget/resizablescrollbar/resizablescrollbar.h +++ b/app/widget/resizablescrollbar/resizablescrollbar.h @@ -35,7 +35,11 @@ public: ResizableScrollBar(Qt::Orientation orientation, QWidget* parent = nullptr); signals: - void RequestScale(const double& multiplier); + void ResizeBegan(int old_bar_width, bool top_handle); + + void ResizeMoved(int movement); + + void ResizeEnded(); protected: virtual void mousePressEvent(QMouseEvent* event) override; @@ -45,6 +49,8 @@ protected: virtual void mouseReleaseEvent(QMouseEvent* event) override; private: + QRect GetScrollBarRect(); + static const int kHandleWidth; enum MouseHandleState { @@ -57,11 +63,13 @@ private: int GetActiveMousePos(QMouseEvent* event); + int GetActiveBarSize(); + MouseHandleState mouse_handle_state_; - bool mouse_dragging_; + bool dragging_; - int mouse_drag_start_; + int drag_start_point_; }; diff --git a/app/widget/timebased/timebasedwidget.cpp b/app/widget/timebased/timebasedwidget.cpp index 17836f59a..7267161f9 100644 --- a/app/widget/timebased/timebasedwidget.cpp +++ b/app/widget/timebased/timebasedwidget.cpp @@ -44,7 +44,8 @@ TimeBasedWidget::TimeBasedWidget(bool ruler_text_visible, bool ruler_cache_statu connect(ruler_, &TimeRuler::TimeChanged, this, &TimeBasedWidget::SetTimeAndSignal); scrollbar_ = new ResizableTimelineScrollBar(Qt::Horizontal, this); - connect(scrollbar_, &ResizableScrollBar::RequestScale, this, &TimeBasedWidget::ScrollBarResized); + connect(scrollbar_, &ResizableScrollBar::ResizeBegan, this, &TimeBasedWidget::ScrollBarResizeBegan); + connect(scrollbar_, &ResizableScrollBar::ResizeMoved, this, &TimeBasedWidget::ScrollBarResizeMoved); PassWheelEventsToScrollBar(ruler_); } @@ -126,7 +127,7 @@ void TimeBasedWidget::ConnectViewerNode(ViewerOutput *node) void TimeBasedWidget::UpdateMaximumScroll() { - rational length = (viewer_node_) ? viewer_node_->GetLength() : rational(); + rational length = (viewer_node_) ? viewer_node_->GetLength() : 0; if (auto_max_scrollbar_) { scrollbar_->setMaximum(qMax(0, qCeil(TimeToScene(length)) - width())); @@ -137,27 +138,47 @@ void TimeBasedWidget::UpdateMaximumScroll() } } -void TimeBasedWidget::ScrollBarResized(const double &multiplier) +void TimeBasedWidget::ScrollBarResizeBegan(int current_bar_width, bool top_handle) { QScrollBar* bar = static_cast(sender()); - // Our extension area (represented by a TimelineViewEndItem) is NOT scaled, but the ResizableScrollBar doesn't know - // this. Here we re-calculate the requested scale knowing that the end item is not affected by scale. + scrollbar_start_width_ = current_bar_width; + scrollbar_start_value_ = bar->value(); + scrollbar_start_scale_ = GetScale(); + scrollbar_top_handle_ = top_handle; +} - int current_max = bar->maximum(); - double proposed_max = static_cast(current_max) * multiplier; +void TimeBasedWidget::ScrollBarResizeMoved(int movement) +{ + ResizableScrollBar* bar = static_cast(sender()); - proposed_max = proposed_max - (bar->width() * 0.5 / multiplier) + (bar->width() * 0.5); - - double corrected_scale; - - if (current_max == 0) { - corrected_scale = multiplier; - } else { - corrected_scale = (proposed_max / static_cast(current_max)); + // Negate movement for the top handle + if (scrollbar_top_handle_) { + movement = -movement; } - SetScale(GetScale() * corrected_scale); + // The user wants the bar to be this size + int proposed_size = scrollbar_start_width_ + movement; + + double ratio = double(scrollbar_start_width_) / double(proposed_size); + + if (ratio > 0) { + SetScale(scrollbar_start_scale_ * ratio); + + if (scrollbar_top_handle_) { + int viewable_area; + + if (timeline_views_.isEmpty()) { + viewable_area = width(); + } else { + viewable_area = timeline_views_.first()->width(); + } + + bar->setValue((scrollbar_start_value_ + viewable_area) * ratio - viewable_area); + } else { + bar->setValue(scrollbar_start_value_ * ratio); + } + } } void TimeBasedWidget::PageScrollToPlayhead() @@ -492,7 +513,7 @@ void TimeBasedWidget::SetMarker() bool ok; QString marker_name; - if (Config::Current()["SetNameWithMarker"].toBool()) { + if (Config::Current()[QStringLiteral("SetNameWithMarker")].toBool()) { marker_name = QInputDialog::getText(this, tr("Set Marker"), tr("Marker name:"), QLineEdit::Normal, QString(), &ok); } else { ok = true; diff --git a/app/widget/timebased/timebasedwidget.h b/app/widget/timebased/timebasedwidget.h index 089e5a209..253a32acf 100644 --- a/app/widget/timebased/timebasedwidget.h +++ b/app/widget/timebased/timebasedwidget.h @@ -205,10 +205,17 @@ private: QVector wheel_passthrough_objects_; + int scrollbar_start_width_; + double scrollbar_start_value_; + double scrollbar_start_scale_; + bool scrollbar_top_handle_; + private slots: void UpdateMaximumScroll(); - void ScrollBarResized(const double& multiplier); + void ScrollBarResizeBegan(int current_bar_width, bool top_handle); + + void ScrollBarResizeMoved(int new_bar_width); /** * @brief Slot to handle page scrolling of the playhead