Commit a2bae731 authored by Daniel Zhang's avatar Daniel Zhang Committed by Commit Bot

Make SearchBoxView Search Box have the functionality to autocomplete

user queries.

Current iteration:
1. Autocomplete queries will only autocomplete if it is confident in a
   url or text query (no apps or answer cards).
2. Autocomplete is based off of the first result view in the
   SearchResultPageView.
3. Up/Down/Left/Right/Tab keys all trigger autocomplete.

Bug: 865543
Change-Id: I8be9ba36f4bb79bc250b64d289919bd898144862
Reviewed-on: https://chromium-review.googlesource.com/1141118
Commit-Queue: Daniel Zhang <oxyflush@google.com>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarAlex Newcomer <newcomer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#579967}
parent fbe7fbc4
......@@ -591,8 +591,7 @@ TEST_P(AppListViewFocusTest, LinearFocusTraversalInHalfState) {
forward_view_list.push_back(contents_view()
->search_result_answer_card_view_for_test()
->GetSearchAnswerContainerViewForTest());
SearchResultListView* list_view =
contents_view()->search_result_list_view_for_test();
SearchResultListView* list_view = contents_view()->search_result_list_view();
for (int i = 0; i < kListResults; ++i)
forward_view_list.push_back(list_view->GetResultViewAt(i));
forward_view_list.push_back(search_box_view()->search_box());
......@@ -744,8 +743,7 @@ TEST_F(AppListViewFocusTest, VerticalFocusTraversalInHalfState) {
forward_view_list.push_back(contents_view()
->search_result_answer_card_view_for_test()
->GetSearchAnswerContainerViewForTest());
SearchResultListView* list_view =
contents_view()->search_result_list_view_for_test();
SearchResultListView* list_view = contents_view()->search_result_list_view();
for (int i = 0; i < kListResults; ++i)
forward_view_list.push_back(list_view->GetResultViewAt(i));
forward_view_list.push_back(search_box_view()->search_box());
......@@ -1031,8 +1029,7 @@ TEST_F(AppListViewFocusTest, FirstResultSelectedAfterSearchResultsUpdated) {
search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test"));
const int kListResults = 2;
SetUpSearchResults(0, kListResults, false);
SearchResultListView* list_view =
contents_view()->search_result_list_view_for_test();
SearchResultListView* list_view = contents_view()->search_result_list_view();
EXPECT_EQ(search_box_view()->search_box(), focused_view());
EXPECT_EQ(list_view->GetResultViewAt(0),
contents_view()->search_results_page_view()->first_result_view());
......@@ -1075,8 +1072,7 @@ TEST_F(AppListViewFocusTest, FirstResultNotSelectedAfterQuicklyHittingTab) {
search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test1"));
const int kListResults = 2;
SetUpSearchResults(0, kListResults, false);
SearchResultListView* list_view =
contents_view()->search_result_list_view_for_test();
SearchResultListView* list_view = contents_view()->search_result_list_view();
SearchResultBaseView* first_result_view =
contents_view()->search_results_page_view()->first_result_view();
EXPECT_EQ(search_box_view()->search_box(), focused_view());
......
......@@ -108,7 +108,7 @@ class APP_LIST_EXPORT ContentsView : public views::View,
const {
return search_result_tile_item_list_view_;
}
SearchResultListView* search_result_list_view_for_test() const {
SearchResultListView* search_result_list_view() const {
return search_result_list_view_;
}
HorizontalPageContainer* horizontal_page_container() const {
......
......@@ -298,6 +298,43 @@ void SearchBoxView::ShowZeroStateSuggestions() {
ContentsChanged(search_box(), empty_query);
}
void SearchBoxView::OnWallpaperColorsChanged() {
GetWallpaperProminentColors(
base::BindOnce(&SearchBoxView::OnWallpaperProminentColorsReceived,
weak_ptr_factory_.GetWeakPtr()));
}
void SearchBoxView::ProcessAutocomplete(bool is_search_result_list_view_first) {
if (!is_search_result_list_view_first || last_key_pressed_ == ui::VKEY_BACK ||
search_model_->results()->item_count() == 0) {
// No first result exists, backspace was pressed, or no results exist.
ClearAutocompleteText();
return;
}
const base::string16& current_text = search_box()->text();
const base::string16& details =
search_model_->results()->GetItemAt(0)->details();
const base::string16& search_text =
search_model_->results()->GetItemAt(0)->title();
if (base::StartsWith(details, current_text,
base::CompareCase::INSENSITIVE_ASCII)) {
// Current text in the search_box matches the first result's url.
SetAutocompleteText(details);
return;
}
if (base::StartsWith(search_text, current_text,
base::CompareCase::INSENSITIVE_ASCII)) {
// Current text in the search_box matches the first result's search result
// text.
SetAutocompleteText(search_text);
return;
}
// Current text in the search_box does not match the first result's url or
// search result text.
ClearAutocompleteText();
}
void SearchBoxView::GetWallpaperProminentColors(
AppListViewDelegate::GetWallpaperProminentColorsCallback callback) {
view_delegate_->GetWallpaperProminentColors(std::move(callback));
......@@ -322,15 +359,86 @@ void SearchBoxView::OnWallpaperProminentColorsReceived(
SchedulePaint();
}
void SearchBoxView::AcceptAutocompleteText() {
if (highlight_range_.start() != search_box()->text().length())
ContentsChanged(search_box(), search_box()->text());
}
void SearchBoxView::AcceptOneCharInAutocompleteText() {
highlight_range_.set_start(highlight_range_.start() + 1);
highlight_range_.set_end(search_box()->text().length());
const base::string16 original_text = search_box()->text();
search_box()->SetText(
search_box()->text().substr(0, highlight_range_.start()));
ContentsChanged(search_box(), search_box()->text());
search_box()->SetText(original_text);
search_box()->SetSelectionRange(highlight_range_);
}
void SearchBoxView::ClearAutocompleteText() {
search_box()->SetText(
search_box()->text().substr(0, highlight_range_.start()));
}
void SearchBoxView::ContentsChanged(views::Textfield* sender,
const base::string16& new_contents) {
// Update autocomplete text highlight range to track user typed text.
highlight_range_.set_start(search_box()->text().length());
search_box::SearchBoxViewBase::ContentsChanged(sender, new_contents);
app_list_view_->SetStateFromSearchBoxView(
IsSearchBoxTrimmedQueryEmpty(), true /*triggered_by_contents_change*/);
}
void SearchBoxView::SetAutocompleteText(
const base::string16& autocomplete_text) {
const base::string16& current_text = search_box()->text();
// Currrent text is a prefix of autocomplete text.
DCHECK(base::StartsWith(autocomplete_text, current_text,
base::CompareCase::INSENSITIVE_ASCII));
// Don't set autocomplete text if it's the same as current search box text.
if (autocomplete_text.length() == current_text.length())
return;
search_box()->SetText(autocomplete_text);
highlight_range_.set_end(autocomplete_text.length());
search_box()->SelectRange(highlight_range_);
}
void SearchBoxView::UpdateAutocompleteSelectionRange(uint32_t start,
uint32_t end) {
highlight_range_.set_start(start);
highlight_range_.set_end(end);
}
bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
const ui::KeyEvent& key_event) {
if (search_box()->HasFocus() && is_search_box_active() &&
!search_box()->text().empty()) {
// If the search box has no text in it currently, autocomplete should not
// work.
last_key_pressed_ = key_event.key_code();
if (key_event.type() == ui::ET_KEY_PRESSED &&
key_event.key_code() != ui::VKEY_BACK) {
if (key_event.key_code() == ui::VKEY_RIGHT ||
key_event.key_code() == ui::VKEY_LEFT ||
key_event.key_code() == ui::VKEY_DOWN ||
key_event.key_code() == ui::VKEY_UP ||
key_event.key_code() == ui::VKEY_TAB) {
AcceptAutocompleteText();
} else {
const base::string16 pending_text = search_box()->GetSelectedText();
// Hitting the next key in the autocompete suggestion continues
// autocomplete suggestion. If the selected range doesn't match the
// recorded highlight range, the selection should be overwritten.
if (!pending_text.empty() &&
key_event.GetCharacter() == pending_text[0] &&
pending_text.length() == highlight_range_.length()) {
AcceptOneCharInAutocompleteText();
return true;
}
}
}
}
if (key_event.type() == ui::ET_KEY_PRESSED &&
key_event.key_code() == ui::VKEY_RETURN) {
if (!IsSearchBoxTrimmedQueryEmpty()) {
......@@ -401,10 +509,4 @@ void SearchBoxView::SearchEngineChanged() {
UpdateSearchIcon();
}
void SearchBoxView::OnWallpaperColorsChanged() {
GetWallpaperProminentColors(
base::BindOnce(&SearchBoxView::OnWallpaperProminentColorsReceived,
weak_ptr_factory_.GetWeakPtr()));
}
} // namespace app_list
......@@ -80,6 +80,9 @@ class APP_LIST_EXPORT SearchBoxView : public search_box::SearchBoxViewBase,
// Called when the wallpaper colors change.
void OnWallpaperColorsChanged();
// Sets the autocomplete text if autocomplete conditions are met.
void ProcessAutocomplete(bool is_search_result_list_view_first);
private:
// Gets the wallpaper prominent colors.
void GetWallpaperProminentColors(
......@@ -90,6 +93,21 @@ class APP_LIST_EXPORT SearchBoxView : public search_box::SearchBoxViewBase,
void OnWallpaperProminentColorsReceived(
const std::vector<SkColor>& prominent_colors);
// Notifies SearchBoxViewDelegate that the autocomplete text is valid.
void AcceptAutocompleteText();
// Accepts one character in the autocomplete text and fires query.
void AcceptOneCharInAutocompleteText();
// Removes all autocomplete text.
void ClearAutocompleteText();
// After verifying autocomplete text is valid, sets the current searchbox
// text to the autocomplete text and sets the text highlight.
void SetAutocompleteText(const base::string16& autocomplete_text);
void UpdateAutocompleteSelectionRange(uint32_t start, uint32_t end);
// Overridden from views::TextfieldController:
void ContentsChanged(views::Textfield* sender,
const base::string16& new_contents) override;
......@@ -104,6 +122,12 @@ class APP_LIST_EXPORT SearchBoxView : public search_box::SearchBoxViewBase,
void Update() override;
void SearchEngineChanged() override;
// The range of highlighted text for autocomplete.
gfx::Range highlight_range_;
// The key most recently pressed.
ui::KeyboardCode last_key_pressed_;
AppListViewDelegate* view_delegate_; // Not owned.
SearchModel* search_model_ = nullptr; // Owned by the profile-keyed service.
......
......@@ -278,6 +278,14 @@ void SearchResultPageView::OnSearchResultContainerResultsChanged() {
first_result_view_ = result_container_views_[0]->GetFirstResultView();
// Update SearchBoxView search box autocomplete as necessary based on new
// first result view.
if (first_result_view_) {
AppListPage::contents_view()->GetSearchBoxView()->ProcessAutocomplete(
result_container_views_[0] ==
AppListPage::contents_view()->search_result_list_view());
}
// If one of the search result is focused, do not highlight the first search
// result.
if (Contains(focused_view))
......
......@@ -78,7 +78,7 @@ class SearchResultPageViewTest
view_ = contents_view->search_results_page_view();
tile_list_view_ =
contents_view->search_result_tile_item_list_view_for_test();
list_view_ = contents_view->search_result_list_view_for_test();
list_view_ = contents_view->search_result_list_view();
}
void TearDown() override {
app_list_view_->GetWidget()->Close();
......
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