Commit eed72c4f authored by Weidong Guo's avatar Weidong Guo Committed by Commit Bot

Fix focus movement inside new style folder

Changes:
Since new style folder is paged horizontally, Up and down key should
move focus only within a single page. Thus, hitting up key when focus is
on the first row in current page moves the focus onto the search box and
hitting down key when focus is on the last row in current page moves the
focus onto the folder title.

Specs:
See "Focus change" section in go/applist-focus

BUG=809316

Change-Id: Ic21e76ad176e117a4789fdba5b6428796ed99064
Reviewed-on: https://chromium-review.googlesource.com/903374
Commit-Queue: Weidong Guo <weidongg@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#534799}
parent ec070c40
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/ax_node_data.h"
#include "ui/app_list/app_list_constants.h" #include "ui/app_list/app_list_constants.h"
#include "ui/app_list/app_list_features.h" #include "ui/app_list/app_list_features.h"
#include "ui/app_list/app_list_util.h"
#include "ui/app_list/pagination_model.h" #include "ui/app_list/pagination_model.h"
#include "ui/app_list/views/app_list_item_view.h" #include "ui/app_list/views/app_list_item_view.h"
#include "ui/app_list/views/app_list_main_view.h" #include "ui/app_list/views/app_list_main_view.h"
...@@ -514,6 +515,19 @@ void AppListFolderView::Layout() { ...@@ -514,6 +515,19 @@ void AppListFolderView::Layout() {
views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_); views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
} }
bool AppListFolderView::OnKeyPressed(const ui::KeyEvent& event) {
// Let the FocusManager handle Left/Right keys.
if (!CanProcessUpDownKeyTraversal(event))
return false;
if (folder_header_view_->HasTextFocus() && event.key_code() == ui::VKEY_UP) {
// Move focus to the last app list item view in the selected page.
items_grid_view_->GetCurrentPageLastItemViewInFolder()->RequestFocus();
return true;
}
return false;
}
void AppListFolderView::OnAppListItemWillBeDeleted(AppListItem* item) { void AppListFolderView::OnAppListItemWillBeDeleted(AppListItem* item) {
if (item == folder_item_) { if (item == folder_item_) {
items_grid_view_->OnFolderItemRemoved(); items_grid_view_->OnFolderItemRemoved();
......
...@@ -66,6 +66,7 @@ class APP_LIST_EXPORT AppListFolderView : public views::View, ...@@ -66,6 +66,7 @@ class APP_LIST_EXPORT AppListFolderView : public views::View,
// views::View // views::View
gfx::Size CalculatePreferredSize() const override; gfx::Size CalculatePreferredSize() const override;
void Layout() override; void Layout() override;
bool OnKeyPressed(const ui::KeyEvent& event) override;
// AppListModelObserver // AppListModelObserver
void OnAppListItemWillBeDeleted(AppListItem* item) override; void OnAppListItemWillBeDeleted(AppListItem* item) override;
......
...@@ -43,6 +43,14 @@ views::View* AppListPage::GetSelectedView() const { ...@@ -43,6 +43,14 @@ views::View* AppListPage::GetSelectedView() const {
return nullptr; return nullptr;
} }
views::View* AppListPage::GetFirstFocusableView() {
return nullptr;
}
views::View* AppListPage::GetLastFocusableView() {
return nullptr;
}
gfx::Rect AppListPage::GetAboveContentsOffscreenBounds( gfx::Rect AppListPage::GetAboveContentsOffscreenBounds(
const gfx::Size& size) const { const gfx::Size& size) const {
gfx::Rect rect(size); gfx::Rect rect(size);
......
...@@ -54,6 +54,12 @@ class APP_LIST_EXPORT AppListPage : public views::View { ...@@ -54,6 +54,12 @@ class APP_LIST_EXPORT AppListPage : public views::View {
// Returns selected view in this page. // Returns selected view in this page.
virtual views::View* GetSelectedView() const; virtual views::View* GetSelectedView() const;
// Returns the first focusable view in this page.
virtual views::View* GetFirstFocusableView();
// Returns the last focusable view in this page.
virtual views::View* GetLastFocusableView();
protected: protected:
AppListPage(); AppListPage();
~AppListPage() override; ~AppListPage() override;
......
...@@ -205,7 +205,7 @@ class AppListViewFocusTest : public views::ViewsTestBase, ...@@ -205,7 +205,7 @@ class AppListViewFocusTest : public views::ViewsTestBase,
// Add suggestion apps, a folder with apps and other app list items. // Add suggestion apps, a folder with apps and other app list items.
const int kSuggestionAppNum = 3; const int kSuggestionAppNum = 3;
const int kItemNumInFolder = 8; const int kItemNumInFolder = 25;
const int kAppListItemNum = test_api_->TilesPerPage(0) + 1; const int kAppListItemNum = test_api_->TilesPerPage(0) + 1;
AppListTestModel* model = delegate_->GetTestModel(); AppListTestModel* model = delegate_->GetTestModel();
SearchModel* search_model = delegate_->GetSearchModel(); SearchModel* search_model = delegate_->GetSearchModel();
...@@ -737,9 +737,9 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInHalfState) { ...@@ -737,9 +737,9 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInHalfState) {
TestFocusTraversal(backward_view_list, ui::VKEY_UP, false); TestFocusTraversal(backward_view_list, ui::VKEY_UP, false);
} }
// Tests the vertical focus traversal in FULLSCREEN_ALL_APPS state within // Tests the vertical focus traversal in FULLSCREEN_ALL_APPS state in the first
// folder. // page within folder.
TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) { TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFirstPageOfFolder) {
Show(); Show();
// Transition to FULLSCREEN_ALL_APPS state and open the folder. // Transition to FULLSCREEN_ALL_APPS state and open the folder.
...@@ -752,7 +752,7 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) { ...@@ -752,7 +752,7 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) {
forward_view_list.push_back(search_box_view()->search_box()); forward_view_list.push_back(search_box_view()->search_box());
const views::ViewModelT<AppListItemView>* view_model = const views::ViewModelT<AppListItemView>* view_model =
app_list_folder_view()->items_grid_view()->view_model_for_test(); app_list_folder_view()->items_grid_view()->view_model_for_test();
for (int i = 0; i < view_model->view_size(); for (size_t i = 0; i < kMaxFolderItemsPerPage;
i += app_list_folder_view()->items_grid_view()->cols()) i += app_list_folder_view()->items_grid_view()->cols())
forward_view_list.push_back(view_model->view_at(i)); forward_view_list.push_back(view_model->view_at(i));
forward_view_list.push_back( forward_view_list.push_back(
...@@ -766,7 +766,7 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) { ...@@ -766,7 +766,7 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) {
backward_view_list.push_back(search_box_view()->search_box()); backward_view_list.push_back(search_box_view()->search_box());
backward_view_list.push_back( backward_view_list.push_back(
app_list_folder_view()->folder_header_view()->GetFolderNameViewForTest()); app_list_folder_view()->folder_header_view()->GetFolderNameViewForTest());
for (int i = view_model->view_size() - 1; i >= 0; for (int i = kMaxFolderItemsPerPage - 1; i >= 0;
i -= app_list_folder_view()->items_grid_view()->cols()) i -= app_list_folder_view()->items_grid_view()->cols())
backward_view_list.push_back(view_model->view_at(i)); backward_view_list.push_back(view_model->view_at(i));
backward_view_list.push_back(search_box_view()->search_box()); backward_view_list.push_back(search_box_view()->search_box());
...@@ -775,6 +775,50 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) { ...@@ -775,6 +775,50 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) {
TestFocusTraversal(backward_view_list, ui::VKEY_UP, false); TestFocusTraversal(backward_view_list, ui::VKEY_UP, false);
} }
// Tests the vertical focus traversal in FULLSCREEN_ALL_APPS state in the second
// page within folder.
TEST_F(AppListViewFocusTest, VerticalFocusTraversalInSecondPageOfFolder) {
Show();
// Transition to FULLSCREEN_ALL_APPS state and open the folder.
SetAppListState(AppListViewState::FULLSCREEN_ALL_APPS);
folder_item_view()->RequestFocus();
SimulateKeyPress(ui::VKEY_RETURN, false);
EXPECT_TRUE(contents_view()->apps_container_view()->IsInFolderView());
// Select the second page.
app_list_folder_view()->items_grid_view()->pagination_model()->SelectPage(
1, false /* animate */);
std::vector<views::View*> forward_view_list;
forward_view_list.push_back(search_box_view()->search_box());
const views::ViewModelT<AppListItemView>* view_model =
app_list_folder_view()->items_grid_view()->view_model_for_test();
for (int i = kMaxFolderItemsPerPage; i < view_model->view_size();
i += app_list_folder_view()->items_grid_view()->cols()) {
forward_view_list.push_back(view_model->view_at(i));
}
forward_view_list.push_back(
app_list_folder_view()->folder_header_view()->GetFolderNameViewForTest());
forward_view_list.push_back(search_box_view()->search_box());
// Test traversal triggered by down.
TestFocusTraversal(forward_view_list, ui::VKEY_DOWN, false);
std::vector<views::View*> backward_view_list;
backward_view_list.push_back(search_box_view()->search_box());
backward_view_list.push_back(
app_list_folder_view()->folder_header_view()->GetFolderNameViewForTest());
for (size_t i = view_model->view_size() - 1; i >= kMaxFolderItemsPerPage;
i -= app_list_folder_view()->items_grid_view()->cols()) {
backward_view_list.push_back(view_model->view_at(i));
}
backward_view_list.push_back(search_box_view()->search_box());
// Test traversal triggered by up.
TestFocusTraversal(backward_view_list, ui::VKEY_UP, false);
}
// Tests that the focus is set back onto search box after state transition. // Tests that the focus is set back onto search box after state transition.
TEST_F(AppListViewFocusTest, FocusResetAfterStateTransition) { TEST_F(AppListViewFocusTest, FocusResetAfterStateTransition) {
Show(); Show();
......
...@@ -303,6 +303,23 @@ views::View* AppsContainerView::GetSelectedView() const { ...@@ -303,6 +303,23 @@ views::View* AppsContainerView::GetSelectedView() const {
: apps_grid_view_->GetSelectedView(); : apps_grid_view_->GetSelectedView();
} }
views::View* AppsContainerView::GetFirstFocusableView() {
if (IsInFolderView()) {
// The pagination inside a folder is set horizontally, so focus should be
// set on the first item view in the selected page when it is moved down
// from the search box.
return app_list_folder_view_->items_grid_view()
->GetCurrentPageFirstItemViewInFolder();
}
return GetFocusManager()->GetNextFocusableView(
this, GetWidget(), false /* reverse */, false /* dont_loop */);
}
views::View* AppsContainerView::GetLastFocusableView() {
return GetFocusManager()->GetNextFocusableView(
this, GetWidget(), true /* reverse */, false /* dont_loop */);
}
void AppsContainerView::SetShowState(ShowState show_state, void AppsContainerView::SetShowState(ShowState show_state,
bool show_apps_with_animation) { bool show_apps_with_animation) {
if (show_state_ == show_state) if (show_state_ == show_state)
......
...@@ -86,6 +86,8 @@ class APP_LIST_EXPORT AppsContainerView : public AppListPage { ...@@ -86,6 +86,8 @@ class APP_LIST_EXPORT AppsContainerView : public AppListPage {
gfx::Rect GetPageBoundsForState(ash::AppListState state) const override; gfx::Rect GetPageBoundsForState(ash::AppListState state) const override;
gfx::Rect GetPageBoundsDuringDragging(ash::AppListState state) const override; gfx::Rect GetPageBoundsDuringDragging(ash::AppListState state) const override;
views::View* GetSelectedView() const override; views::View* GetSelectedView() const override;
views::View* GetFirstFocusableView() override;
views::View* GetLastFocusableView() override;
AppsGridView* apps_grid_view() { return apps_grid_view_; } AppsGridView* apps_grid_view() { return apps_grid_view_; }
FolderBackgroundView* folder_background_view() { FolderBackgroundView* folder_background_view() {
......
...@@ -1558,16 +1558,28 @@ bool AppsGridView::HandleFocusMovementInFullscreenAllAppsState(bool arrow_up) { ...@@ -1558,16 +1558,28 @@ bool AppsGridView::HandleFocusMovementInFullscreenAllAppsState(bool arrow_up) {
} }
// Move focus based on target global focus index. // Move focus based on target global focus index.
if (target_global_index < 0 || target_global_index >= cols_ * row_total) { if (folder_delegate_ &&
// Target index is outside apps grid view. (target_global_index < 0 || target_global_index >= cols_ * row_total ||
if (folder_delegate_ && !arrow_up) { target_global_index / kMaxFolderItemsPerPage !=
global_index / kMaxFolderItemsPerPage)) {
// The pagination inside a folder is set horizontally, so focus should be
// set on the search box when it is moved up outside the current page and
// should be set on the folder title when it is moved down outside the
// current page.
if (arrow_up) {
contents_view_->GetSearchBoxView()->search_box()->RequestFocus();
} else {
contents_view_->apps_container_view() contents_view_->apps_container_view()
->app_list_folder_view() ->app_list_folder_view()
->folder_header_view() ->folder_header_view()
->SetTextFocus(); ->SetTextFocus();
} else {
contents_view_->GetSearchBoxView()->search_box()->RequestFocus();
} }
return true;
}
if (target_global_index < 0 || target_global_index >= cols_ * row_total) {
// Target index is outside apps grid view.
contents_view_->GetSearchBoxView()->search_box()->RequestFocus();
} else if (has_suggestions_app && } else if (has_suggestions_app &&
target_global_index < suggestions_container_->num_results()) { target_global_index < suggestions_container_->num_results()) {
suggestions_container_->tile_views()[target_global_index]->RequestFocus(); suggestions_container_->tile_views()[target_global_index]->RequestFocus();
...@@ -1798,6 +1810,20 @@ bool AppsGridView::HandleScrollFromAppListView(int offset, ui::EventType type) { ...@@ -1798,6 +1810,20 @@ bool AppsGridView::HandleScrollFromAppListView(int offset, ui::EventType type) {
return true; return true;
} }
AppListItemView* AppsGridView::GetCurrentPageFirstItemViewInFolder() {
DCHECK(folder_delegate_);
int first_index = pagination_model_.selected_page() * kMaxFolderItemsPerPage;
return view_model_.view_at(first_index);
}
AppListItemView* AppsGridView::GetCurrentPageLastItemViewInFolder() {
DCHECK(folder_delegate_);
int last_index = std::min(
(pagination_model_.selected_page() + 1) * kMaxFolderItemsPerPage - 1,
item_list_->item_count() - 1);
return view_model_.view_at(last_index);
}
void AppsGridView::StartDragAndDropHostDrag(const gfx::Point& grid_location) { void AppsGridView::StartDragAndDropHostDrag(const gfx::Point& grid_location) {
// When a drag and drop host is given, the item can be dragged out of the app // When a drag and drop host is given, the item can be dragged out of the app
// list window. In that case a proxy widget needs to be used. // list window. In that case a proxy widget needs to be used.
......
...@@ -221,6 +221,12 @@ class APP_LIST_EXPORT AppsGridView : public views::View, ...@@ -221,6 +221,12 @@ class APP_LIST_EXPORT AppsGridView : public views::View,
// returns true if this scroll would change pages. // returns true if this scroll would change pages.
bool HandleScrollFromAppListView(int offset, ui::EventType type); bool HandleScrollFromAppListView(int offset, ui::EventType type);
// Returns the first app list item view in the selected page in the folder.
AppListItemView* GetCurrentPageFirstItemViewInFolder();
// Returns the last app list item view in the selected page in the folder.
AppListItemView* GetCurrentPageLastItemViewInFolder();
// Return the view model for test purposes. // Return the view model for test purposes.
const views::ViewModelT<AppListItemView>* view_model_for_test() const { const views::ViewModelT<AppListItemView>* view_model_for_test() const {
return &view_model_; return &view_model_;
......
...@@ -145,8 +145,11 @@ void SearchBoxView::OnKeyEvent(ui::KeyEvent* event) { ...@@ -145,8 +145,11 @@ void SearchBoxView::OnKeyEvent(ui::KeyEvent* event) {
// If focus is in search box view, up key moves focus to the last element of // If focus is in search box view, up key moves focus to the last element of
// contents view, while down key moves focus to the first element of contents // contents view, while down key moves focus to the first element of contents
// view. // view.
views::View* v = GetFocusManager()->GetNextFocusableView( ContentsView* contents = static_cast<ContentsView*>(contents_view());
contents_view(), GetWidget(), event->key_code() == ui::VKEY_UP, false); AppListPage* page = contents->GetPageView(contents->GetActivePageIndex());
views::View* v = event->key_code() == ui::VKEY_UP
? page->GetLastFocusableView()
: page->GetFirstFocusableView();
if (v) if (v)
v->RequestFocus(); v->RequestFocus();
event->SetHandled(); event->SetHandled();
......
...@@ -345,4 +345,14 @@ gfx::Rect SearchResultPageView::GetSearchBoxBounds() const { ...@@ -345,4 +345,14 @@ gfx::Rect SearchResultPageView::GetSearchBoxBounds() const {
return rect; return rect;
} }
views::View* SearchResultPageView::GetFirstFocusableView() {
return GetFocusManager()->GetNextFocusableView(
this, GetWidget(), false /* reverse */, false /* dont_loop */);
}
views::View* SearchResultPageView::GetLastFocusableView() {
return GetFocusManager()->GetNextFocusableView(
this, GetWidget(), true /* reverse */, false /* dont_loop */);
}
} // namespace app_list } // namespace app_list
...@@ -45,6 +45,8 @@ class APP_LIST_EXPORT SearchResultPageView ...@@ -45,6 +45,8 @@ class APP_LIST_EXPORT SearchResultPageView
ash::AppListState from_state, ash::AppListState from_state,
ash::AppListState to_state) override; ash::AppListState to_state) override;
gfx::Rect GetSearchBoxBounds() const override; gfx::Rect GetSearchBoxBounds() const override;
views::View* GetFirstFocusableView() override;
views::View* GetLastFocusableView() override;
// Overridden from SearchResultContainerView::Delegate : // Overridden from SearchResultContainerView::Delegate :
void OnSearchResultContainerResultsChanged() override; void OnSearchResultContainerResultsChanged() override;
......
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