• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ui/app_list/views/search_result_list_view.h"
6 
7 #include <algorithm>
8 
9 #include "base/bind.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/views/search_result_list_view_delegate.h"
16 #include "ui/app_list/views/search_result_view.h"
17 #include "ui/events/event.h"
18 #include "ui/gfx/animation/linear_animation.h"
19 #include "ui/views/background.h"
20 #include "ui/views/layout/box_layout.h"
21 
22 namespace {
23 
24 const int kMaxResults = 6;
25 const int kExperimentAppListMaxResults = 3;
26 const int kTimeoutIndicatorHeight = 2;
27 const int kTimeoutFramerate = 60;
28 const SkColor kTimeoutIndicatorColor = SkColorSetRGB(30, 144, 255);
29 
30 }  // namespace
31 
32 namespace app_list {
33 
SearchResultListView(SearchResultListViewDelegate * delegate,AppListViewDelegate * view_delegate)34 SearchResultListView::SearchResultListView(
35     SearchResultListViewDelegate* delegate,
36     AppListViewDelegate* view_delegate)
37     : delegate_(delegate),
38       view_delegate_(view_delegate),
39       results_(NULL),
40       results_container_(new views::View),
41       auto_launch_indicator_(new views::View),
42       last_visible_index_(0),
43       selected_index_(-1),
44       update_factory_(this) {
45   results_container_->SetLayoutManager(
46       new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
47 
48   int max_results = kMaxResults;
49   if (app_list::switches::IsExperimentalAppListEnabled())
50     max_results = kExperimentAppListMaxResults;
51 
52   for (int i = 0; i < max_results; ++i)
53     results_container_->AddChildView(new SearchResultView(this));
54   AddChildView(results_container_);
55 
56   auto_launch_indicator_->set_background(
57       views::Background::CreateSolidBackground(kTimeoutIndicatorColor));
58   auto_launch_indicator_->SetVisible(false);
59 
60   AddChildView(auto_launch_indicator_);
61 }
62 
~SearchResultListView()63 SearchResultListView::~SearchResultListView() {
64   if (results_)
65     results_->RemoveObserver(this);
66 }
67 
SetResults(AppListModel::SearchResults * results)68 void SearchResultListView::SetResults(AppListModel::SearchResults* results) {
69   if (results_)
70     results_->RemoveObserver(this);
71 
72   results_ = results;
73   if (results_)
74     results_->AddObserver(this);
75 
76   Update();
77 }
78 
SetSelectedIndex(int selected_index)79 void SearchResultListView::SetSelectedIndex(int selected_index) {
80   if (selected_index_ == selected_index)
81     return;
82 
83   if (selected_index_ >= 0) {
84     SearchResultView* selected_view  = GetResultViewAt(selected_index_);
85     selected_view->ClearSelectedAction();
86     selected_view->SchedulePaint();
87   }
88 
89   selected_index_ = selected_index;
90 
91   if (selected_index_ >= 0) {
92     SearchResultView* selected_view  = GetResultViewAt(selected_index_);
93     selected_view->ClearSelectedAction();
94     selected_view->SchedulePaint();
95     selected_view->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS,
96                                             true);
97   }
98   if (auto_launch_animation_)
99     CancelAutoLaunchTimeout();
100 }
101 
IsResultViewSelected(const SearchResultView * result_view) const102 bool SearchResultListView::IsResultViewSelected(
103     const SearchResultView* result_view) const {
104   if (selected_index_ < 0)
105     return false;
106 
107   return static_cast<const SearchResultView*>(
108       results_container_->child_at(selected_index_)) == result_view;
109 }
110 
UpdateAutoLaunchState()111 void SearchResultListView::UpdateAutoLaunchState() {
112   SetAutoLaunchTimeout(view_delegate_->GetAutoLaunchTimeout());
113 }
114 
OnKeyPressed(const ui::KeyEvent & event)115 bool SearchResultListView::OnKeyPressed(const ui::KeyEvent& event) {
116   if (selected_index_ >= 0 &&
117       results_container_->child_at(selected_index_)->OnKeyPressed(event)) {
118     return true;
119   }
120 
121   switch (event.key_code()) {
122     case ui::VKEY_TAB:
123       if (event.IsShiftDown())
124         SetSelectedIndex(std::max(selected_index_ - 1, 0));
125       else
126         SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
127       return true;
128     case ui::VKEY_UP:
129       SetSelectedIndex(std::max(selected_index_ - 1, 0));
130       return true;
131     case ui::VKEY_DOWN:
132       SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
133       return true;
134     default:
135       break;
136   }
137 
138   return false;
139 }
140 
SetAutoLaunchTimeout(const base::TimeDelta & timeout)141 void SearchResultListView::SetAutoLaunchTimeout(
142     const base::TimeDelta& timeout) {
143   if (timeout > base::TimeDelta()) {
144     auto_launch_indicator_->SetVisible(true);
145     auto_launch_indicator_->SetBounds(0, 0, 0, kTimeoutIndicatorHeight);
146     auto_launch_animation_.reset(new gfx::LinearAnimation(
147         timeout.InMilliseconds(), kTimeoutFramerate, this));
148     auto_launch_animation_->Start();
149   } else {
150     auto_launch_indicator_->SetVisible(false);
151     auto_launch_animation_.reset();
152   }
153 }
154 
CancelAutoLaunchTimeout()155 void SearchResultListView::CancelAutoLaunchTimeout() {
156   SetAutoLaunchTimeout(base::TimeDelta());
157   view_delegate_->AutoLaunchCanceled();
158 }
159 
GetResultViewAt(int index)160 SearchResultView* SearchResultListView::GetResultViewAt(int index) {
161   DCHECK(index >= 0 && index < results_container_->child_count());
162   return static_cast<SearchResultView*>(results_container_->child_at(index));
163 }
164 
Update()165 void SearchResultListView::Update() {
166   last_visible_index_ = 0;
167   for (size_t i = 0; i < static_cast<size_t>(results_container_->child_count());
168        ++i) {
169     SearchResultView* result_view = GetResultViewAt(i);
170     if (i < results_->item_count()) {
171       result_view->SetResult(results_->GetItemAt(i));
172       result_view->SetVisible(true);
173       last_visible_index_ = i;
174     } else {
175       result_view->SetResult(NULL);
176       result_view->SetVisible(false);
177     }
178   }
179   if (selected_index_ > last_visible_index_)
180     SetSelectedIndex(last_visible_index_);
181 
182   Layout();
183   update_factory_.InvalidateWeakPtrs();
184   UpdateAutoLaunchState();
185 }
186 
ScheduleUpdate()187 void SearchResultListView::ScheduleUpdate() {
188   // When search results are added one by one, each addition generates an update
189   // request. Consolidates those update requests into one Update call.
190   if (!update_factory_.HasWeakPtrs()) {
191     base::MessageLoop::current()->PostTask(
192         FROM_HERE,
193         base::Bind(&SearchResultListView::Update,
194                    update_factory_.GetWeakPtr()));
195   }
196 }
197 
ForceAutoLaunchForTest()198 void SearchResultListView::ForceAutoLaunchForTest() {
199   if (auto_launch_animation_)
200     AnimationEnded(auto_launch_animation_.get());
201 }
202 
Layout()203 void SearchResultListView::Layout() {
204   results_container_->SetBoundsRect(GetLocalBounds());
205 }
206 
GetPreferredSize() const207 gfx::Size SearchResultListView::GetPreferredSize() const {
208   return results_container_->GetPreferredSize();
209 }
210 
GetHeightForWidth(int w) const211 int SearchResultListView::GetHeightForWidth(int w) const {
212   return results_container_->GetHeightForWidth(w);
213 }
214 
VisibilityChanged(views::View * starting_from,bool is_visible)215 void SearchResultListView::VisibilityChanged(views::View* starting_from,
216                                              bool is_visible) {
217   if (is_visible)
218     UpdateAutoLaunchState();
219   else
220     CancelAutoLaunchTimeout();
221 }
222 
AnimationEnded(const gfx::Animation * animation)223 void SearchResultListView::AnimationEnded(const gfx::Animation* animation) {
224   DCHECK_EQ(auto_launch_animation_.get(), animation);
225   view_delegate_->OpenSearchResult(results_->GetItemAt(0), true, ui::EF_NONE);
226 
227   // The auto-launch has to be canceled explicitly. Think that one of searcher
228   // is extremely slow. Sometimes the events would happen in the following
229   // order:
230   //  1. The search results arrive, auto-launch is dispatched
231   //  2. Timed out and auto-launch the first search result
232   //  3. Then another searcher adds search results more
233   // At the step 3, we shouldn't dispatch the auto-launch again.
234   CancelAutoLaunchTimeout();
235 }
236 
AnimationProgressed(const gfx::Animation * animation)237 void SearchResultListView::AnimationProgressed(
238     const gfx::Animation* animation) {
239   DCHECK_EQ(auto_launch_animation_.get(), animation);
240   int indicator_width = auto_launch_animation_->CurrentValueBetween(0, width());
241   auto_launch_indicator_->SetBounds(
242       0, 0, indicator_width, kTimeoutIndicatorHeight);
243 }
244 
ListItemsAdded(size_t start,size_t count)245 void SearchResultListView::ListItemsAdded(size_t start, size_t count) {
246   ScheduleUpdate();
247 }
248 
ListItemsRemoved(size_t start,size_t count)249 void SearchResultListView::ListItemsRemoved(size_t start, size_t count) {
250   size_t last = std::min(
251       start + count,
252       static_cast<size_t>(results_container_->child_count()));
253   for (size_t i = start; i < last; ++i)
254     GetResultViewAt(i)->ClearResultNoRepaint();
255 
256   ScheduleUpdate();
257 }
258 
ListItemMoved(size_t index,size_t target_index)259 void SearchResultListView::ListItemMoved(size_t index, size_t target_index) {
260   NOTREACHED();
261 }
262 
ListItemsChanged(size_t start,size_t count)263 void SearchResultListView::ListItemsChanged(size_t start, size_t count) {
264   ScheduleUpdate();
265 }
266 
SearchResultActivated(SearchResultView * view,int event_flags)267 void SearchResultListView::SearchResultActivated(SearchResultView* view,
268                                                  int event_flags) {
269   if (view_delegate_ && view->result())
270     view_delegate_->OpenSearchResult(view->result(), false, event_flags);
271 }
272 
SearchResultActionActivated(SearchResultView * view,size_t action_index,int event_flags)273 void SearchResultListView::SearchResultActionActivated(SearchResultView* view,
274                                                        size_t action_index,
275                                                        int event_flags) {
276   if (view_delegate_ && view->result()) {
277     view_delegate_->InvokeSearchResultAction(
278         view->result(), action_index, event_flags);
279   }
280 }
281 
OnSearchResultInstalled(SearchResultView * view)282 void SearchResultListView::OnSearchResultInstalled(SearchResultView* view) {
283   if (delegate_ && view->result())
284     delegate_->OnResultInstalled(view->result());
285 }
286 
OnSearchResultUninstalled(SearchResultView * view)287 void SearchResultListView::OnSearchResultUninstalled(SearchResultView* view) {
288   if (delegate_ && view->result())
289     delegate_->OnResultUninstalled(view->result());
290 }
291 
292 }  // namespace app_list
293