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_ICON_CLOSE_I
,
67 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
68 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
71 AddChildView(caption_button_container_
);
76 ash::FrameCaptionButtonContainerView
* caption_button_container_
;
78 DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate
);
83 class FrameSizeButtonTest
: public AshTestBase
{
85 FrameSizeButtonTest() {}
86 ~FrameSizeButtonTest() override
{}
88 // Returns the center point of |view| in screen coordinates.
89 gfx::Point
CenterPointInScreen(views::View
* view
) {
90 return view
->GetBoundsInScreen().CenterPoint();
93 // Returns true if the window has |state_type|.
94 bool HasStateType(wm::WindowStateType state_type
) const {
95 return window_state()->GetStateType() == state_type
;
98 // Returns true if all three buttons are in the normal state.
99 bool AllButtonsInNormalState() const {
100 return minimize_button_
->state() == views::Button::STATE_NORMAL
&&
101 size_button_
->state() == views::Button::STATE_NORMAL
&&
102 close_button_
->state() == views::Button::STATE_NORMAL
;
105 // Creates a widget with |delegate|. The returned widget takes ownership of
107 views::Widget
* CreateWidget(views::WidgetDelegate
* delegate
) {
108 views::Widget
* widget
= new views::Widget
;
109 views::Widget::InitParams
params(
110 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
111 params
.context
= CurrentContext();
112 params
.delegate
= delegate
;
113 params
.bounds
= gfx::Rect(10, 10, 100, 100);
114 widget
->Init(params
);
119 // AshTestBase overrides:
120 void SetUp() override
{
121 AshTestBase::SetUp();
123 TestWidgetDelegate
* delegate
= new TestWidgetDelegate();
124 window_state_
= ash::wm::GetWindowState(
125 CreateWidget(delegate
)->GetNativeWindow());
127 FrameCaptionButtonContainerView::TestApi
test(
128 delegate
->caption_button_container());
130 minimize_button_
= test
.minimize_button();
131 size_button_
= test
.size_button();
132 static_cast<FrameSizeButton
*>(
133 size_button_
)->set_delay_to_set_buttons_to_snap_mode(0);
134 close_button_
= test
.close_button();
137 ash::wm::WindowState
* window_state() { return window_state_
; }
138 const ash::wm::WindowState
* window_state() const { return window_state_
; }
140 FrameCaptionButton
* minimize_button() { return minimize_button_
; }
141 FrameCaptionButton
* size_button() { return size_button_
; }
142 FrameCaptionButton
* close_button() { return close_button_
; }
146 ash::wm::WindowState
* window_state_
;
147 FrameCaptionButton
* minimize_button_
;
148 FrameCaptionButton
* size_button_
;
149 FrameCaptionButton
* close_button_
;
151 DISALLOW_COPY_AND_ASSIGN(FrameSizeButtonTest
);
154 // Tests that pressing the left mouse button or tapping down on the size button
155 // puts the button into the pressed state.
156 TEST_F(FrameSizeButtonTest
, PressedState
) {
157 ui::test::EventGenerator
& generator
= GetEventGenerator();
158 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
159 generator
.PressLeftButton();
160 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
161 generator
.ReleaseLeftButton();
162 RunAllPendingInMessageLoop();
163 EXPECT_EQ(views::Button::STATE_NORMAL
, size_button()->state());
165 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
166 generator
.PressTouchId(3);
167 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
168 generator
.ReleaseTouchId(3);
169 RunAllPendingInMessageLoop();
170 EXPECT_EQ(views::Button::STATE_NORMAL
, size_button()->state());
173 // Tests that clicking on the size button toggles between the maximized and
175 TEST_F(FrameSizeButtonTest
, ClickSizeButtonTogglesMaximize
) {
176 EXPECT_FALSE(window_state()->IsMaximized());
178 ui::test::EventGenerator
& generator
= GetEventGenerator();
179 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
180 generator
.ClickLeftButton();
181 RunAllPendingInMessageLoop();
182 EXPECT_TRUE(window_state()->IsMaximized());
184 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
185 generator
.ClickLeftButton();
186 RunAllPendingInMessageLoop();
187 EXPECT_FALSE(window_state()->IsMaximized());
189 generator
.GestureTapAt(CenterPointInScreen(size_button()));
190 RunAllPendingInMessageLoop();
191 EXPECT_TRUE(window_state()->IsMaximized());
193 generator
.GestureTapAt(CenterPointInScreen(size_button()));
194 RunAllPendingInMessageLoop();
195 EXPECT_FALSE(window_state()->IsMaximized());
198 // Test that clicking + dragging to a button adjacent to the size button snaps
199 // the window left or right.
200 TEST_F(FrameSizeButtonTest
, ButtonDrag
) {
201 EXPECT_TRUE(window_state()->IsNormalStateType());
203 // 1) Test by dragging the mouse.
205 ui::test::EventGenerator
& generator
= GetEventGenerator();
206 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
207 generator
.PressLeftButton();
208 generator
.MoveMouseTo(CenterPointInScreen(close_button()));
209 generator
.ReleaseLeftButton();
210 RunAllPendingInMessageLoop();
211 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
));
214 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
215 generator
.PressLeftButton();
216 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
217 generator
.ReleaseLeftButton();
218 RunAllPendingInMessageLoop();
219 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
221 // 2) Test with scroll gestures.
223 generator
.GestureScrollSequence(
224 CenterPointInScreen(size_button()),
225 CenterPointInScreen(close_button()),
226 base::TimeDelta::FromMilliseconds(100),
228 RunAllPendingInMessageLoop();
229 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
));
232 generator
.GestureScrollSequence(
233 CenterPointInScreen(size_button()),
234 CenterPointInScreen(minimize_button()),
235 base::TimeDelta::FromMilliseconds(100),
237 RunAllPendingInMessageLoop();
238 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
240 // 3) Test with tap gestures.
241 const float touch_default_radius
=
242 ui::GestureConfiguration::GetInstance()->default_radius();
243 ui::GestureConfiguration::GetInstance()->set_default_radius(0);
245 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
246 generator
.PressMoveAndReleaseTouchTo(CenterPointInScreen(close_button()));
247 RunAllPendingInMessageLoop();
248 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
));
250 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
251 generator
.PressMoveAndReleaseTouchTo(CenterPointInScreen(minimize_button()));
252 RunAllPendingInMessageLoop();
253 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
254 ui::GestureConfiguration::GetInstance()->set_default_radius(
255 touch_default_radius
);
258 // Test that clicking, dragging, and overshooting the minimize button a bit
259 // horizontally still snaps the window left.
260 TEST_F(FrameSizeButtonTest
, SnapLeftOvershootMinimize
) {
261 EXPECT_TRUE(window_state()->IsNormalStateType());
263 ui::test::EventGenerator
& generator
= GetEventGenerator();
264 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
266 generator
.PressLeftButton();
267 // Move to the minimize button.
268 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
269 // Overshoot the minimize button.
270 generator
.MoveMouseBy(-minimize_button()->width(), 0);
271 generator
.ReleaseLeftButton();
272 RunAllPendingInMessageLoop();
273 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
276 // Test that right clicking the size button has no effect.
277 TEST_F(FrameSizeButtonTest
, RightMouseButton
) {
278 EXPECT_TRUE(window_state()->IsNormalStateType());
280 ui::test::EventGenerator
& generator
= GetEventGenerator();
281 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
282 generator
.PressRightButton();
283 generator
.ReleaseRightButton();
284 RunAllPendingInMessageLoop();
285 EXPECT_TRUE(window_state()->IsNormalStateType());
288 // Test that upon releasing the mouse button after having pressed the size
290 // - The state of all the caption buttons is reset.
291 // - The icon displayed by all of the caption buttons is reset.
292 TEST_F(FrameSizeButtonTest
, ResetButtonsAfterClick
) {
293 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
294 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
295 EXPECT_TRUE(AllButtonsInNormalState());
297 // Pressing the size button should result in the size button being pressed and
298 // the minimize and close button icons changing.
299 ui::test::EventGenerator
& generator
= GetEventGenerator();
300 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
301 generator
.PressLeftButton();
302 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
303 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
304 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
305 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
306 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
308 // Dragging the mouse over the minimize button should hover the minimize
309 // button and the minimize and close button icons should stay changed.
310 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
311 EXPECT_EQ(views::Button::STATE_HOVERED
, minimize_button()->state());
312 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
313 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
314 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
315 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
317 // Release the mouse, snapping the window left.
318 generator
.ReleaseLeftButton();
319 RunAllPendingInMessageLoop();
320 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
322 // None of the buttons should stay pressed and the buttons should have their
324 EXPECT_TRUE(AllButtonsInNormalState());
325 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
326 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
328 // Repeat test but release button where it does not affect the window's state
329 // because the code path is different.
330 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
331 generator
.PressLeftButton();
332 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
333 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
334 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
335 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
336 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
338 const gfx::Rect
& kWorkAreaBoundsInScreen
=
339 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
340 generator
.MoveMouseTo(kWorkAreaBoundsInScreen
.bottom_left());
342 // None of the buttons should be pressed because we are really far away from
343 // any of the caption buttons. The minimize and close button icons should
344 // be changed because the mouse is pressed.
345 EXPECT_TRUE(AllButtonsInNormalState());
346 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
347 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
349 // Release the mouse. The window should stay snapped left.
350 generator
.ReleaseLeftButton();
351 RunAllPendingInMessageLoop();
352 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
));
354 // The buttons should stay unpressed and the buttons should now have their
356 EXPECT_TRUE(AllButtonsInNormalState());
357 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
358 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
361 // Test that the size button is pressed whenever the snap left/right buttons
363 TEST_F(FrameSizeButtonTest
, SizeButtonPressedWhenSnapButtonHovered
) {
364 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
365 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
366 EXPECT_TRUE(AllButtonsInNormalState());
368 // Pressing the size button should result in the size button being pressed and
369 // the minimize and close button icons changing.
370 ui::test::EventGenerator
& generator
= GetEventGenerator();
371 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
372 generator
.PressLeftButton();
373 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
374 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
375 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
376 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, minimize_button()->icon());
377 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, close_button()->icon());
379 // Dragging the mouse over the minimize button (snap left button) should hover
380 // the minimize button and keep the size button pressed.
381 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
382 EXPECT_EQ(views::Button::STATE_HOVERED
, minimize_button()->state());
383 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
384 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
386 // Moving the mouse far away from the caption buttons and then moving it over
387 // the close button (snap right button) should hover the close button and
388 // keep the size button pressed.
389 const gfx::Rect
& kWorkAreaBoundsInScreen
=
390 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
391 generator
.MoveMouseTo(kWorkAreaBoundsInScreen
.bottom_left());
392 EXPECT_TRUE(AllButtonsInNormalState());
393 generator
.MoveMouseTo(CenterPointInScreen(close_button()));
394 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
395 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
396 EXPECT_EQ(views::Button::STATE_HOVERED
, close_button()->state());
399 class FrameSizeButtonTestRTL
: public FrameSizeButtonTest
{
401 FrameSizeButtonTestRTL() {}
402 ~FrameSizeButtonTestRTL() override
{}
404 void SetUp() override
{
405 original_locale_
= l10n_util::GetApplicationLocale(std::string());
406 base::i18n::SetICUDefaultLocale("he");
408 FrameSizeButtonTest::SetUp();
411 void TearDown() override
{
412 FrameSizeButtonTest::TearDown();
413 base::i18n::SetICUDefaultLocale(original_locale_
);
417 std::string original_locale_
;
419 DISALLOW_COPY_AND_ASSIGN(FrameSizeButtonTestRTL
);
422 // Test that clicking + dragging to a button adjacent to the size button presses
423 // the correct button and snaps the window to the correct side.
424 TEST_F(FrameSizeButtonTestRTL
, ButtonDrag
) {
425 // In RTL the close button should be left of the size button and the minimize
426 // button should be right of the size button.
427 ASSERT_LT(close_button()->GetBoundsInScreen().x(),
428 size_button()->GetBoundsInScreen().x());
429 ASSERT_LT(size_button()->GetBoundsInScreen().x(),
430 minimize_button()->GetBoundsInScreen().x());
432 // Test initial state.
433 EXPECT_TRUE(window_state()->IsNormalStateType());
434 EXPECT_TRUE(AllButtonsInNormalState());
435 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
436 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());
438 // Pressing the size button should swap the icons of the minimize and close
439 // buttons to icons for snapping right and for snapping left respectively.
440 ui::test::EventGenerator
& generator
= GetEventGenerator();
441 generator
.MoveMouseTo(CenterPointInScreen(size_button()));
442 generator
.PressLeftButton();
443 EXPECT_EQ(views::Button::STATE_NORMAL
, minimize_button()->state());
444 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
445 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
446 EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED
, minimize_button()->icon());
447 EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED
, close_button()->icon());
449 // Dragging over to the minimize button should press it.
450 generator
.MoveMouseTo(CenterPointInScreen(minimize_button()));
451 EXPECT_EQ(views::Button::STATE_HOVERED
, minimize_button()->state());
452 EXPECT_EQ(views::Button::STATE_PRESSED
, size_button()->state());
453 EXPECT_EQ(views::Button::STATE_NORMAL
, close_button()->state());
455 // Releasing should snap the window right.
456 generator
.ReleaseLeftButton();
457 RunAllPendingInMessageLoop();
458 EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
));
460 // None of the buttons should stay pressed and the buttons should have their
462 EXPECT_TRUE(AllButtonsInNormalState());
463 EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE
, minimize_button()->icon());
464 EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE
, close_button()->icon());