1 // Copyright 2014 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.
5 #include "ash/frame/caption_buttons/frame_size_button.h"
7 #include "ash/frame/caption_buttons/frame_caption_button.h"
8 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
10 #include "ash/test/ash_test_base.h"
11 #include "ash/wm/window_state.h"
12 #include "base/i18n/rtl.h"
13 #include "grit/ash_resources.h"
14 #include "ui/aura/window.h"
15 #include "ui/base/l10n/l10n_util.h"
16 #include "ui/events/gestures/gesture_configuration.h"
17 #include "ui/events/test/event_generator.h"
18 #include "ui/gfx/display.h"
19 #include "ui/gfx/screen.h"
20 #include "ui/views/widget/widget.h"
21 #include "ui/views/widget/widget_delegate.h"
28 class TestWidgetDelegate
: public views::WidgetDelegateView
{
30 TestWidgetDelegate() {}
31 virtual ~TestWidgetDelegate() {}
33 // Overridden from views::WidgetDelegate:
34 virtual views::View
* GetContentsView() OVERRIDE
{
37 virtual bool CanResize() const OVERRIDE
{
40 virtual bool CanMaximize() const OVERRIDE
{
44 ash::FrameCaptionButtonContainerView
* caption_button_container() {
45 return caption_button_container_
;
49 // Overridden from views::View:
50 virtual void Layout() OVERRIDE
{
51 caption_button_container_
->Layout();
53 // Right align the caption button container.
54 gfx::Size preferred_size
= caption_button_container_
->GetPreferredSize();
55 caption_button_container_
->SetBounds(width() - preferred_size
.width(), 0,
56 preferred_size
.width(), preferred_size
.height());
59 virtual void ViewHierarchyChanged(
60 const ViewHierarchyChangedDetails
& details
) OVERRIDE
{
61 if (details
.is_add
&& details
.child
== this) {
62 caption_button_container_
= new FrameCaptionButtonContainerView(
63 GetWidget(), FrameCaptionButtonContainerView::MINIMIZE_ALLOWED
);
65 // Set arbitrary images for the container's buttons so that the buttons
66 // have non-empty sizes.
67 for (int icon
= 0; icon
< CAPTION_BUTTON_ICON_COUNT
; ++icon
) {
68 caption_button_container_
->SetButtonImages(
69 static_cast<CaptionButtonIcon
>(icon
),
70 IDR_AURA_WINDOW_CONTROL_ICON_CLOSE
,
71 IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_I
,
72 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
73 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
76 AddChildView(caption_button_container_
);
81 ash::FrameCaptionButtonContainerView
* caption_button_container_
;
83 DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate
);
88 class FrameSizeButtonTest
: public AshTestBase
{
90 FrameSizeButtonTest() {}
91 virtual ~FrameSizeButtonTest() {}
93 // Returns the center point of |view| in screen coordinates.
94 gfx::Point
CenterPointInScreen(views::View
* view
) {
95 return view
->GetBoundsInScreen().CenterPoint();
98 // Returns true if the window has |state_type|.
99 bool HasStateType(wm::WindowStateType state_type
) const {
100 return window_state()->GetStateType() == state_type
;
103 // Returns true if all three buttons are in the normal state.
104 bool AllButtonsInNormalState() const {
105 return minimize_button_
->state() == views::Button::STATE_NORMAL
&&
106 size_button_
->state() == views::Button::STATE_NORMAL
&&
107 close_button_
->state() == views::Button::STATE_NORMAL
;
110 // Creates a widget with |delegate|. The returned widget takes ownership of
112 views::Widget
* CreateWidget(views::WidgetDelegate
* delegate
) {
113 views::Widget
* widget
= new views::Widget
;
114 views::Widget::InitParams
params(
115 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
116 params
.context
= CurrentContext();
117 params
.delegate
= delegate
;
118 params
.bounds
= gfx::Rect(10, 10, 100, 100);
119 widget
->Init(params
);
124 // AshTestBase overrides:
125 virtual void SetUp() OVERRIDE
{
126 AshTestBase::SetUp();
128 TestWidgetDelegate
* delegate
= new TestWidgetDelegate();
129 window_state_
= ash::wm::GetWindowState(
130 CreateWidget(delegate
)->GetNativeWindow());
132 FrameCaptionButtonContainerView::TestApi
test(
133 delegate
->caption_button_container());
135 minimize_button_
= test
.minimize_button();
136 size_button_
= test
.size_button();
137 static_cast<FrameSizeButton
*>(
138 size_button_
)->set_delay_to_set_buttons_to_snap_mode(0);
139 close_button_
= test
.close_button();
142 ash::wm::WindowState
* window_state() { return window_state_
; }
143 const ash::wm::WindowState
* window_state() const { return window_state_
; }
145 FrameCaptionButton
* minimize_button() { return minimize_button_
; }
146 FrameCaptionButton
* size_button() { return size_button_
; }
147 FrameCaptionButton
* close_button() { return close_button_
; }
151 ash::wm::WindowState
* window_state_
;
152 FrameCaptionButton
* minimize_button_
;
153 FrameCaptionButton
* size_button_
;
154 FrameCaptionButton
* close_button_
;
156 DISALLOW_COPY_AND_ASSIGN(FrameSizeButtonTest
);
159 // Tests that pressing the left mouse button or tapping down on the size button
160 // puts the button into the pressed state.
161 TEST_F(FrameSizeButtonTest
, PressedState
) {
162 ui::test::EventGenerator
& generator
= GetEventGenerator();
163 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
164 generator
.PressLeftButton();
165 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
166 generator
.ReleaseLeftButton();
167 RunAllPendingInMessageLoop();
168 EXPECT_EQ(views::Button::STATE_NORMAL
, size_button()->state());
170 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
171 generator
.PressTouchId(3);
172 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
173 generator
.ReleaseTouchId(3);
174 RunAllPendingInMessageLoop();
175 EXPECT_EQ(views::Button::STATE_NORMAL
, size_button()->state());
178 // Tests that clicking on the size button toggles between the maximized and
180 TEST_F(FrameSizeButtonTest
, ClickSizeButtonTogglesMaximize
) {
181 EXPECT_FALSE(window_state()->IsMaximized());
183 ui::test::EventGenerator
& generator
= GetEventGenerator();
184 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
185 generator
.ClickLeftButton();
186 RunAllPendingInMessageLoop();
187 EXPECT_TRUE(window_state()->IsMaximized());
189 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
190 generator
.ClickLeftButton();
191 RunAllPendingInMessageLoop();
192 EXPECT_FALSE(window_state()->IsMaximized());
194 generator
.GestureTapAt(CenterPointInScreen(size_button()));
195 RunAllPendingInMessageLoop();
196 EXPECT_TRUE(window_state()->IsMaximized());
198 generator
.GestureTapAt(CenterPointInScreen(size_button()));
199 RunAllPendingInMessageLoop();
200 EXPECT_FALSE(window_state()->IsMaximized());
203 // Test that clicking + dragging to a button adjacent to the size button snaps
204 // the window left or right.
205 TEST_F(FrameSizeButtonTest
, ButtonDrag
) {
206 EXPECT_TRUE(window_state()->IsNormalStateType());
208 // 1) Test by dragging the mouse.
210 ui::test::EventGenerator
& generator
= GetEventGenerator();
211 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
212 generator
.PressLeftButton();
213 generator
.MoveMouseTo(CenterPointInScreen(close_button()));
214 generator
.ReleaseLeftButton();
215 RunAllPendingInMessageLoop();
216 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
));
219 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
220 generator
.PressLeftButton();
221 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
222 generator
.ReleaseLeftButton();
223 RunAllPendingInMessageLoop();
224 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
226 // 2) Test with scroll gestures.
228 generator
.GestureScrollSequence(
229 CenterPointInScreen(size_button()),
230 CenterPointInScreen(close_button()),
231 base::TimeDelta::FromMilliseconds(100),
233 RunAllPendingInMessageLoop();
234 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
));
237 generator
.GestureScrollSequence(
238 CenterPointInScreen(size_button()),
239 CenterPointInScreen(minimize_button()),
240 base::TimeDelta::FromMilliseconds(100),
242 RunAllPendingInMessageLoop();
243 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
245 // 3) Test with tap gestures.
246 const int touch_default_radius
=
247 ui::GestureConfiguration::default_radius();
248 ui::GestureConfiguration::set_default_radius(0);
250 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
251 generator
.PressMoveAndReleaseTouchTo(CenterPointInScreen(close_button()));
252 RunAllPendingInMessageLoop();
253 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
));
255 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
256 generator
.PressMoveAndReleaseTouchTo(CenterPointInScreen(minimize_button()));
257 RunAllPendingInMessageLoop();
258 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
259 ui::GestureConfiguration::set_default_radius(touch_default_radius
);
262 // Test that clicking, dragging, and overshooting the minimize button a bit
263 // horizontally still snaps the window left.
264 TEST_F(FrameSizeButtonTest
, SnapLeftOvershootMinimize
) {
265 EXPECT_TRUE(window_state()->IsNormalStateType());
267 ui::test::EventGenerator
& generator
= GetEventGenerator();
268 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
270 generator
.PressLeftButton();
271 // Move to the minimize button.
272 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
273 // Overshoot the minimize button.
274 generator
.MoveMouseBy(-minimize_button()->width(), 0);
275 generator
.ReleaseLeftButton();
276 RunAllPendingInMessageLoop();
277 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
280 // Test that right clicking the size button has no effect.
281 TEST_F(FrameSizeButtonTest
, RightMouseButton
) {
282 EXPECT_TRUE(window_state()->IsNormalStateType());
284 ui::test::EventGenerator
& generator
= GetEventGenerator();
285 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
286 generator
.PressRightButton();
287 generator
.ReleaseRightButton();
288 RunAllPendingInMessageLoop();
289 EXPECT_TRUE(window_state()->IsNormalStateType());
292 // Test that upon releasing the mouse button after having pressed the size
294 // - The state of all the caption buttons is reset.
295 // - The icon displayed by all of the caption buttons is reset.
296 TEST_F(FrameSizeButtonTest
, ResetButtonsAfterClick
) {
297 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
298 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
299 EXPECT_TRUE(AllButtonsInNormalState());
301 // Pressing the size button should result in the size button being pressed and
302 // the minimize and close button icons changing.
303 ui::test::EventGenerator
& generator
= GetEventGenerator();
304 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
305 generator
.PressLeftButton();
306 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
307 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
308 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
309 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
310 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
312 // Dragging the mouse over the minimize button should hover the minimize
313 // button and the minimize and close button icons should stay changed.
314 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
315 EXPECT_EQ(views::Button::STATE_HOVERED
, minimize_button()->state());
316 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
317 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
318 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
319 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
321 // Release the mouse, snapping the window left.
322 generator
.ReleaseLeftButton();
323 RunAllPendingInMessageLoop();
324 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
326 // None of the buttons should stay pressed and the buttons should have their
328 EXPECT_TRUE(AllButtonsInNormalState());
329 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
330 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
332 // Repeat test but release button where it does not affect the window's state
333 // because the code path is different.
334 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
335 generator
.PressLeftButton();
336 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
337 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
338 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
339 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
340 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
342 const gfx::Rect
& kWorkAreaBoundsInScreen
=
343 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
344 generator
.MoveMouseTo(kWorkAreaBoundsInScreen
.bottom_left());
346 // None of the buttons should be pressed because we are really far away from
347 // any of the caption buttons. The minimize and close button icons should
348 // be changed because the mouse is pressed.
349 EXPECT_TRUE(AllButtonsInNormalState());
350 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
351 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
353 // Release the mouse. The window should stay snapped left.
354 generator
.ReleaseLeftButton();
355 RunAllPendingInMessageLoop();
356 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
358 // The buttons should stay unpressed and the buttons should now have their
360 EXPECT_TRUE(AllButtonsInNormalState());
361 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
362 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
365 // Test that the size button is pressed whenever the snap left/right buttons
367 TEST_F(FrameSizeButtonTest
, SizeButtonPressedWhenSnapButtonHovered
) {
368 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
369 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
370 EXPECT_TRUE(AllButtonsInNormalState());
372 // Pressing the size button should result in the size button being pressed and
373 // the minimize and close button icons changing.
374 ui::test::EventGenerator
& generator
= GetEventGenerator();
375 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
376 generator
.PressLeftButton();
377 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
378 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
379 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
380 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
381 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
383 // Dragging the mouse over the minimize button (snap left button) should hover
384 // the minimize button and keep the size button pressed.
385 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
386 EXPECT_EQ(views::Button::STATE_HOVERED
, minimize_button()->state());
387 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
388 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
390 // Moving the mouse far away from the caption buttons and then moving it over
391 // the close button (snap right button) should hover the close button and
392 // keep the size button pressed.
393 const gfx::Rect
& kWorkAreaBoundsInScreen
=
394 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
395 generator
.MoveMouseTo(kWorkAreaBoundsInScreen
.bottom_left());
396 EXPECT_TRUE(AllButtonsInNormalState());
397 generator
.MoveMouseTo(CenterPointInScreen(close_button()));
398 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
399 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
400 EXPECT_EQ(views::Button::STATE_HOVERED
, close_button()->state());
403 class FrameSizeButtonTestRTL
: public FrameSizeButtonTest
{
405 FrameSizeButtonTestRTL() {}
406 virtual ~FrameSizeButtonTestRTL() {}
408 virtual void SetUp() OVERRIDE
{
409 original_locale_
= l10n_util::GetApplicationLocale(std::string());
410 base::i18n::SetICUDefaultLocale("he");
412 FrameSizeButtonTest::SetUp();
415 virtual void TearDown() OVERRIDE
{
416 FrameSizeButtonTest::TearDown();
417 base::i18n::SetICUDefaultLocale(original_locale_
);
421 std::string original_locale_
;
423 DISALLOW_COPY_AND_ASSIGN(FrameSizeButtonTestRTL
);
426 // Test that clicking + dragging to a button adjacent to the size button presses
427 // the correct button and snaps the window to the correct side.
428 TEST_F(FrameSizeButtonTestRTL
, ButtonDrag
) {
429 // In RTL the close button should be left of the size button and the minimize
430 // button should be right of the size button.
431 ASSERT_LT(close_button()->GetBoundsInScreen().x(),
432 size_button()->GetBoundsInScreen().x());
433 ASSERT_LT(size_button()->GetBoundsInScreen().x(),
434 minimize_button()->GetBoundsInScreen().x());
436 // Test initial state.
437 EXPECT_TRUE(window_state()->IsNormalStateType());
438 EXPECT_TRUE(AllButtonsInNormalState());
439 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
440 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
442 // Pressing the size button should swap the icons of the minimize and close
443 // buttons to icons for snapping right and for snapping left respectively.
444 ui::test::EventGenerator
& generator
= GetEventGenerator();
445 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
446 generator
.PressLeftButton();
447 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
448 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
449 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
450 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, minimize_button()->icon());
451 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, close_button()->icon());
453 // Dragging over to the minimize button should press it.
454 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
455 EXPECT_EQ(views::Button::STATE_HOVERED
, minimize_button()->state());
456 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
457 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
459 // Releasing should snap the window right.
460 generator
.ReleaseLeftButton();
461 RunAllPendingInMessageLoop();
462 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
));
464 // None of the buttons should stay pressed and the buttons should have their
466 EXPECT_TRUE(AllButtonsInNormalState());
467 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
468 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());