system_gesture_event_filter_unittest.cc revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright (c) 2012 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 "ash/wm/system_gesture_event_filter.h" 6 7#include "ash/accelerators/accelerator_controller.h" 8#include "ash/ash_switches.h" 9#include "ash/display/display_manager.h" 10#include "ash/launcher/launcher.h" 11#include "ash/shelf/shelf_model.h" 12#include "ash/shell.h" 13#include "ash/system/brightness_control_delegate.h" 14#include "ash/system/tray/system_tray_delegate.h" 15#include "ash/test/ash_test_base.h" 16#include "ash/test/display_manager_test_api.h" 17#include "ash/test/shell_test_api.h" 18#include "ash/test/test_launcher_delegate.h" 19#include "ash/volume_control_delegate.h" 20#include "ash/wm/gestures/long_press_affordance_handler.h" 21#include "ash/wm/window_state.h" 22#include "ash/wm/window_util.h" 23#include "ash/wm/workspace/snap_sizer.h" 24#include "base/command_line.h" 25#include "base/time/time.h" 26#include "base/timer/timer.h" 27#include "ui/aura/root_window.h" 28#include "ui/aura/test/event_generator.h" 29#include "ui/aura/test/test_windows.h" 30#include "ui/base/hit_test.h" 31#include "ui/base/ui_base_switches.h" 32#include "ui/events/event.h" 33#include "ui/events/event_utils.h" 34#include "ui/events/gestures/gesture_configuration.h" 35#include "ui/gfx/screen.h" 36#include "ui/gfx/size.h" 37#include "ui/views/widget/widget_delegate.h" 38 39namespace ash { 40namespace test { 41 42namespace { 43 44class DelegatePercentTracker { 45 public: 46 explicit DelegatePercentTracker() 47 : handle_percent_count_(0), 48 handle_percent_(0){ 49 } 50 int handle_percent_count() const { 51 return handle_percent_count_; 52 } 53 double handle_percent() const { 54 return handle_percent_; 55 } 56 void SetPercent(double percent) { 57 handle_percent_ = percent; 58 handle_percent_count_++; 59 } 60 61 private: 62 int handle_percent_count_; 63 int handle_percent_; 64 65 DISALLOW_COPY_AND_ASSIGN(DelegatePercentTracker); 66}; 67 68class DummyVolumeControlDelegate : public VolumeControlDelegate, 69 public DelegatePercentTracker { 70 public: 71 explicit DummyVolumeControlDelegate() {} 72 virtual ~DummyVolumeControlDelegate() {} 73 74 virtual bool HandleVolumeMute(const ui::Accelerator& accelerator) OVERRIDE { 75 return true; 76 } 77 virtual bool HandleVolumeDown(const ui::Accelerator& accelerator) OVERRIDE { 78 return true; 79 } 80 virtual bool HandleVolumeUp(const ui::Accelerator& accelerator) OVERRIDE { 81 return true; 82 } 83 84 private: 85 DISALLOW_COPY_AND_ASSIGN(DummyVolumeControlDelegate); 86}; 87 88class DummyBrightnessControlDelegate : public BrightnessControlDelegate, 89 public DelegatePercentTracker { 90 public: 91 explicit DummyBrightnessControlDelegate() {} 92 virtual ~DummyBrightnessControlDelegate() {} 93 94 virtual bool HandleBrightnessDown( 95 const ui::Accelerator& accelerator) OVERRIDE { return true; } 96 virtual bool HandleBrightnessUp( 97 const ui::Accelerator& accelerator) OVERRIDE { return true; } 98 virtual void SetBrightnessPercent(double percent, bool gradual) OVERRIDE { 99 SetPercent(percent); 100 } 101 virtual void GetBrightnessPercent( 102 const base::Callback<void(double)>& callback) OVERRIDE { 103 callback.Run(100.0); 104 } 105 106 private: 107 DISALLOW_COPY_AND_ASSIGN(DummyBrightnessControlDelegate); 108}; 109 110class ResizableWidgetDelegate : public views::WidgetDelegateView { 111 public: 112 ResizableWidgetDelegate() {} 113 virtual ~ResizableWidgetDelegate() {} 114 115 private: 116 virtual bool CanResize() const OVERRIDE { return true; } 117 virtual bool CanMaximize() const OVERRIDE { return true; } 118 virtual void DeleteDelegate() OVERRIDE { delete this; } 119 120 DISALLOW_COPY_AND_ASSIGN(ResizableWidgetDelegate); 121}; 122 123// Support class for testing windows with a maximum size. 124class MaxSizeNCFV : public views::NonClientFrameView { 125 public: 126 MaxSizeNCFV() {} 127 private: 128 virtual gfx::Size GetMaximumSize() OVERRIDE { 129 return gfx::Size(200, 200); 130 } 131 virtual gfx::Rect GetBoundsForClientView() const OVERRIDE { 132 return gfx::Rect(); 133 }; 134 135 virtual gfx::Rect GetWindowBoundsForClientBounds( 136 const gfx::Rect& client_bounds) const OVERRIDE { 137 return gfx::Rect(); 138 }; 139 140 // This function must ask the ClientView to do a hittest. We don't do this in 141 // the parent NonClientView because that makes it more difficult to calculate 142 // hittests for regions that are partially obscured by the ClientView, e.g. 143 // HTSYSMENU. 144 virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE { 145 return HTNOWHERE; 146 } 147 virtual void GetWindowMask(const gfx::Size& size, 148 gfx::Path* window_mask) OVERRIDE {} 149 virtual void ResetWindowControls() OVERRIDE {} 150 virtual void UpdateWindowIcon() OVERRIDE {} 151 virtual void UpdateWindowTitle() OVERRIDE {} 152 153 DISALLOW_COPY_AND_ASSIGN(MaxSizeNCFV); 154}; 155 156class MaxSizeWidgetDelegate : public views::WidgetDelegateView { 157 public: 158 MaxSizeWidgetDelegate() {} 159 virtual ~MaxSizeWidgetDelegate() {} 160 161 private: 162 virtual bool CanResize() const OVERRIDE { return true; } 163 virtual bool CanMaximize() const OVERRIDE { return false; } 164 virtual void DeleteDelegate() OVERRIDE { delete this; } 165 virtual views::NonClientFrameView* CreateNonClientFrameView( 166 views::Widget* widget) OVERRIDE { 167 return new MaxSizeNCFV; 168 } 169 170 DISALLOW_COPY_AND_ASSIGN(MaxSizeWidgetDelegate); 171}; 172 173} // namespace 174 175class SystemGestureEventFilterTest 176 : public AshTestBase, 177 public testing::WithParamInterface<bool> { 178 public: 179 SystemGestureEventFilterTest() : AshTestBase(), docked_enabled_(GetParam()) {} 180 virtual ~SystemGestureEventFilterTest() {} 181 182 internal::LongPressAffordanceHandler* GetLongPressAffordance() { 183 ShellTestApi shell_test(Shell::GetInstance()); 184 return shell_test.system_gesture_event_filter()-> 185 long_press_affordance_.get(); 186 } 187 188 base::OneShotTimer<internal::LongPressAffordanceHandler>* 189 GetLongPressAffordanceTimer() { 190 return &GetLongPressAffordance()->timer_; 191 } 192 193 aura::Window* GetLongPressAffordanceTarget() { 194 return GetLongPressAffordance()->tap_down_target_; 195 } 196 197 views::View* GetLongPressAffordanceView() { 198 return reinterpret_cast<views::View*>( 199 GetLongPressAffordance()->view_.get()); 200 } 201 202 // Overridden from AshTestBase: 203 virtual void SetUp() OVERRIDE { 204 CommandLine::ForCurrentProcess()->AppendSwitch( 205 ash::switches::kAshEnableAdvancedGestures); 206 if (!docked_enabled_) { 207 CommandLine::ForCurrentProcess()->AppendSwitch( 208 ash::switches::kAshDisableDockedWindows); 209 } 210 test::AshTestBase::SetUp(); 211 // Enable brightness key. 212 test::DisplayManagerTestApi(Shell::GetInstance()->display_manager()). 213 SetFirstDisplayAsInternalDisplay(); 214 } 215 216 private: 217 // true if docked windows are enabled with a flag. 218 bool docked_enabled_; 219 220 DISALLOW_COPY_AND_ASSIGN(SystemGestureEventFilterTest); 221}; 222 223ui::GestureEvent* CreateGesture(ui::EventType type, 224 int x, 225 int y, 226 float delta_x, 227 float delta_y, 228 int touch_id) { 229 return new ui::GestureEvent(type, x, y, 0, 230 base::TimeDelta::FromMilliseconds(base::Time::Now().ToDoubleT() * 1000), 231 ui::GestureEventDetails(type, delta_x, delta_y), 1 << touch_id); 232} 233 234TEST_P(SystemGestureEventFilterTest, LongPressAffordanceStateOnCaptureLoss) { 235 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 236 237 aura::test::TestWindowDelegate delegate; 238 scoped_ptr<aura::Window> window0( 239 aura::test::CreateTestWindowWithDelegate( 240 &delegate, 9, gfx::Rect(0, 0, 100, 100), root_window)); 241 scoped_ptr<aura::Window> window1( 242 aura::test::CreateTestWindowWithDelegate( 243 &delegate, 10, gfx::Rect(0, 0, 100, 50), window0.get())); 244 scoped_ptr<aura::Window> window2( 245 aura::test::CreateTestWindowWithDelegate( 246 &delegate, 11, gfx::Rect(0, 50, 100, 50), window0.get())); 247 248 const int kTouchId = 5; 249 250 // Capture first window. 251 window1->SetCapture(); 252 EXPECT_TRUE(window1->HasCapture()); 253 254 // Send touch event to first window. 255 ui::TouchEvent press(ui::ET_TOUCH_PRESSED, 256 gfx::Point(10, 10), 257 kTouchId, 258 ui::EventTimeForNow()); 259 root_window->GetDispatcher()->AsRootWindowHostDelegate()->OnHostTouchEvent( 260 &press); 261 EXPECT_TRUE(window1->HasCapture()); 262 263 base::OneShotTimer<internal::LongPressAffordanceHandler>* timer = 264 GetLongPressAffordanceTimer(); 265 EXPECT_TRUE(timer->IsRunning()); 266 EXPECT_EQ(window1, GetLongPressAffordanceTarget()); 267 268 // Force timeout so that the affordance animation can start. 269 timer->user_task().Run(); 270 timer->Stop(); 271 EXPECT_TRUE(GetLongPressAffordance()->is_animating()); 272 273 // Change capture. 274 window2->SetCapture(); 275 EXPECT_TRUE(window2->HasCapture()); 276 277 EXPECT_TRUE(GetLongPressAffordance()->is_animating()); 278 EXPECT_EQ(window1, GetLongPressAffordanceTarget()); 279 280 // Animate to completion. 281 GetLongPressAffordance()->End(); // end grow animation. 282 // Force timeout to start shrink animation. 283 EXPECT_TRUE(timer->IsRunning()); 284 timer->user_task().Run(); 285 timer->Stop(); 286 EXPECT_TRUE(GetLongPressAffordance()->is_animating()); 287 GetLongPressAffordance()->End(); // end shrink animation. 288 289 // Check if state has reset. 290 EXPECT_EQ(NULL, GetLongPressAffordanceTarget()); 291 EXPECT_EQ(NULL, GetLongPressAffordanceView()); 292} 293 294TEST_P(SystemGestureEventFilterTest, MultiFingerSwipeGestures) { 295 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 296 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 297 new ResizableWidgetDelegate, root_window, gfx::Rect(0, 0, 600, 600)); 298 toplevel->Show(); 299 300 const int kSteps = 15; 301 const int kTouchPoints = 4; 302 gfx::Point points[kTouchPoints] = { 303 gfx::Point(250, 250), 304 gfx::Point(250, 350), 305 gfx::Point(350, 250), 306 gfx::Point(350, 350) 307 }; 308 309 aura::test::EventGenerator generator(root_window, 310 toplevel->GetNativeWindow()); 311 312 // Swipe down to minimize. 313 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150); 314 315 wm::WindowState* toplevel_state = 316 wm::GetWindowState(toplevel->GetNativeWindow()); 317 EXPECT_TRUE(toplevel_state->IsMinimized()); 318 319 toplevel->Restore(); 320 321 // Swipe up to maximize. 322 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, -150); 323 EXPECT_TRUE(toplevel_state->IsMaximized()); 324 325 toplevel->Restore(); 326 327 // Swipe right to snap. 328 gfx::Rect normal_bounds = toplevel->GetWindowBoundsInScreen(); 329 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0); 330 gfx::Rect right_tile_bounds = toplevel->GetWindowBoundsInScreen(); 331 EXPECT_NE(normal_bounds.ToString(), right_tile_bounds.ToString()); 332 333 // Swipe left to snap. 334 gfx::Point left_points[kTouchPoints]; 335 for (int i = 0; i < kTouchPoints; ++i) { 336 left_points[i] = points[i]; 337 left_points[i].Offset(right_tile_bounds.x(), right_tile_bounds.y()); 338 } 339 generator.GestureMultiFingerScroll(kTouchPoints, left_points, 15, kSteps, 340 -150, 0); 341 gfx::Rect left_tile_bounds = toplevel->GetWindowBoundsInScreen(); 342 EXPECT_NE(normal_bounds.ToString(), left_tile_bounds.ToString()); 343 EXPECT_NE(right_tile_bounds.ToString(), left_tile_bounds.ToString()); 344 345 // Swipe right again. 346 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0); 347 gfx::Rect current_bounds = toplevel->GetWindowBoundsInScreen(); 348 EXPECT_NE(current_bounds.ToString(), left_tile_bounds.ToString()); 349 EXPECT_EQ(current_bounds.ToString(), right_tile_bounds.ToString()); 350} 351 352TEST_P(SystemGestureEventFilterTest, TwoFingerDrag) { 353 gfx::Rect bounds(0, 0, 600, 600); 354 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 355 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 356 new ResizableWidgetDelegate, root_window, bounds); 357 toplevel->Show(); 358 359 const int kSteps = 15; 360 const int kTouchPoints = 2; 361 gfx::Point points[kTouchPoints] = { 362 gfx::Point(250, 250), 363 gfx::Point(350, 350), 364 }; 365 366 aura::test::EventGenerator generator(root_window, 367 toplevel->GetNativeWindow()); 368 369 wm::WindowState* toplevel_state = 370 wm::GetWindowState(toplevel->GetNativeWindow()); 371 // Swipe down to minimize. 372 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150); 373 EXPECT_TRUE(toplevel_state->IsMinimized()); 374 375 toplevel->Restore(); 376 toplevel->GetNativeWindow()->SetBounds(bounds); 377 378 // Swipe up to maximize. 379 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, -150); 380 EXPECT_TRUE(toplevel_state->IsMaximized()); 381 382 toplevel->Restore(); 383 toplevel->GetNativeWindow()->SetBounds(bounds); 384 385 // Swipe right to snap. 386 gfx::Rect normal_bounds = toplevel->GetWindowBoundsInScreen(); 387 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0); 388 gfx::Rect right_tile_bounds = toplevel->GetWindowBoundsInScreen(); 389 EXPECT_NE(normal_bounds.ToString(), right_tile_bounds.ToString()); 390 391 // Swipe left to snap. 392 gfx::Point left_points[kTouchPoints]; 393 for (int i = 0; i < kTouchPoints; ++i) { 394 left_points[i] = points[i]; 395 left_points[i].Offset(right_tile_bounds.x(), right_tile_bounds.y()); 396 } 397 generator.GestureMultiFingerScroll(kTouchPoints, left_points, 15, kSteps, 398 -150, 0); 399 gfx::Rect left_tile_bounds = toplevel->GetWindowBoundsInScreen(); 400 EXPECT_NE(normal_bounds.ToString(), left_tile_bounds.ToString()); 401 EXPECT_NE(right_tile_bounds.ToString(), left_tile_bounds.ToString()); 402 403 // Swipe right again. 404 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0); 405 gfx::Rect current_bounds = toplevel->GetWindowBoundsInScreen(); 406 EXPECT_NE(current_bounds.ToString(), left_tile_bounds.ToString()); 407 EXPECT_EQ(current_bounds.ToString(), right_tile_bounds.ToString()); 408} 409 410TEST_P(SystemGestureEventFilterTest, TwoFingerDragTwoWindows) { 411 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 412 ui::GestureConfiguration::set_max_separation_for_gesture_touches_in_pixels(0); 413 views::Widget* first = views::Widget::CreateWindowWithContextAndBounds( 414 new ResizableWidgetDelegate, root_window, gfx::Rect(0, 0, 50, 100)); 415 first->Show(); 416 views::Widget* second = views::Widget::CreateWindowWithContextAndBounds( 417 new ResizableWidgetDelegate, root_window, gfx::Rect(100, 0, 100, 100)); 418 second->Show(); 419 420 // Start a two-finger drag on |first|, and then try to use another two-finger 421 // drag to move |second|. The attempt to move |second| should fail. 422 const gfx::Rect& first_bounds = first->GetWindowBoundsInScreen(); 423 const gfx::Rect& second_bounds = second->GetWindowBoundsInScreen(); 424 const int kSteps = 15; 425 const int kTouchPoints = 4; 426 gfx::Point points[kTouchPoints] = { 427 first_bounds.origin() + gfx::Vector2d(5, 5), 428 first_bounds.origin() + gfx::Vector2d(30, 10), 429 second_bounds.origin() + gfx::Vector2d(5, 5), 430 second_bounds.origin() + gfx::Vector2d(40, 20) 431 }; 432 433 aura::test::EventGenerator generator(root_window); 434 // Do not drag too fast to avoid fling. 435 generator.GestureMultiFingerScroll(kTouchPoints, points, 436 50, kSteps, 0, 150); 437 438 EXPECT_NE(first_bounds.ToString(), 439 first->GetWindowBoundsInScreen().ToString()); 440 EXPECT_EQ(second_bounds.ToString(), 441 second->GetWindowBoundsInScreen().ToString()); 442} 443 444TEST_P(SystemGestureEventFilterTest, WindowsWithMaxSizeDontSnap) { 445 gfx::Rect bounds(250, 150, 100, 100); 446 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 447 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 448 new MaxSizeWidgetDelegate, root_window, bounds); 449 toplevel->Show(); 450 451 const int kSteps = 15; 452 const int kTouchPoints = 2; 453 gfx::Point points[kTouchPoints] = { 454 gfx::Point(bounds.x() + 10, bounds.y() + 30), 455 gfx::Point(bounds.x() + 30, bounds.y() + 20), 456 }; 457 458 aura::test::EventGenerator generator(root_window, 459 toplevel->GetNativeWindow()); 460 461 // Swipe down to minimize. 462 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150); 463 wm::WindowState* toplevel_state = 464 wm::GetWindowState(toplevel->GetNativeWindow()); 465 EXPECT_TRUE(toplevel_state->IsMinimized()); 466 467 toplevel->Restore(); 468 toplevel->GetNativeWindow()->SetBounds(bounds); 469 470 // Check that swiping up doesn't maximize. 471 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, -150); 472 EXPECT_FALSE(toplevel_state->IsMaximized()); 473 474 toplevel->Restore(); 475 toplevel->GetNativeWindow()->SetBounds(bounds); 476 477 // Check that swiping right doesn't snap. 478 gfx::Rect normal_bounds = toplevel->GetWindowBoundsInScreen(); 479 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0); 480 normal_bounds.set_x(normal_bounds.x() + 150); 481 EXPECT_EQ(normal_bounds.ToString(), 482 toplevel->GetWindowBoundsInScreen().ToString()); 483 484 toplevel->GetNativeWindow()->SetBounds(bounds); 485 486 // Check that swiping left doesn't snap. 487 normal_bounds = toplevel->GetWindowBoundsInScreen(); 488 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, -150, 0); 489 normal_bounds.set_x(normal_bounds.x() - 150); 490 EXPECT_EQ(normal_bounds.ToString(), 491 toplevel->GetWindowBoundsInScreen().ToString()); 492 493 toplevel->GetNativeWindow()->SetBounds(bounds); 494 495 // Swipe right again, make sure the window still doesn't snap. 496 normal_bounds = toplevel->GetWindowBoundsInScreen(); 497 normal_bounds.set_x(normal_bounds.x() + 150); 498 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0); 499 EXPECT_EQ(normal_bounds.ToString(), 500 toplevel->GetWindowBoundsInScreen().ToString()); 501} 502 503TEST_P(SystemGestureEventFilterTest, TwoFingerDragEdge) { 504 gfx::Rect bounds(0, 0, 100, 100); 505 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 506 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 507 new ResizableWidgetDelegate, root_window, bounds); 508 toplevel->Show(); 509 510 const int kSteps = 15; 511 const int kTouchPoints = 2; 512 gfx::Point points[kTouchPoints] = { 513 gfx::Point(30, 20), // Caption 514 gfx::Point(0, 40), // Left edge 515 }; 516 517 EXPECT_EQ(HTLEFT, toplevel->GetNativeWindow()->delegate()-> 518 GetNonClientComponent(points[1])); 519 520 aura::test::EventGenerator generator(root_window, 521 toplevel->GetNativeWindow()); 522 523 bounds = toplevel->GetNativeWindow()->bounds(); 524 // Swipe down. Nothing should happen. 525 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150); 526 EXPECT_EQ(bounds.ToString(), 527 toplevel->GetNativeWindow()->bounds().ToString()); 528} 529 530TEST_P(SystemGestureEventFilterTest, TwoFingerDragDelayed) { 531 gfx::Rect bounds(0, 0, 100, 100); 532 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 533 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 534 new ResizableWidgetDelegate, root_window, bounds); 535 toplevel->Show(); 536 537 const int kSteps = 15; 538 const int kTouchPoints = 2; 539 gfx::Point points[kTouchPoints] = { 540 gfx::Point(30, 20), // Caption 541 gfx::Point(34, 20), // Caption 542 }; 543 int delays[kTouchPoints] = {0, 120}; 544 545 EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()-> 546 GetNonClientComponent(points[0])); 547 EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()-> 548 GetNonClientComponent(points[1])); 549 550 aura::test::EventGenerator generator(root_window, 551 toplevel->GetNativeWindow()); 552 553 bounds = toplevel->GetNativeWindow()->bounds(); 554 // Swipe right and down starting with one finger. 555 // Add another finger after 120ms and continue dragging. 556 // The window should move and the drag should be determined by the center 557 // point between the fingers. 558 generator.GestureMultiFingerScrollWithDelays( 559 kTouchPoints, points, delays, 15, kSteps, 150, 150); 560 bounds += gfx::Vector2d(150 + (points[1].x() - points[0].x()) / 2, 150); 561 EXPECT_EQ(bounds.ToString(), 562 toplevel->GetNativeWindow()->bounds().ToString()); 563} 564 565TEST_P(SystemGestureEventFilterTest, ThreeFingerGestureStopsDrag) { 566 gfx::Rect bounds(0, 0, 100, 100); 567 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 568 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 569 new ResizableWidgetDelegate, root_window, bounds); 570 toplevel->Show(); 571 572 const int kSteps = 10; 573 const int kTouchPoints = 3; 574 gfx::Point points[kTouchPoints] = { 575 gfx::Point(30, 20), // Caption 576 gfx::Point(34, 20), // Caption 577 gfx::Point(38, 20), // Caption 578 }; 579 int delays[kTouchPoints] = {0, 0, 120}; 580 581 EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()-> 582 GetNonClientComponent(points[0])); 583 EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()-> 584 GetNonClientComponent(points[1])); 585 586 aura::test::EventGenerator generator(root_window, 587 toplevel->GetNativeWindow()); 588 589 bounds = toplevel->GetNativeWindow()->bounds(); 590 // Swipe right and down starting with two fingers. 591 // Add third finger after 120ms and continue dragging. 592 // The window should start moving but stop when the 3rd finger touches down. 593 const int kEventSeparation = 15; 594 generator.GestureMultiFingerScrollWithDelays( 595 kTouchPoints, points, delays, kEventSeparation, kSteps, 150, 150); 596 int expected_drag = 150 / kSteps * 120 / kEventSeparation; 597 bounds += gfx::Vector2d(expected_drag, expected_drag); 598 EXPECT_EQ(bounds.ToString(), 599 toplevel->GetNativeWindow()->bounds().ToString()); 600} 601 602TEST_P(SystemGestureEventFilterTest, DragLeftNearEdgeSnaps) { 603 gfx::Rect bounds(200, 150, 400, 100); 604 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 605 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 606 new ResizableWidgetDelegate, root_window, bounds); 607 toplevel->Show(); 608 609 const int kSteps = 15; 610 const int kTouchPoints = 2; 611 gfx::Point points[kTouchPoints] = { 612 gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5), 613 gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5), 614 }; 615 aura::test::EventGenerator generator(root_window, 616 toplevel->GetNativeWindow()); 617 618 // Check that dragging left snaps before reaching the screen edge. 619 gfx::Rect work_area = 620 Shell::GetScreen()->GetDisplayNearestWindow(root_window).work_area(); 621 int drag_x = work_area.x() + 20 - points[0].x(); 622 generator.GestureMultiFingerScroll( 623 kTouchPoints, points, 120, kSteps, drag_x, 0); 624 625 internal::SnapSizer snap_sizer( 626 wm::GetWindowState(toplevel->GetNativeWindow()), 627 gfx::Point(), 628 internal::SnapSizer::LEFT_EDGE, 629 internal::SnapSizer::OTHER_INPUT); 630 gfx::Rect expected_bounds(snap_sizer.target_bounds()); 631 EXPECT_EQ(expected_bounds.ToString(), 632 toplevel->GetWindowBoundsInScreen().ToString()); 633} 634 635TEST_P(SystemGestureEventFilterTest, DragRightNearEdgeSnaps) { 636 gfx::Rect bounds(200, 150, 400, 100); 637 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 638 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 639 new ResizableWidgetDelegate, root_window, bounds); 640 toplevel->Show(); 641 642 const int kSteps = 15; 643 const int kTouchPoints = 2; 644 gfx::Point points[kTouchPoints] = { 645 gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5), 646 gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5), 647 }; 648 aura::test::EventGenerator generator(root_window, 649 toplevel->GetNativeWindow()); 650 651 // Check that dragging right snaps before reaching the screen edge. 652 gfx::Rect work_area = 653 Shell::GetScreen()->GetDisplayNearestWindow(root_window).work_area(); 654 int drag_x = work_area.right() - 20 - points[0].x(); 655 generator.GestureMultiFingerScroll( 656 kTouchPoints, points, 120, kSteps, drag_x, 0); 657 internal::SnapSizer snap_sizer( 658 wm::GetWindowState(toplevel->GetNativeWindow()), 659 gfx::Point(), 660 internal::SnapSizer::RIGHT_EDGE, 661 internal::SnapSizer::OTHER_INPUT); 662 gfx::Rect expected_bounds(snap_sizer.target_bounds()); 663 EXPECT_EQ(expected_bounds.ToString(), 664 toplevel->GetWindowBoundsInScreen().ToString()); 665} 666 667// Tests run twice - with docked windows disabled or enabled. 668INSTANTIATE_TEST_CASE_P(DockedWindowsDisabledOrEnabled, 669 SystemGestureEventFilterTest, 670 testing::Bool()); 671 672} // namespace test 673} // namespace ash 674