79124359ef60018e55682fdc4d3457929f9992da
[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_container_(new views::View),
41       auto_launch_indicator_(new views::View),
42       last_visible_index_(0),
43       selected_index_(-1) {
44   results_container_->SetLayoutManager(
45       new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
46
47   int max_results = kMaxResults;
48   if (app_list::switches::IsExperimentalAppListEnabled())
49     max_results = kExperimentAppListMaxResults;
50
51   for (int i = 0; i < max_results; ++i)
52     results_container_->AddChildView(new SearchResultView(this));
53   AddChildView(results_container_);
54
55   auto_launch_indicator_->set_background(
56       views::Background::CreateSolidBackground(kTimeoutIndicatorColor));
57   auto_launch_indicator_->SetVisible(false);
58
59   AddChildView(auto_launch_indicator_);
60 }
61
62 SearchResultListView::~SearchResultListView() {
63 }
64
65 void SearchResultListView::SetSelectedIndex(int selected_index) {
66   if (selected_index_ == selected_index)
67     return;
68
69   if (selected_index_ >= 0) {
70     SearchResultView* selected_view  = GetResultViewAt(selected_index_);
71     selected_view->ClearSelectedAction();
72     selected_view->SchedulePaint();
73   }
74
75   selected_index_ = selected_index;
76
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,
82                                             true);
83   }
84   if (auto_launch_animation_)
85     CancelAutoLaunchTimeout();
86 }
87
88 bool SearchResultListView::IsResultViewSelected(
89     const SearchResultView* result_view) const {
90   if (selected_index_ < 0)
91     return false;
92
93   return static_cast<const SearchResultView*>(
94       results_container_->child_at(selected_index_)) == result_view;
95 }
96
97 void SearchResultListView::UpdateAutoLaunchState() {
98   SetAutoLaunchTimeout(view_delegate_->GetAutoLaunchTimeout());
99 }
100
101 bool SearchResultListView::OnKeyPressed(const ui::KeyEvent& event) {
102   if (selected_index_ >= 0 &&
103       results_container_->child_at(selected_index_)->OnKeyPressed(event)) {
104     return true;
105   }
106
107   switch (event.key_code()) {
108     case ui::VKEY_TAB:
109       if (event.IsShiftDown())
110         SetSelectedIndex(std::max(selected_index_ - 1, 0));
111       else
112         SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
113       return true;
114     case ui::VKEY_UP:
115       SetSelectedIndex(std::max(selected_index_ - 1, 0));
116       return true;
117     case ui::VKEY_DOWN:
118       SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
119       return true;
120     default:
121       break;
122   }
123
124   return false;
125 }
126
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();
135   } else {
136     auto_launch_indicator_->SetVisible(false);
137     auto_launch_animation_.reset();
138   }
139 }
140
141 void SearchResultListView::CancelAutoLaunchTimeout() {
142   SetAutoLaunchTimeout(base::TimeDelta());
143   view_delegate_->AutoLaunchCanceled();
144 }
145
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));
149 }
150
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();
156
157   SearchResultContainerView::ListItemsRemoved(start, count);
158 }
159
160 void SearchResultListView::Update() {
161   std::vector<SearchResult*> display_results =
162       AppListModel::FilterSearchResultsByDisplayType(
163           results(),
164           SearchResult::DISPLAY_LIST,
165           results_container_->child_count());
166   last_visible_index_ = display_results.size() - 1;
167
168   for (size_t i = 0; i < static_cast<size_t>(results_container_->child_count());
169        ++i) {
170     SearchResultView* result_view = GetResultViewAt(i);
171     if (i < display_results.size()) {
172       result_view->SetResult(display_results[i]);
173       result_view->SetVisible(true);
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   UpdateAutoLaunchState();
184 }
185
186 void SearchResultListView::ForceAutoLaunchForTest() {
187   if (auto_launch_animation_)
188     AnimationEnded(auto_launch_animation_.get());
189 }
190
191 void SearchResultListView::Layout() {
192   results_container_->SetBoundsRect(GetLocalBounds());
193 }
194
195 gfx::Size SearchResultListView::GetPreferredSize() const {
196   return results_container_->GetPreferredSize();
197 }
198
199 int SearchResultListView::GetHeightForWidth(int w) const {
200   return results_container_->GetHeightForWidth(w);
201 }
202
203 void SearchResultListView::VisibilityChanged(views::View* starting_from,
204                                              bool is_visible) {
205   if (is_visible)
206     UpdateAutoLaunchState();
207   else
208     CancelAutoLaunchTimeout();
209 }
210
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);
214
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
217   // order:
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();
223 }
224
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);
231 }
232
233 void SearchResultListView::SearchResultActivated(SearchResultView* view,
234                                                  int event_flags) {
235   if (view_delegate_ && view->result())
236     view_delegate_->OpenSearchResult(view->result(), false, event_flags);
237 }
238
239 void SearchResultListView::SearchResultActionActivated(SearchResultView* view,
240                                                        size_t action_index,
241                                                        int event_flags) {
242   if (view_delegate_ && view->result()) {
243     view_delegate_->InvokeSearchResultAction(
244         view->result(), action_index, event_flags);
245   }
246 }
247
248 void SearchResultListView::OnSearchResultInstalled(SearchResultView* view) {
249   if (delegate_ && view->result())
250     delegate_->OnResultInstalled(view->result());
251 }
252
253 void SearchResultListView::OnSearchResultUninstalled(SearchResultView* view) {
254   if (delegate_ && view->result())
255     delegate_->OnResultUninstalled(view->result());
256 }
257
258 }  // namespace app_list