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