Workaround for xkbcommon dead keys.
[chromium-blink-merge.git] / ui / app_list / views / app_list_main_view.cc
blob2708b38148df7267214c7e9f3d6e5c67b78974bb
1 // Copyright 2013 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/app_list_main_view.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/files/file_path.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/profiler/scoped_tracker.h"
14 #include "base/strings/string_util.h"
15 #include "ui/app_list/app_list_constants.h"
16 #include "ui/app_list/app_list_folder_item.h"
17 #include "ui/app_list/app_list_item.h"
18 #include "ui/app_list/app_list_model.h"
19 #include "ui/app_list/app_list_switches.h"
20 #include "ui/app_list/app_list_view_delegate.h"
21 #include "ui/app_list/pagination_model.h"
22 #include "ui/app_list/search_box_model.h"
23 #include "ui/app_list/views/app_list_folder_view.h"
24 #include "ui/app_list/views/app_list_item_view.h"
25 #include "ui/app_list/views/apps_container_view.h"
26 #include "ui/app_list/views/apps_grid_view.h"
27 #include "ui/app_list/views/contents_view.h"
28 #include "ui/app_list/views/search_box_view.h"
29 #include "ui/app_list/views/start_page_view.h"
30 #include "ui/views/border.h"
31 #include "ui/views/controls/button/button.h"
32 #include "ui/views/controls/button/custom_button.h"
33 #include "ui/views/controls/textfield/textfield.h"
34 #include "ui/views/layout/box_layout.h"
35 #include "ui/views/layout/fill_layout.h"
36 #include "ui/views/widget/widget.h"
38 namespace app_list {
40 namespace {
42 // The maximum allowed time to wait for icon loading in milliseconds.
43 const int kMaxIconLoadingWaitTimeInMs = 50;
45 // Button for the custom page click zone. Receives click events when the user
46 // clicks on the custom page, and in response, switches to the custom page.
47 class CustomPageButton : public views::CustomButton,
48 public views::ButtonListener {
49 public:
50 explicit CustomPageButton(AppListMainView* app_list_main_view);
52 // ButtonListener overrides:
53 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
55 private:
56 AppListMainView* app_list_main_view_;
58 DISALLOW_COPY_AND_ASSIGN(CustomPageButton);
61 CustomPageButton::CustomPageButton(AppListMainView* app_list_main_view)
62 : views::CustomButton(this), app_list_main_view_(app_list_main_view) {
65 void CustomPageButton::ButtonPressed(views::Button* sender,
66 const ui::Event& event) {
67 // Switch to the custom page.
68 ContentsView* contents_view = app_list_main_view_->contents_view();
69 int custom_page_index = contents_view->GetPageIndexForState(
70 AppListModel::STATE_CUSTOM_LAUNCHER_PAGE);
71 contents_view->SetActivePage(custom_page_index);
74 } // namespace
76 ////////////////////////////////////////////////////////////////////////////////
77 // AppListMainView::IconLoader
79 class AppListMainView::IconLoader : public AppListItemObserver {
80 public:
81 IconLoader(AppListMainView* owner,
82 AppListItem* item,
83 float scale)
84 : owner_(owner),
85 item_(item) {
86 item_->AddObserver(this);
88 // Triggers icon loading for given |scale_factor|.
89 item_->icon().GetRepresentation(scale);
92 ~IconLoader() override { item_->RemoveObserver(this); }
94 private:
95 // AppListItemObserver overrides:
96 void ItemIconChanged() override {
97 owner_->OnItemIconLoaded(this);
98 // Note that IconLoader is released here.
101 AppListMainView* owner_;
102 AppListItem* item_;
104 DISALLOW_COPY_AND_ASSIGN(IconLoader);
107 ////////////////////////////////////////////////////////////////////////////////
108 // AppListMainView:
110 AppListMainView::AppListMainView(AppListViewDelegate* delegate)
111 : delegate_(delegate),
112 model_(delegate->GetModel()),
113 search_box_view_(nullptr),
114 contents_view_(nullptr),
115 custom_page_clickzone_(nullptr),
116 weak_ptr_factory_(this) {
117 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
118 model_->AddObserver(this);
121 AppListMainView::~AppListMainView() {
122 pending_icon_loaders_.clear();
123 model_->RemoveObserver(this);
126 void AppListMainView::Init(gfx::NativeView parent,
127 int initial_apps_page,
128 SearchBoxView* search_box_view) {
129 search_box_view_ = search_box_view;
130 AddContentsViews();
132 // Switch the apps grid view to the specified page.
133 app_list::PaginationModel* pagination_model = GetAppsPaginationModel();
134 if (pagination_model->is_valid_page(initial_apps_page))
135 pagination_model->SelectPage(initial_apps_page, false);
137 // Starts icon loading early.
138 PreloadIcons(parent);
141 void AppListMainView::AddContentsViews() {
142 DCHECK(search_box_view_);
144 contents_view_ = new ContentsView(this);
145 contents_view_->Init(model_);
146 AddChildView(contents_view_);
148 search_box_view_->set_contents_view(contents_view_);
150 contents_view_->SetPaintToLayer(true);
151 contents_view_->SetFillsBoundsOpaquely(false);
152 contents_view_->layer()->SetMasksToBounds(true);
154 delegate_->StartSearch();
157 void AppListMainView::ShowAppListWhenReady() {
158 if (pending_icon_loaders_.empty()) {
159 icon_loading_wait_timer_.Stop();
160 GetWidget()->Show();
161 return;
164 if (icon_loading_wait_timer_.IsRunning())
165 return;
167 icon_loading_wait_timer_.Start(
168 FROM_HERE,
169 base::TimeDelta::FromMilliseconds(kMaxIconLoadingWaitTimeInMs),
170 this, &AppListMainView::OnIconLoadingWaitTimer);
173 void AppListMainView::ResetForShow() {
174 if (switches::IsExperimentalAppListEnabled()) {
175 contents_view_->SetActivePage(
176 contents_view_->GetPageIndexForState(AppListModel::STATE_START));
178 contents_view_->apps_container_view()->ResetForShowApps();
179 // We clear the search when hiding so when app list appears it is not showing
180 // search results.
181 search_box_view_->ClearSearch();
184 void AppListMainView::Close() {
185 icon_loading_wait_timer_.Stop();
186 contents_view_->CancelDrag();
189 void AppListMainView::Prerender() {
190 contents_view_->Prerender();
193 void AppListMainView::ModelChanged() {
194 pending_icon_loaders_.clear();
195 model_->RemoveObserver(this);
196 model_ = delegate_->GetModel();
197 model_->AddObserver(this);
198 search_box_view_->ModelChanged();
199 delete contents_view_;
200 contents_view_ = NULL;
201 AddContentsViews();
202 Layout();
205 views::Widget* AppListMainView::GetCustomPageClickzone() const {
206 // During shutdown, the widgets may be deleted, which means
207 // |custom_page_clickzone_| will be a dangling pointer. Therefore, always
208 // check that the main app list widget (its parent) is still alive before
209 // returning the pointer.
210 if (!GetWidget())
211 return nullptr;
213 return custom_page_clickzone_;
216 void AppListMainView::SetDragAndDropHostOfCurrentAppList(
217 ApplicationDragAndDropHost* drag_and_drop_host) {
218 contents_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
221 bool AppListMainView::ShouldCenterWindow() const {
222 return delegate_->ShouldCenterWindow();
225 PaginationModel* AppListMainView::GetAppsPaginationModel() {
226 return contents_view_->apps_container_view()
227 ->apps_grid_view()
228 ->pagination_model();
231 void AppListMainView::PreloadIcons(gfx::NativeView parent) {
232 float scale_factor = 1.0f;
233 if (parent)
234 scale_factor = ui::GetScaleFactorForNativeView(parent);
236 // The PaginationModel could have -1 as the initial selected page and
237 // assumes first page (i.e. index 0) will be used in this case.
238 const int selected_page =
239 std::max(0, GetAppsPaginationModel()->selected_page());
241 const AppsGridView* const apps_grid_view =
242 contents_view_->apps_container_view()->apps_grid_view();
243 const int tiles_per_page =
244 apps_grid_view->cols() * apps_grid_view->rows_per_page();
246 const int start_model_index = selected_page * tiles_per_page;
247 const int end_model_index =
248 std::min(static_cast<int>(model_->top_level_item_list()->item_count()),
249 start_model_index + tiles_per_page);
251 pending_icon_loaders_.clear();
252 for (int i = start_model_index; i < end_model_index; ++i) {
253 AppListItem* item = model_->top_level_item_list()->item_at(i);
254 if (item->icon().HasRepresentation(scale_factor))
255 continue;
257 pending_icon_loaders_.push_back(new IconLoader(this, item, scale_factor));
261 void AppListMainView::OnIconLoadingWaitTimer() {
262 GetWidget()->Show();
265 void AppListMainView::OnItemIconLoaded(IconLoader* loader) {
266 ScopedVector<IconLoader>::iterator it = std::find(
267 pending_icon_loaders_.begin(), pending_icon_loaders_.end(), loader);
268 DCHECK(it != pending_icon_loaders_.end());
269 pending_icon_loaders_.erase(it);
271 if (pending_icon_loaders_.empty() && icon_loading_wait_timer_.IsRunning()) {
272 icon_loading_wait_timer_.Stop();
273 GetWidget()->Show();
277 void AppListMainView::NotifySearchBoxVisibilityChanged() {
278 // Repaint the AppListView's background which will repaint the background for
279 // the search box. This is needed because this view paints to a layer and
280 // won't propagate paints upward.
281 if (parent())
282 parent()->SchedulePaint();
285 void AppListMainView::InitWidgets() {
286 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
287 tracked_objects::ScopedTracker tracking_profile(
288 FROM_HERE_WITH_EXPLICIT_FUNCTION("431326 AppListMainView::InitWidgets"));
290 // The widget that receives click events to transition to the custom page.
291 views::Widget::InitParams custom_page_clickzone_params(
292 views::Widget::InitParams::TYPE_CONTROL);
294 custom_page_clickzone_params.parent = GetWidget()->GetNativeView();
295 custom_page_clickzone_params.layer_type = aura::WINDOW_LAYER_NOT_DRAWN;
297 gfx::Rect custom_page_bounds = contents_view_->GetCustomPageCollapsedBounds();
298 custom_page_bounds.Intersect(contents_view_->bounds());
299 custom_page_bounds = contents_view_->ConvertRectToWidget(custom_page_bounds);
300 custom_page_clickzone_params.bounds = custom_page_bounds;
302 // Create a widget for the custom page click zone. This widget masks click
303 // events from the WebContents that rests underneath it. (It has to be a
304 // widget, not an ordinary view, so that it can be placed in front of the
305 // WebContents.)
306 custom_page_clickzone_ = new views::Widget;
307 custom_page_clickzone_->Init(custom_page_clickzone_params);
308 custom_page_clickzone_->SetContentsView(new CustomPageButton(this));
309 // The widget is shown by default. If the ContentsView determines that we do
310 // not need a clickzone upon startup, hide it.
311 if (!contents_view_->ShouldShowCustomPageClickzone())
312 custom_page_clickzone_->Hide();
315 void AppListMainView::OnCustomLauncherPageEnabledStateChanged(bool enabled) {
316 // Allow the start page to update |custom_page_clickzone_|.
317 if (contents_view_->IsStateActive(AppListModel::STATE_START))
318 contents_view_->start_page_view()->UpdateCustomPageClickzoneVisibility();
321 void AppListMainView::ActivateApp(AppListItem* item, int event_flags) {
322 // TODO(jennyz): Activate the folder via AppListModel notification.
323 if (item->GetItemType() == AppListFolderItem::kItemType)
324 contents_view_->ShowFolderContent(static_cast<AppListFolderItem*>(item));
325 else
326 item->Activate(event_flags);
329 void AppListMainView::GetShortcutPathForApp(
330 const std::string& app_id,
331 const base::Callback<void(const base::FilePath&)>& callback) {
332 delegate_->GetShortcutPathForApp(app_id, callback);
335 void AppListMainView::CancelDragInActiveFolder() {
336 contents_view_->apps_container_view()
337 ->app_list_folder_view()
338 ->items_grid_view()
339 ->EndDrag(true);
342 void AppListMainView::QueryChanged(SearchBoxView* sender) {
343 base::string16 query;
344 base::TrimWhitespace(model_->search_box()->text(), base::TRIM_ALL, &query);
345 bool should_show_search = !query.empty();
346 contents_view_->ShowSearchResults(should_show_search);
348 delegate_->StartSearch();
351 void AppListMainView::BackButtonPressed() {
352 contents_view_->Back();
355 void AppListMainView::OnResultInstalled(SearchResult* result) {
356 // Clears the search to show the apps grid. The last installed app
357 // should be highlighted and made visible already.
358 search_box_view_->ClearSearch();
361 } // namespace app_list