From 69ff11fb40096ff5206ed112ad0c71811ab2c0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 11 Jan 2023 10:36:00 +0100 Subject: [PATCH] UI: Make vertical scrollbars directly draggable. --- Common/UI/ScrollView.cpp | 86 ++++++++++++++++++++++++++++++++-------- Common/UI/ScrollView.h | 18 +++++++++ 2 files changed, 87 insertions(+), 17 deletions(-) diff --git a/Common/UI/ScrollView.cpp b/Common/UI/ScrollView.cpp index 0158839e82..0feeefaca9 100644 --- a/Common/UI/ScrollView.cpp +++ b/Common/UI/ScrollView.cpp @@ -1,6 +1,7 @@ #include "Common/UI/Context.h" #include "Common/UI/ScrollView.h" #include "Common/Data/Text/I18n.h" +#include "Common/Log.h" namespace UI { @@ -134,7 +135,15 @@ const float friction = 0.92f; const float stop_threshold = 0.1f; bool ScrollView::Touch(const TouchInput &input) { - if ((input.flags & TOUCH_DOWN) && scrollTouchId_ == -1) { + if ((input.flags & TOUCH_DOWN) && scrollTouchId_ == -1 && bounds_.Contains(input.x, input.y)) { + if (orientation_ == ORIENT_VERTICAL) { + Bob bob = ComputeBob(); + float internalY = input.y - bounds_.y; + draggingBob_ = internalY >= bob.offset && internalY <= bob.offset + bob.size && input.x >= bounds_.x2() - 20.0f; + barDragStart_ = bob.offset; + barDragOffset_ = internalY - bob.offset; + } + scrollStart_ = scrollPos_; inertia_ = 0.0f; scrollTouchId_ = input.id; @@ -148,17 +157,35 @@ bool ScrollView::Touch(const TouchInput &input) { inertia_ = info[1]; } scrollTouchId_ = -1; + draggingBob_ = false; } TouchInput input2; if (CanScroll()) { - input2 = gesture_.Update(input, bounds_); - float info[4]; - if (input.id == scrollTouchId_ && gesture_.GetGestureInfo(gesture, input.id, info) && !(input.flags & TOUCH_DOWN)) { - float pos = scrollStart_ - info[0]; - scrollPos_ = pos; - scrollTarget_ = pos; + if (draggingBob_) { + input2 = input; + // Skip the gesture, do calculations directly. + // Might switch to the gesture later. + Bob bob = ComputeBob(); + float internalY = input.y - bounds_.y; + + float bobPos = internalY - barDragOffset_; + float bobDragMax = bounds_.h - bob.size; + + float newScrollPos = Clamp(bobPos / bobDragMax, 0.0f, 1.0f) * bob.scrollMax; + + scrollPos_ = newScrollPos; + scrollTarget_ = newScrollPos; scrollToTarget_ = false; + } else { + input2 = gesture_.Update(input, bounds_); + float info[4]; + if (input.id == scrollTouchId_ && gesture_.GetGestureInfo(gesture, input.id, info) && !(input.flags & TOUCH_DOWN)) { + float pos = scrollStart_ - info[0]; + scrollPos_ = pos; + scrollTarget_ = pos; + scrollToTarget_ = false; + } } } else { input2 = input; @@ -173,6 +200,22 @@ bool ScrollView::Touch(const TouchInput &input) { } } +ScrollView::Bob ScrollView::ComputeBob() const { + Bob bob{}; + float childHeight = std::max(0.01f, views_[0]->GetBounds().h); + float scrollMax = std::max(0.0f, childHeight - bounds_.h); + float ratio = bounds_.h / childHeight; + + if (ratio < 1.0f && scrollMax > 0.0f) { + bob.show = true; + bob.thickness = draggingBob_ ? 15.0f : 5.0f; + bob.size = ratio * bounds_.h; + bob.offset = (HardClampedScrollPos(scrollPos_) / scrollMax) * (bounds_.h - bob.size); + bob.scrollMax = scrollMax; + } + return bob; +} + void ScrollView::Draw(UIContext &dc) { if (!views_.size()) { ViewGroup::Draw(dc); @@ -187,18 +230,16 @@ void ScrollView::Draw(UIContext &dc) { views_[0]->Draw(dc); dc.PopScissor(); - float childHeight = views_[0]->GetBounds().h; - float scrollMax = std::max(0.0f, childHeight - bounds_.h); + // Vertical scroll bob. We don't support a horizontal yet. + if (orientation_ != ORIENT_VERTICAL) { + return; + } - float ratio = bounds_.h / std::max(0.01f, views_[0]->GetBounds().h); + Bob bob = ComputeBob(); - float bobWidth = 5; - if (ratio < 1.0f && scrollMax > 0.0f) { - float bobHeight = ratio * bounds_.h; - float bobOffset = (ClampedScrollPos(scrollPos_) / scrollMax) * (bounds_.h - bobHeight); - - Bounds bob(bounds_.x2() - bobWidth, bounds_.y + bobOffset, bobWidth, bobHeight); - dc.FillRect(Drawable(0x80FFFFFF), bob); + if (bob.show) { + Bounds bobBounds(bounds_.x2() - bob.thickness, bounds_.y + bob.offset, bob.thickness, bob.size); + dc.FillRect(Drawable(0x80FFFFFF), bobBounds); } } @@ -331,6 +372,16 @@ void ScrollView::ScrollRelative(float distance) { scrollToTarget_ = true; } +float ScrollView::HardClampedScrollPos(float pos) const { + if (!views_.size() || bounds_.h == 0.0f) { + return 0.0f; + } + float childSize = orientation_ == ORIENT_VERTICAL ? views_[0]->GetBounds().h : views_[0]->GetBounds().w; + float containerSize = (orientation_ == ORIENT_VERTICAL ? bounds_.h : bounds_.w); + float scrollMax = std::max(0.0f, childSize - containerSize); + return Clamp(pos, 0.0f, scrollMax); +} + float ScrollView::ClampedScrollPos(float pos) { if (!views_.size() || bounds_.h == 0.0f) { return 0.0f; @@ -342,6 +393,7 @@ float ScrollView::ClampedScrollPos(float pos) { Gesture gesture = orientation_ == ORIENT_VERTICAL ? GESTURE_DRAG_VERTICAL : GESTURE_DRAG_HORIZONTAL; + // TODO: Not all of this is properly orientation independent. if (scrollTouchId_ >= 0 && gesture_.IsGestureActive(gesture, scrollTouchId_) && bounds_.h > 0.0f) { float maxPull = bounds_.h * 0.1f; if (pos < 0.0f) { diff --git a/Common/UI/ScrollView.h b/Common/UI/ScrollView.h index d9092d3d34..9a0877b1df 100644 --- a/Common/UI/ScrollView.h +++ b/Common/UI/ScrollView.h @@ -47,8 +47,22 @@ public: NeighborResult FindScrollNeighbor(View *view, const Point &target, FocusDirection direction, NeighborResult best) override; private: + float HardClampedScrollPos(float pos) const; + + // TODO: Don't adjust pull_ within this! float ClampedScrollPos(float pos); + // The "bob" is the draggable thingy on a scroll bar. Don't know a better name for it. + struct Bob { + bool show; + float thickness; + float size; + float offset; + float scrollMax; + }; + + Bob ComputeBob() const; + GestureDetector gesture_; Orientation orientation_; float scrollPos_ = 0.0f; @@ -62,6 +76,10 @@ private: float lastViewSize_ = 0.0f; float *rememberPos_ = nullptr; bool alignOpposite_ = false; + bool draggingBob_ = false; + + float barDragStart_ = 0.0f; + float barDragOffset_ = 0.0f; static float lastScrollPosX; static float lastScrollPosY;