full_restore: Restore windows that are out-of-bounds properly.
Currently, if a user has a window that is slightly out-of-bounds, upon restoration it the restored window will be moved into the work area such that it's fully contained. The ideal behavior is that out-of-bounds windows are restored to their previous bounds. Additionally, if the window would be placed fully out-of-bounds, we should ensure that a portion of the window is within the work area. This CL implements the ideal behavior by restoring out-of-bounds windows with the FullRestoreController, ensuring these windows are at least partially visible. Test: added Bug: 1208927 Change-Id: If43415c6abe39017183d0e11e81b60ca4e4d0ba1 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2911344 Commit-Queue: Jeremy Chinsen <chinsenj@chromium.org> Reviewed-by: Sammie Quon <sammiequon@chromium.org> Cr-Commit-Position: refs/heads/master@{#886548}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
23afd147ab
commit
10c90140f1
ash/wm/full_restore
@ -16,6 +16,7 @@
|
||||
#include "ash/wm/desks/desks_util.h"
|
||||
#include "ash/wm/mru_window_tracker.h"
|
||||
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
|
||||
#include "ash/wm/window_positioning_utils.h"
|
||||
#include "ash/wm/window_state.h"
|
||||
#include "ash/wm/wm_event.h"
|
||||
#include "base/auto_reset.h"
|
||||
@ -121,6 +122,40 @@ aura::Window* GetSiblingToStackBelow(aura::Window* window) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If `window`'s saved window info makes the `window` out-of-bounds for the
|
||||
// display, manually restore its bounds. Also ensures that at least 30% of the
|
||||
// window is visible to handle the case where the display a window is restored
|
||||
// to is drastically smaller than the pre-restore display.
|
||||
void MaybeRestoreOutOfBoundsWindows(aura::Window* window) {
|
||||
std::unique_ptr<full_restore::WindowInfo> window_info = GetWindowInfo(window);
|
||||
if (!window_info)
|
||||
return;
|
||||
|
||||
gfx::Rect current_bounds =
|
||||
window_info->current_bounds.value_or(gfx::Rect(0, 0));
|
||||
if (current_bounds.IsEmpty())
|
||||
return;
|
||||
|
||||
const auto& closest_display =
|
||||
display::Screen::GetScreen()->GetDisplayNearestWindow(window);
|
||||
gfx::Rect display_area = closest_display.work_area();
|
||||
if (display_area.Contains(current_bounds))
|
||||
return;
|
||||
|
||||
AdjustBoundsToEnsureMinimumWindowVisibility(display_area, ¤t_bounds);
|
||||
|
||||
auto* window_state = WindowState::Get(window);
|
||||
if (window_state->HasRestoreBounds()) {
|
||||
// When a `window` is in maximized, minimized, or snapped its restore bounds
|
||||
// are saved in `WindowInfo.current_bounds` and its
|
||||
// maximized/minimized/snapped bounds are determined by the system, so apply
|
||||
// this adjustment to `window`'s restore bounds instead.
|
||||
window_state->SetRestoreBoundsInScreen(current_bounds);
|
||||
} else {
|
||||
window->SetBoundsInScreen(current_bounds, closest_display);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FullRestoreController::FullRestoreController() {
|
||||
@ -213,6 +248,14 @@ void FullRestoreController::OnWidgetInitialized(views::Widget* widget) {
|
||||
return;
|
||||
|
||||
UpdateAndObserveWindow(window);
|
||||
|
||||
// If the restored bounds are out of the screen, move the window to the bounds
|
||||
// manually as most widget types force windows to be within the work area on
|
||||
// creation.
|
||||
// TODO(chinsenj|sammiequon): The Files app uses async Mojo calls to activate
|
||||
// and set its bounds, making this approach not work. In the future, we'll
|
||||
// need to address the Files app.
|
||||
MaybeRestoreOutOfBoundsWindows(window);
|
||||
}
|
||||
|
||||
void FullRestoreController::OnARCTaskReadyForUnparentedWindow(
|
||||
|
@ -933,12 +933,13 @@ TEST_F(FullRestoreControllerTest, HotseatIsHiddenOnRestoration) {
|
||||
|
||||
// Add two entries, where the window highest on the z-order is minimized.
|
||||
// Restore both entries. The hotseat should now be hidden.
|
||||
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
|
||||
AddEntryToFakeFile(/*restore_window_id=*/1, gfx::Rect(200, 200),
|
||||
chromeos::WindowStateType::kMinimized,
|
||||
/*activation_index=*/1);
|
||||
/*activation_index=*/1, /*display_id=*/primary_id);
|
||||
AddEntryToFakeFile(/*restore_window_id=*/2, gfx::Rect(200, 200),
|
||||
chromeos::WindowStateType::kNormal,
|
||||
/*activation_index=*/2);
|
||||
/*activation_index=*/2, /*display_id=*/primary_id);
|
||||
views::Widget* restored_widget_1 =
|
||||
CreateTestFullRestoredWidgetFromRestoreId(/*restore_window_id=*/1);
|
||||
views::Widget* restored_widget_2 =
|
||||
@ -963,12 +964,13 @@ TEST_F(FullRestoreControllerTest,
|
||||
|
||||
// Create multiple minimized entries and restore them. The app list should
|
||||
// still be active.
|
||||
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
|
||||
AddEntryToFakeFile(/*restore_window_id=*/1, gfx::Rect(200, 200),
|
||||
chromeos::WindowStateType::kMinimized,
|
||||
/*activation_index=*/1);
|
||||
/*activation_index=*/1, /*display_id=*/primary_id);
|
||||
AddEntryToFakeFile(/*restore_window_id=*/2, gfx::Rect(200, 200),
|
||||
chromeos::WindowStateType::kMinimized,
|
||||
/*activation_index=*/2);
|
||||
/*activation_index=*/2, /*display_id=*/primary_id);
|
||||
views::Widget* restored_widget_1 =
|
||||
CreateTestFullRestoredWidgetFromRestoreId(/*restore_window_id=*/1);
|
||||
views::Widget* restored_widget_2 =
|
||||
@ -1097,4 +1099,52 @@ TEST_F(FullRestoreControllerTest, ArcAppWindowCreatedWithoutTaskMultiDisplay) {
|
||||
restored_window2->parent());
|
||||
}
|
||||
|
||||
// Tests that windows that are out-of-bounds of the display they're being
|
||||
// restored to are properly restored.
|
||||
TEST_F(FullRestoreControllerTest, OutOfBoundsWindows) {
|
||||
UpdateDisplay("800x800");
|
||||
const gfx::Rect kScreenBounds(0, 0, 800, 800);
|
||||
const gfx::Rect kPartialBounds(-100, 100, 200, 200);
|
||||
const gfx::Rect kFullBounds(801, 801, 400, 200);
|
||||
|
||||
// Add an entry that is partially out-of-bounds, one that is completely
|
||||
// out-of-bounds, and one that is completely out-of-bounds and snapped.
|
||||
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
|
||||
AddEntryToFakeFile(/*restore_id=*/1, kPartialBounds,
|
||||
chromeos::WindowStateType::kNormal, /*activation_index=*/1,
|
||||
/*display_id=*/primary_id);
|
||||
AddEntryToFakeFile(/*restore_id=*/2, kFullBounds,
|
||||
chromeos::WindowStateType::kNormal, /*activation_index=*/2,
|
||||
/*display_id=*/primary_id);
|
||||
AddEntryToFakeFile(/*restore_id=*/3, kFullBounds,
|
||||
chromeos::WindowStateType::kLeftSnapped,
|
||||
/*activation_index=*/3, /*display_id=*/primary_id);
|
||||
|
||||
// Restore the first window. The window should have the exact same bounds.
|
||||
const gfx::Rect& window_bounds_1 =
|
||||
CreateTestFullRestoredWidgetFromRestoreId(/*restore_id=*/1)
|
||||
->GetNativeWindow()
|
||||
->GetBoundsInScreen();
|
||||
EXPECT_EQ(kPartialBounds, window_bounds_1);
|
||||
|
||||
// Restore the second window. The window should be moved such that part of it
|
||||
// is within the display.
|
||||
gfx::Rect window_bounds_2(
|
||||
CreateTestFullRestoredWidgetFromRestoreId(/*restore_id=*/2)
|
||||
->GetNativeWindow()
|
||||
->GetBoundsInScreen());
|
||||
EXPECT_TRUE(window_bounds_2.Intersects(kScreenBounds));
|
||||
EXPECT_LT(0, IntersectRects(kScreenBounds, window_bounds_2).size().GetArea());
|
||||
|
||||
// Restore the third window. The window's restore bounds should be moved such
|
||||
// that part of it is within the display.
|
||||
const gfx::Rect& window_bounds_3 =
|
||||
WindowState::Get(
|
||||
CreateTestFullRestoredWidgetFromRestoreId(/*restore_id=*/3)
|
||||
->GetNativeWindow())
|
||||
->GetRestoreBoundsInScreen();
|
||||
EXPECT_TRUE(window_bounds_3.Intersects(kScreenBounds));
|
||||
EXPECT_LT(0, IntersectRects(kScreenBounds, window_bounds_3).size().GetArea());
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
Reference in New Issue
Block a user