Files.app: Use core-icon for the dropdown button of combobutton.
[chromium-blink-merge.git] / ui / app_list / views / start_page_view.cc
blob343fcad263b0ca6a7679a7e7966bb9c84ab5d1f3
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 ~StartPageTilesContainer() override;
115 TileItemView* GetTileItemView(int index);
117 const std::vector<SearchResultTileItemView*>& tile_views() const {
118 return search_result_tile_views_;
121 AllAppsTileItemView* all_apps_button() { return all_apps_button_; }
123 // Overridden from SearchResultContainerView:
124 int Update() override;
125 void UpdateSelectedIndex(int old_selected, int new_selected) override;
126 void OnContainerSelected(bool from_bottom,
127 bool directional_movement) override;
129 private:
130 ContentsView* contents_view_;
132 std::vector<SearchResultTileItemView*> search_result_tile_views_;
133 AllAppsTileItemView* all_apps_button_;
135 DISALLOW_COPY_AND_ASSIGN(StartPageTilesContainer);
138 StartPageView::StartPageTilesContainer::StartPageTilesContainer(
139 ContentsView* contents_view,
140 AllAppsTileItemView* all_apps_button)
141 : contents_view_(contents_view), all_apps_button_(all_apps_button) {
142 views::BoxLayout* tiles_layout_manager =
143 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, kTileSpacing);
144 tiles_layout_manager->set_main_axis_alignment(
145 views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
146 SetLayoutManager(tiles_layout_manager);
147 set_background(
148 views::Background::CreateSolidBackground(kLabelBackgroundColor));
150 // Add SearchResultTileItemViews to the container.
151 for (size_t i = 0; i < kNumStartPageTiles; ++i) {
152 SearchResultTileItemView* tile_item = new SearchResultTileItemView(this);
153 AddChildView(tile_item);
154 tile_item->SetParentBackgroundColor(kLabelBackgroundColor);
155 search_result_tile_views_.push_back(tile_item);
158 // Also add a special "all apps" button to the end of the container.
159 all_apps_button_->UpdateIcon();
160 all_apps_button_->SetParentBackgroundColor(kLabelBackgroundColor);
161 AddChildView(all_apps_button_);
164 StartPageView::StartPageTilesContainer::~StartPageTilesContainer() {
167 TileItemView* StartPageView::StartPageTilesContainer::GetTileItemView(
168 int index) {
169 DCHECK_GT(num_results(), index);
170 if (index == num_results() - 1)
171 return all_apps_button_;
173 return search_result_tile_views_[index];
176 int StartPageView::StartPageTilesContainer::Update() {
177 // Ignore updates and disable buttons when transitioning to a different
178 // state.
179 if (contents_view_->GetActiveState() != AppListModel::STATE_START) {
180 for (auto* view : search_result_tile_views_)
181 view->SetEnabled(false);
183 return num_results();
186 std::vector<SearchResult*> display_results =
187 AppListModel::FilterSearchResultsByDisplayType(
188 results(), SearchResult::DISPLAY_RECOMMENDATION, kNumStartPageTiles);
190 // Update the tile item results.
191 for (size_t i = 0; i < search_result_tile_views_.size(); ++i) {
192 SearchResult* item = NULL;
193 if (i < display_results.size())
194 item = display_results[i];
195 search_result_tile_views_[i]->SetSearchResult(item);
196 search_result_tile_views_[i]->SetEnabled(true);
199 Layout();
200 parent()->Layout();
201 // Add 1 to the results size to account for the all apps button.
202 return display_results.size() + 1;
205 void StartPageView::StartPageTilesContainer::UpdateSelectedIndex(
206 int old_selected,
207 int new_selected) {
208 if (old_selected >= 0)
209 GetTileItemView(old_selected)->SetSelected(false);
211 if (new_selected >= 0)
212 GetTileItemView(new_selected)->SetSelected(true);
215 void StartPageView::StartPageTilesContainer::OnContainerSelected(
216 bool /*from_bottom*/,
217 bool /*directional_movement*/) {
220 ////////////////////////////////////////////////////////////////////////////////
221 // StartPageView implementation:
222 StartPageView::StartPageView(AppListMainView* app_list_main_view,
223 AppListViewDelegate* view_delegate)
224 : app_list_main_view_(app_list_main_view),
225 view_delegate_(view_delegate),
226 search_box_spacer_view_(new SearchBoxSpacerView(
227 app_list_main_view->search_box_view()->GetPreferredSize())),
228 instant_container_(new views::View),
229 custom_launcher_page_background_(new CustomLauncherPageBackgroundView(
230 view_delegate_->GetModel()->custom_launcher_page_name())),
231 tiles_container_(new StartPageTilesContainer(
232 app_list_main_view->contents_view(),
233 new AllAppsTileItemView(
234 app_list_main_view_->contents_view(),
235 view_delegate_->GetModel()->top_level_item_list()))) {
236 // The view containing the start page WebContents and SearchBoxSpacerView.
237 InitInstantContainer();
238 AddChildView(instant_container_);
240 // The view containing the start page tiles.
241 AddChildView(tiles_container_);
243 AddChildView(custom_launcher_page_background_);
245 tiles_container_->SetResults(view_delegate_->GetModel()->results());
246 Reset();
249 StartPageView::~StartPageView() {
252 void StartPageView::InitInstantContainer() {
253 views::BoxLayout* instant_layout_manager = new views::BoxLayout(
254 views::BoxLayout::kVertical, 0, 0, kInstantContainerSpacing);
255 instant_layout_manager->set_inside_border_insets(
256 gfx::Insets(0, 0, kSearchBoxAndTilesSpacing, 0));
257 instant_layout_manager->set_main_axis_alignment(
258 views::BoxLayout::MAIN_AXIS_ALIGNMENT_END);
259 instant_layout_manager->set_cross_axis_alignment(
260 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
261 instant_container_->SetLayoutManager(instant_layout_manager);
263 views::View* web_view = view_delegate_->CreateStartPageWebView(
264 gfx::Size(kWebViewWidth, kWebViewHeight));
265 if (web_view) {
266 web_view->SetFocusable(false);
267 instant_container_->AddChildView(web_view);
270 instant_container_->AddChildView(search_box_spacer_view_);
273 void StartPageView::MaybeOpenCustomLauncherPage() {
274 // Switch to the custom page.
275 ContentsView* contents_view = app_list_main_view_->contents_view();
276 if (!app_list_main_view_->ShouldShowCustomLauncherPage())
277 return;
279 UMA_HISTOGRAM_ENUMERATION(kPageOpenedHistogram,
280 AppListModel::STATE_CUSTOM_LAUNCHER_PAGE,
281 AppListModel::STATE_LAST);
283 contents_view->SetActiveState(AppListModel::STATE_CUSTOM_LAUNCHER_PAGE);
286 void StartPageView::Reset() {
287 tiles_container_->Update();
290 void StartPageView::UpdateForTesting() {
291 tiles_container_->Update();
294 const std::vector<SearchResultTileItemView*>& StartPageView::tile_views()
295 const {
296 return tiles_container_->tile_views();
299 TileItemView* StartPageView::all_apps_button() const {
300 return tiles_container_->all_apps_button();
303 void StartPageView::OnShown() {
304 // When the start page is shown, show or hide the custom launcher page
305 // based on whether it is enabled.
306 CustomLauncherPageView* custom_page_view =
307 app_list_main_view_->contents_view()->custom_page_view();
308 if (custom_page_view) {
309 custom_page_view->SetVisible(
310 app_list_main_view_->ShouldShowCustomLauncherPage());
312 tiles_container_->Update();
313 tiles_container_->ClearSelectedIndex();
314 custom_launcher_page_background_->SetSelected(false);
317 gfx::Rect StartPageView::GetPageBoundsForState(
318 AppListModel::State state) const {
319 gfx::Rect onscreen_bounds(GetFullContentsBounds());
320 if (state == AppListModel::STATE_START)
321 return onscreen_bounds;
323 return GetAboveContentsOffscreenBounds(onscreen_bounds.size());
326 gfx::Rect StartPageView::GetSearchBoxBounds() const {
327 return search_box_spacer_view_->bounds() +
328 GetPageBoundsForState(AppListModel::STATE_START).OffsetFromOrigin();
331 void StartPageView::Layout() {
332 gfx::Rect bounds(GetContentsBounds());
333 bounds.set_height(instant_container_->GetHeightForWidth(bounds.width()));
334 instant_container_->SetBoundsRect(bounds);
336 // Tiles begin where the instant container ends.
337 bounds.set_y(bounds.bottom());
338 bounds.set_height(tiles_container_->GetHeightForWidth(bounds.width()));
339 tiles_container_->SetBoundsRect(bounds);
341 CustomLauncherPageView* custom_launcher_page_view =
342 app_list_main_view_->contents_view()->custom_page_view();
343 if (!custom_launcher_page_view)
344 return;
346 bounds = app_list_main_view_->contents_view()
347 ->custom_page_view()
348 ->GetCollapsedLauncherPageBounds();
349 bounds.Intersect(GetContentsBounds());
350 bounds.ClampToCenteredSize(
351 gfx::Size(kLauncherPageBackgroundWidth, bounds.height()));
352 custom_launcher_page_background_->SetBoundsRect(bounds);
355 bool StartPageView::OnKeyPressed(const ui::KeyEvent& event) {
356 const int forward_dir = base::i18n::IsRTL() ? -1 : 1;
357 int selected_index = tiles_container_->selected_index();
359 if (custom_launcher_page_background_->selected()) {
360 selected_index = tiles_container_->num_results();
361 switch (event.key_code()) {
362 case ui::VKEY_RETURN:
363 MaybeOpenCustomLauncherPage();
364 return true;
365 default:
366 break;
368 } else if (selected_index >= 0 &&
369 tiles_container_->GetTileItemView(selected_index)
370 ->OnKeyPressed(event)) {
371 return true;
374 int dir = 0;
375 switch (event.key_code()) {
376 case ui::VKEY_LEFT:
377 dir = -forward_dir;
378 break;
379 case ui::VKEY_RIGHT:
380 // Don't go to the custom launcher page from the All apps tile.
381 if (selected_index != tiles_container_->num_results() - 1)
382 dir = forward_dir;
383 break;
384 case ui::VKEY_UP:
385 // Up selects the first tile if the custom launcher is selected.
386 if (custom_launcher_page_background_->selected()) {
387 selected_index = -1;
388 dir = 1;
390 break;
391 case ui::VKEY_DOWN:
392 // Down selects the first tile if nothing is selected.
393 dir = 1;
394 // If something is selected, select the custom launcher page.
395 if (tiles_container_->IsValidSelectionIndex(selected_index))
396 selected_index = tiles_container_->num_results() - 1;
397 break;
398 case ui::VKEY_TAB:
399 dir = event.IsShiftDown() ? -1 : 1;
400 break;
401 default:
402 break;
405 if (dir == 0)
406 return false;
408 if (selected_index == -1) {
409 custom_launcher_page_background_->SetSelected(false);
410 tiles_container_->SetSelectedIndex(
411 dir == -1 ? tiles_container_->num_results() - 1 : 0);
412 return true;
415 int selection_index = selected_index + dir;
416 if (tiles_container_->IsValidSelectionIndex(selection_index)) {
417 custom_launcher_page_background_->SetSelected(false);
418 tiles_container_->SetSelectedIndex(selection_index);
419 return true;
422 if (selection_index == tiles_container_->num_results() &&
423 app_list_main_view_->ShouldShowCustomLauncherPage()) {
424 custom_launcher_page_background_->SetSelected(true);
425 tiles_container_->ClearSelectedIndex();
426 return true;
429 if (event.key_code() == ui::VKEY_TAB && selection_index == -1)
430 tiles_container_->ClearSelectedIndex(); // ContentsView will handle focus.
432 return false;
435 bool StartPageView::OnMousePressed(const ui::MouseEvent& event) {
436 ContentsView* contents_view = app_list_main_view_->contents_view();
437 if (contents_view->custom_page_view() &&
438 !contents_view->custom_page_view()
439 ->GetCollapsedLauncherPageBounds()
440 .Contains(event.location())) {
441 return false;
444 MaybeOpenCustomLauncherPage();
445 return true;
448 bool StartPageView::OnMouseWheel(const ui::MouseWheelEvent& event) {
449 // Negative y_offset is a downward scroll.
450 if (event.y_offset() < 0) {
451 MaybeOpenCustomLauncherPage();
452 return true;
455 return false;
458 void StartPageView::OnGestureEvent(ui::GestureEvent* event) {
459 if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN &&
460 event->details().scroll_y_hint() < 0) {
461 MaybeOpenCustomLauncherPage();
464 ContentsView* contents_view = app_list_main_view_->contents_view();
465 if (event->type() == ui::ET_GESTURE_TAP &&
466 contents_view->custom_page_view() &&
467 contents_view->custom_page_view()
468 ->GetCollapsedLauncherPageBounds()
469 .Contains(event->location())) {
470 MaybeOpenCustomLauncherPage();
474 void StartPageView::OnScrollEvent(ui::ScrollEvent* event) {
475 // Negative y_offset is a downward scroll (or upward, if Australian Scrolling
476 // is enabled).
477 if (event->type() == ui::ET_SCROLL && event->y_offset() < 0)
478 MaybeOpenCustomLauncherPage();
481 TileItemView* StartPageView::GetTileItemView(size_t index) {
482 return tiles_container_->GetTileItemView(index);
485 } // namespace app_list