Commit 4d23d3e2 authored by Weidong Guo's avatar Weidong Guo Committed by Commit Bot

applist-focus: Handle arrow key focus traversal in SearchResultPageView

Changes:
1. Handle arrow up/down in SearchResultTileItemListView, so that focus
   could traverse vertically.
2. Move focus to search box when it is moved outside
   SearchResultPageView during vertical focus traversal.

Design Doc: go/applist-focus

BUG=766810
TEST=AppListViewFocusTest.*

Change-Id: Idcd54e7d7c4f2cd6fe425e80218310581c7bcaaa
Reviewed-on: https://chromium-review.googlesource.com/701578
Commit-Queue: Weidong Guo <weidongg@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#506849}
parent 48c6e545
...@@ -556,8 +556,8 @@ TEST_F(AppListViewFocusTest, LinearFocusTraversalInHalfState) { ...@@ -556,8 +556,8 @@ TEST_F(AppListViewFocusTest, LinearFocusTraversalInHalfState) {
// fake search results. // fake search results.
search_box_view()->search_box()->InsertText(base::UTF8ToUTF16("test")); search_box_view()->search_box()->InsertText(base::UTF8ToUTF16("test"));
EXPECT_EQ(app_list_view()->app_list_state(), AppListView::HALF); EXPECT_EQ(app_list_view()->app_list_state(), AppListView::HALF);
const int kTileResults = 3; constexpr int kTileResults = 3;
const int kListResults = 2; constexpr int kListResults = 2;
SetUpSearchResults(kTileResults, kListResults); SetUpSearchResults(kTileResults, kListResults);
std::vector<views::View*> forward_view_list; std::vector<views::View*> forward_view_list;
...@@ -705,6 +705,46 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFullscreenAllAppsState) { ...@@ -705,6 +705,46 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFullscreenAllAppsState) {
TestFocusTraversal(backward_view_list, ui::VKEY_UP, false); TestFocusTraversal(backward_view_list, ui::VKEY_UP, false);
} }
// Tests the vertical focus traversal in HALF state with opened search box.
TEST_F(AppListViewFocusTest, VerticalFocusTraversalInHalfState) {
Show();
// Type something in search box to transition to HALF state and populate
// fake search results.
search_box_view()->search_box()->InsertText(base::UTF8ToUTF16("test"));
EXPECT_EQ(app_list_view()->app_list_state(), AppListView::HALF);
constexpr int kTileResults = 3;
constexpr int kListResults = 2;
SetUpSearchResults(kTileResults, kListResults);
std::vector<views::View*> forward_view_list;
forward_view_list.push_back(search_box_view()->search_box());
const std::vector<SearchResultTileItemView*>& tile_views =
contents_view()
->search_result_tile_item_list_view_for_test()
->tile_views_for_test();
forward_view_list.push_back(tile_views[0]);
views::View* results_container = contents_view()
->search_result_list_view_for_test()
->results_container_for_test();
for (int i = 0; i < kListResults; ++i)
forward_view_list.push_back(results_container->child_at(i));
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());
for (int i = kListResults - 1; i >= 0; --i)
backward_view_list.push_back(results_container->child_at(i));
backward_view_list.push_back(tile_views[kTileResults - 1]);
backward_view_list.push_back(search_box_view()->search_box());
// Test traversal triggered by up.
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 within
// folder. // folder.
TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) { TEST_F(AppListViewFocusTest, VerticalFocusTraversalInFolder) {
......
...@@ -90,7 +90,8 @@ void ContentsView::Init(AppListModel* model) { ...@@ -90,7 +90,8 @@ void ContentsView::Init(AppListModel* model) {
} }
search_result_tile_item_list_view_ = new SearchResultTileItemListView( search_result_tile_item_list_view_ = new SearchResultTileItemListView(
GetSearchBoxView()->search_box(), view_delegate); search_results_page_view_, GetSearchBoxView()->search_box(),
view_delegate);
search_results_page_view_->AddSearchResultContainerView( search_results_page_view_->AddSearchResultContainerView(
results, search_result_tile_item_list_view_); results, search_result_tile_item_list_view_);
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "ui/views/background.h" #include "ui/views/background.h"
#include "ui/views/controls/scroll_view.h" #include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/scrollbar/overlay_scroll_bar.h" #include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/layout/box_layout.h" #include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h" #include "ui/views/layout/fill_layout.h"
#include "ui/views/shadow_border.h" #include "ui/views/shadow_border.h"
...@@ -76,6 +77,15 @@ class ZeroWidthVerticalScrollBar : public views::OverlayScrollBar { ...@@ -76,6 +77,15 @@ class ZeroWidthVerticalScrollBar : public views::OverlayScrollBar {
// OverlayScrollBar overrides: // OverlayScrollBar overrides:
int GetThickness() const override { return 0; } int GetThickness() const override { return 0; }
bool OnKeyPressed(const ui::KeyEvent& event) override {
if (!features::IsAppListFocusEnabled())
return OverlayScrollBar::OnKeyPressed(event);
// Arrow keys should be handled by FocusManager to move focus. When a search
// result is focued, it will be set visible in scroll view.
return false;
}
private: private:
DISALLOW_COPY_AND_ASSIGN(ZeroWidthVerticalScrollBar); DISALLOW_COPY_AND_ASSIGN(ZeroWidthVerticalScrollBar);
}; };
...@@ -215,7 +225,28 @@ void SearchResultPageView::AddSearchResultContainerView( ...@@ -215,7 +225,28 @@ void SearchResultPageView::AddSearchResultContainerView(
bool SearchResultPageView::OnKeyPressed(const ui::KeyEvent& event) { bool SearchResultPageView::OnKeyPressed(const ui::KeyEvent& event) {
if (is_app_list_focus_enabled_) { if (is_app_list_focus_enabled_) {
// TODO(weidongg/766810) Add handling of arrow up and down here. views::View* next_focusable_view = nullptr;
if (event.key_code() == ui::VKEY_UP) {
next_focusable_view = GetFocusManager()->GetNextFocusableView(
GetFocusManager()->GetFocusedView(), GetWidget(), true, false);
} else if (event.key_code() == ui::VKEY_DOWN) {
next_focusable_view = GetFocusManager()->GetNextFocusableView(
GetFocusManager()->GetFocusedView(), GetWidget(), false, false);
}
if (next_focusable_view && !Contains(next_focusable_view)) {
// Hitting up key when focus is on first search result or hitting down
// key when focus is on last search result should move focus onto search
// box.
AppListPage::contents_view()
->GetSearchBoxView()
->search_box()
->RequestFocus();
return true;
}
// Return false to let FocusManager to handle default focus move by key
// events.
return false; return false;
} }
// TODO(weidongg/766807) Remove everything below when the flag is enabled by // TODO(weidongg/766807) Remove everything below when the flag is enabled by
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "ui/app_list/app_list_features.h" #include "ui/app_list/app_list_features.h"
#include "ui/app_list/app_list_view_delegate.h" #include "ui/app_list/app_list_view_delegate.h"
#include "ui/app_list/search_result.h" #include "ui/app_list/search_result.h"
#include "ui/app_list/views/search_result_page_view.h"
#include "ui/app_list/views/search_result_tile_item_view.h" #include "ui/app_list/views/search_result_tile_item_view.h"
#include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/insets.h"
#include "ui/views/background.h" #include "ui/views/background.h"
...@@ -42,9 +43,11 @@ constexpr SkColor kSeparatorColor = SkColorSetARGBMacro(0x1F, 0x00, 0x00, 0x00); ...@@ -42,9 +43,11 @@ constexpr SkColor kSeparatorColor = SkColorSetARGBMacro(0x1F, 0x00, 0x00, 0x00);
namespace app_list { namespace app_list {
SearchResultTileItemListView::SearchResultTileItemListView( SearchResultTileItemListView::SearchResultTileItemListView(
SearchResultPageView* search_result_page_view,
views::Textfield* search_box, views::Textfield* search_box,
AppListViewDelegate* view_delegate) AppListViewDelegate* view_delegate)
: search_box_(search_box), : search_result_page_view_(search_result_page_view),
search_box_(search_box),
is_play_store_app_search_enabled_( is_play_store_app_search_enabled_(
features::IsPlayStoreAppSearchEnabled()), features::IsPlayStoreAppSearchEnabled()),
is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) { is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) {
...@@ -191,10 +194,33 @@ void SearchResultTileItemListView::UpdateSelectedIndex(int old_selected, ...@@ -191,10 +194,33 @@ void SearchResultTileItemListView::UpdateSelectedIndex(int old_selected,
bool SearchResultTileItemListView::OnKeyPressed(const ui::KeyEvent& event) { bool SearchResultTileItemListView::OnKeyPressed(const ui::KeyEvent& event) {
if (features::IsAppListFocusEnabled()) { if (features::IsAppListFocusEnabled()) {
// TODO(weidongg/766807) Remove this function when the flag is enabled by views::View* next_focusable_view = nullptr;
// default.
// Since search result tile item views have horizontal layout, hitting
// up/down when one of them is focused moves focus to the previous/next
// search result container.
if (event.key_code() == ui::VKEY_UP) {
next_focusable_view = GetFocusManager()->GetNextFocusableView(
tile_views_.front(), GetWidget(), true, false);
if (!search_result_page_view_->Contains(next_focusable_view)) {
// Focus should be moved to search box when it is moved outside search
// result page view.
search_box_->RequestFocus();
return true;
}
} else if (event.key_code() == ui::VKEY_DOWN) {
next_focusable_view = GetFocusManager()->GetNextFocusableView(
tile_views_.back(), GetWidget(), false, false);
}
if (next_focusable_view) {
next_focusable_view->RequestFocus();
return true;
}
return false; return false;
} }
// TODO(weidongg/766807) Remove everything below when the flag is enabled by
// default.
int selection_index = selected_index(); int selection_index = selected_index();
// Also count the separator when Play Store app search feature is enabled. // Also count the separator when Play Store app search feature is enabled.
const int child_index = is_play_store_app_search_enabled_ const int child_index = is_play_store_app_search_enabled_
......
...@@ -18,14 +18,16 @@ class Separator; ...@@ -18,14 +18,16 @@ class Separator;
namespace app_list { namespace app_list {
class AppListViewDelegate; class AppListViewDelegate;
class SearchResultPageView;
class SearchResultTileItemView; class SearchResultTileItemView;
// Displays a list of SearchResultTileItemView. // Displays a list of SearchResultTileItemView.
class APP_LIST_EXPORT SearchResultTileItemListView class APP_LIST_EXPORT SearchResultTileItemListView
: public SearchResultContainerView { : public SearchResultContainerView {
public: public:
explicit SearchResultTileItemListView(views::Textfield* search_box, SearchResultTileItemListView(SearchResultPageView* search_result_page_view,
AppListViewDelegate* view_delegate); views::Textfield* search_box,
AppListViewDelegate* view_delegate);
~SearchResultTileItemListView() override; ~SearchResultTileItemListView() override;
// Overridden from SearchResultContainerView: // Overridden from SearchResultContainerView:
...@@ -52,7 +54,9 @@ class APP_LIST_EXPORT SearchResultTileItemListView ...@@ -52,7 +54,9 @@ class APP_LIST_EXPORT SearchResultTileItemListView
std::vector<views::Separator*> separator_views_; std::vector<views::Separator*> separator_views_;
views::Textfield* search_box_; // Owned by the views hierarchy. // Owned by the views hierarchy.
SearchResultPageView* const search_result_page_view_;
views::Textfield* search_box_;
const bool is_play_store_app_search_enabled_; const bool is_play_store_app_search_enabled_;
......
...@@ -56,8 +56,8 @@ class SearchResultTileItemListViewTest ...@@ -56,8 +56,8 @@ class SearchResultTileItemListViewTest
// Sets up the views. // Sets up the views.
textfield_ = base::MakeUnique<views::Textfield>(); textfield_ = base::MakeUnique<views::Textfield>();
view_ = base::MakeUnique<SearchResultTileItemListView>(textfield_.get(), view_ = base::MakeUnique<SearchResultTileItemListView>(
&view_delegate_); nullptr, textfield_.get(), &view_delegate_);
view_->SetResults(view_delegate_.GetModel()->results()); view_->SetResults(view_delegate_.GetModel()->results());
} }
......
...@@ -234,6 +234,8 @@ void SearchResultTileItemView::OnFocus() { ...@@ -234,6 +234,8 @@ void SearchResultTileItemView::OnFocus() {
view_delegate_->GetModel()->state() == AppListModel::STATE_APPS) { view_delegate_->GetModel()->state() == AppListModel::STATE_APPS) {
// Go back to first page when app in suggestions container is focused. // Go back to first page when app in suggestions container is focused.
pagination_model_->SelectPage(0, false); pagination_model_->SelectPage(0, false);
} else if (!is_recommendation()) {
ScrollRectToVisible(GetLocalBounds());
} }
TileItemView::OnFocus(); TileItemView::OnFocus();
} }
......
...@@ -186,6 +186,11 @@ void SearchResultView::SetSelected(bool selected) { ...@@ -186,6 +186,11 @@ void SearchResultView::SetSelected(bool selected) {
if (selected_ == selected) if (selected_ == selected)
return; return;
selected_ = selected; selected_ = selected;
if (features::IsAppListFocusEnabled() && selected_) {
ScrollRectToVisible(GetLocalBounds());
NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
}
SchedulePaint(); SchedulePaint();
} }
......
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