| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/message_center/views/slide_out_controller.h" |
| |
| #include "ui/views/test/views_test_base.h" |
| #include "ui/views/view.h" |
| |
| namespace message_center { |
| |
| namespace { |
| constexpr int kSwipeControlWidth = 30; // px |
| constexpr int kTargetWidth = 200; // px |
| } // namespace |
| |
| class SlideOutControllerDelegate : public SlideOutController::Delegate { |
| public: |
| explicit SlideOutControllerDelegate(views::View* target) : target_(target) {} |
| virtual ~SlideOutControllerDelegate() = default; |
| |
| ui::Layer* GetSlideOutLayer() override { return target_->layer(); } |
| |
| void OnSlideStarted() override { ++slide_started_count_; } |
| |
| void OnSlideChanged(bool in_progress) override { |
| slide_changed_last_value_ = in_progress; |
| ++slide_changed_count_; |
| } |
| |
| bool IsOnSlideChangedCalled() const { return (slide_changed_count_ > 0); } |
| |
| void OnSlideOut() override { ++slide_out_count_; } |
| |
| void reset() { |
| slide_started_count_ = 0; |
| slide_changed_count_ = 0; |
| slide_out_count_ = 0; |
| slide_changed_last_value_ = base::nullopt; |
| } |
| |
| base::Optional<bool> slide_changed_last_value_; |
| int slide_started_count_ = 0; |
| int slide_changed_count_ = 0; |
| int slide_out_count_ = 0; |
| |
| private: |
| views::View* const target_; |
| }; |
| |
| class SlideOutControllerTest : public views::ViewsTestBase { |
| public: |
| SlideOutControllerTest() = default; |
| ~SlideOutControllerTest() override = default; |
| |
| void SetUp() override { |
| views::ViewsTestBase::SetUp(); |
| |
| widget_ = std::make_unique<views::Widget>(); |
| |
| views::Widget::InitParams params = |
| CreateParams(views::Widget::InitParams::TYPE_POPUP); |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.bounds = gfx::Rect(50, 50, 650, 650); |
| widget_->Init(params); |
| views::View* root = widget_->GetRootView(); |
| |
| views::View* target_ = new views::View(); |
| target_->SetPaintToLayer(ui::LAYER_TEXTURED); |
| target_->SetSize(gfx::Size(kTargetWidth, 50)); |
| |
| root->AddChildView(target_); |
| widget_->Show(); |
| |
| delegate_ = std::make_unique<SlideOutControllerDelegate>(target_); |
| slide_out_controller_ = |
| std::make_unique<SlideOutController>(target_, delegate_.get()); |
| } |
| |
| void TearDown() override { |
| slide_out_controller_.reset(); |
| delegate_.reset(); |
| widget_.reset(); |
| |
| views::ViewsTestBase::TearDown(); |
| } |
| |
| protected: |
| SlideOutController* slide_out_controller() { |
| return slide_out_controller_.get(); |
| } |
| |
| SlideOutControllerDelegate* delegate() { return delegate_.get(); } |
| |
| void PostSequentialGestureEvent(const ui::GestureEventDetails& details) { |
| // Set the timestamp ahead one microsecond. |
| sequential_event_timestamp_ += base::TimeDelta::FromMicroseconds(1); |
| |
| ui::GestureEvent gesture_event( |
| 0, 0, ui::EF_NONE, base::TimeTicks() + sequential_event_timestamp_, |
| details); |
| slide_out_controller()->OnGestureEvent(&gesture_event); |
| } |
| |
| void PostSequentialSwipeEvent(int swipe_amount) { |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, swipe_amount, 0)); |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); |
| } |
| |
| private: |
| std::unique_ptr<views::Widget> widget_; |
| std::unique_ptr<SlideOutController> slide_out_controller_; |
| std::unique_ptr<SlideOutControllerDelegate> delegate_; |
| base::TimeDelta sequential_event_timestamp_; |
| }; |
| |
| TEST_F(SlideOutControllerTest, OnGestureEventAndDelegate) { |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); |
| |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| |
| delegate()->reset(); |
| |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE)); |
| |
| EXPECT_EQ(0, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_TRUE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| |
| delegate()->reset(); |
| |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); |
| |
| EXPECT_EQ(0, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_FALSE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| } |
| |
| TEST_F(SlideOutControllerTest, SlideOutAndClose) { |
| // Place a finger on notification. |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); |
| |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_EQ(0, delegate()->slide_changed_count_); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| |
| delegate()->reset(); |
| |
| // Move the finger horizontally by 101 px. (101 px is more than half of the |
| // target width 200 px) |
| PostSequentialGestureEvent(ui::GestureEventDetails( |
| ui::ET_GESTURE_SCROLL_UPDATE, kTargetWidth / 2 + 1, 0)); |
| |
| EXPECT_EQ(0, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_TRUE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| |
| delegate()->reset(); |
| |
| // Release the finger. |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); |
| |
| EXPECT_EQ(0, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_FALSE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| // The target has been scrolled out and the current location is moved by the |
| // width (200px). |
| EXPECT_EQ(kTargetWidth, |
| delegate()->GetSlideOutLayer()->transform().To2dTranslation().x()); |
| |
| delegate()->reset(); |
| |
| // Ensure a deferred OnSlideOut handler is called. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(1, delegate()->slide_out_count_); |
| } |
| |
| TEST_F(SlideOutControllerTest, SlideLittleAmountAndNotClose) { |
| // Place a finger on notification. |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); |
| |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| |
| delegate()->reset(); |
| |
| // Move the finger horizontally by 99 px. (99 px is less than half of the |
| // target width 200 px) |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 99, 0)); |
| |
| EXPECT_EQ(0, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_TRUE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| |
| delegate()->reset(); |
| |
| // Release the finger. |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); |
| |
| EXPECT_EQ(0, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_FALSE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| // The target has been moved back to the origin. |
| EXPECT_EQ(0.f, |
| delegate()->GetSlideOutLayer()->transform().To2dTranslation().x()); |
| |
| delegate()->reset(); |
| |
| // Ensure no deferred SlideOut handler. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| } |
| |
| TEST_F(SlideOutControllerTest, SetSwipeControlWidth_SwipeLessThanControlWidth) { |
| // Set the width of swipe control. |
| slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth); |
| |
| // Place a finger on notification. |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); |
| |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| |
| delegate()->reset(); |
| |
| // Move the finger horizontally by 29 px. (29 px is less than the swipe |
| // control width). |
| PostSequentialGestureEvent(ui::GestureEventDetails( |
| ui::ET_GESTURE_SCROLL_UPDATE, kSwipeControlWidth - 1, 0)); |
| |
| EXPECT_EQ(0, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_TRUE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| |
| delegate()->reset(); |
| |
| // Release the finger. |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); |
| |
| EXPECT_EQ(0, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_FALSE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| // The target has been moved back to the origin. |
| EXPECT_EQ(0.f, |
| delegate()->GetSlideOutLayer()->transform().To2dTranslation().x()); |
| |
| delegate()->reset(); |
| |
| // Ensure no deferred SlideOut handler. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| } |
| |
| TEST_F(SlideOutControllerTest, SwipeControlWidth_SwipeMoreThanControlWidth) { |
| // Set the width of swipe control. |
| slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth); |
| |
| // Place a finger on notification. |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); |
| |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| |
| delegate()->reset(); |
| |
| // Move the finger horizontally by 31 px. (31 px is more than the swipe |
| // control width). |
| PostSequentialGestureEvent(ui::GestureEventDetails( |
| ui::ET_GESTURE_SCROLL_UPDATE, kSwipeControlWidth + 1, 0)); |
| |
| EXPECT_EQ(0, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_TRUE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| |
| delegate()->reset(); |
| |
| // Release the finger. |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); |
| |
| EXPECT_EQ(0, delegate()->slide_started_count_); |
| // Slide is in progress. |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_TRUE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| // Swipe amount is the swipe control width. |
| EXPECT_EQ(kSwipeControlWidth, |
| delegate()->GetSlideOutLayer()->transform().To2dTranslation().x()); |
| |
| delegate()->reset(); |
| |
| // Ensure no deferred SlideOut handler. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| } |
| |
| TEST_F(SlideOutControllerTest, SetSwipeControlWidth_SwipeOut) { |
| // Set the width of swipe control. |
| slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth); |
| |
| // Place a finger on notification. |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); |
| |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| |
| delegate()->reset(); |
| |
| // Move the finger horizontally by 101 px. (101 px is more than the half of |
| // the target width). |
| PostSequentialGestureEvent(ui::GestureEventDetails( |
| ui::ET_GESTURE_SCROLL_UPDATE, kTargetWidth / 2 + 1, 0)); |
| |
| EXPECT_EQ(0, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_TRUE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| |
| delegate()->reset(); |
| |
| // Release the finger. |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); |
| |
| // ... and it is automatically slided out. |
| EXPECT_EQ(0, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_FALSE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| EXPECT_EQ(kTargetWidth, |
| delegate()->GetSlideOutLayer()->transform().To2dTranslation().x()); |
| |
| delegate()->reset(); |
| |
| // Ensure a deferred SlideOut handler is called once. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(1, delegate()->slide_out_count_); |
| } |
| |
| TEST_F(SlideOutControllerTest, SwipeControlWidth_SnapAndSwipeOut) { |
| // Set the width of swipe control. |
| slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth); |
| |
| // Snap to the swipe control. |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); |
| PostSequentialGestureEvent(ui::GestureEventDetails( |
| ui::ET_GESTURE_SCROLL_UPDATE, kSwipeControlWidth, 0)); |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_TRUE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| EXPECT_EQ(kSwipeControlWidth, |
| delegate()->GetSlideOutLayer()->transform().To2dTranslation().x()); |
| |
| delegate()->reset(); |
| |
| // Swipe horizontally by 70 px. |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 70, 0)); |
| PostSequentialGestureEvent( |
| ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); |
| |
| // ... and it is automatically slided out. |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_FALSE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| EXPECT_EQ(kTargetWidth, |
| delegate()->GetSlideOutLayer()->transform().To2dTranslation().x()); |
| |
| delegate()->reset(); |
| |
| // Ensure a deferred OnSlideOut handler is called. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(1, delegate()->slide_out_count_); |
| } |
| |
| TEST_F(SlideOutControllerTest, SwipeControlWidth_SnapAndSnapToControl) { |
| // Set the width of swipe control. |
| slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth); |
| |
| // Snap to the swipe control. |
| PostSequentialSwipeEvent(kSwipeControlWidth + 10); |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_TRUE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| EXPECT_EQ(kSwipeControlWidth, |
| delegate()->GetSlideOutLayer()->transform().To2dTranslation().x()); |
| |
| delegate()->reset(); |
| |
| // Swipe horizontally by 40 px for the same direction. |
| PostSequentialSwipeEvent(40); |
| |
| // Snap automatically back to the swipe control. |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_TRUE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| EXPECT_EQ(kSwipeControlWidth, |
| delegate()->GetSlideOutLayer()->transform().To2dTranslation().x()); |
| |
| delegate()->reset(); |
| |
| // Ensure no deferred OnSlideOut handler. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| } |
| |
| TEST_F(SlideOutControllerTest, SwipeControlWidth_SnapAndBackToOrigin) { |
| // Set the width of swipe control. |
| slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth); |
| |
| // Snap to the swipe control. |
| PostSequentialSwipeEvent(kSwipeControlWidth + 20); |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_TRUE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| EXPECT_EQ(kSwipeControlWidth, |
| delegate()->GetSlideOutLayer()->transform().To2dTranslation().x()); |
| |
| delegate()->reset(); |
| |
| // Swipe to the reversed direction by -1 px. |
| PostSequentialSwipeEvent(-1); |
| |
| // Snap automatically back to the origin. |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_FALSE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| EXPECT_EQ(0, |
| delegate()->GetSlideOutLayer()->transform().To2dTranslation().x()); |
| |
| delegate()->reset(); |
| |
| // Ensure no deferred OnSlideOut handler. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| } |
| |
| TEST_F(SlideOutControllerTest, SwipeControlWidth_NotSnapAndBackToOrigin) { |
| // Set the width of swipe control. |
| slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth); |
| |
| // Swipe partially but it's not enough to snap to the swipe control. So it is |
| // back to the origin |
| PostSequentialSwipeEvent(kSwipeControlWidth - 1); |
| EXPECT_EQ(1, delegate()->slide_started_count_); |
| EXPECT_TRUE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_FALSE(delegate()->slide_changed_last_value_.value()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| EXPECT_EQ(0, |
| delegate()->GetSlideOutLayer()->transform().To2dTranslation().x()); |
| |
| delegate()->reset(); |
| |
| // Ensure no deferred OnSlideOut handler. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(delegate()->IsOnSlideChangedCalled()); |
| EXPECT_EQ(0, delegate()->slide_out_count_); |
| } |
| |
| } // namespace message_center |