| // 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/app_list_main_view.h" |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include "ash/app_list/app_list_view_delegate.h" |
| #include "ash/app_list/model/app_list_folder_item.h" |
| #include "ash/app_list/model/app_list_item.h" |
| #include "ash/app_list/model/app_list_model.h" |
| #include "ash/app_list/pagination_model.h" |
| #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/apps_container_view.h" |
| #include "ash/app_list/views/apps_grid_view.h" |
| #include "ash/app_list/views/contents_view.h" |
| #include "ash/app_list/views/search_box_view.h" |
| #include "ash/app_list/views/search_result_base_view.h" |
| #include "ash/app_list/views/search_result_page_view.h" |
| #include "ash/public/cpp/app_list/app_list_constants.h" |
| #include "ash/public/cpp/app_list/app_list_features.h" |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/files/file_path.h" |
| #include "base/macros.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/strings/string_util.h" |
| #include "chromeos/constants/chromeos_switches.h" |
| #include "ui/aura/window.h" |
| #include "ui/chromeos/search_box/search_box_view_base.h" |
| #include "ui/gfx/geometry/insets.h" |
| #include "ui/views/border.h" |
| #include "ui/views/controls/button/button.h" |
| #include "ui/views/controls/textfield/textfield.h" |
| #include "ui/views/layout/fill_layout.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/wm/public/activation_client.h" |
| |
| namespace app_list { |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // AppListMainView: |
| |
| AppListMainView::AppListMainView(AppListViewDelegate* delegate, |
| AppListView* app_list_view) |
| : delegate_(delegate), |
| model_(delegate->GetModel()), |
| search_model_(delegate->GetSearchModel()), |
| search_box_view_(nullptr), |
| contents_view_(nullptr), |
| app_list_view_(app_list_view) { |
| // We need a layer to apply transform to in small display so that the apps |
| // grid fits in the display. |
| SetPaintToLayer(); |
| layer()->SetFillsBoundsOpaquely(false); |
| |
| model_->AddObserver(this); |
| } |
| |
| AppListMainView::~AppListMainView() { |
| model_->RemoveObserver(this); |
| } |
| |
| void AppListMainView::Init(int initial_apps_page, |
| SearchBoxView* search_box_view) { |
| search_box_view_ = search_box_view; |
| AddContentsViews(); |
| |
| // Switch the apps grid view to the specified page. |
| app_list::PaginationModel* pagination_model = GetAppsPaginationModel(); |
| if (pagination_model->is_valid_page(initial_apps_page)) |
| pagination_model->SelectPage(initial_apps_page, false); |
| } |
| |
| void AppListMainView::AddContentsViews() { |
| DCHECK(search_box_view_); |
| contents_view_ = new ContentsView(app_list_view_); |
| contents_view_->Init(model_); |
| contents_view_->SetPaintToLayer(ui::LAYER_NOT_DRAWN); |
| contents_view_->layer()->SetMasksToBounds(true); |
| AddChildView(contents_view_); |
| |
| search_box_view_->set_contents_view(contents_view_); |
| |
| // Clear the old query and start search. |
| search_box_view_->ClearSearch(); |
| } |
| |
| void AppListMainView::ShowAppListWhenReady() { |
| // After switching to tablet mode, other app windows may be active. Show the |
| // app list without activating it to avoid breaking other windows' state. |
| const aura::Window* active_window = |
| wm::GetActivationClient( |
| app_list_view_->GetWidget()->GetNativeView()->GetRootWindow()) |
| ->GetActiveWindow(); |
| if (app_list_view_->is_tablet_mode() && active_window) |
| GetWidget()->ShowInactive(); |
| else |
| GetWidget()->Show(); |
| } |
| |
| void AppListMainView::ResetForShow() { |
| contents_view_->SetActiveState(ash::AppListState::kStateStart); |
| contents_view_->GetAppsContainerView()->ResetForShowApps(); |
| // We clear the search when hiding so when app list appears it is not showing |
| // search results. |
| search_box_view_->ClearSearch(); |
| } |
| |
| void AppListMainView::Close() { |
| contents_view_->CancelDrag(); |
| } |
| |
| void AppListMainView::ModelChanged() { |
| model_->RemoveObserver(this); |
| model_ = delegate_->GetModel(); |
| model_->AddObserver(this); |
| search_model_ = delegate_->GetSearchModel(); |
| search_box_view_->ModelChanged(); |
| delete contents_view_; |
| contents_view_ = nullptr; |
| AddContentsViews(); |
| Layout(); |
| } |
| |
| void AppListMainView::SetDragAndDropHostOfCurrentAppList( |
| ApplicationDragAndDropHost* drag_and_drop_host) { |
| contents_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host); |
| } |
| |
| PaginationModel* AppListMainView::GetAppsPaginationModel() { |
| return contents_view_->GetAppsContainerView() |
| ->apps_grid_view() |
| ->pagination_model(); |
| } |
| |
| void AppListMainView::NotifySearchBoxVisibilityChanged() { |
| // Repaint the AppListView's background which will repaint the background for |
| // the search box. This is needed because this view paints to a layer and |
| // won't propagate paints upward. |
| if (parent()) |
| parent()->SchedulePaint(); |
| } |
| |
| const char* AppListMainView::GetClassName() const { |
| return "AppListMainView"; |
| } |
| |
| void AppListMainView::Layout() { |
| gfx::Rect rect = GetContentsBounds(); |
| if (!rect.IsEmpty()) |
| contents_view_->SetBoundsRect(rect); |
| } |
| |
| void AppListMainView::ActivateApp(AppListItem* item, int event_flags) { |
| // TODO(jennyz): Activate the folder via AppListModel notification. |
| if (item->GetItemType() == AppListFolderItem::kItemType) { |
| contents_view_->ShowFolderContent(static_cast<AppListFolderItem*>(item)); |
| UMA_HISTOGRAM_ENUMERATION(kAppListFolderOpenedHistogram, |
| kFullscreenAppListFolders, kMaxFolderOpened); |
| } else { |
| base::RecordAction(base::UserMetricsAction("AppList_ClickOnApp")); |
| delegate_->ActivateItem(item->id(), event_flags); |
| UMA_HISTOGRAM_BOOLEAN(kAppListAppLaunchedFullscreen, |
| false /*not a suggested app*/); |
| } |
| } |
| |
| void AppListMainView::CancelDragInActiveFolder() { |
| contents_view_->GetAppsContainerView() |
| ->app_list_folder_view() |
| ->items_grid_view() |
| ->EndDrag(true); |
| } |
| |
| void AppListMainView::OnResultInstalled(SearchResult* result) { |
| // Clears the search to show the apps grid. The last installed app |
| // should be highlighted and made visible already. |
| search_box_view_->ClearSearch(); |
| } |
| |
| void AppListMainView::QueryChanged(search_box::SearchBoxViewBase* sender) { |
| base::string16 raw_query = search_model_->search_box()->text(); |
| base::string16 query; |
| base::TrimWhitespace(raw_query, base::TRIM_ALL, &query); |
| bool should_show_search = |
| app_list_features::IsZeroStateSuggestionsEnabled() |
| ? search_box_view_->is_search_box_active() || !query.empty() |
| : !query.empty(); |
| contents_view_->ShowSearchResults(should_show_search); |
| |
| delegate_->StartSearch(raw_query); |
| } |
| |
| void AppListMainView::ActiveChanged(search_box::SearchBoxViewBase* sender) { |
| if (!app_list_features::IsZeroStateSuggestionsEnabled()) |
| return; |
| |
| if (search_box_view_->is_search_box_active()) { |
| // Show zero state suggestions when search box is activated with an empty |
| // query. |
| base::string16 raw_query = search_model_->search_box()->text(); |
| base::string16 query; |
| base::TrimWhitespace(raw_query, base::TRIM_ALL, &query); |
| if (query.empty()) |
| search_box_view_->ShowZeroStateSuggestions(); |
| } else { |
| // Close the search results page if the search box is inactive. |
| contents_view_->ShowSearchResults(false); |
| } |
| } |
| |
| void AppListMainView::SearchBoxFocusChanged( |
| search_box::SearchBoxViewBase* sender) { |
| // A fake focus (highlight) is always set on the first search result. When the |
| // user moves focus from the search box textfield (e.g. to close button or |
| // last search result), the fake focus should be removed. |
| if (sender->search_box()->HasFocus()) |
| return; |
| |
| SearchResultBaseView* first_result_view = |
| contents_view_->search_results_page_view()->first_result_view(); |
| if (!first_result_view || !first_result_view->background_highlighted()) |
| return; |
| |
| first_result_view->SetBackgroundHighlighted(false); |
| } |
| |
| void AppListMainView::AssistantButtonPressed() { |
| DCHECK(chromeos::switches::IsAssistantEnabled()); |
| delegate_->StartAssistant(); |
| } |
| |
| void AppListMainView::BackButtonPressed() { |
| if (!contents_view_->Back()) |
| app_list_view_->Dismiss(); |
| } |
| |
| } // namespace app_list |