Commit 0cf0fde6 authored by chinsenj's avatar chinsenj Committed by Commit Bot

cros: Refactor window cycle list to decouple scrolling and focus ring.

Currently the logic that handles the focus ring and scrolling the
window cycle list are coupled together. However, there are cases where
the focus ring should move and the list should not scroll and cases
where the focus ring shouldn't move and the list should scroll.

To handle the aforementioned cases, this CL separates the handling for
scrolling and the focus ring.

Test: Manual
Bug: 1067327
Change-Id: I9fed7362c33dd2c5589b8bb5f52e81269273cad4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2425483
Commit-Queue: Jeremy Chinsen <chinsenj@chromium.org>
Reviewed-by: default avatarXiaoqian Dai <xdai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810732}
parent f913656f
...@@ -91,6 +91,17 @@ void WindowCycleController::HandleCycleWindow(Direction direction) { ...@@ -91,6 +91,17 @@ void WindowCycleController::HandleCycleWindow(Direction direction) {
Step(direction); Step(direction);
} }
void WindowCycleController::Scroll(Direction direction) {
if (!CanCycle())
return;
if (!IsCycling())
StartCycling();
DCHECK(window_cycle_list_);
window_cycle_list_->ScrollInDirection(direction);
}
void WindowCycleController::StartCycling() { void WindowCycleController::StartCycling() {
// Close the wallpaper preview if it is open to prevent visual glitches where // Close the wallpaper preview if it is open to prevent visual glitches where
// the window view item for the preview is transparent // the window view item for the preview is transparent
...@@ -127,9 +138,12 @@ void WindowCycleController::MaybeResetCycleList() { ...@@ -127,9 +138,12 @@ void WindowCycleController::MaybeResetCycleList() {
window_cycle_list_->ReplaceWindows(window_list); window_cycle_list_->ReplaceWindows(window_list);
} }
void WindowCycleController::StepToWindow(aura::Window* window) { void WindowCycleController::SetFocusedWindow(aura::Window* window) {
if (!IsCycling())
return;
DCHECK(window_cycle_list_); DCHECK(window_cycle_list_);
window_cycle_list_->StepToWindow(window); window_cycle_list_->SetFocusedWindow(window);
} }
bool WindowCycleController::IsEventInCycleView(ui::LocatedEvent* event) { bool WindowCycleController::IsEventInCycleView(ui::LocatedEvent* event) {
......
...@@ -45,9 +45,14 @@ class ASH_EXPORT WindowCycleController { ...@@ -45,9 +45,14 @@ class ASH_EXPORT WindowCycleController {
// certain times, such as when the lock screen is visible. // certain times, such as when the lock screen is visible.
static bool CanCycle(); static bool CanCycle();
// Cycles between windows in the given |direction|. // Cycles between windows in the given |direction|. This moves the focus ring
// to the window in the given |direction| and also scrolls the list.
void HandleCycleWindow(Direction direction); void HandleCycleWindow(Direction direction);
// Scrolls the window in the given |direction|. This does not move the focus
// ring.
void Scroll(Direction direction);
// Returns true if we are in the middle of a window cycling gesture. // Returns true if we are in the middle of a window cycling gesture.
bool IsCycling() const { return window_cycle_list_.get() != NULL; } bool IsCycling() const { return window_cycle_list_.get() != NULL; }
...@@ -66,8 +71,9 @@ class ASH_EXPORT WindowCycleController { ...@@ -66,8 +71,9 @@ class ASH_EXPORT WindowCycleController {
// cycling. // cycling.
void MaybeResetCycleList(); void MaybeResetCycleList();
// Skip window cycle list directly to |window|. // Moves the focus ring to |window|. Does not scroll the list. Do nothing if
void StepToWindow(aura::Window* window); // not cycling.
void SetFocusedWindow(aura::Window* window);
// Checks whether |event| occurs within the cycle view. // Checks whether |event| occurs within the cycle view.
bool IsEventInCycleView(ui::LocatedEvent* event); bool IsEventInCycleView(ui::LocatedEvent* event);
......
...@@ -130,6 +130,13 @@ class WindowCycleControllerTest : public AshTestBase { ...@@ -130,6 +130,13 @@ class WindowCycleControllerTest : public AshTestBase {
->cycle_view_for_testing(); ->cycle_view_for_testing();
} }
int GetCurrentIndex() const {
return Shell::Get()
->window_cycle_controller()
->window_cycle_list()
->current_index_for_testing();
}
private: private:
std::unique_ptr<ShelfViewTestAPI> shelf_view_test_; std::unique_ptr<ShelfViewTestAPI> shelf_view_test_;
...@@ -288,6 +295,94 @@ TEST_F(WindowCycleControllerTest, HandleCycleWindow) { ...@@ -288,6 +295,94 @@ TEST_F(WindowCycleControllerTest, HandleCycleWindow) {
EXPECT_FALSE(wm::IsActiveWindow(window1.get())); EXPECT_FALSE(wm::IsActiveWindow(window1.get()));
} }
TEST_F(WindowCycleControllerTest, Scroll) {
WindowCycleController* controller = Shell::Get()->window_cycle_controller();
// Doesn't crash if there are no windows.
controller->Scroll(WindowCycleController::FORWARD);
// Create test windows.
std::unique_ptr<Window> w5 = CreateTestWindow(gfx::Rect(0, 0, 200, 200));
std::unique_ptr<Window> w4 = CreateTestWindow(gfx::Rect(0, 0, 200, 200));
std::unique_ptr<Window> w3 = CreateTestWindow(gfx::Rect(0, 0, 200, 200));
std::unique_ptr<Window> w2 = CreateTestWindow(gfx::Rect(0, 0, 200, 200));
std::unique_ptr<Window> w1 = CreateTestWindow(gfx::Rect(0, 0, 200, 200));
std::unique_ptr<Window> w0 = CreateTestWindow(gfx::Rect(0, 0, 200, 200));
auto ScrollAndReturnCurrentIndex =
[this](WindowCycleController::Direction direction, int num_of_scrolls) {
WindowCycleController* controller =
Shell::Get()->window_cycle_controller();
for (int i = 0; i < num_of_scrolls; i++)
controller->Scroll(direction);
return GetCurrentIndex();
};
auto GetXOfCycleListCenterPoint = [this]() {
return GetWindowCycleListWidget()
->GetWindowBoundsInScreen()
.CenterPoint()
.x();
};
auto GetXOfWindowCycleItemViewCenterPoint = [this](int index) {
return GetWindowCycleItemViews()[index]
->GetBoundsInScreen()
.CenterPoint()
.x();
};
// Start cycling and scroll forward. The list should be not be centered around
// w1. Since w1 is so close to the beginning of the list.
controller->StartCycling();
int current_index =
ScrollAndReturnCurrentIndex(WindowCycleController::FORWARD, 1);
EXPECT_EQ(1, current_index);
EXPECT_GT(GetXOfCycleListCenterPoint(),
GetXOfWindowCycleItemViewCenterPoint(current_index));
// Scroll forward twice. The list should be centered around w3.
current_index =
ScrollAndReturnCurrentIndex(WindowCycleController::FORWARD, 2);
EXPECT_EQ(3, current_index);
EXPECT_EQ(GetXOfCycleListCenterPoint(),
GetXOfWindowCycleItemViewCenterPoint(current_index));
// Scroll backward once. The list should be centered around w2.
current_index =
ScrollAndReturnCurrentIndex(WindowCycleController::BACKWARD, 1);
EXPECT_EQ(2, current_index);
EXPECT_EQ(GetXOfCycleListCenterPoint(),
GetXOfWindowCycleItemViewCenterPoint(current_index));
// Scroll backward three times. The list should not be centered around w5.
current_index =
ScrollAndReturnCurrentIndex(WindowCycleController::BACKWARD, 3);
EXPECT_EQ(5, current_index);
EXPECT_LT(GetXOfCycleListCenterPoint(),
GetXOfWindowCycleItemViewCenterPoint(current_index));
// Cycle forward. Since the target window != current window, it should scroll
// to target window then cycle. The target_window was w0 prior to cycling.
controller->HandleCycleWindow(WindowCycleController::FORWARD);
current_index = GetCurrentIndex();
EXPECT_EQ(1, current_index);
EXPECT_GT(GetXOfCycleListCenterPoint(),
GetXOfWindowCycleItemViewCenterPoint(current_index));
controller->CompleteCycling();
EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
// Start cycling, scroll backward once and complete cycling. Scroll should not
// affect the selected window.
controller->StartCycling();
current_index =
ScrollAndReturnCurrentIndex(WindowCycleController::BACKWARD, 1);
EXPECT_EQ(5, current_index);
controller->CompleteCycling();
EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
}
// Cycles between a maximized and normal window. // Cycles between a maximized and normal window.
TEST_F(WindowCycleControllerTest, MaximizedWindow) { TEST_F(WindowCycleControllerTest, MaximizedWindow) {
// Create a couple of test windows. // Create a couple of test windows.
......
This diff is collapsed.
...@@ -46,13 +46,16 @@ class ASH_EXPORT WindowCycleList : public aura::WindowObserver, ...@@ -46,13 +46,16 @@ class ASH_EXPORT WindowCycleList : public aura::WindowObserver,
// |windows| is empty, cancels cycling. // |windows| is empty, cancels cycling.
void ReplaceWindows(const WindowList& windows); void ReplaceWindows(const WindowList& windows);
// Cycles to the next or previous window based on |direction| and re-layouts // Cycles to the next or previous window based on |direction|. This moves the
// the window cycle list, scrolling the list. // focus ring to the next/previous window and also scrolls the list.
void Step(WindowCycleController::Direction direction); void Step(WindowCycleController::Direction direction);
// Skip window cycle list directly to |window| and don't re-layout the window // Scrolls windows in given |direction|. Does not move the focus ring.
// cycle list, only moving the focus ring. void ScrollInDirection(WindowCycleController::Direction direction);
void StepToWindow(aura::Window* window);
// Moves the focus ring to the respective preview for |window|. Does not
// scroll the window cycle list.
void SetFocusedWindow(aura::Window* window);
// Checks whether |event| occurs within the cycle view. Returns false if // Checks whether |event| occurs within the cycle view. Returns false if
// |cycle_view_| does not exist. // |cycle_view_| does not exist.
...@@ -95,18 +98,27 @@ class ASH_EXPORT WindowCycleList : public aura::WindowObserver, ...@@ -95,18 +98,27 @@ class ASH_EXPORT WindowCycleList : public aura::WindowObserver,
// PIP. // PIP.
void SelectWindow(aura::Window* window); void SelectWindow(aura::Window* window);
// Cycles windows by |offset|. If |should_layout|, layouts the window cycle // Scrolls windows by |offset|. Does not move the focus ring. If you want to
// list and moves the focus border to the newly selected window. If not // scroll the list and move the focus ring in one animation, call
// |should_layout|, just moves the focus border to the newly selected window. // SetFocusedWindow() before this.
// Should be called with |should_layout| if we want the Step() call to animate void Scroll(int offset);
// the window cycle list, scrolling it.
void Step(int offset, bool should_layout); // Returns the index for the window |offset| away from |current_index_|. Can
// only be called if |windows_| is not empty. Also checks that the window for
// the returned index exists.
int GetOffsettedWindowIndex(int offset) const;
// Returns the index for |window| in |windows_|. |window| must be in
// |windows_|.
int GetIndexOfWindow(aura::Window* window) const;
// Returns the views for the window cycle list. // Returns the views for the window cycle list.
const views::View::Views& GetWindowCycleItemViewsForTesting() const; const views::View::Views& GetWindowCycleItemViewsForTesting() const;
WindowCycleView* cycle_view_for_testing() const { return cycle_view_; } WindowCycleView* cycle_view_for_testing() const { return cycle_view_; }
int current_index_for_testing() const { return current_index_; }
// List of weak pointers to windows to use while cycling with the keyboard. // List of weak pointers to windows to use while cycling with the keyboard.
// List is built when the user initiates the gesture (i.e. hits alt-tab the // List is built when the user initiates the gesture (i.e. hits alt-tab the
// first time) and is emptied when the gesture is complete (i.e. releases the // first time) and is emptied when the gesture is complete (i.e. releases the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment