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 @@
#include "ui/accessibility/ax_node_data.h"
#include "ui/app_list/app_list_constants.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/views/app_list_item_view.h"
#include "ui/app_list/views/app_list_main_view.h"
......@@ -514,6 +515,19 @@ void AppListFolderView::Layout() {
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) {
if (item == folder_item_) {
items_grid_view_->OnFolderItemRemoved();
......
......@@ -66,6 +66,7 @@ class APP_LIST_EXPORT AppListFolderView : public views::View,
// views::View
gfx::Size CalculatePreferredSize() const override;
void Layout() override;
bool OnKeyPressed(const ui::KeyEvent& event) override;
// AppListModelObserver
void OnAppListItemWillBeDeleted(AppListItem* item) override;
......
......@@ -43,6 +43,14 @@ views::View* AppListPage::GetSelectedView() const {
return nullptr;
}
views::View* AppListPage::GetFirstFocusableView() {
return nullptr;
}
views::View* AppListPage::GetLastFocusableView() {
return nullptr;
}
gfx::Rect AppListPage::GetAboveContentsOffscreenBounds(
const gfx::Size& size) const {
gfx::Rect rect(size);
......
......@@ -54,6 +54,12 @@ class APP_LIST_EXPORT AppListPage : public views::View {
// Returns selected view in this page.
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:
AppListPage();
~AppListPage() override;
......
......@@ -205,7 +205,7 @@ class AppListViewFocusTest : public views::ViewsTestBase,
// Add suggestion apps, a folder with apps and other app list items.
const int kSuggestionAppNum = 3;
const int kItemNumInFolder = 8;
const int kItemNumInFolder = 25;
const int kAppListItemNum = test_api_->TilesPerPage(0) + 1;
AppListTestModel* model = delegate_->GetTestModel();
SearchModel* search_model = delegate_->GetSearchModel();
......@@ -737,9 +737,9 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInHalfState) {
TestFocusTraversal(backward_view_list, ui::VKEY_UP, false);
}
// Tests the vertical focus traversal in FULLSCREEN_ALL_APPS state within
// folder.
TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) {
// Tests the vertical focus traversal in FULLSCREEN_ALL_APPS state in the first
// page within folder.
TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFirstPageOfFolder) {
Show();
// Transition to FULLSCREEN_ALL_APPS state and open the folder.
......@@ -752,7 +752,7 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) {
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 = 0; i < view_model->view_size();
for (size_t i = 0; i < kMaxFolderItemsPerPage;
i += app_list_folder_view()->items_grid_view()->cols())
forward_view_list.push_back(view_model->view_at(i));
forward_view_list.push_back(
......@@ -766,7 +766,7 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) {
backward_view_list.push_back(search_box_view()->search_box());
backward_view_list.push_back(
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())
backward_view_list.push_back(view_model->view_at(i));
backward_view_list.push_back(search_box_view()->search_box());
......@@ -775,6 +775,50 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) {
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.
TEST_F(AppListViewFocusTest, FocusResetAfterStateTransition) {
Show();
......
......@@ -303,6 +303,23 @@ views::View* AppsContainerView::GetSelectedView() const {
: 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,
bool show_apps_with_animation) {
if (show_state_ == show_state)
......
......@@ -86,6 +86,8 @@ class APP_LIST_EXPORT AppsContainerView : public AppListPage {
gfx::Rect GetPageBoundsForState(ash::AppListState state) const override;
gfx::Rect GetPageBoundsDuringDragging(ash::AppListState state) const override;
views::View* GetSelectedView() const override;
views::View* GetFirstFocusableView() override;
views::View* GetLastFocusableView() override;
AppsGridView* apps_grid_view() { return apps_grid_view_; }
FolderBackgroundView* folder_background_view() {
......
......@@ -1558,16 +1558,28 @@ bool AppsGridView::HandleFocusMovementInFullscreenAllAppsState(bool arrow_up) {
}
// Move focus based on target global focus index.
if (target_global_index < 0 || target_global_index >= cols_ * row_total) {
// Target index is outside apps grid view.
if (folder_delegate_ && !arrow_up) {
if (folder_delegate_ &&
(target_global_index < 0 || target_global_index >= cols_ * row_total ||
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()
->app_list_folder_view()
->folder_header_view()
->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 &&
target_global_index < suggestions_container_->num_results()) {
suggestions_container_->tile_views()[target_global_index]->RequestFocus();
......@@ -1798,6 +1810,20 @@ bool AppsGridView::HandleScrollFromAppListView(int offset, ui::EventType type) {
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) {
// 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.
......
......@@ -221,6 +221,12 @@ class APP_LIST_EXPORT AppsGridView : public views::View,
// returns true if this scroll would change pages.
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.
const views::ViewModelT<AppListItemView>* view_model_for_test() const {
return &view_model_;
......
......@@ -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
// contents view, while down key moves focus to the first element of contents
// view.
views::View* v = GetFocusManager()->GetNextFocusableView(
contents_view(), GetWidget(), event->key_code() == ui::VKEY_UP, false);
ContentsView* contents = static_cast<ContentsView*>(contents_view());
AppListPage* page = contents->GetPageView(contents->GetActivePageIndex());
views::View* v = event->key_code() == ui::VKEY_UP
? page->GetLastFocusableView()
: page->GetFirstFocusableView();
if (v)
v->RequestFocus();
event->SetHandled();
......
......@@ -345,4 +345,14 @@ gfx::Rect SearchResultPageView::GetSearchBoxBounds() const {
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
......@@ -45,6 +45,8 @@ class APP_LIST_EXPORT SearchResultPageView
ash::AppListState from_state,
ash::AppListState to_state) override;
gfx::Rect GetSearchBoxBounds() const override;
views::View* GetFirstFocusableView() override;
views::View* GetLastFocusableView() override;
// Overridden from SearchResultContainerView::Delegate :
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