| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/app_list/views/search_result_answer_card_view.h" |
| |
| #include <vector> |
| |
| #include "ui/accessibility/ax_node.h" |
| #include "ui/accessibility/ax_node_data.h" |
| #include "ui/app_list/app_list_constants.h" |
| #include "ui/app_list/app_list_view_delegate.h" |
| #include "ui/app_list/views/search_result_base_view.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/views/background.h" |
| #include "ui/views/layout/box_layout.h" |
| #include "ui/views/layout/fill_layout.h" |
| |
| namespace app_list { |
| |
| // Container of the search answer view. |
| class SearchResultAnswerCardView::SearchAnswerContainerView |
| : public SearchResultBaseView { |
| public: |
| explicit SearchAnswerContainerView(AppListViewDelegate* view_delegate) |
| : view_delegate_(view_delegate) { |
| SetFocusBehavior(FocusBehavior::ALWAYS); |
| // Center the card horizontally in the container. Padding is set on the |
| // server. |
| auto answer_container_layout = |
| std::make_unique<views::BoxLayout>(views::BoxLayout::kHorizontal); |
| answer_container_layout->set_main_axis_alignment( |
| views::BoxLayout::MAIN_AXIS_ALIGNMENT_START); |
| SetLayoutManager(std::move(answer_container_layout)); |
| } |
| |
| ~SearchAnswerContainerView() override { |
| if (search_result_) |
| search_result_->RemoveObserver(this); |
| } |
| |
| bool SetSearchResult(SearchResult* search_result) { |
| views::View* const old_result_view = child_count() ? child_at(0) : nullptr; |
| views::View* const new_result_view = |
| search_result ? search_result->view() : nullptr; |
| |
| if (old_result_view != new_result_view) { |
| if (old_result_view != nullptr) |
| RemoveChildView(old_result_view); |
| if (new_result_view != nullptr) |
| AddChildView(new_result_view); |
| } |
| |
| base::string16 old_title, new_title; |
| if (search_result_) { |
| search_result_->RemoveObserver(this); |
| old_title = search_result_->title(); |
| } |
| search_result_ = search_result; |
| if (search_result_) { |
| search_result_->AddObserver(this); |
| new_title = search_result_->title(); |
| SetAccessibleName(new_title); |
| } |
| |
| return old_title != new_title; |
| } |
| |
| // views::Button overrides: |
| const char* GetClassName() const override { |
| return "SearchAnswerContainerView"; |
| } |
| |
| void OnBlur() override { SetBackgroundHighlighted(false); } |
| |
| void OnFocus() override { |
| ScrollRectToVisible(GetLocalBounds()); |
| NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true); |
| SetBackgroundHighlighted(true); |
| } |
| |
| bool OnKeyPressed(const ui::KeyEvent& event) override { |
| if (event.key_code() == ui::VKEY_SPACE) { |
| // Shouldn't eat Space; we want Space to go to the search box. |
| return false; |
| } |
| |
| return Button::OnKeyPressed(event); |
| } |
| |
| void GetAccessibleNodeData(ui::AXNodeData* node_data) override { |
| // Default button role is atomic for ChromeVox, so assign a generic |
| // container role to allow accessibility focus to get into this view. |
| node_data->role = ui::AX_ROLE_GENERIC_CONTAINER; |
| node_data->SetName(accessible_name()); |
| } |
| |
| void PaintButtonContents(gfx::Canvas* canvas) override { |
| if (background_highlighted()) |
| canvas->FillRect(GetContentsBounds(), kAnswerCardSelectedColor); |
| } |
| |
| // views::ButtonListener overrides: |
| void ButtonPressed(views::Button* sender, const ui::Event& event) override { |
| DCHECK(sender == this); |
| if (search_result_) |
| view_delegate_->OpenSearchResult(search_result_, event.flags()); |
| } |
| |
| // SearchResultObserver overrides: |
| void OnResultDestroying() override { search_result_ = nullptr; } |
| |
| private: |
| AppListViewDelegate* const view_delegate_; // Not owned. |
| SearchResult* search_result_ = nullptr; // Not owned. |
| |
| DISALLOW_COPY_AND_ASSIGN(SearchAnswerContainerView); |
| }; |
| |
| SearchResultAnswerCardView::SearchResultAnswerCardView( |
| AppListViewDelegate* view_delegate) |
| : search_answer_container_view_( |
| new SearchAnswerContainerView(view_delegate)) { |
| AddChildView(search_answer_container_view_); |
| SetLayoutManager(std::make_unique<views::FillLayout>()); |
| } |
| |
| SearchResultAnswerCardView::~SearchResultAnswerCardView() {} |
| |
| const char* SearchResultAnswerCardView::GetClassName() const { |
| return "SearchResultAnswerCardView"; |
| } |
| |
| void SearchResultAnswerCardView::OnContainerSelected( |
| bool from_bottom, |
| bool directional_movement) { |
| if (num_results() == 0) |
| return; |
| |
| SetSelectedIndex(0); |
| } |
| |
| int SearchResultAnswerCardView::GetYSize() { |
| return num_results(); |
| } |
| |
| int SearchResultAnswerCardView::DoUpdate() { |
| std::vector<SearchResult*> display_results = |
| SearchModel::FilterSearchResultsByDisplayType( |
| results(), SearchResult::DISPLAY_CARD, 1); |
| |
| const bool have_result = !display_results.empty(); |
| |
| const bool title_changed = search_answer_container_view_->SetSearchResult( |
| have_result ? display_results[0] : nullptr); |
| parent()->SetVisible(have_result); |
| |
| set_container_score(have_result ? display_results.front()->relevance() : 0); |
| if (title_changed && search_answer_container_view_->HasFocus()) { |
| search_answer_container_view_->NotifyAccessibilityEvent( |
| ui::AX_EVENT_SELECTION, true); |
| } |
| return have_result ? 1 : 0; |
| } |
| |
| void SearchResultAnswerCardView::UpdateSelectedIndex(int old_selected, |
| int new_selected) { |
| // TODO(weidongg): This implementation is deprecated and should be removed as |
| // part of removing "pseudo-focus" logic work (https://crbug.com/766807). |
| } |
| |
| bool SearchResultAnswerCardView::OnKeyPressed(const ui::KeyEvent& event) { |
| if (selected_index() == 0 && |
| search_answer_container_view_->OnKeyPressed(event)) { |
| return true; |
| } |
| |
| return SearchResultContainerView::OnKeyPressed(event); |
| } |
| |
| views::View* SearchResultAnswerCardView::GetSelectedView() { |
| // TODO(weidongg): This implementation is deprecated and should be removed as |
| // part of removing "pseudo-focus" logic work (https://crbug.com/766807). |
| return nullptr; |
| } |
| |
| SearchResultBaseView* SearchResultAnswerCardView::GetFirstResultView() { |
| return num_results() <= 0 ? nullptr : search_answer_container_view_; |
| } |
| |
| views::View* SearchResultAnswerCardView::GetSearchAnswerContainerViewForTest() |
| const { |
| return search_answer_container_view_; |
| } |
| |
| } // namespace app_list |