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/gesture_detection/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 ~TestWidgetDelegate() override
{}
33 // Overridden from views::WidgetDelegate:
34 views::View
* GetContentsView() override
{ return this; }
35 bool CanResize() const override
{ return true; }
36 bool CanMaximize() const override
{ return true; }
37 bool CanMinimize() const override
{ return true; }
39 ash::FrameCaptionButtonContainerView
* caption_button_container() {
40 return caption_button_container_
;
44 // Overridden from views::View:
45 void Layout() override
{
46 caption_button_container_
->Layout();
48 // Right align the caption button container.
49 gfx::Size preferred_size
= caption_button_container_
->GetPreferredSize();
50 caption_button_container_
->SetBounds(width() - preferred_size
.width(), 0,
51 preferred_size
.width(), preferred_size
.height());
54 void ViewHierarchyChanged(
55 const ViewHierarchyChangedDetails
& details
) override
{
56 if (details
.is_add
&& details
.child
== this) {
57 caption_button_container_
=
58 new FrameCaptionButtonContainerView(GetWidget());
60 // Set arbitrary images for the container's buttons so that the buttons
61 // have non-empty sizes.
62 for (int icon
= 0; icon
< CAPTION_BUTTON_ICON_COUNT
; ++icon
) {
63 caption_button_container_
->SetButtonImages(
64 static_cast<CaptionButtonIcon
>(icon
),
65 IDR_AURA_WINDOW_CONTROL_ICON_CLOSE
,
66 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
67 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
70 AddChildView(caption_button_container_
);
75 ash::FrameCaptionButtonContainerView
* caption_button_container_
;
77 DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate
);
82 class FrameSizeButtonTest
: public AshTestBase
{
84 FrameSizeButtonTest() {}
85 ~FrameSizeButtonTest() override
{}
87 // Returns the center point of |view| in screen coordinates.
88 gfx::Point
CenterPointInScreen(views::View
* view
) {
89 return view
->GetBoundsInScreen().CenterPoint();
92 // Returns true if the window has |state_type|.
93 bool HasStateType(wm::WindowStateType state_type
) const {
94 return window_state()->GetStateType() == state_type
;
97 // Returns true if all three buttons are in the normal state.
98 bool AllButtonsInNormalState() const {
99 return minimize_button_
->state() == views::Button::STATE_NORMAL
&&
100 size_button_
->state() == views::Button::STATE_NORMAL
&&
101 close_button_
->state() == views::Button::STATE_NORMAL
;
104 // Creates a widget with |delegate|. The returned widget takes ownership of
106 views::Widget
* CreateWidget(views::WidgetDelegate
* delegate
) {
107 views::Widget
* widget
= new views::Widget
;
108 views::Widget::InitParams
params(
109 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
110 params
.context
= CurrentContext();
111 params
.delegate
= delegate
;
112 params
.bounds
= gfx::Rect(10, 10, 100, 100);
113 widget
->Init(params
);
118 // AshTestBase overrides:
119 void SetUp() override
{
120 AshTestBase::SetUp();
122 TestWidgetDelegate
* delegate
= new TestWidgetDelegate();
123 window_state_
= ash::wm::GetWindowState(
124 CreateWidget(delegate
)->GetNativeWindow());
126 FrameCaptionButtonContainerView::TestApi
test(
127 delegate
->caption_button_container());
129 minimize_button_
= test
.minimize_button();
130 size_button_
= test
.size_button();
131 static_cast<FrameSizeButton
*>(
132 size_button_
)->set_delay_to_set_buttons_to_snap_mode(0);
133 close_button_
= test
.close_button();
136 ash::wm::WindowState
* window_state() { return window_state_
; }
137 const ash::wm::WindowState
* window_state() const { return window_state_
; }
139 FrameCaptionButton
* minimize_button() { return minimize_button_
; }
140 FrameCaptionButton
* size_button() { return size_button_
; }
141 FrameCaptionButton
* close_button() { return close_button_
; }
145 ash::wm::WindowState
* window_state_
;
146 FrameCaptionButton
* minimize_button_
;
147 FrameCaptionButton
* size_button_
;
148 FrameCaptionButton
* close_button_
;
150 DISALLOW_COPY_AND_ASSIGN(FrameSizeButtonTest
);
153 // Tests that pressing the left mouse button or tapping down on the size button
154 // puts the button into the pressed state.
155 TEST_F(FrameSizeButtonTest
, PressedState
) {
156 ui::test::EventGenerator
& generator
= GetEventGenerator();
157 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
158 generator
.PressLeftButton();
159 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
160 generator
.ReleaseLeftButton();
161 RunAllPendingInMessageLoop();
162 EXPECT_EQ(views::Button::STATE_NORMAL
, size_button()->state());
164 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
165 generator
.PressTouchId(3);
166 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
167 generator
.ReleaseTouchId(3);
168 RunAllPendingInMessageLoop();
169 EXPECT_EQ(views::Button::STATE_NORMAL
, size_button()->state());
172 // Tests that clicking on the size button toggles between the maximized and
174 TEST_F(FrameSizeButtonTest
, ClickSizeButtonTogglesMaximize
) {
175 EXPECT_FALSE(window_state()->IsMaximized());
177 ui::test::EventGenerator
& generator
= GetEventGenerator();
178 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
179 generator
.ClickLeftButton();
180 RunAllPendingInMessageLoop();
181 EXPECT_TRUE(window_state()->IsMaximized());
183 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
184 generator
.ClickLeftButton();
185 RunAllPendingInMessageLoop();
186 EXPECT_FALSE(window_state()->IsMaximized());
188 generator
.GestureTapAt(CenterPointInScreen(size_button()));
189 RunAllPendingInMessageLoop();
190 EXPECT_TRUE(window_state()->IsMaximized());
192 generator
.GestureTapAt(CenterPointInScreen(size_button()));
193 RunAllPendingInMessageLoop();
194 EXPECT_FALSE(window_state()->IsMaximized());
197 // Test that clicking + dragging to a button adjacent to the size button snaps
198 // the window left or right.
199 TEST_F(FrameSizeButtonTest
, ButtonDrag
) {
200 EXPECT_TRUE(window_state()->IsNormalStateType());
202 // 1) Test by dragging the mouse.
204 ui::test::EventGenerator
& generator
= GetEventGenerator();
205 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
206 generator
.PressLeftButton();
207 generator
.MoveMouseTo(CenterPointInScreen(close_button()));
208 generator
.ReleaseLeftButton();
209 RunAllPendingInMessageLoop();
210 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
));
213 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
214 generator
.PressLeftButton();
215 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
216 generator
.ReleaseLeftButton();
217 RunAllPendingInMessageLoop();
218 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
220 // 2) Test with scroll gestures.
222 generator
.GestureScrollSequence(
223 CenterPointInScreen(size_button()),
224 CenterPointInScreen(close_button()),
225 base::TimeDelta::FromMilliseconds(100),
227 RunAllPendingInMessageLoop();
228 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
));
231 generator
.GestureScrollSequence(
232 CenterPointInScreen(size_button()),
233 CenterPointInScreen(minimize_button()),
234 base::TimeDelta::FromMilliseconds(100),
236 RunAllPendingInMessageLoop();
237 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
239 // 3) Test with tap gestures.
240 const float touch_default_radius
=
241 ui::GestureConfiguration::GetInstance()->default_radius();
242 ui::GestureConfiguration::GetInstance()->set_default_radius(0);
244 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
245 generator
.PressMoveAndReleaseTouchTo(CenterPointInScreen(close_button()));
246 RunAllPendingInMessageLoop();
247 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
));
249 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
250 generator
.PressMoveAndReleaseTouchTo(CenterPointInScreen(minimize_button()));
251 RunAllPendingInMessageLoop();
252 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
253 ui::GestureConfiguration::GetInstance()->set_default_radius(
254 touch_default_radius
);
257 // Test that clicking, dragging, and overshooting the minimize button a bit
258 // horizontally still snaps the window left.
259 TEST_F(FrameSizeButtonTest
, SnapLeftOvershootMinimize
) {
260 EXPECT_TRUE(window_state()->IsNormalStateType());
262 ui::test::EventGenerator
& generator
= GetEventGenerator();
263 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
265 generator
.PressLeftButton();
266 // Move to the minimize button.
267 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
268 // Overshoot the minimize button.
269 generator
.MoveMouseBy(-minimize_button()->width(), 0);
270 generator
.ReleaseLeftButton();
271 RunAllPendingInMessageLoop();
272 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
275 // Test that right clicking the size button has no effect.
276 TEST_F(FrameSizeButtonTest
, RightMouseButton
) {
277 EXPECT_TRUE(window_state()->IsNormalStateType());
279 ui::test::EventGenerator
& generator
= GetEventGenerator();
280 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
281 generator
.PressRightButton();
282 generator
.ReleaseRightButton();
283 RunAllPendingInMessageLoop();
284 EXPECT_TRUE(window_state()->IsNormalStateType());
287 // Test that upon releasing the mouse button after having pressed the size
289 // - The state of all the caption buttons is reset.
290 // - The icon displayed by all of the caption buttons is reset.
291 TEST_F(FrameSizeButtonTest
, ResetButtonsAfterClick
) {
292 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
293 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
294 EXPECT_TRUE(AllButtonsInNormalState());
296 // Pressing the size button should result in the size button being pressed and
297 // the minimize and close button icons changing.
298 ui::test::EventGenerator
& generator
= GetEventGenerator();
299 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
300 generator
.PressLeftButton();
301 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
302 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
303 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
304 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
305 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
307 // Dragging the mouse over the minimize button should hover the minimize
308 // button and the minimize and close button icons should stay changed.
309 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
310 EXPECT_EQ(views::Button::STATE_HOVERED
, minimize_button()->state());
311 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
312 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
313 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
314 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
316 // Release the mouse, snapping the window left.
317 generator
.ReleaseLeftButton();
318 RunAllPendingInMessageLoop();
319 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
321 // None of the buttons should stay pressed and the buttons should have their
323 EXPECT_TRUE(AllButtonsInNormalState());
324 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
325 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
327 // Repeat test but release button where it does not affect the window's state
328 // because the code path is different.
329 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
330 generator
.PressLeftButton();
331 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
332 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
333 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
334 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
335 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
337 const gfx::Rect
& kWorkAreaBoundsInScreen
=
338 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
339 generator
.MoveMouseTo(kWorkAreaBoundsInScreen
.bottom_left());
341 // None of the buttons should be pressed because we are really far away from
342 // any of the caption buttons. The minimize and close button icons should
343 // be changed because the mouse is pressed.
344 EXPECT_TRUE(AllButtonsInNormalState());
345 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
346 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
348 // Release the mouse. The window should stay snapped left.
349 generator
.ReleaseLeftButton();
350 RunAllPendingInMessageLoop();
351 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
353 // The buttons should stay unpressed and the buttons should now have their
355 EXPECT_TRUE(AllButtonsInNormalState());
356 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
357 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
360 // Test that the size button is pressed whenever the snap left/right buttons
362 TEST_F(FrameSizeButtonTest
, SizeButtonPressedWhenSnapButtonHovered
) {
363 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
364 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
365 EXPECT_TRUE(AllButtonsInNormalState());
367 // Pressing the size button should result in the size button being pressed and
368 // the minimize and close button icons changing.
369 ui::test::EventGenerator
& generator
= GetEventGenerator();
370 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
371 generator
.PressLeftButton();
372 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
373 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
374 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
375 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
376 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
378 // Dragging the mouse over the minimize button (snap left button) should hover
379 // the minimize button and keep the size button pressed.
380 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
381 EXPECT_EQ(views::Button::STATE_HOVERED
, minimize_button()->state());
382 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
383 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
385 // Moving the mouse far away from the caption buttons and then moving it over
386 // the close button (snap right button) should hover the close button and
387 // keep the size button pressed.
388 const gfx::Rect
& kWorkAreaBoundsInScreen
=
389 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
390 generator
.MoveMouseTo(kWorkAreaBoundsInScreen
.bottom_left());
391 EXPECT_TRUE(AllButtonsInNormalState());
392 generator
.MoveMouseTo(CenterPointInScreen(close_button()));
393 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
394 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
395 EXPECT_EQ(views::Button::STATE_HOVERED
, close_button()->state());
398 class FrameSizeButtonTestRTL
: public FrameSizeButtonTest
{
400 FrameSizeButtonTestRTL() {}
401 ~FrameSizeButtonTestRTL() override
{}
403 void SetUp() override
{
404 original_locale_
= l10n_util::GetApplicationLocale(std::string());
405 base::i18n::SetICUDefaultLocale("he");
407 FrameSizeButtonTest::SetUp();
410 void TearDown() override
{
411 FrameSizeButtonTest::TearDown();
412 base::i18n::SetICUDefaultLocale(original_locale_
);
416 std::string original_locale_
;
418 DISALLOW_COPY_AND_ASSIGN(FrameSizeButtonTestRTL
);
421 // Test that clicking + dragging to a button adjacent to the size button presses
422 // the correct button and snaps the window to the correct side.
423 TEST_F(FrameSizeButtonTestRTL
, ButtonDrag
) {
424 // In RTL the close button should be left of the size button and the minimize
425 // button should be right of the size button.
426 ASSERT_LT(close_button()->GetBoundsInScreen().x(),
427 size_button()->GetBoundsInScreen().x());
428 ASSERT_LT(size_button()->GetBoundsInScreen().x(),
429 minimize_button()->GetBoundsInScreen().x());
431 // Test initial state.
432 EXPECT_TRUE(window_state()->IsNormalStateType());
433 EXPECT_TRUE(AllButtonsInNormalState());
434 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
435 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
437 // Pressing the size button should swap the icons of the minimize and close
438 // buttons to icons for snapping right and for snapping left respectively.
439 ui::test::EventGenerator
& generator
= GetEventGenerator();
440 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
441 generator
.PressLeftButton();
442 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
443 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
444 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
445 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, minimize_button()->icon());
446 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, close_button()->icon());
448 // Dragging over to the minimize button should press it.
449 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
450 EXPECT_EQ(views::Button::STATE_HOVERED
, minimize_button()->state());
451 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
452 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
454 // Releasing should snap the window right.
455 generator
.ReleaseLeftButton();
456 RunAllPendingInMessageLoop();
457 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
));
459 // None of the buttons should stay pressed and the buttons should have their
461 EXPECT_TRUE(AllButtonsInNormalState());
462 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
463 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());