1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/app_list/views/search_result_list_view.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/time/time.h"
12 #include "third_party/skia/include/core/SkColor.h"
13 #include "ui/app_list/app_list_switches.h"
14 #include "ui/app_list/app_list_view_delegate.h"
15 #include "ui/app_list/search_result.h"
16 #include "ui/app_list/views/search_result_list_view_delegate.h"
17 #include "ui/app_list/views/search_result_view.h"
18 #include "ui/events/event.h"
19 #include "ui/gfx/animation/linear_animation.h"
20 #include "ui/views/background.h"
21 #include "ui/views/layout/box_layout.h"
25 const int kMaxResults = 6;
26 const int kExperimentAppListMaxResults = 3;
27 const int kTimeoutIndicatorHeight = 2;
28 const int kTimeoutFramerate = 60;
29 const SkColor kTimeoutIndicatorColor = SkColorSetRGB(30, 144, 255);
35 SearchResultListView::SearchResultListView(
36 SearchResultListViewDelegate* delegate,
37 AppListViewDelegate* view_delegate)
38 : delegate_(delegate),
39 view_delegate_(view_delegate),
40 results_container_(new views::View),
41 auto_launch_indicator_(new views::View),
42 last_visible_index_(0),
44 results_container_->SetLayoutManager(
45 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
47 int max_results = kMaxResults;
48 if (app_list::switches::IsExperimentalAppListEnabled())
49 max_results = kExperimentAppListMaxResults;
51 for (int i = 0; i < max_results; ++i)
52 results_container_->AddChildView(new SearchResultView(this));
53 AddChildView(results_container_);
55 auto_launch_indicator_->set_background(
56 views::Background::CreateSolidBackground(kTimeoutIndicatorColor));
57 auto_launch_indicator_->SetVisible(false);
59 AddChildView(auto_launch_indicator_);
62 SearchResultListView::~SearchResultListView() {
65 void SearchResultListView::SetSelectedIndex(int selected_index) {
66 if (selected_index_ == selected_index)
69 if (selected_index_ >= 0) {
70 SearchResultView* selected_view = GetResultViewAt(selected_index_);
71 selected_view->ClearSelectedAction();
72 selected_view->SchedulePaint();
75 selected_index_ = selected_index;
77 if (selected_index_ >= 0) {
78 SearchResultView* selected_view = GetResultViewAt(selected_index_);
79 selected_view->ClearSelectedAction();
80 selected_view->SchedulePaint();
81 selected_view->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS,
84 if (auto_launch_animation_)
85 CancelAutoLaunchTimeout();
88 bool SearchResultListView::IsResultViewSelected(
89 const SearchResultView* result_view) const {
90 if (selected_index_ < 0)
93 return static_cast<const SearchResultView*>(
94 results_container_->child_at(selected_index_)) == result_view;
97 void SearchResultListView::UpdateAutoLaunchState() {
98 SetAutoLaunchTimeout(view_delegate_->GetAutoLaunchTimeout());
101 bool SearchResultListView::OnKeyPressed(const ui::KeyEvent& event) {
102 if (selected_index_ >= 0 &&
103 results_container_->child_at(selected_index_)->OnKeyPressed(event)) {
107 switch (event.key_code()) {
109 if (event.IsShiftDown())
110 SetSelectedIndex(std::max(selected_index_ - 1, 0));
112 SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
115 SetSelectedIndex(std::max(selected_index_ - 1, 0));
118 SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
127 void SearchResultListView::SetAutoLaunchTimeout(
128 const base::TimeDelta& timeout) {
129 if (timeout > base::TimeDelta()) {
130 auto_launch_indicator_->SetVisible(true);
131 auto_launch_indicator_->SetBounds(0, 0, 0, kTimeoutIndicatorHeight);
132 auto_launch_animation_.reset(new gfx::LinearAnimation(
133 timeout.InMilliseconds(), kTimeoutFramerate, this));
134 auto_launch_animation_->Start();
136 auto_launch_indicator_->SetVisible(false);
137 auto_launch_animation_.reset();
141 void SearchResultListView::CancelAutoLaunchTimeout() {
142 SetAutoLaunchTimeout(base::TimeDelta());
143 view_delegate_->AutoLaunchCanceled();
146 SearchResultView* SearchResultListView::GetResultViewAt(int index) {
147 DCHECK(index >= 0 && index < results_container_->child_count());
148 return static_cast<SearchResultView*>(results_container_->child_at(index));
151 void SearchResultListView::ListItemsRemoved(size_t start, size_t count) {
152 size_t last = std::min(
153 start + count, static_cast<size_t>(results_container_->child_count()));
154 for (size_t i = start; i < last; ++i)
155 GetResultViewAt(i)->ClearResultNoRepaint();
157 SearchResultContainerView::ListItemsRemoved(start, count);
160 void SearchResultListView::Update() {
161 std::vector<SearchResult*> display_results =
162 AppListModel::FilterSearchResultsByDisplayType(
164 SearchResult::DISPLAY_LIST,
165 results_container_->child_count());
166 last_visible_index_ = display_results.size() - 1;
168 for (size_t i = 0; i < static_cast<size_t>(results_container_->child_count());
170 SearchResultView* result_view = GetResultViewAt(i);
171 if (i < display_results.size()) {
172 result_view->SetResult(display_results[i]);
173 result_view->SetVisible(true);
175 result_view->SetResult(NULL);
176 result_view->SetVisible(false);
179 if (selected_index_ > last_visible_index_)
180 SetSelectedIndex(last_visible_index_);
183 UpdateAutoLaunchState();
186 void SearchResultListView::ForceAutoLaunchForTest() {
187 if (auto_launch_animation_)
188 AnimationEnded(auto_launch_animation_.get());
191 void SearchResultListView::Layout() {
192 results_container_->SetBoundsRect(GetLocalBounds());
195 gfx::Size SearchResultListView::GetPreferredSize() const {
196 return results_container_->GetPreferredSize();
199 int SearchResultListView::GetHeightForWidth(int w) const {
200 return results_container_->GetHeightForWidth(w);
203 void SearchResultListView::VisibilityChanged(views::View* starting_from,
206 UpdateAutoLaunchState();
208 CancelAutoLaunchTimeout();
211 void SearchResultListView::AnimationEnded(const gfx::Animation* animation) {
212 DCHECK_EQ(auto_launch_animation_.get(), animation);
213 view_delegate_->OpenSearchResult(results()->GetItemAt(0), true, ui::EF_NONE);
215 // The auto-launch has to be canceled explicitly. Think that one of searcher
216 // is extremely slow. Sometimes the events would happen in the following
218 // 1. The search results arrive, auto-launch is dispatched
219 // 2. Timed out and auto-launch the first search result
220 // 3. Then another searcher adds search results more
221 // At the step 3, we shouldn't dispatch the auto-launch again.
222 CancelAutoLaunchTimeout();
225 void SearchResultListView::AnimationProgressed(
226 const gfx::Animation* animation) {
227 DCHECK_EQ(auto_launch_animation_.get(), animation);
228 int indicator_width = auto_launch_animation_->CurrentValueBetween(0, width());
229 auto_launch_indicator_->SetBounds(
230 0, 0, indicator_width, kTimeoutIndicatorHeight);
233 void SearchResultListView::SearchResultActivated(SearchResultView* view,
235 if (view_delegate_ && view->result())
236 view_delegate_->OpenSearchResult(view->result(), false, event_flags);
239 void SearchResultListView::SearchResultActionActivated(SearchResultView* view,
242 if (view_delegate_ && view->result()) {
243 view_delegate_->InvokeSearchResultAction(
244 view->result(), action_index, event_flags);
248 void SearchResultListView::OnSearchResultInstalled(SearchResultView* view) {
249 if (delegate_ && view->result())
250 delegate_->OnResultInstalled(view->result());
253 void SearchResultListView::OnSearchResultUninstalled(SearchResultView* view) {
254 if (delegate_ && view->result())
255 delegate_->OnResultUninstalled(view->result());
258 } // namespace app_list