| // 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/wm/window_state.h" |
| |
| #include <utility> |
| |
| #include "ash/constants/app_types.h" |
| #include "ash/metrics/pip_uma.h" |
| #include "ash/public/cpp/accelerators.h" |
| #include "ash/public/cpp/shelf_config.h" |
| #include "ash/public/cpp/shelf_prefs.h" |
| #include "ash/public/cpp/window_properties.h" |
| #include "ash/root_window_controller.h" |
| #include "ash/screen_util.h" |
| #include "ash/shelf/shelf.h" |
| #include "ash/shell.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/test/test_window_builder.h" |
| #include "ash/wm/desks/desks_test_util.h" |
| #include "ash/wm/overview/overview_item.h" |
| #include "ash/wm/overview/overview_test_util.h" |
| #include "ash/wm/overview/overview_utils.h" |
| #include "ash/wm/pip/pip_positioner.h" |
| #include "ash/wm/splitview/split_view_controller.h" |
| #include "ash/wm/splitview/split_view_divider.h" |
| #include "ash/wm/tablet_mode/tablet_mode_controller.h" |
| #include "ash/wm/window_resizer.h" |
| #include "ash/wm/window_state_util.h" |
| #include "ash/wm/window_util.h" |
| #include "ash/wm/wm_event.h" |
| #include "ash/wm/wm_metrics.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "chromeos/ui/base/window_state_type.h" |
| #include "chromeos/ui/frame/caption_buttons/snap_controller.h" |
| #include "chromeos/ui/frame/multitask_menu/multitask_menu_metrics.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/test/test_window_delegate.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/hit_test.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/layer_animator.h" |
| #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| #include "ui/display/screen.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/wm/core/window_util.h" |
| |
| using chromeos::WindowStateType; |
| |
| namespace ash { |
| namespace { |
| |
| class AlwaysMaximizeTestState : public WindowState::State { |
| public: |
| explicit AlwaysMaximizeTestState(WindowStateType initial_state_type) |
| : state_type_(initial_state_type) {} |
| |
| AlwaysMaximizeTestState(const AlwaysMaximizeTestState&) = delete; |
| AlwaysMaximizeTestState& operator=(const AlwaysMaximizeTestState&) = delete; |
| |
| ~AlwaysMaximizeTestState() override = default; |
| |
| // WindowState::State overrides: |
| void OnWMEvent(WindowState* window_state, const WMEvent* event) override { |
| // We don't do anything here. |
| } |
| WindowStateType GetType() const override { return state_type_; } |
| void AttachState(WindowState* window_state, |
| WindowState::State* previous_state) override { |
| // We always maximize. |
| if (state_type_ != WindowStateType::kMaximized) { |
| window_state->Maximize(); |
| state_type_ = WindowStateType::kMaximized; |
| } |
| } |
| void DetachState(WindowState* window_state) override {} |
| |
| private: |
| WindowStateType state_type_; |
| }; |
| |
| class WindowStateTest : public AshTestBase { |
| public: |
| WindowStateTest() { |
| scoped_feature_list_.InitWithFeatures( |
| /*enabled_features=*/{features::kFasterSplitScreenSetup, |
| features::kOsSettingsRevampWayfinding}, |
| /*disabled_features=*/{}); |
| } |
| WindowStateTest(const WindowStateTest&) = delete; |
| WindowStateTest& operator=(const WindowStateTest&) = delete; |
| ~WindowStateTest() override = default; |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| using Sample = base::HistogramBase::Sample; |
| |
| // Test that a window gets properly snapped to the display's edges in a |
| // multi monitor environment. |
| TEST_F(WindowStateTest, SnapWindowBasic) { |
| UpdateDisplay("0+0-500x400, 0+500-600x400"); |
| const gfx::Rect kPrimaryDisplayWorkAreaBounds = |
| display::Screen::GetScreen()->GetPrimaryDisplay().work_area(); |
| const gfx::Rect kSecondaryDisplayWorkAreaBounds = |
| GetSecondaryDisplay().work_area(); |
| |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| WindowState* window_state = WindowState::Get(window.get()); |
| const WindowSnapWMEvent snap_primary(WM_EVENT_SNAP_PRIMARY); |
| window_state->OnWMEvent(&snap_primary); |
| gfx::Rect expected = gfx::Rect(kPrimaryDisplayWorkAreaBounds.x(), |
| kPrimaryDisplayWorkAreaBounds.y(), |
| kPrimaryDisplayWorkAreaBounds.width() / 2, |
| kPrimaryDisplayWorkAreaBounds.height()); |
| EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString()); |
| |
| const WindowSnapWMEvent snap_secondary(WM_EVENT_SNAP_SECONDARY); |
| window_state->OnWMEvent(&snap_secondary); |
| expected.set_x(kPrimaryDisplayWorkAreaBounds.right() - expected.width()); |
| EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString()); |
| |
| // Move the window to the secondary display. |
| window->SetBoundsInScreen(gfx::Rect(600, 0, 100, 100), GetSecondaryDisplay()); |
| |
| window_state->OnWMEvent(&snap_secondary); |
| expected = gfx::Rect(kSecondaryDisplayWorkAreaBounds.x() + |
| kSecondaryDisplayWorkAreaBounds.width() / 2, |
| kSecondaryDisplayWorkAreaBounds.y(), |
| kSecondaryDisplayWorkAreaBounds.width() / 2, |
| kSecondaryDisplayWorkAreaBounds.height()); |
| EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString()); |
| |
| window_state->OnWMEvent(&snap_primary); |
| expected.set_x(kSecondaryDisplayWorkAreaBounds.x()); |
| EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString()); |
| } |
| |
| // Test snapped window bounds when the work area length is odd. For multiresize |
| // functionality to work, it is important that the snapped windows exactly |
| // touch. An odd work area length makes this requirement tricky because the |
| // window widths must be unequal to add up to an odd number. |
| TEST_F(WindowStateTest, SnapWindowOddWorkAreaLength) { |
| UpdateDisplay("1517x805"); |
| const gfx::Rect work_area = |
| display::Screen::GetScreen()->GetPrimaryDisplay().work_area(); |
| ASSERT_EQ(0, work_area.x()); |
| ASSERT_EQ(1517, work_area.width()); |
| |
| std::unique_ptr<aura::Window> left_window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| std::unique_ptr<aura::Window> right_window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| const WindowSnapWMEvent snap_primary(WM_EVENT_SNAP_PRIMARY); |
| const WindowSnapWMEvent snap_secondary(WM_EVENT_SNAP_SECONDARY); |
| WindowState::Get(left_window.get())->OnWMEvent(&snap_primary); |
| WindowState::Get(right_window.get())->OnWMEvent(&snap_secondary); |
| EXPECT_EQ(gfx::Rect(0, work_area.y(), 758, work_area.bottom()), |
| left_window->GetBoundsInScreen()); |
| EXPECT_EQ(gfx::Rect(758, work_area.y(), 759, work_area.bottom()), |
| right_window->GetBoundsInScreen()); |
| } |
| |
| // Test how the minimum width and maximize behavior specified by the |
| // aura::WindowDelegate affect snapping in landscape display layout. |
| TEST_F(WindowStateTest, SnapWindowMinimumSizeLandscape) { |
| UpdateDisplay("900x600"); |
| const gfx::Rect kWorkAreaBounds = |
| display::Screen::GetScreen()->GetPrimaryDisplay().work_area(); |
| |
| aura::test::TestWindowDelegate delegate; |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( |
| &delegate, -1, gfx::Rect(0, 100, kWorkAreaBounds.width() - 1, 100))); |
| |
| // It should be possible to snap a window with a minimum size. |
| const int kMinimumWidth = 750; |
| delegate.set_minimum_size(gfx::Size(kMinimumWidth, 0)); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->CanSnap()); |
| const WindowSnapWMEvent snap_secondary(WM_EVENT_SNAP_SECONDARY); |
| window_state->OnWMEvent(&snap_secondary); |
| // Expect right snap with the minimum width. |
| const gfx::Rect expected_right_snap(kWorkAreaBounds.width() - kMinimumWidth, |
| kWorkAreaBounds.y(), kMinimumWidth, |
| kWorkAreaBounds.height()); |
| EXPECT_EQ(expected_right_snap, window->GetBoundsInScreen()); |
| |
| // It should not be possible to snap a window if not maximizable. |
| window->SetProperty(aura::client::kResizeBehaviorKey, |
| window->GetProperty(aura::client::kResizeBehaviorKey) ^ |
| aura::client::kResizeBehaviorCanMaximize); |
| EXPECT_FALSE(window_state->CanSnap()); |
| window->SetProperty(aura::client::kResizeBehaviorKey, |
| window->GetProperty(aura::client::kResizeBehaviorKey) | |
| aura::client::kResizeBehaviorCanMaximize); |
| // It should be possible to snap a window if it can be maximized. |
| EXPECT_TRUE(window_state->CanSnap()); |
| } |
| |
| // Test that a unresizable snappable property allows the window to be snapped. |
| TEST_F(WindowStateTest, UnresizableWindowSnap) { |
| const std::array<bool, 2> orientation_params{false, true}; |
| |
| for (const auto is_landscape : orientation_params) { |
| UpdateDisplay(is_landscape ? "900x600,200x100" : "600x900,100x200"); |
| auto* const screen = display::Screen::GetScreen(); |
| ASSERT_EQ(2, screen->GetNumDisplays()); |
| |
| const display::Display primary_display = screen->GetAllDisplays()[0]; |
| const display::Display secondary_small_display = |
| screen->GetAllDisplays()[1]; |
| ASSERT_EQ(is_landscape, primary_display.is_landscape()); |
| ASSERT_EQ(is_landscape, secondary_small_display.is_landscape()); |
| |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| |
| // Make the window unresizable. |
| window->SetProperty(aura::client::kResizeBehaviorKey, |
| aura::client::kResizeBehaviorNone); |
| |
| auto* const window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->CanSnap()); |
| EXPECT_FALSE(window_state->CanSnapOnDisplay(primary_display)); |
| EXPECT_FALSE(window_state->CanSnapOnDisplay(secondary_small_display)); |
| |
| auto* const opposite_orientation_size = |
| is_landscape ? new gfx::Size(0, 300) : new gfx::Size(300, 0); |
| window->SetProperty(kUnresizableSnappedSizeKey, opposite_orientation_size); |
| EXPECT_FALSE(window_state->CanSnap()); |
| EXPECT_FALSE(window_state->CanSnapOnDisplay(primary_display)); |
| EXPECT_FALSE(window_state->CanSnapOnDisplay(secondary_small_display)); |
| |
| auto* const correct_orientation_size = |
| is_landscape ? new gfx::Size(300, 0) : new gfx::Size(0, 300); |
| window->SetProperty(kUnresizableSnappedSizeKey, correct_orientation_size); |
| EXPECT_TRUE(window_state->CanSnap()); |
| EXPECT_TRUE(window_state->CanSnapOnDisplay(primary_display)); |
| EXPECT_FALSE(window_state->CanSnapOnDisplay(secondary_small_display)); |
| |
| window_util::MoveWindowToDisplay(window.get(), |
| secondary_small_display.id()); |
| EXPECT_FALSE(window_state->CanSnap()); |
| EXPECT_TRUE(window_state->CanSnapOnDisplay(primary_display)); |
| EXPECT_FALSE(window_state->CanSnapOnDisplay(secondary_small_display)); |
| } |
| } |
| |
| // Test that a unresizable snappable property doesn't have any effect in tablet |
| // mode. |
| TEST_F(WindowStateTest, UnresizableWindowSnapInTablet) { |
| UpdateDisplay("900x600"); |
| |
| // Enter tablet mode. |
| Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); |
| |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| |
| window->SetProperty(aura::client::kResizeBehaviorKey, |
| aura::client::kResizeBehaviorNone); |
| window->SetProperty(kUnresizableSnappedSizeKey, new gfx::Size(300, 0)); |
| |
| auto* const window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->CanSnap()); |
| } |
| |
| // Test that a window's state type can be changed to PIP via a WM transition |
| // event. |
| TEST_F(WindowStateTest, CanTransitionToPipWindow) { |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->IsPip()); |
| |
| const WMEvent enter_pip(WM_EVENT_PIP); |
| window_state->OnWMEvent(&enter_pip); |
| EXPECT_TRUE(window_state->IsPip()); |
| } |
| |
| // Test that the PIP window is set to the `PipController` before the |
| // widget is deactivated. Regression test for http://b/309362942. |
| TEST_F(WindowStateTest, PipWindowIsSetBeforeWidgetDeactivate) { |
| // Make `background_widget` to trigger shelf visibility change after |
| // entering PIP. |
| auto background_widget = CreateTestWidget(); |
| auto* window_state = WindowState::Get(background_widget->GetNativeWindow()); |
| const WMEvent enter_fullscreen(WM_EVENT_FULLSCREEN); |
| window_state->OnWMEvent(&enter_fullscreen); |
| |
| auto pip_widget = CreateTestWidget(); |
| auto* pip_window_state = WindowState::Get(pip_widget->GetNativeWindow()); |
| const WMEvent enter_pip(WM_EVENT_PIP); |
| |
| // Entering PIP results in shelf visibility change, but it shouldn't |
| // cause any crash. |
| pip_window_state->OnWMEvent(&enter_pip); |
| } |
| |
| // Test that a PIP window cannot be snapped. |
| TEST_F(WindowStateTest, PipWindowCannotSnap) { |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->CanSnap()); |
| |
| const WMEvent enter_pip(WM_EVENT_PIP); |
| window_state->OnWMEvent(&enter_pip); |
| |
| EXPECT_FALSE(window_state->CanSnap()); |
| } |
| |
| TEST_F(WindowStateTest, ChromePipWindowUmaMetrics) { |
| base::HistogramTester histograms; |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| |
| WindowState* window_state = WindowState::Get(window.get()); |
| const WMEvent enter_pip(WM_EVENT_PIP); |
| window_state->OnWMEvent(&enter_pip); |
| |
| EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName, |
| Sample(AshPipEvents::PIP_START))); |
| EXPECT_EQ(1, |
| histograms.GetBucketCount(kAshPipEventsHistogramName, |
| Sample(AshPipEvents::CHROME_PIP_START))); |
| histograms.ExpectTotalCount(kAshPipEventsHistogramName, 2); |
| |
| const WMEvent enter_normal(WM_EVENT_NORMAL); |
| window_state->OnWMEvent(&enter_normal); |
| |
| EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName, |
| Sample(AshPipEvents::PIP_END))); |
| EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName, |
| Sample(AshPipEvents::CHROME_PIP_END))); |
| histograms.ExpectTotalCount(kAshPipEventsHistogramName, 4); |
| } |
| |
| TEST_F(WindowStateTest, AndroidPipWindowUmaMetrics) { |
| base::HistogramTester histograms; |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| window->SetProperty(aura::client::kAppType, |
| static_cast<int>(ash::AppType::ARC_APP)); |
| |
| WindowState* window_state = WindowState::Get(window.get()); |
| const WMEvent enter_pip(WM_EVENT_PIP); |
| window_state->OnWMEvent(&enter_pip); |
| |
| EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName, |
| Sample(AshPipEvents::PIP_START))); |
| EXPECT_EQ(1, |
| histograms.GetBucketCount(kAshPipEventsHistogramName, |
| Sample(AshPipEvents::ANDROID_PIP_START))); |
| histograms.ExpectTotalCount(kAshPipEventsHistogramName, 2); |
| |
| const WMEvent enter_normal(WM_EVENT_NORMAL); |
| window_state->OnWMEvent(&enter_normal); |
| |
| EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName, |
| Sample(AshPipEvents::PIP_END))); |
| EXPECT_EQ(1, |
| histograms.GetBucketCount(kAshPipEventsHistogramName, |
| Sample(AshPipEvents::ANDROID_PIP_END))); |
| histograms.ExpectTotalCount(kAshPipEventsHistogramName, 4); |
| |
| // Check time count: |
| histograms.ExpectTotalCount(kAshPipAndroidPipUseTimeHistogramName, 1); |
| } |
| |
| TEST_F(WindowStateTest, ChromePipWindowUmaMetricsCountsExitOnDestroy) { |
| base::HistogramTester histograms; |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| |
| WindowState* window_state = WindowState::Get(window.get()); |
| const WMEvent enter_pip(WM_EVENT_PIP); |
| window_state->OnWMEvent(&enter_pip); |
| |
| // Destroy the window. |
| window.reset(); |
| |
| EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName, |
| Sample(AshPipEvents::PIP_END))); |
| EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName, |
| Sample(AshPipEvents::CHROME_PIP_END))); |
| histograms.ExpectTotalCount(kAshPipEventsHistogramName, 4); |
| } |
| |
| TEST_F(WindowStateTest, AndroidPipWindowUmaMetricsCountsExitOnDestroy) { |
| base::HistogramTester histograms; |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| window->SetProperty(aura::client::kAppType, |
| static_cast<int>(ash::AppType::ARC_APP)); |
| |
| WindowState* window_state = WindowState::Get(window.get()); |
| const WMEvent enter_pip(WM_EVENT_PIP); |
| window_state->OnWMEvent(&enter_pip); |
| |
| // Destroy the window. |
| window.reset(); |
| |
| EXPECT_EQ(1, histograms.GetBucketCount(kAshPipEventsHistogramName, |
| Sample(AshPipEvents::PIP_END))); |
| EXPECT_EQ(1, |
| histograms.GetBucketCount(kAshPipEventsHistogramName, |
| Sample(AshPipEvents::ANDROID_PIP_END))); |
| histograms.ExpectTotalCount(kAshPipEventsHistogramName, 4); |
| } |
| |
| // Test that modal window dialogs can be snapped. |
| TEST_F(WindowStateTest, SnapModalWindow) { |
| UpdateDisplay("0+0-600x900"); |
| const gfx::Rect kWorkAreaBounds = |
| display::Screen::GetScreen()->GetPrimaryDisplay().work_area(); |
| |
| aura::test::TestWindowDelegate parent_delegate; |
| std::unique_ptr<aura::Window> parent_window( |
| CreateTestWindowInShellWithDelegate( |
| &parent_delegate, -1, |
| gfx::Rect(kWorkAreaBounds.width(), 0, kWorkAreaBounds.width() / 2, |
| kWorkAreaBounds.height() - 1))); |
| |
| aura::test::TestWindowDelegate delegate; |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( |
| &delegate, -1, gfx::Rect(100, 100, 400, 500))); |
| |
| delegate.set_minimum_size(gfx::Size(200, 300)); |
| |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->CanSnap()); |
| |
| ::wm::AddTransientChild(parent_window.get(), window.get()); |
| EXPECT_TRUE(window_state->CanSnap()); |
| |
| window->SetProperty(aura::client::kResizeBehaviorKey, |
| aura::client::kResizeBehaviorCanResize); |
| EXPECT_FALSE(window_state->CanSnap()); |
| |
| ::wm::RemoveTransientChild(parent_window.get(), window.get()); |
| } |
| |
| // Test that the minimum size specified by aura::WindowDelegate gets respected. |
| TEST_F(WindowStateTest, TestRespectMinimumSize) { |
| UpdateDisplay("0+0-1024x768"); |
| |
| aura::test::TestWindowDelegate delegate; |
| const gfx::Size minimum_size(gfx::Size(500, 300)); |
| delegate.set_minimum_size(minimum_size); |
| |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( |
| &delegate, -1, gfx::Rect(0, 100, 100, 100))); |
| |
| // Check that the window has the correct minimum size. |
| EXPECT_EQ(minimum_size.ToString(), window->bounds().size().ToString()); |
| |
| // Set the size to something bigger - that should work. |
| gfx::Rect bigger_bounds(700, 500, 700, 500); |
| window->SetBounds(bigger_bounds); |
| EXPECT_EQ(bigger_bounds.ToString(), window->bounds().ToString()); |
| |
| // Set the size to something smaller - that should only resize to the smallest |
| // possible size. |
| gfx::Rect smaller_bounds(700, 500, 100, 100); |
| window->SetBounds(smaller_bounds); |
| EXPECT_EQ(minimum_size.ToString(), window->bounds().size().ToString()); |
| } |
| |
| // Test that the minimum window size specified by aura::WindowDelegate does not |
| // exceed the screen size. |
| TEST_F(WindowStateTest, TestIgnoreTooBigMinimumSize) { |
| UpdateDisplay("0+0-1024x768"); |
| const gfx::Size work_area_size = |
| display::Screen::GetScreen()->GetPrimaryDisplay().work_area().size(); |
| const gfx::Size illegal_size(1280, 960); |
| const gfx::Rect illegal_bounds(gfx::Point(0, 0), illegal_size); |
| |
| aura::test::TestWindowDelegate delegate; |
| const gfx::Size minimum_size(illegal_size); |
| delegate.set_minimum_size(minimum_size); |
| |
| // The creation should force the window to respect the screen size. |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithDelegate(&delegate, -1, illegal_bounds)); |
| EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString()); |
| |
| // Trying to set the size to something bigger then the screen size should be |
| // ignored. |
| window->SetBounds(illegal_bounds); |
| EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString()); |
| |
| // Maximizing the window should not allow it to go bigger than that either. |
| WindowState* window_state = WindowState::Get(window.get()); |
| window_state->Maximize(); |
| EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString()); |
| } |
| |
| // Test that the maximum size specified by aura::WindowDelegate gets respected. |
| TEST_F(WindowStateTest, TestRespectMaximumSize) { |
| aura::test::TestWindowDelegate delegate; |
| constexpr gfx::Size max_size(300, 250); |
| constexpr gfx::Size smaller_size(100, 100); |
| constexpr gfx::Size larger_size(500, 400); |
| |
| delegate.set_maximum_size(max_size); |
| |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( |
| &delegate, -1, gfx::Rect(larger_size))); |
| |
| // Check that the window has the correct maximum size. |
| EXPECT_EQ(max_size, window->bounds().size()); |
| |
| window->SetBounds(gfx::Rect(smaller_size)); |
| EXPECT_EQ(smaller_size, window->bounds().size()); |
| |
| window->SetBounds(gfx::Rect(larger_size)); |
| EXPECT_EQ(max_size, window->bounds().size()); |
| } |
| |
| // Tests UpdateSnapRatio. (1) It should have ratio reset when window |
| // enters snapped state; (2) it should update ratio on bounds event when |
| // snapped. |
| TEST_F(WindowStateTest, UpdateSnapWidthRatioTest) { |
| UpdateDisplay("0+0-900x600"); |
| const gfx::Rect kWorkAreaBounds = |
| display::Screen::GetScreen()->GetPrimaryDisplay().work_area(); |
| aura::test::TestWindowDelegate delegate; |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( |
| &delegate, -1, gfx::Rect(100, 100, 100, 100))); |
| delegate.set_window_component(HTRIGHT); |
| WindowState* window_state = WindowState::Get(window.get()); |
| const WindowSnapWMEvent cycle_snap_primary(WM_EVENT_CYCLE_SNAP_PRIMARY); |
| window_state->OnWMEvent(&cycle_snap_primary); |
| EXPECT_EQ(WindowStateType::kPrimarySnapped, window_state->GetStateType()); |
| gfx::Rect expected = |
| gfx::Rect(kWorkAreaBounds.x(), kWorkAreaBounds.y(), |
| kWorkAreaBounds.width() / 2, kWorkAreaBounds.height()); |
| EXPECT_EQ(expected, window->GetBoundsInScreen()); |
| EXPECT_EQ(chromeos::kDefaultSnapRatio, *window_state->snap_ratio()); |
| |
| // Drag to change snapped window width. |
| const int kIncreasedWidth = 225; |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->MoveMouseTo(window->bounds().right(), window->bounds().y()); |
| generator->PressLeftButton(); |
| generator->MoveMouseTo(window->bounds().right() + kIncreasedWidth, |
| window->bounds().y()); |
| generator->ReleaseLeftButton(); |
| expected.set_width(expected.width() + kIncreasedWidth); |
| EXPECT_EQ(expected, window->GetBoundsInScreen()); |
| EXPECT_EQ(WindowStateType::kPrimarySnapped, window_state->GetStateType()); |
| EXPECT_EQ(0.75f, *window_state->snap_ratio()); |
| |
| // Another cycle snap left event will restore window state to normal. |
| window_state->OnWMEvent(&cycle_snap_primary); |
| EXPECT_EQ(WindowStateType::kNormal, window_state->GetStateType()); |
| EXPECT_TRUE(window_state->snap_ratio()); |
| |
| // Another cycle snap left event will snap window and reset snapped width |
| // ratio. |
| window_state->OnWMEvent(&cycle_snap_primary); |
| EXPECT_EQ(WindowStateType::kPrimarySnapped, window_state->GetStateType()); |
| EXPECT_EQ(chromeos::kDefaultSnapRatio, *window_state->snap_ratio()); |
| } |
| |
| // Tests that dragging and snapping the snapped window update the width ratio |
| // correctly (crbug.com/1208969). |
| TEST_F(WindowStateTest, SnapSnappedWindow) { |
| ui::ScopedAnimationDurationScaleMode test_duration_mode( |
| ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); |
| UpdateDisplay("800x600"); |
| const gfx::Rect kWorkAreaBounds = |
| display::Screen::GetScreen()->GetPrimaryDisplay().work_area(); |
| aura::test::TestWindowDelegate delegate; |
| gfx::Size window_normal_size = gfx::Size(800, 100); |
| std::unique_ptr<aura::Window> window = |
| TestWindowBuilder() |
| .SetBounds(gfx::Rect(window_normal_size)) |
| .SetDelegate(&delegate) |
| .AllowAllWindowStates() |
| .Build(); |
| delegate.set_window_component(HTCAPTION); |
| WindowState* window_state = WindowState::Get(window.get()); |
| const WindowSnapWMEvent cycle_snap_primary(WM_EVENT_CYCLE_SNAP_PRIMARY); |
| window_state->OnWMEvent(&cycle_snap_primary); |
| |
| // Snap window to primary position (left). |
| EXPECT_EQ(WindowStateType::kPrimarySnapped, window_state->GetStateType()); |
| gfx::Rect expected = |
| gfx::Rect(kWorkAreaBounds.x(), kWorkAreaBounds.y(), |
| kWorkAreaBounds.width() / 2, kWorkAreaBounds.height()); |
| // Wait for the snapped animation to complete and test that the window bound |
| // is primary-snapped and the snap width ratio is updated. |
| window->layer()->GetAnimator()->Step(base::TimeTicks::Now() + |
| base::Seconds(1)); |
| EXPECT_EQ(expected, window->GetBoundsInScreen()); |
| EXPECT_EQ(chromeos::kDefaultSnapRatio, *window_state->snap_ratio()); |
| |
| // Drag the window to unsnap but do not release. |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->MoveMouseTo(window->bounds().CenterPoint()); |
| generator->PressLeftButton(); |
| generator->MoveMouseBy(5, 0); |
| // While dragged, the window size should restore to its normal bound. |
| EXPECT_EQ(window_normal_size, window->bounds().size()); |
| EXPECT_EQ(1.0f, *window_state->snap_ratio()); |
| |
| // Continue dragging the window and snap it back to the same position. |
| generator->MoveMouseBy(-405, 0); |
| generator->ReleaseLeftButton(); |
| |
| // The snapped ratio should be correct regardless of whether the animation |
| // is finished or not. |
| EXPECT_EQ(chromeos::kDefaultSnapRatio, *window_state->snap_ratio()); |
| } |
| |
| // Test that snapping left/right preserves the restore bounds. |
| TEST_F(WindowStateTest, RestoreBounds) { |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| WindowState* window_state = WindowState::Get(window.get()); |
| |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| |
| // 1) Start with restored window with restore bounds set. |
| gfx::Rect restore_bounds = window->GetBoundsInScreen(); |
| restore_bounds.set_width(restore_bounds.width() + 1); |
| window_state->SetRestoreBoundsInScreen(restore_bounds); |
| const WindowSnapWMEvent snap_primary(WM_EVENT_SNAP_PRIMARY); |
| window_state->OnWMEvent(&snap_primary); |
| const WindowSnapWMEvent snap_secondary(WM_EVENT_SNAP_SECONDARY); |
| window_state->OnWMEvent(&snap_secondary); |
| EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString()); |
| EXPECT_EQ(restore_bounds.ToString(), |
| window_state->GetRestoreBoundsInScreen().ToString()); |
| window_state->Restore(); |
| EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString()); |
| |
| // 2) Start with restored bounds set as a result of maximizing the window. |
| window_state->Maximize(); |
| gfx::Rect maximized_bounds = window->GetBoundsInScreen(); |
| EXPECT_NE(maximized_bounds.ToString(), restore_bounds.ToString()); |
| EXPECT_EQ(restore_bounds.ToString(), |
| window_state->GetRestoreBoundsInScreen().ToString()); |
| |
| window_state->OnWMEvent(&snap_primary); |
| EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString()); |
| EXPECT_NE(maximized_bounds.ToString(), |
| window->GetBoundsInScreen().ToString()); |
| EXPECT_EQ(restore_bounds.ToString(), |
| window_state->GetRestoreBoundsInScreen().ToString()); |
| |
| window_state->Restore(); |
| EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString()); |
| } |
| |
| // Test that maximizing an auto managed window, then snapping it puts the window |
| // at the snapped bounds and not at the auto-managed (centered) bounds. |
| TEST_F(WindowStateTest, AutoManaged) { |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); |
| WindowState* window_state = WindowState::Get(window.get()); |
| window_state->SetWindowPositionManaged(true); |
| window->Hide(); |
| window->SetBounds(gfx::Rect(100, 100, 100, 100)); |
| window->Show(); |
| |
| window_state->Maximize(); |
| const WindowSnapWMEvent snap_secondary(WM_EVENT_SNAP_SECONDARY); |
| window_state->OnWMEvent(&snap_secondary); |
| |
| const gfx::Rect kWorkAreaBounds = |
| display::Screen::GetScreen()->GetPrimaryDisplay().work_area(); |
| gfx::Rect expected_snapped_bounds( |
| kWorkAreaBounds.x() + kWorkAreaBounds.width() / 2, kWorkAreaBounds.y(), |
| kWorkAreaBounds.width() / 2, kWorkAreaBounds.height()); |
| EXPECT_EQ(expected_snapped_bounds.ToString(), |
| window->GetBoundsInScreen().ToString()); |
| |
| // The window should still be auto managed despite being right maximized. |
| EXPECT_TRUE(window_state->GetWindowPositionManaged()); |
| } |
| |
| // Test that the replacement of a State object works as expected. |
| TEST_F(WindowStateTest, SimpleStateSwap) { |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->IsMaximized()); |
| window_state->SetStateObject(std::unique_ptr<WindowState::State>( |
| new AlwaysMaximizeTestState(window_state->GetStateType()))); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| } |
| |
| // Test that the replacement of a state object, following a restore with the |
| // original one restores the window to its original state. |
| TEST_F(WindowStateTest, StateSwapRestore) { |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->IsMaximized()); |
| std::unique_ptr<WindowState::State> old( |
| window_state->SetStateObject(std::unique_ptr<WindowState::State>( |
| new AlwaysMaximizeTestState(window_state->GetStateType())))); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| window_state->SetStateObject(std::move(old)); |
| EXPECT_FALSE(window_state->IsMaximized()); |
| } |
| |
| // Tests that a window that had same bounds as the work area shrinks after the |
| // window is maximized and then restored. |
| TEST_F(WindowStateTest, RestoredWindowBoundsShrink) { |
| UpdateDisplay("0+0-600x900"); |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->IsMaximized()); |
| gfx::Rect work_area = |
| display::Screen::GetScreen()->GetPrimaryDisplay().work_area(); |
| |
| window->SetBounds(work_area); |
| window_state->Maximize(); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| EXPECT_EQ(work_area.ToString(), window->bounds().ToString()); |
| |
| window_state->Restore(); |
| EXPECT_FALSE(window_state->IsMaximized()); |
| EXPECT_NE(work_area.ToString(), window->bounds().ToString()); |
| EXPECT_TRUE(work_area.Contains(window->bounds())); |
| } |
| |
| TEST_F(WindowStateTest, DoNotResizeMaximizedWindowInFullscreen) { |
| const int shelf_inset_first = 600 - ShelfConfig::Get()->shelf_size(); |
| const int shelf_inset_second = 700 - ShelfConfig::Get()->shelf_size(); |
| std::unique_ptr<aura::Window> maximized(CreateTestWindowInShellWithId(0)); |
| std::unique_ptr<aura::Window> fullscreen(CreateTestWindowInShellWithId(1)); |
| WindowState* maximized_state = WindowState::Get(maximized.get()); |
| maximized_state->Maximize(); |
| ASSERT_TRUE(maximized_state->IsMaximized()); |
| EXPECT_EQ(gfx::Rect(0, 0, 800, shelf_inset_first).ToString(), |
| maximized->GetBoundsInScreen().ToString()); |
| |
| // Entering fullscreen mode will not update the maximized window's size |
| // under fullscreen. |
| WMEvent fullscreen_event(WM_EVENT_FULLSCREEN); |
| WindowState* fullscreen_state = WindowState::Get(fullscreen.get()); |
| fullscreen_state->OnWMEvent(&fullscreen_event); |
| ASSERT_TRUE(fullscreen_state->IsFullscreen()); |
| ASSERT_TRUE(maximized_state->IsMaximized()); |
| EXPECT_EQ(gfx::Rect(0, 0, 800, shelf_inset_first).ToString(), |
| maximized->GetBoundsInScreen().ToString()); |
| |
| // Updating display size will update the maximum window size. |
| UpdateDisplay("900x700"); |
| EXPECT_EQ("0,0 900x700", maximized->GetBoundsInScreen().ToString()); |
| fullscreen.reset(); |
| |
| // Exiting fullscreen will update the maximized window to the work area. |
| EXPECT_EQ(gfx::Rect(0, 0, 900, shelf_inset_second).ToString(), |
| maximized->GetBoundsInScreen().ToString()); |
| } |
| |
| TEST_F(WindowStateTest, TrustedPinned) { |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->IsTrustedPinned()); |
| window_util::PinWindow(window.get(), true /* trusted */); |
| EXPECT_TRUE(window_state->IsTrustedPinned()); |
| |
| gfx::Rect work_area = |
| display::Screen::GetScreen()->GetPrimaryDisplay().work_area(); |
| EXPECT_EQ(work_area.ToString(), window->bounds().ToString()); |
| |
| // Sending non-unpin/non-workspace related event should be ignored. |
| { |
| const WMEvent fullscreen_event(WM_EVENT_FULLSCREEN); |
| window_state->OnWMEvent(&fullscreen_event); |
| } |
| EXPECT_TRUE(window_state->IsTrustedPinned()); |
| |
| // Update display triggers workspace event. |
| UpdateDisplay("300x200"); |
| EXPECT_EQ("0,0 300x200", window->GetBoundsInScreen().ToString()); |
| |
| // Unpin should work. |
| window_state->Restore(); |
| EXPECT_FALSE(window_state->IsTrustedPinned()); |
| } |
| |
| TEST_F(WindowStateTest, AllowSetBoundsDirect) { |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->IsMaximized()); |
| gfx::Rect work_area = |
| display::Screen::GetScreen()->GetPrimaryDisplay().work_area(); |
| gfx::Rect original_bounds(50, 50, 200, 200); |
| window->SetBounds(original_bounds); |
| ASSERT_EQ(original_bounds, window->bounds()); |
| |
| window_state->set_allow_set_bounds_direct(true); |
| window_state->Maximize(); |
| |
| EXPECT_TRUE(window_state->IsMaximized()); |
| EXPECT_EQ(work_area, window->bounds()); |
| |
| gfx::Rect new_bounds(10, 10, 300, 300); |
| window->SetBounds(new_bounds); |
| EXPECT_EQ(new_bounds, window->bounds()); |
| |
| window_state->Restore(); |
| EXPECT_FALSE(window_state->IsMaximized()); |
| EXPECT_EQ(original_bounds, window->bounds()); |
| |
| window_state->set_allow_set_bounds_direct(false); |
| window_state->Maximize(); |
| |
| EXPECT_TRUE(window_state->IsMaximized()); |
| EXPECT_EQ(work_area, window->bounds()); |
| window->SetBounds(new_bounds); |
| EXPECT_EQ(work_area, window->bounds()); |
| } |
| |
| TEST_F(WindowStateTest, FullscreenMinimizedSwitching) { |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); |
| WindowState* window_state = WindowState::Get(window.get()); |
| |
| ToggleFullScreen(window_state, nullptr); |
| ASSERT_TRUE(window_state->IsFullscreen()); |
| |
| // Toggling the fullscreen window should restore to normal. |
| ToggleFullScreen(window_state, nullptr); |
| ASSERT_TRUE(window_state->IsNormalStateType()); |
| |
| window_state->Maximize(); |
| ASSERT_TRUE(window_state->IsMaximized()); |
| |
| ToggleFullScreen(window_state, nullptr); |
| ASSERT_TRUE(window_state->IsFullscreen()); |
| |
| // Toggling the fullscreen window should restore to maximized. |
| ToggleFullScreen(window_state, nullptr); |
| ASSERT_TRUE(window_state->IsMaximized()); |
| |
| ToggleFullScreen(window_state, nullptr); |
| ASSERT_TRUE(window_state->IsFullscreen()); |
| |
| // Minimize from fullscreen. |
| window_state->Minimize(); |
| ASSERT_TRUE(window_state->IsMinimized()); |
| |
| // Unminimize should restore to fullscreen. |
| window_state->Unminimize(); |
| ASSERT_TRUE(window_state->IsFullscreen()); |
| |
| // Toggling the fullscreen window should restore to maximized. |
| ToggleFullScreen(window_state, nullptr); |
| ASSERT_TRUE(window_state->IsMaximized()); |
| |
| // Minimize from fullscreen. |
| window_state->Minimize(); |
| ASSERT_TRUE(window_state->IsMinimized()); |
| |
| // Fullscreen a minimized window. |
| ToggleFullScreen(window_state, nullptr); |
| ASSERT_TRUE(window_state->IsFullscreen()); |
| |
| // Toggling the fullscreen window should not return to minimized. It should |
| // return to the state before minimizing and fullscreen. |
| ToggleFullScreen(window_state, nullptr); |
| ASSERT_TRUE(window_state->IsMaximized()); |
| } |
| |
| TEST_F(WindowStateTest, FullscreenToCurrentDisplayExplicitly) { |
| UpdateDisplay("800x600,1024x768"); |
| const auto& displays = display_manager()->active_display_list(); |
| ASSERT_EQ(displays.size(), 2u); |
| EXPECT_EQ(displays[0].size(), gfx::Size(800, 600)); |
| EXPECT_EQ(displays[1].size(), gfx::Size(1024, 768)); |
| |
| display::Screen* screen = display::Screen::GetScreen(); |
| |
| // Start from the 1st display. |
| const gfx::Rect initial_bounds(100, 10, 200, 100); |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(initial_bounds)); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[0].id()); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->IsFullscreen()); |
| |
| // Fullscreen onto current display explicitly. |
| ::wm::SetWindowFullscreen(window.get(), true, displays[0].id()); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[0].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), displays[0].bounds()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| |
| // Restore back to current display. |
| ToggleFullScreen(window_state, nullptr); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[0].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), initial_bounds); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| } |
| |
| TEST_F(WindowStateTest, FullscreenToAnotherDisplayFromNormal) { |
| UpdateDisplay("800x600,1024x768,1280x720"); |
| const auto& displays = display_manager()->active_display_list(); |
| ASSERT_EQ(displays.size(), 3u); |
| EXPECT_EQ(displays[0].size(), gfx::Size(800, 600)); |
| EXPECT_EQ(displays[1].size(), gfx::Size(1024, 768)); |
| EXPECT_EQ(displays[2].size(), gfx::Size(1280, 720)); |
| |
| display::Screen* screen = display::Screen::GetScreen(); |
| |
| // Start from the 2nd display. |
| const gfx::Rect initial_bounds(900, 10, 200, 100); |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(initial_bounds)); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->IsFullscreen()); |
| |
| // Fullscreen onto 3rd display. |
| ::wm::SetWindowFullscreen(window.get(), true, displays[2].id()); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[2].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), displays[2].bounds()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| |
| // Restore back to 2nd display. |
| ToggleFullScreen(window_state, nullptr); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), initial_bounds); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| } |
| |
| TEST_F(WindowStateTest, FullscreenToAnotherDisplayFromOtherStates) { |
| UpdateDisplay("800x600,1024x768,1280x720"); |
| const auto& displays = display_manager()->active_display_list(); |
| ASSERT_EQ(displays.size(), 3u); |
| EXPECT_EQ(displays[0].size(), gfx::Size(800, 600)); |
| EXPECT_EQ(displays[1].size(), gfx::Size(1024, 768)); |
| EXPECT_EQ(displays[2].size(), gfx::Size(1280, 720)); |
| |
| display::Screen* screen = display::Screen::GetScreen(); |
| |
| // Start from the 2nd display. |
| const gfx::Rect initial_bounds(900, 10, 200, 100); |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(initial_bounds)); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->IsFullscreen()); |
| |
| const WindowSnapWMEvent snap_right_event(WM_EVENT_SNAP_SECONDARY); |
| window_state->OnWMEvent(&snap_right_event); |
| EXPECT_TRUE(window_state->IsSnapped()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| const gfx::Rect snapped_bounds = window_state->GetCurrentBoundsInScreen(); |
| |
| window_state->Maximize(); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| const gfx::Rect maximized_bounds = window_state->GetCurrentBoundsInScreen(); |
| EXPECT_EQ(maximized_bounds, displays[1].work_area()); |
| |
| // Fullscreen onto 3rd display. |
| ::wm::SetWindowFullscreen(window.get(), true, displays[2].id()); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[2].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), displays[2].bounds()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| |
| // Restore back to 2nd display maximized. |
| ToggleFullScreen(window_state, nullptr); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), maximized_bounds); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| |
| // Restore again back to snapped. |
| window_state->Restore(); |
| EXPECT_TRUE(window_state->IsSnapped()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), snapped_bounds); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| |
| // Restore again back to normal state. |
| window_state->Restore(); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), initial_bounds); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| } |
| |
| TEST_F(WindowStateTest, FullscreenToAnotherDisplayFromFullscreen) { |
| UpdateDisplay("800x600,1024x768,1280x720"); |
| const auto& displays = display_manager()->active_display_list(); |
| ASSERT_EQ(displays.size(), 3u); |
| EXPECT_EQ(displays[0].size(), gfx::Size(800, 600)); |
| EXPECT_EQ(displays[1].size(), gfx::Size(1024, 768)); |
| EXPECT_EQ(displays[2].size(), gfx::Size(1280, 720)); |
| |
| display::Screen* screen = display::Screen::GetScreen(); |
| |
| // Start from the 2nd display. |
| const gfx::Rect initial_bounds(900, 10, 200, 100); |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(initial_bounds)); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->IsFullscreen()); |
| |
| // Fullscreen onto 2nd display. |
| ToggleFullScreen(window_state, nullptr); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), displays[1].bounds()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| |
| // Fullscreen onto 3rd display. |
| ::wm::SetWindowFullscreen(window.get(), true, displays[2].id()); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[2].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), displays[2].bounds()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| |
| // Restore back to normal state. |
| ToggleFullScreen(window_state, nullptr); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), initial_bounds); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| } |
| |
| TEST_F(WindowStateTest, FullscreenToAnotherDisplayWithMinimize) { |
| UpdateDisplay("800x600,1024x768,1280x720"); |
| const auto& displays = display_manager()->active_display_list(); |
| ASSERT_EQ(displays.size(), 3u); |
| EXPECT_EQ(displays[0].size(), gfx::Size(800, 600)); |
| EXPECT_EQ(displays[1].size(), gfx::Size(1024, 768)); |
| EXPECT_EQ(displays[2].size(), gfx::Size(1280, 720)); |
| |
| display::Screen* screen = display::Screen::GetScreen(); |
| |
| // Start from the 2nd display. |
| const gfx::Rect initial_bounds(900, 10, 200, 100); |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(initial_bounds)); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_FALSE(window_state->IsFullscreen()); |
| |
| window_state->Maximize(); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| const gfx::Rect maximized_bounds = window_state->GetCurrentBoundsInScreen(); |
| EXPECT_EQ(maximized_bounds, displays[1].work_area()); |
| |
| // Fullscreen onto 3rd display. |
| ::wm::SetWindowFullscreen(window.get(), true, displays[2].id()); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[2].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), displays[2].bounds()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| |
| // Minimize and restore. |
| window_state->Minimize(); |
| EXPECT_TRUE(window_state->IsMinimized()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| |
| window_state->Restore(); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[2].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), displays[2].bounds()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| |
| // Restore back to 2nd display snapped. |
| ToggleFullScreen(window_state, nullptr); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), maximized_bounds); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), initial_bounds); |
| |
| // Restore again back to normal state. |
| window_state->Restore(); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_EQ(screen->GetDisplayNearestWindow(window.get()).id(), |
| displays[1].id()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), initial_bounds); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| } |
| |
| // Tests the window state changes in multi displays while dragging the window to |
| // a different display through mouse. |
| TEST_F(WindowStateTest, MouseDragWindowInMultiDisplays) { |
| UpdateDisplay("0+0-800x600,801+0-1024x768"); |
| const auto& displays = display_manager()->active_display_list(); |
| |
| // Starts with kDefault window state in the 1st display. |
| display::Screen* screen = display::Screen::GetScreen(); |
| const gfx::Rect initial_bounds(10, 20, 200, 100); |
| |
| aura::test::TestWindowDelegate test_window_delegate; |
| test_window_delegate.set_window_component(HTCAPTION); |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithDelegateAndType( |
| &test_window_delegate, aura::client::WINDOW_TYPE_NORMAL, 0, |
| initial_bounds)); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_EQ(displays[0].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| |
| const std::vector<chromeos::WindowStateType>& restore_stack = |
| window_state->window_state_restore_history(); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| // Transition to kPrimarySnapped window state. |
| const WindowSnapWMEvent snap_primary(WM_EVENT_SNAP_PRIMARY); |
| window_state->OnWMEvent(&snap_primary); |
| EXPECT_TRUE(window_state->IsSnapped()); |
| EXPECT_EQ(displays[0].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| EXPECT_EQ(initial_bounds, window_state->GetRestoreBoundsInScreen()); |
| ASSERT_EQ(1u, restore_stack.size()); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| |
| // Then transition to kMaximized window state. |
| const WMEvent maximize_event(WM_EVENT_MAXIMIZE); |
| window_state->OnWMEvent(&maximize_event); |
| EXPECT_EQ(displays[0].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| ASSERT_EQ(2u, restore_stack.size()); |
| EXPECT_EQ(initial_bounds, window_state->GetRestoreBoundsInScreen()); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kPrimarySnapped); |
| |
| // Mouse drag the window to the 2nd display. Both the restore bounds property |
| // and resotore bounds inside the history stack should be updated to bounds |
| // inside the 2nd display. |
| ui::test::EventGenerator event_generator(window->GetRootWindow(), |
| window.get()); |
| const gfx::Rect display2_bounds = displays[1].bounds(); |
| event_generator.DragMouseTo(display2_bounds.CenterPoint()); |
| EXPECT_EQ(displays[1].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| EXPECT_TRUE(window_state->IsSnapped()); |
| EXPECT_TRUE( |
| display2_bounds.Contains(window_state->GetRestoreBoundsInScreen())); |
| EXPECT_EQ(1u, restore_stack.size()); |
| |
| // Maximize the window, it should stay inside the 2nd display. |
| window_state->OnWMEvent(&maximize_event); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| EXPECT_EQ(displays[1].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| |
| // Restore the window, it should go back to snapped state and stay inside the |
| // 2nd display. |
| window_state->Restore(); |
| EXPECT_TRUE(window_state->IsSnapped()); |
| EXPECT_EQ(displays[1].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| |
| // Restore the window, it should further go back to normal state and stay |
| // inside the 2nd display. |
| window_state->Restore(); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_EQ(displays[1].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| EXPECT_TRUE(restore_stack.empty()); |
| } |
| |
| // Tests the window state changes in multi displays while moving the window to a |
| // different display through shortcut. |
| TEST_F(WindowStateTest, ShortcutMovingWindowInMultiDisplays) { |
| UpdateDisplay("0+0-800x600,801+0-1024x768"); |
| const auto& displays = display_manager()->active_display_list(); |
| |
| // Starts with kDefault window state in the 1st display. |
| display::Screen* screen = display::Screen::GetScreen(); |
| const gfx::Rect initial_bounds(10, 20, 200, 100); |
| std::unique_ptr<aura::Window> window = CreateAppWindow(initial_bounds); |
| WindowState* window_state = WindowState::Get(window.get()); |
| |
| // Snap and then maximize the window in the 1st display to get the restore |
| // bounds property and history stack. |
| const WindowSnapWMEvent snap_primary(WM_EVENT_SNAP_PRIMARY); |
| window_state->OnWMEvent(&snap_primary); |
| const WMEvent maximize_event(WM_EVENT_MAXIMIZE); |
| window_state->OnWMEvent(&maximize_event); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| |
| // Using the shortcut ALT+SEARCH+M to move the window to the 2nd display. |
| PressAndReleaseKey(ui::VKEY_M, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN); |
| EXPECT_EQ(displays[1].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| // The restore bounds should be updated to bounds inside the new display. |
| const std::vector<chromeos::WindowStateType>& restore_stack = |
| window_state->window_state_restore_history(); |
| const gfx::Rect display2_bounds = displays[1].bounds(); |
| EXPECT_TRUE( |
| display2_bounds.Contains(window_state->GetRestoreBoundsInScreen())); |
| EXPECT_EQ(2u, restore_stack.size()); |
| |
| // Restore the window, it should go back to snapped state and stay inside the |
| // 2nd display. |
| window_state->Restore(); |
| EXPECT_TRUE(window_state->IsSnapped()); |
| EXPECT_EQ(displays[1].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| |
| // Maximize the window, it should stay inside the 2nd display. |
| window_state->OnWMEvent(&maximize_event); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| EXPECT_EQ(displays[1].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| |
| // Restore the maximized window, it should go back to snapped state and stay |
| // inside the 2nd display. |
| window_state->Restore(); |
| EXPECT_TRUE(window_state->IsSnapped()); |
| EXPECT_EQ(displays[1].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| |
| // Restore the window, it should further go back to normal state and stay |
| // inside the 2nd display. |
| window_state->Restore(); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_EQ(displays[1].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| } |
| |
| // Tests that the window should not be almost offscreen while being moved to |
| // another display with multiple historical window states. |
| TEST_F(WindowStateTest, WindowNoOffscreenInMultiDisplays) { |
| UpdateDisplay("0+0-800x600,801+0-1024x768"); |
| const auto& displays = display_manager()->active_display_list(); |
| |
| // Starts with kDefault window state in the 2nd display. |
| display::Screen* screen = display::Screen::GetScreen(); |
| const gfx::Rect initial_bounds(900, 10, 200, 100); |
| std::unique_ptr<aura::Window> window = CreateAppWindow(initial_bounds); |
| WindowState* window_state = WindowState::Get(window.get()); |
| |
| // Maximize and then fullscreen the window inside the 2nd display to get the |
| // restore bounds property and restore history stack. |
| const WMEvent maximize_event(WM_EVENT_MAXIMIZE); |
| window_state->OnWMEvent(&maximize_event); |
| const WMEvent toggle_fullscreen_event(WM_EVENT_TOGGLE_FULLSCREEN); |
| window_state->OnWMEvent(&toggle_fullscreen_event); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_EQ(displays[1].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| |
| // Detach the display. The window should be moved to the 1st display and stay |
| // in fullscreen state. |
| UpdateDisplay("800x600"); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_EQ(displays[0].id(), |
| screen->GetDisplayNearestWindow(window.get()).id()); |
| |
| // Un-fullscreen the window. It should be restored to maximize state. |
| window_state->OnWMEvent(&toggle_fullscreen_event); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| EXPECT_TRUE(displays[0].bounds().Contains(window->bounds())); |
| |
| // Restore the window, it should go back to normal state and fully inside the |
| // display. No offscreen should happen. |
| window_state->Restore(); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_TRUE(displays[0].bounds().Contains(window->bounds())); |
| } |
| |
| TEST_F(WindowStateTest, CanFullscreen) { |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| WindowState* window_state = WindowState::Get(window.get()); |
| |
| // Allow everything to test for cross interactions with other flags. |
| int behavior = ~aura::client::kResizeBehaviorCanFullscreen; |
| |
| window->SetProperty(aura::client::kResizeBehaviorKey, |
| behavior | aura::client::kResizeBehaviorCanFullscreen); |
| EXPECT_TRUE(window_state->CanFullscreen()); |
| ToggleFullScreen(window_state, nullptr); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| ToggleFullScreen(window_state, nullptr); |
| EXPECT_FALSE(window_state->IsFullscreen()); |
| |
| window->SetProperty(aura::client::kResizeBehaviorKey, |
| behavior & ~aura::client::kResizeBehaviorCanFullscreen); |
| EXPECT_FALSE(window_state->CanFullscreen()); |
| ToggleFullScreen(window_state, nullptr); |
| EXPECT_FALSE(window_state->IsFullscreen()); |
| } |
| |
| TEST_F(WindowStateTest, CanConsumeSystemKeys) { |
| std::unique_ptr<aura::Window> window( |
| CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); |
| WindowState* window_state = WindowState::Get(window.get()); |
| |
| EXPECT_FALSE(window_state->CanConsumeSystemKeys()); |
| |
| window->SetProperty(kCanConsumeSystemKeysKey, true); |
| EXPECT_TRUE(window_state->CanConsumeSystemKeys()); |
| } |
| |
| TEST_F(WindowStateTest, |
| RestoreStateAfterEnteringPipViaOcculusionAndDismissingPip) { |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); |
| WindowState* window_state = WindowState::Get(window.get()); |
| window->Show(); |
| EXPECT_TRUE(window->layer()->visible()); |
| |
| // Ensure a maximized window gets maximized again after it enters PIP via |
| // occlusion, gets minimized, and unminimized. |
| window_state->Maximize(); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| |
| const WMEvent enter_pip(WM_EVENT_PIP); |
| window_state->OnWMEvent(&enter_pip); |
| EXPECT_TRUE(window_state->IsPip()); |
| |
| window_state->Minimize(); |
| EXPECT_TRUE(window_state->IsMinimized()); |
| |
| window_state->Unminimize(); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| |
| // Ensure a freeform window gets freeform again after it enters PIP via |
| // occulusion, gets minimized, and unminimized. |
| ::wm::SetWindowState(window.get(), ui::SHOW_STATE_NORMAL); |
| |
| window_state->OnWMEvent(&enter_pip); |
| EXPECT_TRUE(window_state->IsPip()); |
| |
| window_state->Minimize(); |
| EXPECT_TRUE(window_state->IsMinimized()); |
| |
| window_state->Unminimize(); |
| EXPECT_TRUE(window_state->GetStateType() == WindowStateType::kNormal); |
| } |
| |
| TEST_F(WindowStateTest, RestoreStateAfterEnterPipViaMinimizeAndDismissingPip) { |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); |
| WindowState* window_state = WindowState::Get(window.get()); |
| window->Show(); |
| EXPECT_TRUE(window->layer()->visible()); |
| |
| // Ensure a maximized window gets maximized again after it enters PIP via |
| // minimize, gets minimized, and unminimized. |
| window_state->Maximize(); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| |
| window_state->Minimize(); |
| EXPECT_TRUE(window_state->IsMinimized()); |
| |
| const WMEvent enter_pip(WM_EVENT_PIP); |
| window_state->OnWMEvent(&enter_pip); |
| EXPECT_TRUE(window_state->IsPip()); |
| |
| window_state->Minimize(); |
| EXPECT_TRUE(window_state->IsMinimized()); |
| |
| window_state->Unminimize(); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| |
| // Ensure a freeform window gets freeform again after it enters PIP via |
| // minimize, gets minimized, and unminimized. |
| ::wm::SetWindowState(window.get(), ui::SHOW_STATE_NORMAL); |
| |
| window_state->Minimize(); |
| EXPECT_TRUE(window_state->IsMinimized()); |
| |
| window_state->OnWMEvent(&enter_pip); |
| EXPECT_TRUE(window_state->IsPip()); |
| |
| window_state->Minimize(); |
| EXPECT_TRUE(window_state->IsMinimized()); |
| |
| window_state->Unminimize(); |
| EXPECT_TRUE(window_state->GetStateType() == WindowStateType::kNormal); |
| } |
| |
| TEST_F(WindowStateTest, SetBoundsUpdatesSizeOfPipRestoreBounds) { |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); |
| WindowState* window_state = WindowState::Get(window.get()); |
| window->Show(); |
| window->SetBounds(gfx::Rect(0, 0, 50, 50)); |
| |
| const WMEvent enter_pip(WM_EVENT_PIP); |
| window_state->OnWMEvent(&enter_pip); |
| |
| EXPECT_TRUE(window_state->IsPip()); |
| EXPECT_TRUE(window_state->HasRestoreBounds()); |
| EXPECT_EQ(gfx::Rect(8, 8, 50, 50), window_state->GetRestoreBoundsInScreen()); |
| window_state->window()->SetBounds(gfx::Rect(100, 100, 100, 100)); |
| // SetBounds only updates the size of the restore bounds. |
| EXPECT_EQ(gfx::Rect(8, 8, 100, 100), |
| window_state->GetRestoreBoundsInScreen()); |
| } |
| |
| TEST_F(WindowStateTest, SetBoundsSnapsPipBoundsToScreenEdge) { |
| UpdateDisplay("600x900"); |
| // Create a new PiP window using TestWindowBuilder(). |
| // Set SetShow to false upon creation to simulate the window being created |
| // as a PiP rather than being changed to PiP. |
| aura::test::TestWindowDelegate delegate; |
| delegate.set_minimum_size(gfx::Size(51, 51)); |
| std::unique_ptr<aura::Window> window( |
| TestWindowBuilder() |
| .AllowAllWindowStates() |
| .SetBounds(gfx::Rect(541, 50, 50, 50)) |
| .SetDelegate(&delegate) |
| .SetShow(false) |
| .Build() |
| .release()); |
| WindowState* window_state = WindowState::Get(window.get()); |
| const WMEvent enter_pip(WM_EVENT_PIP); |
| window_state->OnWMEvent(&enter_pip); |
| window->SetProperty(aura::client::kZOrderingKey, |
| ui::ZOrderLevel::kFloatingWindow); |
| EXPECT_TRUE(window_state->IsPip()); |
| window->Show(); |
| |
| // Ensure that SnapFraction is set when entering PiP. |
| EXPECT_TRUE(PipPositioner::HasSnapFraction(window_state)); |
| |
| // Ensure that the PIP window is along the right edge of the screen even when |
| // the new bounds is adjusted by the minimum size. |
| // 541 (left origin) + 51 (PIP width) + 8 (PIP insets) == 600. |
| EXPECT_EQ(gfx::Rect(541, 50, 51, 51), |
| window_state->window()->GetBoundsInScreen()); |
| |
| PipPositioner::SaveSnapFraction(window_state, |
| window_state->window()->GetBoundsInScreen()); |
| |
| // Ensure that SnapFraction is set. |
| EXPECT_TRUE(PipPositioner::HasSnapFraction(window_state)); |
| |
| // Ensure PiP is set to correct position. |
| EXPECT_EQ(gfx::Rect(541, 50, 51, 51), |
| PipPositioner::GetPositionAfterMovementAreaChange(window_state)); |
| } |
| |
| // Make sure the window is transparent only when it is in normal state. |
| TEST_F(WindowStateTest, OpacityChange) { |
| std::unique_ptr<aura::Window> window = CreateAppWindow(); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_TRUE(window->GetTransparent()); |
| |
| window_state->Maximize(); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| EXPECT_FALSE(window->GetTransparent()); |
| |
| window_state->Restore(); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_TRUE(window->GetTransparent()); |
| |
| window_state->Minimize(); |
| EXPECT_TRUE(window_state->IsMinimized()); |
| EXPECT_FALSE(window->GetTransparent()); |
| |
| window_state->Unminimize(); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_TRUE(window->GetTransparent()); |
| |
| ToggleFullScreen(window_state, nullptr); |
| ASSERT_TRUE(window_state->IsFullscreen()); |
| EXPECT_FALSE(window->GetTransparent()); |
| |
| window_state->Restore(); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_TRUE(window->GetTransparent()); |
| |
| const WindowSnapWMEvent snap_primary(WM_EVENT_SNAP_PRIMARY); |
| window_state->OnWMEvent(&snap_primary); |
| EXPECT_FALSE(window->GetTransparent()); |
| |
| window_state->Restore(); |
| EXPECT_TRUE(window->GetTransparent()); |
| |
| const WindowSnapWMEvent snap_secondary(WM_EVENT_SNAP_SECONDARY); |
| window_state->OnWMEvent(&snap_primary); |
| EXPECT_FALSE(window->GetTransparent()); |
| |
| window_state->OnWMEvent(&snap_primary); |
| EXPECT_FALSE(window->GetTransparent()); |
| } |
| |
| // Tests the basic functionalties related to window state restore history stack. |
| TEST_F(WindowStateTest, WindowStateRestoreHistoryBasicFunctionalites) { |
| UpdateDisplay("800x600"); |
| const gfx::Rect fullscreen_bounds = GetPrimaryDisplay().bounds(); |
| const gfx::Rect work_area_bounds = GetPrimaryDisplay().work_area(); |
| const gfx::Size snap_window_size(work_area_bounds.width() / 2, |
| work_area_bounds.height()); |
| |
| // Start with kDefault window state. |
| const gfx::Rect default_bounds(20, 10, 200, 150); |
| std::unique_ptr<aura::Window> window = CreateAppWindow(default_bounds); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_EQ(window->GetBoundsInScreen(), default_bounds); |
| |
| const std::vector<chromeos::WindowStateType>& restore_stack = |
| window_state->window_state_restore_history(); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| // Transition to kPrimarySnapped window state. |
| const WindowSnapWMEvent snap_primary(WM_EVENT_SNAP_PRIMARY); |
| window_state->OnWMEvent(&snap_primary); |
| EXPECT_EQ(window->GetBoundsInScreen(), gfx::Rect(snap_window_size)); |
| ASSERT_EQ(restore_stack.size(), 1u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Then transition to kMaximized window state. |
| const WMEvent maximize_event(WM_EVENT_MAXIMIZE); |
| window_state->OnWMEvent(&maximize_event); |
| EXPECT_EQ(window->GetBoundsInScreen(), work_area_bounds); |
| ASSERT_EQ(restore_stack.size(), 2u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kPrimarySnapped); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), |
| WindowStateType::kPrimarySnapped); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Then transition to kFullscreen window state. |
| const WMEvent fullscreen_event(WM_EVENT_FULLSCREEN); |
| window_state->OnWMEvent(&fullscreen_event); |
| EXPECT_EQ(window->GetBoundsInScreen(), fullscreen_bounds); |
| ASSERT_EQ(restore_stack.size(), 3u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kPrimarySnapped); |
| EXPECT_EQ(restore_stack[2], WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Then transition to kMinimized window state. |
| const WMEvent minimized_event(WM_EVENT_MINIMIZE); |
| window_state->OnWMEvent(&minimized_event); |
| ASSERT_EQ(restore_stack.size(), 4u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kPrimarySnapped); |
| EXPECT_EQ(restore_stack[2], WindowStateType::kMaximized); |
| EXPECT_EQ(restore_stack[3], WindowStateType::kFullscreen); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), |
| WindowStateType::kFullscreen); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Then start restore from here. It should restore back to kFullscreen window |
| // state. |
| window_state->Restore(); |
| EXPECT_EQ(window->GetBoundsInScreen(), fullscreen_bounds); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kFullscreen); |
| ASSERT_EQ(restore_stack.size(), 3u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kPrimarySnapped); |
| EXPECT_EQ(restore_stack[2], WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Then restore back to kMaximized window state. |
| window_state->Restore(); |
| EXPECT_EQ(window->GetBoundsInScreen(), work_area_bounds); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kMaximized); |
| ASSERT_EQ(restore_stack.size(), 2u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kPrimarySnapped); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), |
| WindowStateType::kPrimarySnapped); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Then restore back to kPrimarySnapped window state. |
| window_state->Restore(); |
| EXPECT_EQ(window->GetBoundsInScreen(), gfx::Rect(snap_window_size)); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kPrimarySnapped); |
| ASSERT_EQ(restore_stack.size(), 1u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Then restore back to kNormal window state. |
| window_state->Restore(); |
| EXPECT_EQ(window->GetBoundsInScreen(), default_bounds); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kNormal); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| // Restore a kNormal window state window will keep the window's kNormal window |
| // state. |
| window_state->Restore(); |
| EXPECT_EQ(window->GetBoundsInScreen(), default_bounds); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kNormal); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| } |
| |
| // Tests that window state transitioning from higher to lower layer will erase |
| // the window state restore history in between. |
| TEST_F(WindowStateTest, TransitionFromHighToLowerLayerEraseRestoreHistory) { |
| UpdateDisplay("800x600"); |
| |
| // Start with kDefault window state. |
| const gfx::Rect default_bounds(20, 10, 200, 150); |
| std::unique_ptr<aura::Window> window = CreateAppWindow(default_bounds); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| |
| const std::vector<chromeos::WindowStateType>& restore_stack = |
| window_state->window_state_restore_history(); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| // Transition to kPrimarySnapped window state. |
| const WindowSnapWMEvent snap_primary(WM_EVENT_SNAP_PRIMARY); |
| window_state->OnWMEvent(&snap_primary); |
| |
| // Then transition to kMaximized window state. |
| const WMEvent maximize_event(WM_EVENT_MAXIMIZE); |
| window_state->OnWMEvent(&maximize_event); |
| |
| // Then transition to kFullscreen window state. |
| const WMEvent fullscreen_event(WM_EVENT_FULLSCREEN); |
| window_state->OnWMEvent(&fullscreen_event); |
| ASSERT_EQ(restore_stack.size(), 3u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kPrimarySnapped); |
| EXPECT_EQ(restore_stack[2], WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Now transition back to kPrimarySnapped window state. It should have erased |
| // any restore history after kPrimarySnapped. |
| window_state->OnWMEvent(&snap_primary); |
| ASSERT_EQ(restore_stack.size(), 1u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| } |
| |
| // Tests the restore behaviors when window state transitions in the same layer. |
| // There are 3 cases: {kNormal & kDefault}, {kPrimarySnapped & |
| // kSecondarySnapped}, and {kMinimized & kPip}. |
| TEST_F(WindowStateTest, TransitionInTheSameLayerKeepSameRestoreHistory) { |
| UpdateDisplay("800x600"); |
| |
| // First we test kNormal & kDefault. |
| // Start with kDefault window state. |
| const gfx::Rect default_bounds(20, 10, 200, 150); |
| std::unique_ptr<aura::Window> window = CreateAppWindow(default_bounds); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| |
| const std::vector<chromeos::WindowStateType>& restore_stack = |
| window_state->window_state_restore_history(); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| // Transition to kNormal window state. Since it's on the same layer as |
| // kDefault, kDefault won't be pushed into the restore history stack. |
| const WMEvent normal_event(WM_EVENT_NORMAL); |
| window_state->OnWMEvent(&normal_event); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| // Test kPrimarySnapped & kSecondarySnapped. |
| // Transition to kPrimarySnapped window state. |
| const WindowSnapWMEvent snap_primary(WM_EVENT_SNAP_PRIMARY); |
| window_state->OnWMEvent(&snap_primary); |
| ASSERT_EQ(restore_stack.size(), 1u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Transition to kSecondarySnapped window state. Since it's on the same layer |
| // as kPrimarySnapped, kPrimarySnapped won't be pushed into the restore |
| // history stack. |
| const WindowSnapWMEvent snap_secondary(WM_EVENT_SNAP_SECONDARY); |
| window_state->OnWMEvent(&snap_secondary); |
| ASSERT_EQ(restore_stack.size(), 1u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Test kMinimized & kPip. |
| // Transition to kMinimized window state. |
| const WMEvent minimized_event(WM_EVENT_MINIMIZE); |
| window_state->OnWMEvent(&minimized_event); |
| ASSERT_EQ(restore_stack.size(), 2u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kNormal); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kSecondarySnapped); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), |
| WindowStateType::kSecondarySnapped); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Transition to kPip Window state. Since it's on the same layer as |
| // kMinimized, kMinimized won't be pushed into the restore history stack. |
| const WMEvent pip_event(WM_EVENT_PIP); |
| window_state->OnWMEvent(&pip_event); |
| ASSERT_EQ(restore_stack.size(), 2u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kNormal); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kSecondarySnapped); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), |
| WindowStateType::kSecondarySnapped); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| } |
| |
| // TODO(minch): Check the expected behavior of restoring from window state like |
| // kPinned, kTrustedPinned that do not support the window state restore history. |
| // Test the restore behaviors of kPinned and kTrustedPinned window state. They |
| // are different with kFullscreen restore behaviors. |
| TEST_F(WindowStateTest, PinnedRestoreTest) { |
| UpdateDisplay("800x600"); |
| const gfx::Rect fullscreen_bounds = GetPrimaryDisplay().bounds(); |
| const gfx::Rect work_area_bounds = GetPrimaryDisplay().work_area(); |
| |
| // Start with kDefault window state. |
| const gfx::Rect default_bounds(20, 10, 200, 150); |
| std::unique_ptr<aura::Window> window = CreateAppWindow(default_bounds); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| |
| const std::vector<chromeos::WindowStateType>& restore_stack = |
| window_state->window_state_restore_history(); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| // Transition to kPrimarySnapped window state. |
| const WindowSnapWMEvent snap_primary(WM_EVENT_SNAP_PRIMARY); |
| window_state->OnWMEvent(&snap_primary); |
| |
| // Then transition to kMaximized window state. |
| const WMEvent maximize_event(WM_EVENT_MAXIMIZE); |
| window_state->OnWMEvent(&maximize_event); |
| ASSERT_EQ(restore_stack.size(), 2u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kPrimarySnapped); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), |
| WindowStateType::kPrimarySnapped); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Then transition to kPinned window state. Since kPinned window state is not |
| // supported in the window state restore history layer, the restore history |
| // stack will be cleared. It can only restore back to kNormal window state. |
| const WMEvent pinned_event(WM_EVENT_PIN); |
| window_state->OnWMEvent(&pinned_event); |
| EXPECT_EQ(window->GetBoundsInScreen(), fullscreen_bounds); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| window_state->Restore(); |
| EXPECT_EQ(window->GetBoundsInScreen(), work_area_bounds); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kNormal); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| // Same should happen for kTrustedPinned as well. |
| window_state->OnWMEvent(&snap_primary); |
| window_state->OnWMEvent(&maximize_event); |
| ASSERT_EQ(restore_stack.size(), 2u); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), |
| WindowStateType::kPrimarySnapped); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), work_area_bounds); |
| |
| const WMEvent trusted_pinned_event(WM_EVENT_TRUSTED_PIN); |
| window_state->OnWMEvent(&trusted_pinned_event); |
| EXPECT_EQ(window->GetBoundsInScreen(), fullscreen_bounds); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| window_state->Restore(); |
| EXPECT_EQ(window->GetBoundsInScreen(), work_area_bounds); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kNormal); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| } |
| |
| // Test the restore behaviors of kMinimized and kPip window state. They are both |
| // viewed as the final state in the restore layer. |
| TEST_F(WindowStateTest, MinimizedAndPipRestoreTest) { |
| UpdateDisplay("800x600"); |
| |
| // Start with kDefault window state. |
| const gfx::Rect default_bounds(20, 10, 200, 150); |
| std::unique_ptr<aura::Window> window = CreateAppWindow(default_bounds); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| |
| const std::vector<chromeos::WindowStateType>& restore_stack = |
| window_state->window_state_restore_history(); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| // Maximize the window. |
| const WMEvent maximize_event(WM_EVENT_MAXIMIZE); |
| window_state->OnWMEvent(&maximize_event); |
| ASSERT_EQ(restore_stack.size(), 1u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // kPip window can be minimized to kMinimized window state, but restoring from |
| // kMinimized window state can't restore back to kPip window state. |
| const WMEvent pip_event(WM_EVENT_PIP); |
| window_state->OnWMEvent(&pip_event); |
| ASSERT_EQ(restore_stack.size(), 2u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| const WMEvent minimized_event(WM_EVENT_MINIMIZE); |
| window_state->OnWMEvent(&minimized_event); |
| ASSERT_EQ(restore_stack.size(), 2u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Restore the minimized window. It should go back to pre-pip window state. |
| window_state->Restore(); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kMaximized); |
| ASSERT_EQ(restore_stack.size(), 1u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Similarly, if the pre-pip window state is kMinimized, restoring from kPip |
| // should go back to the pre-minimized window state. |
| window_state->OnWMEvent(&minimized_event); |
| ASSERT_EQ(restore_stack.size(), 2u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| window_state->OnWMEvent(&pip_event); |
| ASSERT_EQ(restore_stack.size(), 2u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(restore_stack[1], WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kMaximized); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Restore the Pip window. It should go back to pre-minimized window state. |
| window_state->Restore(); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kMaximized); |
| ASSERT_EQ(restore_stack.size(), 1u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| } |
| |
| TEST_F(WindowStateTest, HorizontalMaximizeThenMinimizeAndRestore) { |
| UpdateDisplay("800x600"); |
| const gfx::Rect work_area_bounds = GetPrimaryDisplay().work_area(); |
| |
| // Start with kDefault window state. |
| const gfx::Rect default_bounds(20, 10, 200, 150); |
| std::unique_ptr<aura::Window> window = CreateAppWindow(default_bounds); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| |
| const std::vector<chromeos::WindowStateType>& restore_stack = |
| window_state->window_state_restore_history(); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| const gfx::Rect horizontal_maximize_bounds( |
| 0, default_bounds.y(), work_area_bounds.width(), default_bounds.height()); |
| const WMEvent horizontal_maximize_event(WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE); |
| window_state->OnWMEvent(&horizontal_maximize_event); |
| EXPECT_EQ(window->GetBoundsInScreen(), horizontal_maximize_bounds); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| const WMEvent minimize_event(WM_EVENT_MINIMIZE); |
| window_state->OnWMEvent(&minimize_event); |
| ASSERT_EQ(restore_stack.size(), 1u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Unminimize should restore back to horizontally maximized bounds while |
| // maintaining restore bounds. |
| window_state->Restore(); |
| EXPECT_EQ(window->GetBoundsInScreen(), horizontal_maximize_bounds); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kNormal); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| } |
| |
| TEST_F(WindowStateTest, HorizontalMaximizeThenMaximizeAndRestore) { |
| UpdateDisplay("800x600"); |
| const gfx::Rect work_area_bounds = GetPrimaryDisplay().work_area(); |
| |
| // Start with kDefault window state. |
| const gfx::Rect default_bounds(20, 10, 200, 150); |
| std::unique_ptr<aura::Window> window = CreateAppWindow(default_bounds); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| |
| const std::vector<chromeos::WindowStateType>& restore_stack = |
| window_state->window_state_restore_history(); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| const gfx::Rect horizontal_maximize_bounds( |
| 0, default_bounds.y(), work_area_bounds.width(), default_bounds.height()); |
| const WMEvent horizontal_maximize_event(WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE); |
| window_state->OnWMEvent(&horizontal_maximize_event); |
| EXPECT_EQ(window->GetBoundsInScreen(), horizontal_maximize_bounds); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| const WMEvent maximize_event(WM_EVENT_MAXIMIZE); |
| window_state->OnWMEvent(&maximize_event); |
| ASSERT_EQ(restore_stack.size(), 1u); |
| EXPECT_EQ(restore_stack[0], WindowStateType::kDefault); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| // Restore from maximized should go back to default bounds, not the |
| // horizontally maximized bounds. |
| window_state->Restore(); |
| EXPECT_EQ(window->GetBoundsInScreen(), default_bounds); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kNormal); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| } |
| |
| TEST_F(WindowStateTest, SnapThenResize) { |
| UpdateDisplay("800x600"); |
| const gfx::Rect work_area_bounds = GetPrimaryDisplay().work_area(); |
| |
| // Start with kDefault window state. |
| constexpr gfx::Rect default_bounds(20, 10, 200, 150); |
| std::unique_ptr<aura::Window> window = CreateAppWindow(default_bounds); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| |
| const std::vector<chromeos::WindowStateType>& restore_stack = |
| window_state->window_state_restore_history(); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetRestoreWindowState(), WindowStateType::kNormal); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| |
| // Important! Change the restore bounds, so they are not the same. This will |
| // create a conflict between the window's current bounds and the restore |
| // bounds when restoring. |
| constexpr gfx::Rect moved_bounds(10, 10, 200, 150); |
| window->SetBounds(moved_bounds); |
| window_state->SetRestoreBoundsInScreen(default_bounds); |
| |
| const WindowSnapWMEvent snap_left_event(WM_EVENT_SNAP_PRIMARY); |
| const gfx::Rect snapped_left_bounds(0, 0, work_area_bounds.width() / 2, |
| work_area_bounds.height()); |
| window_state->OnWMEvent(&snap_left_event); |
| EXPECT_EQ(window->GetBoundsInScreen(), snapped_left_bounds); |
| EXPECT_EQ(restore_stack.size(), 1U); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), default_bounds); |
| |
| const int resize = 100; |
| const gfx::Rect resized_bounds(0, resize, work_area_bounds.width() / 2, |
| work_area_bounds.height() - resize); |
| { |
| // Drag the top of the window to unsnap and resize. |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->MoveMouseTo(snapped_left_bounds.top_center().x(), |
| snapped_left_bounds.top_center().y()); |
| generator->PressLeftButton(); |
| generator->MoveMouseTo(snapped_left_bounds.top_center().x(), resize); |
| generator->ReleaseLeftButton(); |
| } |
| |
| EXPECT_TRUE(window_state->IsNormalStateType()); |
| EXPECT_TRUE(restore_stack.empty()); |
| EXPECT_EQ(window_state->GetCurrentBoundsInScreen(), resized_bounds); |
| EXPECT_EQ(window_state->GetRestoreBoundsInScreen(), gfx::Rect()); |
| } |
| |
| // Tests the restore behavior for default or normal window. |
| TEST_F(WindowStateTest, NormalOrDefaultRestore) { |
| // Start with kDefault window state. |
| std::unique_ptr<aura::Window> window = CreateAppWindow(); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kDefault); |
| |
| // Restoring a kDefault window will change its window state to kNormal. |
| window_state->Restore(); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kNormal); |
| |
| // Restoring kNormal window will do nothing. |
| window_state->Restore(); |
| EXPECT_EQ(window_state->GetStateType(), WindowStateType::kNormal); |
| } |
| |
| TEST_F(WindowStateTest, WindowSnapActionSourceUmaMetrics) { |
| UpdateDisplay("800x600"); |
| base::HistogramTester histograms; |
| std::unique_ptr<aura::Window> window(CreateAppWindow()); |
| WindowState* window_state = WindowState::Get(window.get()); |
| |
| // Use WMEvent to directly snap the window. |
| WindowSnapWMEvent snap_primary(WM_EVENT_SNAP_PRIMARY); |
| window_state->OnWMEvent(&snap_primary); |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kNotSpecified, 1); |
| window_state->Maximize(); |
| |
| // Drag the window to the screen edge to snap. |
| std::unique_ptr<WindowResizer> resizer(CreateWindowResizer( |
| window.get(), gfx::PointF(), HTCAPTION, ::wm::WINDOW_MOVE_SOURCE_TOUCH)); |
| resizer->Drag(gfx::PointF(0, 400), 0); |
| resizer->CompleteDrag(); |
| resizer.reset(); |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kDragWindowToEdgeToSnap, |
| 1); |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kNotSpecified, 1); |
| window_state->Maximize(); |
| |
| // Use keyboard to snap a window. |
| AcceleratorController::Get()->PerformActionIfEnabled( |
| AcceleratorAction::kWindowCycleSnapLeft, {}); |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kKeyboardShortcutToSnap, |
| 1); |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kNotSpecified, 1); |
| window_state->Maximize(); |
| |
| // Restore the maximized window to snap window state. |
| window_state->Restore(); |
| histograms.ExpectBucketCount( |
| kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kSnapByWindowStateRestore, 1); |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kNotSpecified, 1); |
| window_state->Maximize(); |
| |
| // Drag or select overview window to snap window. |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| EnterOverview(); |
| ASSERT_TRUE(GetOverviewSession()); |
| const gfx::Point center_point = |
| gfx::ToRoundedPoint(GetOverviewSession() |
| ->GetOverviewItemForWindow(window.get()) |
| ->target_bounds() |
| .CenterPoint()); |
| generator->MoveMouseTo(center_point); |
| generator->DragMouseTo(gfx::Point(0, 400)); |
| histograms.ExpectBucketCount( |
| kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kDragOrSelectOverviewWindowToSnap, 1); |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kNotSpecified, 1); |
| window_state->Maximize(); |
| |
| Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); |
| EXPECT_TRUE(display::Screen::GetScreen()->InTabletMode()); |
| |
| // Use keyboard to snap the window in tablet mode. |
| AcceleratorController::Get()->PerformActionIfEnabled( |
| AcceleratorAction::kWindowCycleSnapLeft, {}); |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kKeyboardShortcutToSnap, |
| 2); |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kNotSpecified, 1); |
| |
| // Auto-snap in splitview. |
| std::unique_ptr<aura::Window> window2(CreateAppWindow()); |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kAutoSnapInSplitView, 1); |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kNotSpecified, 1); |
| |
| // Resize in splitview. |
| auto* split_view_controller = |
| SplitViewController::Get(Shell::GetPrimaryRootWindow()); |
| auto* split_view_divider = split_view_controller->split_view_divider(); |
| gfx::Rect divider_bounds = |
| split_view_divider->GetDividerBoundsInScreen(false); |
| split_view_divider->StartResizeWithDivider(divider_bounds.CenterPoint()); |
| gfx::Rect display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer( |
| window.get()); |
| gfx::Point resize_point(display_bounds.width() * 0.33f, 0); |
| split_view_divider->ResizeWithDivider(resize_point); |
| // This should not cause any metrics change. |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kNotSpecified, 1); |
| split_view_divider->EndResizeWithDivider(resize_point); |
| histograms.ExpectBucketCount(kWindowSnapActionSourceHistogram, |
| WindowSnapActionSource::kNotSpecified, 1); |
| } |
| |
| // Test how the minimum height specified by the aura::WindowDelegate affects |
| // snapping in portrait display layout. |
| TEST_F(WindowStateTest, SnapWindowMinimumSizePortrait) { |
| UpdateDisplay("600x900"); |
| const gfx::Rect kWorkAreaBounds = |
| display::Screen::GetScreen()->GetPrimaryDisplay().work_area(); |
| |
| aura::test::TestWindowDelegate delegate; |
| std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( |
| &delegate, -1, gfx::Rect(0, 100, kWorkAreaBounds.width() - 1, 100))); |
| |
| // It should be possible to snap a window with a minimum width that is larger |
| // a half screen width in horizontal snap layout and snap a window with a |
| // minimum height that is longer than a half screen height in vertical snap |
| // layout. |
| const gfx::Size kMinimumSize = gfx::Size(0, 500); |
| delegate.set_minimum_size(kMinimumSize); |
| WindowState* window_state = WindowState::Get(window.get()); |
| EXPECT_TRUE(window_state->CanSnap()); |
| const WindowSnapWMEvent snap_secondary(WM_EVENT_SNAP_SECONDARY); |
| window_state->OnWMEvent(&snap_secondary); |
| // Expect right snap for horizontal snap layout with the minimum width and |
| // bottom snap for vertical snap layout with the minimum height. |
| const gfx::Rect expected_snap = gfx::Rect( |
| kWorkAreaBounds.x(), kWorkAreaBounds.height() - kMinimumSize.height(), |
| kWorkAreaBounds.width(), kMinimumSize.height()); |
| EXPECT_EQ(expected_snap, window->GetBoundsInScreen()); |
| } |
| |
| // Tests the snapped window states in the external display while removing the |
| // internal display. |
| TEST_F(WindowStateTest, SnappedWindowsInExternalDisplay) { |
| UpdateDisplay("800x600,1920x1200"); |
| |
| const auto& displays = display_manager()->active_display_list(); |
| const int64_t primary_id = displays[0].id(); |
| const int64_t secondary_id = displays[1].id(); |
| display::Screen* screen = display::Screen::GetScreen(); |
| |
| // Create two windows inside the external display. |
| std::unique_ptr<aura::Window> w1 = |
| CreateTestWindow(gfx::Rect(801, 0, 200, 100)); |
| std::unique_ptr<aura::Window> w2 = |
| CreateTestWindow(gfx::Rect(1000, 0, 200, 100)); |
| ASSERT_EQ(secondary_id, screen->GetDisplayNearestWindow(w1.get()).id()); |
| ASSERT_EQ(secondary_id, screen->GetDisplayNearestWindow(w2.get()).id()); |
| |
| // Put the shelf of the internal display at the bottom while the external |
| // display shelf at the left side. |
| PrefService* prefs = |
| Shell::Get()->session_controller()->GetLastActiveUserPrefService(); |
| SetShelfAlignmentPref(prefs, primary_id, ShelfAlignment::kBottom); |
| SetShelfAlignmentPref(prefs, secondary_id, ShelfAlignment::kLeft); |
| EXPECT_EQ(ShelfAlignment::kBottom, |
| Shell::GetRootWindowControllerWithDisplayId(primary_id) |
| ->shelf() |
| ->alignment()); |
| EXPECT_EQ(ShelfAlignment::kLeft, |
| Shell::GetRootWindowControllerWithDisplayId(secondary_id) |
| ->shelf() |
| ->alignment()); |
| |
| // Make `w1` to be left snapped. |
| WindowState* window_state1 = WindowState::Get(w1.get()); |
| const WindowSnapWMEvent snap_left(WM_EVENT_SNAP_PRIMARY); |
| window_state1->OnWMEvent(&snap_left); |
| EXPECT_TRUE(window_state1->IsSnapped()); |
| EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(w1.get()).id()); |
| EXPECT_EQ(chromeos::kDefaultSnapRatio, *window_state1->snap_ratio()); |
| |
| // Make `w2` to be right snapped. |
| WindowState* window_state2 = WindowState::Get(w2.get()); |
| const WindowSnapWMEvent snap_right(WM_EVENT_SNAP_SECONDARY); |
| window_state2->OnWMEvent(&snap_right); |
| EXPECT_TRUE(window_state2->IsSnapped()); |
| EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(w2.get()).id()); |
| EXPECT_EQ(chromeos::kDefaultSnapRatio, *window_state2->snap_ratio()); |
| |
| // Store the two snapped window bounds with a left aligned shelf. |
| const gfx::Rect w1_local_bounds = w1->bounds(); |
| const gfx::Rect w2_local_bounds = w2->bounds(); |
| |
| display::ManagedDisplayInfo secondary_info = |
| display_manager()->GetDisplayInfo(secondary_id); |
| // Remove the primary display. |
| std::vector<display::ManagedDisplayInfo> display_info_list; |
| display_info_list.push_back(secondary_info); |
| display_manager()->OnNativeDisplaysChanged(display_info_list); |
| |
| // Verify that both `w1` and `w2` are still snapped in the external display |
| // with unchanged snap ratio, unchanged bounds. There should have no gap |
| // between the two snapped windows. |
| EXPECT_TRUE(window_state1->IsSnapped()); |
| EXPECT_TRUE(window_state2->IsSnapped()); |
| EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(w1.get()).id()); |
| EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(w2.get()).id()); |
| EXPECT_EQ(chromeos::kDefaultSnapRatio, *window_state1->snap_ratio()); |
| EXPECT_EQ(chromeos::kDefaultSnapRatio, *window_state2->snap_ratio()); |
| EXPECT_EQ(w1_local_bounds, w1->bounds()); |
| EXPECT_EQ(w2_local_bounds, w2->bounds()); |
| EXPECT_EQ(ShelfAlignment::kLeft, |
| Shell::GetRootWindowControllerWithDisplayId(secondary_id) |
| ->shelf() |
| ->alignment()); |
| } |
| |
| class WindowStateMetricsTest : public AshTestBase { |
| public: |
| WindowStateMetricsTest() |
| : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} |
| |
| void AdvanceClock(base::TimeDelta delta) { |
| task_environment()->AdvanceClock(delta); |
| task_environment()->RunUntilIdle(); |
| } |
| }; |
| |
| TEST_F(WindowStateMetricsTest, PartialSplitDuration) { |
| base::HistogramTester histogram_tester; |
| const std::string kHistogramName = |
| chromeos::kPartialSplitDurationHistogramName; |
| std::unique_ptr<aura::Window> window(CreateAppWindow()); |
| WindowState* window_state = WindowState::Get(window.get()); |
| |
| auto* desks_controller = DesksController::Get(); |
| NewDesk(); |
| ASSERT_EQ(2u, desks_controller->desks().size()); |
| |
| // Partial split for 30 seconds, then maximize. Test that it records 0 since |
| // it has been less than 1 minute. |
| WindowSnapWMEvent partial_event(WM_EVENT_SNAP_PRIMARY, |
| chromeos::kTwoThirdSnapRatio); |
| window_state->OnWMEvent(&partial_event); |
| AdvanceClock(base::Seconds(30)); |
| window_state->Maximize(); |
| histogram_tester.ExpectBucketCount(kHistogramName, 0, 1); |
| |
| // Partial split for 3 minutes, then minimize. Test that it records. |
| window_state->OnWMEvent(&partial_event); |
| AdvanceClock(base::Minutes(3)); |
| window_state->Minimize(); |
| histogram_tester.ExpectBucketCount(kHistogramName, 3, 1); |
| |
| // Partial split for 3 hours, then default split. Test that it records |
| // in the 180 minute bucket. |
| window_state->OnWMEvent(&partial_event); |
| AdvanceClock(base::Hours(3)); |
| WindowSnapWMEvent snap_event(WM_EVENT_SNAP_PRIMARY); |
| window_state->OnWMEvent(&snap_event); |
| histogram_tester.ExpectBucketCount(kHistogramName, 180, 1); |
| |
| // Partial split for 3 minutes, then change display work area, then wait 3 |
| // minutes, then drag to resize. Test that it continues recording through the |
| // work area change but stops when the snap ratio is adjusted. |
| window_state->OnWMEvent(&partial_event); |
| AdvanceClock(base::Minutes(3)); |
| GetPrimaryShelf()->SetAlignment(ShelfAlignment::kLeft); |
| AdvanceClock(base::Minutes(3)); |
| const int kIncreasedWidth = 225; |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->MoveMouseTo(window->bounds().right(), window->bounds().y()); |
| generator->PressLeftButton(); |
| generator->MoveMouseTo(window->bounds().right() + kIncreasedWidth, |
| window->bounds().y()); |
| generator->ReleaseLeftButton(); |
| histogram_tester.ExpectBucketCount(kHistogramName, 6, 1); |
| |
| // Partial split for 3 minutes, then activate desk 2. Test that it |
| // records as the partial window is no longer active and visible. |
| window_state->OnWMEvent(&partial_event); |
| AdvanceClock(base::Minutes(3)); |
| ActivateDesk(desks_controller->desks()[1].get()); |
| histogram_tester.ExpectBucketCount(kHistogramName, 3, 2); |
| |
| // Activate desk 1. The partial window will be visible again and start the |
| // recording. Test that sending the window to desk 2 records the duration. |
| ActivateDesk(desks_controller->desks()[0].get()); |
| AdvanceClock(base::Minutes(3)); |
| desks_controller->SendToDeskAtIndex(window.get(), 1); |
| histogram_tester.ExpectBucketCount(kHistogramName, 3, 3); |
| |
| // Activate desk 2 with the partial window, wait 1 minute, create another |
| // partial window, wait another minute, then close both windows. Test that |
| // window 1 records in the 2 minute bucket, and window 2 in the 1 minute |
| // bucket. |
| ActivateDesk(desks_controller->desks()[1].get()); |
| AdvanceClock(base::Minutes(1)); |
| std::unique_ptr<aura::Window> window2(CreateAppWindow()); |
| WindowSnapWMEvent partial_secondary(WM_EVENT_SNAP_SECONDARY, |
| chromeos::kOneThirdSnapRatio); |
| WindowState::Get(window2.get())->OnWMEvent(&partial_secondary); |
| AdvanceClock(base::Minutes(1)); |
| window.reset(); |
| window2.reset(); |
| histogram_tester.ExpectBucketCount(kHistogramName, 2, 1); |
| histogram_tester.ExpectBucketCount(kHistogramName, 1, 1); |
| |
| // TODO(sophiewen): Determine whether to stop recording if a partial split |
| // window swaps sides, e.g. from one third to two thirds. |
| } |
| |
| // TODO(skuhne): Add more unit test to verify the correctness for the restore |
| // operation. |
| |
| } // namespace |
| } // namespace ash |