| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/frame/frame_view_ash.h" |
| |
| #include <memory> |
| |
| #include "ash/accelerators/accelerator_controller_impl.h" |
| #include "ash/constants/ash_pref_names.h" |
| #include "ash/frame/wide_frame_view.h" |
| #include "ash/resources/vector_icons/vector_icons.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shell.h" |
| #include "ash/style/ash_color_provider.h" |
| #include "ash/style/dark_light_mode_controller_impl.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/wm/desks/desks_util.h" |
| #include "ash/wm/overview/overview_controller.h" |
| #include "ash/wm/splitview/split_view_controller.h" |
| #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h" |
| #include "ash/wm/window_state.h" |
| #include "ash/wm/window_state_delegate.h" |
| #include "ash/wm/window_util.h" |
| #include "ash/wm/wm_event.h" |
| #include "base/command_line.h" |
| #include "base/containers/flat_set.h" |
| #include "base/memory/raw_ptr.h" |
| #include "chromeos/ui/base/window_properties.h" |
| #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h" |
| #include "chromeos/ui/frame/default_frame_header.h" |
| #include "chromeos/ui/frame/header_view.h" |
| #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h" |
| #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h" |
| #include "chromeos/ui/vector_icons/vector_icons.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_targeter.h" |
| #include "ui/base/accelerators/accelerator.h" |
| #include "ui/base/accelerators/test_accelerator_target.h" |
| #include "ui/chromeos/styles/cros_tokens_color_mappings.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| #include "ui/compositor/test/draw_waiter_for_test.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/gfx/image/image_unittest_util.h" |
| #include "ui/gfx/vector_icon_types.h" |
| #include "ui/views/test/test_widget_builder.h" |
| #include "ui/views/test/views_test_utils.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/widget/widget_delegate.h" |
| #include "ui/views/window/caption_button_layout_constants.h" |
| #include "ui/views/window/frame_caption_button.h" |
| #include "ui/views/window/vector_icons/vector_icons.h" |
| #include "ui/wm/core/window_util.h" |
| |
| namespace ash { |
| |
| using ::chromeos::DefaultFrameHeader; |
| using ::chromeos::FrameCaptionButtonContainerView; |
| using ::chromeos::ImmersiveFullscreenController; |
| using ::chromeos::ImmersiveFullscreenControllerDelegate; |
| using ::chromeos::ImmersiveFullscreenControllerTestApi; |
| using ::chromeos::kFrameActiveColorKey; |
| using ::chromeos::kFrameInactiveColorKey; |
| using ::chromeos::kTrackDefaultFrameColors; |
| |
| // A views::WidgetDelegate which uses a FrameViewAsh. |
| class FrameViewAshTestWidgetDelegate : public views::WidgetDelegateView { |
| public: |
| FrameViewAshTestWidgetDelegate() = default; |
| |
| FrameViewAshTestWidgetDelegate(const FrameViewAshTestWidgetDelegate&) = |
| delete; |
| FrameViewAshTestWidgetDelegate& operator=( |
| const FrameViewAshTestWidgetDelegate&) = delete; |
| |
| ~FrameViewAshTestWidgetDelegate() override = default; |
| |
| std::unique_ptr<views::FrameView> CreateFrameView( |
| views::Widget* widget) override { |
| auto frame_view = std::make_unique<FrameViewAsh>(widget); |
| frame_view_ = frame_view.get(); |
| return frame_view; |
| } |
| |
| int GetFrameViewTopBorderHeight() { |
| return frame_view_->NonClientTopBorderHeight(); |
| } |
| |
| FrameViewAsh* frame_view() const { return frame_view_; } |
| |
| chromeos::HeaderView* header_view() const { |
| return frame_view_->GetHeaderView(); |
| } |
| |
| private: |
| // Not owned. |
| raw_ptr<FrameViewAsh> frame_view_ = nullptr; |
| }; |
| |
| class TestWidgetConstraintsDelegate : public FrameViewAshTestWidgetDelegate { |
| public: |
| TestWidgetConstraintsDelegate() { |
| SetCanMaximize(true); |
| SetCanMinimize(true); |
| } |
| |
| TestWidgetConstraintsDelegate(const TestWidgetConstraintsDelegate&) = delete; |
| TestWidgetConstraintsDelegate& operator=( |
| const TestWidgetConstraintsDelegate&) = delete; |
| |
| ~TestWidgetConstraintsDelegate() override = default; |
| |
| // views::View: |
| gfx::Size GetMinimumSize() const override { return minimum_size_; } |
| |
| gfx::Size GetMaximumSize() const override { return maximum_size_; } |
| |
| views::View* GetContentsView() override { |
| // Set this instance as the contents view so that the maximum and minimum |
| // size constraints will be used. |
| return this; |
| } |
| |
| // views::WidgetDelegate: |
| void set_minimum_size(const gfx::Size& min_size) { minimum_size_ = min_size; } |
| |
| void set_maximum_size(const gfx::Size& max_size) { maximum_size_ = max_size; } |
| |
| const gfx::Rect& GetFrameCaptionButtonContainerViewBounds() { |
| return frame_view()->GetFrameCaptionButtonContainerViewForTest()->bounds(); |
| } |
| |
| void EndFrameCaptionButtonContainerViewAnimations() { |
| FrameCaptionButtonContainerView::TestApi test( |
| frame_view()->GetFrameCaptionButtonContainerViewForTest()); |
| test.EndAnimations(); |
| } |
| |
| int GetTitleBarHeight() const { |
| return frame_view()->NonClientTopBorderHeight(); |
| } |
| |
| private: |
| gfx::Size minimum_size_; |
| gfx::Size maximum_size_; |
| }; |
| |
| using FrameViewAshTest = AshTestBase; |
| |
| // Verifies the client view is not placed at a y location of 0. |
| TEST_F(FrameViewAshTest, ClientViewCorrectlyPlaced) { |
| std::unique_ptr<views::Widget> widget = |
| CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, |
| new FrameViewAshTestWidgetDelegate); |
| EXPECT_NE(0, widget->client_view()->bounds().y()); |
| } |
| |
| // Test that the height of the header is correct upon initially displaying |
| // the widget. |
| TEST_F(FrameViewAshTest, HeaderHeight) { |
| FrameViewAshTestWidgetDelegate* delegate = new FrameViewAshTestWidgetDelegate; |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| |
| // The header should have enough room for the window controls. The |
| // header/content separator line overlays the window controls. |
| EXPECT_EQ(views::GetCaptionButtonLayoutSize( |
| views::CaptionButtonLayoutSize::kNonBrowserCaption) |
| .height(), |
| delegate->frame_view()->GetHeaderView()->height()); |
| } |
| |
| // Regression test for https://crbug.com/839955 |
| TEST_F(FrameViewAshTest, ActiveStateOfButtonMatchesWidget) { |
| FrameViewAshTestWidgetDelegate* delegate = new FrameViewAshTestWidgetDelegate; |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| FrameCaptionButtonContainerView::TestApi test_api( |
| delegate->frame_view()->GetHeaderView()->caption_button_container()); |
| |
| widget->Show(); |
| EXPECT_TRUE(widget->IsActive()); |
| // The paint state doesn't change till the next paint. |
| ui::DrawWaiterForTest::WaitForCommit(widget->GetLayer()->GetCompositor()); |
| EXPECT_TRUE(test_api.size_button()->GetPaintAsActive()); |
| |
| // Activate a different widget so the original one loses activation. |
| std::unique_ptr<views::Widget> widget2 = |
| CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, |
| new FrameViewAshTestWidgetDelegate); |
| widget2->Show(); |
| ui::DrawWaiterForTest::WaitForCommit(widget->GetLayer()->GetCompositor()); |
| |
| EXPECT_FALSE(widget->IsActive()); |
| EXPECT_FALSE(test_api.size_button()->GetPaintAsActive()); |
| } |
| |
| // Verify that FrameViewAsh returns the correct minimum and maximum |
| // frame sizes when the client view does not specify any size constraints. |
| TEST_F(FrameViewAshTest, NoSizeConstraints) { |
| TestWidgetConstraintsDelegate* delegate = new TestWidgetConstraintsDelegate; |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| |
| FrameViewAsh* frame_view = delegate->frame_view(); |
| gfx::Size min_frame_size = frame_view->GetMinimumSize(); |
| gfx::Size max_frame_size = frame_view->GetMaximumSize(); |
| |
| EXPECT_EQ(delegate->GetTitleBarHeight(), min_frame_size.height()); |
| |
| // A width and height constraint of 0 denotes unbounded. |
| EXPECT_EQ(0, max_frame_size.width()); |
| EXPECT_EQ(0, max_frame_size.height()); |
| } |
| |
| // Verify that FrameViewAsh returns the correct minimum and maximum |
| // frame sizes when the client view specifies size constraints. |
| TEST_F(FrameViewAshTest, MinimumAndMaximumSize) { |
| gfx::Size min_client_size(500, 500); |
| gfx::Size max_client_size(800, 800); |
| TestWidgetConstraintsDelegate* delegate = new TestWidgetConstraintsDelegate; |
| delegate->set_minimum_size(min_client_size); |
| delegate->set_maximum_size(max_client_size); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| |
| FrameViewAsh* frame_view = delegate->frame_view(); |
| gfx::Size min_frame_size = frame_view->GetMinimumSize(); |
| gfx::Size max_frame_size = frame_view->GetMaximumSize(); |
| |
| EXPECT_EQ(min_client_size.width(), min_frame_size.width()); |
| EXPECT_EQ(max_client_size.width(), max_frame_size.width()); |
| EXPECT_EQ(min_client_size.height() + delegate->GetTitleBarHeight(), |
| min_frame_size.height()); |
| EXPECT_EQ(max_client_size.height() + delegate->GetTitleBarHeight(), |
| max_frame_size.height()); |
| } |
| |
| // Verify that FrameViewAsh updates the avatar icon based on the |
| // avatar icon window property. |
| TEST_F(FrameViewAshTest, AvatarIcon) { |
| TestWidgetConstraintsDelegate* delegate = new TestWidgetConstraintsDelegate; |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| |
| FrameViewAsh* frame_view = delegate->frame_view(); |
| EXPECT_FALSE(frame_view->GetAvatarIconViewForTest()); |
| |
| // Avatar image becomes available. |
| widget->GetNativeWindow()->SetProperty( |
| aura::client::kAvatarIconKey, |
| new gfx::ImageSkia(gfx::test::CreateImage(27, 27).AsImageSkia())); |
| EXPECT_TRUE(frame_view->GetAvatarIconViewForTest()); |
| |
| // Avatar image is gone; the ImageView for the avatar icon should be |
| // removed. |
| widget->GetNativeWindow()->ClearProperty(aura::client::kAvatarIconKey); |
| EXPECT_FALSE(frame_view->GetAvatarIconViewForTest()); |
| } |
| |
| // Tests that a window is minimized, toggling tablet mode doesn't trigger |
| // caption button update (https://crbug.com/822890). |
| TEST_F(FrameViewAshTest, ToggleTabletModeOnMinimizedWindow) { |
| FrameViewAshTestWidgetDelegate* delegate = new FrameViewAshTestWidgetDelegate; |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| FrameCaptionButtonContainerView::TestApi test( |
| delegate->frame_view()->GetHeaderView()->caption_button_container()); |
| widget->Maximize(); |
| |
| // Restore icon for size button in maximized window state. Compare by name |
| // because the address may not be the same for different build targets in the |
| // component build. |
| EXPECT_STREQ(views::kWindowControlRestoreIcon.name, |
| test.size_button()->icon_definition_for_test()->name); |
| widget->Minimize(); |
| |
| // Enter and exit tablet mode while the window is minimized. |
| ash::TabletModeControllerTestApi().EnterTabletMode(); |
| ash::TabletModeControllerTestApi().LeaveTabletMode(); |
| |
| // When unminimizing in non-tablet mode, size button should match with |
| // maximized window state, which is restore icon. |
| ::wm::Unminimize(widget->GetNativeWindow()); |
| EXPECT_TRUE(widget->IsMaximized()); |
| EXPECT_STREQ(views::kWindowControlRestoreIcon.name, |
| test.size_button()->icon_definition_for_test()->name); |
| } |
| |
| // Verify that when in tablet mode with a maximized window, the height of the |
| // header is zero. |
| TEST_F(FrameViewAshTest, FrameHiddenInTabletModeForMaximizedWindows) { |
| FrameViewAshTestWidgetDelegate* delegate = new FrameViewAshTestWidgetDelegate; |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| widget->Maximize(); |
| |
| ash::TabletModeControllerTestApi().EnterTabletMode(); |
| EXPECT_EQ(0, delegate->GetFrameViewTopBorderHeight()); |
| } |
| |
| // Verify that when in tablet mode with a non maximized window, the height of |
| // the header is non zero. |
| TEST_F(FrameViewAshTest, FrameShownInTabletModeForNonMaximizedWindows) { |
| auto* delegate = new FrameViewAshTestWidgetDelegate(); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| |
| ash::TabletModeControllerTestApi().EnterTabletMode(); |
| EXPECT_EQ(views::GetCaptionButtonLayoutSize( |
| views::CaptionButtonLayoutSize::kNonBrowserCaption) |
| .height(), |
| delegate->GetFrameViewTopBorderHeight()); |
| } |
| |
| // Verify that if originally in fullscreen mode, and enter tablet mode, the |
| // height of the header remains zero. |
| TEST_F(FrameViewAshTest, FrameRemainsHiddenInTabletModeWhenTogglingFullscreen) { |
| FrameViewAshTestWidgetDelegate* delegate = new FrameViewAshTestWidgetDelegate; |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| |
| widget->SetFullscreen(true); |
| EXPECT_EQ(0, delegate->GetFrameViewTopBorderHeight()); |
| ash::TabletModeControllerTestApi().EnterTabletMode(); |
| EXPECT_EQ(0, delegate->GetFrameViewTopBorderHeight()); |
| ash::TabletModeControllerTestApi().LeaveTabletMode(); |
| EXPECT_EQ(0, delegate->GetFrameViewTopBorderHeight()); |
| } |
| |
| TEST_F(FrameViewAshTest, OpeningAppsInTabletMode) { |
| auto* delegate = new TestWidgetConstraintsDelegate; |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| widget->Maximize(); |
| |
| ash::TabletModeControllerTestApi().EnterTabletMode(); |
| EXPECT_EQ(0, delegate->GetFrameViewTopBorderHeight()); |
| |
| // Verify that after minimizing and showing the widget, the height of the |
| // header is zero. |
| widget->Minimize(); |
| widget->Show(); |
| EXPECT_TRUE(widget->IsMaximized()); |
| EXPECT_EQ(0, delegate->GetFrameViewTopBorderHeight()); |
| |
| // Verify that when we toggle maximize, the header is shown. For example, |
| // maximized can be toggled in tablet mode by using the accessibility |
| // keyboard. |
| WMEvent event(WM_EVENT_TOGGLE_MAXIMIZE); |
| WindowState::Get(widget->GetNativeWindow())->OnWMEvent(&event); |
| EXPECT_EQ(0, delegate->GetFrameViewTopBorderHeight()); |
| |
| ash::TabletModeControllerTestApi().LeaveTabletMode(); |
| EXPECT_EQ(views::GetCaptionButtonLayoutSize( |
| views::CaptionButtonLayoutSize::kNonBrowserCaption) |
| .height(), |
| delegate->GetFrameViewTopBorderHeight()); |
| } |
| |
| // Regression test for https://b/331238593. See bug for more details. |
| TEST_F(FrameViewAshTest, NoCrashOnTabletChangesWithinWindowDestruction) { |
| class WindowTestObserver : public aura::WindowObserver { |
| public: |
| explicit WindowTestObserver(aura::Window* window) { |
| window_observation_.Observe(window); |
| } |
| ~WindowTestObserver() override = default; |
| |
| void OnWindowDestroying(aura::Window* window) override { |
| // Simulate a tablet state change from within window destruction. It's not |
| // clear how this may happen in production, but it triggers the same |
| // reported crash stack. |
| TabletModeControllerTestApi().EnterTabletMode(); |
| window_observation_.Reset(); |
| } |
| |
| private: |
| base::ScopedObservation<aura::Window, aura::WindowObserver> |
| window_observation_{this}; |
| }; |
| |
| auto test_window = CreateTestWindow(gfx::Rect(200, 200)); |
| WindowTestObserver obs(test_window.get()); |
| test_window.reset(); |
| } |
| |
| // Test if creating a new window in tablet mode uses maximzied state |
| // and immersive mode. |
| TEST_F(FrameViewAshTest, GetPreferredOnScreenHeightInTabletMaximzied) { |
| ash::TabletModeControllerTestApi().EnterTabletMode(); |
| |
| auto* delegate = new TestWidgetConstraintsDelegate; |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| auto* frame_view = |
| static_cast<FrameViewAsh*>(widget->non_client_view()->frame_view()); |
| auto* header_view = frame_view->GetHeaderView(); |
| ASSERT_TRUE(widget->IsMaximized()); |
| EXPECT_TRUE(header_view->in_immersive_mode()); |
| static_cast<ImmersiveFullscreenControllerDelegate*>(header_view) |
| ->SetVisibleFraction(0.5); |
| // The height should be ~(33 *.5) |
| EXPECT_NEAR(16, header_view->GetPreferredOnScreenHeight(), 1); |
| static_cast<ImmersiveFullscreenControllerDelegate*>(header_view) |
| ->SetVisibleFraction(0.0); |
| EXPECT_EQ(0, header_view->GetPreferredOnScreenHeight()); |
| } |
| |
| // Verify windows that are minimized and then entered into tablet mode will have |
| // no header when unminimized in tablet mode. |
| TEST_F(FrameViewAshTest, MinimizedWindowsInTabletMode) { |
| std::unique_ptr<views::Widget> widget = |
| CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, |
| new FrameViewAshTestWidgetDelegate); |
| widget->GetNativeWindow()->SetProperty( |
| aura::client::kResizeBehaviorKey, |
| aura::client::kResizeBehaviorCanMaximize); |
| widget->Maximize(); |
| widget->Minimize(); |
| ash::TabletModeControllerTestApi().EnterTabletMode(); |
| |
| widget->Show(); |
| EXPECT_EQ(widget->non_client_view()->bounds(), |
| widget->client_view()->bounds()); |
| } |
| |
| TEST_F(FrameViewAshTest, HeaderVisibilityInFullscreen) { |
| auto* delegate = new FrameViewAshTestWidgetDelegate(); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| |
| auto* controller = ImmersiveFullscreenController::Get(widget.get()); |
| ImmersiveFullscreenControllerTestApi test_api(controller); |
| ui::ScopedAnimationDurationScaleMode test_duration_mode( |
| ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); |
| |
| FrameViewAsh* frame_view = delegate->frame_view(); |
| chromeos::HeaderView* header_view = frame_view->GetHeaderView(); |
| EXPECT_FALSE(header_view->in_immersive_mode()); |
| EXPECT_TRUE(header_view->GetVisible()); |
| |
| widget->SetFullscreen(true); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(header_view->in_immersive_mode()); |
| EXPECT_TRUE(header_view->GetVisible()); |
| test_api.EndAnimation(); |
| EXPECT_FALSE(header_view->GetVisible()); |
| |
| widget->SetFullscreen(false); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_FALSE(header_view->in_immersive_mode()); |
| EXPECT_TRUE(header_view->GetVisible()); |
| test_api.EndAnimation(); |
| EXPECT_TRUE(header_view->GetVisible()); |
| |
| // Turn immersive off, and make sure that header view is invisible |
| // in fullscreen. |
| widget->SetFullscreen(true); |
| ImmersiveFullscreenController::EnableForWidget(widget.get(), false); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_FALSE(header_view->in_immersive_mode()); |
| EXPECT_FALSE(header_view->GetVisible()); |
| widget->SetFullscreen(false); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_FALSE(header_view->in_immersive_mode()); |
| EXPECT_TRUE(header_view->GetVisible()); |
| } |
| |
| namespace { |
| |
| class TestButtonModel : public chromeos::CaptionButtonModel { |
| public: |
| TestButtonModel() = default; |
| |
| TestButtonModel(const TestButtonModel&) = delete; |
| TestButtonModel& operator=(const TestButtonModel&) = delete; |
| |
| ~TestButtonModel() override = default; |
| |
| void set_zoom_mode(bool zoom_mode) { zoom_mode_ = zoom_mode; } |
| |
| void SetVisible(views::CaptionButtonIcon type, bool visible) { |
| if (visible) |
| visible_buttons_.insert(type); |
| else |
| visible_buttons_.erase(type); |
| } |
| |
| void SetEnabled(views::CaptionButtonIcon type, bool enabled) { |
| if (enabled) |
| enabled_buttons_.insert(type); |
| else |
| enabled_buttons_.erase(type); |
| } |
| |
| // CaptionButtonModel:: |
| bool IsVisible(views::CaptionButtonIcon type) const override { |
| return visible_buttons_.count(type); |
| } |
| bool IsEnabled(views::CaptionButtonIcon type) const override { |
| return enabled_buttons_.count(type); |
| } |
| bool InZoomMode() const override { return zoom_mode_; } |
| |
| private: |
| base::flat_set<views::CaptionButtonIcon> visible_buttons_; |
| base::flat_set<views::CaptionButtonIcon> enabled_buttons_; |
| bool zoom_mode_ = false; |
| }; |
| |
| } // namespace |
| |
| TEST_F(FrameViewAshTest, BackButton) { |
| AcceleratorControllerImpl* controller = |
| Shell::Get()->accelerator_controller(); |
| std::unique_ptr<TestButtonModel> model = std::make_unique<TestButtonModel>(); |
| TestButtonModel* model_ptr = model.get(); |
| |
| auto* delegate = new FrameViewAshTestWidgetDelegate(); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate, |
| desks_util::GetActiveDeskContainerId(), gfx::Rect(0, 0, 400, 500)); |
| |
| ui::Accelerator accelerator_back_press(ui::VKEY_BROWSER_BACK, ui::EF_NONE); |
| accelerator_back_press.set_key_state(ui::Accelerator::KeyState::PRESSED); |
| ui::TestAcceleratorTarget target_back_press; |
| controller->Register({accelerator_back_press}, &target_back_press); |
| |
| ui::Accelerator accelerator_back_release(ui::VKEY_BROWSER_BACK, ui::EF_NONE); |
| accelerator_back_release.set_key_state(ui::Accelerator::KeyState::RELEASED); |
| ui::TestAcceleratorTarget target_back_release; |
| controller->Register({accelerator_back_release}, &target_back_release); |
| |
| FrameViewAsh* frame_view = delegate->frame_view(); |
| frame_view->SetCaptionButtonModel(std::move(model)); |
| |
| chromeos::HeaderView* header_view = frame_view->GetHeaderView(); |
| EXPECT_FALSE(header_view->GetBackButton()); |
| model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_BACK, true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(header_view->GetBackButton()); |
| EXPECT_FALSE(header_view->GetBackButton()->GetEnabled()); |
| |
| // Back button is disabled, so clicking on it should not should |
| // generate back key sequence. |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->MoveMouseTo( |
| header_view->GetBackButton()->GetBoundsInScreen().CenterPoint()); |
| generator->ClickLeftButton(); |
| EXPECT_EQ(0, target_back_press.accelerator_count()); |
| EXPECT_EQ(0, target_back_release.accelerator_count()); |
| |
| model_ptr->SetEnabled(views::CAPTION_BUTTON_ICON_BACK, true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(header_view->GetBackButton()); |
| EXPECT_TRUE(header_view->GetBackButton()->GetEnabled()); |
| |
| // Back button is now enabled, so clicking on it should generate |
| // back key sequence. |
| generator->MoveMouseTo( |
| header_view->GetBackButton()->GetBoundsInScreen().CenterPoint()); |
| |
| generator->ClickLeftButton(); |
| EXPECT_EQ(1, target_back_press.accelerator_count()); |
| EXPECT_EQ(1, target_back_release.accelerator_count()); |
| |
| model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_BACK, false); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_FALSE(header_view->GetBackButton()); |
| } |
| |
| // Make sure that client view occupies the entire window when the |
| // frame is hidden. |
| TEST_F(FrameViewAshTest, FrameVisibility) { |
| FrameViewAshTestWidgetDelegate* delegate = new FrameViewAshTestWidgetDelegate; |
| gfx::Rect window_bounds(10, 10, 200, 100); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate, |
| desks_util::GetActiveDeskContainerId(), window_bounds); |
| |
| // The height is smaller by the top border height. |
| gfx::Size client_bounds(200, 68); |
| FrameViewAsh* frame_view = delegate->frame_view(); |
| EXPECT_EQ(client_bounds, widget->client_view()->GetLocalBounds().size()); |
| |
| frame_view->SetFrameEnabled(false); |
| views::test::RunScheduledLayout(widget->GetRootView()); |
| EXPECT_EQ(gfx::Size(200, 100), |
| widget->client_view()->GetLocalBounds().size()); |
| EXPECT_FALSE(frame_view->GetFrameEnabled()); |
| EXPECT_EQ(window_bounds, |
| frame_view->GetClientBoundsForWindowBounds(window_bounds)); |
| |
| frame_view->SetFrameEnabled(true); |
| views::test::RunScheduledLayout(widget->GetRootView()); |
| EXPECT_EQ(client_bounds, widget->client_view()->GetLocalBounds().size()); |
| EXPECT_TRUE(frame_view->GetFrameEnabled()); |
| EXPECT_EQ(32, delegate->GetFrameViewTopBorderHeight()); |
| EXPECT_EQ(gfx::Rect(gfx::Point(10, 42), client_bounds), |
| frame_view->GetClientBoundsForWindowBounds(window_bounds)); |
| } |
| |
| TEST_F(FrameViewAshTest, CustomButtonModel) { |
| std::unique_ptr<TestButtonModel> model = std::make_unique<TestButtonModel>(); |
| TestButtonModel* model_ptr = model.get(); |
| |
| auto* delegate = new FrameViewAshTestWidgetDelegate(); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| |
| FrameViewAsh* frame_view = delegate->frame_view(); |
| frame_view->SetCaptionButtonModel(std::move(model)); |
| |
| chromeos::HeaderView* header_view = frame_view->GetHeaderView(); |
| FrameCaptionButtonContainerView::TestApi test_api( |
| header_view->caption_button_container()); |
| |
| EXPECT_FALSE(test_api.close_button()->GetVisible()); |
| EXPECT_FALSE(test_api.minimize_button()->GetVisible()); |
| EXPECT_FALSE(test_api.size_button()->GetVisible()); |
| EXPECT_FALSE(test_api.menu_button()->GetVisible()); |
| |
| // Close button |
| model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_CLOSE, true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(test_api.close_button()->GetVisible()); |
| EXPECT_FALSE(test_api.close_button()->GetEnabled()); |
| |
| model_ptr->SetEnabled(views::CAPTION_BUTTON_ICON_CLOSE, true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(test_api.close_button()->GetEnabled()); |
| |
| // Back button |
| model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_BACK, true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(header_view->GetBackButton()->GetVisible()); |
| EXPECT_FALSE(header_view->GetBackButton()->GetEnabled()); |
| |
| model_ptr->SetEnabled(views::CAPTION_BUTTON_ICON_BACK, true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(header_view->GetBackButton()->GetEnabled()); |
| |
| // size button |
| model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(test_api.size_button()->GetVisible()); |
| EXPECT_FALSE(test_api.size_button()->GetEnabled()); |
| |
| model_ptr->SetEnabled(views::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(test_api.size_button()->GetEnabled()); |
| |
| // minimize button |
| model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_MINIMIZE, true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(test_api.minimize_button()->GetVisible()); |
| EXPECT_FALSE(test_api.minimize_button()->GetEnabled()); |
| |
| model_ptr->SetEnabled(views::CAPTION_BUTTON_ICON_MINIMIZE, true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(test_api.minimize_button()->GetEnabled()); |
| |
| // menu button |
| model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_MENU, true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(test_api.menu_button()->GetVisible()); |
| EXPECT_FALSE(test_api.menu_button()->GetEnabled()); |
| |
| model_ptr->SetEnabled(views::CAPTION_BUTTON_ICON_MENU, true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_TRUE(test_api.menu_button()->GetEnabled()); |
| |
| // zoom button |
| EXPECT_STREQ(views::kWindowControlMaximizeIcon.name, |
| test_api.size_button()->icon_definition_for_test()->name); |
| model_ptr->set_zoom_mode(true); |
| frame_view->SizeConstraintsChanged(); |
| widget->LayoutRootViewIfNecessary(); |
| EXPECT_STREQ(chromeos::kWindowControlZoomIcon.name, |
| test_api.size_button()->icon_definition_for_test()->name); |
| widget->Maximize(); |
| EXPECT_STREQ(chromeos::kWindowControlDezoomIcon.name, |
| test_api.size_button()->icon_definition_for_test()->name); |
| } |
| |
| TEST_F(FrameViewAshTest, WideFrame) { |
| auto* delegate = new FrameViewAshTestWidgetDelegate(); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate, |
| desks_util::GetActiveDeskContainerId(), gfx::Rect(100, 0, 400, 500)); |
| |
| FrameViewAsh* frame_view = delegate->frame_view(); |
| chromeos::HeaderView* header_view = frame_view->GetHeaderView(); |
| delegate->SetCanMaximize(true); |
| widget->Maximize(); |
| |
| std::unique_ptr<WideFrameView> wide_frame_view = |
| std::make_unique<WideFrameView>(widget.get()); |
| wide_frame_view->GetWidget()->Show(); |
| |
| chromeos::HeaderView* wide_header_view = wide_frame_view->header_view(); |
| display::Screen* screen = display::Screen::Get(); |
| |
| const gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); |
| gfx::Rect frame_bounds = |
| wide_frame_view->GetWidget()->GetWindowBoundsInScreen(); |
| EXPECT_EQ(work_area.width(), frame_bounds.width()); |
| EXPECT_EQ(work_area.origin(), frame_bounds.origin()); |
| EXPECT_FALSE(header_view->should_paint()); |
| EXPECT_TRUE(wide_header_view->should_paint()); |
| |
| // Test immersive. |
| ImmersiveFullscreenController controller; |
| wide_frame_view->Init(&controller); |
| EXPECT_FALSE(wide_header_view->in_immersive_mode()); |
| EXPECT_FALSE(header_view->in_immersive_mode()); |
| EXPECT_TRUE(header_view->GetVisible()); |
| |
| ImmersiveFullscreenController::EnableForWidget(widget.get(), true); |
| EXPECT_TRUE(header_view->in_immersive_mode()); |
| EXPECT_TRUE(wide_header_view->in_immersive_mode()); |
| EXPECT_TRUE(header_view->GetVisible()); |
| // The height should be ~(33 *.5) |
| wide_header_view->SetVisibleFraction(0.5); |
| EXPECT_NEAR(16, wide_header_view->GetPreferredOnScreenHeight(), 1); |
| |
| // Make sure the frame can be revaled outside of the target window. |
| EXPECT_FALSE(ImmersiveFullscreenControllerTestApi(&controller) |
| .IsTopEdgeHoverTimerRunning()); |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->MoveMouseTo(gfx::Point(10, 0)); |
| generator->MoveMouseBy(1, 0); |
| EXPECT_TRUE(ImmersiveFullscreenControllerTestApi(&controller) |
| .IsTopEdgeHoverTimerRunning()); |
| |
| generator->MoveMouseTo(gfx::Point(10, 10)); |
| generator->MoveMouseBy(1, 0); |
| EXPECT_FALSE(ImmersiveFullscreenControllerTestApi(&controller) |
| .IsTopEdgeHoverTimerRunning()); |
| |
| generator->MoveMouseTo(gfx::Point(600, 0)); |
| generator->MoveMouseBy(1, 0); |
| EXPECT_TRUE(ImmersiveFullscreenControllerTestApi(&controller) |
| .IsTopEdgeHoverTimerRunning()); |
| |
| ImmersiveFullscreenController::EnableForWidget(widget.get(), false); |
| EXPECT_FALSE(header_view->in_immersive_mode()); |
| EXPECT_FALSE(wide_header_view->in_immersive_mode()); |
| // visible fraction should be ignored in non immersive. |
| wide_header_view->SetVisibleFraction(0.5); |
| EXPECT_EQ(32, wide_header_view->GetPreferredOnScreenHeight()); |
| |
| UpdateDisplay("1234x800"); |
| EXPECT_EQ(1234, |
| wide_frame_view->GetWidget()->GetWindowBoundsInScreen().width()); |
| |
| // Double Click |
| EXPECT_TRUE(widget->IsMaximized()); |
| generator->MoveMouseToCenterOf( |
| wide_header_view->GetWidget()->GetNativeWindow()); |
| generator->DoubleClickLeftButton(); |
| EXPECT_FALSE(widget->IsMaximized()); |
| } |
| |
| TEST_F(FrameViewAshTest, WideFrameButton) { |
| auto* delegate = new FrameViewAshTestWidgetDelegate(); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate, |
| desks_util::GetActiveDeskContainerId(), gfx::Rect(100, 0, 400, 500)); |
| widget->Maximize(); |
| std::unique_ptr<WideFrameView> wide_frame_view = |
| std::make_unique<WideFrameView>(widget.get()); |
| wide_frame_view->GetWidget()->Show(); |
| chromeos::HeaderView* header_view = wide_frame_view->header_view(); |
| FrameCaptionButtonContainerView::TestApi test_api( |
| header_view->caption_button_container()); |
| |
| EXPECT_STREQ(views::kWindowControlRestoreIcon.name, |
| test_api.size_button()->icon_definition_for_test()->name); |
| |
| widget->SetFullscreen(true); |
| views::test::RunScheduledLayout(header_view); |
| EXPECT_STREQ(views::kWindowControlRestoreIcon.name, |
| test_api.size_button()->icon_definition_for_test()->name); |
| { |
| WMEvent event(WM_EVENT_PIN); |
| WindowState::Get(widget->GetNativeWindow())->OnWMEvent(&event); |
| views::test::RunScheduledLayout(header_view); |
| EXPECT_STREQ(views::kWindowControlRestoreIcon.name, |
| test_api.size_button()->icon_definition_for_test()->name); |
| } |
| { |
| WMEvent event(WM_EVENT_LOCKED_FULLSCREEN); |
| WindowState::Get(widget->GetNativeWindow())->OnWMEvent(&event); |
| views::test::RunScheduledLayout(header_view); |
| EXPECT_STREQ(views::kWindowControlRestoreIcon.name, |
| test_api.size_button()->icon_definition_for_test()->name); |
| } |
| } |
| |
| TEST_F(FrameViewAshTest, MoveFullscreenWideFrameBetweenDisplay) { |
| UpdateDisplay("800x600, 1000x600"); |
| |
| auto* screen = display::Screen::Get(); |
| auto display_list = screen->GetAllDisplays(); |
| |
| auto* delegate = new FrameViewAshTestWidgetDelegate(); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate, |
| desks_util::GetActiveDeskContainerId(), gfx::Rect(100, 0, 400, 500)); |
| widget->SetFullscreen(true); |
| std::unique_ptr<WideFrameView> wide_frame_view = |
| std::make_unique<WideFrameView>(widget.get()); |
| wide_frame_view->GetWidget()->Show(); |
| ASSERT_EQ(display_list[0].id(), |
| screen->GetDisplayNearestWindow(widget->GetNativeWindow()).id()); |
| EXPECT_EQ(800, |
| wide_frame_view->GetWidget()->GetWindowBoundsInScreen().width()); |
| |
| window_util::MoveWindowToDisplay(widget->GetNativeWindow(), |
| display_list[1].id()); |
| EXPECT_EQ(display_list[1].id(), |
| screen->GetDisplayNearestWindow(widget->GetNativeWindow()).id()); |
| EXPECT_EQ(1000, |
| wide_frame_view->GetWidget()->GetWindowBoundsInScreen().width()); |
| } |
| |
| namespace { |
| |
| class FrameViewAshFrameColorTest : public FrameViewAshTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| FrameViewAshFrameColorTest() = default; |
| |
| FrameViewAshFrameColorTest(const FrameViewAshFrameColorTest&) = delete; |
| FrameViewAshFrameColorTest& operator=(const FrameViewAshFrameColorTest&) = |
| delete; |
| |
| ~FrameViewAshFrameColorTest() override = default; |
| }; |
| |
| class TestWidgetDelegate : public TestWidgetConstraintsDelegate { |
| public: |
| TestWidgetDelegate(bool custom) : custom_(custom) {} |
| |
| TestWidgetDelegate(const TestWidgetDelegate&) = delete; |
| TestWidgetDelegate& operator=(const TestWidgetDelegate&) = delete; |
| |
| ~TestWidgetDelegate() override = default; |
| |
| // views::WidgetDelegate: |
| std::unique_ptr<views::FrameView> CreateFrameView( |
| views::Widget* widget) override { |
| if (custom_) { |
| WindowState* window_state = WindowState::Get(widget->GetNativeWindow()); |
| window_state->SetDelegate(std::make_unique<WindowStateDelegate>()); |
| } |
| return TestWidgetConstraintsDelegate::CreateFrameView(widget); |
| } |
| |
| private: |
| bool custom_; |
| }; |
| |
| } // namespace |
| |
| // Verify that FrameViewAsh updates the active color based on the |
| // kFrameActiveColorKey window property. |
| TEST_P(FrameViewAshFrameColorTest, kFrameActiveColorKey) { |
| TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam()); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| |
| SkColor active_color = |
| widget->GetNativeWindow()->GetProperty(kFrameActiveColorKey); |
| constexpr SkColor new_color = SK_ColorWHITE; |
| EXPECT_NE(active_color, new_color); |
| |
| widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, new_color); |
| active_color = widget->GetNativeWindow()->GetProperty(kFrameActiveColorKey); |
| EXPECT_EQ(active_color, new_color); |
| EXPECT_EQ(new_color, delegate->frame_view()->GetActiveFrameColorForTest()); |
| |
| // Test that changing the property updates the caption button images. |
| FrameCaptionButtonContainerView::TestApi test_api( |
| delegate->frame_view()->GetHeaderView()->caption_button_container()); |
| ui::DrawWaiterForTest::WaitForCommit(widget->GetLayer()->GetCompositor()); |
| gfx::ImageSkia original_icon_image = test_api.size_button()->icon_image(); |
| widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, SK_ColorBLACK); |
| ui::DrawWaiterForTest::WaitForCommit(widget->GetLayer()->GetCompositor()); |
| EXPECT_FALSE(original_icon_image.BackedBySameObjectAs( |
| test_api.size_button()->icon_image())); |
| } |
| |
| // Verify that FrameViewAsh updates the inactive color based on the |
| // kFrameInactiveColorKey window property. |
| TEST_P(FrameViewAshFrameColorTest, KFrameInactiveColor) { |
| TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam()); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| |
| SkColor active_color = |
| widget->GetNativeWindow()->GetProperty(kFrameInactiveColorKey); |
| constexpr SkColor new_color = SK_ColorWHITE; |
| EXPECT_NE(active_color, new_color); |
| |
| widget->GetNativeWindow()->SetProperty(kFrameInactiveColorKey, new_color); |
| active_color = widget->GetNativeWindow()->GetProperty(kFrameInactiveColorKey); |
| EXPECT_EQ(active_color, new_color); |
| EXPECT_EQ(new_color, delegate->frame_view()->GetInactiveFrameColorForTest()); |
| } |
| |
| // Verify that FrameViewAsh updates the active and inactive colors at |
| // construction. |
| TEST_P(FrameViewAshFrameColorTest, KFrameColorCtor) { |
| TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam()); |
| // Build the window, this implicit constructs the FrameView. |
| constexpr SkColor non_default_color = SK_ColorWHITE; |
| std::unique_ptr<views::Widget> widget = |
| views::test::TestWidgetBuilder() |
| .SetDelegate(delegate) |
| .SetBounds(gfx::Rect()) |
| .SetParent(Shell::GetPrimaryRootWindow()->GetChildById( |
| desks_util::GetActiveDeskContainerId())) |
| .SetShow(true) |
| .SetWindowProperty(kTrackDefaultFrameColors, false) |
| .SetWindowProperty(kFrameActiveColorKey, non_default_color) |
| .SetWindowProperty(kFrameInactiveColorKey, non_default_color) |
| .BuildOwnsNativeWidget(); |
| |
| // Check that the default color is different from the one used in the test. |
| SkColor inactive_color = |
| widget->GetNativeWindow()->GetProperty(kFrameInactiveColorKey); |
| SkColor active_color = |
| widget->GetNativeWindow()->GetProperty(kFrameActiveColorKey); |
| EXPECT_EQ(active_color, non_default_color); |
| EXPECT_EQ(inactive_color, non_default_color); |
| EXPECT_EQ(delegate->frame_view()->GetInactiveFrameColorForTest(), |
| non_default_color); |
| EXPECT_EQ(delegate->frame_view()->GetActiveFrameColorForTest(), |
| non_default_color); |
| } |
| |
| // Verify that FrameViewAsh updates the active color based on the |
| // kFrameActiveColorKey window property. |
| TEST_P(FrameViewAshFrameColorTest, WideFrameInitialColor) { |
| TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam()); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| widget->Maximize(); |
| aura::Window* window = widget->GetNativeWindow(); |
| SkColor active_color = window->GetProperty(kFrameActiveColorKey); |
| SkColor inactive_color = window->GetProperty(kFrameInactiveColorKey); |
| constexpr SkColor new_active_color = SK_ColorWHITE; |
| constexpr SkColor new_inactive_color = SK_ColorBLACK; |
| EXPECT_NE(active_color, new_active_color); |
| EXPECT_NE(inactive_color, new_inactive_color); |
| window->SetProperty(kFrameActiveColorKey, new_active_color); |
| window->SetProperty(kFrameInactiveColorKey, new_inactive_color); |
| |
| std::unique_ptr<WideFrameView> wide_frame_view = |
| std::make_unique<WideFrameView>(widget.get()); |
| chromeos::HeaderView* wide_header_view = wide_frame_view->header_view(); |
| DefaultFrameHeader* header = wide_header_view->GetFrameHeader(); |
| EXPECT_EQ(new_active_color, header->active_frame_color_); |
| EXPECT_EQ(new_inactive_color, header->inactive_frame_color_); |
| } |
| |
| // Tests to make sure that the FrameViewAsh tracks default frame colors |
| // for both light and dark mode. |
| TEST_P(FrameViewAshFrameColorTest, DefaultFrameColorsDarkAndLight) { |
| auto* dark_light_mode_controller = DarkLightModeControllerImpl::Get(); |
| dark_light_mode_controller->OnActiveUserPrefServiceChanged( |
| Shell::Get()->session_controller()->GetActivePrefService()); |
| const bool initial_dark_mode_status = |
| dark_light_mode_controller->IsDarkModeEnabled(); |
| |
| TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam()); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| aura::Window* window = widget->GetNativeWindow(); |
| |
| auto* color_provider = delegate->frame_view()->GetColorProvider(); |
| SkColor dialog_title_bar_color = |
| color_provider->GetColor(cros_tokens::kDialogTitleBarColor); |
| const SkColor initial_active_default = dialog_title_bar_color; |
| const SkColor initial_inactive_default = dialog_title_bar_color; |
| SkColor active_color = window->GetProperty(kFrameActiveColorKey); |
| SkColor inactive_color = window->GetProperty(kFrameInactiveColorKey); |
| |
| EXPECT_EQ(initial_active_default, active_color); |
| EXPECT_EQ(initial_inactive_default, inactive_color); |
| |
| // Switch the color mode |
| dark_light_mode_controller->ToggleColorMode(); |
| ASSERT_NE(initial_dark_mode_status, |
| dark_light_mode_controller->IsDarkModeEnabled()); |
| // Get the `color_provider` again as it might have changed because of the |
| // color mode change. |
| color_provider = delegate->frame_view()->GetColorProvider(); |
| dialog_title_bar_color = |
| color_provider->GetColor(cros_tokens::kDialogTitleBarColor); |
| |
| const SkColor active_default = dialog_title_bar_color; |
| const SkColor inactive_default = dialog_title_bar_color; |
| active_color = window->GetProperty(kFrameActiveColorKey); |
| inactive_color = window->GetProperty(kFrameInactiveColorKey); |
| |
| EXPECT_NE(initial_active_default, active_default); |
| EXPECT_NE(initial_inactive_default, inactive_default); |
| EXPECT_EQ(active_default, active_color); |
| EXPECT_EQ(inactive_default, inactive_color); |
| } |
| |
| // Tests to make sure that FrameViewAsh does not clobber custom frame |
| // colors when the kTrackDefaultFrameColors property is set to false. |
| TEST_P(FrameViewAshFrameColorTest, CanSetPersistentFrameColorsDarkAndLight) { |
| auto* dark_light_mode_controller = DarkLightModeControllerImpl::Get(); |
| dark_light_mode_controller->OnActiveUserPrefServiceChanged( |
| Shell::Get()->session_controller()->GetActivePrefService()); |
| const bool initial_dark_mode_status = |
| dark_light_mode_controller->IsDarkModeEnabled(); |
| |
| TestWidgetDelegate* delegate = new TestWidgetDelegate(GetParam()); |
| std::unique_ptr<views::Widget> widget = CreateTestWidget( |
| views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET, delegate); |
| aura::Window* window = widget->GetNativeWindow(); |
| |
| constexpr SkColor new_active_color = SK_ColorWHITE; |
| constexpr SkColor new_inactive_color = SK_ColorBLACK; |
| |
| EXPECT_NE(new_active_color, window->GetProperty(kFrameActiveColorKey)); |
| EXPECT_NE(new_inactive_color, window->GetProperty(kFrameInactiveColorKey)); |
| |
| window->SetProperty(kTrackDefaultFrameColors, false); |
| window->SetProperty(kFrameActiveColorKey, new_active_color); |
| window->SetProperty(kFrameInactiveColorKey, new_inactive_color); |
| |
| EXPECT_EQ(new_active_color, window->GetProperty(kFrameActiveColorKey)); |
| EXPECT_EQ(new_inactive_color, window->GetProperty(kFrameInactiveColorKey)); |
| |
| // Switch the color mode. |
| dark_light_mode_controller->ToggleColorMode(); |
| ASSERT_NE(initial_dark_mode_status, |
| dark_light_mode_controller->IsDarkModeEnabled()); |
| |
| EXPECT_EQ(new_active_color, window->GetProperty(kFrameActiveColorKey)); |
| EXPECT_EQ(new_inactive_color, window->GetProperty(kFrameInactiveColorKey)); |
| } |
| |
| // Run frame color tests with and without custom WindowStateDelegate. |
| INSTANTIATE_TEST_SUITE_P(All, FrameViewAshFrameColorTest, testing::Bool()); |
| |
| } // namespace ash |