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/bind.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/time/time.h"
13 #include "third_party/skia/include/core/SkColor.h"
14 #include "ui/app_list/app_list_switches.h"
15 #include "ui/app_list/app_list_view_delegate.h"
16 #include "ui/app_list/search_result.h"
17 #include "ui/app_list/views/search_result_list_view_delegate.h"
18 #include "ui/app_list/views/search_result_view.h"
19 #include "ui/events/event.h"
20 #include "ui/gfx/animation/linear_animation.h"
21 #include "ui/views/background.h"
22 #include "ui/views/layout/box_layout.h"
26 const int kMaxResults
= 6;
27 const int kTimeoutIndicatorHeight
= 2;
28 const int kTimeoutFramerate
= 60;
29 const SkColor kTimeoutIndicatorColor
= SkColorSetRGB(30, 144, 255);
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 results_container_
->SetLayoutManager(
43 new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 0));
45 for (int i
= 0; i
< kMaxResults
; ++i
)
46 results_container_
->AddChildView(new SearchResultView(this));
47 AddChildView(results_container_
);
49 auto_launch_indicator_
->set_background(
50 views::Background::CreateSolidBackground(kTimeoutIndicatorColor
));
51 auto_launch_indicator_
->SetVisible(false);
53 AddChildView(auto_launch_indicator_
);
56 SearchResultListView::~SearchResultListView() {
59 bool SearchResultListView::IsResultViewSelected(
60 const SearchResultView
* result_view
) const {
61 if (selected_index() < 0)
64 return static_cast<const SearchResultView
*>(
65 results_container_
->child_at(selected_index())) == result_view
;
68 void SearchResultListView::UpdateAutoLaunchState() {
69 SetAutoLaunchTimeout(view_delegate_
->GetAutoLaunchTimeout());
72 bool SearchResultListView::OnKeyPressed(const ui::KeyEvent
& event
) {
73 if (selected_index() >= 0 &&
74 results_container_
->child_at(selected_index())->OnKeyPressed(event
)) {
78 int selection_index
= -1;
79 switch (event
.key_code()) {
81 if (event
.IsShiftDown())
82 selection_index
= selected_index() - 1;
84 selection_index
= selected_index() + 1;
87 selection_index
= selected_index() - 1;
90 selection_index
= selected_index() + 1;
96 if (IsValidSelectionIndex(selection_index
)) {
97 SetSelectedIndex(selection_index
);
98 if (auto_launch_animation_
)
99 CancelAutoLaunchTimeout();
106 void SearchResultListView::SetAutoLaunchTimeout(
107 const base::TimeDelta
& timeout
) {
108 if (timeout
> base::TimeDelta()) {
109 auto_launch_indicator_
->SetVisible(true);
110 auto_launch_indicator_
->SetBounds(0, 0, 0, kTimeoutIndicatorHeight
);
111 auto_launch_animation_
.reset(new gfx::LinearAnimation(
112 timeout
.InMilliseconds(), kTimeoutFramerate
, this));
113 auto_launch_animation_
->Start();
115 auto_launch_indicator_
->SetVisible(false);
116 auto_launch_animation_
.reset();
120 void SearchResultListView::CancelAutoLaunchTimeout() {
121 SetAutoLaunchTimeout(base::TimeDelta());
122 view_delegate_
->AutoLaunchCanceled();
125 SearchResultView
* SearchResultListView::GetResultViewAt(int index
) {
126 DCHECK(index
>= 0 && index
< results_container_
->child_count());
127 return static_cast<SearchResultView
*>(results_container_
->child_at(index
));
130 void SearchResultListView::ListItemsRemoved(size_t start
, size_t count
) {
131 size_t last
= std::min(
132 start
+ count
, static_cast<size_t>(results_container_
->child_count()));
133 for (size_t i
= start
; i
< last
; ++i
)
134 GetResultViewAt(i
)->ClearResultNoRepaint();
136 SearchResultContainerView::ListItemsRemoved(start
, count
);
139 void SearchResultListView::OnContainerSelected(bool from_bottom
,
140 bool /*directional_movement*/) {
141 if (num_results() == 0)
144 SetSelectedIndex(from_bottom
? num_results() - 1 : 0);
147 int SearchResultListView::Update() {
148 std::vector
<SearchResult
*> display_results
=
149 AppListModel::FilterSearchResultsByDisplayType(
151 SearchResult::DISPLAY_LIST
,
152 results_container_
->child_count());
154 for (size_t i
= 0; i
< static_cast<size_t>(results_container_
->child_count());
156 SearchResultView
* result_view
= GetResultViewAt(i
);
157 result_view
->set_is_last_result(i
== display_results
.size() - 1);
158 if (i
< display_results
.size()) {
159 result_view
->SetResult(display_results
[i
]);
160 result_view
->SetVisible(true);
162 result_view
->SetResult(NULL
);
163 result_view
->SetVisible(false);
166 UpdateAutoLaunchState();
169 display_results
.empty() ? 0 : display_results
.front()->relevance());
171 return display_results
.size();
174 void SearchResultListView::UpdateSelectedIndex(int old_selected
,
176 if (old_selected
>= 0) {
177 SearchResultView
* selected_view
= GetResultViewAt(old_selected
);
178 selected_view
->ClearSelectedAction();
179 selected_view
->SchedulePaint();
182 if (new_selected
>= 0) {
183 SearchResultView
* selected_view
= GetResultViewAt(new_selected
);
184 selected_view
->ClearSelectedAction();
185 selected_view
->SchedulePaint();
186 selected_view
->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS
, true);
190 void SearchResultListView::ForceAutoLaunchForTest() {
191 if (auto_launch_animation_
)
192 AnimationEnded(auto_launch_animation_
.get());
195 void SearchResultListView::Layout() {
196 results_container_
->SetBoundsRect(GetLocalBounds());
199 gfx::Size
SearchResultListView::GetPreferredSize() const {
200 return results_container_
->GetPreferredSize();
203 int SearchResultListView::GetHeightForWidth(int w
) const {
204 return results_container_
->GetHeightForWidth(w
);
207 void SearchResultListView::VisibilityChanged(views::View
* starting_from
,
210 UpdateAutoLaunchState();
212 CancelAutoLaunchTimeout();
215 void SearchResultListView::AnimationEnded(const gfx::Animation
* animation
) {
216 DCHECK_EQ(auto_launch_animation_
.get(), animation
);
217 view_delegate_
->OpenSearchResult(results()->GetItemAt(0), true, ui::EF_NONE
);
219 // The auto-launch has to be canceled explicitly. Think that one of searcher
220 // is extremely slow. Sometimes the events would happen in the following
222 // 1. The search results arrive, auto-launch is dispatched
223 // 2. Timed out and auto-launch the first search result
224 // 3. Then another searcher adds search results more
225 // At the step 3, we shouldn't dispatch the auto-launch again.
226 CancelAutoLaunchTimeout();
229 void SearchResultListView::AnimationProgressed(
230 const gfx::Animation
* animation
) {
231 DCHECK_EQ(auto_launch_animation_
.get(), animation
);
232 int indicator_width
= auto_launch_animation_
->CurrentValueBetween(0, width());
233 auto_launch_indicator_
->SetBounds(
234 0, 0, indicator_width
, kTimeoutIndicatorHeight
);
237 void SearchResultListView::SearchResultActivated(SearchResultView
* view
,
239 if (view_delegate_
&& view
->result())
240 view_delegate_
->OpenSearchResult(view
->result(), false, event_flags
);
243 void SearchResultListView::SearchResultActionActivated(SearchResultView
* view
,
246 if (view_delegate_
&& view
->result()) {
247 view_delegate_
->InvokeSearchResultAction(
248 view
->result(), action_index
, event_flags
);
252 void SearchResultListView::OnSearchResultInstalled(SearchResultView
* view
) {
253 if (delegate_
&& view
->result())
254 delegate_
->OnResultInstalled(view
->result());
257 } // namespace app_list