Change ContentsView::SetActivePage to use States rather than indexes.
[chromium-blink-merge.git] / ui / app_list / views / start_page_view.cc
blob1c62aca9be534cbe90dd5edc048b74845d4fd5ad
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/start_page_view.h"
7 #include <string>
9 #include "base/i18n/rtl.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "ui/accessibility/ax_view_state.h"
13 #include "ui/app_list/app_list_constants.h"
14 #include "ui/app_list/app_list_item.h"
15 #include "ui/app_list/app_list_model.h"
16 #include "ui/app_list/app_list_view_delegate.h"
17 #include "ui/app_list/search_result.h"
18 #include "ui/app_list/views/all_apps_tile_item_view.h"
19 #include "ui/app_list/views/app_list_main_view.h"
20 #include "ui/app_list/views/contents_view.h"
21 #include "ui/app_list/views/search_box_view.h"
22 #include "ui/app_list/views/search_result_container_view.h"
23 #include "ui/app_list/views/search_result_tile_item_view.h"
24 #include "ui/app_list/views/tile_item_view.h"
25 #include "ui/gfx/canvas.h"
26 #include "ui/views/background.h"
27 #include "ui/views/controls/image_view.h"
28 #include "ui/views/controls/label.h"
29 #include "ui/views/controls/textfield/textfield.h"
30 #include "ui/views/layout/box_layout.h"
31 #include "ui/views/widget/widget.h"
33 namespace app_list {
35 namespace {
37 // Layout constants.
38 const int kInstantContainerSpacing = 24;
39 const int kSearchBoxAndTilesSpacing = 35;
40 const int kStartPageSearchBoxWidth = 480;
42 // WebView constants.
43 const int kWebViewWidth = 700;
44 const int kWebViewHeight = 244;
46 // Tile container constants.
47 const size_t kNumStartPageTiles = 4;
48 const int kTileSpacing = 7;
50 const int kLauncherPageBackgroundWidth = 400;
52 // An invisible placeholder view which fills the space for the search box view
53 // in a box layout. The search box view itself is a child of the AppListView
54 // (because it is visible on many different pages).
55 class SearchBoxSpacerView : public views::View {
56 public:
57 explicit SearchBoxSpacerView(const gfx::Size& search_box_size)
58 : size_(kStartPageSearchBoxWidth, search_box_size.height()) {}
60 ~SearchBoxSpacerView() override {}
62 // Overridden from views::View:
63 gfx::Size GetPreferredSize() const override { return size_; }
65 private:
66 gfx::Size size_;
68 DISALLOW_COPY_AND_ASSIGN(SearchBoxSpacerView);
71 } // namespace
73 class CustomLauncherPageBackgroundView : public views::View {
74 public:
75 explicit CustomLauncherPageBackgroundView(
76 const std::string& custom_launcher_page_name)
77 : selected_(false),
78 custom_launcher_page_name_(custom_launcher_page_name) {
79 set_background(views::Background::CreateSolidBackground(kSelectedColor));
81 ~CustomLauncherPageBackgroundView() override {}
83 void SetSelected(bool selected) {
84 selected_ = selected;
85 SetVisible(selected);
86 if (selected)
87 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
90 bool selected() { return selected_; }
92 // Overridden from views::View:
93 void GetAccessibleState(ui::AXViewState* state) override {
94 state->role = ui::AX_ROLE_BUTTON;
95 state->name = base::UTF8ToUTF16(custom_launcher_page_name_);
98 private:
99 bool selected_;
100 std::string custom_launcher_page_name_;
102 DISALLOW_COPY_AND_ASSIGN(CustomLauncherPageBackgroundView);
105 // A container that holds the start page recommendation tiles and the all apps
106 // tile.
107 class StartPageView::StartPageTilesContainer
108 : public SearchResultContainerView {
109 public:
110 StartPageTilesContainer(ContentsView* contents_view,
111 AllAppsTileItemView* all_apps_button);
112 ~StartPageTilesContainer() override;
114 TileItemView* GetTileItemView(int index);
116 const std::vector<SearchResultTileItemView*>& tile_views() const {
117 return search_result_tile_views_;
120 AllAppsTileItemView* all_apps_button() { return all_apps_button_; }
122 // Overridden from SearchResultContainerView:
123 int Update() override;
124 void UpdateSelectedIndex(int old_selected, int new_selected) override;
125 void OnContainerSelected(bool from_bottom,
126 bool directional_movement) override;
128 private:
129 ContentsView* contents_view_;
131 std::vector<SearchResultTileItemView*> search_result_tile_views_;
132 AllAppsTileItemView* all_apps_button_;
134 DISALLOW_COPY_AND_ASSIGN(StartPageTilesContainer);
137 StartPageView::StartPageTilesContainer::StartPageTilesContainer(
138 ContentsView* contents_view,
139 AllAppsTileItemView* all_apps_button)
140 : contents_view_(contents_view), all_apps_button_(all_apps_button) {
141 views::BoxLayout* tiles_layout_manager =
142 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, kTileSpacing);
143 tiles_layout_manager->set_main_axis_alignment(
144 views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
145 SetLayoutManager(tiles_layout_manager);
146 set_background(
147 views::Background::CreateSolidBackground(kLabelBackgroundColor));
149 // Add SearchResultTileItemViews to the container.
150 for (size_t i = 0; i < kNumStartPageTiles; ++i) {
151 SearchResultTileItemView* tile_item = new SearchResultTileItemView(this);
152 AddChildView(tile_item);
153 tile_item->SetParentBackgroundColor(kLabelBackgroundColor);
154 search_result_tile_views_.push_back(tile_item);
157 // Also add a special "all apps" button to the end of the container.
158 all_apps_button_->UpdateIcon();
159 all_apps_button_->SetParentBackgroundColor(kLabelBackgroundColor);
160 AddChildView(all_apps_button_);
163 StartPageView::StartPageTilesContainer::~StartPageTilesContainer() {
166 TileItemView* StartPageView::StartPageTilesContainer::GetTileItemView(
167 int index) {
168 DCHECK_GT(num_results(), index);
169 if (index == num_results() - 1)
170 return all_apps_button_;
172 return search_result_tile_views_[index];
175 int StartPageView::StartPageTilesContainer::Update() {
176 // Ignore updates and disable buttons when transitioning to a different
177 // state.
178 if (contents_view_->GetActiveState() != AppListModel::STATE_START) {
179 for (auto* view : search_result_tile_views_)
180 view->SetEnabled(false);
182 return num_results();
185 std::vector<SearchResult*> display_results =
186 AppListModel::FilterSearchResultsByDisplayType(
187 results(), SearchResult::DISPLAY_RECOMMENDATION, kNumStartPageTiles);
189 // Update the tile item results.
190 for (size_t i = 0; i < search_result_tile_views_.size(); ++i) {
191 SearchResult* item = NULL;
192 if (i < display_results.size())
193 item = display_results[i];
194 search_result_tile_views_[i]->SetSearchResult(item);
195 search_result_tile_views_[i]->SetEnabled(true);
198 Layout();
199 parent()->Layout();
200 // Add 1 to the results size to account for the all apps button.
201 return display_results.size() + 1;
204 void StartPageView::StartPageTilesContainer::UpdateSelectedIndex(
205 int old_selected,
206 int new_selected) {
207 if (old_selected >= 0)
208 GetTileItemView(old_selected)->SetSelected(false);
210 if (new_selected >= 0)
211 GetTileItemView(new_selected)->SetSelected(true);
214 void StartPageView::StartPageTilesContainer::OnContainerSelected(
215 bool /*from_bottom*/,
216 bool /*directional_movement*/) {
219 ////////////////////////////////////////////////////////////////////////////////
220 // StartPageView implementation:
221 StartPageView::StartPageView(AppListMainView* app_list_main_view,
222 AppListViewDelegate* view_delegate)
223 : app_list_main_view_(app_list_main_view),
224 view_delegate_(view_delegate),
225 search_box_spacer_view_(new SearchBoxSpacerView(
226 app_list_main_view->search_box_view()->GetPreferredSize())),
227 instant_container_(new views::View),
228 custom_launcher_page_background_(new CustomLauncherPageBackgroundView(
229 view_delegate_->GetModel()->custom_launcher_page_name())),
230 tiles_container_(new StartPageTilesContainer(
231 app_list_main_view->contents_view(),
232 new AllAppsTileItemView(
233 app_list_main_view_->contents_view(),
234 view_delegate_->GetModel()->top_level_item_list()))) {
235 // The view containing the start page WebContents and SearchBoxSpacerView.
236 InitInstantContainer();
237 AddChildView(instant_container_);
239 // The view containing the start page tiles.
240 AddChildView(tiles_container_);
242 AddChildView(custom_launcher_page_background_);
244 tiles_container_->SetResults(view_delegate_->GetModel()->results());
245 Reset();
248 StartPageView::~StartPageView() {
251 void StartPageView::InitInstantContainer() {
252 views::BoxLayout* instant_layout_manager = new views::BoxLayout(
253 views::BoxLayout::kVertical, 0, 0, kInstantContainerSpacing);
254 instant_layout_manager->set_inside_border_insets(
255 gfx::Insets(0, 0, kSearchBoxAndTilesSpacing, 0));
256 instant_layout_manager->set_main_axis_alignment(
257 views::BoxLayout::MAIN_AXIS_ALIGNMENT_END);
258 instant_layout_manager->set_cross_axis_alignment(
259 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
260 instant_container_->SetLayoutManager(instant_layout_manager);
262 views::View* web_view = view_delegate_->CreateStartPageWebView(
263 gfx::Size(kWebViewWidth, kWebViewHeight));
264 if (web_view) {
265 web_view->SetFocusable(false);
266 instant_container_->AddChildView(web_view);
269 instant_container_->AddChildView(search_box_spacer_view_);
272 void StartPageView::MaybeOpenCustomLauncherPage() {
273 // Switch to the custom page.
274 ContentsView* contents_view = app_list_main_view_->contents_view();
275 if (!app_list_main_view_->ShouldShowCustomLauncherPage())
276 return;
278 UMA_HISTOGRAM_ENUMERATION(kPageOpenedHistogram,
279 AppListModel::STATE_CUSTOM_LAUNCHER_PAGE,
280 AppListModel::STATE_LAST);
282 contents_view->SetActiveState(AppListModel::STATE_CUSTOM_LAUNCHER_PAGE);
285 void StartPageView::Reset() {
286 tiles_container_->Update();
289 void StartPageView::UpdateForTesting() {
290 tiles_container_->Update();
293 const std::vector<SearchResultTileItemView*>& StartPageView::tile_views()
294 const {
295 return tiles_container_->tile_views();
298 TileItemView* StartPageView::all_apps_button() const {
299 return tiles_container_->all_apps_button();
302 void StartPageView::OnShow() {
303 tiles_container_->Update();
304 tiles_container_->ClearSelectedIndex();
305 custom_launcher_page_background_->SetSelected(false);
308 void StartPageView::Layout() {
309 gfx::Rect bounds(GetContentsBounds());
310 bounds.set_height(instant_container_->GetHeightForWidth(bounds.width()));
311 instant_container_->SetBoundsRect(bounds);
313 // Tiles begin where the instant container ends.
314 bounds.set_y(bounds.bottom());
315 bounds.set_height(tiles_container_->GetHeightForWidth(bounds.width()));
316 tiles_container_->SetBoundsRect(bounds);
318 bounds = app_list_main_view_->contents_view()->GetCustomPageCollapsedBounds();
319 bounds.Intersect(GetContentsBounds());
320 bounds.ClampToCenteredSize(
321 gfx::Size(kLauncherPageBackgroundWidth, bounds.height()));
322 custom_launcher_page_background_->SetBoundsRect(bounds);
325 bool StartPageView::OnKeyPressed(const ui::KeyEvent& event) {
326 const int forward_dir = base::i18n::IsRTL() ? -1 : 1;
327 int selected_index = tiles_container_->selected_index();
329 if (custom_launcher_page_background_->selected()) {
330 selected_index = tiles_container_->num_results();
331 switch (event.key_code()) {
332 case ui::VKEY_RETURN:
333 MaybeOpenCustomLauncherPage();
334 return true;
335 default:
336 break;
338 } else if (selected_index >= 0 &&
339 tiles_container_->GetTileItemView(selected_index)
340 ->OnKeyPressed(event)) {
341 return true;
344 int dir = 0;
345 switch (event.key_code()) {
346 case ui::VKEY_LEFT:
347 dir = -forward_dir;
348 break;
349 case ui::VKEY_RIGHT:
350 // Don't go to the custom launcher page from the All apps tile.
351 if (selected_index != tiles_container_->num_results() - 1)
352 dir = forward_dir;
353 break;
354 case ui::VKEY_UP:
355 // Up selects the first tile if the custom launcher is selected.
356 if (custom_launcher_page_background_->selected()) {
357 selected_index = -1;
358 dir = 1;
360 break;
361 case ui::VKEY_DOWN:
362 // Down selects the first tile if nothing is selected.
363 dir = 1;
364 // If something is selected, select the custom launcher page.
365 if (tiles_container_->IsValidSelectionIndex(selected_index))
366 selected_index = tiles_container_->num_results() - 1;
367 break;
368 case ui::VKEY_TAB:
369 dir = event.IsShiftDown() ? -1 : 1;
370 break;
371 default:
372 break;
375 if (dir == 0)
376 return false;
378 if (selected_index == -1) {
379 custom_launcher_page_background_->SetSelected(false);
380 tiles_container_->SetSelectedIndex(
381 dir == -1 ? tiles_container_->num_results() - 1 : 0);
382 return true;
385 int selection_index = selected_index + dir;
386 if (tiles_container_->IsValidSelectionIndex(selection_index)) {
387 custom_launcher_page_background_->SetSelected(false);
388 tiles_container_->SetSelectedIndex(selection_index);
389 return true;
392 if (selection_index == tiles_container_->num_results() &&
393 app_list_main_view_->ShouldShowCustomLauncherPage()) {
394 custom_launcher_page_background_->SetSelected(true);
395 tiles_container_->ClearSelectedIndex();
396 return true;
399 if (event.key_code() == ui::VKEY_TAB && selection_index == -1)
400 tiles_container_->ClearSelectedIndex(); // ContentsView will handle focus.
402 return false;
405 bool StartPageView::OnMousePressed(const ui::MouseEvent& event) {
406 ContentsView* contents_view = app_list_main_view_->contents_view();
407 if (!contents_view->GetCustomPageCollapsedBounds().Contains(event.location()))
408 return false;
410 MaybeOpenCustomLauncherPage();
411 return true;
414 bool StartPageView::OnMouseWheel(const ui::MouseWheelEvent& event) {
415 // Negative y_offset is a downward scroll.
416 if (event.y_offset() < 0) {
417 MaybeOpenCustomLauncherPage();
418 return true;
421 return false;
424 void StartPageView::OnGestureEvent(ui::GestureEvent* event) {
425 if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN &&
426 event->details().scroll_y_hint() < 0)
427 MaybeOpenCustomLauncherPage();
429 ContentsView* contents_view = app_list_main_view_->contents_view();
430 if (event->type() == ui::ET_GESTURE_TAP &&
431 contents_view->GetCustomPageCollapsedBounds().Contains(event->location()))
432 MaybeOpenCustomLauncherPage();
435 void StartPageView::OnScrollEvent(ui::ScrollEvent* event) {
436 // Negative y_offset is a downward scroll (or upward, if Australian Scrolling
437 // is enabled).
438 if (event->type() == ui::ET_SCROLL && event->y_offset() < 0)
439 MaybeOpenCustomLauncherPage();
442 gfx::Rect StartPageView::GetSearchBoxBounds() const {
443 return search_box_spacer_view_->bounds();
446 TileItemView* StartPageView::GetTileItemView(size_t index) {
447 return tiles_container_->GetTileItemView(index);
450 } // namespace app_list