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/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"
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);
35 SearchResultListView::SearchResultListView(
36 SearchResultListViewDelegate
* delegate
,
37 AppListViewDelegate
* view_delegate
)
38 : delegate_(delegate
),
39 view_delegate_(view_delegate
),
41 results_container_(new views::View
),
42 auto_launch_indicator_(new views::View
),
43 last_visible_index_(0),
45 update_factory_(this) {
46 results_container_
->SetLayoutManager(
47 new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 0));
49 int max_results
= kMaxResults
;
50 if (app_list::switches::IsExperimentalAppListEnabled())
51 max_results
= kExperimentAppListMaxResults
;
53 for (int i
= 0; i
< max_results
; ++i
)
54 results_container_
->AddChildView(new SearchResultView(this));
55 AddChildView(results_container_
);
57 auto_launch_indicator_
->set_background(
58 views::Background::CreateSolidBackground(kTimeoutIndicatorColor
));
59 auto_launch_indicator_
->SetVisible(false);
61 AddChildView(auto_launch_indicator_
);
64 SearchResultListView::~SearchResultListView() {
66 results_
->RemoveObserver(this);
69 void SearchResultListView::SetResults(AppListModel::SearchResults
* results
) {
71 results_
->RemoveObserver(this);
75 results_
->AddObserver(this);
80 void SearchResultListView::SetSelectedIndex(int selected_index
) {
81 if (selected_index_
== selected_index
)
84 if (selected_index_
>= 0) {
85 SearchResultView
* selected_view
= GetResultViewAt(selected_index_
);
86 selected_view
->ClearSelectedAction();
87 selected_view
->SchedulePaint();
90 selected_index_
= selected_index
;
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
,
99 if (auto_launch_animation_
)
100 CancelAutoLaunchTimeout();
103 bool SearchResultListView::IsResultViewSelected(
104 const SearchResultView
* result_view
) const {
105 if (selected_index_
< 0)
108 return static_cast<const SearchResultView
*>(
109 results_container_
->child_at(selected_index_
)) == result_view
;
112 void SearchResultListView::UpdateAutoLaunchState() {
113 SetAutoLaunchTimeout(view_delegate_
->GetAutoLaunchTimeout());
116 bool SearchResultListView::OnKeyPressed(const ui::KeyEvent
& event
) {
117 if (selected_index_
>= 0 &&
118 results_container_
->child_at(selected_index_
)->OnKeyPressed(event
)) {
122 switch (event
.key_code()) {
124 if (event
.IsShiftDown())
125 SetSelectedIndex(std::max(selected_index_
- 1, 0));
127 SetSelectedIndex(std::min(selected_index_
+ 1, last_visible_index_
));
130 SetSelectedIndex(std::max(selected_index_
- 1, 0));
133 SetSelectedIndex(std::min(selected_index_
+ 1, last_visible_index_
));
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();
151 auto_launch_indicator_
->SetVisible(false);
152 auto_launch_animation_
.reset();
156 void SearchResultListView::CancelAutoLaunchTimeout() {
157 SetAutoLaunchTimeout(base::TimeDelta());
158 view_delegate_
->AutoLaunchCanceled();
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
));
166 void SearchResultListView::Update() {
167 std::vector
<SearchResult
*> display_results
=
168 AppListModel::FilterSearchResultsByDisplayType(
170 SearchResult::DISPLAY_LIST
,
171 results_container_
->child_count());
172 last_visible_index_
= display_results
.size() - 1;
174 for (size_t i
= 0; i
< static_cast<size_t>(results_container_
->child_count());
176 SearchResultView
* result_view
= GetResultViewAt(i
);
177 if (i
< display_results
.size()) {
178 result_view
->SetResult(display_results
[i
]);
179 result_view
->SetVisible(true);
181 result_view
->SetResult(NULL
);
182 result_view
->SetVisible(false);
185 if (selected_index_
> last_visible_index_
)
186 SetSelectedIndex(last_visible_index_
);
189 update_factory_
.InvalidateWeakPtrs();
190 UpdateAutoLaunchState();
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(
199 base::Bind(&SearchResultListView::Update
,
200 update_factory_
.GetWeakPtr()));
204 void SearchResultListView::ForceAutoLaunchForTest() {
205 if (auto_launch_animation_
)
206 AnimationEnded(auto_launch_animation_
.get());
209 void SearchResultListView::Layout() {
210 results_container_
->SetBoundsRect(GetLocalBounds());
213 gfx::Size
SearchResultListView::GetPreferredSize() const {
214 return results_container_
->GetPreferredSize();
217 int SearchResultListView::GetHeightForWidth(int w
) const {
218 return results_container_
->GetHeightForWidth(w
);
221 void SearchResultListView::VisibilityChanged(views::View
* starting_from
,
224 UpdateAutoLaunchState();
226 CancelAutoLaunchTimeout();
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
);
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
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();
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
);
251 void SearchResultListView::ListItemsAdded(size_t start
, size_t count
) {
255 void SearchResultListView::ListItemsRemoved(size_t start
, size_t count
) {
256 size_t last
= std::min(
258 static_cast<size_t>(results_container_
->child_count()));
259 for (size_t i
= start
; i
< last
; ++i
)
260 GetResultViewAt(i
)->ClearResultNoRepaint();
265 void SearchResultListView::ListItemMoved(size_t index
, size_t target_index
) {
269 void SearchResultListView::ListItemsChanged(size_t start
, size_t count
) {
273 void SearchResultListView::SearchResultActivated(SearchResultView
* view
,
275 if (view_delegate_
&& view
->result())
276 view_delegate_
->OpenSearchResult(view
->result(), false, event_flags
);
279 void SearchResultListView::SearchResultActionActivated(SearchResultView
* view
,
282 if (view_delegate_
&& view
->result()) {
283 view_delegate_
->InvokeSearchResultAction(
284 view
->result(), action_index
, event_flags
);
288 void SearchResultListView::OnSearchResultInstalled(SearchResultView
* view
) {
289 if (delegate_
&& view
->result())
290 delegate_
->OnResultInstalled(view
->result());
293 void SearchResultListView::OnSearchResultUninstalled(SearchResultView
* view
) {
294 if (delegate_
&& view
->result())
295 delegate_
->OnResultUninstalled(view
->result());
298 } // namespace app_list