non_client_frame_view_ash_unittest.cc 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  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 "ash/frame/non_client_frame_view_ash.h"
  5. #include <memory>
  6. #include "ash/accelerators/accelerator_controller_impl.h"
  7. #include "ash/constants/ash_pref_names.h"
  8. #include "ash/frame/header_view.h"
  9. #include "ash/frame/wide_frame_view.h"
  10. #include "ash/resources/vector_icons/vector_icons.h"
  11. #include "ash/session/session_controller_impl.h"
  12. #include "ash/shell.h"
  13. #include "ash/style/ash_color_provider.h"
  14. #include "ash/style/dark_light_mode_controller_impl.h"
  15. #include "ash/test/ash_test_base.h"
  16. #include "ash/test/test_widget_builder.h"
  17. #include "ash/wm/desks/desks_util.h"
  18. #include "ash/wm/overview/overview_controller.h"
  19. #include "ash/wm/splitview/split_view_controller.h"
  20. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  21. #include "ash/wm/window_state.h"
  22. #include "ash/wm/window_state_delegate.h"
  23. #include "ash/wm/window_util.h"
  24. #include "ash/wm/wm_event.h"
  25. #include "base/command_line.h"
  26. #include "base/containers/flat_set.h"
  27. #include "base/test/scoped_feature_list.h"
  28. #include "chromeos/constants/chromeos_features.h"
  29. #include "chromeos/ui/base/window_properties.h"
  30. #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
  31. #include "chromeos/ui/frame/default_frame_header.h"
  32. #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
  33. #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
  34. #include "chromeos/ui/vector_icons/vector_icons.h"
  35. #include "ui/aura/client/aura_constants.h"
  36. #include "ui/aura/window.h"
  37. #include "ui/aura/window_targeter.h"
  38. #include "ui/base/accelerators/accelerator.h"
  39. #include "ui/base/accelerators/test_accelerator_target.h"
  40. #include "ui/base/ui_base_features.h"
  41. #include "ui/compositor/layer.h"
  42. #include "ui/compositor/test/draw_waiter_for_test.h"
  43. #include "ui/events/test/event_generator.h"
  44. #include "ui/gfx/geometry/rect.h"
  45. #include "ui/gfx/image/image_skia.h"
  46. #include "ui/gfx/image/image_unittest_util.h"
  47. #include "ui/gfx/vector_icon_types.h"
  48. #include "ui/views/widget/widget.h"
  49. #include "ui/views/widget/widget_delegate.h"
  50. #include "ui/views/window/caption_button_layout_constants.h"
  51. #include "ui/views/window/frame_caption_button.h"
  52. #include "ui/views/window/vector_icons/vector_icons.h"
  53. #include "ui/wm/core/window_util.h"
  54. namespace ash {
  55. using ::chromeos::DefaultFrameHeader;
  56. using ::chromeos::FrameCaptionButtonContainerView;
  57. using ::chromeos::ImmersiveFullscreenController;
  58. using ::chromeos::ImmersiveFullscreenControllerDelegate;
  59. using ::chromeos::ImmersiveFullscreenControllerTestApi;
  60. using ::chromeos::kFrameActiveColorKey;
  61. using ::chromeos::kFrameInactiveColorKey;
  62. using ::chromeos::kTrackDefaultFrameColors;
  63. // A views::WidgetDelegate which uses a NonClientFrameViewAsh.
  64. class NonClientFrameViewAshTestWidgetDelegate
  65. : public views::WidgetDelegateView {
  66. public:
  67. NonClientFrameViewAshTestWidgetDelegate() = default;
  68. NonClientFrameViewAshTestWidgetDelegate(
  69. const NonClientFrameViewAshTestWidgetDelegate&) = delete;
  70. NonClientFrameViewAshTestWidgetDelegate& operator=(
  71. const NonClientFrameViewAshTestWidgetDelegate&) = delete;
  72. ~NonClientFrameViewAshTestWidgetDelegate() override = default;
  73. std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView(
  74. views::Widget* widget) override {
  75. auto non_client_frame_view =
  76. std::make_unique<NonClientFrameViewAsh>(widget);
  77. non_client_frame_view_ = non_client_frame_view.get();
  78. return non_client_frame_view;
  79. }
  80. int GetNonClientFrameViewTopBorderHeight() {
  81. return non_client_frame_view_->NonClientTopBorderHeight();
  82. }
  83. NonClientFrameViewAsh* non_client_frame_view() const {
  84. return non_client_frame_view_;
  85. }
  86. HeaderView* header_view() const {
  87. return non_client_frame_view_->header_view_;
  88. }
  89. private:
  90. // Not owned.
  91. NonClientFrameViewAsh* non_client_frame_view_ = nullptr;
  92. };
  93. class TestWidgetConstraintsDelegate
  94. : public NonClientFrameViewAshTestWidgetDelegate {
  95. public:
  96. TestWidgetConstraintsDelegate() {
  97. SetCanMaximize(true);
  98. SetCanMinimize(true);
  99. }
  100. TestWidgetConstraintsDelegate(const TestWidgetConstraintsDelegate&) = delete;
  101. TestWidgetConstraintsDelegate& operator=(
  102. const TestWidgetConstraintsDelegate&) = delete;
  103. ~TestWidgetConstraintsDelegate() override = default;
  104. // views::View:
  105. gfx::Size GetMinimumSize() const override { return minimum_size_; }
  106. gfx::Size GetMaximumSize() const override { return maximum_size_; }
  107. views::View* GetContentsView() override {
  108. // Set this instance as the contents view so that the maximum and minimum
  109. // size constraints will be used.
  110. return this;
  111. }
  112. // views::WidgetDelegate:
  113. void set_minimum_size(const gfx::Size& min_size) { minimum_size_ = min_size; }
  114. void set_maximum_size(const gfx::Size& max_size) { maximum_size_ = max_size; }
  115. const gfx::Rect& GetFrameCaptionButtonContainerViewBounds() {
  116. return non_client_frame_view()
  117. ->GetFrameCaptionButtonContainerViewForTest()
  118. ->bounds();
  119. }
  120. void EndFrameCaptionButtonContainerViewAnimations() {
  121. FrameCaptionButtonContainerView::TestApi test(
  122. non_client_frame_view()->GetFrameCaptionButtonContainerViewForTest());
  123. test.EndAnimations();
  124. }
  125. int GetTitleBarHeight() const {
  126. return non_client_frame_view()->NonClientTopBorderHeight();
  127. }
  128. private:
  129. gfx::Size minimum_size_;
  130. gfx::Size maximum_size_;
  131. };
  132. using NonClientFrameViewAshTest = AshTestBase;
  133. // Verifies the client view is not placed at a y location of 0.
  134. TEST_F(NonClientFrameViewAshTest, ClientViewCorrectlyPlaced) {
  135. std::unique_ptr<views::Widget> widget =
  136. CreateTestWidget(new NonClientFrameViewAshTestWidgetDelegate);
  137. EXPECT_NE(0, widget->client_view()->bounds().y());
  138. }
  139. // Test that the height of the header is correct upon initially displaying
  140. // the widget.
  141. TEST_F(NonClientFrameViewAshTest, HeaderHeight) {
  142. NonClientFrameViewAshTestWidgetDelegate* delegate =
  143. new NonClientFrameViewAshTestWidgetDelegate;
  144. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  145. // The header should have enough room for the window controls. The
  146. // header/content separator line overlays the window controls.
  147. EXPECT_EQ(views::GetCaptionButtonLayoutSize(
  148. views::CaptionButtonLayoutSize::kNonBrowserCaption)
  149. .height(),
  150. delegate->non_client_frame_view()->GetHeaderView()->height());
  151. }
  152. // Regression test for https://crbug.com/839955
  153. TEST_F(NonClientFrameViewAshTest, ActiveStateOfButtonMatchesWidget) {
  154. NonClientFrameViewAshTestWidgetDelegate* delegate =
  155. new NonClientFrameViewAshTestWidgetDelegate;
  156. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  157. FrameCaptionButtonContainerView::TestApi test_api(
  158. delegate->non_client_frame_view()
  159. ->GetHeaderView()
  160. ->caption_button_container());
  161. widget->Show();
  162. EXPECT_TRUE(widget->IsActive());
  163. // The paint state doesn't change till the next paint.
  164. ui::DrawWaiterForTest::WaitForCommit(widget->GetLayer()->GetCompositor());
  165. EXPECT_TRUE(test_api.size_button()->GetPaintAsActive());
  166. // Activate a different widget so the original one loses activation.
  167. std::unique_ptr<views::Widget> widget2 =
  168. CreateTestWidget(new NonClientFrameViewAshTestWidgetDelegate);
  169. widget2->Show();
  170. ui::DrawWaiterForTest::WaitForCommit(widget->GetLayer()->GetCompositor());
  171. EXPECT_FALSE(widget->IsActive());
  172. EXPECT_FALSE(test_api.size_button()->GetPaintAsActive());
  173. }
  174. // Verify that NonClientFrameViewAsh returns the correct minimum and maximum
  175. // frame sizes when the client view does not specify any size constraints.
  176. TEST_F(NonClientFrameViewAshTest, NoSizeConstraints) {
  177. TestWidgetConstraintsDelegate* delegate = new TestWidgetConstraintsDelegate;
  178. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  179. NonClientFrameViewAsh* non_client_frame_view =
  180. delegate->non_client_frame_view();
  181. gfx::Size min_frame_size = non_client_frame_view->GetMinimumSize();
  182. gfx::Size max_frame_size = non_client_frame_view->GetMaximumSize();
  183. EXPECT_EQ(delegate->GetTitleBarHeight(), min_frame_size.height());
  184. // A width and height constraint of 0 denotes unbounded.
  185. EXPECT_EQ(0, max_frame_size.width());
  186. EXPECT_EQ(0, max_frame_size.height());
  187. }
  188. // Verify that NonClientFrameViewAsh returns the correct minimum and maximum
  189. // frame sizes when the client view specifies size constraints.
  190. TEST_F(NonClientFrameViewAshTest, MinimumAndMaximumSize) {
  191. gfx::Size min_client_size(500, 500);
  192. gfx::Size max_client_size(800, 800);
  193. TestWidgetConstraintsDelegate* delegate = new TestWidgetConstraintsDelegate;
  194. delegate->set_minimum_size(min_client_size);
  195. delegate->set_maximum_size(max_client_size);
  196. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  197. NonClientFrameViewAsh* non_client_frame_view =
  198. delegate->non_client_frame_view();
  199. gfx::Size min_frame_size = non_client_frame_view->GetMinimumSize();
  200. gfx::Size max_frame_size = non_client_frame_view->GetMaximumSize();
  201. EXPECT_EQ(min_client_size.width(), min_frame_size.width());
  202. EXPECT_EQ(max_client_size.width(), max_frame_size.width());
  203. EXPECT_EQ(min_client_size.height() + delegate->GetTitleBarHeight(),
  204. min_frame_size.height());
  205. EXPECT_EQ(max_client_size.height() + delegate->GetTitleBarHeight(),
  206. max_frame_size.height());
  207. }
  208. // Verify that NonClientFrameViewAsh updates the avatar icon based on the
  209. // avatar icon window property.
  210. TEST_F(NonClientFrameViewAshTest, AvatarIcon) {
  211. TestWidgetConstraintsDelegate* delegate = new TestWidgetConstraintsDelegate;
  212. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  213. NonClientFrameViewAsh* non_client_frame_view =
  214. delegate->non_client_frame_view();
  215. EXPECT_FALSE(non_client_frame_view->GetAvatarIconViewForTest());
  216. // Avatar image becomes available.
  217. widget->GetNativeWindow()->SetProperty(
  218. aura::client::kAvatarIconKey,
  219. new gfx::ImageSkia(gfx::test::CreateImage(27, 27).AsImageSkia()));
  220. EXPECT_TRUE(non_client_frame_view->GetAvatarIconViewForTest());
  221. // Avatar image is gone; the ImageView for the avatar icon should be
  222. // removed.
  223. widget->GetNativeWindow()->ClearProperty(aura::client::kAvatarIconKey);
  224. EXPECT_FALSE(non_client_frame_view->GetAvatarIconViewForTest());
  225. }
  226. // Tests that a window is minimized, toggling tablet mode doesn't trigger
  227. // caption button update (https://crbug.com/822890).
  228. TEST_F(NonClientFrameViewAshTest, ToggleTabletModeOnMinimizedWindow) {
  229. NonClientFrameViewAshTestWidgetDelegate* delegate =
  230. new NonClientFrameViewAshTestWidgetDelegate;
  231. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  232. FrameCaptionButtonContainerView::TestApi test(
  233. delegate->non_client_frame_view()
  234. ->GetHeaderView()
  235. ->caption_button_container());
  236. widget->Maximize();
  237. // Restore icon for size button in maximized window state. Compare by name
  238. // because the address may not be the same for different build targets in the
  239. // component build.
  240. EXPECT_STREQ(views::kWindowControlRestoreIcon.name,
  241. test.size_button()->icon_definition_for_test()->name);
  242. widget->Minimize();
  243. // Enter and exit tablet mode while the window is minimized.
  244. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  245. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
  246. // When unminimizing in non-tablet mode, size button should match with
  247. // maximized window state, which is restore icon.
  248. ::wm::Unminimize(widget->GetNativeWindow());
  249. EXPECT_TRUE(widget->IsMaximized());
  250. EXPECT_STREQ(views::kWindowControlRestoreIcon.name,
  251. test.size_button()->icon_definition_for_test()->name);
  252. }
  253. // Verify that when in tablet mode with a maximized window, the height of the
  254. // header is zero.
  255. TEST_F(NonClientFrameViewAshTest, FrameHiddenInTabletModeForMaximizedWindows) {
  256. NonClientFrameViewAshTestWidgetDelegate* delegate =
  257. new NonClientFrameViewAshTestWidgetDelegate;
  258. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  259. widget->Maximize();
  260. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  261. EXPECT_EQ(0, delegate->GetNonClientFrameViewTopBorderHeight());
  262. }
  263. // Verify that when in tablet mode with a non maximized window, the height of
  264. // the header is non zero.
  265. TEST_F(NonClientFrameViewAshTest,
  266. FrameShownInTabletModeForNonMaximizedWindows) {
  267. auto* delegate = new NonClientFrameViewAshTestWidgetDelegate();
  268. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  269. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  270. EXPECT_EQ(views::GetCaptionButtonLayoutSize(
  271. views::CaptionButtonLayoutSize::kNonBrowserCaption)
  272. .height(),
  273. delegate->GetNonClientFrameViewTopBorderHeight());
  274. }
  275. // Verify that if originally in fullscreen mode, and enter tablet mode, the
  276. // height of the header remains zero.
  277. TEST_F(NonClientFrameViewAshTest,
  278. FrameRemainsHiddenInTabletModeWhenTogglingFullscreen) {
  279. NonClientFrameViewAshTestWidgetDelegate* delegate =
  280. new NonClientFrameViewAshTestWidgetDelegate;
  281. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  282. widget->SetFullscreen(true);
  283. EXPECT_EQ(0, delegate->GetNonClientFrameViewTopBorderHeight());
  284. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  285. EXPECT_EQ(0, delegate->GetNonClientFrameViewTopBorderHeight());
  286. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
  287. EXPECT_EQ(0, delegate->GetNonClientFrameViewTopBorderHeight());
  288. }
  289. TEST_F(NonClientFrameViewAshTest, OpeningAppsInTabletMode) {
  290. auto* delegate = new TestWidgetConstraintsDelegate;
  291. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  292. widget->Maximize();
  293. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  294. EXPECT_EQ(0, delegate->GetNonClientFrameViewTopBorderHeight());
  295. // Verify that after minimizing and showing the widget, the height of the
  296. // header is zero.
  297. widget->Minimize();
  298. widget->Show();
  299. EXPECT_TRUE(widget->IsMaximized());
  300. EXPECT_EQ(0, delegate->GetNonClientFrameViewTopBorderHeight());
  301. // Verify that when we toggle maximize, the header is shown. For example,
  302. // maximized can be toggled in tablet mode by using the accessibility
  303. // keyboard.
  304. WMEvent event(WM_EVENT_TOGGLE_MAXIMIZE);
  305. WindowState::Get(widget->GetNativeWindow())->OnWMEvent(&event);
  306. EXPECT_EQ(0, delegate->GetNonClientFrameViewTopBorderHeight());
  307. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
  308. EXPECT_EQ(views::GetCaptionButtonLayoutSize(
  309. views::CaptionButtonLayoutSize::kNonBrowserCaption)
  310. .height(),
  311. delegate->GetNonClientFrameViewTopBorderHeight());
  312. }
  313. // Test if creating a new window in tablet mode uses maximzied state
  314. // and immersive mode.
  315. TEST_F(NonClientFrameViewAshTest, GetPreferredOnScreenHeightInTabletMaximzied) {
  316. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  317. auto* delegate = new TestWidgetConstraintsDelegate;
  318. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  319. auto* frame_view = static_cast<NonClientFrameViewAsh*>(
  320. widget->non_client_view()->frame_view());
  321. auto* header_view = frame_view->GetHeaderView();
  322. ASSERT_TRUE(widget->IsMaximized());
  323. EXPECT_TRUE(header_view->in_immersive_mode());
  324. static_cast<ImmersiveFullscreenControllerDelegate*>(header_view)
  325. ->SetVisibleFraction(0.5);
  326. // The height should be ~(33 *.5)
  327. EXPECT_NEAR(16, header_view->GetPreferredOnScreenHeight(), 1);
  328. static_cast<ImmersiveFullscreenControllerDelegate*>(header_view)
  329. ->SetVisibleFraction(0.0);
  330. EXPECT_EQ(0, header_view->GetPreferredOnScreenHeight());
  331. }
  332. // Verify windows that are minimized and then entered into tablet mode will have
  333. // no header when unminimized in tablet mode.
  334. TEST_F(NonClientFrameViewAshTest, MinimizedWindowsInTabletMode) {
  335. std::unique_ptr<views::Widget> widget =
  336. CreateTestWidget(new NonClientFrameViewAshTestWidgetDelegate);
  337. widget->GetNativeWindow()->SetProperty(
  338. aura::client::kResizeBehaviorKey,
  339. aura::client::kResizeBehaviorCanMaximize);
  340. widget->Maximize();
  341. widget->Minimize();
  342. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  343. widget->Show();
  344. EXPECT_EQ(widget->non_client_view()->bounds(),
  345. widget->client_view()->bounds());
  346. }
  347. TEST_F(NonClientFrameViewAshTest, HeaderVisibilityInFullscreen) {
  348. auto* delegate = new NonClientFrameViewAshTestWidgetDelegate();
  349. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  350. NonClientFrameViewAsh* non_client_frame_view =
  351. delegate->non_client_frame_view();
  352. HeaderView* header_view = non_client_frame_view->GetHeaderView();
  353. EXPECT_FALSE(header_view->in_immersive_mode());
  354. EXPECT_TRUE(header_view->GetVisible());
  355. widget->SetFullscreen(true);
  356. widget->LayoutRootViewIfNecessary();
  357. EXPECT_TRUE(header_view->in_immersive_mode());
  358. EXPECT_TRUE(header_view->GetVisible());
  359. widget->SetFullscreen(false);
  360. widget->LayoutRootViewIfNecessary();
  361. EXPECT_FALSE(header_view->in_immersive_mode());
  362. EXPECT_TRUE(header_view->GetVisible());
  363. // Turn immersive off, and make sure that header view is invisible
  364. // in fullscreen.
  365. widget->SetFullscreen(true);
  366. ImmersiveFullscreenController::EnableForWidget(widget.get(), false);
  367. widget->LayoutRootViewIfNecessary();
  368. EXPECT_FALSE(header_view->in_immersive_mode());
  369. EXPECT_FALSE(header_view->GetVisible());
  370. widget->SetFullscreen(false);
  371. widget->LayoutRootViewIfNecessary();
  372. EXPECT_FALSE(header_view->in_immersive_mode());
  373. EXPECT_TRUE(header_view->GetVisible());
  374. }
  375. namespace {
  376. class TestButtonModel : public chromeos::CaptionButtonModel {
  377. public:
  378. TestButtonModel() = default;
  379. TestButtonModel(const TestButtonModel&) = delete;
  380. TestButtonModel& operator=(const TestButtonModel&) = delete;
  381. ~TestButtonModel() override = default;
  382. void set_zoom_mode(bool zoom_mode) { zoom_mode_ = zoom_mode; }
  383. void SetVisible(views::CaptionButtonIcon type, bool visible) {
  384. if (visible)
  385. visible_buttons_.insert(type);
  386. else
  387. visible_buttons_.erase(type);
  388. }
  389. void SetEnabled(views::CaptionButtonIcon type, bool enabled) {
  390. if (enabled)
  391. enabled_buttons_.insert(type);
  392. else
  393. enabled_buttons_.erase(type);
  394. }
  395. // CaptionButtonModel::
  396. bool IsVisible(views::CaptionButtonIcon type) const override {
  397. return visible_buttons_.count(type);
  398. }
  399. bool IsEnabled(views::CaptionButtonIcon type) const override {
  400. return enabled_buttons_.count(type);
  401. }
  402. bool InZoomMode() const override { return zoom_mode_; }
  403. private:
  404. base::flat_set<views::CaptionButtonIcon> visible_buttons_;
  405. base::flat_set<views::CaptionButtonIcon> enabled_buttons_;
  406. bool zoom_mode_ = false;
  407. };
  408. } // namespace
  409. TEST_F(NonClientFrameViewAshTest, BackButton) {
  410. AcceleratorControllerImpl* controller =
  411. Shell::Get()->accelerator_controller();
  412. std::unique_ptr<TestButtonModel> model = std::make_unique<TestButtonModel>();
  413. TestButtonModel* model_ptr = model.get();
  414. auto* delegate = new NonClientFrameViewAshTestWidgetDelegate();
  415. std::unique_ptr<views::Widget> widget =
  416. CreateTestWidget(delegate, desks_util::GetActiveDeskContainerId(),
  417. gfx::Rect(0, 0, 400, 500));
  418. ui::Accelerator accelerator_back_press(ui::VKEY_BROWSER_BACK, ui::EF_NONE);
  419. accelerator_back_press.set_key_state(ui::Accelerator::KeyState::PRESSED);
  420. ui::TestAcceleratorTarget target_back_press;
  421. controller->Register({accelerator_back_press}, &target_back_press);
  422. ui::Accelerator accelerator_back_release(ui::VKEY_BROWSER_BACK, ui::EF_NONE);
  423. accelerator_back_release.set_key_state(ui::Accelerator::KeyState::RELEASED);
  424. ui::TestAcceleratorTarget target_back_release;
  425. controller->Register({accelerator_back_release}, &target_back_release);
  426. NonClientFrameViewAsh* non_client_frame_view =
  427. delegate->non_client_frame_view();
  428. non_client_frame_view->SetCaptionButtonModel(std::move(model));
  429. HeaderView* header_view = non_client_frame_view->GetHeaderView();
  430. EXPECT_FALSE(header_view->GetBackButton());
  431. model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_BACK, true);
  432. non_client_frame_view->SizeConstraintsChanged();
  433. widget->LayoutRootViewIfNecessary();
  434. EXPECT_TRUE(header_view->GetBackButton());
  435. EXPECT_FALSE(header_view->GetBackButton()->GetEnabled());
  436. // Back button is disabled, so clicking on it should not should
  437. // generate back key sequence.
  438. ui::test::EventGenerator* generator = GetEventGenerator();
  439. generator->MoveMouseTo(
  440. header_view->GetBackButton()->GetBoundsInScreen().CenterPoint());
  441. generator->ClickLeftButton();
  442. EXPECT_EQ(0, target_back_press.accelerator_count());
  443. EXPECT_EQ(0, target_back_release.accelerator_count());
  444. model_ptr->SetEnabled(views::CAPTION_BUTTON_ICON_BACK, true);
  445. non_client_frame_view->SizeConstraintsChanged();
  446. widget->LayoutRootViewIfNecessary();
  447. EXPECT_TRUE(header_view->GetBackButton());
  448. EXPECT_TRUE(header_view->GetBackButton()->GetEnabled());
  449. // Back button is now enabled, so clicking on it should generate
  450. // back key sequence.
  451. generator->MoveMouseTo(
  452. header_view->GetBackButton()->GetBoundsInScreen().CenterPoint());
  453. generator->ClickLeftButton();
  454. EXPECT_EQ(1, target_back_press.accelerator_count());
  455. EXPECT_EQ(1, target_back_release.accelerator_count());
  456. model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_BACK, false);
  457. non_client_frame_view->SizeConstraintsChanged();
  458. widget->LayoutRootViewIfNecessary();
  459. EXPECT_FALSE(header_view->GetBackButton());
  460. }
  461. // Make sure that client view occupies the entire window when the
  462. // frame is hidden.
  463. TEST_F(NonClientFrameViewAshTest, FrameVisibility) {
  464. NonClientFrameViewAshTestWidgetDelegate* delegate =
  465. new NonClientFrameViewAshTestWidgetDelegate;
  466. gfx::Rect window_bounds(10, 10, 200, 100);
  467. std::unique_ptr<views::Widget> widget = CreateTestWidget(
  468. delegate, desks_util::GetActiveDeskContainerId(), window_bounds);
  469. // The height is smaller by the top border height.
  470. gfx::Size client_bounds(200, 68);
  471. NonClientFrameViewAsh* non_client_frame_view =
  472. delegate->non_client_frame_view();
  473. EXPECT_EQ(client_bounds, widget->client_view()->GetLocalBounds().size());
  474. non_client_frame_view->SetFrameEnabled(false);
  475. widget->GetRootView()->Layout();
  476. EXPECT_EQ(gfx::Size(200, 100),
  477. widget->client_view()->GetLocalBounds().size());
  478. EXPECT_FALSE(non_client_frame_view->GetFrameEnabled());
  479. EXPECT_EQ(
  480. window_bounds,
  481. non_client_frame_view->GetClientBoundsForWindowBounds(window_bounds));
  482. non_client_frame_view->SetFrameEnabled(true);
  483. widget->GetRootView()->Layout();
  484. EXPECT_EQ(client_bounds, widget->client_view()->GetLocalBounds().size());
  485. EXPECT_TRUE(non_client_frame_view->GetFrameEnabled());
  486. EXPECT_EQ(32, delegate->GetNonClientFrameViewTopBorderHeight());
  487. EXPECT_EQ(
  488. gfx::Rect(gfx::Point(10, 42), client_bounds),
  489. non_client_frame_view->GetClientBoundsForWindowBounds(window_bounds));
  490. }
  491. TEST_F(NonClientFrameViewAshTest, CustomButtonModel) {
  492. std::unique_ptr<TestButtonModel> model = std::make_unique<TestButtonModel>();
  493. TestButtonModel* model_ptr = model.get();
  494. auto* delegate = new NonClientFrameViewAshTestWidgetDelegate();
  495. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  496. NonClientFrameViewAsh* non_client_frame_view =
  497. delegate->non_client_frame_view();
  498. non_client_frame_view->SetCaptionButtonModel(std::move(model));
  499. HeaderView* header_view = non_client_frame_view->GetHeaderView();
  500. FrameCaptionButtonContainerView::TestApi test_api(
  501. header_view->caption_button_container());
  502. // CLOSE button is always enabled.
  503. EXPECT_TRUE(test_api.close_button());
  504. EXPECT_FALSE(test_api.close_button()->GetVisible());
  505. EXPECT_TRUE(test_api.close_button()->GetEnabled());
  506. model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_CLOSE, true);
  507. non_client_frame_view->SizeConstraintsChanged();
  508. widget->LayoutRootViewIfNecessary();
  509. EXPECT_TRUE(test_api.close_button()->GetVisible());
  510. EXPECT_FALSE(test_api.minimize_button()->GetVisible());
  511. EXPECT_FALSE(test_api.size_button()->GetVisible());
  512. EXPECT_FALSE(test_api.menu_button()->GetVisible());
  513. // Back button
  514. model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_BACK, true);
  515. non_client_frame_view->SizeConstraintsChanged();
  516. widget->LayoutRootViewIfNecessary();
  517. EXPECT_TRUE(header_view->GetBackButton()->GetVisible());
  518. EXPECT_FALSE(header_view->GetBackButton()->GetEnabled());
  519. model_ptr->SetEnabled(views::CAPTION_BUTTON_ICON_BACK, true);
  520. non_client_frame_view->SizeConstraintsChanged();
  521. widget->LayoutRootViewIfNecessary();
  522. EXPECT_TRUE(header_view->GetBackButton()->GetEnabled());
  523. // size button
  524. model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, true);
  525. non_client_frame_view->SizeConstraintsChanged();
  526. widget->LayoutRootViewIfNecessary();
  527. EXPECT_TRUE(test_api.size_button()->GetVisible());
  528. EXPECT_FALSE(test_api.size_button()->GetEnabled());
  529. model_ptr->SetEnabled(views::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, true);
  530. non_client_frame_view->SizeConstraintsChanged();
  531. widget->LayoutRootViewIfNecessary();
  532. EXPECT_TRUE(test_api.size_button()->GetEnabled());
  533. // minimize button
  534. model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_MINIMIZE, true);
  535. non_client_frame_view->SizeConstraintsChanged();
  536. widget->LayoutRootViewIfNecessary();
  537. EXPECT_TRUE(test_api.minimize_button()->GetVisible());
  538. EXPECT_FALSE(test_api.minimize_button()->GetEnabled());
  539. model_ptr->SetEnabled(views::CAPTION_BUTTON_ICON_MINIMIZE, true);
  540. non_client_frame_view->SizeConstraintsChanged();
  541. widget->LayoutRootViewIfNecessary();
  542. EXPECT_TRUE(test_api.minimize_button()->GetEnabled());
  543. // menu button
  544. model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_MENU, true);
  545. non_client_frame_view->SizeConstraintsChanged();
  546. widget->LayoutRootViewIfNecessary();
  547. EXPECT_TRUE(test_api.menu_button()->GetVisible());
  548. EXPECT_FALSE(test_api.menu_button()->GetEnabled());
  549. model_ptr->SetEnabled(views::CAPTION_BUTTON_ICON_MENU, true);
  550. non_client_frame_view->SizeConstraintsChanged();
  551. widget->LayoutRootViewIfNecessary();
  552. EXPECT_TRUE(test_api.menu_button()->GetEnabled());
  553. // zoom button
  554. EXPECT_STREQ(views::kWindowControlMaximizeIcon.name,
  555. test_api.size_button()->icon_definition_for_test()->name);
  556. model_ptr->set_zoom_mode(true);
  557. non_client_frame_view->SizeConstraintsChanged();
  558. widget->LayoutRootViewIfNecessary();
  559. EXPECT_STREQ(chromeos::kWindowControlZoomIcon.name,
  560. test_api.size_button()->icon_definition_for_test()->name);
  561. widget->Maximize();
  562. EXPECT_STREQ(chromeos::kWindowControlDezoomIcon.name,
  563. test_api.size_button()->icon_definition_for_test()->name);
  564. }
  565. TEST_F(NonClientFrameViewAshTest, WideFrame) {
  566. auto* delegate = new NonClientFrameViewAshTestWidgetDelegate();
  567. std::unique_ptr<views::Widget> widget =
  568. CreateTestWidget(delegate, desks_util::GetActiveDeskContainerId(),
  569. gfx::Rect(100, 0, 400, 500));
  570. NonClientFrameViewAsh* non_client_frame_view =
  571. delegate->non_client_frame_view();
  572. HeaderView* header_view = non_client_frame_view->GetHeaderView();
  573. widget->Maximize();
  574. std::unique_ptr<WideFrameView> wide_frame_view =
  575. std::make_unique<WideFrameView>(widget.get());
  576. wide_frame_view->GetWidget()->Show();
  577. HeaderView* wide_header_view = wide_frame_view->header_view();
  578. display::Screen* screen = display::Screen::GetScreen();
  579. const gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
  580. gfx::Rect frame_bounds =
  581. wide_frame_view->GetWidget()->GetWindowBoundsInScreen();
  582. EXPECT_EQ(work_area.width(), frame_bounds.width());
  583. EXPECT_EQ(work_area.origin(), frame_bounds.origin());
  584. EXPECT_FALSE(header_view->should_paint());
  585. EXPECT_TRUE(wide_header_view->should_paint());
  586. // Test immersive.
  587. ImmersiveFullscreenController controller;
  588. wide_frame_view->Init(&controller);
  589. EXPECT_FALSE(wide_header_view->in_immersive_mode());
  590. EXPECT_FALSE(header_view->in_immersive_mode());
  591. EXPECT_TRUE(header_view->GetVisible());
  592. ImmersiveFullscreenController::EnableForWidget(widget.get(), true);
  593. EXPECT_TRUE(header_view->in_immersive_mode());
  594. EXPECT_TRUE(wide_header_view->in_immersive_mode());
  595. EXPECT_TRUE(header_view->GetVisible());
  596. // The height should be ~(33 *.5)
  597. wide_header_view->SetVisibleFraction(0.5);
  598. EXPECT_NEAR(16, wide_header_view->GetPreferredOnScreenHeight(), 1);
  599. // Make sure the frame can be revaled outside of the target window.
  600. EXPECT_FALSE(ImmersiveFullscreenControllerTestApi(&controller)
  601. .IsTopEdgeHoverTimerRunning());
  602. ui::test::EventGenerator* generator = GetEventGenerator();
  603. generator->MoveMouseTo(gfx::Point(10, 0));
  604. generator->MoveMouseBy(1, 0);
  605. EXPECT_TRUE(ImmersiveFullscreenControllerTestApi(&controller)
  606. .IsTopEdgeHoverTimerRunning());
  607. generator->MoveMouseTo(gfx::Point(10, 10));
  608. generator->MoveMouseBy(1, 0);
  609. EXPECT_FALSE(ImmersiveFullscreenControllerTestApi(&controller)
  610. .IsTopEdgeHoverTimerRunning());
  611. generator->MoveMouseTo(gfx::Point(600, 0));
  612. generator->MoveMouseBy(1, 0);
  613. EXPECT_TRUE(ImmersiveFullscreenControllerTestApi(&controller)
  614. .IsTopEdgeHoverTimerRunning());
  615. ImmersiveFullscreenController::EnableForWidget(widget.get(), false);
  616. EXPECT_FALSE(header_view->in_immersive_mode());
  617. EXPECT_FALSE(wide_header_view->in_immersive_mode());
  618. // visible fraction should be ignored in non immersive.
  619. wide_header_view->SetVisibleFraction(0.5);
  620. EXPECT_EQ(32, wide_header_view->GetPreferredOnScreenHeight());
  621. UpdateDisplay("1234x800");
  622. EXPECT_EQ(1234,
  623. wide_frame_view->GetWidget()->GetWindowBoundsInScreen().width());
  624. // Double Click
  625. EXPECT_TRUE(widget->IsMaximized());
  626. generator->MoveMouseToCenterOf(
  627. wide_header_view->GetWidget()->GetNativeWindow());
  628. generator->DoubleClickLeftButton();
  629. EXPECT_FALSE(widget->IsMaximized());
  630. }
  631. TEST_F(NonClientFrameViewAshTest, WideFrameButton) {
  632. auto* delegate = new NonClientFrameViewAshTestWidgetDelegate();
  633. std::unique_ptr<views::Widget> widget =
  634. CreateTestWidget(delegate, desks_util::GetActiveDeskContainerId(),
  635. gfx::Rect(100, 0, 400, 500));
  636. widget->Maximize();
  637. std::unique_ptr<WideFrameView> wide_frame_view =
  638. std::make_unique<WideFrameView>(widget.get());
  639. wide_frame_view->GetWidget()->Show();
  640. HeaderView* header_view = wide_frame_view->header_view();
  641. FrameCaptionButtonContainerView::TestApi test_api(
  642. header_view->caption_button_container());
  643. EXPECT_STREQ(views::kWindowControlRestoreIcon.name,
  644. test_api.size_button()->icon_definition_for_test()->name);
  645. widget->SetFullscreen(true);
  646. header_view->Layout();
  647. EXPECT_STREQ(views::kWindowControlRestoreIcon.name,
  648. test_api.size_button()->icon_definition_for_test()->name);
  649. {
  650. WMEvent event(WM_EVENT_PIN);
  651. WindowState::Get(widget->GetNativeWindow())->OnWMEvent(&event);
  652. header_view->Layout();
  653. EXPECT_STREQ(views::kWindowControlRestoreIcon.name,
  654. test_api.size_button()->icon_definition_for_test()->name);
  655. }
  656. {
  657. WMEvent event(WM_EVENT_TRUSTED_PIN);
  658. WindowState::Get(widget->GetNativeWindow())->OnWMEvent(&event);
  659. header_view->Layout();
  660. EXPECT_STREQ(views::kWindowControlRestoreIcon.name,
  661. test_api.size_button()->icon_definition_for_test()->name);
  662. }
  663. }
  664. TEST_F(NonClientFrameViewAshTest, MoveFullscreenWideFrameBetweenDisplay) {
  665. UpdateDisplay("800x600, 1000x600");
  666. auto* screen = display::Screen::GetScreen();
  667. auto display_list = screen->GetAllDisplays();
  668. auto* delegate = new NonClientFrameViewAshTestWidgetDelegate();
  669. std::unique_ptr<views::Widget> widget =
  670. CreateTestWidget(delegate, desks_util::GetActiveDeskContainerId(),
  671. gfx::Rect(100, 0, 400, 500));
  672. widget->SetFullscreen(true);
  673. std::unique_ptr<WideFrameView> wide_frame_view =
  674. std::make_unique<WideFrameView>(widget.get());
  675. wide_frame_view->GetWidget()->Show();
  676. ASSERT_EQ(display_list[0].id(),
  677. screen->GetDisplayNearestWindow(widget->GetNativeWindow()).id());
  678. EXPECT_EQ(800,
  679. wide_frame_view->GetWidget()->GetWindowBoundsInScreen().width());
  680. window_util::MoveWindowToDisplay(widget->GetNativeWindow(),
  681. display_list[1].id());
  682. EXPECT_EQ(display_list[1].id(),
  683. screen->GetDisplayNearestWindow(widget->GetNativeWindow()).id());
  684. EXPECT_EQ(1000,
  685. wide_frame_view->GetWidget()->GetWindowBoundsInScreen().width());
  686. }
  687. namespace {
  688. class NonClientFrameViewAshFrameColorTest
  689. : public NonClientFrameViewAshTest,
  690. public testing::WithParamInterface<bool> {
  691. public:
  692. NonClientFrameViewAshFrameColorTest() = default;
  693. NonClientFrameViewAshFrameColorTest(
  694. const NonClientFrameViewAshFrameColorTest&) = delete;
  695. NonClientFrameViewAshFrameColorTest& operator=(
  696. const NonClientFrameViewAshFrameColorTest&) = delete;
  697. ~NonClientFrameViewAshFrameColorTest() override = default;
  698. };
  699. class TestWidgetDelegate : public TestWidgetConstraintsDelegate {
  700. public:
  701. TestWidgetDelegate(bool custom) : custom_(custom) {}
  702. TestWidgetDelegate(const TestWidgetDelegate&) = delete;
  703. TestWidgetDelegate& operator=(const TestWidgetDelegate&) = delete;
  704. ~TestWidgetDelegate() override = default;
  705. // views::WidgetDelegate:
  706. std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView(
  707. views::Widget* widget) override {
  708. if (custom_) {
  709. WindowState* window_state = WindowState::Get(widget->GetNativeWindow());
  710. window_state->SetDelegate(std::make_unique<WindowStateDelegate>());
  711. }
  712. return TestWidgetConstraintsDelegate::CreateNonClientFrameView(widget);
  713. }
  714. private:
  715. bool custom_;
  716. };
  717. } // namespace
  718. // Verify that NonClientFrameViewAsh updates the active color based on the
  719. // kFrameActiveColorKey window property.
  720. TEST_P(NonClientFrameViewAshFrameColorTest, kFrameActiveColorKey) {
  721. TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam());
  722. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  723. SkColor active_color =
  724. widget->GetNativeWindow()->GetProperty(kFrameActiveColorKey);
  725. constexpr SkColor new_color = SK_ColorWHITE;
  726. EXPECT_NE(active_color, new_color);
  727. widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, new_color);
  728. active_color = widget->GetNativeWindow()->GetProperty(kFrameActiveColorKey);
  729. EXPECT_EQ(active_color, new_color);
  730. EXPECT_EQ(new_color,
  731. delegate->non_client_frame_view()->GetActiveFrameColorForTest());
  732. // Test that changing the property updates the caption button images.
  733. FrameCaptionButtonContainerView::TestApi test_api(
  734. delegate->non_client_frame_view()
  735. ->GetHeaderView()
  736. ->caption_button_container());
  737. ui::DrawWaiterForTest::WaitForCommit(widget->GetLayer()->GetCompositor());
  738. gfx::ImageSkia original_icon_image = test_api.size_button()->icon_image();
  739. widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, SK_ColorBLACK);
  740. ui::DrawWaiterForTest::WaitForCommit(widget->GetLayer()->GetCompositor());
  741. EXPECT_FALSE(original_icon_image.BackedBySameObjectAs(
  742. test_api.size_button()->icon_image()));
  743. }
  744. // Verify that NonClientFrameViewAsh updates the inactive color based on the
  745. // kFrameInactiveColorKey window property.
  746. TEST_P(NonClientFrameViewAshFrameColorTest, KFrameInactiveColor) {
  747. TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam());
  748. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  749. SkColor active_color =
  750. widget->GetNativeWindow()->GetProperty(kFrameInactiveColorKey);
  751. constexpr SkColor new_color = SK_ColorWHITE;
  752. EXPECT_NE(active_color, new_color);
  753. widget->GetNativeWindow()->SetProperty(kFrameInactiveColorKey, new_color);
  754. active_color = widget->GetNativeWindow()->GetProperty(kFrameInactiveColorKey);
  755. EXPECT_EQ(active_color, new_color);
  756. EXPECT_EQ(new_color,
  757. delegate->non_client_frame_view()->GetInactiveFrameColorForTest());
  758. }
  759. // Verify that NonClientFrameViewAsh updates the active and inactive colors at
  760. // construction.
  761. TEST_P(NonClientFrameViewAshFrameColorTest, KFrameColorCtor) {
  762. TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam());
  763. // Build the window, this implicit constructs the NonClientFrameView.
  764. constexpr SkColor non_default_color = SK_ColorWHITE;
  765. std::unique_ptr<views::Widget> widget =
  766. TestWidgetBuilder()
  767. .SetDelegate(delegate)
  768. .SetBounds(gfx::Rect())
  769. .SetParent(Shell::GetPrimaryRootWindow()->GetChildById(
  770. desks_util::GetActiveDeskContainerId()))
  771. .SetShow(true)
  772. .SetWindowProperty(kTrackDefaultFrameColors, false)
  773. .SetWindowProperty(kFrameActiveColorKey, non_default_color)
  774. .SetWindowProperty(kFrameInactiveColorKey, non_default_color)
  775. .BuildOwnsNativeWidget();
  776. // Check that the default color is different from the one used in the test.
  777. SkColor inactive_color =
  778. widget->GetNativeWindow()->GetProperty(kFrameInactiveColorKey);
  779. SkColor active_color =
  780. widget->GetNativeWindow()->GetProperty(kFrameActiveColorKey);
  781. EXPECT_EQ(active_color, non_default_color);
  782. EXPECT_EQ(inactive_color, non_default_color);
  783. EXPECT_EQ(delegate->non_client_frame_view()->GetInactiveFrameColorForTest(),
  784. non_default_color);
  785. EXPECT_EQ(delegate->non_client_frame_view()->GetActiveFrameColorForTest(),
  786. non_default_color);
  787. }
  788. // Verify that NonClientFrameViewAsh updates the active color based on the
  789. // kFrameActiveColorKey window property.
  790. TEST_P(NonClientFrameViewAshFrameColorTest, WideFrameInitialColor) {
  791. TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam());
  792. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  793. widget->Maximize();
  794. aura::Window* window = widget->GetNativeWindow();
  795. SkColor active_color = window->GetProperty(kFrameActiveColorKey);
  796. SkColor inactive_color = window->GetProperty(kFrameInactiveColorKey);
  797. constexpr SkColor new_active_color = SK_ColorWHITE;
  798. constexpr SkColor new_inactive_color = SK_ColorBLACK;
  799. EXPECT_NE(active_color, new_active_color);
  800. EXPECT_NE(inactive_color, new_inactive_color);
  801. window->SetProperty(kFrameActiveColorKey, new_active_color);
  802. window->SetProperty(kFrameInactiveColorKey, new_inactive_color);
  803. std::unique_ptr<WideFrameView> wide_frame_view =
  804. std::make_unique<WideFrameView>(widget.get());
  805. HeaderView* wide_header_view = wide_frame_view->header_view();
  806. DefaultFrameHeader* header = wide_header_view->GetFrameHeader();
  807. EXPECT_EQ(new_active_color, header->active_frame_color_for_testing());
  808. EXPECT_EQ(new_inactive_color, header->inactive_frame_color_for_testing());
  809. }
  810. // Tests to make sure that the NonClientFrameViewAsh tracks default frame colors
  811. // for both light and dark mode.
  812. TEST_P(NonClientFrameViewAshFrameColorTest, DefaultFrameColorsDarkAndLight) {
  813. base::test::ScopedFeatureList scoped_feature_list(
  814. chromeos::features::kDarkLightMode);
  815. auto* color_provider = AshColorProvider::Get();
  816. auto* dark_light_mode_controller = DarkLightModeControllerImpl::Get();
  817. dark_light_mode_controller->OnActiveUserPrefServiceChanged(
  818. Shell::Get()->session_controller()->GetActivePrefService());
  819. ASSERT_TRUE(chromeos::features::IsDarkLightModeEnabled());
  820. const bool initial_dark_mode_status =
  821. dark_light_mode_controller->IsDarkModeEnabled();
  822. TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam());
  823. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  824. aura::Window* window = widget->GetNativeWindow();
  825. const SkColor initial_active_default =
  826. color_provider->GetActiveDialogTitleBarColor();
  827. const SkColor initial_inactive_default =
  828. color_provider->GetInactiveDialogTitleBarColor();
  829. SkColor active_color = window->GetProperty(kFrameActiveColorKey);
  830. SkColor inactive_color = window->GetProperty(kFrameInactiveColorKey);
  831. EXPECT_EQ(initial_active_default, active_color);
  832. EXPECT_EQ(initial_inactive_default, inactive_color);
  833. // Switch the color mode
  834. dark_light_mode_controller->ToggleColorMode();
  835. ASSERT_NE(initial_dark_mode_status,
  836. dark_light_mode_controller->IsDarkModeEnabled());
  837. const SkColor active_default = color_provider->GetActiveDialogTitleBarColor();
  838. const SkColor inactive_default =
  839. color_provider->GetInactiveDialogTitleBarColor();
  840. active_color = window->GetProperty(kFrameActiveColorKey);
  841. inactive_color = window->GetProperty(kFrameInactiveColorKey);
  842. EXPECT_NE(initial_active_default, active_default);
  843. EXPECT_NE(initial_inactive_default, inactive_default);
  844. EXPECT_EQ(active_default, active_color);
  845. EXPECT_EQ(inactive_default, inactive_color);
  846. }
  847. // Tests to make sure that NonClientFrameViewAsh does not clobber custom frame
  848. // colors when the kTrackDefaultFrameColors property is set to false.
  849. TEST_P(NonClientFrameViewAshFrameColorTest,
  850. CanSetPersistentFrameColorsDarkAndLight) {
  851. base::test::ScopedFeatureList scoped_feature_list(
  852. chromeos::features::kDarkLightMode);
  853. auto* dark_light_mode_controller = DarkLightModeControllerImpl::Get();
  854. dark_light_mode_controller->OnActiveUserPrefServiceChanged(
  855. Shell::Get()->session_controller()->GetActivePrefService());
  856. ASSERT_TRUE(chromeos::features::IsDarkLightModeEnabled());
  857. const bool initial_dark_mode_status =
  858. dark_light_mode_controller->IsDarkModeEnabled();
  859. TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam());
  860. std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
  861. aura::Window* window = widget->GetNativeWindow();
  862. constexpr SkColor new_active_color = SK_ColorWHITE;
  863. constexpr SkColor new_inactive_color = SK_ColorBLACK;
  864. EXPECT_NE(new_active_color, window->GetProperty(kFrameActiveColorKey));
  865. EXPECT_NE(new_inactive_color, window->GetProperty(kFrameInactiveColorKey));
  866. window->SetProperty(kTrackDefaultFrameColors, false);
  867. window->SetProperty(kFrameActiveColorKey, new_active_color);
  868. window->SetProperty(kFrameInactiveColorKey, new_inactive_color);
  869. EXPECT_EQ(new_active_color, window->GetProperty(kFrameActiveColorKey));
  870. EXPECT_EQ(new_inactive_color, window->GetProperty(kFrameInactiveColorKey));
  871. // Switch the color mode.
  872. dark_light_mode_controller->ToggleColorMode();
  873. ASSERT_NE(initial_dark_mode_status,
  874. dark_light_mode_controller->IsDarkModeEnabled());
  875. EXPECT_EQ(new_active_color, window->GetProperty(kFrameActiveColorKey));
  876. EXPECT_EQ(new_inactive_color, window->GetProperty(kFrameInactiveColorKey));
  877. }
  878. // Run frame color tests with and without custom WindowStateDelegate.
  879. INSTANTIATE_TEST_SUITE_P(All,
  880. NonClientFrameViewAshFrameColorTest,
  881. testing::Bool());
  882. } // namespace ash