1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "ash/controls/gradient_layer_delegate.h"
8 #include "ash/controls/scroll_view_gradient_helper.h"
9 #include "ui/compositor/layer.h"
10 #include "ui/compositor/layer_type.h"
11 #include "ui/gfx/geometry/rect.h"
12 #include "ui/views/controls/scroll_view.h"
13 #include "ui/views/test/views_test_base.h"
14 #include "ui/views/test/views_test_utils.h"
15 #include "ui/views/widget/unique_widget_ptr.h"
16 #include "ui/views/widget/widget.h"
17 #include "ui/views/widget/widget_utils.h"
22 constexpr int kWidgetHeight = 100;
23 constexpr int kWidgetWidth = 100;
24 constexpr int kGradientSize = 16;
26 // Uses ViewsTestBase because we may want to move this helper into //ui/views
28 class ScrollViewGradientHelperTest : public views::ViewsTestBase {
30 ScrollViewGradientHelperTest() = default;
31 ~ScrollViewGradientHelperTest() override = default;
34 void SetUp() override {
35 ViewsTestBase::SetUp();
37 // Create a small widget.
38 widget_ = std::make_unique<views::Widget>();
39 views::Widget::InitParams params =
40 CreateParams(views::Widget::InitParams::TYPE_POPUP);
41 params.bounds = gfx::Rect(10, 10, kWidgetWidth, kWidgetHeight);
42 widget_->Init(std::move(params));
45 // Add a scroll view and gradient helper.
46 auto* contents = widget_->SetContentsView(std::make_unique<views::View>());
48 contents->AddChildView(std::make_unique<views::ScrollView>());
49 scroll_view_->SetBounds(0, 0, kWidgetWidth, kWidgetHeight);
50 scroll_view_->SetPaintToLayer(ui::LAYER_NOT_DRAWN);
52 std::make_unique<ScrollViewGradientHelper>(scroll_view_, kGradientSize);
55 void TearDown() override {
56 gradient_helper_.reset();
58 ViewsTestBase::TearDown();
61 void AddScrollViewContentsWithHeight(int height) {
62 auto contents = std::make_unique<views::View>();
63 contents->SetSize({kWidgetWidth, height});
64 scroll_view_->SetContents(std::move(contents));
65 views::test::RunScheduledLayout(scroll_view_);
68 bool HasGradientAtTop() {
69 const auto& gradient_mask = gradient_helper_->gradient_mask_for_test();
70 EXPECT_FALSE(gradient_mask.IsEmpty());
71 return cc::MathUtil::IsWithinEpsilon(gradient_mask.steps()[0].fraction,
75 bool HasGradientAtBottom() {
76 const auto& gradient_mask = gradient_helper_->gradient_mask_for_test();
77 EXPECT_FALSE(gradient_mask.IsEmpty());
78 return cc::MathUtil::IsWithinEpsilon(
79 gradient_mask.steps()[gradient_mask.step_count() - 1].fraction, 1.f);
82 bool HasGradientMask(const ui::Layer* layer) {
83 return !layer->gradient_mask().IsEmpty();
86 views::UniqueWidgetPtr widget_;
87 views::ScrollView* scroll_view_ = nullptr;
88 std::unique_ptr<ScrollViewGradientHelper> gradient_helper_;
91 TEST_F(ScrollViewGradientHelperTest, NoGradientForViewThatDoesNotScroll) {
92 // Create a short contents view, so the scroll view won't scroll.
93 AddScrollViewContentsWithHeight(10);
94 gradient_helper_->UpdateGradientMask();
96 EXPECT_FALSE(HasGradientMask(scroll_view_->layer()));
97 EXPECT_TRUE(gradient_helper_->gradient_mask_for_test().IsEmpty());
100 TEST_F(ScrollViewGradientHelperTest, HasGradientForViewThatScrolls) {
101 // Create a tall contents view.
102 AddScrollViewContentsWithHeight(500);
103 gradient_helper_->UpdateGradientMask();
105 // Gradient is shown.
106 EXPECT_TRUE(HasGradientMask(scroll_view_->layer()));
107 EXPECT_FALSE(gradient_helper_->gradient_mask_for_test().IsEmpty());
109 // Shrink the contents view.
110 scroll_view_->contents()->SetSize({kWidgetWidth, 10});
111 views::test::RunScheduledLayout(scroll_view_);
112 gradient_helper_->UpdateGradientMask();
114 // Gradient is removed.
115 EXPECT_FALSE(HasGradientMask(scroll_view_->layer()));
116 EXPECT_TRUE(gradient_helper_->gradient_mask_for_test().IsEmpty());
119 TEST_F(ScrollViewGradientHelperTest, ShowsGradientsBasedOnScrollPosition) {
120 // Create a tall contents view.
121 AddScrollViewContentsWithHeight(500);
122 ASSERT_TRUE(scroll_view_->vertical_scroll_bar());
123 gradient_helper_->UpdateGradientMask();
125 // Because the scroll position is at the top, only the bottom gradient shows.
126 EXPECT_FALSE(HasGradientAtTop());
127 EXPECT_TRUE(HasGradientAtBottom());
129 // Scroll down. Now both top and bottom should have gradients.
130 scroll_view_->vertical_scroll_bar()->ScrollByAmount(
131 views::ScrollBar::ScrollAmount::kNextLine);
132 EXPECT_TRUE(HasGradientAtTop());
133 EXPECT_TRUE(HasGradientAtBottom());
135 // Scroll to end. Now only the top should have a gradient.
136 scroll_view_->vertical_scroll_bar()->ScrollByAmount(
137 views::ScrollBar::ScrollAmount::kEnd);
138 EXPECT_TRUE(HasGradientAtTop());
139 EXPECT_FALSE(HasGradientAtBottom());
142 TEST_F(ScrollViewGradientHelperTest, DeletingHelperRemovesMaskLayer) {
143 // Create a tall contents view.
144 AddScrollViewContentsWithHeight(500);
145 gradient_helper_->UpdateGradientMask();
147 // Precondition: Mask exists.
148 ASSERT_TRUE(HasGradientMask(scroll_view_->layer()));
150 gradient_helper_.reset();
151 ASSERT_FALSE(HasGradientMask(scroll_view_->layer()));