diff options
Diffstat (limited to 'chromium/cc/input')
-rw-r--r-- | chromium/cc/input/input_handler.h | 4 | ||||
-rw-r--r-- | chromium/cc/input/scrollbar.h | 26 | ||||
-rw-r--r-- | chromium/cc/input/scrollbar_animation_controller.cc | 3 | ||||
-rw-r--r-- | chromium/cc/input/scrollbar_animation_controller_unittest.cc | 229 | ||||
-rw-r--r-- | chromium/cc/input/scrollbar_controller.cc | 370 | ||||
-rw-r--r-- | chromium/cc/input/scrollbar_controller.h | 62 | ||||
-rw-r--r-- | chromium/cc/input/single_scrollbar_animation_controller_thinning.cc | 3 | ||||
-rw-r--r-- | chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc | 59 |
8 files changed, 486 insertions, 270 deletions
diff --git a/chromium/cc/input/input_handler.h b/chromium/cc/input/input_handler.h index e7794a5be53..f39e0062ffe 100644 --- a/chromium/cc/input/input_handler.h +++ b/chromium/cc/input/input_handler.h @@ -198,8 +198,8 @@ class CC_EXPORT InputHandler { virtual InputHandlerPointerResult MouseMoveAt( const gfx::Point& mouse_position) = 0; - virtual InputHandlerPointerResult MouseDown( - const gfx::PointF& mouse_position) = 0; + virtual InputHandlerPointerResult MouseDown(const gfx::PointF& mouse_position, + bool shift_modifier) = 0; virtual InputHandlerPointerResult MouseUp( const gfx::PointF& mouse_position) = 0; virtual void MouseLeave() = 0; diff --git a/chromium/cc/input/scrollbar.h b/chromium/cc/input/scrollbar.h index c63c800b1e4..4c739c69dba 100644 --- a/chromium/cc/input/scrollbar.h +++ b/chromium/cc/input/scrollbar.h @@ -20,10 +20,16 @@ static constexpr float kAutoscrollMultiplier = 20.f; static constexpr base::TimeDelta kInitialAutoscrollTimerDelay = base::TimeDelta::FromMilliseconds(250); +// Constants used to figure the how far out in the non-scrolling direction +// should trigger the thumb to snap back to its origin. These calculations are +// based on observing the behavior of the MSVC8 main window scrollbar + some +// guessing/extrapolation. +static constexpr int kOffSideMultiplier = 8; +static constexpr int kDefaultWinScrollbarThickness = 17; + namespace cc { enum ScrollbarOrientation { HORIZONTAL, VERTICAL }; -enum ScrollDirection { SCROLL_BACKWARD, SCROLL_FORWARD }; enum ScrollbarPart { THUMB, @@ -45,17 +51,29 @@ class Scrollbar { virtual gfx::Point Location() const = 0; virtual bool IsOverlay() const = 0; virtual bool HasThumb() const = 0; + virtual bool SupportsDragSnapBack() const = 0; virtual int ThumbThickness() const = 0; virtual int ThumbLength() const = 0; + + // Returns the track rect relative to the scrollbar's origin. virtual gfx::Rect TrackRect() const = 0; + // Returns the back button rect relative to the scrollbar's origin. virtual gfx::Rect BackButtonRect() const = 0; + // Returns the forward button rect relative to the scrollbar's origin. virtual gfx::Rect ForwardButtonRect() const = 0; + virtual float ThumbOpacity() const = 0; virtual bool HasTickmarks() const = 0; + + // Whether we need to repaint the part. Only THUMB and TRACK are supported. + // For TRACK, the return value means that the track, any buttons or tickmarks + // need repaint. virtual bool NeedsPaintPart(ScrollbarPart part) const = 0; - virtual void PaintPart(PaintCanvas* canvas, - ScrollbarPart part, - const gfx::Rect& content_rect) = 0; + // Paints the part. Only THUMB, TRACK and TICKMARKS are supported. When TRACK + // is specified, track, buttons and tickmarks will be painted. The canvas's + // coordinate space is relative to the part's origin. + virtual void PaintPart(PaintCanvas* canvas, ScrollbarPart part) = 0; + virtual bool UsesNinePatchThumbResource() const = 0; virtual gfx::Size NinePatchThumbCanvasSize() const = 0; virtual gfx::Rect NinePatchThumbAperture() const = 0; diff --git a/chromium/cc/input/scrollbar_animation_controller.cc b/chromium/cc/input/scrollbar_animation_controller.cc index bed51a0282d..60d7ecb8f31 100644 --- a/chromium/cc/input/scrollbar_animation_controller.cc +++ b/chromium/cc/input/scrollbar_animation_controller.cc @@ -7,6 +7,7 @@ #include <algorithm> #include "base/bind.h" +#include "base/numerics/ranges.h" #include "base/time/time.h" #include "cc/trees/layer_tree_impl.h" @@ -162,7 +163,7 @@ float ScrollbarAnimationController::AnimationProgressAtTime( base::TimeTicks now) { base::TimeDelta delta = now - last_awaken_time_; float progress = delta.InSecondsF() / fade_duration_.InSecondsF(); - return std::max(std::min(progress, 1.f), 0.f); + return base::ClampToRange(progress, 0.0f, 1.0f); } void ScrollbarAnimationController::RunAnimationFrame(float progress) { diff --git a/chromium/cc/input/scrollbar_animation_controller_unittest.cc b/chromium/cc/input/scrollbar_animation_controller_unittest.cc index 0836aeb1afa..d88bbdbf5b5 100644 --- a/chromium/cc/input/scrollbar_animation_controller_unittest.cc +++ b/chromium/cc/input/scrollbar_animation_controller_unittest.cc @@ -5,10 +5,8 @@ #include "cc/input/scrollbar_animation_controller.h" #include "cc/layers/solid_color_scrollbar_layer_impl.h" -#include "cc/test/fake_impl_task_runner_provider.h" -#include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/geometry_test_utils.h" -#include "cc/test/test_task_graph_runner.h" +#include "cc/test/layer_tree_impl_test_base.h" #include "cc/trees/layer_tree_impl.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -58,11 +56,11 @@ class MockScrollbarAnimationControllerClient LayerTreeHostImpl* host_impl_; }; -class ScrollbarAnimationControllerAuraOverlayTest : public testing::Test { +class ScrollbarAnimationControllerAuraOverlayTest + : public LayerTreeImplTestBase, + public testing::Test { public: - ScrollbarAnimationControllerAuraOverlayTest() - : host_impl_(&task_runner_provider_, &task_graph_runner_), - client_(&host_impl_) {} + ScrollbarAnimationControllerAuraOverlayTest() : client_(host_impl()) {} void ExpectScrollbarsOpacity(float opacity) { EXPECT_FLOAT_EQ(opacity, v_scrollbar_layer_->Opacity()); @@ -75,57 +73,47 @@ class ScrollbarAnimationControllerAuraOverlayTest : public testing::Test { const base::TimeDelta kThinningDuration = base::TimeDelta::FromSeconds(2); void SetUp() override { - std::unique_ptr<LayerImpl> scroll_layer = - LayerImpl::Create(host_impl_.active_tree(), 1); - std::unique_ptr<LayerImpl> clip = - LayerImpl::Create(host_impl_.active_tree(), 2); - clip_layer_ = clip.get(); - scroll_layer->SetElementId( - LayerIdToElementIdForTesting(scroll_layer->id())); - LayerImpl* scroll_layer_ptr = scroll_layer.get(); - const int kTrackStart = 0; const int kTrackLength = 100; const bool kIsLeftSideVerticalScrollbar = false; - const bool kIsOverlayScrollbar = true; - - std::unique_ptr<SolidColorScrollbarLayerImpl> h_scrollbar = - SolidColorScrollbarLayerImpl::Create( - host_impl_.active_tree(), 3, HORIZONTAL, kThumbThickness, - kTrackStart, kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar); - h_scrollbar->test_properties()->opacity = 0.0f; - std::unique_ptr<SolidColorScrollbarLayerImpl> v_scrollbar = - SolidColorScrollbarLayerImpl::Create( - host_impl_.active_tree(), 4, VERTICAL, kThumbThickness, kTrackStart, - kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar); - v_scrollbar->test_properties()->opacity = 0.0f; - v_scrollbar_layer_ = v_scrollbar.get(); - h_scrollbar_layer_ = h_scrollbar.get(); - - scroll_layer->test_properties()->AddChild(std::move(v_scrollbar)); - scroll_layer->test_properties()->AddChild(std::move(h_scrollbar)); - clip_layer_->test_properties()->AddChild(std::move(scroll_layer)); - host_impl_.active_tree()->SetRootLayerForTesting(std::move(clip)); + + scroll_layer_ = AddLayer<LayerImpl>(); + h_scrollbar_layer_ = AddLayer<SolidColorScrollbarLayerImpl>( + HORIZONTAL, kThumbThickness, kTrackStart, kIsLeftSideVerticalScrollbar); + v_scrollbar_layer_ = AddLayer<SolidColorScrollbarLayerImpl>( + VERTICAL, kThumbThickness, kTrackStart, kIsLeftSideVerticalScrollbar); + SetElementIdsForTesting(); + + clip_layer_ = root_layer(); + clip_layer_->SetBounds(gfx::Size(100, 100)); + + scroll_layer_->SetScrollable(gfx::Size(100, 100)); + scroll_layer_->SetBounds(gfx::Size(200, 200)); + CopyProperties(clip_layer_, scroll_layer_); + CreateTransformNode(scroll_layer_); + CreateScrollNode(scroll_layer_); v_scrollbar_layer_->SetBounds(gfx::Size(kThumbThickness, kTrackLength)); - v_scrollbar_layer_->test_properties()->position = gfx::PointF(90, 0); - v_scrollbar_layer_->SetScrollElementId(scroll_layer_ptr->element_id()); - v_scrollbar_layer_->test_properties()->opacity_can_animate = true; + v_scrollbar_layer_->SetScrollElementId(scroll_layer_->element_id()); + CopyProperties(scroll_layer_, v_scrollbar_layer_); + v_scrollbar_layer_->SetOffsetToTransformParent(gfx::Vector2dF(90, 0)); + auto& v_scrollbar_effect = CreateEffectNode(v_scrollbar_layer_); + v_scrollbar_effect.opacity = 0.f; + v_scrollbar_effect.has_potential_opacity_animation = true; h_scrollbar_layer_->SetBounds(gfx::Size(kTrackLength, kThumbThickness)); - h_scrollbar_layer_->test_properties()->position = gfx::PointF(0, 90); - h_scrollbar_layer_->SetScrollElementId(scroll_layer_ptr->element_id()); - h_scrollbar_layer_->test_properties()->opacity_can_animate = true; + h_scrollbar_layer_->SetScrollElementId(scroll_layer_->element_id()); + CopyProperties(scroll_layer_, h_scrollbar_layer_); + h_scrollbar_layer_->SetOffsetToTransformParent(gfx::Vector2dF(0, 90)); + auto& h_scrollbar_effect = CreateEffectNode(h_scrollbar_layer_); + h_scrollbar_effect.opacity = 0.f; + h_scrollbar_effect.has_potential_opacity_animation = true; - clip_layer_->SetBounds(gfx::Size(100, 100)); - scroll_layer_ptr->SetScrollable(gfx::Size(100, 100)); - scroll_layer_ptr->SetBounds(gfx::Size(200, 200)); - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); - host_impl_.active_tree()->UpdateScrollbarGeometries(); + UpdateActiveTreeDrawProperties(); scrollbar_controller_ = ScrollbarAnimationController:: CreateScrollbarAnimationControllerAuraOverlay( - scroll_layer_ptr->element_id(), &client_, kFadeDelay, kFadeDuration, + scroll_layer_->element_id(), &client_, kFadeDelay, kFadeDuration, kThinningDuration, 0.0f); v_scrollbar_layer_->SetCurrentPos(0); h_scrollbar_layer_->SetCurrentPos(0); @@ -153,11 +141,9 @@ class ScrollbarAnimationControllerAuraOverlayTest : public testing::Test { return p; } - FakeImplTaskRunnerProvider task_runner_provider_; - TestTaskGraphRunner task_graph_runner_; - FakeLayerTreeHostImpl host_impl_; std::unique_ptr<ScrollbarAnimationController> scrollbar_controller_; LayerImpl* clip_layer_; + LayerImpl* scroll_layer_; SolidColorScrollbarLayerImpl* v_scrollbar_layer_; SolidColorScrollbarLayerImpl* h_scrollbar_layer_; NiceMock<MockScrollbarAnimationControllerClient> client_; @@ -185,16 +171,17 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, AppearOnResize) { // Make the Layer non-scrollable, scrollbar disappears. clip_layer_->SetBounds(gfx::Size(200, 200)); - LayerImpl* scroll_layer = host_impl_.active_tree()->LayerById(1); - scroll_layer->SetScrollable(gfx::Size(200, 200)); - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + scroll_layer_->SetScrollable(gfx::Size(200, 200)); + GetScrollNode(scroll_layer_)->container_bounds = gfx::Size(200, 200); + UpdateActiveTreeDrawProperties(); scrollbar_controller_->DidScrollUpdate(); ExpectScrollbarsOpacity(0); // Make the layer scrollable, scrollbar appears again. clip_layer_->SetBounds(gfx::Size(100, 100)); - scroll_layer->SetScrollable(gfx::Size(100, 100)); - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + scroll_layer_->SetScrollable(gfx::Size(100, 100)); + GetScrollNode(scroll_layer_)->container_bounds = gfx::Size(100, 100); + UpdateActiveTreeDrawProperties(); scrollbar_controller_->DidScrollUpdate(); ExpectScrollbarsOpacity(1); } @@ -204,15 +191,14 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, HideOnResize) { base::TimeTicks time; time += base::TimeDelta::FromSeconds(1); - LayerImpl* scroll_layer = host_impl_.active_tree()->LayerById(1); - ASSERT_TRUE(scroll_layer); - EXPECT_EQ(gfx::Size(200, 200), scroll_layer->bounds()); + EXPECT_EQ(gfx::Size(200, 200), scroll_layer_->bounds()); // Shrink along X axis, horizontal scrollbar should appear. clip_layer_->SetBounds(gfx::Size(100, 200)); EXPECT_EQ(gfx::Size(100, 200), clip_layer_->bounds()); - scroll_layer->SetScrollable(gfx::Size(100, 200)); - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + scroll_layer_->SetScrollable(gfx::Size(100, 200)); + GetScrollNode(scroll_layer_)->container_bounds = gfx::Size(100, 200); + UpdateActiveTreeDrawProperties(); scrollbar_controller_->DidScrollBegin(); @@ -225,8 +211,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, HideOnResize) { // should disappear. clip_layer_->SetBounds(gfx::Size(200, 100)); EXPECT_EQ(gfx::Size(200, 100), clip_layer_->bounds()); - scroll_layer->SetScrollable(gfx::Size(200, 100)); - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + scroll_layer_->SetScrollable(gfx::Size(200, 100)); + GetScrollNode(scroll_layer_)->container_bounds = gfx::Size(200, 100); + UpdateActiveTreeDrawProperties(); scrollbar_controller_->DidScrollBegin(); @@ -1363,13 +1350,12 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, TickmakrsShowHide) { } class ScrollbarAnimationControllerAndroidTest - : public testing::Test, + : public LayerTreeImplTestBase, + public testing::Test, public ScrollbarAnimationControllerClient { public: ScrollbarAnimationControllerAndroidTest() - : host_impl_(&task_runner_provider_, &task_graph_runner_), - did_request_redraw_(false), - did_request_animate_(false) {} + : did_request_redraw_(false), did_request_animate_(false) {} void PostDelayedScrollbarAnimationTask(base::OnceClosure start_fade, base::TimeDelta delay) override { @@ -1383,7 +1369,7 @@ class ScrollbarAnimationControllerAndroidTest did_request_animate_ = true; } ScrollbarSet ScrollbarsFor(ElementId scroll_element_id) const override { - return host_impl_.ScrollbarsFor(scroll_element_id); + return host_impl()->ScrollbarsFor(scroll_element_id); } void DidChangeScrollbarVisibility() override {} @@ -1391,47 +1377,38 @@ class ScrollbarAnimationControllerAndroidTest void SetUp() override { const int kTrackStart = 0; const bool kIsLeftSideVerticalScrollbar = false; - const bool kIsOverlayScrollbar = true; // Allow opacity animations. - - std::unique_ptr<LayerImpl> scroll_layer = - LayerImpl::Create(host_impl_.active_tree(), 1); - std::unique_ptr<SolidColorScrollbarLayerImpl> scrollbar = - SolidColorScrollbarLayerImpl::Create( - host_impl_.active_tree(), 2, orientation(), kThumbThickness, - kTrackStart, kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar); - scrollbar->test_properties()->opacity = 0.0f; - scrollbar_layer_ = scrollbar.get(); - scrollbar_layer_->test_properties()->opacity_can_animate = true; - std::unique_ptr<LayerImpl> root = - LayerImpl::Create(host_impl_.active_tree(), 3); - - scroll_layer->SetScrollable(gfx::Size(100, 100)); - scroll_layer->SetElementId( - LayerIdToElementIdForTesting(scroll_layer->id())); - LayerImpl* scroll_layer_ptr = scroll_layer.get(); - scroll_layer->test_properties()->AddChild(std::move(scrollbar)); - root->test_properties()->AddChild(std::move(scroll_layer)); - host_impl_.active_tree()->SetRootLayerForTesting(std::move(root)); - - scrollbar_layer_->SetScrollElementId(scroll_layer_ptr->element_id()); - scroll_layer_ptr->SetBounds(gfx::Size(200, 200)); - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); - DCHECK(host_impl_.active_tree()->ScrollbarGeometriesNeedUpdate()); - host_impl_.active_tree()->UpdateScrollbarGeometries(); + + LayerImpl* root = root_layer(); + scroll_layer_ = AddLayer<LayerImpl>(); + scrollbar_layer_ = AddLayer<SolidColorScrollbarLayerImpl>( + orientation(), kThumbThickness, kTrackStart, + kIsLeftSideVerticalScrollbar); + SetElementIdsForTesting(); + + scroll_layer_->SetBounds(gfx::Size(200, 200)); + scroll_layer_->SetScrollable(gfx::Size(100, 100)); + CopyProperties(root, scroll_layer_); + CreateTransformNode(scroll_layer_); + CreateScrollNode(scroll_layer_); + + scrollbar_layer_->SetScrollElementId(scroll_layer_->element_id()); + CopyProperties(scroll_layer_, scrollbar_layer_); + auto& scrollbar_effect = CreateEffectNode(scrollbar_layer_); + scrollbar_effect.opacity = 0.f; + scrollbar_effect.has_potential_opacity_animation = true; + + UpdateActiveTreeDrawProperties(); scrollbar_controller_ = ScrollbarAnimationController::CreateScrollbarAnimationControllerAndroid( - scroll_layer_ptr->element_id(), this, - base::TimeDelta::FromSeconds(2), base::TimeDelta::FromSeconds(3), - 0.0f); + scroll_layer_->element_id(), this, base::TimeDelta::FromSeconds(2), + base::TimeDelta::FromSeconds(3), 0.0f); } virtual ScrollbarOrientation orientation() const { return HORIZONTAL; } - FakeImplTaskRunnerProvider task_runner_provider_; - TestTaskGraphRunner task_graph_runner_; - FakeLayerTreeHostImpl host_impl_; std::unique_ptr<ScrollbarAnimationController> scrollbar_controller_; + LayerImpl* scroll_layer_; SolidColorScrollbarLayerImpl* scrollbar_layer_; base::OnceClosure start_fade_; @@ -1490,15 +1467,14 @@ TEST_F(ScrollbarAnimationControllerAndroidTest, } TEST_F(ScrollbarAnimationControllerAndroidTest, HideOnResize) { - LayerImpl* scroll_layer = host_impl_.active_tree()->LayerById(1); - ASSERT_TRUE(scroll_layer); - EXPECT_EQ(gfx::Size(200, 200), scroll_layer->bounds()); + EXPECT_EQ(gfx::Size(200, 200), scroll_layer_->bounds()); EXPECT_EQ(HORIZONTAL, scrollbar_layer_->orientation()); // Shrink along X axis, horizontal scrollbar should appear. - scroll_layer->SetScrollable(gfx::Size(100, 200)); - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + scroll_layer_->SetScrollable(gfx::Size(100, 200)); + GetScrollNode(scroll_layer_)->container_bounds = gfx::Size(100, 200); + UpdateActiveTreeDrawProperties(); scrollbar_controller_->DidScrollBegin(); scrollbar_controller_->DidScrollUpdate(); @@ -1507,8 +1483,9 @@ TEST_F(ScrollbarAnimationControllerAndroidTest, HideOnResize) { // Shrink along Y axis and expand along X, horizontal scrollbar // should disappear. - scroll_layer->SetScrollable(gfx::Size(200, 100)); - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + scroll_layer_->SetScrollable(gfx::Size(200, 100)); + GetScrollNode(scroll_layer_)->container_bounds = gfx::Size(200, 100); + UpdateActiveTreeDrawProperties(); scrollbar_controller_->DidScrollBegin(); @@ -1519,15 +1496,14 @@ TEST_F(ScrollbarAnimationControllerAndroidTest, HideOnResize) { } TEST_F(VerticalScrollbarAnimationControllerAndroidTest, HideOnResize) { - LayerImpl* scroll_layer = host_impl_.active_tree()->LayerById(1); - ASSERT_TRUE(scroll_layer); - EXPECT_EQ(gfx::Size(200, 200), scroll_layer->bounds()); + EXPECT_EQ(gfx::Size(200, 200), scroll_layer_->bounds()); EXPECT_EQ(VERTICAL, scrollbar_layer_->orientation()); // Shrink along X axis, vertical scrollbar should remain invisible. - scroll_layer->SetScrollable(gfx::Size(100, 200)); - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + scroll_layer_->SetScrollable(gfx::Size(100, 200)); + GetScrollNode(scroll_layer_)->container_bounds = gfx::Size(100, 200); + UpdateActiveTreeDrawProperties(); scrollbar_controller_->DidScrollBegin(); scrollbar_controller_->DidScrollUpdate(); @@ -1535,8 +1511,9 @@ TEST_F(VerticalScrollbarAnimationControllerAndroidTest, HideOnResize) { scrollbar_controller_->DidScrollEnd(); // Shrink along Y axis and expand along X, vertical scrollbar should appear. - scroll_layer->SetScrollable(gfx::Size(200, 100)); - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + scroll_layer_->SetScrollable(gfx::Size(200, 100)); + GetScrollNode(scroll_layer_)->container_bounds = gfx::Size(200, 100); + UpdateActiveTreeDrawProperties(); scrollbar_controller_->DidScrollBegin(); @@ -1549,10 +1526,8 @@ TEST_F(VerticalScrollbarAnimationControllerAndroidTest, HideOnResize) { TEST_F(ScrollbarAnimationControllerAndroidTest, HideOnUserNonScrollableHorz) { EXPECT_EQ(HORIZONTAL, scrollbar_layer_->orientation()); - LayerImpl* scroll_layer = host_impl_.active_tree()->LayerById(1); - ASSERT_TRUE(scroll_layer); - scroll_layer->test_properties()->user_scrollable_horizontal = false; - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + GetScrollNode(scroll_layer_)->user_scrollable_horizontal = false; + UpdateActiveTreeDrawProperties(); scrollbar_controller_->DidScrollBegin(); @@ -1565,10 +1540,8 @@ TEST_F(ScrollbarAnimationControllerAndroidTest, HideOnUserNonScrollableHorz) { TEST_F(ScrollbarAnimationControllerAndroidTest, ShowOnUserNonScrollableVert) { EXPECT_EQ(HORIZONTAL, scrollbar_layer_->orientation()); - LayerImpl* scroll_layer = host_impl_.active_tree()->LayerById(1); - ASSERT_TRUE(scroll_layer); - scroll_layer->test_properties()->user_scrollable_vertical = false; - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + GetScrollNode(scroll_layer_)->user_scrollable_vertical = false; + UpdateActiveTreeDrawProperties(); scrollbar_controller_->DidScrollBegin(); @@ -1582,10 +1555,8 @@ TEST_F(VerticalScrollbarAnimationControllerAndroidTest, HideOnUserNonScrollableVert) { EXPECT_EQ(VERTICAL, scrollbar_layer_->orientation()); - LayerImpl* scroll_layer = host_impl_.active_tree()->LayerById(1); - ASSERT_TRUE(scroll_layer); - scroll_layer->test_properties()->user_scrollable_vertical = false; - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + GetScrollNode(scroll_layer_)->user_scrollable_vertical = false; + UpdateActiveTreeDrawProperties(); scrollbar_controller_->DidScrollBegin(); @@ -1599,10 +1570,8 @@ TEST_F(VerticalScrollbarAnimationControllerAndroidTest, ShowOnUserNonScrollableHorz) { EXPECT_EQ(VERTICAL, scrollbar_layer_->orientation()); - LayerImpl* scroll_layer = host_impl_.active_tree()->LayerById(1); - ASSERT_TRUE(scroll_layer); - scroll_layer->test_properties()->user_scrollable_horizontal = false; - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + GetScrollNode(scroll_layer_)->user_scrollable_horizontal = false; + UpdateActiveTreeDrawProperties(); scrollbar_controller_->DidScrollBegin(); diff --git a/chromium/cc/input/scrollbar_controller.cc b/chromium/cc/input/scrollbar_controller.cc index b66d42df486..2f4221e3d59 100644 --- a/chromium/cc/input/scrollbar_controller.cc +++ b/chromium/cc/input/scrollbar_controller.cc @@ -26,38 +26,13 @@ ScrollbarController::ScrollbarController( : layer_tree_host_impl_(layer_tree_host_impl), scrollbar_scroll_is_active_(false), currently_captured_scrollbar_(nullptr), - previous_pointer_position_(gfx::PointF(0, 0)), + last_known_pointer_position_(gfx::PointF(0, 0)), drag_processed_for_current_frame_(false), cancelable_autoscroll_task_(nullptr) {} void ScrollbarController::WillBeginImplFrame() { drag_processed_for_current_frame_ = false; - if (!autoscroll_state_.has_value()) - return; - - // TODO(arakeri): Revisit this when addressing crbug.com/967004. The - // animations need to be aborted/restarted based on the pointer location (i.e - // leaving/entering the track/arrows, reaching the track end etc). The - // autoscroll_state_ however, needs to be reset on pointer changes. - if (ShouldCancelTrackAutoscroll()) - layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(); - - // When the scroller is autoscrolling forward, its dimensions need to be - // monitored. If the length of the scroller layer increases, the old one needs - // to be aborted and a new autoscroll animation needs to start. This needs to - // be done only for the "autoscroll forward" case. Autoscrolling backward - // always has a constant value to animate to (which is '0'. See the function - // ScrollbarController::StartAutoScrollAnimation). - if (autoscroll_state_->direction == AutoScrollDirection::AUTOSCROLL_FORWARD) { - const float scroll_layer_length = - currently_captured_scrollbar_->scroll_layer_length(); - if (autoscroll_state_->scroll_layer_length != scroll_layer_length) { - layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(); - StartAutoScrollAnimation( - autoscroll_state_->velocity, - currently_captured_scrollbar_->scroll_element_id()); - } - } + RecomputeAutoscrollStateIfNeeded(); } gfx::Vector2dF ScrollbarController::GetThumbRelativePoint( @@ -78,16 +53,20 @@ gfx::Vector2dF ScrollbarController::GetThumbRelativePoint( // Performs hit test and prepares scroll deltas that will be used by GSB and // GSU. InputHandlerPointerResult ScrollbarController::HandlePointerDown( - const gfx::PointF position_in_widget) { - InputHandlerPointerResult scroll_result; + const gfx::PointF position_in_widget, + bool shift_modifier) { LayerImpl* layer_impl = GetLayerHitByPoint(position_in_widget); // If a non-custom scrollbar layer was not found, we return early as there is - // no point in setting additional state in the ScrollbarController. + // no point in setting additional state in the ScrollbarController. Return an + // empty InputHandlerPointerResult in this case so that when it is bubbled up + // to InputHandlerProxy::RouteToTypeSpecificHandler, the pointer event gets + // passed on to the main thread. if (!(layer_impl && layer_impl->ToScrollbarLayer())) - return scroll_result; + return InputHandlerPointerResult(); currently_captured_scrollbar_ = layer_impl->ToScrollbarLayer(); + InputHandlerPointerResult scroll_result; scroll_result.target_scroller = currently_captured_scrollbar_->scroll_element_id(); scroll_result.type = PointerResultType::kScrollbarScroll; @@ -95,18 +74,21 @@ InputHandlerPointerResult ScrollbarController::HandlePointerDown( const ScrollbarPart scrollbar_part = GetScrollbarPartFromPointerDown(position_in_widget); scroll_result.scroll_offset = GetScrollOffsetForScrollbarPart( - scrollbar_part, currently_captured_scrollbar_->orientation()); - previous_pointer_position_ = position_in_widget; + scrollbar_part, currently_captured_scrollbar_->orientation(), + shift_modifier); + last_known_pointer_position_ = position_in_widget; scrollbar_scroll_is_active_ = true; + scroll_result.scroll_units = Granularity(scrollbar_part, shift_modifier); if (scrollbar_part == ScrollbarPart::THUMB) { - scroll_result.scroll_units = - ui::input_types::ScrollGranularity::kScrollByPrecisePixel; - drag_anchor_relative_to_thumb_ = GetThumbRelativePoint(position_in_widget); - } else { - // TODO(arakeri): This needs to be updated to kLine once cc implements - // handling it. crbug.com/959441 - scroll_result.scroll_units = - ui::input_types::ScrollGranularity::kScrollByPixel; + drag_state_ = DragState(); + drag_state_->anchor_relative_to_thumb_ = + GetThumbRelativePoint(position_in_widget); + + // Record the current scroller offset. This will be needed to snap the + // thumb back to its original position if the pointer moves too far away + // from the track during a thumb drag. + drag_state_->scroll_position_at_start_ = + currently_captured_scrollbar_->current_pos(); } if (!scroll_result.scroll_offset.IsZero()) { @@ -120,7 +102,8 @@ InputHandlerPointerResult ScrollbarController::HandlePointerDown( &ScrollbarController::StartAutoScrollAnimation, base::Unretained(this), InitialDeltaToAutoscrollVelocity(scroll_result.scroll_offset), - currently_captured_scrollbar_->scroll_element_id())); + currently_captured_scrollbar_->scroll_element_id(), + scrollbar_part)); layer_tree_host_impl_->task_runner_provider() ->ImplThreadTaskRunner() ->PostDelayedTask(FROM_HERE, cancelable_autoscroll_task_->callback(), @@ -129,14 +112,134 @@ InputHandlerPointerResult ScrollbarController::HandlePointerDown( return scroll_result; } +bool ScrollbarController::SnapToDragOrigin( + const gfx::PointF pointer_position_in_widget) { + // Consult the ScrollbarTheme to check if thumb snapping is supported on the + // current platform. + if (!currently_captured_scrollbar_->SupportsDragSnapBack()) + return false; + + bool clipped = false; + const gfx::PointF pointer_position_in_layer = + GetScrollbarRelativePosition(pointer_position_in_widget, &clipped); + + if (clipped) + return false; + + layer_tree_host_impl_->active_tree()->UpdateScrollbarGeometries(); + const ScrollbarOrientation orientation = + currently_captured_scrollbar_->orientation(); + const gfx::Rect forward_track_rect = + currently_captured_scrollbar_->ForwardTrackRect(); + + // When dragging the thumb, there needs to exist "gutters" on either side of + // the track. The thickness of these gutters is a multiple of the track (or + // thumb) thickness. As long as the pointer remains within the bounds of these + // gutters in the non-scrolling direction, thumb drag proceeds as expected. + // The moment the pointer moves outside the bounds, the scroller needs to snap + // back to the drag_origin (aka the scroll offset of the parent scroller + // before the thumb drag initiated). + int track_thickness = orientation == ScrollbarOrientation::VERTICAL + ? forward_track_rect.width() + : forward_track_rect.height(); + + if (!track_thickness) { + // For overlay scrollbars (or for tests that do not set up a track + // thickness), use the thumb_thickness instead to determine the gutters. + const int thumb_thickness = currently_captured_scrollbar_->ThumbThickness(); + + // If the thumb doesn't have thickness, the gutters can't be determined. + // Snapping shouldn't occur in this case. + if (!thumb_thickness) + return false; + + track_thickness = thumb_thickness; + } + + const float gutter_thickness = kOffSideMultiplier * track_thickness; + const float gutter_min_bound = + orientation == ScrollbarOrientation::VERTICAL + ? (forward_track_rect.x() - gutter_thickness) + : (forward_track_rect.y() - gutter_thickness); + const float gutter_max_bound = + orientation == ScrollbarOrientation::VERTICAL + ? (forward_track_rect.x() + track_thickness + gutter_thickness) + : (forward_track_rect.y() + track_thickness + gutter_thickness); + + const float pointer_location = orientation == ScrollbarOrientation::VERTICAL + ? pointer_position_in_layer.x() + : pointer_position_in_layer.y(); + + return pointer_location < gutter_min_bound || + pointer_location > gutter_max_bound; +} + +ui::input_types::ScrollGranularity ScrollbarController::Granularity( + const ScrollbarPart scrollbar_part, + const bool shift_modifier) { + const bool shift_click_on_scrollbar_track = + shift_modifier && (scrollbar_part == ScrollbarPart::FORWARD_TRACK || + scrollbar_part == ScrollbarPart::BACK_TRACK); + if (shift_click_on_scrollbar_track || scrollbar_part == ScrollbarPart::THUMB) + return ui::input_types::ScrollGranularity::kScrollByPrecisePixel; + + // TODO(arakeri): This needs to be updated to kLine once cc implements + // handling it. crbug.com/959441 + return ui::input_types::ScrollGranularity::kScrollByPixel; +} + +float ScrollbarController::GetScrollDeltaForShiftClick() { + layer_tree_host_impl_->active_tree()->UpdateScrollbarGeometries(); + + bool clipped = false; + const gfx::PointF pointer_position_in_layer = + GetScrollbarRelativePosition(last_known_pointer_position_, &clipped); + + if (clipped) + return 0; + + const ScrollbarOrientation orientation = + currently_captured_scrollbar_->orientation(); + const float pointer_location = orientation == ScrollbarOrientation::VERTICAL + ? pointer_position_in_layer.y() + : pointer_position_in_layer.x(); + + // During a shift + click, the pointers current location (on the track) needs + // to be considered as the center of the thumb and the thumb origin needs to + // be calculated based on that. This will ensure that when shift + click is + // processed, the thumb will be centered on the pointer. + const int thumb_length = currently_captured_scrollbar_->ThumbLength(); + const float desired_thumb_origin = pointer_location - thumb_length / 2.f; + + const gfx::Rect thumb_rect( + currently_captured_scrollbar_->ComputeThumbQuadRect()); + const float current_thumb_origin = + orientation == ScrollbarOrientation::VERTICAL ? thumb_rect.y() + : thumb_rect.x(); + + const float delta = + round(std::abs(desired_thumb_origin - current_thumb_origin)); + return delta * GetScrollerToScrollbarRatio(); +} + gfx::ScrollOffset ScrollbarController::GetScrollOffsetForDragPosition( const gfx::PointF pointer_position_in_widget) { layer_tree_host_impl_->active_tree()->UpdateScrollbarGeometries(); + const ScrollbarOrientation orientation = + currently_captured_scrollbar_->orientation(); + if (SnapToDragOrigin(pointer_position_in_widget)) { + const float delta = currently_captured_scrollbar_->current_pos() - + drag_state_->scroll_position_at_start_; + return orientation == ScrollbarOrientation::VERTICAL + ? gfx::ScrollOffset(0, -delta) + : gfx::ScrollOffset(-delta, 0); + } + const gfx::Rect thumb_rect( currently_captured_scrollbar_->ComputeThumbQuadRect()); const gfx::PointF drag_position_relative_to_layer = - gfx::PointF(thumb_rect.origin()) + drag_anchor_relative_to_thumb_.value(); + gfx::PointF(thumb_rect.origin()) + drag_state_->anchor_relative_to_thumb_; bool clipped = false; const gfx::PointF pointer_position_in_layer = @@ -149,29 +252,52 @@ gfx::ScrollOffset ScrollbarController::GetScrollOffsetForDragPosition( const gfx::Vector2dF pointer_delta = pointer_position_in_layer - drag_position_relative_to_layer; + float scaled_scroller_to_scrollbar_ratio = GetScrollerToScrollbarRatio(); + float current_scroll_position = currently_captured_scrollbar_->current_pos(); + + // Thumb position needs to be floored and Values between 0 and 1 are rounded + // to one to match main thread per pixel behavior. Corresponding main thread + // code in ScrollbarTheme::ThumbPosition + float thumb_position = std::max(0.0f, current_scroll_position) / + scaled_scroller_to_scrollbar_ratio; + thumb_position = (thumb_position < 1.0 && thumb_position > 0.0) + ? 1.0 + : floorf(thumb_position); + + float delta_in_orientation = orientation == ScrollbarOrientation::VERTICAL + ? pointer_delta.y() + : pointer_delta.x(); + + // This is effectively equal to delta_in_orientation * + // scaled_scroller_to_scrollbar_ratio but is necessary due to truncated delta + // value. Floored thumb_position cancels out the rounding error introduced + // in pointer_delta due to static_cast<int> in + // ScrollbarLayerImplBase::ComputeThumbQuadRectWithThumbThicknessScale + float scroll_delta = (delta_in_orientation + thumb_position) * + scaled_scroller_to_scrollbar_ratio - + current_scroll_position; + gfx::ScrollOffset scaled_thumb_drag_delta; - const ScrollbarOrientation orientation = - currently_captured_scrollbar_->orientation(); + + // Scroll delta floored to match main thread per pixel behavior orientation == ScrollbarOrientation::VERTICAL - ? scaled_thumb_drag_delta.set_y(pointer_delta.y()) - : scaled_thumb_drag_delta.set_x(pointer_delta.x()); + ? scaled_thumb_drag_delta.set_y(floorf(scroll_delta)) + : scaled_thumb_drag_delta.set_x(floorf(scroll_delta)); - float scaled_scroller_to_scrollbar_ratio = GetScrollerToScrollbarRatio(); - scaled_thumb_drag_delta.Scale(scaled_scroller_to_scrollbar_ratio); return scaled_thumb_drag_delta; } // Performs hit test and prepares scroll deltas that will be used by GSU. -InputHandlerPointerResult ScrollbarController::HandleMouseMove( +InputHandlerPointerResult ScrollbarController::HandlePointerMove( const gfx::PointF position_in_widget) { - previous_pointer_position_ = position_in_widget; + last_known_pointer_position_ = position_in_widget; + RecomputeAutoscrollStateIfNeeded(); InputHandlerPointerResult scroll_result; // If a thumb drag is not in progress or if a GSU was already produced for a // thumb drag in this frame, there's no point in continuing on. Please see the // header file for details. - if (!drag_anchor_relative_to_thumb_.has_value() || - drag_processed_for_current_frame_) + if (!drag_state_.has_value() || drag_processed_for_current_frame_) return scroll_result; const ScrollNode* currently_scrolling_node = @@ -180,7 +306,7 @@ InputHandlerPointerResult ScrollbarController::HandleMouseMove( // Thumb drag needs a scroll_node. Clear the thumb drag state and exit if it // is unset. if (currently_scrolling_node == nullptr) { - drag_anchor_relative_to_thumb_ = base::nullopt; + drag_state_ = base::nullopt; return scroll_result; } @@ -258,30 +384,27 @@ float ScrollbarController::GetScrollerToScrollbarRatio() { const LayerImpl* owner_scroll_layer = layer_tree_host_impl_->active_tree()->ScrollableLayerByElementId( currently_captured_scrollbar_->scroll_element_id()); - float viewport_length = + const float viewport_length = orientation == ScrollbarOrientation::VERTICAL ? owner_scroll_layer->scroll_container_bounds().height() : (owner_scroll_layer->scroll_container_bounds().width()); + + // For platforms which have use_zoom_for_dsf set to false (like Mac), the + // device_scale_factor should not be used while determining the + // scaled_scroller_to_scrollbar_ratio as thumb drag would appear jittery due + // to constant over and under corrections. + // (See ScrollbarController::ScreenSpaceScaleFactor()). float scaled_scroller_to_scrollbar_ratio = ((scroll_layer_length - viewport_length) / (scrollbar_track_length - scrollbar_thumb_length)) * - layer_tree_host_impl_->active_tree()->device_scale_factor(); - - // Avoid precision loss later on by rounding up 3 decimal places here. - // TODO(arakeri): Revisit this while fixing crbug.com/986174. There is a - // precision loss that is happening somewhere which affects root scrollbars - // only. Even though it is not visible to the end user, without rounding up, - // the scrolling tests will fail due to this precision loss. - DCHECK_GT(scaled_scroller_to_scrollbar_ratio, 0); - scaled_scroller_to_scrollbar_ratio = - ceil(scaled_scroller_to_scrollbar_ratio * 1000.0) / 1000.0; + ScreenSpaceScaleFactor(); return scaled_scroller_to_scrollbar_ratio; } -bool ScrollbarController::ShouldCancelTrackAutoscroll() { - // This function should only ever be called if an autoscroll is in progress. - DCHECK(autoscroll_state_.has_value()); +void ScrollbarController::RecomputeAutoscrollStateIfNeeded() { + if (!autoscroll_state_.has_value()) + return; layer_tree_host_impl_->active_tree()->UpdateScrollbarGeometries(); const ScrollbarOrientation orientation = @@ -291,10 +414,10 @@ bool ScrollbarController::ShouldCancelTrackAutoscroll() { bool clipped; gfx::PointF scroller_relative_position( - GetScrollbarRelativePosition(previous_pointer_position_, &clipped)); + GetScrollbarRelativePosition(last_known_pointer_position_, &clipped)); if (clipped) - return false; + return; // Based on the orientation of the scrollbar and the direction of the // autoscroll, the code below makes a decision of whether the track autoscroll @@ -312,15 +435,49 @@ bool ScrollbarController::ShouldCancelTrackAutoscroll() { pointer_position = scroller_relative_position.x(); } + // If the thumb reaches the pointer while autoscrolling, abort. if ((autoscroll_state_->direction == AutoScrollDirection::AUTOSCROLL_FORWARD && thumb_end > pointer_position) || (autoscroll_state_->direction == AutoScrollDirection::AUTOSCROLL_BACKWARD && thumb_start < pointer_position)) - return true; + layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(); + + // When the scroller is autoscrolling forward, its dimensions need to be + // monitored. If the length of the scroller layer increases, the old one needs + // to be aborted and a new autoscroll animation needs to start. This needs to + // be done only for the "autoscroll forward" case. Autoscrolling backward + // always has a constant value to animate to (which is '0'. See the function + // ScrollbarController::StartAutoScrollAnimation). + if (autoscroll_state_->direction == AutoScrollDirection::AUTOSCROLL_FORWARD) { + const float scroll_layer_length = + currently_captured_scrollbar_->scroll_layer_length(); + if (autoscroll_state_->scroll_layer_length != scroll_layer_length) { + layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(); + StartAutoScrollAnimation( + autoscroll_state_->velocity, + currently_captured_scrollbar_->scroll_element_id(), + autoscroll_state_->pressed_scrollbar_part); + } + } - return false; + // The animations need to be aborted/restarted based on the pointer location + // (i.e leaving/entering the track/arrows, reaching the track end etc). The + // autoscroll_state_ however, needs to be reset on pointer changes. + const gfx::RectF scrollbar_part_rect( + GetRectForScrollbarPart(autoscroll_state_->pressed_scrollbar_part)); + if (!scrollbar_part_rect.Contains(scroller_relative_position)) { + // Stop animating if pointer moves outside the rect bounds. + layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(); + } else if (scrollbar_part_rect.Contains(scroller_relative_position) && + !layer_tree_host_impl_->mutator_host()->IsElementAnimating( + currently_captured_scrollbar_->scroll_element_id())) { + // Start animating if pointer re-enters the bounds. + StartAutoScrollAnimation(autoscroll_state_->velocity, + currently_captured_scrollbar_->scroll_element_id(), + autoscroll_state_->pressed_scrollbar_part); + } } // Helper to calculate the autoscroll velocity. @@ -333,11 +490,13 @@ float ScrollbarController::InitialDeltaToAutoscrollVelocity( return scroll_delta * kAutoscrollMultiplier; } -void ScrollbarController::StartAutoScrollAnimation(const float velocity, - ElementId element_id) { +void ScrollbarController::StartAutoScrollAnimation( + const float velocity, + ElementId element_id, + ScrollbarPart pressed_scrollbar_part) { // Autoscroll and thumb drag are mutually exclusive. Both can't be active at // the same time. - DCHECK(!drag_anchor_relative_to_thumb_.has_value()); + DCHECK(!drag_state_.has_value()); DCHECK_NE(velocity, 0); // scroll_node is set up while handling GSB. If there's no node to scroll, we @@ -372,6 +531,7 @@ void ScrollbarController::StartAutoScrollAnimation(const float velocity, autoscroll_state_ = AutoScrollState(); autoscroll_state_->velocity = velocity; autoscroll_state_->scroll_layer_length = scroll_layer_length; + autoscroll_state_->pressed_scrollbar_part = pressed_scrollbar_part; autoscroll_state_->direction = velocity < 0 ? AutoScrollDirection::AUTOSCROLL_BACKWARD : AutoScrollDirection::AUTOSCROLL_FORWARD; @@ -400,7 +560,7 @@ InputHandlerPointerResult ScrollbarController::HandlePointerUp( cancelable_autoscroll_task_.reset(); } - drag_anchor_relative_to_thumb_ = base::nullopt; + drag_state_ = base::nullopt; autoscroll_state_ = base::nullopt; return scroll_result; } @@ -414,14 +574,14 @@ LayerImpl* ScrollbarController::GetLayerHitByPoint( gfx::PointF device_viewport_point = gfx::ScalePoint( gfx::PointF(viewport_point), active_tree->device_scale_factor()); LayerImpl* layer_impl = - active_tree->FindFirstScrollingLayerOrScrollbarThatIsHitByPoint( - device_viewport_point); + active_tree->FindLayerThatIsHitByPoint(device_viewport_point); return layer_impl; } int ScrollbarController::GetScrollDeltaForScrollbarPart( - ScrollbarPart scrollbar_part) { + const ScrollbarPart scrollbar_part, + const bool shift_modifier) { int scroll_delta = 0; int viewport_length = 0; LayerImpl* owner_scroll_layer = nullptr; @@ -433,6 +593,10 @@ int ScrollbarController::GetScrollDeltaForScrollbarPart( break; case ScrollbarPart::BACK_TRACK: case ScrollbarPart::FORWARD_TRACK: + if (shift_modifier) { + scroll_delta = GetScrollDeltaForShiftClick(); + break; + } owner_scroll_layer = layer_tree_host_impl_->active_tree()->ScrollableLayerByElementId( currently_captured_scrollbar_->scroll_element_id()); @@ -446,8 +610,20 @@ int ScrollbarController::GetScrollDeltaForScrollbarPart( default: scroll_delta = 0; } - return scroll_delta * - layer_tree_host_impl_->active_tree()->device_scale_factor(); + + return scroll_delta * ScreenSpaceScaleFactor(); +} + +float ScrollbarController::ScreenSpaceScaleFactor() const { + // TODO(arakeri): When crbug.com/716231 is fixed, this needs to be updated. + // If use_zoom_for_dsf is false, the click deltas and thumb drag ratios + // shouldn't be scaled. For example: On Mac, when the use_zoom_for_dsf is + // false and the device_scale_factor is 2, the scroll delta for pointer clicks + // on arrows would be incorrectly calculated as 80px instead of 40px. This is + // also necessary to ensure that hit testing works as intended. + return layer_tree_host_impl_->settings().use_zoom_for_dsf + ? layer_tree_host_impl_->active_tree()->device_scale_factor() + : 1.f; } gfx::PointF ScrollbarController::GetScrollbarRelativePosition( @@ -455,7 +631,17 @@ gfx::PointF ScrollbarController::GetScrollbarRelativePosition( bool* clipped) { gfx::Transform inverse_screen_space_transform( gfx::Transform::kSkipInitialization); - if (!currently_captured_scrollbar_->ScreenSpaceTransform().GetInverse( + + // If use_zoom_for_dsf is false, the ScreenSpaceTransform needs to be scaled + // down by the DSF to ensure that position_in_widget is transformed correctly. + const float scale = + !layer_tree_host_impl_->settings().use_zoom_for_dsf + ? 1.f / layer_tree_host_impl_->active_tree()->device_scale_factor() + : 1.f; + gfx::Transform scaled_screen_space_transform( + currently_captured_scrollbar_->ScreenSpaceTransform()); + scaled_screen_space_transform.PostScale(scale, scale); + if (!scaled_screen_space_transform.GetInverse( &inverse_screen_space_transform)) return gfx::PointF(0, 0); @@ -480,12 +666,28 @@ ScrollbarPart ScrollbarController::GetScrollbarPartFromPointerDown( scroller_relative_position); } +// Determines the corresponding rect for the given scrollbar part. +gfx::Rect ScrollbarController::GetRectForScrollbarPart( + const ScrollbarPart scrollbar_part) { + if (scrollbar_part == ScrollbarPart::BACK_BUTTON) + return currently_captured_scrollbar_->BackButtonRect(); + if (scrollbar_part == ScrollbarPart::FORWARD_BUTTON) + return currently_captured_scrollbar_->ForwardButtonRect(); + if (scrollbar_part == ScrollbarPart::BACK_TRACK) + return currently_captured_scrollbar_->BackTrackRect(); + if (scrollbar_part == ScrollbarPart::FORWARD_TRACK) + return currently_captured_scrollbar_->ForwardTrackRect(); + return gfx::Rect(0, 0); +} + // Determines the scroll offsets based on the ScrollbarPart and the scrollbar // orientation. gfx::ScrollOffset ScrollbarController::GetScrollOffsetForScrollbarPart( const ScrollbarPart scrollbar_part, - const ScrollbarOrientation orientation) { - float scroll_delta = GetScrollDeltaForScrollbarPart(scrollbar_part); + const ScrollbarOrientation orientation, + const bool shift_modifier) { + float scroll_delta = + GetScrollDeltaForScrollbarPart(scrollbar_part, shift_modifier); // See CreateScrollStateForGesture for more information on how these values // will be interpreted. diff --git a/chromium/cc/input/scrollbar_controller.h b/chromium/cc/input/scrollbar_controller.h index a407640ce0b..e1b13dc2e03 100644 --- a/chromium/cc/input/scrollbar_controller.h +++ b/chromium/cc/input/scrollbar_controller.h @@ -20,8 +20,9 @@ class CC_EXPORT ScrollbarController { virtual ~ScrollbarController(); InputHandlerPointerResult HandlePointerDown( - const gfx::PointF position_in_widget); - InputHandlerPointerResult HandleMouseMove( + const gfx::PointF position_in_widget, + const bool shift_modifier); + InputHandlerPointerResult HandlePointerMove( const gfx::PointF position_in_widget); InputHandlerPointerResult HandlePointerUp( const gfx::PointF position_in_widget); @@ -30,7 +31,9 @@ class CC_EXPORT ScrollbarController { // InitialDeltaToAutoscrollVelocity). This value carries a "sign" which is // needed to determine whether we should set up the autoscrolling in the // forwards or the backwards direction. - void StartAutoScrollAnimation(float velocity, ElementId element_id); + void StartAutoScrollAnimation(float velocity, + ElementId element_id, + ScrollbarPart pressed_scrollbar_part); bool ScrollbarScrollIsActive() { return scrollbar_scroll_is_active_; } ScrollbarOrientation orientation() { return currently_captured_scrollbar_->orientation(); @@ -54,8 +57,26 @@ class CC_EXPORT ScrollbarController { // Used to track the scroller length while autoscrolling. Helpful for // setting up infinite scrolling. float scroll_layer_length = 0.f; + + // Used to lookup the rect corresponding to the ScrollbarPart so that + // autoscroll animations can be played/paused depending on the current + // pointer location. + ScrollbarPart pressed_scrollbar_part; + }; + + struct CC_EXPORT DragState { + // This is used to track the pointer location relative to the thumb origin + // when a drag has started. + gfx::Vector2dF anchor_relative_to_thumb_; + + // This is needed for thumb snapping when the pointer moves too far away + // from the track while scrolling. + float scroll_position_at_start_; }; + // Returns the DSF based on whether use-zoom-for-dsf is enabled. + float ScreenSpaceScaleFactor() const; + // Helper to convert scroll offset to autoscroll velocity. float InitialDeltaToAutoscrollVelocity(gfx::ScrollOffset scroll_offset) const; @@ -66,17 +87,36 @@ class CC_EXPORT ScrollbarController { // Returns scroll offsets based on which ScrollbarPart was hit tested. gfx::ScrollOffset GetScrollOffsetForScrollbarPart( const ScrollbarPart scrollbar_part, - const ScrollbarOrientation orientation); + const ScrollbarOrientation orientation, + const bool shift_modifier); + + // Returns the rect for the ScrollbarPart. + gfx::Rect GetRectForScrollbarPart(const ScrollbarPart scrollbar_part); LayerImpl* GetLayerHitByPoint(const gfx::PointF position_in_widget); - int GetScrollDeltaForScrollbarPart(ScrollbarPart scrollbar_part); + int GetScrollDeltaForScrollbarPart(const ScrollbarPart scrollbar_part, + const bool shift_modifier); // Makes position_in_widget relative to the scrollbar. gfx::PointF GetScrollbarRelativePosition(const gfx::PointF position_in_widget, bool* clipped); - // Decides whether a track autoscroll should be aborted. - bool ShouldCancelTrackAutoscroll(); + // Decides if the scroller should snap to the offset that it was originally at + // (i.e the offset before the thumb drag). + bool SnapToDragOrigin(const gfx::PointF pointer_position_in_widget); + + // Decides whether a track autoscroll should be aborted (or restarted) due to + // the thumb reaching the pointer or the pointer leaving (or re-entering) the + // bounds. + void RecomputeAutoscrollStateIfNeeded(); + + // Shift + click is expected to do a non-animated jump to a certain offset. + float GetScrollDeltaForShiftClick(); + + // Determines if the delta needs to be animated. + ui::input_types::ScrollGranularity Granularity( + const ScrollbarPart scrollbar_part, + bool shift_modifier); // Calculates the scroll_offset based on position_in_widget and // drag_anchor_relative_to_thumb_. @@ -98,15 +138,15 @@ class CC_EXPORT ScrollbarController { const ScrollbarLayerImplBase* currently_captured_scrollbar_; // This is relative to the RenderWidget's origin. - gfx::PointF previous_pointer_position_; + gfx::PointF last_known_pointer_position_; // Holds information pertaining to autoscrolling. This member is empty if and // only if an autoscroll is *not* in progress. base::Optional<AutoScrollState> autoscroll_state_; - // This is used to track the pointer location relative to the thumb origin - // when a drag has started. It is empty if a thumb drag is *not* in progress. - base::Optional<gfx::Vector2dF> drag_anchor_relative_to_thumb_; + // Holds information pertaining to thumb drags. Useful while making decisions + // about thumb anchoring/snapping. + base::Optional<DragState> drag_state_; // Used to track if a GSU was processed for the current frame or not. Without // this, thumb drag will appear jittery. The reason this happens is because diff --git a/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc b/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc index 0515aaff879..d01e1ac2bcd 100644 --- a/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc +++ b/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc @@ -7,6 +7,7 @@ #include <algorithm> #include "base/memory/ptr_util.h" +#include "base/numerics/ranges.h" #include "base/time/time.h" #include "cc/input/scrollbar_animation_controller.h" #include "cc/layers/layer_impl.h" @@ -95,7 +96,7 @@ float SingleScrollbarAnimationControllerThinning::AnimationProgressAtTime( base::TimeTicks now) { base::TimeDelta delta = now - last_awaken_time_; float progress = delta.InSecondsF() / Duration().InSecondsF(); - return std::max(std::min(progress, 1.f), 0.f); + return base::ClampToRange(progress, 0.0f, 1.0f); } const base::TimeDelta& SingleScrollbarAnimationControllerThinning::Duration() { diff --git a/chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc b/chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc index a08e6aa8838..b851a58f77f 100644 --- a/chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc +++ b/chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc @@ -5,10 +5,8 @@ #include "cc/input/single_scrollbar_animation_controller_thinning.h" #include "cc/layers/solid_color_scrollbar_layer_impl.h" -#include "cc/test/fake_impl_task_runner_provider.h" -#include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/geometry_test_utils.h" -#include "cc/test/test_task_graph_runner.h" +#include "cc/test/layer_tree_impl_test_base.h" #include "cc/trees/layer_tree_impl.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -51,62 +49,49 @@ class MockSingleScrollbarAnimationControllerClient LayerTreeHostImpl* host_impl_; }; -class SingleScrollbarAnimationControllerThinningTest : public testing::Test { +class SingleScrollbarAnimationControllerThinningTest + : public LayerTreeImplTestBase, + public testing::Test { public: - SingleScrollbarAnimationControllerThinningTest() - : host_impl_(&task_runner_provider_, &task_graph_runner_), - client_(&host_impl_) {} + SingleScrollbarAnimationControllerThinningTest() : client_(host_impl()) {} protected: const base::TimeDelta kThinningDuration = base::TimeDelta::FromSeconds(2); void SetUp() override { - std::unique_ptr<LayerImpl> scroll_layer = - LayerImpl::Create(host_impl_.active_tree(), 1); - std::unique_ptr<LayerImpl> clip = - LayerImpl::Create(host_impl_.active_tree(), 3); + root_layer()->SetBounds(gfx::Size(100, 100)); + auto* scroll_layer = AddLayer<LayerImpl>(); + scroll_layer->SetBounds(gfx::Size(200, 200)); + scroll_layer->SetScrollable(gfx::Size(100, 100)); scroll_layer->SetElementId( LayerIdToElementIdForTesting(scroll_layer->id())); - clip_layer_ = clip.get(); - LayerImpl* scroll_layer_ptr = scroll_layer.get(); - const int kId = 2; const int kThumbThickness = 10; const int kTrackStart = 0; const int kTrackLength = 100; const bool kIsLeftSideVerticalScrollbar = false; - const bool kIsOverlayScrollbar = true; - std::unique_ptr<SolidColorScrollbarLayerImpl> scrollbar = - SolidColorScrollbarLayerImpl::Create( - host_impl_.active_tree(), kId, HORIZONTAL, kThumbThickness, - kTrackStart, kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar); - scrollbar_layer_ = scrollbar.get(); - - scroll_layer->test_properties()->AddChild(std::move(scrollbar)); - clip_layer_->test_properties()->AddChild(std::move(scroll_layer)); - host_impl_.active_tree()->SetRootLayerForTesting(std::move(clip)); + scrollbar_layer_ = AddLayer<SolidColorScrollbarLayerImpl>( + HORIZONTAL, kThumbThickness, kTrackStart, kIsLeftSideVerticalScrollbar); scrollbar_layer_->SetBounds(gfx::Size(kThumbThickness, kTrackLength)); - scrollbar_layer_->test_properties()->position = gfx::PointF(90, 0); - scrollbar_layer_->SetScrollElementId(scroll_layer_ptr->element_id()); - scrollbar_layer_->test_properties()->opacity_can_animate = true; - clip_layer_->SetBounds(gfx::Size(100, 100)); - scroll_layer_ptr->SetBounds(gfx::Size(200, 200)); - host_impl_.active_tree()->UpdateScrollbarGeometries(); - host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + scrollbar_layer_->SetScrollElementId(scroll_layer->element_id()); + + CopyProperties(root_layer(), scroll_layer); + CreateTransformNode(scroll_layer); + CreateScrollNode(scroll_layer); + CopyProperties(scroll_layer, scrollbar_layer_); + scrollbar_layer_->SetOffsetToTransformParent(gfx::Vector2dF(90, 0)); + CreateEffectNode(scrollbar_layer_).has_potential_opacity_animation = true; + + UpdateActiveTreeDrawProperties(); scrollbar_controller_ = SingleScrollbarAnimationControllerThinning::Create( - scroll_layer_ptr->element_id(), HORIZONTAL, &client_, - kThinningDuration); + scroll_layer->element_id(), HORIZONTAL, &client_, kThinningDuration); } - FakeImplTaskRunnerProvider task_runner_provider_; - TestTaskGraphRunner task_graph_runner_; - FakeLayerTreeHostImpl host_impl_; std::unique_ptr<SingleScrollbarAnimationControllerThinning> scrollbar_controller_; - LayerImpl* clip_layer_; SolidColorScrollbarLayerImpl* scrollbar_layer_; NiceMock<MockSingleScrollbarAnimationControllerClient> client_; }; |