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/workspace/multi_window_resize_controller.h"
6
7#include "ash/shell.h"
8#include "ash/test/ash_test_base.h"
9#include "ash/test/shell_test_api.h"
10#include "ash/wm/window_util.h"
11#include "ash/wm/workspace/workspace_event_handler_test_helper.h"
12#include "ash/wm/workspace_controller.h"
13#include "ash/wm/workspace_controller_test_helper.h"
14#include "ui/aura/test/test_window_delegate.h"
15#include "ui/aura/window.h"
16#include "ui/base/hit_test.h"
17#include "ui/events/test/event_generator.h"
18#include "ui/gfx/screen.h"
19#include "ui/views/widget/widget.h"
20
21namespace ash {
22
23class MultiWindowResizeControllerTest : public test::AshTestBase {
24 public:
25  MultiWindowResizeControllerTest() : resize_controller_(NULL) {}
26  virtual ~MultiWindowResizeControllerTest() {}
27
28  virtual void SetUp() OVERRIDE {
29    test::AshTestBase::SetUp();
30    WorkspaceController* wc =
31        test::ShellTestApi(Shell::GetInstance()).workspace_controller();
32    WorkspaceEventHandler* event_handler =
33        WorkspaceControllerTestHelper(wc).GetEventHandler();
34    resize_controller_ = WorkspaceEventHandlerTestHelper(event_handler).
35        resize_controller();
36  }
37
38 protected:
39  aura::Window* CreateTestWindow(aura::WindowDelegate* delegate,
40                                 const gfx::Rect& bounds) {
41    aura::Window* window = new aura::Window(delegate);
42    window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
43    window->Init(aura::WINDOW_LAYER_TEXTURED);
44    ParentWindowInPrimaryRootWindow(window);
45    window->SetBounds(bounds);
46    window->Show();
47    return window;
48  }
49
50  void ShowNow() {
51    resize_controller_->ShowNow();
52  }
53
54  bool IsShowing() {
55    return resize_controller_->IsShowing();
56  }
57
58  bool HasPendingShow() {
59    return resize_controller_->show_timer_.IsRunning();
60  }
61
62  bool HasPendingHide() {
63    return resize_controller_->hide_timer_.IsRunning();
64  }
65
66  void Hide() {
67    resize_controller_->Hide();
68  }
69
70  bool HasTarget(aura::Window* window) {
71    if (!resize_controller_->windows_.is_valid())
72      return false;
73    if ((resize_controller_->windows_.window1 == window ||
74         resize_controller_->windows_.window2 == window))
75      return true;
76    for (size_t i = 0;
77         i < resize_controller_->windows_.other_windows.size(); ++i) {
78      if (resize_controller_->windows_.other_windows[i] == window)
79        return true;
80    }
81    return false;
82  }
83
84  bool IsOverWindows(const gfx::Point& loc) {
85    return resize_controller_->IsOverWindows(loc);
86  }
87
88  views::Widget* resize_widget() {
89    return resize_controller_->resize_widget_.get();
90  }
91
92  MultiWindowResizeController* resize_controller_;
93
94 private:
95  DISALLOW_COPY_AND_ASSIGN(MultiWindowResizeControllerTest);
96};
97
98// Assertions around moving mouse over 2 windows.
99TEST_F(MultiWindowResizeControllerTest, BasicTests) {
100  aura::test::TestWindowDelegate delegate1;
101  scoped_ptr<aura::Window> w1(
102      CreateTestWindow(&delegate1, gfx::Rect(0, 0, 100, 100)));
103  delegate1.set_window_component(HTRIGHT);
104  aura::test::TestWindowDelegate delegate2;
105  scoped_ptr<aura::Window> w2(
106      CreateTestWindow(&delegate2, gfx::Rect(100, 0, 100, 100)));
107  delegate2.set_window_component(HTRIGHT);
108  ui::test::EventGenerator generator(w1->GetRootWindow());
109  generator.MoveMouseTo(w1->bounds().CenterPoint());
110  EXPECT_TRUE(HasPendingShow());
111  EXPECT_TRUE(IsShowing());
112  EXPECT_FALSE(HasPendingHide());
113
114  // Force a show now.
115  ShowNow();
116  EXPECT_FALSE(HasPendingShow());
117  EXPECT_TRUE(IsShowing());
118  EXPECT_FALSE(HasPendingHide());
119
120  EXPECT_FALSE(IsOverWindows(gfx::Point(200, 200)));
121
122  // Have to explicitly invoke this as MouseWatcher listens for native events.
123  resize_controller_->MouseMovedOutOfHost();
124  EXPECT_FALSE(HasPendingShow());
125  EXPECT_FALSE(IsShowing());
126  EXPECT_FALSE(HasPendingHide());
127}
128
129// Makes sure deleting a window hides.
130TEST_F(MultiWindowResizeControllerTest, DeleteWindow) {
131  aura::test::TestWindowDelegate delegate1;
132  scoped_ptr<aura::Window> w1(
133      CreateTestWindow(&delegate1, gfx::Rect(0, 0, 100, 100)));
134  delegate1.set_window_component(HTRIGHT);
135  aura::test::TestWindowDelegate delegate2;
136  scoped_ptr<aura::Window> w2(
137      CreateTestWindow(&delegate2, gfx::Rect(100, 0, 100, 100)));
138  delegate2.set_window_component(HTRIGHT);
139  ui::test::EventGenerator generator(w1->GetRootWindow());
140  generator.MoveMouseTo(w1->bounds().CenterPoint());
141  EXPECT_TRUE(HasPendingShow());
142  EXPECT_TRUE(IsShowing());
143  EXPECT_FALSE(HasPendingHide());
144
145  // Force a show now.
146  ShowNow();
147  EXPECT_FALSE(HasPendingShow());
148  EXPECT_TRUE(IsShowing());
149  EXPECT_FALSE(HasPendingHide());
150
151  // Move the mouse over the resize widget.
152  ASSERT_TRUE(resize_widget());
153  gfx::Rect bounds(resize_widget()->GetWindowBoundsInScreen());
154  generator.MoveMouseTo(bounds.x() + 1, bounds.y() + 1);
155  EXPECT_FALSE(HasPendingShow());
156  EXPECT_TRUE(IsShowing());
157  EXPECT_FALSE(HasPendingHide());
158
159  // Move the resize widget
160  generator.PressLeftButton();
161  generator.MoveMouseTo(bounds.x() + 10, bounds.y() + 10);
162
163  // Delete w2.
164  w2.reset();
165  EXPECT_TRUE(resize_widget() == NULL);
166  EXPECT_FALSE(HasPendingShow());
167  EXPECT_FALSE(IsShowing());
168  EXPECT_FALSE(HasPendingHide());
169  EXPECT_FALSE(HasTarget(w1.get()));
170}
171
172// Tests resizing.
173TEST_F(MultiWindowResizeControllerTest, Drag) {
174  aura::test::TestWindowDelegate delegate1;
175  scoped_ptr<aura::Window> w1(
176      CreateTestWindow(&delegate1, gfx::Rect(0, 0, 100, 100)));
177  delegate1.set_window_component(HTRIGHT);
178  aura::test::TestWindowDelegate delegate2;
179  scoped_ptr<aura::Window> w2(
180      CreateTestWindow(&delegate2, gfx::Rect(100, 0, 100, 100)));
181  delegate2.set_window_component(HTRIGHT);
182  ui::test::EventGenerator generator(w1->GetRootWindow());
183  generator.MoveMouseTo(w1->bounds().CenterPoint());
184  EXPECT_TRUE(HasPendingShow());
185  EXPECT_TRUE(IsShowing());
186  EXPECT_FALSE(HasPendingHide());
187
188  // Force a show now.
189  ShowNow();
190  EXPECT_FALSE(HasPendingShow());
191  EXPECT_TRUE(IsShowing());
192  EXPECT_FALSE(HasPendingHide());
193
194  // Move the mouse over the resize widget.
195  ASSERT_TRUE(resize_widget());
196  gfx::Rect bounds(resize_widget()->GetWindowBoundsInScreen());
197  generator.MoveMouseTo(bounds.x() + 1, bounds.y() + 1);
198  EXPECT_FALSE(HasPendingShow());
199  EXPECT_TRUE(IsShowing());
200  EXPECT_FALSE(HasPendingHide());
201
202  // Move the resize widget
203  generator.PressLeftButton();
204  generator.MoveMouseTo(bounds.x() + 11, bounds.y() + 10);
205  generator.ReleaseLeftButton();
206
207  EXPECT_TRUE(resize_widget());
208  EXPECT_FALSE(HasPendingShow());
209  EXPECT_TRUE(IsShowing());
210  EXPECT_FALSE(HasPendingHide());
211  EXPECT_EQ("0,0 110x100", w1->bounds().ToString());
212  EXPECT_EQ("110,0 100x100", w2->bounds().ToString());
213}
214
215// Makes sure three windows are picked up.
216TEST_F(MultiWindowResizeControllerTest, Three) {
217  aura::test::TestWindowDelegate delegate1;
218  scoped_ptr<aura::Window> w1(
219      CreateTestWindow(&delegate1, gfx::Rect(0, 0, 100, 100)));
220  delegate1.set_window_component(HTRIGHT);
221  aura::test::TestWindowDelegate delegate2;
222  scoped_ptr<aura::Window> w2(
223      CreateTestWindow(&delegate2, gfx::Rect(100, 0, 100, 100)));
224  delegate2.set_window_component(HTRIGHT);
225  aura::test::TestWindowDelegate delegate3;
226  scoped_ptr<aura::Window> w3(
227      CreateTestWindow(&delegate3, gfx::Rect(200, 0, 100, 100)));
228  delegate3.set_window_component(HTRIGHT);
229
230  ui::test::EventGenerator generator(w1->GetRootWindow());
231  generator.MoveMouseTo(w1->bounds().CenterPoint());
232  EXPECT_TRUE(HasPendingShow());
233  EXPECT_TRUE(IsShowing());
234  EXPECT_FALSE(HasPendingHide());
235  EXPECT_FALSE(HasTarget(w3.get()));
236
237  ShowNow();
238  EXPECT_FALSE(HasPendingShow());
239  EXPECT_TRUE(IsShowing());
240  EXPECT_FALSE(HasPendingHide());
241
242  // w3 should be picked up when resize is started.
243  gfx::Rect bounds(resize_widget()->GetWindowBoundsInScreen());
244  generator.MoveMouseTo(bounds.x() + 1, bounds.y() + 1);
245  generator.PressLeftButton();
246  generator.MoveMouseTo(bounds.x() + 11, bounds.y() + 10);
247
248  EXPECT_TRUE(HasTarget(w3.get()));
249
250  // Release the mouse. The resizer should still be visible and a subsequent
251  // press should not trigger a DCHECK.
252  generator.ReleaseLeftButton();
253  EXPECT_TRUE(IsShowing());
254  generator.PressLeftButton();
255}
256
257}  // namespace ash
258