1 // Copyright (c) 2012 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.
4 
5 #include "ash/app_list/app_list_presenter_delegate_impl.h"
6 
7 #include "ash/app_list/app_list_controller_impl.h"
8 #include "ash/app_list/app_list_presenter_impl.h"
9 #include "ash/app_list/app_list_util.h"
10 #include "ash/app_list/views/app_list_main_view.h"
11 #include "ash/app_list/views/app_list_view.h"
12 #include "ash/app_list/views/contents_view.h"
13 #include "ash/app_list/views/search_box_view.h"
14 #include "ash/capture_mode/capture_mode_controller.h"
15 #include "ash/keyboard/ui/keyboard_ui_controller.h"
16 #include "ash/public/cpp/app_list/app_list_switches.h"
17 #include "ash/public/cpp/ash_features.h"
18 #include "ash/public/cpp/ash_switches.h"
19 #include "ash/public/cpp/shelf_types.h"
20 #include "ash/public/cpp/shell_window_ids.h"
21 #include "ash/root_window_controller.h"
22 #include "ash/shelf/back_button.h"
23 #include "ash/shelf/home_button.h"
24 #include "ash/shelf/hotseat_widget.h"
25 #include "ash/shelf/shelf_layout_manager.h"
26 #include "ash/shelf/shelf_navigation_widget.h"
27 #include "ash/shelf/shelf_widget.h"
28 #include "ash/shell.h"
29 #include "ash/system/status_area_widget.h"
30 #include "ash/wm/container_finder.h"
31 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
32 #include "base/command_line.h"
33 #include "chromeos/constants/chromeos_switches.h"
34 #include "ui/aura/window.h"
35 #include "ui/display/manager/display_manager.h"
36 #include "ui/events/event.h"
37 #include "ui/events/keycodes/keyboard_codes_posix.h"
38 #include "ui/views/widget/widget.h"
39 #include "ui/wm/core/coordinate_conversion.h"
40 
41 namespace ash {
42 namespace {
43 
44 // Whether the shelf is oriented on the side, not on the bottom.
IsSideShelf(Shelf * shelf)45 bool IsSideShelf(Shelf* shelf) {
46   switch (shelf->alignment()) {
47     case ShelfAlignment::kBottom:
48     case ShelfAlignment::kBottomLocked:
49       return false;
50     case ShelfAlignment::kLeft:
51     case ShelfAlignment::kRight:
52       return true;
53   }
54   return false;
55 }
56 
57 // Whether the shelf background type indicates that shelf has rounded corners.
IsShelfBackgroundTypeWithRoundedCorners(ShelfBackgroundType background_type)58 bool IsShelfBackgroundTypeWithRoundedCorners(
59     ShelfBackgroundType background_type) {
60   switch (background_type) {
61     case ShelfBackgroundType::kDefaultBg:
62     case ShelfBackgroundType::kAppList:
63     case ShelfBackgroundType::kOverview:
64       return true;
65     case ShelfBackgroundType::kMaximized:
66     case ShelfBackgroundType::kMaximizedWithAppList:
67     case ShelfBackgroundType::kOobe:
68     case ShelfBackgroundType::kHomeLauncher:
69     case ShelfBackgroundType::kLogin:
70     case ShelfBackgroundType::kLoginNonBlurredWallpaper:
71     case ShelfBackgroundType::kInApp:
72       return false;
73   }
74 }
75 
76 }  // namespace
77 
78 ////////////////////////////////////////////////////////////////////////////////
79 // AppListPresenterDelegateImpl, public:
80 
AppListPresenterDelegateImpl(AppListControllerImpl * controller)81 AppListPresenterDelegateImpl::AppListPresenterDelegateImpl(
82     AppListControllerImpl* controller)
83     : controller_(controller) {
84   display_observer_.Add(display::Screen::GetScreen());
85 }
86 
~AppListPresenterDelegateImpl()87 AppListPresenterDelegateImpl::~AppListPresenterDelegateImpl() {
88   Shell::Get()->RemovePreTargetHandler(this);
89 }
90 
SetPresenter(AppListPresenterImpl * presenter)91 void AppListPresenterDelegateImpl::SetPresenter(
92     AppListPresenterImpl* presenter) {
93   presenter_ = presenter;
94 }
95 
Init(AppListView * view,int64_t display_id)96 void AppListPresenterDelegateImpl::Init(AppListView* view, int64_t display_id) {
97   view_ = view;
98   view->InitView(controller_->GetContainerForDisplayId(display_id));
99 
100   // By setting us as DnD recipient, the app list knows that we can
101   // handle items.
102   Shelf* shelf = Shelf::ForWindow(Shell::GetRootWindowForDisplayId(display_id));
103   view->SetDragAndDropHostOfCurrentAppList(
104       shelf->shelf_widget()->GetDragAndDropHostForAppList());
105 }
106 
ShowForDisplay(int64_t display_id)107 void AppListPresenterDelegateImpl::ShowForDisplay(int64_t display_id) {
108   is_visible_ = true;
109 
110   controller_->UpdateLauncherContainer(display_id);
111 
112   // App list needs to know the new shelf layout in order to calculate its
113   // UI layout when AppListView visibility changes.
114   Shell::GetPrimaryRootWindowController()
115       ->GetShelfLayoutManager()
116       ->UpdateAutoHideState();
117 
118   Shelf* shelf =
119       Shelf::ForWindow(view_->GetWidget()->GetNativeView()->GetRootWindow());
120   if (!shelf_observer_.IsObserving(shelf))
121     shelf_observer_.Add(shelf);
122 
123   view_->SetShelfHasRoundedCorners(
124       IsShelfBackgroundTypeWithRoundedCorners(shelf->GetBackgroundType()));
125   view_->Show(IsSideShelf(shelf));
126 
127   SnapAppListBoundsToDisplayEdge();
128 
129   Shell::Get()->AddPreTargetHandler(this);
130   controller_->ViewShown(display_id);
131 }
132 
OnClosing()133 void AppListPresenterDelegateImpl::OnClosing() {
134   DCHECK(is_visible_);
135   DCHECK(view_);
136   is_visible_ = false;
137   Shell::Get()->RemovePreTargetHandler(this);
138   controller_->ViewClosing();
139 }
140 
OnClosed()141 void AppListPresenterDelegateImpl::OnClosed() {
142   if (!is_visible_)
143     shelf_observer_.RemoveAll();
144   controller_->ViewClosed();
145 }
146 
IsTabletMode() const147 bool AppListPresenterDelegateImpl::IsTabletMode() const {
148   return Shell::Get()->tablet_mode_controller()->InTabletMode();
149 }
150 
GetAppListViewDelegate()151 AppListViewDelegate* AppListPresenterDelegateImpl::GetAppListViewDelegate() {
152   return controller_;
153 }
154 
GetOnScreenKeyboardShown()155 bool AppListPresenterDelegateImpl::GetOnScreenKeyboardShown() {
156   return controller_->onscreen_keyboard_shown();
157 }
158 
GetContainerForWindow(aura::Window * window)159 aura::Window* AppListPresenterDelegateImpl::GetContainerForWindow(
160     aura::Window* window) {
161   return ash::GetContainerForWindow(window);
162 }
163 
GetRootWindowForDisplayId(int64_t display_id)164 aura::Window* AppListPresenterDelegateImpl::GetRootWindowForDisplayId(
165     int64_t display_id) {
166   return Shell::Get()->GetRootWindowForDisplayId(display_id);
167 }
168 
OnVisibilityChanged(bool visible,int64_t display_id)169 void AppListPresenterDelegateImpl::OnVisibilityChanged(bool visible,
170                                                        int64_t display_id) {
171   controller_->OnVisibilityChanged(visible, display_id);
172 }
173 
OnVisibilityWillChange(bool visible,int64_t display_id)174 void AppListPresenterDelegateImpl::OnVisibilityWillChange(bool visible,
175                                                           int64_t display_id) {
176   controller_->OnVisibilityWillChange(visible, display_id);
177 }
178 
IsVisible(const base::Optional<int64_t> & display_id)179 bool AppListPresenterDelegateImpl::IsVisible(
180     const base::Optional<int64_t>& display_id) {
181   return controller_->IsVisible(display_id);
182 }
183 
OnDisplayMetricsChanged(const display::Display & display,uint32_t changed_metrics)184 void AppListPresenterDelegateImpl::OnDisplayMetricsChanged(
185     const display::Display& display,
186     uint32_t changed_metrics) {
187   if (!presenter_->GetWindow())
188     return;
189 
190   view_->OnParentWindowBoundsChanged();
191   SnapAppListBoundsToDisplayEdge();
192 }
193 
OnBackgroundTypeChanged(ShelfBackgroundType background_type,AnimationChangeType change_type)194 void AppListPresenterDelegateImpl::OnBackgroundTypeChanged(
195     ShelfBackgroundType background_type,
196     AnimationChangeType change_type) {
197   view_->SetShelfHasRoundedCorners(
198       IsShelfBackgroundTypeWithRoundedCorners(background_type));
199 }
200 
201 ////////////////////////////////////////////////////////////////////////////////
202 // AppListPresenterDelegateImpl, private:
203 
ProcessLocatedEvent(ui::LocatedEvent * event)204 void AppListPresenterDelegateImpl::ProcessLocatedEvent(
205     ui::LocatedEvent* event) {
206   if (!view_ || !is_visible_)
207     return;
208 
209   // Users in a capture session may be trying to capture the app list.
210   if (features::IsCaptureModeEnabled() &&
211       CaptureModeController::Get()->IsActive()) {
212     return;
213   }
214 
215   aura::Window* target = static_cast<aura::Window*>(event->target());
216   if (!target)
217     return;
218 
219   // If the event happened on a menu, then the event should not close the app
220   // list.
221   RootWindowController* root_controller =
222       RootWindowController::ForWindow(target);
223   if (root_controller) {
224     aura::Window* menu_container =
225         root_controller->GetContainer(kShellWindowId_MenuContainer);
226     if (menu_container->Contains(target))
227       return;
228     aura::Window* keyboard_container =
229         root_controller->GetContainer(kShellWindowId_VirtualKeyboardContainer);
230     if (keyboard_container->Contains(target))
231       return;
232   }
233 
234   // If the event happened on the home button's widget, it'll get handled by the
235   // button.
236   Shelf* shelf = Shelf::ForWindow(target);
237   HomeButton* home_button = shelf->navigation_widget()->GetHomeButton();
238   if (home_button && home_button->GetWidget() &&
239       target == home_button->GetWidget()->GetNativeWindow()) {
240     gfx::Point location_in_home_button = event->location();
241     views::View::ConvertPointFromWidget(home_button, &location_in_home_button);
242     if (home_button->HitTestPoint(location_in_home_button))
243       return;
244   }
245 
246   // If the event happened on the back button, it'll get handled by the
247   // button.
248   BackButton* back_button = shelf->navigation_widget()->GetBackButton();
249   if (back_button && back_button->GetWidget() &&
250       target == back_button->GetWidget()->GetNativeWindow()) {
251     gfx::Point location_in_back_button = event->location();
252     views::View::ConvertPointFromWidget(back_button, &location_in_back_button);
253     if (back_button->HitTestPoint(location_in_back_button))
254       return;
255   }
256 
257   aura::Window* window = view_->GetWidget()->GetNativeView()->parent();
258   if (!window->Contains(target) && !presenter_->HandleCloseOpenFolder() &&
259       !switches::ShouldNotDismissOnBlur() && !IsTabletMode()) {
260     // Do not dismiss the app list if the event is targeting shelf area
261     // containing app icons.
262     if (target == shelf->hotseat_widget()->GetNativeWindow() &&
263         shelf->hotseat_widget()->EventTargetsShelfView(*event)) {
264       return;
265     }
266 
267     // Don't dismiss the auto-hide shelf if event happened in status area. Then
268     // the event can still be propagated.
269     base::Optional<Shelf::ScopedAutoHideLock> auto_hide_lock;
270     const aura::Window* status_window =
271         shelf->shelf_widget()->status_area_widget()->GetNativeWindow();
272     if (status_window && status_window->Contains(target))
273       auto_hide_lock.emplace(shelf);
274 
275     // Record the current AppListViewState to be used later for metrics. The
276     // AppListViewState will change on app launch, so this will record the
277     // AppListViewState before the app was launched.
278     controller_->RecordAppListState();
279     presenter_->Dismiss(event->time_stamp());
280   }
281 }
282 
283 ////////////////////////////////////////////////////////////////////////////////
284 // AppListPresenterDelegateImpl, aura::EventFilter implementation:
285 
OnMouseEvent(ui::MouseEvent * event)286 void AppListPresenterDelegateImpl::OnMouseEvent(ui::MouseEvent* event) {
287   // Moving the mouse shouldn't hide focus rings.
288   if (event->IsAnyButton())
289     controller_->SetKeyboardTraversalMode(false);
290 
291   if (event->type() == ui::ET_MOUSE_PRESSED)
292     ProcessLocatedEvent(event);
293 }
294 
OnGestureEvent(ui::GestureEvent * event)295 void AppListPresenterDelegateImpl::OnGestureEvent(ui::GestureEvent* event) {
296   controller_->SetKeyboardTraversalMode(false);
297 
298   if (event->type() == ui::ET_GESTURE_TAP ||
299       event->type() == ui::ET_GESTURE_TWO_FINGER_TAP ||
300       event->type() == ui::ET_GESTURE_LONG_PRESS) {
301     ProcessLocatedEvent(event);
302   }
303 }
304 
OnKeyEvent(ui::KeyEvent * event)305 void AppListPresenterDelegateImpl::OnKeyEvent(ui::KeyEvent* event) {
306   // If keyboard traversal is already engaged, no-op.
307   if (controller_->KeyboardTraversalEngaged())
308     return;
309 
310   // If the home launcher is not shown in tablet mode, ignore events.
311   if (IsTabletMode() && !IsVisible(base::nullopt))
312     return;
313 
314   // Don't absorb the first event for the search box while it is open
315   if (view_->search_box_view()->is_search_box_active())
316     return;
317 
318   // Don't absorb the first event when showing Assistant.
319   if (view_->IsShowingEmbeddedAssistantUI())
320     return;
321 
322   // Don't absorb the first event when renaming folder.
323   if (view_->IsFolderBeingRenamed())
324     return;
325 
326   // Arrow keys or Tab will engage the traversal mode.
327   if ((IsUnhandledArrowKeyEvent(*event) || event->key_code() == ui::VKEY_TAB)) {
328     // Handle the first arrow key event to just show the focus rings.
329     event->SetHandled();
330     controller_->SetKeyboardTraversalMode(true);
331   }
332 }
333 
SnapAppListBoundsToDisplayEdge()334 void AppListPresenterDelegateImpl::SnapAppListBoundsToDisplayEdge() {
335   CHECK(view_ && view_->GetWidget());
336   aura::Window* window = view_->GetWidget()->GetNativeView();
337   const gfx::Rect bounds =
338       controller_->SnapBoundsToDisplayEdge(window->bounds());
339   window->SetBounds(bounds);
340 }
341 
342 }  // namespace ash
343