1 // Copyright 2013 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/ash_constants.h" 6 #include "ash/frame/custom_frame_view_ash.h" 7 #include "ash/shell.h" 8 #include "ash/test/ash_test_base.h" 9 #include "ash/test/cursor_manager_test_api.h" 10 #include "ash/wm/resize_shadow.h" 11 #include "ash/wm/resize_shadow_controller.h" 12 #include "ash/wm/window_state.h" 13 #include "base/bind.h" 14 #include "ui/aura/window_event_dispatcher.h" 15 #include "ui/base/cursor/cursor.h" 16 #include "ui/base/hit_test.h" 17 #include "ui/events/test/event_generator.h" 18 #include "ui/views/widget/widget.h" 19 #include "ui/views/widget/widget_delegate.h" 20 21 namespace ash { 22 namespace test { 23 24 namespace { 25 26 // views::WidgetDelegate which uses ash::CustomFrameViewAsh. 27 class TestWidgetDelegate : public views::WidgetDelegateView { 28 public: 29 TestWidgetDelegate() {} 30 virtual ~TestWidgetDelegate() {} 31 32 // views::WidgetDelegateView overrides: 33 virtual bool CanResize() const OVERRIDE { 34 return true; 35 } 36 virtual bool CanMaximize() const OVERRIDE { 37 return true; 38 } 39 virtual bool CanMinimize() const OVERRIDE { 40 return true; 41 } 42 virtual views::NonClientFrameView* CreateNonClientFrameView( 43 views::Widget* widget) OVERRIDE { 44 return new ash::CustomFrameViewAsh(widget); 45 } 46 47 private: 48 DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate); 49 }; 50 51 } // namespace 52 53 // The test tests that the mouse cursor is changed and that the resize shadows 54 // are shown when the mouse is hovered over the window edge. 55 class ResizeShadowAndCursorTest : public AshTestBase { 56 public: 57 ResizeShadowAndCursorTest() {} 58 virtual ~ResizeShadowAndCursorTest() {} 59 60 // AshTestBase override: 61 virtual void SetUp() OVERRIDE { 62 AshTestBase::SetUp(); 63 64 views::Widget* widget(views::Widget::CreateWindowWithContextAndBounds( 65 new TestWidgetDelegate(), CurrentContext(), gfx::Rect(0, 0, 200, 100))); 66 widget->Show(); 67 window_ = widget->GetNativeView(); 68 69 // Add a child window to |window_| in order to properly test that the resize 70 // handles and the resize shadows are shown when the mouse is 71 // ash::kResizeInsideBoundsSize inside of |window_|'s edges. 72 aura::Window* child = CreateTestWindowInShell( 73 SK_ColorWHITE, 0, gfx::Rect(0, 10, 200, 90)); 74 window_->AddChild(child); 75 } 76 77 // Returns the hit test code if there is a resize shadow. Returns HTNOWHERE if 78 // there is no resize shadow. 79 int ResizeShadowHitTest() const { 80 ash::ResizeShadow* resize_shadow = ash::Shell::GetInstance() 81 ->resize_shadow_controller() 82 ->GetShadowForWindowForTest(window_); 83 return resize_shadow ? resize_shadow->GetLastHitTestForTest() : HTNOWHERE; 84 } 85 86 // Returns true if there is a resize shadow. 87 bool HasResizeShadow() const { 88 return ResizeShadowHitTest() != HTNOWHERE; 89 } 90 91 // Returns the current cursor type. 92 int GetCurrentCursorType() const { 93 CursorManagerTestApi test_api(ash::Shell::GetInstance()->cursor_manager()); 94 return test_api.GetCurrentCursor().native_type(); 95 } 96 97 // Called for each step of a scroll sequence initiated at the bottom right 98 // corner of |window_|. Tests whether the resize shadow is shown. 99 void ProcessBottomRightResizeGesture(ui::EventType type, 100 const gfx::Vector2dF& delta) { 101 if (type == ui::ET_GESTURE_SCROLL_END) { 102 // After gesture scroll ends, there should be no resize shadow. 103 EXPECT_FALSE(HasResizeShadow()); 104 } else { 105 EXPECT_EQ(HTBOTTOMRIGHT, ResizeShadowHitTest()); 106 } 107 } 108 109 aura::Window* window() { 110 return window_; 111 } 112 113 private: 114 aura::Window* window_; 115 116 DISALLOW_COPY_AND_ASSIGN(ResizeShadowAndCursorTest); 117 }; 118 119 // Test whether the resize shadows are visible and the cursor type based on the 120 // mouse's position. 121 TEST_F(ResizeShadowAndCursorTest, MouseHover) { 122 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 123 ASSERT_TRUE(ash::wm::GetWindowState(window())->IsNormalStateType()); 124 125 generator.MoveMouseTo(50, 50); 126 EXPECT_FALSE(HasResizeShadow()); 127 EXPECT_EQ(ui::kCursorNull, GetCurrentCursorType()); 128 129 generator.MoveMouseTo(gfx::Point(50, 0)); 130 EXPECT_EQ(HTTOP, ResizeShadowHitTest()); 131 EXPECT_EQ(ui::kCursorNorthResize, GetCurrentCursorType()); 132 133 generator.MoveMouseTo(50, 50); 134 EXPECT_FALSE(HasResizeShadow()); 135 EXPECT_EQ(ui::kCursorNull, GetCurrentCursorType()); 136 137 generator.MoveMouseTo(200, 100); 138 EXPECT_EQ(HTBOTTOMRIGHT, ResizeShadowHitTest()); 139 EXPECT_EQ(ui::kCursorSouthEastResize, GetCurrentCursorType()); 140 141 generator.MoveMouseTo(50, 100); 142 EXPECT_EQ(HTBOTTOM, ResizeShadowHitTest()); 143 EXPECT_EQ(ui::kCursorSouthResize, GetCurrentCursorType()); 144 145 generator.MoveMouseTo(50, 100 + ash::kResizeOutsideBoundsSize - 1); 146 EXPECT_EQ(HTBOTTOM, ResizeShadowHitTest()); 147 EXPECT_EQ(ui::kCursorSouthResize, GetCurrentCursorType()); 148 149 generator.MoveMouseTo(50, 100 + ash::kResizeOutsideBoundsSize + 10); 150 EXPECT_FALSE(HasResizeShadow()); 151 EXPECT_EQ(ui::kCursorNull, GetCurrentCursorType()); 152 153 generator.MoveMouseTo(50, 100 - ash::kResizeInsideBoundsSize); 154 EXPECT_EQ(HTBOTTOM, ResizeShadowHitTest()); 155 EXPECT_EQ(ui::kCursorSouthResize, GetCurrentCursorType()); 156 157 generator.MoveMouseTo(50, 100 - ash::kResizeInsideBoundsSize - 10); 158 EXPECT_FALSE(HasResizeShadow()); 159 EXPECT_EQ(ui::kCursorNull, GetCurrentCursorType()); 160 } 161 162 // Test that the resize shadows stay visible and that the cursor stays the same 163 // as long as a user is resizing a window. 164 TEST_F(ResizeShadowAndCursorTest, MouseDrag) { 165 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 166 ASSERT_TRUE(ash::wm::GetWindowState(window())->IsNormalStateType()); 167 gfx::Size initial_size(window()->bounds().size()); 168 169 generator.MoveMouseTo(200, 50); 170 generator.PressLeftButton(); 171 EXPECT_EQ(HTRIGHT, ResizeShadowHitTest()); 172 EXPECT_EQ(ui::kCursorEastResize, GetCurrentCursorType()); 173 174 generator.MoveMouseTo(210, 50); 175 EXPECT_EQ(HTRIGHT, ResizeShadowHitTest()); 176 EXPECT_EQ(ui::kCursorEastResize, GetCurrentCursorType()); 177 178 generator.ReleaseLeftButton(); 179 EXPECT_EQ(HTRIGHT, ResizeShadowHitTest()); 180 EXPECT_EQ(ui::kCursorEastResize, GetCurrentCursorType()); 181 182 gfx::Size new_size(window()->bounds().size()); 183 EXPECT_NE(new_size.ToString(), initial_size.ToString()); 184 } 185 186 // Test that the resize shadows stay visible while resizing a window via touch. 187 TEST_F(ResizeShadowAndCursorTest, Touch) { 188 ASSERT_TRUE(ash::wm::GetWindowState(window())->IsNormalStateType()); 189 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 190 191 int start_x = 200 + ash::kResizeOutsideBoundsSize - 1; 192 int start_y = 100 + ash::kResizeOutsideBoundsSize - 1; 193 generator.GestureScrollSequenceWithCallback( 194 gfx::Point(start_x, start_y), 195 gfx::Point(start_x + 50, start_y + 50), 196 base::TimeDelta::FromMilliseconds(200), 197 3, 198 base::Bind(&ResizeShadowAndCursorTest::ProcessBottomRightResizeGesture, 199 base::Unretained(this))); 200 } 201 202 // Test that the resize shadows are not visible and that the default cursor is 203 // used when the window is maximized. 204 TEST_F(ResizeShadowAndCursorTest, MaximizeRestore) { 205 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 206 ASSERT_TRUE(ash::wm::GetWindowState(window())->IsNormalStateType()); 207 208 generator.MoveMouseTo(200, 50); 209 EXPECT_EQ(HTRIGHT, ResizeShadowHitTest()); 210 EXPECT_EQ(ui::kCursorEastResize, GetCurrentCursorType()); 211 generator.MoveMouseTo(200 - ash::kResizeInsideBoundsSize, 50); 212 EXPECT_EQ(HTRIGHT, ResizeShadowHitTest()); 213 EXPECT_EQ(ui::kCursorEastResize, GetCurrentCursorType()); 214 215 ash::wm::GetWindowState(window())->Maximize(); 216 gfx::Rect bounds(window()->GetBoundsInRootWindow()); 217 gfx::Point right_center(bounds.right() - 1, 218 (bounds.y() + bounds.bottom()) / 2); 219 generator.MoveMouseTo(right_center); 220 EXPECT_FALSE(HasResizeShadow()); 221 EXPECT_EQ(ui::kCursorNull, GetCurrentCursorType()); 222 223 ash::wm::GetWindowState(window())->Restore(); 224 generator.MoveMouseTo(200, 50); 225 EXPECT_EQ(HTRIGHT, ResizeShadowHitTest()); 226 EXPECT_EQ(ui::kCursorEastResize, GetCurrentCursorType()); 227 generator.MoveMouseTo(200 - ash::kResizeInsideBoundsSize, 50); 228 EXPECT_EQ(HTRIGHT, ResizeShadowHitTest()); 229 EXPECT_EQ(ui::kCursorEastResize, GetCurrentCursorType()); 230 } 231 232 } // namespace test 233 } // namespace ash 234