1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/views/animation/slide_out_controller.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "ui/views/animation/slide_out_controller_delegate.h"
11 #include "ui/views/test/views_test_base.h"
12 #include "ui/views/view.h"
13 
14 namespace views {
15 
16 namespace {
17 constexpr int kSwipeControlWidth = 30;  // px
18 constexpr int kTargetWidth = 200;       // px
19 }  // namespace
20 
21 class TestSlideOutControllerDelegate : public SlideOutControllerDelegate {
22  public:
TestSlideOutControllerDelegate(View * target)23   explicit TestSlideOutControllerDelegate(View* target) : target_(target) {}
24   ~TestSlideOutControllerDelegate() override = default;
25 
GetSlideOutLayer()26   ui::Layer* GetSlideOutLayer() override { return target_->layer(); }
27 
OnSlideStarted()28   void OnSlideStarted() override { ++slide_started_count_; }
29 
OnSlideChanged(bool in_progress)30   void OnSlideChanged(bool in_progress) override {
31     slide_changed_last_value_ = in_progress;
32     ++slide_changed_count_;
33   }
34 
IsOnSlideChangedCalled() const35   bool IsOnSlideChangedCalled() const { return (slide_changed_count_ > 0); }
36 
OnSlideOut()37   void OnSlideOut() override { ++slide_out_count_; }
38 
reset()39   void reset() {
40     slide_started_count_ = 0;
41     slide_changed_count_ = 0;
42     slide_out_count_ = 0;
43     slide_changed_last_value_ = base::nullopt;
44   }
45 
46   base::Optional<bool> slide_changed_last_value_;
47   int slide_started_count_ = 0;
48   int slide_changed_count_ = 0;
49   int slide_out_count_ = 0;
50 
51  private:
52   View* const target_;
53 };
54 
55 class SlideOutControllerTest : public ViewsTestBase {
56  public:
57   SlideOutControllerTest() = default;
58   ~SlideOutControllerTest() override = default;
59 
SetUp()60   void SetUp() override {
61     ViewsTestBase::SetUp();
62 
63     widget_ = std::make_unique<Widget>();
64 
65     Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
66     params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
67     params.bounds = gfx::Rect(50, 50, 650, 650);
68     widget_->Init(std::move(params));
69     View* root = widget_->GetRootView();
70 
71     View* target_ = new View();
72     target_->SetPaintToLayer(ui::LAYER_TEXTURED);
73     target_->SetSize(gfx::Size(kTargetWidth, 50));
74 
75     root->AddChildView(target_);
76     widget_->Show();
77 
78     delegate_ = std::make_unique<TestSlideOutControllerDelegate>(target_);
79     slide_out_controller_ =
80         std::make_unique<SlideOutController>(target_, delegate_.get());
81   }
82 
TearDown()83   void TearDown() override {
84     slide_out_controller_.reset();
85     delegate_.reset();
86     widget_.reset();
87 
88     ViewsTestBase::TearDown();
89   }
90 
91  protected:
slide_out_controller()92   SlideOutController* slide_out_controller() {
93     return slide_out_controller_.get();
94   }
95 
delegate()96   TestSlideOutControllerDelegate* delegate() { return delegate_.get(); }
97 
PostSequentialGestureEvent(const ui::GestureEventDetails & details)98   void PostSequentialGestureEvent(const ui::GestureEventDetails& details) {
99     // Set the timestamp ahead one microsecond.
100     sequential_event_timestamp_ += base::TimeDelta::FromMicroseconds(1);
101 
102     ui::GestureEvent gesture_event(
103         0, 0, ui::EF_NONE, base::TimeTicks() + sequential_event_timestamp_,
104         details);
105     slide_out_controller()->OnGestureEvent(&gesture_event);
106   }
107 
PostSequentialSwipeEvent(int swipe_amount)108   void PostSequentialSwipeEvent(int swipe_amount) {
109     PostSequentialGestureEvent(
110         ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
111     PostSequentialGestureEvent(
112         ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, swipe_amount, 0));
113     PostSequentialGestureEvent(
114         ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
115   }
116 
117  private:
118   std::unique_ptr<Widget> widget_;
119   std::unique_ptr<SlideOutController> slide_out_controller_;
120   std::unique_ptr<TestSlideOutControllerDelegate> delegate_;
121   base::TimeDelta sequential_event_timestamp_;
122 };
123 
TEST_F(SlideOutControllerTest,OnGestureEventAndDelegate)124 TEST_F(SlideOutControllerTest, OnGestureEventAndDelegate) {
125   PostSequentialGestureEvent(
126       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
127 
128   EXPECT_EQ(1, delegate()->slide_started_count_);
129   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
130   EXPECT_EQ(0, delegate()->slide_out_count_);
131 
132   delegate()->reset();
133 
134   PostSequentialGestureEvent(
135       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE));
136 
137   EXPECT_EQ(0, delegate()->slide_started_count_);
138   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
139   EXPECT_TRUE(delegate()->slide_changed_last_value_.value());
140   EXPECT_EQ(0, delegate()->slide_out_count_);
141 
142   delegate()->reset();
143 
144   PostSequentialGestureEvent(
145       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
146 
147   EXPECT_EQ(0, delegate()->slide_started_count_);
148   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
149   EXPECT_FALSE(delegate()->slide_changed_last_value_.value());
150   EXPECT_EQ(0, delegate()->slide_out_count_);
151 }
152 
TEST_F(SlideOutControllerTest,SlideOutAndClose)153 TEST_F(SlideOutControllerTest, SlideOutAndClose) {
154   // Place a finger on notification.
155   PostSequentialGestureEvent(
156       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
157 
158   EXPECT_EQ(1, delegate()->slide_started_count_);
159   EXPECT_EQ(0, delegate()->slide_changed_count_);
160   EXPECT_EQ(0, delegate()->slide_out_count_);
161 
162   delegate()->reset();
163 
164   // Move the finger horizontally by 101 px. (101 px is more than half of the
165   // target width 200 px)
166   PostSequentialGestureEvent(ui::GestureEventDetails(
167       ui::ET_GESTURE_SCROLL_UPDATE, kTargetWidth / 2 + 1, 0));
168 
169   EXPECT_EQ(0, delegate()->slide_started_count_);
170   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
171   EXPECT_TRUE(delegate()->slide_changed_last_value_.value());
172   EXPECT_EQ(0, delegate()->slide_out_count_);
173 
174   delegate()->reset();
175 
176   // Release the finger.
177   PostSequentialGestureEvent(
178       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
179 
180   EXPECT_EQ(0, delegate()->slide_started_count_);
181   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
182   EXPECT_FALSE(delegate()->slide_changed_last_value_.value());
183   EXPECT_EQ(0, delegate()->slide_out_count_);
184   // The target has been scrolled out and the current location is moved by the
185   // width (200px).
186   EXPECT_EQ(kTargetWidth,
187             delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
188 
189   delegate()->reset();
190 
191   // Ensure a deferred OnSlideOut handler is called.
192   base::RunLoop().RunUntilIdle();
193   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
194   EXPECT_EQ(1, delegate()->slide_out_count_);
195 }
196 
TEST_F(SlideOutControllerTest,SlideLittleAmountAndNotClose)197 TEST_F(SlideOutControllerTest, SlideLittleAmountAndNotClose) {
198   // Place a finger on notification.
199   PostSequentialGestureEvent(
200       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
201 
202   EXPECT_EQ(1, delegate()->slide_started_count_);
203   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
204   EXPECT_EQ(0, delegate()->slide_out_count_);
205 
206   delegate()->reset();
207 
208   // Move the finger horizontally by 99 px. (99 px is less than half of the
209   // target width 200 px)
210   PostSequentialGestureEvent(
211       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 99, 0));
212 
213   EXPECT_EQ(0, delegate()->slide_started_count_);
214   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
215   EXPECT_TRUE(delegate()->slide_changed_last_value_.value());
216   EXPECT_EQ(0, delegate()->slide_out_count_);
217 
218   delegate()->reset();
219 
220   // Release the finger.
221   PostSequentialGestureEvent(
222       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
223 
224   EXPECT_EQ(0, delegate()->slide_started_count_);
225   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
226   EXPECT_FALSE(delegate()->slide_changed_last_value_.value());
227   EXPECT_EQ(0, delegate()->slide_out_count_);
228   // The target has been moved back to the origin.
229   EXPECT_EQ(0.f,
230             delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
231 
232   delegate()->reset();
233 
234   // Ensure no deferred SlideOut handler.
235   base::RunLoop().RunUntilIdle();
236   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
237   EXPECT_EQ(0, delegate()->slide_out_count_);
238 }
239 
TEST_F(SlideOutControllerTest,SetSwipeControlWidth_SwipeLessThanControlWidth)240 TEST_F(SlideOutControllerTest, SetSwipeControlWidth_SwipeLessThanControlWidth) {
241   // Set the width of swipe control.
242   slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth);
243 
244   // Place a finger on notification.
245   PostSequentialGestureEvent(
246       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
247 
248   EXPECT_EQ(1, delegate()->slide_started_count_);
249   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
250   EXPECT_EQ(0, delegate()->slide_out_count_);
251 
252   delegate()->reset();
253 
254   // Move the finger horizontally by 29 px. (29 px is less than the swipe
255   // control width).
256   PostSequentialGestureEvent(ui::GestureEventDetails(
257       ui::ET_GESTURE_SCROLL_UPDATE, kSwipeControlWidth - 1, 0));
258 
259   EXPECT_EQ(0, delegate()->slide_started_count_);
260   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
261   EXPECT_TRUE(delegate()->slide_changed_last_value_.value());
262   EXPECT_EQ(0, delegate()->slide_out_count_);
263 
264   delegate()->reset();
265 
266   // Release the finger.
267   PostSequentialGestureEvent(
268       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
269 
270   EXPECT_EQ(0, delegate()->slide_started_count_);
271   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
272   EXPECT_FALSE(delegate()->slide_changed_last_value_.value());
273   EXPECT_EQ(0, delegate()->slide_out_count_);
274   // The target has been moved back to the origin.
275   EXPECT_EQ(0.f,
276             delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
277 
278   delegate()->reset();
279 
280   // Ensure no deferred SlideOut handler.
281   base::RunLoop().RunUntilIdle();
282   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
283   EXPECT_EQ(0, delegate()->slide_out_count_);
284 }
285 
TEST_F(SlideOutControllerTest,SwipeControlWidth_SwipeMoreThanControlWidth)286 TEST_F(SlideOutControllerTest, SwipeControlWidth_SwipeMoreThanControlWidth) {
287   // Set the width of swipe control.
288   slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth);
289 
290   // Place a finger on notification.
291   PostSequentialGestureEvent(
292       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
293 
294   EXPECT_EQ(1, delegate()->slide_started_count_);
295   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
296   EXPECT_EQ(0, delegate()->slide_out_count_);
297 
298   delegate()->reset();
299 
300   // Move the finger horizontally by 31 px. (31 px is more than the swipe
301   // control width).
302   PostSequentialGestureEvent(ui::GestureEventDetails(
303       ui::ET_GESTURE_SCROLL_UPDATE, kSwipeControlWidth + 1, 0));
304 
305   EXPECT_EQ(0, delegate()->slide_started_count_);
306   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
307   EXPECT_TRUE(delegate()->slide_changed_last_value_.value());
308   EXPECT_EQ(0, delegate()->slide_out_count_);
309 
310   delegate()->reset();
311 
312   // Release the finger.
313   PostSequentialGestureEvent(
314       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
315 
316   EXPECT_EQ(0, delegate()->slide_started_count_);
317   // Slide is in progress.
318   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
319   EXPECT_TRUE(delegate()->slide_changed_last_value_.value());
320   EXPECT_EQ(0, delegate()->slide_out_count_);
321   // Swipe amount is the swipe control width.
322   EXPECT_EQ(kSwipeControlWidth,
323             delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
324 
325   delegate()->reset();
326 
327   // Ensure no deferred SlideOut handler.
328   base::RunLoop().RunUntilIdle();
329   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
330   EXPECT_EQ(0, delegate()->slide_out_count_);
331 }
332 
TEST_F(SlideOutControllerTest,SetSwipeControlWidth_SwipeOut)333 TEST_F(SlideOutControllerTest, SetSwipeControlWidth_SwipeOut) {
334   // Set the width of swipe control.
335   slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth);
336 
337   // Place a finger on notification.
338   PostSequentialGestureEvent(
339       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
340 
341   EXPECT_EQ(1, delegate()->slide_started_count_);
342   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
343   EXPECT_EQ(0, delegate()->slide_out_count_);
344 
345   delegate()->reset();
346 
347   // Move the finger horizontally by 101 px. (101 px is more than the half of
348   // the target width).
349   PostSequentialGestureEvent(ui::GestureEventDetails(
350       ui::ET_GESTURE_SCROLL_UPDATE, kTargetWidth / 2 + 1, 0));
351 
352   EXPECT_EQ(0, delegate()->slide_started_count_);
353   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
354   EXPECT_TRUE(delegate()->slide_changed_last_value_.value());
355   EXPECT_EQ(0, delegate()->slide_out_count_);
356 
357   delegate()->reset();
358 
359   // Release the finger.
360   PostSequentialGestureEvent(
361       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
362 
363   // ... and it is automatically slided out.
364   EXPECT_EQ(0, delegate()->slide_started_count_);
365   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
366   EXPECT_FALSE(delegate()->slide_changed_last_value_.value());
367   EXPECT_EQ(0, delegate()->slide_out_count_);
368   EXPECT_EQ(kTargetWidth,
369             delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
370 
371   delegate()->reset();
372 
373   // Ensure a deferred SlideOut handler is called once.
374   base::RunLoop().RunUntilIdle();
375   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
376   EXPECT_EQ(1, delegate()->slide_out_count_);
377 }
378 
TEST_F(SlideOutControllerTest,SwipeControlWidth_SnapAndSwipeOut)379 TEST_F(SlideOutControllerTest, SwipeControlWidth_SnapAndSwipeOut) {
380   // Set the width of swipe control.
381   slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth);
382 
383   // Snap to the swipe control.
384   PostSequentialGestureEvent(
385       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
386   PostSequentialGestureEvent(ui::GestureEventDetails(
387       ui::ET_GESTURE_SCROLL_UPDATE, kSwipeControlWidth, 0));
388   PostSequentialGestureEvent(
389       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
390   EXPECT_EQ(1, delegate()->slide_started_count_);
391   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
392   EXPECT_TRUE(delegate()->slide_changed_last_value_.value());
393   EXPECT_EQ(0, delegate()->slide_out_count_);
394   EXPECT_EQ(kSwipeControlWidth,
395             delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
396 
397   delegate()->reset();
398 
399   // Swipe horizontally by 70 px.
400   PostSequentialGestureEvent(
401       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
402   PostSequentialGestureEvent(
403       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 70, 0));
404   PostSequentialGestureEvent(
405       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
406 
407   // ... and it is automatically slided out.
408   EXPECT_EQ(1, delegate()->slide_started_count_);
409   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
410   EXPECT_FALSE(delegate()->slide_changed_last_value_.value());
411   EXPECT_EQ(0, delegate()->slide_out_count_);
412   EXPECT_EQ(kTargetWidth,
413             delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
414 
415   delegate()->reset();
416 
417   // Ensure a deferred OnSlideOut handler is called.
418   base::RunLoop().RunUntilIdle();
419   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
420   EXPECT_EQ(1, delegate()->slide_out_count_);
421 }
422 
TEST_F(SlideOutControllerTest,SwipeControlWidth_SnapAndSnapToControl)423 TEST_F(SlideOutControllerTest, SwipeControlWidth_SnapAndSnapToControl) {
424   // Set the width of swipe control.
425   slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth);
426 
427   // Snap to the swipe control.
428   PostSequentialSwipeEvent(kSwipeControlWidth + 10);
429   EXPECT_EQ(1, delegate()->slide_started_count_);
430   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
431   EXPECT_TRUE(delegate()->slide_changed_last_value_.value());
432   EXPECT_EQ(0, delegate()->slide_out_count_);
433   EXPECT_EQ(kSwipeControlWidth,
434             delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
435 
436   delegate()->reset();
437 
438   // Swipe horizontally by 40 px for the same direction.
439   PostSequentialSwipeEvent(40);
440 
441   // Snap automatically back to the swipe control.
442   EXPECT_EQ(1, delegate()->slide_started_count_);
443   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
444   EXPECT_TRUE(delegate()->slide_changed_last_value_.value());
445   EXPECT_EQ(0, delegate()->slide_out_count_);
446   EXPECT_EQ(kSwipeControlWidth,
447             delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
448 
449   delegate()->reset();
450 
451   // Ensure no deferred OnSlideOut handler.
452   base::RunLoop().RunUntilIdle();
453   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
454   EXPECT_EQ(0, delegate()->slide_out_count_);
455 }
456 
TEST_F(SlideOutControllerTest,SwipeControlWidth_SnapAndBackToOrigin)457 TEST_F(SlideOutControllerTest, SwipeControlWidth_SnapAndBackToOrigin) {
458   // Set the width of swipe control.
459   slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth);
460 
461   // Snap to the swipe control.
462   PostSequentialSwipeEvent(kSwipeControlWidth + 20);
463   EXPECT_EQ(1, delegate()->slide_started_count_);
464   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
465   EXPECT_TRUE(delegate()->slide_changed_last_value_.value());
466   EXPECT_EQ(0, delegate()->slide_out_count_);
467   EXPECT_EQ(kSwipeControlWidth,
468             delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
469 
470   delegate()->reset();
471 
472   // Swipe to the reversed direction by -1 px.
473   PostSequentialSwipeEvent(-1);
474 
475   // Snap automatically back to the origin.
476   EXPECT_EQ(1, delegate()->slide_started_count_);
477   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
478   EXPECT_FALSE(delegate()->slide_changed_last_value_.value());
479   EXPECT_EQ(0, delegate()->slide_out_count_);
480   EXPECT_EQ(0,
481             delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
482 
483   delegate()->reset();
484 
485   // Ensure no deferred OnSlideOut handler.
486   base::RunLoop().RunUntilIdle();
487   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
488   EXPECT_EQ(0, delegate()->slide_out_count_);
489 }
490 
TEST_F(SlideOutControllerTest,SwipeControlWidth_NotSnapAndBackToOrigin)491 TEST_F(SlideOutControllerTest, SwipeControlWidth_NotSnapAndBackToOrigin) {
492   // Set the width of swipe control.
493   slide_out_controller()->SetSwipeControlWidth(kSwipeControlWidth);
494 
495   // Swipe partially but it's not enough to snap to the swipe control. So it is
496   // back to the origin
497   PostSequentialSwipeEvent(kSwipeControlWidth - 1);
498   EXPECT_EQ(1, delegate()->slide_started_count_);
499   EXPECT_TRUE(delegate()->IsOnSlideChangedCalled());
500   EXPECT_FALSE(delegate()->slide_changed_last_value_.value());
501   EXPECT_EQ(0, delegate()->slide_out_count_);
502   EXPECT_EQ(0,
503             delegate()->GetSlideOutLayer()->transform().To2dTranslation().x());
504 
505   delegate()->reset();
506 
507   // Ensure no deferred OnSlideOut handler.
508   base::RunLoop().RunUntilIdle();
509   EXPECT_FALSE(delegate()->IsOnSlideChangedCalled());
510   EXPECT_EQ(0, delegate()->slide_out_count_);
511 }
512 
513 }  // namespace views
514