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 <vector> 8 9 #include "ash/accelerators/accelerator_controller.h" 10 #include "ash/display/display_manager.h" 11 #include "ash/shelf/shelf.h" 12 #include "ash/shelf/shelf_model.h" 13 #include "ash/shell.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_shelf_delegate.h" 19 #include "ash/wm/gestures/long_press_affordance_handler.h" 20 #include "ash/wm/window_state.h" 21 #include "ash/wm/window_util.h" 22 #include "base/time/time.h" 23 #include "base/timer/timer.h" 24 #include "ui/aura/env.h" 25 #include "ui/aura/test/test_window_delegate.h" 26 #include "ui/aura/test/test_windows.h" 27 #include "ui/aura/window_event_dispatcher.h" 28 #include "ui/base/hit_test.h" 29 #include "ui/events/event.h" 30 #include "ui/events/event_handler.h" 31 #include "ui/events/event_utils.h" 32 #include "ui/events/gestures/gesture_configuration.h" 33 #include "ui/events/test/event_generator.h" 34 #include "ui/events/test/test_event_handler.h" 35 #include "ui/gfx/screen.h" 36 #include "ui/gfx/size.h" 37 #include "ui/views/widget/widget.h" 38 #include "ui/views/widget/widget_delegate.h" 39 #include "ui/views/window/non_client_view.h" 40 #include "ui/views/window/window_button_order_provider.h" 41 42 namespace ash { 43 namespace test { 44 45 namespace { 46 47 class ResizableWidgetDelegate : public views::WidgetDelegateView { 48 public: 49 ResizableWidgetDelegate() {} 50 virtual ~ResizableWidgetDelegate() {} 51 52 private: 53 virtual bool CanResize() const OVERRIDE { return true; } 54 virtual bool CanMaximize() const OVERRIDE { return true; } 55 virtual bool CanMinimize() const OVERRIDE { return true; } 56 virtual void DeleteDelegate() OVERRIDE { delete this; } 57 58 DISALLOW_COPY_AND_ASSIGN(ResizableWidgetDelegate); 59 }; 60 61 // Support class for testing windows with a maximum size. 62 class MaxSizeNCFV : public views::NonClientFrameView { 63 public: 64 MaxSizeNCFV() {} 65 private: 66 virtual gfx::Size GetMaximumSize() const OVERRIDE { 67 return gfx::Size(200, 200); 68 } 69 virtual gfx::Rect GetBoundsForClientView() const OVERRIDE { 70 return gfx::Rect(); 71 }; 72 73 virtual gfx::Rect GetWindowBoundsForClientBounds( 74 const gfx::Rect& client_bounds) const OVERRIDE { 75 return gfx::Rect(); 76 }; 77 78 // This function must ask the ClientView to do a hittest. We don't do this in 79 // the parent NonClientView because that makes it more difficult to calculate 80 // hittests for regions that are partially obscured by the ClientView, e.g. 81 // HTSYSMENU. 82 virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE { 83 return HTNOWHERE; 84 } 85 virtual void GetWindowMask(const gfx::Size& size, 86 gfx::Path* window_mask) OVERRIDE {} 87 virtual void ResetWindowControls() OVERRIDE {} 88 virtual void UpdateWindowIcon() OVERRIDE {} 89 virtual void UpdateWindowTitle() OVERRIDE {} 90 virtual void SizeConstraintsChanged() OVERRIDE {} 91 92 DISALLOW_COPY_AND_ASSIGN(MaxSizeNCFV); 93 }; 94 95 class MaxSizeWidgetDelegate : public views::WidgetDelegateView { 96 public: 97 MaxSizeWidgetDelegate() {} 98 virtual ~MaxSizeWidgetDelegate() {} 99 100 private: 101 virtual bool CanResize() const OVERRIDE { return true; } 102 virtual bool CanMaximize() const OVERRIDE { return false; } 103 virtual void DeleteDelegate() OVERRIDE { delete this; } 104 virtual views::NonClientFrameView* CreateNonClientFrameView( 105 views::Widget* widget) OVERRIDE { 106 return new MaxSizeNCFV; 107 } 108 109 DISALLOW_COPY_AND_ASSIGN(MaxSizeWidgetDelegate); 110 }; 111 112 } // namespace 113 114 class SystemGestureEventFilterTest : public AshTestBase { 115 public: 116 SystemGestureEventFilterTest() : AshTestBase() {} 117 virtual ~SystemGestureEventFilterTest() {} 118 119 LongPressAffordanceHandler* GetLongPressAffordance() { 120 ShellTestApi shell_test(Shell::GetInstance()); 121 return shell_test.system_gesture_event_filter()-> 122 long_press_affordance_.get(); 123 } 124 125 base::OneShotTimer<LongPressAffordanceHandler>* 126 GetLongPressAffordanceTimer() { 127 return &GetLongPressAffordance()->timer_; 128 } 129 130 aura::Window* GetLongPressAffordanceTarget() { 131 return GetLongPressAffordance()->tap_down_target_; 132 } 133 134 views::View* GetLongPressAffordanceView() { 135 return reinterpret_cast<views::View*>( 136 GetLongPressAffordance()->view_.get()); 137 } 138 139 // Overridden from AshTestBase: 140 virtual void SetUp() OVERRIDE { 141 // TODO(jonross): TwoFingerDragDelayed() and ThreeFingerGestureStopsDrag() 142 // both use hardcoded touch points, assuming that they target empty header 143 // space. Window control order now reflects configuration files and can 144 // change. The tests should be improved to dynamically decide touch points. 145 // To address this we specify the originally expected window control 146 // positions to be consistent across tests. 147 std::vector<views::FrameButton> leading; 148 std::vector<views::FrameButton> trailing; 149 trailing.push_back(views::FRAME_BUTTON_MINIMIZE); 150 trailing.push_back(views::FRAME_BUTTON_MAXIMIZE); 151 trailing.push_back(views::FRAME_BUTTON_CLOSE); 152 views::WindowButtonOrderProvider::GetInstance()-> 153 SetWindowButtonOrder(leading, trailing); 154 155 test::AshTestBase::SetUp(); 156 // Enable brightness key. 157 test::DisplayManagerTestApi(Shell::GetInstance()->display_manager()). 158 SetFirstDisplayAsInternalDisplay(); 159 } 160 161 private: 162 DISALLOW_COPY_AND_ASSIGN(SystemGestureEventFilterTest); 163 }; 164 165 ui::GestureEvent* CreateGesture(ui::EventType type, 166 int x, 167 int y, 168 float delta_x, 169 float delta_y, 170 int touch_id) { 171 ui::GestureEventDetails details = 172 ui::GestureEventDetails(type, delta_x, delta_y); 173 details.set_oldest_touch_id(touch_id); 174 return new ui::GestureEvent(x, y, 0, 175 base::TimeDelta::FromMilliseconds(base::Time::Now().ToDoubleT() * 1000), 176 ui::GestureEventDetails(type, delta_x, delta_y)); 177 } 178 179 TEST_F(SystemGestureEventFilterTest, LongPressAffordanceStateOnCaptureLoss) { 180 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 181 182 aura::test::TestWindowDelegate delegate; 183 scoped_ptr<aura::Window> window0( 184 aura::test::CreateTestWindowWithDelegate( 185 &delegate, 9, gfx::Rect(0, 0, 100, 100), root_window)); 186 scoped_ptr<aura::Window> window1( 187 aura::test::CreateTestWindowWithDelegate( 188 &delegate, 10, gfx::Rect(0, 0, 100, 50), window0.get())); 189 scoped_ptr<aura::Window> window2( 190 aura::test::CreateTestWindowWithDelegate( 191 &delegate, 11, gfx::Rect(0, 50, 100, 50), window0.get())); 192 193 const int kTouchId = 5; 194 195 // Capture first window. 196 window1->SetCapture(); 197 EXPECT_TRUE(window1->HasCapture()); 198 199 // Send touch event to first window. 200 ui::TouchEvent press(ui::ET_TOUCH_PRESSED, 201 gfx::Point(10, 10), 202 kTouchId, 203 ui::EventTimeForNow()); 204 ui::EventDispatchDetails details = 205 root_window->GetHost()->dispatcher()->OnEventFromSource(&press); 206 ASSERT_FALSE(details.dispatcher_destroyed); 207 EXPECT_TRUE(window1->HasCapture()); 208 209 base::OneShotTimer<LongPressAffordanceHandler>* timer = 210 GetLongPressAffordanceTimer(); 211 EXPECT_TRUE(timer->IsRunning()); 212 EXPECT_EQ(window1, GetLongPressAffordanceTarget()); 213 214 // Force timeout so that the affordance animation can start. 215 timer->user_task().Run(); 216 timer->Stop(); 217 EXPECT_TRUE(GetLongPressAffordance()->is_animating()); 218 219 // Change capture. 220 window2->SetCapture(); 221 EXPECT_TRUE(window2->HasCapture()); 222 223 EXPECT_TRUE(GetLongPressAffordance()->is_animating()); 224 EXPECT_EQ(window1, GetLongPressAffordanceTarget()); 225 226 // Animate to completion. 227 GetLongPressAffordance()->End(); // end grow animation. 228 // Force timeout to start shrink animation. 229 EXPECT_TRUE(timer->IsRunning()); 230 timer->user_task().Run(); 231 timer->Stop(); 232 EXPECT_TRUE(GetLongPressAffordance()->is_animating()); 233 GetLongPressAffordance()->End(); // end shrink animation. 234 235 // Check if state has reset. 236 EXPECT_EQ(NULL, GetLongPressAffordanceTarget()); 237 EXPECT_EQ(NULL, GetLongPressAffordanceView()); 238 } 239 240 TEST_F(SystemGestureEventFilterTest, TwoFingerDrag) { 241 gfx::Rect bounds(0, 0, 600, 600); 242 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 243 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 244 new ResizableWidgetDelegate, root_window, bounds); 245 toplevel->Show(); 246 247 const int kSteps = 15; 248 const int kTouchPoints = 2; 249 gfx::Point points[kTouchPoints] = { 250 gfx::Point(250, 250), 251 gfx::Point(350, 350), 252 }; 253 254 ui::test::EventGenerator generator(root_window, toplevel->GetNativeWindow()); 255 256 wm::WindowState* toplevel_state = 257 wm::GetWindowState(toplevel->GetNativeWindow()); 258 // Swipe down to minimize. 259 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150); 260 EXPECT_TRUE(toplevel_state->IsMinimized()); 261 262 toplevel->Restore(); 263 toplevel->GetNativeWindow()->SetBounds(bounds); 264 265 // Swipe up to maximize. 266 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, -150); 267 EXPECT_TRUE(toplevel_state->IsMaximized()); 268 269 toplevel->Restore(); 270 toplevel->GetNativeWindow()->SetBounds(bounds); 271 272 // Swipe right to snap. 273 gfx::Rect normal_bounds = toplevel->GetWindowBoundsInScreen(); 274 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0); 275 gfx::Rect right_tile_bounds = toplevel->GetWindowBoundsInScreen(); 276 EXPECT_NE(normal_bounds.ToString(), right_tile_bounds.ToString()); 277 278 // Swipe left to snap. 279 gfx::Point left_points[kTouchPoints]; 280 for (int i = 0; i < kTouchPoints; ++i) { 281 left_points[i] = points[i]; 282 left_points[i].Offset(right_tile_bounds.x(), right_tile_bounds.y()); 283 } 284 generator.GestureMultiFingerScroll(kTouchPoints, left_points, 15, kSteps, 285 -150, 0); 286 gfx::Rect left_tile_bounds = toplevel->GetWindowBoundsInScreen(); 287 EXPECT_NE(normal_bounds.ToString(), left_tile_bounds.ToString()); 288 EXPECT_NE(right_tile_bounds.ToString(), left_tile_bounds.ToString()); 289 290 // Swipe right again. 291 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0); 292 gfx::Rect current_bounds = toplevel->GetWindowBoundsInScreen(); 293 EXPECT_NE(current_bounds.ToString(), left_tile_bounds.ToString()); 294 EXPECT_EQ(current_bounds.ToString(), right_tile_bounds.ToString()); 295 } 296 297 TEST_F(SystemGestureEventFilterTest, TwoFingerDragTwoWindows) { 298 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 299 ui::GestureConfiguration::set_max_separation_for_gesture_touches_in_pixels(0); 300 views::Widget* first = views::Widget::CreateWindowWithContextAndBounds( 301 new ResizableWidgetDelegate, root_window, gfx::Rect(10, 0, 50, 100)); 302 first->Show(); 303 views::Widget* second = views::Widget::CreateWindowWithContextAndBounds( 304 new ResizableWidgetDelegate, root_window, gfx::Rect(100, 0, 100, 100)); 305 second->Show(); 306 307 // Start a two-finger drag on |first|, and then try to use another two-finger 308 // drag to move |second|. The attempt to move |second| should fail. 309 const gfx::Rect& first_bounds = first->GetWindowBoundsInScreen(); 310 const gfx::Rect& second_bounds = second->GetWindowBoundsInScreen(); 311 const int kSteps = 15; 312 const int kTouchPoints = 4; 313 gfx::Point points[kTouchPoints] = { 314 first_bounds.origin() + gfx::Vector2d(5, 5), 315 first_bounds.origin() + gfx::Vector2d(30, 10), 316 second_bounds.origin() + gfx::Vector2d(5, 5), 317 second_bounds.origin() + gfx::Vector2d(40, 20) 318 }; 319 320 ui::test::EventGenerator generator(root_window); 321 // Do not drag too fast to avoid fling. 322 generator.GestureMultiFingerScroll(kTouchPoints, points, 323 50, kSteps, 0, 150); 324 325 EXPECT_NE(first_bounds.ToString(), 326 first->GetWindowBoundsInScreen().ToString()); 327 EXPECT_EQ(second_bounds.ToString(), 328 second->GetWindowBoundsInScreen().ToString()); 329 } 330 331 TEST_F(SystemGestureEventFilterTest, WindowsWithMaxSizeDontSnap) { 332 gfx::Rect bounds(250, 150, 100, 100); 333 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 334 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 335 new MaxSizeWidgetDelegate, root_window, bounds); 336 toplevel->Show(); 337 338 const int kSteps = 15; 339 const int kTouchPoints = 2; 340 gfx::Point points[kTouchPoints] = { 341 gfx::Point(bounds.x() + 10, bounds.y() + 30), 342 gfx::Point(bounds.x() + 30, bounds.y() + 20), 343 }; 344 345 ui::test::EventGenerator generator(root_window, toplevel->GetNativeWindow()); 346 347 // Swipe down to minimize. 348 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150); 349 wm::WindowState* toplevel_state = 350 wm::GetWindowState(toplevel->GetNativeWindow()); 351 EXPECT_TRUE(toplevel_state->IsMinimized()); 352 353 toplevel->Restore(); 354 toplevel->GetNativeWindow()->SetBounds(bounds); 355 356 // Check that swiping up doesn't maximize. 357 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, -150); 358 EXPECT_FALSE(toplevel_state->IsMaximized()); 359 360 toplevel->Restore(); 361 toplevel->GetNativeWindow()->SetBounds(bounds); 362 363 // Check that swiping right doesn't snap. 364 gfx::Rect normal_bounds = toplevel->GetWindowBoundsInScreen(); 365 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0); 366 normal_bounds.set_x(normal_bounds.x() + 150); 367 EXPECT_EQ(normal_bounds.ToString(), 368 toplevel->GetWindowBoundsInScreen().ToString()); 369 370 toplevel->GetNativeWindow()->SetBounds(bounds); 371 372 // Check that swiping left doesn't snap. 373 normal_bounds = toplevel->GetWindowBoundsInScreen(); 374 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, -150, 0); 375 normal_bounds.set_x(normal_bounds.x() - 150); 376 EXPECT_EQ(normal_bounds.ToString(), 377 toplevel->GetWindowBoundsInScreen().ToString()); 378 379 toplevel->GetNativeWindow()->SetBounds(bounds); 380 381 // Swipe right again, make sure the window still doesn't snap. 382 normal_bounds = toplevel->GetWindowBoundsInScreen(); 383 normal_bounds.set_x(normal_bounds.x() + 150); 384 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 150, 0); 385 EXPECT_EQ(normal_bounds.ToString(), 386 toplevel->GetWindowBoundsInScreen().ToString()); 387 } 388 389 TEST_F(SystemGestureEventFilterTest, DISABLED_TwoFingerDragEdge) { 390 gfx::Rect bounds(0, 0, 200, 100); 391 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 392 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 393 new ResizableWidgetDelegate, root_window, bounds); 394 toplevel->Show(); 395 396 const int kSteps = 15; 397 const int kTouchPoints = 2; 398 gfx::Point points[kTouchPoints] = { 399 gfx::Point(30, 20), // Caption 400 gfx::Point(0, 40), // Left edge 401 }; 402 403 EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()-> 404 GetNonClientComponent(points[0])); 405 EXPECT_EQ(HTLEFT, toplevel->GetNativeWindow()->delegate()-> 406 GetNonClientComponent(points[1])); 407 408 ui::test::EventGenerator generator(root_window, toplevel->GetNativeWindow()); 409 410 bounds = toplevel->GetNativeWindow()->bounds(); 411 // Swipe down. Nothing should happen. 412 generator.GestureMultiFingerScroll(kTouchPoints, points, 15, kSteps, 0, 150); 413 EXPECT_EQ(bounds.ToString(), 414 toplevel->GetNativeWindow()->bounds().ToString()); 415 } 416 417 // We do not allow resizing a window via multiple edges simultaneously. Test 418 // that the behavior is reasonable if a user attempts to resize a window via 419 // several edges. 420 TEST_F(SystemGestureEventFilterTest, 421 TwoFingerAttemptResizeLeftAndRightEdgesSimultaneously) { 422 gfx::Rect initial_bounds(0, 0, 400, 400); 423 views::Widget* toplevel = 424 views::Widget::CreateWindowWithContextAndBounds( 425 new ResizableWidgetDelegate, CurrentContext(), initial_bounds); 426 toplevel->Show(); 427 428 const int kSteps = 15; 429 const int kTouchPoints = 2; 430 gfx::Point points[kTouchPoints] = { 431 gfx::Point(0, 40), // Left edge 432 gfx::Point(399, 40), // Right edge 433 }; 434 int delays[kTouchPoints] = {0, 120}; 435 436 EXPECT_EQ(HTLEFT, toplevel->GetNonClientComponent(points[0])); 437 EXPECT_EQ(HTRIGHT, toplevel->GetNonClientComponent(points[1])); 438 439 GetEventGenerator().GestureMultiFingerScrollWithDelays( 440 kTouchPoints, points, delays, 15, kSteps, 0, 40); 441 442 // The window bounds should not have changed because neither of the fingers 443 // moved horizontally. 444 EXPECT_EQ(initial_bounds.ToString(), 445 toplevel->GetNativeWindow()->bounds().ToString()); 446 } 447 448 TEST_F(SystemGestureEventFilterTest, TwoFingerDragDelayed) { 449 gfx::Rect bounds(0, 0, 200, 100); 450 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 451 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 452 new ResizableWidgetDelegate, root_window, bounds); 453 toplevel->Show(); 454 455 const int kSteps = 15; 456 const int kTouchPoints = 2; 457 gfx::Point points[kTouchPoints] = { 458 gfx::Point(30, 20), // Caption 459 gfx::Point(34, 20), // Caption 460 }; 461 int delays[kTouchPoints] = {0, 120}; 462 463 EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()-> 464 GetNonClientComponent(points[0])); 465 EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()-> 466 GetNonClientComponent(points[1])); 467 468 ui::test::EventGenerator generator(root_window, toplevel->GetNativeWindow()); 469 470 bounds = toplevel->GetNativeWindow()->bounds(); 471 // Swipe right and down starting with one finger. 472 // Add another finger after 120ms and continue dragging. 473 // The window should move and the drag should be determined by the center 474 // point between the fingers. 475 generator.GestureMultiFingerScrollWithDelays( 476 kTouchPoints, points, delays, 15, kSteps, 150, 150); 477 bounds += gfx::Vector2d(150 + (points[1].x() - points[0].x()) / 2, 150); 478 EXPECT_EQ(bounds.ToString(), 479 toplevel->GetNativeWindow()->bounds().ToString()); 480 } 481 482 TEST_F(SystemGestureEventFilterTest, ThreeFingerGestureStopsDrag) { 483 gfx::Rect bounds(0, 0, 200, 100); 484 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 485 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 486 new ResizableWidgetDelegate, root_window, bounds); 487 toplevel->Show(); 488 489 const int kSteps = 10; 490 const int kTouchPoints = 3; 491 gfx::Point points[kTouchPoints] = { 492 gfx::Point(30, 20), // Caption 493 gfx::Point(34, 20), // Caption 494 gfx::Point(38, 20), // Caption 495 }; 496 int delays[kTouchPoints] = {0, 0, 120}; 497 498 EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()-> 499 GetNonClientComponent(points[0])); 500 EXPECT_EQ(HTCAPTION, toplevel->GetNativeWindow()->delegate()-> 501 GetNonClientComponent(points[1])); 502 503 ui::test::EventGenerator generator(root_window, toplevel->GetNativeWindow()); 504 505 bounds = toplevel->GetNativeWindow()->bounds(); 506 // Swipe right and down starting with two fingers. 507 // Add third finger after 120ms and continue dragging. 508 // The window should start moving but stop when the 3rd finger touches down. 509 const int kEventSeparation = 15; 510 generator.GestureMultiFingerScrollWithDelays( 511 kTouchPoints, points, delays, kEventSeparation, kSteps, 150, 150); 512 int expected_drag = 150 / kSteps * 120 / kEventSeparation; 513 bounds += gfx::Vector2d(expected_drag, expected_drag); 514 EXPECT_EQ(bounds.ToString(), 515 toplevel->GetNativeWindow()->bounds().ToString()); 516 } 517 518 TEST_F(SystemGestureEventFilterTest, DragLeftNearEdgeSnaps) { 519 gfx::Rect bounds(200, 150, 400, 100); 520 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 521 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 522 new ResizableWidgetDelegate, root_window, bounds); 523 toplevel->Show(); 524 525 const int kSteps = 15; 526 const int kTouchPoints = 2; 527 gfx::Point points[kTouchPoints] = { 528 gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5), 529 gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5), 530 }; 531 aura::Window* toplevel_window = toplevel->GetNativeWindow(); 532 ui::test::EventGenerator generator(root_window, toplevel_window); 533 534 // Check that dragging left snaps before reaching the screen edge. 535 gfx::Rect work_area = 536 Shell::GetScreen()->GetDisplayNearestWindow(root_window).work_area(); 537 int drag_x = work_area.x() + 20 - points[0].x(); 538 generator.GestureMultiFingerScroll( 539 kTouchPoints, points, 120, kSteps, drag_x, 0); 540 541 EXPECT_EQ(wm::GetDefaultLeftSnappedWindowBoundsInParent( 542 toplevel_window).ToString(), 543 toplevel_window->bounds().ToString()); 544 } 545 546 TEST_F(SystemGestureEventFilterTest, DragRightNearEdgeSnaps) { 547 gfx::Rect bounds(200, 150, 400, 100); 548 aura::Window* root_window = Shell::GetPrimaryRootWindow(); 549 views::Widget* toplevel = views::Widget::CreateWindowWithContextAndBounds( 550 new ResizableWidgetDelegate, root_window, bounds); 551 toplevel->Show(); 552 553 const int kSteps = 15; 554 const int kTouchPoints = 2; 555 gfx::Point points[kTouchPoints] = { 556 gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5), 557 gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5), 558 }; 559 aura::Window* toplevel_window = toplevel->GetNativeWindow(); 560 ui::test::EventGenerator generator(root_window, toplevel_window); 561 562 // Check that dragging right snaps before reaching the screen edge. 563 gfx::Rect work_area = 564 Shell::GetScreen()->GetDisplayNearestWindow(root_window).work_area(); 565 int drag_x = work_area.right() - 20 - points[0].x(); 566 generator.GestureMultiFingerScroll( 567 kTouchPoints, points, 120, kSteps, drag_x, 0); 568 EXPECT_EQ(wm::GetDefaultRightSnappedWindowBoundsInParent( 569 toplevel_window).ToString(), 570 toplevel_window->bounds().ToString()); 571 } 572 573 // Tests that the window manager does not consume gesture events targeted to 574 // windows of type WINDOW_TYPE_CONTROL. This is important because the web 575 // contents are often (but not always) of type WINDOW_TYPE_CONTROL. 576 TEST_F(SystemGestureEventFilterTest, 577 ControlWindowGetsMultiFingerGestureEvents) { 578 scoped_ptr<aura::Window> parent( 579 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100))); 580 581 aura::test::EventCountDelegate delegate; 582 delegate.set_window_component(HTCLIENT); 583 scoped_ptr<aura::Window> child(new aura::Window(&delegate)); 584 child->SetType(ui::wm::WINDOW_TYPE_CONTROL); 585 child->Init(aura::WINDOW_LAYER_TEXTURED); 586 parent->AddChild(child.get()); 587 child->SetBounds(gfx::Rect(100, 100)); 588 child->Show(); 589 590 ui::test::TestEventHandler event_handler; 591 aura::Env::GetInstance()->PrependPreTargetHandler(&event_handler); 592 593 GetEventGenerator().MoveMouseTo(0, 0); 594 for (int i = 1; i <= 3; ++i) 595 GetEventGenerator().PressTouchId(i); 596 for (int i = 1; i <= 3; ++i) 597 GetEventGenerator().ReleaseTouchId(i); 598 EXPECT_EQ(event_handler.num_gesture_events(), 599 delegate.GetGestureCountAndReset()); 600 601 aura::Env::GetInstance()->RemovePreTargetHandler(&event_handler); 602 } 603 604 } // namespace test 605 } // namespace ash 606