| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/app_list/views/apps_container_view.h" |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "ash/app_list/views/app_list_folder_view.h" |
| #include "ash/app_list/views/app_list_item_view.h" |
| #include "ash/app_list/views/app_list_main_view.h" |
| #include "ash/app_list/views/app_list_view.h" |
| #include "ash/app_list/views/apps_grid_view.h" |
| #include "ash/app_list/views/contents_view.h" |
| #include "ash/app_list/views/folder_background_view.h" |
| #include "ash/app_list/views/horizontal_page_container.h" |
| #include "ash/app_list/views/page_switcher.h" |
| #include "ash/app_list/views/search_box_view.h" |
| #include "ash/app_list/views/suggestion_chip_container_view.h" |
| #include "ash/public/cpp/app_list/app_list_config.h" |
| #include "ash/public/cpp/app_list/app_list_features.h" |
| #include "ash/public/cpp/app_list/app_list_switches.h" |
| #include "base/command_line.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/chromeos/search_box/search_box_constants.h" |
| #include "ui/display/display.h" |
| #include "ui/display/screen.h" |
| #include "ui/events/event.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| #include "ui/strings/grit/ui_strings.h" |
| #include "ui/views/accessibility/view_accessibility.h" |
| #include "ui/views/controls/textfield/textfield.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Suggestion chip container top margin (from the search box view). |
| constexpr int kSuggestionChipContainerTopMarginForSmallScreens = 8; |
| |
| // The ratio of allowed bounds for apps grid view to its maximum margin. |
| constexpr int kAppsGridMarginRatio = 16; |
| constexpr int kAppsGridMarginRatioForSmallWidth = 12; |
| |
| // The width threshold under which kAppsGridMarginRatioForSmallWidth should be |
| // used to calculate apps grid horizontal margins. |
| constexpr int kAppsGridMarginSmallWidthThreshold = 600; |
| |
| // The minimum margin of apps grid view. |
| constexpr int kAppsGridMinimumMargin = 8; |
| |
| // The horizontal spacing between apps grid view and page switcher. |
| constexpr int kAppsGridPageSwitcherSpacing = 8; |
| |
| // The range of app list transition progress in which the suggestion chips' |
| // opacity changes from 0 to 1. |
| constexpr float kSuggestionChipOpacityStartProgress = 0.66; |
| constexpr float kSuggestionChipOpacityEndProgress = 1; |
| |
| // The app list transition progress value for fullscreen state. |
| constexpr float kAppListFullscreenProgressValue = 2.0; |
| |
| // Returns ideal horizontal padding for apps container with provided contents |
| // bounds. |
| int GetContainerHorizontalPaddingForBounds(const gfx::Rect& bounds) { |
| const int horizontal_margin_ratio = |
| (app_list_features::IsScalableAppListEnabled() && |
| bounds.width() <= kAppsGridMarginSmallWidthThreshold) |
| ? kAppsGridMarginRatioForSmallWidth |
| : kAppsGridMarginRatio; |
| return bounds.width() / horizontal_margin_ratio; |
| } |
| |
| } // namespace |
| |
| // static |
| int AppsContainerView::GetMinimumGridHorizontalMargin() { |
| // If ScalableAppList feature is enabled, there is no extra horizontal margin |
| // between grid view and the page switcher. |
| return kAppsGridPageSwitcherSpacing + |
| PageSwitcher::kPreferredButtonStripWidth + |
| (app_list_features::IsScalableAppListEnabled() |
| ? 0 |
| : kAppsGridMinimumMargin); |
| } |
| |
| AppsContainerView::AppsContainerView(ContentsView* contents_view, |
| AppListModel* model) |
| : contents_view_(contents_view) { |
| SetPaintToLayer(ui::LAYER_NOT_DRAWN); |
| |
| suggestion_chip_container_view_ = |
| new SuggestionChipContainerView(contents_view); |
| AddChildView(suggestion_chip_container_view_); |
| |
| apps_grid_view_ = new AppsGridView(contents_view_, nullptr); |
| AddChildView(apps_grid_view_); |
| |
| // Page switcher should be initialized after AppsGridView. |
| page_switcher_ = |
| new PageSwitcher(apps_grid_view_->pagination_model(), true /* vertical */, |
| contents_view_->app_list_view()->is_tablet_mode()); |
| AddChildView(page_switcher_); |
| |
| app_list_folder_view_ = new AppListFolderView(this, model, contents_view_); |
| // The folder view is initially hidden. |
| app_list_folder_view_->SetVisible(false); |
| folder_background_view_ = new FolderBackgroundView(app_list_folder_view_); |
| AddChildView(folder_background_view_); |
| AddChildView(app_list_folder_view_); |
| |
| apps_grid_view_->SetModel(model); |
| apps_grid_view_->SetItemList(model->top_level_item_list()); |
| SetShowState(SHOW_APPS, false); |
| } |
| |
| AppsContainerView::~AppsContainerView() { |
| // Make sure |page_switcher_| is deleted before |apps_grid_view_| because |
| // |page_switcher_| uses the PaginationModel owned by |apps_grid_view_|. |
| delete page_switcher_; |
| } |
| |
| void AppsContainerView::ShowActiveFolder(AppListFolderItem* folder_item) { |
| // Prevent new animations from starting if there are currently animations |
| // pending. This fixes crbug.com/357099. |
| if (app_list_folder_view_->IsAnimationRunning()) |
| return; |
| |
| app_list_folder_view_->SetAppListFolderItem(folder_item); |
| |
| SetShowState(SHOW_ACTIVE_FOLDER, false); |
| |
| // If there is no selected view in the root grid when a folder is opened, |
| // silently focus the first item in the folder to avoid showing the selection |
| // highlight or announcing to A11y, but still ensuring the arrow keys navigate |
| // from the first item. |
| AppListItemView* first_item_view_in_folder_grid = |
| app_list_folder_view_->items_grid_view()->view_model()->view_at(0); |
| if (!apps_grid_view()->has_selected_view()) { |
| first_item_view_in_folder_grid->SilentlyRequestFocus(); |
| } else { |
| first_item_view_in_folder_grid->RequestFocus(); |
| } |
| // Disable all the items behind the folder so that they will not be reached |
| // during focus traversal. |
| |
| DisableFocusForShowingActiveFolder(true); |
| } |
| |
| void AppsContainerView::ShowApps(AppListFolderItem* folder_item) { |
| if (app_list_folder_view_->IsAnimationRunning()) |
| return; |
| |
| SetShowState(SHOW_APPS, folder_item ? true : false); |
| DisableFocusForShowingActiveFolder(false); |
| } |
| |
| void AppsContainerView::ResetForShowApps() { |
| UpdateSuggestionChips(); |
| SetShowState(SHOW_APPS, false); |
| DisableFocusForShowingActiveFolder(false); |
| } |
| |
| void AppsContainerView::SetDragAndDropHostOfCurrentAppList( |
| ApplicationDragAndDropHost* drag_and_drop_host) { |
| apps_grid_view()->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host); |
| app_list_folder_view()->items_grid_view()->SetDragAndDropHostOfCurrentAppList( |
| drag_and_drop_host); |
| } |
| |
| void AppsContainerView::ReparentFolderItemTransit( |
| AppListFolderItem* folder_item) { |
| if (app_list_folder_view_->IsAnimationRunning()) |
| return; |
| SetShowState(SHOW_ITEM_REPARENT, false); |
| DisableFocusForShowingActiveFolder(false); |
| } |
| |
| bool AppsContainerView::IsInFolderView() const { |
| return show_state_ == SHOW_ACTIVE_FOLDER; |
| } |
| |
| void AppsContainerView::ReparentDragEnded() { |
| DCHECK_EQ(SHOW_ITEM_REPARENT, show_state_); |
| show_state_ = AppsContainerView::SHOW_APPS; |
| } |
| |
| void AppsContainerView::UpdateControlVisibility( |
| ash::AppListViewState app_list_state, |
| bool is_in_drag) { |
| if (app_list_state == ash::AppListViewState::kClosed) |
| return; |
| |
| set_can_process_events_within_subtree( |
| app_list_state == ash::AppListViewState::kFullscreenAllApps || |
| app_list_state == ash::AppListViewState::kPeeking); |
| |
| apps_grid_view_->UpdateControlVisibility(app_list_state, is_in_drag); |
| page_switcher_->SetVisible( |
| is_in_drag || |
| app_list_state == ash::AppListViewState::kFullscreenAllApps || |
| (app_list_features::IsScalableAppListEnabled() && |
| app_list_state == ash::AppListViewState::kFullscreenSearch)); |
| |
| // Ignore button press during dragging to avoid app list item views' opacity |
| // being set to wrong value. |
| page_switcher_->set_ignore_button_press(is_in_drag); |
| |
| suggestion_chip_container_view_->SetVisible( |
| app_list_state == ash::AppListViewState::kFullscreenAllApps || |
| app_list_state == ash::AppListViewState::kPeeking || is_in_drag); |
| } |
| |
| void AppsContainerView::UpdateYPositionAndOpacity(float progress, |
| bool restore_opacity) { |
| apps_grid_view_->UpdateOpacity(restore_opacity); |
| |
| // Updates the opacity of page switcher buttons. The same rule as all apps in |
| // AppsGridView. |
| AppListView* app_list_view = contents_view_->app_list_view(); |
| int screen_bottom = app_list_view->GetScreenBottom(); |
| gfx::Rect switcher_bounds = page_switcher_->GetBoundsInScreen(); |
| float centerline_above_work_area = |
| std::max<float>(screen_bottom - switcher_bounds.CenterPoint().y(), 0.f); |
| const float start_px = AppListConfig::instance().all_apps_opacity_start_px(); |
| float opacity = std::min( |
| std::max( |
| (centerline_above_work_area - start_px) / |
| (AppListConfig::instance().all_apps_opacity_end_px() - start_px), |
| 0.f), |
| 1.0f); |
| page_switcher_->layer()->SetOpacity(restore_opacity ? 1.0f : opacity); |
| |
| // Changes the opacity of suggestion chips between 0 and 1 when app list |
| // transition progress changes between |kSuggestionChipOpacityStartProgress| |
| // and |kSuggestionChipOpacityEndProgress|. |
| float chips_opacity = |
| std::min(std::max((progress - kSuggestionChipOpacityStartProgress) / |
| (kSuggestionChipOpacityEndProgress - |
| kSuggestionChipOpacityStartProgress), |
| 0.f), |
| 1.0f); |
| suggestion_chip_container_view_->layer()->SetOpacity( |
| restore_opacity ? 1.0 : chips_opacity); |
| |
| suggestion_chip_container_view_->SetY(GetExpectedSuggestionChipY(progress)); |
| |
| apps_grid_view_->SetY(suggestion_chip_container_view_->y() + |
| chip_grid_y_distance_); |
| page_switcher_->SetY(suggestion_chip_container_view_->y() + |
| chip_grid_y_distance_); |
| } |
| |
| void AppsContainerView::OnTabletModeChanged(bool started) { |
| suggestion_chip_container_view_->OnTabletModeChanged(started); |
| apps_grid_view_->OnTabletModeChanged(started); |
| app_list_folder_view_->OnTabletModeChanged(started); |
| page_switcher_->set_is_tablet_mode(started); |
| } |
| |
| void AppsContainerView::Layout() { |
| gfx::Rect rect(GetContentsBounds()); |
| if (rect.IsEmpty()) |
| return; |
| |
| switch (show_state_) { |
| case SHOW_APPS: { |
| // Layout suggestion chips. |
| gfx::Rect chip_container_rect = rect; |
| chip_container_rect.set_y(GetExpectedSuggestionChipY( |
| contents_view_->app_list_view()->GetAppListTransitionProgress( |
| AppListView::kProgressFlagNone))); |
| chip_container_rect.set_height( |
| GetAppListConfig().suggestion_chip_container_height()); |
| if (app_list_features::IsScalableAppListEnabled()) { |
| chip_container_rect.Inset(GetContainerHorizontalPaddingForBounds(rect), |
| 0); |
| } |
| suggestion_chip_container_view_->SetBoundsRect(chip_container_rect); |
| |
| // Leave the same available bounds for the apps grid view in both |
| // fullscreen and peeking state to avoid resizing the view during |
| // animation and dragging, which is an expensive operation. |
| rect.set_y(chip_container_rect.bottom()); |
| rect.set_height( |
| rect.height() - |
| GetExpectedSuggestionChipY(kAppListFullscreenProgressValue) - |
| chip_container_rect.height()); |
| |
| const int page_switcher_width = |
| page_switcher_->GetPreferredSize().width(); |
| // With scalable app list feature enabled, the margins are calculated from |
| // the edge of the apps container, instead of container bounds inset by |
| // page switcher area. |
| if (!app_list_features::IsScalableAppListEnabled()) |
| rect.Inset(kAppsGridPageSwitcherSpacing + page_switcher_width, 0); |
| |
| const GridLayout grid_layout = CalculateGridLayout(); |
| apps_grid_view_->SetLayout(grid_layout.columns, grid_layout.rows); |
| |
| // Layout apps grid. |
| gfx::Rect grid_rect = rect; |
| |
| if (app_list_features::IsScalableAppListEnabled()) { |
| const gfx::Insets grid_insets = apps_grid_view_->GetInsets(); |
| const gfx::Insets margins = CalculateMarginsForAvailableBounds( |
| GetContentsBounds(), |
| contents_view_->GetSearchBoxSize(ash::AppListState::kStateApps), |
| true /*for_full_container_bounds*/); |
| grid_rect.Inset( |
| margins.left(), |
| GetAppListConfig().grid_fadeout_zone_height() - grid_insets.top(), |
| margins.right(), margins.bottom()); |
| // The grid rect insets are added to calculated margins. Given that the |
| // grid bounds rect should include insets, they have to be removed from |
| // added margins. |
| grid_rect.Inset(-grid_insets.left(), 0, -grid_insets.right(), |
| -grid_insets.bottom()); |
| } else { |
| grid_rect.Inset(CalculateMarginsForAvailableBounds( |
| rect, gfx::Size(), false /*for_full_container_bounds*/)); |
| // The grid rect insets are added to calculated margins. Given that the |
| // grid bounds rect should include insets, they have to be removed from |
| // the added margins. |
| grid_rect.Inset(-apps_grid_view_->GetInsets()); |
| } |
| |
| apps_grid_view_->SetBoundsRect(grid_rect); |
| |
| // Record the distance of y position between suggestion chip container |
| // and apps grid view to avoid duplicate calculation of apps grid view's |
| // y position during dragging. |
| chip_grid_y_distance_ = |
| apps_grid_view_->y() - suggestion_chip_container_view_->y(); |
| |
| // Layout page switcher. |
| page_switcher_->SetBoundsRect( |
| gfx::Rect(grid_rect.right() + kAppsGridPageSwitcherSpacing, |
| grid_rect.y(), page_switcher_width, grid_rect.height())); |
| break; |
| } |
| case SHOW_ACTIVE_FOLDER: { |
| folder_background_view_->SetBoundsRect(rect); |
| app_list_folder_view_->SetBoundsRect( |
| app_list_folder_view_->preferred_bounds()); |
| break; |
| } |
| case SHOW_ITEM_REPARENT: |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| bool AppsContainerView::OnKeyPressed(const ui::KeyEvent& event) { |
| if (show_state_ == SHOW_APPS) |
| return apps_grid_view_->OnKeyPressed(event); |
| else |
| return app_list_folder_view_->OnKeyPressed(event); |
| } |
| |
| const char* AppsContainerView::GetClassName() const { |
| return "AppsContainerView"; |
| } |
| |
| void AppsContainerView::OnGestureEvent(ui::GestureEvent* event) { |
| // Ignore tap/long-press, allow those to pass to the ancestor view. |
| if (event->type() == ui::ET_GESTURE_TAP || |
| event->type() == ui::ET_GESTURE_LONG_PRESS) { |
| return; |
| } |
| |
| // Will forward events to |apps_grid_view_| if they occur in the same y-region |
| if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN && |
| event->location().y() <= apps_grid_view_->bounds().y()) { |
| return; |
| } |
| |
| // If a folder is currently opening or closing, we should ignore the event. |
| // This is here until the animation for pagination while closing folders is |
| // fixed: https://crbug.com/875133 |
| if (app_list_folder_view_->IsAnimationRunning()) { |
| event->SetHandled(); |
| return; |
| } |
| |
| // Temporary event for use by |apps_grid_view_| |
| ui::GestureEvent grid_event(*event); |
| ConvertEventToTarget(apps_grid_view_, &grid_event); |
| apps_grid_view_->OnGestureEvent(&grid_event); |
| |
| // If the temporary event was handled, we don't want to handle it again. |
| if (grid_event.handled()) |
| event->SetHandled(); |
| } |
| |
| void AppsContainerView::OnWillBeHidden() { |
| if (show_state_ == SHOW_APPS || show_state_ == SHOW_ITEM_REPARENT) |
| apps_grid_view_->EndDrag(true); |
| else if (show_state_ == SHOW_ACTIVE_FOLDER) |
| app_list_folder_view_->CloseFolderPage(); |
| } |
| |
| views::View* AppsContainerView::GetFirstFocusableView() { |
| if (IsInFolderView()) { |
| // The pagination inside a folder is set horizontally, so focus should be |
| // set on the first item view in the selected page when it is moved down |
| // from the search box. |
| return app_list_folder_view_->items_grid_view() |
| ->GetCurrentPageFirstItemViewInFolder(); |
| } |
| return GetFocusManager()->GetNextFocusableView( |
| this, GetWidget(), false /* reverse */, false /* dont_loop */); |
| } |
| |
| gfx::Rect AppsContainerView::GetPageBoundsForState( |
| ash::AppListState state) const { |
| return contents_view_->GetContentsBounds(); |
| } |
| |
| const gfx::Insets& AppsContainerView::CalculateMarginsForAvailableBounds( |
| const gfx::Rect& available_bounds, |
| const gfx::Size& search_box_size, |
| bool for_full_container_bounds) { |
| DCHECK_EQ(for_full_container_bounds, |
| app_list_features::IsScalableAppListEnabled()); |
| |
| if (cached_container_margins_.bounds_size == available_bounds.size() && |
| cached_container_margins_.search_box_size == search_box_size) { |
| return cached_container_margins_.margins; |
| } |
| |
| const GridLayout grid_layout = CalculateGridLayout(); |
| const gfx::Size min_grid_size = apps_grid_view()->GetMinimumTileGridSize( |
| grid_layout.columns, grid_layout.rows); |
| const gfx::Size max_grid_size = apps_grid_view()->GetMaximumTileGridSize( |
| grid_layout.columns, grid_layout.rows); |
| |
| int available_height = available_bounds.height(); |
| // If calculating the bounds for the full apps container (rather than apps |
| // grid only), add search box, and suggestion chips container height (with |
| // its margins to search box and apps grid) to non apps grid size. |
| // NOTE: Not removing bottom apps grid inset (or top inset when |
| // |for_full_container_bounds| is false) because they are included into the |
| // total margin values. |
| if (for_full_container_bounds) { |
| available_height -= |
| search_box_size.height() + |
| GetAppListConfig().grid_fadeout_zone_height() + |
| GetAppListConfig().suggestion_chip_container_height() + |
| GetAppListConfig().suggestion_chip_container_top_margin(); |
| } |
| |
| // Calculates margin value to ensure the apps grid size is within required |
| // bounds. |
| // |ideal_margin|: The value the margin would have with no restrictions on |
| // grid size. |
| // |available_size|: The available size for apps grid in the dimension where |
| // margin is applied. |
| // |min_size|: The min allowed size for apps grid in the dimension where |
| // margin is applied. |
| // |max_size|: The max allowed size for apps grid in the dimension where |
| // margin is applied. |
| const auto calculate_margin = [](int ideal_margin, int available_size, |
| int min_size, int max_size) -> int { |
| const int ideal_size = available_size - 2 * ideal_margin; |
| if (ideal_size < min_size) |
| return ideal_margin - (min_size - ideal_size + 1) / 2; |
| if (ideal_size > max_size) |
| return ideal_margin + (ideal_size - max_size) / 2; |
| return ideal_margin; |
| }; |
| |
| const int ideal_vertical_margin = |
| available_bounds.height() / kAppsGridMarginRatio; |
| const int vertical_margin = |
| calculate_margin(ideal_vertical_margin, available_height, |
| min_grid_size.height(), max_grid_size.height()); |
| |
| const int ideal_horizontal_margin = |
| GetContainerHorizontalPaddingForBounds(available_bounds); |
| const int horizontal_margin = |
| calculate_margin(ideal_horizontal_margin, available_bounds.width(), |
| min_grid_size.width(), max_grid_size.width()); |
| |
| const int min_horizontal_margin = |
| app_list_features::IsScalableAppListEnabled() |
| ? kAppsGridPageSwitcherSpacing + |
| page_switcher_->GetPreferredSize().width() |
| : kAppsGridMinimumMargin; |
| |
| cached_container_margins_.margins = gfx::Insets( |
| std::max(vertical_margin, GetAppListConfig().grid_fadeout_zone_height()), |
| std::max(horizontal_margin, min_horizontal_margin), |
| std::max(vertical_margin, GetAppListConfig().grid_fadeout_zone_height()), |
| std::max(horizontal_margin, min_horizontal_margin)); |
| cached_container_margins_.bounds_size = available_bounds.size(); |
| cached_container_margins_.search_box_size = search_box_size; |
| |
| return cached_container_margins_.margins; |
| } |
| |
| void AppsContainerView::UpdateSuggestionChips() { |
| suggestion_chip_container_view_->SetResults( |
| contents_view_->GetAppListMainView() |
| ->view_delegate() |
| ->GetSearchModel() |
| ->results()); |
| } |
| |
| const AppListConfig& AppsContainerView::GetAppListConfig() const { |
| return contents_view_->app_list_view()->GetAppListConfig(); |
| } |
| |
| void AppsContainerView::SetShowState(ShowState show_state, |
| bool show_apps_with_animation) { |
| if (show_state_ == show_state) |
| return; |
| |
| show_state_ = show_state; |
| |
| // Layout before showing animation because the animation's target bounds are |
| // calculated based on the layout. |
| Layout(); |
| |
| switch (show_state_) { |
| case SHOW_APPS: |
| page_switcher_->set_can_process_events_within_subtree(true); |
| folder_background_view_->SetVisible(false); |
| apps_grid_view_->ResetForShowApps(); |
| if (show_apps_with_animation) |
| app_list_folder_view_->ScheduleShowHideAnimation(false, false); |
| else |
| app_list_folder_view_->HideViewImmediately(); |
| break; |
| case SHOW_ACTIVE_FOLDER: |
| page_switcher_->set_can_process_events_within_subtree(false); |
| folder_background_view_->SetVisible(true); |
| app_list_folder_view_->ScheduleShowHideAnimation(true, false); |
| break; |
| case SHOW_ITEM_REPARENT: |
| page_switcher_->set_can_process_events_within_subtree(true); |
| folder_background_view_->SetVisible(false); |
| app_list_folder_view_->ScheduleShowHideAnimation(false, true); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void AppsContainerView::DisableFocusForShowingActiveFolder(bool disabled) { |
| suggestion_chip_container_view_->DisableFocusForShowingActiveFolder(disabled); |
| apps_grid_view_->DisableFocusForShowingActiveFolder(disabled); |
| |
| // Ignore the page switcher in accessibility tree so that buttons inside it |
| // will not be accessed by ChromeVox. |
| page_switcher_->GetViewAccessibility().OverrideIsIgnored(disabled); |
| page_switcher_->GetViewAccessibility().NotifyAccessibilityEvent( |
| ax::mojom::Event::kTreeChanged); |
| } |
| |
| int AppsContainerView::GetSuggestionChipContainerTopMargin( |
| float progress) const { |
| // For small screen sizes in fullscreen state, reduce the margin between the |
| // search box and suggestion chips to reclaim as much of the vertical space as |
| // possible. |
| if (GetContentsBounds().height() < kAppsGridMarginSmallWidthThreshold && |
| !app_list_features::IsScalableAppListEnabled() && progress > 1.0) { |
| return gfx::Tween::IntValueBetween( |
| progress - 1, GetAppListConfig().suggestion_chip_container_top_margin(), |
| kSuggestionChipContainerTopMarginForSmallScreens); |
| } |
| return GetAppListConfig().suggestion_chip_container_top_margin(); |
| } |
| |
| int AppsContainerView::GetExpectedSuggestionChipY(float progress) { |
| const gfx::Rect search_box_bounds = |
| contents_view_->GetSearchBoxExpectedBoundsForProgress( |
| ash::AppListState::kStateApps, progress); |
| return search_box_bounds.bottom() + |
| GetSuggestionChipContainerTopMargin(progress); |
| } |
| |
| AppsContainerView::GridLayout AppsContainerView::CalculateGridLayout() const { |
| // Adapt columns and rows based on the work area size. |
| const gfx::Size size = |
| display::Screen::GetScreen() |
| ->GetDisplayNearestView(GetWidget()->GetNativeView()) |
| .work_area() |
| .size(); |
| |
| GridLayout result; |
| const AppListConfig& config = GetAppListConfig(); |
| // Switch columns and rows for portrait mode. |
| if (size.width() < size.height()) { |
| result.columns = config.preferred_rows(); |
| result.rows = config.preferred_cols(); |
| } else { |
| result.columns = config.preferred_cols(); |
| result.rows = config.preferred_rows(); |
| } |
| return result; |
| } |
| |
| } // namespace ash |