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