frame_size_button_unittest.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  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.
  4. #include "chromeos/ui/frame/caption_buttons/frame_size_button.h"
  5. #include "ash/shell.h"
  6. #include "ash/test/ash_test_base.h"
  7. #include "ash/wm/splitview/split_view_constants.h"
  8. #include "ash/wm/window_positioning_utils.h"
  9. #include "ash/wm/window_state.h"
  10. #include "ash/wm/wm_event.h"
  11. #include "base/check_op.h"
  12. #include "base/i18n/rtl.h"
  13. #include "base/run_loop.h"
  14. #include "base/test/scoped_feature_list.h"
  15. #include "chromeos/ui/base/window_properties.h"
  16. #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
  17. #include "chromeos/ui/frame/default_frame_header.h"
  18. #include "chromeos/ui/frame/multitask_menu/multitask_button.h"
  19. #include "chromeos/ui/frame/multitask_menu/multitask_menu.h"
  20. #include "chromeos/ui/frame/multitask_menu/split_button.h"
  21. #include "chromeos/ui/vector_icons/vector_icons.h"
  22. #include "chromeos/ui/wm/features.h"
  23. #include "ui/aura/window.h"
  24. #include "ui/display/display.h"
  25. #include "ui/display/screen.h"
  26. #include "ui/events/gesture_detection/gesture_configuration.h"
  27. #include "ui/events/test/event_generator.h"
  28. #include "ui/gfx/vector_icon_types.h"
  29. #include "ui/views/widget/widget.h"
  30. #include "ui/views/widget/widget_delegate.h"
  31. #include "ui/views/window/caption_button_layout_constants.h"
  32. #include "ui/views/window/frame_caption_button.h"
  33. #include "ui/views/window/vector_icons/vector_icons.h"
  34. namespace ash {
  35. namespace {
  36. using ::chromeos::FrameCaptionButtonContainerView;
  37. using ::chromeos::FrameSizeButton;
  38. using ::chromeos::MultitaskBaseButton;
  39. using ::chromeos::MultitaskMenu;
  40. using ::chromeos::SplitButtonView;
  41. using ::chromeos::WindowStateType;
  42. class TestWidgetDelegate : public views::WidgetDelegateView {
  43. public:
  44. explicit TestWidgetDelegate(bool resizable) {
  45. SetCanMaximize(true);
  46. SetCanMinimize(true);
  47. SetCanResize(resizable);
  48. }
  49. TestWidgetDelegate(const TestWidgetDelegate&) = delete;
  50. TestWidgetDelegate& operator=(const TestWidgetDelegate&) = delete;
  51. ~TestWidgetDelegate() override = default;
  52. FrameCaptionButtonContainerView* caption_button_container() {
  53. return caption_button_container_;
  54. }
  55. private:
  56. // Overridden from views::View:
  57. void Layout() override {
  58. caption_button_container_->Layout();
  59. // Right align the caption button container.
  60. gfx::Size preferred_size = caption_button_container_->GetPreferredSize();
  61. caption_button_container_->SetBounds(width() - preferred_size.width(), 0,
  62. preferred_size.width(),
  63. preferred_size.height());
  64. }
  65. void ViewHierarchyChanged(
  66. const views::ViewHierarchyChangedDetails& details) override {
  67. if (details.is_add && details.child == this) {
  68. caption_button_container_ =
  69. new FrameCaptionButtonContainerView(GetWidget());
  70. // Set images for the button icons and assign the default caption button
  71. // size.
  72. caption_button_container_->SetButtonSize(
  73. views::GetCaptionButtonLayoutSize(
  74. views::CaptionButtonLayoutSize::kNonBrowserCaption));
  75. caption_button_container_->SetButtonImage(
  76. views::CAPTION_BUTTON_ICON_MINIMIZE,
  77. views::kWindowControlMinimizeIcon);
  78. caption_button_container_->SetButtonImage(views::CAPTION_BUTTON_ICON_MENU,
  79. chromeos::kFloatWindowIcon);
  80. caption_button_container_->SetButtonImage(
  81. views::CAPTION_BUTTON_ICON_CLOSE, views::kWindowControlCloseIcon);
  82. caption_button_container_->SetButtonImage(
  83. views::CAPTION_BUTTON_ICON_LEFT_TOP_SNAPPED,
  84. chromeos::kWindowControlLeftSnappedIcon);
  85. caption_button_container_->SetButtonImage(
  86. views::CAPTION_BUTTON_ICON_RIGHT_BOTTOM_SNAPPED,
  87. chromeos::kWindowControlRightSnappedIcon);
  88. caption_button_container()->SetButtonImage(
  89. views::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE,
  90. views::kWindowControlMaximizeIcon);
  91. AddChildView(caption_button_container_);
  92. }
  93. }
  94. // Not owned.
  95. FrameCaptionButtonContainerView* caption_button_container_;
  96. };
  97. class FrameSizeButtonTest : public AshTestBase {
  98. public:
  99. FrameSizeButtonTest() = default;
  100. explicit FrameSizeButtonTest(bool resizable) : resizable_(resizable) {}
  101. FrameSizeButtonTest(const FrameSizeButtonTest&) = delete;
  102. FrameSizeButtonTest& operator=(const FrameSizeButtonTest&) = delete;
  103. ~FrameSizeButtonTest() override = default;
  104. // Returns the center point of |view| in screen coordinates.
  105. gfx::Point CenterPointInScreen(views::View* view) {
  106. return view->GetBoundsInScreen().CenterPoint();
  107. }
  108. // Returns true if the window has |state_type|.
  109. bool HasStateType(WindowStateType state_type) const {
  110. return window_state()->GetStateType() == state_type;
  111. }
  112. // Returns true if all three buttons are in the normal state.
  113. bool AllButtonsInNormalState() const {
  114. return minimize_button_->GetState() == views::Button::STATE_NORMAL &&
  115. size_button_->GetState() == views::Button::STATE_NORMAL &&
  116. close_button_->GetState() == views::Button::STATE_NORMAL;
  117. }
  118. // Creates a widget with |delegate|. The returned widget takes ownership of
  119. // |delegate|.
  120. views::Widget* CreateWidget(views::WidgetDelegate* delegate) {
  121. views::Widget* widget = new views::Widget;
  122. views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
  123. params.delegate = delegate;
  124. params.bounds = gfx::Rect(10, 10, 100, 100);
  125. params.context = GetContext();
  126. widget->Init(std::move(params));
  127. widget->Show();
  128. return widget;
  129. }
  130. // AshTestBase overrides:
  131. void SetUp() override {
  132. AshTestBase::SetUp();
  133. widget_delegate_ = new TestWidgetDelegate(resizable_);
  134. widget_ = CreateWidget(widget_delegate_);
  135. window_state_ = WindowState::Get(widget_->GetNativeWindow());
  136. FrameCaptionButtonContainerView::TestApi test(
  137. widget_delegate_->caption_button_container());
  138. minimize_button_ = test.minimize_button();
  139. size_button_ = test.size_button();
  140. static_cast<FrameSizeButton*>(size_button_)
  141. ->set_delay_to_set_buttons_to_snap_mode(0);
  142. close_button_ = test.close_button();
  143. }
  144. WindowState* window_state() { return window_state_; }
  145. const WindowState* window_state() const { return window_state_; }
  146. views::Widget* GetWidget() const { return widget_; }
  147. views::FrameCaptionButton* minimize_button() { return minimize_button_; }
  148. views::FrameCaptionButton* size_button() { return size_button_; }
  149. views::FrameCaptionButton* close_button() { return close_button_; }
  150. TestWidgetDelegate* widget_delegate() { return widget_delegate_; }
  151. private:
  152. // Not owned.
  153. WindowState* window_state_;
  154. views::Widget* widget_;
  155. views::FrameCaptionButton* minimize_button_;
  156. views::FrameCaptionButton* size_button_;
  157. views::FrameCaptionButton* close_button_;
  158. TestWidgetDelegate* widget_delegate_;
  159. bool resizable_ = true;
  160. };
  161. } // namespace
  162. // Tests that pressing the left mouse button or tapping down on the size button
  163. // puts the button into the pressed state.
  164. TEST_F(FrameSizeButtonTest, PressedState) {
  165. ui::test::EventGenerator* generator = GetEventGenerator();
  166. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  167. generator->PressLeftButton();
  168. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  169. generator->ReleaseLeftButton();
  170. base::RunLoop().RunUntilIdle();
  171. EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->GetState());
  172. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  173. generator->PressTouchId(3);
  174. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  175. generator->ReleaseTouchId(3);
  176. base::RunLoop().RunUntilIdle();
  177. EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->GetState());
  178. }
  179. // Tests that clicking on the size button toggles between the maximized and
  180. // normal state.
  181. TEST_F(FrameSizeButtonTest, ClickSizeButtonTogglesMaximize) {
  182. EXPECT_FALSE(window_state()->IsMaximized());
  183. ui::test::EventGenerator* generator = GetEventGenerator();
  184. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  185. generator->ClickLeftButton();
  186. base::RunLoop().RunUntilIdle();
  187. EXPECT_TRUE(window_state()->IsMaximized());
  188. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  189. generator->ClickLeftButton();
  190. base::RunLoop().RunUntilIdle();
  191. EXPECT_FALSE(window_state()->IsMaximized());
  192. generator->GestureTapAt(CenterPointInScreen(size_button()));
  193. base::RunLoop().RunUntilIdle();
  194. EXPECT_TRUE(window_state()->IsMaximized());
  195. generator->GestureTapAt(CenterPointInScreen(size_button()));
  196. base::RunLoop().RunUntilIdle();
  197. EXPECT_FALSE(window_state()->IsMaximized());
  198. }
  199. // Test that clicking + dragging to a button adjacent to the size button snaps
  200. // the window left or right.
  201. TEST_F(FrameSizeButtonTest, ButtonDrag) {
  202. EXPECT_TRUE(window_state()->IsNormalStateType());
  203. // 1) Test by dragging the mouse.
  204. // Snap right.
  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. base::RunLoop().RunUntilIdle();
  211. EXPECT_TRUE(HasStateType(WindowStateType::kSecondarySnapped));
  212. // Snap left.
  213. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  214. generator->PressLeftButton();
  215. generator->MoveMouseTo(CenterPointInScreen(minimize_button()));
  216. generator->ReleaseLeftButton();
  217. base::RunLoop().RunUntilIdle();
  218. EXPECT_TRUE(HasStateType(WindowStateType::kPrimarySnapped));
  219. // 2) Test with scroll gestures.
  220. // Snap right.
  221. generator->GestureScrollSequence(CenterPointInScreen(size_button()),
  222. CenterPointInScreen(close_button()),
  223. base::Milliseconds(100), 3);
  224. base::RunLoop().RunUntilIdle();
  225. EXPECT_TRUE(HasStateType(WindowStateType::kSecondarySnapped));
  226. // Snap left.
  227. generator->GestureScrollSequence(CenterPointInScreen(size_button()),
  228. CenterPointInScreen(minimize_button()),
  229. base::Milliseconds(100), 3);
  230. base::RunLoop().RunUntilIdle();
  231. EXPECT_TRUE(HasStateType(WindowStateType::kPrimarySnapped));
  232. // 3) Test with tap gestures.
  233. const float touch_default_radius =
  234. ui::GestureConfiguration::GetInstance()->default_radius();
  235. ui::GestureConfiguration::GetInstance()->set_default_radius(0);
  236. // Snap right.
  237. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  238. generator->PressMoveAndReleaseTouchTo(CenterPointInScreen(close_button()));
  239. base::RunLoop().RunUntilIdle();
  240. EXPECT_TRUE(HasStateType(WindowStateType::kSecondarySnapped));
  241. // Snap left.
  242. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  243. generator->PressMoveAndReleaseTouchTo(CenterPointInScreen(minimize_button()));
  244. base::RunLoop().RunUntilIdle();
  245. EXPECT_TRUE(HasStateType(WindowStateType::kPrimarySnapped));
  246. ui::GestureConfiguration::GetInstance()->set_default_radius(
  247. touch_default_radius);
  248. }
  249. // Test that clicking, dragging, and overshooting the minimize button a bit
  250. // horizontally still snaps the window left.
  251. TEST_F(FrameSizeButtonTest, SnapLeftOvershootMinimize) {
  252. EXPECT_TRUE(window_state()->IsNormalStateType());
  253. ui::test::EventGenerator* generator = GetEventGenerator();
  254. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  255. generator->PressLeftButton();
  256. // Move to the minimize button.
  257. generator->MoveMouseTo(CenterPointInScreen(minimize_button()));
  258. // Overshoot the minimize button.
  259. generator->MoveMouseBy(-minimize_button()->width(), 0);
  260. generator->ReleaseLeftButton();
  261. base::RunLoop().RunUntilIdle();
  262. EXPECT_TRUE(HasStateType(WindowStateType::kPrimarySnapped));
  263. }
  264. // Test that right clicking the size button has no effect.
  265. TEST_F(FrameSizeButtonTest, RightMouseButton) {
  266. EXPECT_TRUE(window_state()->IsNormalStateType());
  267. ui::test::EventGenerator* generator = GetEventGenerator();
  268. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  269. generator->PressRightButton();
  270. generator->ReleaseRightButton();
  271. base::RunLoop().RunUntilIdle();
  272. EXPECT_TRUE(window_state()->IsNormalStateType());
  273. }
  274. // Test that during the waiting to snap mode, if the window's state is changed,
  275. // or the window is put in overview, we should cancel the waiting to snap mode.
  276. TEST_F(FrameSizeButtonTest, CancelSnapTest) {
  277. EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->GetState());
  278. // Press on the size button and drag toward to close buton to enter waiting-
  279. // for-snap mode.
  280. ui::test::EventGenerator* generator = GetEventGenerator();
  281. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  282. generator->PressLeftButton();
  283. generator->MoveMouseTo(CenterPointInScreen(close_button()));
  284. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  285. EXPECT_TRUE(
  286. static_cast<FrameSizeButton*>(size_button())->in_snap_mode_for_testing());
  287. // Maximize the window.
  288. window_state()->Maximize();
  289. EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->GetState());
  290. EXPECT_FALSE(
  291. static_cast<FrameSizeButton*>(size_button())->in_snap_mode_for_testing());
  292. generator->ReleaseLeftButton();
  293. // Test that if window is put in overview, the waiting-to-snap is canceled.
  294. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  295. generator->PressLeftButton();
  296. generator->MoveMouseTo(CenterPointInScreen(close_button()));
  297. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  298. EXPECT_TRUE(
  299. static_cast<FrameSizeButton*>(size_button())->in_snap_mode_for_testing());
  300. window_state()->window()->SetProperty(chromeos::kIsShowingInOverviewKey,
  301. true);
  302. EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->GetState());
  303. EXPECT_FALSE(
  304. static_cast<FrameSizeButton*>(size_button())->in_snap_mode_for_testing());
  305. generator->ReleaseLeftButton();
  306. }
  307. // Test that upon releasing the mouse button after having pressed the size
  308. // button
  309. // - The state of all the caption buttons is reset.
  310. // - The icon displayed by all of the caption buttons is reset.
  311. TEST_F(FrameSizeButtonTest, ResetButtonsAfterClick) {
  312. EXPECT_EQ(views::CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->GetIcon());
  313. EXPECT_EQ(views::CAPTION_BUTTON_ICON_CLOSE, close_button()->GetIcon());
  314. EXPECT_TRUE(AllButtonsInNormalState());
  315. // Pressing the size button should result in the size button being pressed and
  316. // the minimize and close button icons changing.
  317. ui::test::EventGenerator* generator = GetEventGenerator();
  318. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  319. generator->PressLeftButton();
  320. EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->GetState());
  321. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  322. EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->GetState());
  323. EXPECT_EQ(views::CAPTION_BUTTON_ICON_LEFT_TOP_SNAPPED,
  324. minimize_button()->GetIcon());
  325. EXPECT_TRUE(chromeos::kWindowControlLeftSnappedIcon.name ==
  326. minimize_button()->icon_definition_for_test()->name);
  327. EXPECT_EQ(views::CAPTION_BUTTON_ICON_RIGHT_BOTTOM_SNAPPED,
  328. close_button()->GetIcon());
  329. EXPECT_TRUE(chromeos::kWindowControlRightSnappedIcon.name ==
  330. close_button()->icon_definition_for_test()->name);
  331. // Dragging the mouse over the minimize button should hover the minimize
  332. // button and the minimize and close button icons should stay changed.
  333. generator->MoveMouseTo(CenterPointInScreen(minimize_button()));
  334. EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->GetState());
  335. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  336. EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->GetState());
  337. EXPECT_EQ(views::CAPTION_BUTTON_ICON_LEFT_TOP_SNAPPED,
  338. minimize_button()->GetIcon());
  339. EXPECT_EQ(views::CAPTION_BUTTON_ICON_RIGHT_BOTTOM_SNAPPED,
  340. close_button()->GetIcon());
  341. // Release the mouse, snapping the window left.
  342. generator->ReleaseLeftButton();
  343. base::RunLoop().RunUntilIdle();
  344. EXPECT_TRUE(HasStateType(WindowStateType::kPrimarySnapped));
  345. // None of the buttons should stay pressed and the buttons should have their
  346. // regular icons.
  347. EXPECT_TRUE(AllButtonsInNormalState());
  348. EXPECT_EQ(views::CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->GetIcon());
  349. EXPECT_EQ(views::CAPTION_BUTTON_ICON_CLOSE, close_button()->GetIcon());
  350. // Repeat test but release button where it does not affect the window's state
  351. // because the code path is different.
  352. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  353. generator->PressLeftButton();
  354. EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->GetState());
  355. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  356. EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->GetState());
  357. EXPECT_EQ(views::CAPTION_BUTTON_ICON_LEFT_TOP_SNAPPED,
  358. minimize_button()->GetIcon());
  359. EXPECT_EQ(views::CAPTION_BUTTON_ICON_RIGHT_BOTTOM_SNAPPED,
  360. close_button()->GetIcon());
  361. const gfx::Rect work_area_bounds_in_screen =
  362. display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
  363. generator->MoveMouseTo(work_area_bounds_in_screen.bottom_left());
  364. // None of the buttons should be pressed because we are really far away from
  365. // any of the caption buttons. The minimize and close button icons should
  366. // be changed because the mouse is pressed.
  367. EXPECT_TRUE(AllButtonsInNormalState());
  368. EXPECT_EQ(views::CAPTION_BUTTON_ICON_LEFT_TOP_SNAPPED,
  369. minimize_button()->GetIcon());
  370. EXPECT_EQ(views::CAPTION_BUTTON_ICON_RIGHT_BOTTOM_SNAPPED,
  371. close_button()->GetIcon());
  372. // Release the mouse. The window should stay snapped left.
  373. generator->ReleaseLeftButton();
  374. base::RunLoop().RunUntilIdle();
  375. EXPECT_TRUE(HasStateType(WindowStateType::kPrimarySnapped));
  376. // The buttons should stay unpressed and the buttons should now have their
  377. // regular icons.
  378. EXPECT_TRUE(AllButtonsInNormalState());
  379. EXPECT_EQ(views::CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->GetIcon());
  380. EXPECT_EQ(views::CAPTION_BUTTON_ICON_CLOSE, close_button()->GetIcon());
  381. }
  382. // Test that the size button is pressed whenever the snap left/right buttons
  383. // are hovered.
  384. TEST_F(FrameSizeButtonTest, SizeButtonPressedWhenSnapButtonHovered) {
  385. EXPECT_EQ(views::CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->GetIcon());
  386. EXPECT_EQ(views::CAPTION_BUTTON_ICON_CLOSE, close_button()->GetIcon());
  387. EXPECT_TRUE(AllButtonsInNormalState());
  388. // Pressing the size button should result in the size button being pressed and
  389. // the minimize and close button icons changing.
  390. ui::test::EventGenerator* generator = GetEventGenerator();
  391. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  392. generator->PressLeftButton();
  393. EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->GetState());
  394. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  395. EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->GetState());
  396. EXPECT_EQ(views::CAPTION_BUTTON_ICON_LEFT_TOP_SNAPPED,
  397. minimize_button()->GetIcon());
  398. EXPECT_EQ(views::CAPTION_BUTTON_ICON_RIGHT_BOTTOM_SNAPPED,
  399. close_button()->GetIcon());
  400. // Dragging the mouse over the minimize button (snap left button) should hover
  401. // the minimize button and keep the size button pressed.
  402. generator->MoveMouseTo(CenterPointInScreen(minimize_button()));
  403. EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->GetState());
  404. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  405. EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->GetState());
  406. // Moving the mouse far away from the caption buttons and then moving it over
  407. // the close button (snap right button) should hover the close button and
  408. // keep the size button pressed.
  409. const gfx::Rect work_area_bounds_in_screen =
  410. display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
  411. generator->MoveMouseTo(work_area_bounds_in_screen.bottom_left());
  412. EXPECT_TRUE(AllButtonsInNormalState());
  413. generator->MoveMouseTo(CenterPointInScreen(close_button()));
  414. EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->GetState());
  415. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  416. EXPECT_EQ(views::Button::STATE_HOVERED, close_button()->GetState());
  417. }
  418. class FrameSizeButtonTestRTL : public FrameSizeButtonTest {
  419. public:
  420. FrameSizeButtonTestRTL() = default;
  421. FrameSizeButtonTestRTL(const FrameSizeButtonTestRTL&) = delete;
  422. FrameSizeButtonTestRTL& operator=(const FrameSizeButtonTestRTL&) = delete;
  423. ~FrameSizeButtonTestRTL() override = default;
  424. void SetUp() override {
  425. original_locale_ = base::i18n::GetConfiguredLocale();
  426. base::i18n::SetICUDefaultLocale("he");
  427. FrameSizeButtonTest::SetUp();
  428. }
  429. void TearDown() override {
  430. FrameSizeButtonTest::TearDown();
  431. base::i18n::SetICUDefaultLocale(original_locale_);
  432. }
  433. private:
  434. std::string original_locale_;
  435. };
  436. // Test that clicking + dragging to a button adjacent to the size button presses
  437. // the correct button and snaps the window to the correct side.
  438. TEST_F(FrameSizeButtonTestRTL, ButtonDrag) {
  439. // In RTL the close button should be left of the size button and the minimize
  440. // button should be right of the size button.
  441. ASSERT_LT(close_button()->GetBoundsInScreen().x(),
  442. size_button()->GetBoundsInScreen().x());
  443. ASSERT_LT(size_button()->GetBoundsInScreen().x(),
  444. minimize_button()->GetBoundsInScreen().x());
  445. // Test initial state.
  446. EXPECT_TRUE(window_state()->IsNormalStateType());
  447. EXPECT_TRUE(AllButtonsInNormalState());
  448. EXPECT_EQ(views::CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->GetIcon());
  449. EXPECT_EQ(views::CAPTION_BUTTON_ICON_CLOSE, close_button()->GetIcon());
  450. // Pressing the size button should swap the icons of the minimize and close
  451. // buttons to icons for snapping right and for snapping left respectively.
  452. ui::test::EventGenerator* generator = GetEventGenerator();
  453. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  454. generator->PressLeftButton();
  455. EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->GetState());
  456. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  457. EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->GetState());
  458. EXPECT_EQ(views::CAPTION_BUTTON_ICON_RIGHT_BOTTOM_SNAPPED,
  459. minimize_button()->GetIcon());
  460. EXPECT_EQ(views::CAPTION_BUTTON_ICON_LEFT_TOP_SNAPPED,
  461. close_button()->GetIcon());
  462. // Dragging over to the minimize button should press it.
  463. generator->MoveMouseTo(CenterPointInScreen(minimize_button()));
  464. EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->GetState());
  465. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  466. EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->GetState());
  467. // Releasing should snap the window right.
  468. generator->ReleaseLeftButton();
  469. base::RunLoop().RunUntilIdle();
  470. EXPECT_TRUE(HasStateType(WindowStateType::kSecondarySnapped));
  471. // None of the buttons should stay pressed and the buttons should have their
  472. // regular icons.
  473. EXPECT_TRUE(AllButtonsInNormalState());
  474. EXPECT_EQ(views::CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->GetIcon());
  475. EXPECT_EQ(views::CAPTION_BUTTON_ICON_CLOSE, close_button()->GetIcon());
  476. }
  477. namespace {
  478. class FrameSizeButtonNonResizableTest : public FrameSizeButtonTest {
  479. public:
  480. FrameSizeButtonNonResizableTest() : FrameSizeButtonTest(false) {}
  481. FrameSizeButtonNonResizableTest(const FrameSizeButtonNonResizableTest&) =
  482. delete;
  483. FrameSizeButtonNonResizableTest& operator=(
  484. const FrameSizeButtonNonResizableTest&) = delete;
  485. ~FrameSizeButtonNonResizableTest() override {}
  486. };
  487. } // namespace
  488. TEST_F(FrameSizeButtonNonResizableTest, NoSnap) {
  489. EXPECT_EQ(views::CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->GetIcon());
  490. EXPECT_EQ(views::CAPTION_BUTTON_ICON_CLOSE, close_button()->GetIcon());
  491. EXPECT_TRUE(AllButtonsInNormalState());
  492. // Pressing the size button should result in the size button being pressed and
  493. // the minimize and close button icons changing.
  494. ui::test::EventGenerator* generator = GetEventGenerator();
  495. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  496. generator->PressLeftButton();
  497. EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->GetState());
  498. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  499. EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->GetState());
  500. EXPECT_EQ(views::CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->GetIcon());
  501. EXPECT_EQ(views::CAPTION_BUTTON_ICON_CLOSE, close_button()->GetIcon());
  502. }
  503. // FrameSizeButtonPortraitDisplayTest is used to test functionalities to snap
  504. // top and bottom in portrait layout, affecting snap icons.
  505. using FrameSizeButtonPortraitDisplayTest = FrameSizeButtonTest;
  506. // Test that upon pressed the size button should show left and right arrows for
  507. // horizontal snap and upward and downward arrow for vertical snap.
  508. TEST_F(FrameSizeButtonPortraitDisplayTest, SnapButtons) {
  509. UpdateDisplay("600x800");
  510. FrameCaptionButtonContainerView* container =
  511. widget_delegate()->caption_button_container();
  512. views::Widget* widget = widget_delegate()->GetWidget();
  513. chromeos::DefaultFrameHeader frame_header(
  514. widget, widget->non_client_view()->frame_view(), container);
  515. frame_header.LayoutHeader();
  516. EXPECT_EQ(views::CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->GetIcon());
  517. EXPECT_EQ(views::CAPTION_BUTTON_ICON_CLOSE, close_button()->GetIcon());
  518. EXPECT_TRUE(AllButtonsInNormalState());
  519. // Pressing the size button should result in the size button being pressed and
  520. // the minimize and close button icons changing.
  521. ui::test::EventGenerator* generator = GetEventGenerator();
  522. generator->MoveMouseTo(CenterPointInScreen(size_button()));
  523. generator->PressLeftButton();
  524. EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->GetState());
  525. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  526. EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->GetState());
  527. EXPECT_EQ(views::CAPTION_BUTTON_ICON_LEFT_TOP_SNAPPED,
  528. minimize_button()->GetIcon());
  529. EXPECT_EQ(views::CAPTION_BUTTON_ICON_RIGHT_BOTTOM_SNAPPED,
  530. close_button()->GetIcon());
  531. const gfx::VectorIcon* left_icon = &chromeos::kWindowControlTopSnappedIcon;
  532. const gfx::VectorIcon* right_icon =
  533. &chromeos::kWindowControlBottomSnappedIcon;
  534. EXPECT_TRUE(left_icon->name ==
  535. minimize_button()->icon_definition_for_test()->name);
  536. EXPECT_TRUE(right_icon->name ==
  537. close_button()->icon_definition_for_test()->name);
  538. // Dragging the mouse over the minimize button should hover the minimize
  539. // button (snap top/left). The minimize and close button icons should stay
  540. // changed.
  541. generator->MoveMouseTo(CenterPointInScreen(minimize_button()));
  542. EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->GetState());
  543. EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->GetState());
  544. EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->GetState());
  545. EXPECT_EQ(views::CAPTION_BUTTON_ICON_LEFT_TOP_SNAPPED,
  546. minimize_button()->GetIcon());
  547. EXPECT_EQ(views::CAPTION_BUTTON_ICON_RIGHT_BOTTOM_SNAPPED,
  548. close_button()->GetIcon());
  549. // Release the mouse, snapping the window to the primary position.
  550. generator->ReleaseLeftButton();
  551. base::RunLoop().RunUntilIdle();
  552. EXPECT_TRUE(HasStateType(WindowStateType::kPrimarySnapped));
  553. }
  554. // Test multitask menu requires kFloatWindow feature to be enabled during setup.
  555. class MultitaskMenuTest : public FrameSizeButtonTest {
  556. public:
  557. MultitaskMenuTest() = default;
  558. MultitaskMenuTest(const MultitaskMenuTest&) = delete;
  559. MultitaskMenuTest& operator=(const MultitaskMenuTest&) = delete;
  560. ~MultitaskMenuTest() override = default;
  561. void SetUp() override {
  562. // Ensure float feature is enabled.
  563. scoped_feature_list_.InitAndEnableFeature(
  564. chromeos::wm::features::kFloatWindow);
  565. FrameSizeButtonTest::SetUp();
  566. }
  567. void ShowMultitaskMenu() {
  568. DCHECK(size_button());
  569. multitask_menu_ = static_cast<FrameSizeButton*>(size_button())
  570. ->GetMultitaskMenuForTesting();
  571. }
  572. MultitaskMenu* multitask_menu() { return multitask_menu_; }
  573. private:
  574. MultitaskMenu* multitask_menu_;
  575. base::test::ScopedFeatureList scoped_feature_list_;
  576. };
  577. // Test Float Button Functionality.
  578. TEST_F(MultitaskMenuTest, TestMultitaskMenuFloatFunctionality) {
  579. EXPECT_TRUE(window_state()->IsNormalStateType());
  580. ui::test::EventGenerator* generator = GetEventGenerator();
  581. ShowMultitaskMenu();
  582. generator->MoveMouseTo(
  583. CenterPointInScreen(multitask_menu()
  584. ->multitask_menu_view_for_testing()
  585. ->float_button_for_testing()));
  586. generator->ClickLeftButton();
  587. EXPECT_TRUE(window_state()->IsFloated());
  588. }
  589. // Test Half Button Functionality.
  590. TEST_F(MultitaskMenuTest, TestMultitaskMenuHalfFunctionality) {
  591. EXPECT_TRUE(window_state()->IsNormalStateType());
  592. ui::test::EventGenerator* generator = GetEventGenerator();
  593. ShowMultitaskMenu();
  594. generator->MoveMouseTo(multitask_menu()
  595. ->multitask_menu_view_for_testing()
  596. ->half_button_for_testing()
  597. ->GetBoundsInScreen()
  598. .left_center());
  599. generator->ClickLeftButton();
  600. EXPECT_TRUE(window_state()->GetStateType() ==
  601. WindowStateType::kPrimarySnapped);
  602. }
  603. // Test Partial Split Button Functionality.
  604. TEST_F(MultitaskMenuTest, TestMultitaskMenuPartialSplit) {
  605. EXPECT_TRUE(window_state()->IsNormalStateType());
  606. ui::test::EventGenerator* generator = GetEventGenerator();
  607. const gfx::Rect work_area_bounds_in_screen =
  608. display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
  609. // Snap to primary with 0.67f screen ratio.
  610. ShowMultitaskMenu();
  611. generator->MoveMouseTo(multitask_menu()
  612. ->multitask_menu_view_for_testing()
  613. ->partial_button_for_testing()
  614. ->GetBoundsInScreen()
  615. .left_center());
  616. generator->ClickLeftButton();
  617. EXPECT_TRUE(window_state()->GetStateType() ==
  618. WindowStateType::kPrimarySnapped);
  619. EXPECT_EQ(window_state()->window()->bounds().width(),
  620. work_area_bounds_in_screen.width() * 0.67);
  621. // Snap to secondary with 0.33f screen ratio.
  622. ShowMultitaskMenu();
  623. gfx::Rect partial_bounds(multitask_menu()
  624. ->multitask_menu_view_for_testing()
  625. ->partial_button_for_testing()
  626. ->GetBoundsInScreen());
  627. gfx::Point secondary_center(
  628. gfx::Point(partial_bounds.x() + partial_bounds.width() * 0.67f,
  629. partial_bounds.y() + partial_bounds.y() / 2));
  630. generator->MoveMouseTo(secondary_center);
  631. generator->ClickLeftButton();
  632. EXPECT_TRUE(window_state()->GetStateType() ==
  633. WindowStateType::kSecondarySnapped);
  634. EXPECT_EQ(window_state()->window()->bounds().width(),
  635. work_area_bounds_in_screen.width() * 0.33);
  636. }
  637. // Test Full Button Functionality.
  638. TEST_F(MultitaskMenuTest, TestMultitaskMenuFullFunctionality) {
  639. ASSERT_TRUE(window_state()->IsNormalStateType());
  640. ui::test::EventGenerator* generator = GetEventGenerator();
  641. ShowMultitaskMenu();
  642. generator->MoveMouseTo(
  643. CenterPointInScreen(multitask_menu()
  644. ->multitask_menu_view_for_testing()
  645. ->full_button_for_testing()));
  646. generator->ClickLeftButton();
  647. EXPECT_TRUE(window_state()->IsFullscreen());
  648. }
  649. TEST_F(MultitaskMenuTest, MultitaskMenuClosesOnTabletMode) {
  650. ShowMultitaskMenu();
  651. ASSERT_TRUE(multitask_menu()->GetWidget());
  652. ash::TabletMode::Get()->SetEnabledForTest(true);
  653. EXPECT_FALSE(multitask_menu()->GetWidget());
  654. }
  655. } // namespace ash