123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547 |
- // 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/views/animation/slide_out_controller.h"
- #include <memory>
- #include <utility>
- #include "base/memory/raw_ptr.h"
- #include "base/time/time.h"
- #include "ui/compositor/layer.h"
- #include "ui/views/animation/slide_out_controller_delegate.h"
- #include "ui/views/test/views_test_base.h"
- #include "ui/views/view.h"
- namespace views {
- namespace {
- constexpr int kSwipeControlWidth = 30; // px
- constexpr int kTargetWidth = 200; // px
- } // namespace
- class TestSlideOutControllerDelegate : public SlideOutControllerDelegate {
- public:
- explicit TestSlideOutControllerDelegate(View* target) : target_(target) {}
- ~TestSlideOutControllerDelegate() override = 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_ = absl::nullopt;
- }
- absl::optional<bool> slide_changed_last_value_;
- int slide_started_count_ = 0;
- int slide_changed_count_ = 0;
- int slide_out_count_ = 0;
- private:
- const raw_ptr<View> target_;
- };
- class SlideOutControllerTest : public ViewsTestBase {
- public:
- SlideOutControllerTest() = default;
- ~SlideOutControllerTest() override = default;
- void SetUp() override {
- ViewsTestBase::SetUp();
- widget_ = std::make_unique<Widget>();
- Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
- params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- params.bounds = gfx::Rect(50, 50, 650, 650);
- widget_->Init(std::move(params));
- View* root = widget_->GetRootView();
- View* target_ = new View();
- target_->SetPaintToLayer(ui::LAYER_TEXTURED);
- target_->SetSize(gfx::Size(kTargetWidth, 50));
- root->AddChildView(target_);
- widget_->Show();
- delegate_ = std::make_unique<TestSlideOutControllerDelegate>(target_);
- slide_out_controller_ =
- std::make_unique<SlideOutController>(target_, delegate_.get());
- }
- void TearDown() override {
- slide_out_controller_.reset();
- delegate_.reset();
- widget_.reset();
- ViewsTestBase::TearDown();
- }
- protected:
- SlideOutController* slide_out_controller() {
- return slide_out_controller_.get();
- }
- TestSlideOutControllerDelegate* delegate() { return delegate_.get(); }
- void PostSequentialGestureEvent(const ui::GestureEventDetails& details) {
- // Set the timestamp ahead one microsecond.
- sequential_event_timestamp_ += base::Microseconds(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<Widget> widget_;
- std::unique_ptr<SlideOutController> slide_out_controller_;
- std::unique_ptr<TestSlideOutControllerDelegate> 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_);
- }
- class SwipeControlTest
- : public SlideOutControllerTest,
- public testing::WithParamInterface<SlideOutController::SlideMode> {
- public:
- SwipeControlTest() = default;
- SwipeControlTest(const SwipeControlTest&) = delete;
- SwipeControlTest& operator=(const SwipeControlTest&) = delete;
- ~SwipeControlTest() override = default;
- void SetUp() override {
- SlideOutControllerTest::SetUp();
- slide_out_controller()->set_slide_mode(GetParam());
- }
- };
- INSTANTIATE_TEST_SUITE_P(
- All,
- SwipeControlTest,
- ::testing::Values(SlideOutController::SlideMode::kFull,
- SlideOutController::SlideMode::kPartial));
- TEST_P(SwipeControlTest, 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_P(SwipeControlTest, 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_P(SwipeControlTest, SetSwipeControlWidth_SwipeOut) {
- const bool swipe_out_supported =
- slide_out_controller()->mode() == SlideOutController::SlideMode::kFull;
- // 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 if |swipe_out_supported|.
- EXPECT_EQ(0, delegate()->slide_started_count_);
- EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
- EXPECT_EQ(delegate()->slide_changed_last_value_.value(),
- !swipe_out_supported);
- EXPECT_EQ(0, delegate()->slide_out_count_);
- EXPECT_EQ(swipe_out_supported ? kTargetWidth : kSwipeControlWidth,
- delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
- delegate()->reset();
- // Ensure a deferred SlideOut handler is called once.
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
- EXPECT_EQ(swipe_out_supported ? 1 : 0, delegate()->slide_out_count_);
- }
- TEST_P(SwipeControlTest, SwipeControlWidth_SnapAndSwipeOut) {
- const bool swipe_out_supported =
- slide_out_controller()->mode() == SlideOutController::SlideMode::kFull;
- // 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 if if |swipe_out_supported|.
- EXPECT_EQ(1, delegate()->slide_started_count_);
- EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
- EXPECT_EQ(delegate()->slide_changed_last_value_.value(),
- !swipe_out_supported);
- EXPECT_EQ(0, delegate()->slide_out_count_);
- EXPECT_EQ(swipe_out_supported ? kTargetWidth : kSwipeControlWidth,
- delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
- delegate()->reset();
- // Ensure a deferred OnSlideOut handler is called.
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
- EXPECT_EQ(swipe_out_supported ? 1 : 0, delegate()->slide_out_count_);
- }
- TEST_P(SwipeControlTest, 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_P(SwipeControlTest, 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_P(SwipeControlTest, 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 views
|