1 // Copyright 2014 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_page_view.h"
9 #include "ui/app_list/app_list_constants.h"
10 #include "ui/app_list/app_list_switches.h"
11 #include "ui/app_list/app_list_view_delegate.h"
12 #include "ui/app_list/views/app_list_main_view.h"
13 #include "ui/app_list/views/search_box_view.h"
14 #include "ui/app_list/views/search_result_list_view.h"
15 #include "ui/app_list/views/search_result_tile_item_list_view.h"
16 #include "ui/gfx/shadow_value.h"
17 #include "ui/views/background.h"
18 #include "ui/views/layout/box_layout.h"
19 #include "ui/views/layout/fill_layout.h"
20 #include "ui/views/shadow_border.h"
26 const int kGroupSpacing
= 6;
27 const int kTopPadding
= 8;
29 // The z-height of the search box and cards in this view.
30 const int kSearchResultZHeight
= 1;
32 // A container view that ensures the card background and the shadow are painted
33 // in the correct order.
34 class SearchCardView
: public views::View
{
36 explicit SearchCardView(views::View
* content_view
) {
37 SetBorder(make_scoped_ptr(
38 new views::ShadowBorder(GetShadowForZHeight(kSearchResultZHeight
))));
39 SetLayoutManager(new views::FillLayout());
40 content_view
->set_background(
41 views::Background::CreateSolidBackground(kCardBackgroundColor
));
42 AddChildView(content_view
);
45 ~SearchCardView() override
{}
50 SearchResultPageView::SearchResultPageView() : selected_index_(0) {
51 if (switches::IsExperimentalAppListEnabled()) {
52 gfx::ShadowValue shadow
= GetShadowForZHeight(kSearchResultZHeight
);
53 scoped_ptr
<views::Border
> border(new views::ShadowBorder(shadow
));
55 gfx::Insets insets
= gfx::Insets(kTopPadding
, kExperimentalSearchBoxPadding
,
56 0, kExperimentalSearchBoxPadding
);
57 insets
+= -border
->GetInsets();
59 views::BoxLayout
* layout
=
60 new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, kGroupSpacing
);
61 layout
->set_inside_border_insets(insets
);
63 SetLayoutManager(layout
);
65 SetLayoutManager(new views::FillLayout
);
69 SearchResultPageView::~SearchResultPageView() {
72 void SearchResultPageView::SetSelection(bool select
) {
74 SetSelectedIndex(0, false);
79 void SearchResultPageView::AddSearchResultContainerView(
80 AppListModel::SearchResults
* results_model
,
81 SearchResultContainerView
* result_container
) {
82 views::View
* view_to_add
= result_container
;
83 if (switches::IsExperimentalAppListEnabled())
84 view_to_add
= new SearchCardView(result_container
);
86 AddChildView(view_to_add
);
87 result_container_views_
.push_back(result_container
);
88 result_container
->SetResults(results_model
);
89 result_container
->set_delegate(this);
92 bool SearchResultPageView::OnKeyPressed(const ui::KeyEvent
& event
) {
94 result_container_views_
.at(selected_index_
)->OnKeyPressed(event
)) {
99 bool directional_movement
= false;
100 switch (event
.key_code()) {
102 dir
= event
.IsShiftDown() ? -1 : 1;
106 directional_movement
= true;
110 directional_movement
= true;
116 // Find the next result container with results.
117 int new_selected
= selected_index_
;
120 } while (IsValidSelectionIndex(new_selected
) &&
121 result_container_views_
[new_selected
]->num_results() == 0);
123 if (IsValidSelectionIndex(new_selected
)) {
124 SetSelectedIndex(new_selected
, directional_movement
);
131 void SearchResultPageView::ClearSelectedIndex() {
133 result_container_views_
[selected_index_
]->ClearSelectedIndex();
135 selected_index_
= -1;
138 void SearchResultPageView::SetSelectedIndex(int index
,
139 bool directional_movement
) {
140 bool from_bottom
= index
< selected_index_
;
142 // Reset the old selected view's selection.
143 ClearSelectedIndex();
145 selected_index_
= index
;
146 // Set the new selected view's selection to its first result.
147 result_container_views_
[selected_index_
]->OnContainerSelected(
148 from_bottom
, directional_movement
);
151 bool SearchResultPageView::IsValidSelectionIndex(int index
) {
152 return index
>= 0 && index
< static_cast<int>(result_container_views_
.size());
155 void SearchResultPageView::OnSearchResultContainerResultsChanged() {
156 DCHECK(!result_container_views_
.empty());
158 // Only sort and layout the containers when they have all updated.
159 for (SearchResultContainerView
* view
: result_container_views_
) {
160 if (view
->UpdateScheduled()) {
165 SearchResultContainerView
* old_selection
=
166 HasSelection() ? result_container_views_
[selected_index_
] : nullptr;
168 // Truncate the currently selected container's selection if necessary. If
169 // there are no results, the selection will be cleared below.
170 if (old_selection
&& old_selection
->num_results() > 0 &&
171 old_selection
->selected_index() >= old_selection
->num_results()) {
172 old_selection
->SetSelectedIndex(old_selection
->num_results() - 1);
175 if (switches::IsExperimentalAppListEnabled()) {
176 // Sort the result container views by their score.
177 std::sort(result_container_views_
.begin(), result_container_views_
.end(),
178 [](const SearchResultContainerView
* a
,
179 const SearchResultContainerView
* b
) -> bool {
180 return a
->container_score() > b
->container_score();
183 int result_y_index
= 0;
184 for (size_t i
= 0; i
< result_container_views_
.size(); ++i
) {
185 SearchResultContainerView
* view
= result_container_views_
[i
];
186 ReorderChildView(view
->parent(), i
);
188 view
->NotifyFirstResultYIndex(result_y_index
);
190 result_y_index
+= view
->GetYSize();
196 SearchResultContainerView
* new_selection
= nullptr;
197 if (HasSelection() &&
198 result_container_views_
[selected_index_
]->num_results() > 0) {
199 new_selection
= result_container_views_
[selected_index_
];
202 // If there was no previous selection or the new view at the selection index
203 // is different from the old one, update the selected view.
204 if (!HasSelection() || old_selection
!= new_selection
) {
206 old_selection
->ClearSelectedIndex();
208 int new_selection_index
= new_selection
? selected_index_
: 0;
209 // Clear the current selection so that the selection always comes in from
211 ClearSelectedIndex();
212 SetSelectedIndex(new_selection_index
, false);
216 gfx::Rect
SearchResultPageView::GetPageBoundsForState(
217 AppListModel::State state
) const {
218 gfx::Rect onscreen_bounds
= GetDefaultContentsBounds();
220 case AppListModel::STATE_SEARCH_RESULTS
:
221 return onscreen_bounds
;
223 return GetAboveContentsOffscreenBounds(onscreen_bounds
.size());
227 void SearchResultPageView::OnAnimationUpdated(double progress
,
228 AppListModel::State from_state
,
229 AppListModel::State to_state
) {
230 if (from_state
!= AppListModel::STATE_SEARCH_RESULTS
&&
231 to_state
!= AppListModel::STATE_SEARCH_RESULTS
) {
235 gfx::Rect
onscreen_bounds(
236 GetPageBoundsForState(AppListModel::STATE_SEARCH_RESULTS
));
237 set_clip_insets(bounds().InsetsFrom(onscreen_bounds
));
240 int SearchResultPageView::GetSearchBoxZHeight() const {
241 return switches::IsExperimentalAppListEnabled()
242 ? kSearchResultZHeight
243 : AppListPage::GetSearchBoxZHeight();
246 void SearchResultPageView::OnHidden() {
247 ClearSelectedIndex();
250 } // namespace app_list