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_view_delegate.h"
14 #include "ui/app_list/views/search_result_list_view_delegate.h"
15 #include "ui/app_list/views/search_result_view.h"
16 #include "ui/events/event.h"
17 #include "ui/gfx/animation/linear_animation.h"
18 #include "ui/views/background.h"
19 #include "ui/views/layout/box_layout.h"
23 const int kMaxResults = 6;
24 const int kTimeoutIndicatorHeight = 2;
25 const int kTimeoutFramerate = 60;
26 const SkColor kTimeoutIndicatorColor = SkColorSetRGB(30, 144, 255);
32 SearchResultListView::SearchResultListView(
33 SearchResultListViewDelegate* delegate,
34 AppListViewDelegate* view_delegate)
35 : delegate_(delegate),
36 view_delegate_(view_delegate),
38 results_container_(new views::View),
39 auto_launch_indicator_(new views::View),
40 last_visible_index_(0),
42 update_factory_(this) {
43 results_container_->SetLayoutManager(
44 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
46 for (int i = 0; i < kMaxResults; ++i)
47 results_container_->AddChildView(new SearchResultView(this, this));
48 AddChildView(results_container_);
50 auto_launch_indicator_->set_background(
51 views::Background::CreateSolidBackground(kTimeoutIndicatorColor));
52 auto_launch_indicator_->SetVisible(false);
54 AddChildView(auto_launch_indicator_);
57 SearchResultListView::~SearchResultListView() {
59 results_->RemoveObserver(this);
62 void SearchResultListView::SetResults(AppListModel::SearchResults* results) {
64 results_->RemoveObserver(this);
68 results_->AddObserver(this);
73 void SearchResultListView::SetSelectedIndex(int selected_index) {
74 if (selected_index_ == selected_index)
77 if (selected_index_ >= 0) {
78 SearchResultView* selected_view = GetResultViewAt(selected_index_);
79 selected_view->ClearSelectedAction();
80 selected_view->SchedulePaint();
83 selected_index_ = selected_index;
85 if (selected_index_ >= 0) {
86 SearchResultView* selected_view = GetResultViewAt(selected_index_);
87 selected_view->ClearSelectedAction();
88 selected_view->SchedulePaint();
89 selected_view->NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS,
92 if (auto_launch_animation_)
93 CancelAutoLaunchTimeout();
96 bool SearchResultListView::IsResultViewSelected(
97 const SearchResultView* result_view) const {
98 if (selected_index_ < 0)
101 return static_cast<const SearchResultView*>(
102 results_container_->child_at(selected_index_)) == result_view;
105 void SearchResultListView::UpdateAutoLaunchState() {
106 SetAutoLaunchTimeout(view_delegate_->GetAutoLaunchTimeout());
109 bool SearchResultListView::OnKeyPressed(const ui::KeyEvent& event) {
110 if (selected_index_ >= 0 &&
111 results_container_->child_at(selected_index_)->OnKeyPressed(event)) {
115 switch (event.key_code()) {
117 if (event.IsShiftDown())
118 SetSelectedIndex(std::max(selected_index_ - 1, 0));
120 SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
123 SetSelectedIndex(std::max(selected_index_ - 1, 0));
126 SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
135 void SearchResultListView::SetAutoLaunchTimeout(
136 const base::TimeDelta& timeout) {
137 if (timeout > base::TimeDelta()) {
138 auto_launch_indicator_->SetVisible(true);
139 auto_launch_indicator_->SetBounds(0, 0, 0, kTimeoutIndicatorHeight);
140 auto_launch_animation_.reset(new gfx::LinearAnimation(
141 timeout.InMilliseconds(), kTimeoutFramerate, this));
142 auto_launch_animation_->Start();
144 auto_launch_indicator_->SetVisible(false);
145 auto_launch_animation_.reset();
149 void SearchResultListView::CancelAutoLaunchTimeout() {
150 SetAutoLaunchTimeout(base::TimeDelta());
151 view_delegate_->AutoLaunchCanceled();
154 SearchResultView* SearchResultListView::GetResultViewAt(int index) {
155 DCHECK(index >= 0 && index < results_container_->child_count());
156 return static_cast<SearchResultView*>(results_container_->child_at(index));
159 void SearchResultListView::Update() {
160 last_visible_index_ = 0;
161 for (size_t i = 0; i < static_cast<size_t>(results_container_->child_count());
163 SearchResultView* result_view = GetResultViewAt(i);
164 if (i < results_->item_count()) {
165 result_view->SetResult(results_->GetItemAt(i));
166 result_view->SetVisible(true);
167 last_visible_index_ = i;
169 result_view->SetResult(NULL);
170 result_view->SetVisible(false);
173 if (selected_index_ > last_visible_index_)
174 SetSelectedIndex(last_visible_index_);
177 update_factory_.InvalidateWeakPtrs();
178 UpdateAutoLaunchState();
181 void SearchResultListView::ScheduleUpdate() {
182 // When search results are added one by one, each addition generates an update
183 // request. Consolidates those update requests into one Update call.
184 if (!update_factory_.HasWeakPtrs()) {
185 base::MessageLoop::current()->PostTask(
187 base::Bind(&SearchResultListView::Update,
188 update_factory_.GetWeakPtr()));
192 void SearchResultListView::ForceAutoLaunchForTest() {
193 if (auto_launch_animation_)
194 AnimationEnded(auto_launch_animation_.get());
197 void SearchResultListView::Layout() {
198 results_container_->SetBoundsRect(GetLocalBounds());
201 gfx::Size SearchResultListView::GetPreferredSize() {
202 return results_container_->GetPreferredSize();
205 int SearchResultListView::GetHeightForWidth(int w) {
206 return results_container_->GetHeightForWidth(w);
209 void SearchResultListView::VisibilityChanged(views::View* starting_from,
212 UpdateAutoLaunchState();
214 CancelAutoLaunchTimeout();
217 void SearchResultListView::AnimationEnded(const gfx::Animation* animation) {
218 DCHECK_EQ(auto_launch_animation_.get(), animation);
219 delegate_->OpenResult(results_->GetItemAt(0), true, ui::EF_NONE);
221 // The auto-launch has to be canceled explicitly. Think that one of searcher
222 // is extremely slow. Sometimes the events would happen in the following
224 // 1. The search results arrive, auto-launch is dispatched
225 // 2. Timed out and auto-launch the first search result
226 // 3. Then another searcher adds search results more
227 // At the step 3, we shouldn't dispatch the auto-launch again.
228 CancelAutoLaunchTimeout();
231 void SearchResultListView::AnimationProgressed(
232 const gfx::Animation* animation) {
233 DCHECK_EQ(auto_launch_animation_.get(), animation);
234 int indicator_width = auto_launch_animation_->CurrentValueBetween(0, width());
235 auto_launch_indicator_->SetBounds(
236 0, 0, indicator_width, kTimeoutIndicatorHeight);
239 void SearchResultListView::ListItemsAdded(size_t start, size_t count) {
243 void SearchResultListView::ListItemsRemoved(size_t start, size_t count) {
244 size_t last = std::min(
246 static_cast<size_t>(results_container_->child_count()));
247 for (size_t i = start; i < last; ++i)
248 GetResultViewAt(i)->ClearResultNoRepaint();
253 void SearchResultListView::ListItemMoved(size_t index, size_t target_index) {
257 void SearchResultListView::ListItemsChanged(size_t start, size_t count) {
261 void SearchResultListView::SearchResultActivated(SearchResultView* view,
263 if (delegate_ && view->result())
264 delegate_->OpenResult(view->result(), false, event_flags);
267 void SearchResultListView::SearchResultActionActivated(SearchResultView* view,
270 if (delegate_ && view->result()) {
271 delegate_->InvokeResultAction(
272 view->result(), action_index, event_flags);
276 void SearchResultListView::OnSearchResultInstalled(SearchResultView* view) {
277 if (delegate_ && view->result())
278 delegate_->OnResultInstalled(view->result());
281 void SearchResultListView::OnSearchResultUninstalled(SearchResultView* view) {
282 if (delegate_ && view->result())
283 delegate_->OnResultUninstalled(view->result());
286 } // namespace app_list