Upstream version 5.34.104.0
[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_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"
20
21 namespace {
22
23 const int kMaxResults = 6;
24 const int kTimeoutIndicatorHeight = 2;
25 const int kTimeoutFramerate = 60;
26 const SkColor kTimeoutIndicatorColor = SkColorSetRGB(30, 144, 255);
27
28 }  // namespace
29
30 namespace app_list {
31
32 SearchResultListView::SearchResultListView(
33     SearchResultListViewDelegate* delegate,
34     AppListViewDelegate* view_delegate)
35     : delegate_(delegate),
36       view_delegate_(view_delegate),
37       results_(NULL),
38       results_container_(new views::View),
39       auto_launch_indicator_(new views::View),
40       last_visible_index_(0),
41       selected_index_(-1),
42       update_factory_(this) {
43   results_container_->SetLayoutManager(
44       new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
45
46   for (int i = 0; i < kMaxResults; ++i)
47     results_container_->AddChildView(new SearchResultView(this, this));
48   AddChildView(results_container_);
49
50   auto_launch_indicator_->set_background(
51       views::Background::CreateSolidBackground(kTimeoutIndicatorColor));
52   auto_launch_indicator_->SetVisible(false);
53
54   AddChildView(auto_launch_indicator_);
55 }
56
57 SearchResultListView::~SearchResultListView() {
58   if (results_)
59     results_->RemoveObserver(this);
60 }
61
62 void SearchResultListView::SetResults(AppListModel::SearchResults* results) {
63   if (results_)
64     results_->RemoveObserver(this);
65
66   results_ = results;
67   if (results_)
68     results_->AddObserver(this);
69
70   Update();
71 }
72
73 void SearchResultListView::SetSelectedIndex(int selected_index) {
74   if (selected_index_ == selected_index)
75     return;
76
77   if (selected_index_ >= 0) {
78     SearchResultView* selected_view  = GetResultViewAt(selected_index_);
79     selected_view->ClearSelectedAction();
80     selected_view->SchedulePaint();
81   }
82
83   selected_index_ = selected_index;
84
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,
90                                             true);
91   }
92   if (auto_launch_animation_)
93     CancelAutoLaunchTimeout();
94 }
95
96 bool SearchResultListView::IsResultViewSelected(
97     const SearchResultView* result_view) const {
98   if (selected_index_ < 0)
99     return false;
100
101   return static_cast<const SearchResultView*>(
102       results_container_->child_at(selected_index_)) == result_view;
103 }
104
105 void SearchResultListView::UpdateAutoLaunchState() {
106   SetAutoLaunchTimeout(view_delegate_->GetAutoLaunchTimeout());
107 }
108
109 bool SearchResultListView::OnKeyPressed(const ui::KeyEvent& event) {
110   if (selected_index_ >= 0 &&
111       results_container_->child_at(selected_index_)->OnKeyPressed(event)) {
112     return true;
113   }
114
115   switch (event.key_code()) {
116     case ui::VKEY_TAB:
117       if (event.IsShiftDown())
118         SetSelectedIndex(std::max(selected_index_ - 1, 0));
119       else
120         SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
121       return true;
122     case ui::VKEY_UP:
123       SetSelectedIndex(std::max(selected_index_ - 1, 0));
124       return true;
125     case ui::VKEY_DOWN:
126       SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
127       return true;
128     default:
129       break;
130   }
131
132   return false;
133 }
134
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();
143   } else {
144     auto_launch_indicator_->SetVisible(false);
145     auto_launch_animation_.reset();
146   }
147 }
148
149 void SearchResultListView::CancelAutoLaunchTimeout() {
150   SetAutoLaunchTimeout(base::TimeDelta());
151   view_delegate_->AutoLaunchCanceled();
152 }
153
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));
157 }
158
159 void SearchResultListView::Update() {
160   last_visible_index_ = 0;
161   for (size_t i = 0; i < static_cast<size_t>(results_container_->child_count());
162        ++i) {
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;
168     } else {
169       result_view->SetResult(NULL);
170       result_view->SetVisible(false);
171     }
172   }
173   if (selected_index_ > last_visible_index_)
174     SetSelectedIndex(last_visible_index_);
175
176   Layout();
177   update_factory_.InvalidateWeakPtrs();
178   UpdateAutoLaunchState();
179 }
180
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(
186         FROM_HERE,
187         base::Bind(&SearchResultListView::Update,
188                    update_factory_.GetWeakPtr()));
189   }
190 }
191
192 void SearchResultListView::ForceAutoLaunchForTest() {
193   if (auto_launch_animation_)
194     AnimationEnded(auto_launch_animation_.get());
195 }
196
197 void SearchResultListView::Layout() {
198   results_container_->SetBoundsRect(GetLocalBounds());
199 }
200
201 gfx::Size SearchResultListView::GetPreferredSize() {
202   return results_container_->GetPreferredSize();
203 }
204
205 int SearchResultListView::GetHeightForWidth(int w) {
206   return results_container_->GetHeightForWidth(w);
207 }
208
209 void SearchResultListView::VisibilityChanged(views::View* starting_from,
210                                              bool is_visible) {
211   if (is_visible)
212     UpdateAutoLaunchState();
213   else
214     CancelAutoLaunchTimeout();
215 }
216
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);
220
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
223   // order:
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();
229 }
230
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);
237 }
238
239 void SearchResultListView::ListItemsAdded(size_t start, size_t count) {
240   ScheduleUpdate();
241 }
242
243 void SearchResultListView::ListItemsRemoved(size_t start, size_t count) {
244   size_t last = std::min(
245       start + count,
246       static_cast<size_t>(results_container_->child_count()));
247   for (size_t i = start; i < last; ++i)
248     GetResultViewAt(i)->ClearResultNoRepaint();
249
250   ScheduleUpdate();
251 }
252
253 void SearchResultListView::ListItemMoved(size_t index, size_t target_index) {
254   NOTREACHED();
255 }
256
257 void SearchResultListView::ListItemsChanged(size_t start, size_t count) {
258   ScheduleUpdate();
259 }
260
261 void SearchResultListView::SearchResultActivated(SearchResultView* view,
262                                                  int event_flags) {
263   if (delegate_ && view->result())
264     delegate_->OpenResult(view->result(), false, event_flags);
265 }
266
267 void SearchResultListView::SearchResultActionActivated(SearchResultView* view,
268                                                        size_t action_index,
269                                                        int event_flags) {
270   if (delegate_ && view->result()) {
271     delegate_->InvokeResultAction(
272         view->result(), action_index, event_flags);
273   }
274 }
275
276 void SearchResultListView::OnSearchResultInstalled(SearchResultView* view) {
277   if (delegate_ && view->result())
278     delegate_->OnResultInstalled(view->result());
279 }
280
281 void SearchResultListView::OnSearchResultUninstalled(SearchResultView* view) {
282   if (delegate_ && view->result())
283     delegate_->OnResultUninstalled(view->result());
284 }
285
286 }  // namespace app_list