Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / app_list / views / start_page_view.cc
blob7b0dace01fced64ef677c64c501dca1217e9856d
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/custom_launcher_page_view.h"
22 #include "ui/app_list/views/search_box_view.h"
23 #include "ui/app_list/views/search_result_container_view.h"
24 #include "ui/app_list/views/search_result_tile_item_view.h"
25 #include "ui/app_list/views/tile_item_view.h"
26 #include "ui/gfx/canvas.h"
27 #include "ui/views/background.h"
28 #include "ui/views/controls/image_view.h"
29 #include "ui/views/controls/label.h"
30 #include "ui/views/controls/textfield/textfield.h"
31 #include "ui/views/layout/box_layout.h"
32 #include "ui/views/widget/widget.h"
34 namespace app_list {
36 namespace {
38 // Layout constants.
39 const int kInstantContainerSpacing = 24;
40 const int kSearchBoxAndTilesSpacing = 35;
41 const int kStartPageSearchBoxWidth = 480;
43 // WebView constants.
44 const int kWebViewWidth = 700;
45 const int kWebViewHeight = 244;
47 // Tile container constants.
48 const size_t kNumStartPageTiles = 4;
49 const int kTileSpacing = 7;
51 const int kLauncherPageBackgroundWidth = 400;
53 // An invisible placeholder view which fills the space for the search box view
54 // in a box layout. The search box view itself is a child of the AppListView
55 // (because it is visible on many different pages).
56 class SearchBoxSpacerView : public views::View {
57 public:
58 explicit SearchBoxSpacerView(const gfx::Size& search_box_size)
59 : size_(kStartPageSearchBoxWidth, search_box_size.height()) {}
61 ~SearchBoxSpacerView() override {}
63 // Overridden from views::View:
64 gfx::Size GetPreferredSize() const override { return size_; }
66 private:
67 gfx::Size size_;
69 DISALLOW_COPY_AND_ASSIGN(SearchBoxSpacerView);
72 } // namespace
74 class CustomLauncherPageBackgroundView : public views::View {
75 public:
76 explicit CustomLauncherPageBackgroundView(
77 const std::string& custom_launcher_page_name)
78 : selected_(false),
79 custom_launcher_page_name_(custom_launcher_page_name) {
80 set_background(views::Background::CreateSolidBackground(kSelectedColor));
82 ~CustomLauncherPageBackgroundView() override {}
84 void SetSelected(bool selected) {
85 selected_ = selected;
86 SetVisible(selected);
87 if (selected)
88 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
91 bool selected() { return selected_; }
93 // Overridden from views::View:
94 void GetAccessibleState(ui::AXViewState* state) override {
95 state->role = ui::AX_ROLE_BUTTON;
96 state->name = base::UTF8ToUTF16(custom_launcher_page_name_);
99 private:
100 bool selected_;
101 std::string custom_launcher_page_name_;
103 DISALLOW_COPY_AND_ASSIGN(CustomLauncherPageBackgroundView);
106 // A container that holds the start page recommendation tiles and the all apps
107 // tile.
108 class StartPageView::StartPageTilesContainer
109 : public SearchResultContainerView {
110 public:
111 StartPageTilesContainer(ContentsView* contents_view,
112 AllAppsTileItemView* all_apps_button,
113 AppListViewDelegate* view_delegate);
114 ~StartPageTilesContainer() override;
116 TileItemView* GetTileItemView(int index);
118 const std::vector<SearchResultTileItemView*>& tile_views() const {
119 return search_result_tile_views_;
122 AllAppsTileItemView* all_apps_button() { return all_apps_button_; }
124 // Overridden from SearchResultContainerView:
125 int Update() override;
126 void UpdateSelectedIndex(int old_selected, int new_selected) override;
127 void OnContainerSelected(bool from_bottom,
128 bool directional_movement) override;
129 void NotifyFirstResultYIndex(int y_index) override;
130 int GetYSize() override;
132 private:
133 ContentsView* contents_view_;
135 std::vector<SearchResultTileItemView*> search_result_tile_views_;
136 AllAppsTileItemView* all_apps_button_;
138 DISALLOW_COPY_AND_ASSIGN(StartPageTilesContainer);
141 StartPageView::StartPageTilesContainer::StartPageTilesContainer(
142 ContentsView* contents_view,
143 AllAppsTileItemView* all_apps_button,
144 AppListViewDelegate* view_delegate)
145 : contents_view_(contents_view), all_apps_button_(all_apps_button) {
146 views::BoxLayout* tiles_layout_manager =
147 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, kTileSpacing);
148 tiles_layout_manager->set_main_axis_alignment(
149 views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
150 SetLayoutManager(tiles_layout_manager);
151 set_background(
152 views::Background::CreateSolidBackground(kLabelBackgroundColor));
154 // Add SearchResultTileItemViews to the container.
155 for (size_t i = 0; i < kNumStartPageTiles; ++i) {
156 SearchResultTileItemView* tile_item =
157 new SearchResultTileItemView(this, view_delegate);
158 AddChildView(tile_item);
159 tile_item->SetParentBackgroundColor(kLabelBackgroundColor);
160 tile_item->SetHoverStyle(TileItemView::HOVER_STYLE_ANIMATE_SHADOW);
161 search_result_tile_views_.push_back(tile_item);
164 // Also add a special "all apps" button to the end of the container.
165 all_apps_button_->SetHoverStyle(TileItemView::HOVER_STYLE_ANIMATE_SHADOW);
166 all_apps_button_->UpdateIcon();
167 all_apps_button_->SetParentBackgroundColor(kLabelBackgroundColor);
168 AddChildView(all_apps_button_);
171 StartPageView::StartPageTilesContainer::~StartPageTilesContainer() {
174 TileItemView* StartPageView::StartPageTilesContainer::GetTileItemView(
175 int index) {
176 DCHECK_GT(num_results(), index);
177 if (index == num_results() - 1)
178 return all_apps_button_;
180 return search_result_tile_views_[index];
183 int StartPageView::StartPageTilesContainer::Update() {
184 // Ignore updates and disable buttons when transitioning to a different
185 // state.
186 if (contents_view_->GetActiveState() != AppListModel::STATE_START) {
187 for (auto* view : search_result_tile_views_)
188 view->SetEnabled(false);
190 return num_results();
193 std::vector<SearchResult*> display_results =
194 AppListModel::FilterSearchResultsByDisplayType(
195 results(), SearchResult::DISPLAY_RECOMMENDATION, kNumStartPageTiles);
197 // Update the tile item results.
198 for (size_t i = 0; i < search_result_tile_views_.size(); ++i) {
199 SearchResult* item = NULL;
200 if (i < display_results.size())
201 item = display_results[i];
202 search_result_tile_views_[i]->SetSearchResult(item);
203 search_result_tile_views_[i]->SetEnabled(true);
206 Layout();
207 parent()->Layout();
208 // Add 1 to the results size to account for the all apps button.
209 return display_results.size() + 1;
212 void StartPageView::StartPageTilesContainer::UpdateSelectedIndex(
213 int old_selected,
214 int new_selected) {
215 if (old_selected >= 0)
216 GetTileItemView(old_selected)->SetSelected(false);
218 if (new_selected >= 0)
219 GetTileItemView(new_selected)->SetSelected(true);
222 void StartPageView::StartPageTilesContainer::OnContainerSelected(
223 bool /*from_bottom*/,
224 bool /*directional_movement*/) {
225 NOTREACHED();
228 void StartPageView::StartPageTilesContainer::NotifyFirstResultYIndex(
229 int /*y_index*/) {
230 NOTREACHED();
233 int StartPageView::StartPageTilesContainer::GetYSize() {
234 NOTREACHED();
235 return 0;
238 ////////////////////////////////////////////////////////////////////////////////
239 // StartPageView implementation:
240 StartPageView::StartPageView(AppListMainView* app_list_main_view,
241 AppListViewDelegate* view_delegate)
242 : app_list_main_view_(app_list_main_view),
243 view_delegate_(view_delegate),
244 search_box_spacer_view_(new SearchBoxSpacerView(
245 app_list_main_view->search_box_view()->GetPreferredSize())),
246 instant_container_(new views::View),
247 custom_launcher_page_background_(new CustomLauncherPageBackgroundView(
248 view_delegate_->GetModel()->custom_launcher_page_name())),
249 tiles_container_(new StartPageTilesContainer(
250 app_list_main_view->contents_view(),
251 new AllAppsTileItemView(
252 app_list_main_view_->contents_view(),
253 view_delegate_->GetModel()->top_level_item_list()),
254 view_delegate)) {
255 // The view containing the start page WebContents and SearchBoxSpacerView.
256 InitInstantContainer();
257 AddChildView(instant_container_);
259 // The view containing the start page tiles.
260 AddChildView(tiles_container_);
262 AddChildView(custom_launcher_page_background_);
264 tiles_container_->SetResults(view_delegate_->GetModel()->results());
265 Reset();
268 StartPageView::~StartPageView() {
271 void StartPageView::InitInstantContainer() {
272 views::BoxLayout* instant_layout_manager = new views::BoxLayout(
273 views::BoxLayout::kVertical, 0, 0, kInstantContainerSpacing);
274 instant_layout_manager->set_inside_border_insets(
275 gfx::Insets(0, 0, kSearchBoxAndTilesSpacing, 0));
276 instant_layout_manager->set_main_axis_alignment(
277 views::BoxLayout::MAIN_AXIS_ALIGNMENT_END);
278 instant_layout_manager->set_cross_axis_alignment(
279 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
280 instant_container_->SetLayoutManager(instant_layout_manager);
282 views::View* web_view = view_delegate_->CreateStartPageWebView(
283 gfx::Size(kWebViewWidth, kWebViewHeight));
284 if (web_view) {
285 web_view->SetFocusable(false);
286 instant_container_->AddChildView(web_view);
289 instant_container_->AddChildView(search_box_spacer_view_);
292 void StartPageView::MaybeOpenCustomLauncherPage() {
293 // Switch to the custom page.
294 ContentsView* contents_view = app_list_main_view_->contents_view();
295 if (!app_list_main_view_->ShouldShowCustomLauncherPage())
296 return;
298 UMA_HISTOGRAM_ENUMERATION(kPageOpenedHistogram,
299 AppListModel::STATE_CUSTOM_LAUNCHER_PAGE,
300 AppListModel::STATE_LAST);
302 contents_view->SetActiveState(AppListModel::STATE_CUSTOM_LAUNCHER_PAGE);
305 void StartPageView::Reset() {
306 tiles_container_->Update();
309 void StartPageView::UpdateForTesting() {
310 tiles_container_->Update();
313 const std::vector<SearchResultTileItemView*>& StartPageView::tile_views()
314 const {
315 return tiles_container_->tile_views();
318 TileItemView* StartPageView::all_apps_button() const {
319 return tiles_container_->all_apps_button();
322 void StartPageView::OnShown() {
323 // When the start page is shown, show or hide the custom launcher page
324 // based on whether it is enabled.
325 CustomLauncherPageView* custom_page_view =
326 app_list_main_view_->contents_view()->custom_page_view();
327 if (custom_page_view) {
328 custom_page_view->SetVisible(
329 app_list_main_view_->ShouldShowCustomLauncherPage());
331 tiles_container_->Update();
332 tiles_container_->ClearSelectedIndex();
333 custom_launcher_page_background_->SetSelected(false);
336 gfx::Rect StartPageView::GetPageBoundsForState(
337 AppListModel::State state) const {
338 gfx::Rect onscreen_bounds(GetFullContentsBounds());
339 if (state == AppListModel::STATE_START)
340 return onscreen_bounds;
342 return GetAboveContentsOffscreenBounds(onscreen_bounds.size());
345 gfx::Rect StartPageView::GetSearchBoxBounds() const {
346 return search_box_spacer_view_->bounds() +
347 GetPageBoundsForState(AppListModel::STATE_START).OffsetFromOrigin();
350 void StartPageView::Layout() {
351 gfx::Rect bounds(GetContentsBounds());
352 bounds.set_height(instant_container_->GetHeightForWidth(bounds.width()));
353 instant_container_->SetBoundsRect(bounds);
355 // Tiles begin where the instant container ends.
356 bounds.set_y(bounds.bottom());
357 bounds.set_height(tiles_container_->GetHeightForWidth(bounds.width()));
358 tiles_container_->SetBoundsRect(bounds);
360 CustomLauncherPageView* custom_launcher_page_view =
361 app_list_main_view_->contents_view()->custom_page_view();
362 if (!custom_launcher_page_view)
363 return;
365 bounds = app_list_main_view_->contents_view()
366 ->custom_page_view()
367 ->GetCollapsedLauncherPageBounds();
368 bounds.Intersect(GetContentsBounds());
369 bounds.ClampToCenteredSize(
370 gfx::Size(kLauncherPageBackgroundWidth, bounds.height()));
371 custom_launcher_page_background_->SetBoundsRect(bounds);
374 bool StartPageView::OnKeyPressed(const ui::KeyEvent& event) {
375 const int forward_dir = base::i18n::IsRTL() ? -1 : 1;
376 int selected_index = tiles_container_->selected_index();
378 if (custom_launcher_page_background_->selected()) {
379 selected_index = tiles_container_->num_results();
380 switch (event.key_code()) {
381 case ui::VKEY_RETURN:
382 MaybeOpenCustomLauncherPage();
383 return true;
384 default:
385 break;
387 } else if (selected_index >= 0 &&
388 tiles_container_->GetTileItemView(selected_index)
389 ->OnKeyPressed(event)) {
390 return true;
393 int dir = 0;
394 switch (event.key_code()) {
395 case ui::VKEY_LEFT:
396 dir = -forward_dir;
397 break;
398 case ui::VKEY_RIGHT:
399 // Don't go to the custom launcher page from the All apps tile.
400 if (selected_index != tiles_container_->num_results() - 1)
401 dir = forward_dir;
402 break;
403 case ui::VKEY_UP:
404 // Up selects the first tile if the custom launcher is selected.
405 if (custom_launcher_page_background_->selected()) {
406 selected_index = -1;
407 dir = 1;
409 break;
410 case ui::VKEY_DOWN:
411 // Down selects the first tile if nothing is selected.
412 dir = 1;
413 // If something is selected, select the custom launcher page.
414 if (tiles_container_->IsValidSelectionIndex(selected_index))
415 selected_index = tiles_container_->num_results() - 1;
416 break;
417 case ui::VKEY_TAB:
418 dir = event.IsShiftDown() ? -1 : 1;
419 break;
420 default:
421 break;
424 if (dir == 0)
425 return false;
427 if (selected_index == -1) {
428 custom_launcher_page_background_->SetSelected(false);
429 tiles_container_->SetSelectedIndex(
430 dir == -1 ? tiles_container_->num_results() - 1 : 0);
431 return true;
434 int selection_index = selected_index + dir;
435 if (tiles_container_->IsValidSelectionIndex(selection_index)) {
436 custom_launcher_page_background_->SetSelected(false);
437 tiles_container_->SetSelectedIndex(selection_index);
438 return true;
441 if (selection_index == tiles_container_->num_results() &&
442 app_list_main_view_->ShouldShowCustomLauncherPage()) {
443 custom_launcher_page_background_->SetSelected(true);
444 tiles_container_->ClearSelectedIndex();
445 return true;
448 if (event.key_code() == ui::VKEY_TAB && selection_index == -1)
449 tiles_container_->ClearSelectedIndex(); // ContentsView will handle focus.
451 return false;
454 bool StartPageView::OnMousePressed(const ui::MouseEvent& event) {
455 ContentsView* contents_view = app_list_main_view_->contents_view();
456 if (contents_view->custom_page_view() &&
457 !contents_view->custom_page_view()
458 ->GetCollapsedLauncherPageBounds()
459 .Contains(event.location())) {
460 return false;
463 MaybeOpenCustomLauncherPage();
464 return true;
467 bool StartPageView::OnMouseWheel(const ui::MouseWheelEvent& event) {
468 // Negative y_offset is a downward scroll.
469 if (event.y_offset() < 0) {
470 MaybeOpenCustomLauncherPage();
471 return true;
474 return false;
477 void StartPageView::OnGestureEvent(ui::GestureEvent* event) {
478 if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN &&
479 event->details().scroll_y_hint() < 0) {
480 MaybeOpenCustomLauncherPage();
483 ContentsView* contents_view = app_list_main_view_->contents_view();
484 if (event->type() == ui::ET_GESTURE_TAP &&
485 contents_view->custom_page_view() &&
486 contents_view->custom_page_view()
487 ->GetCollapsedLauncherPageBounds()
488 .Contains(event->location())) {
489 MaybeOpenCustomLauncherPage();
493 void StartPageView::OnScrollEvent(ui::ScrollEvent* event) {
494 // Negative y_offset is a downward scroll (or upward, if Australian Scrolling
495 // is enabled).
496 if (event->type() == ui::ET_SCROLL && event->y_offset() < 0)
497 MaybeOpenCustomLauncherPage();
500 TileItemView* StartPageView::GetTileItemView(size_t index) {
501 return tiles_container_->GetTileItemView(index);
504 } // namespace app_list