OLD | NEW |
| (Empty) |
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_shower_delegate.h" | |
6 | |
7 #include "ash/app_list/app_list_view_delegate_factory.h" | |
8 #include "ash/ash_switches.h" | |
9 #include "ash/root_window_controller.h" | |
10 #include "ash/screen_util.h" | |
11 #include "ash/shelf/shelf.h" | |
12 #include "ash/shelf/shelf_layout_manager.h" | |
13 #include "ash/shell.h" | |
14 #include "ash/shell_delegate.h" | |
15 #include "ash/shell_window_ids.h" | |
16 #include "ash/wm/maximize_mode/maximize_mode_controller.h" | |
17 #include "base/command_line.h" | |
18 #include "ui/app_list/app_list_constants.h" | |
19 #include "ui/app_list/app_list_switches.h" | |
20 #include "ui/app_list/shower/app_list_shower.h" | |
21 #include "ui/app_list/views/app_list_view.h" | |
22 #include "ui/aura/window.h" | |
23 #include "ui/events/event.h" | |
24 #include "ui/keyboard/keyboard_controller.h" | |
25 #include "ui/views/widget/widget.h" | |
26 | |
27 namespace ash { | |
28 namespace { | |
29 | |
30 // Offset in pixels to animation away/towards the shelf. | |
31 const int kAnimationOffset = 8; | |
32 | |
33 // The minimal anchor position offset to make sure that the bubble is still on | |
34 // the screen with 8 pixels spacing on the left / right. This constant is a | |
35 // result of minimal bubble arrow sizes and offsets. | |
36 const int kMinimalAnchorPositionOffset = 57; | |
37 | |
38 // The minimal margin (in pixels) around the app list when in centered mode. | |
39 const int kMinimalCenteredAppListMargin = 10; | |
40 | |
41 // Gets arrow location based on shelf alignment. | |
42 views::BubbleBorder::Arrow GetBubbleArrow(aura::Window* window) { | |
43 DCHECK(Shell::HasInstance()); | |
44 return Shelf::ForWindow(window)->SelectValueForShelfAlignment( | |
45 views::BubbleBorder::BOTTOM_CENTER, views::BubbleBorder::LEFT_CENTER, | |
46 views::BubbleBorder::RIGHT_CENTER); | |
47 } | |
48 | |
49 // Using |button_bounds|, determine the anchor offset so that the bubble gets | |
50 // shown above the shelf (used for the alternate shelf theme). | |
51 gfx::Vector2d GetAnchorPositionOffsetToShelf( | |
52 const gfx::Rect& button_bounds, views::Widget* widget) { | |
53 DCHECK(Shell::HasInstance()); | |
54 ShelfAlignment shelf_alignment = Shell::GetInstance()->GetShelfAlignment( | |
55 widget->GetNativeView()->GetRootWindow()); | |
56 gfx::Point anchor(button_bounds.CenterPoint()); | |
57 switch (shelf_alignment) { | |
58 case SHELF_ALIGNMENT_BOTTOM: | |
59 if (base::i18n::IsRTL()) { | |
60 int screen_width = widget->GetWorkAreaBoundsInScreen().width(); | |
61 return gfx::Vector2d( | |
62 std::min(screen_width - kMinimalAnchorPositionOffset - anchor.x(), | |
63 0), 0); | |
64 } | |
65 return gfx::Vector2d( | |
66 std::max(kMinimalAnchorPositionOffset - anchor.x(), 0), 0); | |
67 case SHELF_ALIGNMENT_LEFT: | |
68 return gfx::Vector2d( | |
69 0, std::max(kMinimalAnchorPositionOffset - anchor.y(), 0)); | |
70 case SHELF_ALIGNMENT_RIGHT: | |
71 return gfx::Vector2d( | |
72 0, std::max(kMinimalAnchorPositionOffset - anchor.y(), 0)); | |
73 } | |
74 NOTREACHED(); | |
75 return gfx::Vector2d(); | |
76 } | |
77 | |
78 // Gets the point at the center of the display that a particular view is on. | |
79 // This calculation excludes the virtual keyboard area. If the height of the | |
80 // display area is less than |minimum_height|, its bottom will be extended to | |
81 // that height (so that the app list never starts above the top of the screen). | |
82 gfx::Point GetCenterOfDisplayForView(const views::View* view, | |
83 int minimum_height) { | |
84 aura::Window* window = view->GetWidget()->GetNativeView(); | |
85 gfx::Rect bounds = ScreenUtil::GetShelfDisplayBoundsInRoot(window); | |
86 bounds = ScreenUtil::ConvertRectToScreen(window->GetRootWindow(), bounds); | |
87 | |
88 // If the virtual keyboard is active, subtract it from the display bounds, so | |
89 // that the app list is centered in the non-keyboard area of the display. | |
90 // (Note that work_area excludes the keyboard, but it doesn't get updated | |
91 // until after this function is called.) | |
92 keyboard::KeyboardController* keyboard_controller = | |
93 keyboard::KeyboardController::GetInstance(); | |
94 if (keyboard_controller && keyboard_controller->keyboard_visible()) | |
95 bounds.Subtract(keyboard_controller->current_keyboard_bounds()); | |
96 | |
97 // Apply the |minimum_height|. | |
98 if (bounds.height() < minimum_height) | |
99 bounds.set_height(minimum_height); | |
100 | |
101 return bounds.CenterPoint(); | |
102 } | |
103 | |
104 // Gets the minimum height of the rectangle to center the app list in. | |
105 int GetMinimumBoundsHeightForAppList(const app_list::AppListView* app_list) { | |
106 return app_list->bounds().height() + 2 * kMinimalCenteredAppListMargin; | |
107 } | |
108 | |
109 bool IsFullscreenAppListEnabled() { | |
110 #if defined(OS_CHROMEOS) | |
111 return base::CommandLine::ForCurrentProcess()->HasSwitch( | |
112 switches::kAshEnableFullscreenAppList) && | |
113 app_list::switches::IsExperimentalAppListEnabled(); | |
114 #else | |
115 return false; | |
116 #endif | |
117 } | |
118 | |
119 } // namespace | |
120 | |
121 //////////////////////////////////////////////////////////////////////////////// | |
122 // AppListShowerDelegate, public: | |
123 | |
124 AppListShowerDelegate::AppListShowerDelegate( | |
125 app_list::AppListShower* shower, | |
126 AppListViewDelegateFactory* view_delegate_factory) | |
127 : shower_(shower), view_delegate_factory_(view_delegate_factory) { | |
128 Shell::GetInstance()->AddShellObserver(this); | |
129 } | |
130 | |
131 AppListShowerDelegate::~AppListShowerDelegate() { | |
132 DCHECK(view_); | |
133 keyboard::KeyboardController* keyboard_controller = | |
134 keyboard::KeyboardController::GetInstance(); | |
135 if (keyboard_controller) | |
136 keyboard_controller->RemoveObserver(this); | |
137 views::Widget* widget = view_->GetWidget(); | |
138 Shell::GetInstance()->RemovePreTargetHandler(this); | |
139 Shelf::ForWindow(widget->GetNativeWindow())->RemoveIconObserver(this); | |
140 Shell::GetInstance()->RemoveShellObserver(this); | |
141 } | |
142 | |
143 app_list::AppListViewDelegate* AppListShowerDelegate::GetViewDelegate() { | |
144 return view_delegate_factory_->GetDelegate(); | |
145 } | |
146 | |
147 void AppListShowerDelegate::Init(app_list::AppListView* view, | |
148 aura::Window* root_window, | |
149 int current_apps_page) { | |
150 // App list needs to know the new shelf layout in order to calculate its | |
151 // UI layout when AppListView visibility changes. | |
152 ash::Shell::GetPrimaryRootWindowController() | |
153 ->GetShelfLayoutManager() | |
154 ->UpdateAutoHideState(); | |
155 view_ = view; | |
156 aura::Window* container = GetRootWindowController(root_window) | |
157 ->GetContainer(kShellWindowId_AppListContainer); | |
158 views::View* applist_button = | |
159 Shelf::ForWindow(container)->GetAppListButtonView(); | |
160 is_centered_ = view->ShouldCenterWindow(); | |
161 bool is_fullscreen = IsFullscreenAppListEnabled() && | |
162 Shell::GetInstance() | |
163 ->maximize_mode_controller() | |
164 ->IsMaximizeModeWindowManagerEnabled(); | |
165 if (is_fullscreen) { | |
166 view->InitAsFramelessWindow( | |
167 container, current_apps_page, | |
168 ScreenUtil::GetDisplayWorkAreaBoundsInParent(container)); | |
169 } else if (is_centered_) { | |
170 // Note: We can't center the app list until we have its dimensions, so we | |
171 // init at (0, 0) and then reset its anchor point. | |
172 view->InitAsBubbleAtFixedLocation(container, | |
173 current_apps_page, | |
174 gfx::Point(), | |
175 views::BubbleBorder::FLOAT, | |
176 true /* border_accepts_events */); | |
177 // The experimental app list is centered over the display of the app list | |
178 // button that was pressed (if triggered via keyboard, this is the display | |
179 // with the currently focused window). | |
180 view->SetAnchorPoint(GetCenterOfDisplayForView( | |
181 applist_button, GetMinimumBoundsHeightForAppList(view))); | |
182 } else { | |
183 gfx::Rect applist_button_bounds = applist_button->GetBoundsInScreen(); | |
184 // We need the location of the button within the local screen. | |
185 applist_button_bounds = ScreenUtil::ConvertRectFromScreen( | |
186 root_window, | |
187 applist_button_bounds); | |
188 view->InitAsBubbleAttachedToAnchor( | |
189 container, | |
190 current_apps_page, | |
191 Shelf::ForWindow(container)->GetAppListButtonView(), | |
192 GetAnchorPositionOffsetToShelf( | |
193 applist_button_bounds, | |
194 Shelf::ForWindow(container)->GetAppListButtonView()->GetWidget()), | |
195 GetBubbleArrow(container), | |
196 true /* border_accepts_events */); | |
197 view->SetArrowPaintType(views::BubbleBorder::PAINT_NONE); | |
198 } | |
199 | |
200 keyboard::KeyboardController* keyboard_controller = | |
201 keyboard::KeyboardController::GetInstance(); | |
202 if (keyboard_controller) | |
203 keyboard_controller->AddObserver(this); | |
204 Shell::GetInstance()->AddPreTargetHandler(this); | |
205 views::Widget* widget = view->GetWidget(); | |
206 Shelf::ForWindow(widget->GetNativeWindow())->AddIconObserver(this); | |
207 | |
208 // By setting us as DnD recipient, the app list knows that we can | |
209 // handle items. | |
210 view->SetDragAndDropHostOfCurrentAppList( | |
211 Shelf::ForWindow(root_window)->GetDragAndDropHostForAppList()); | |
212 } | |
213 | |
214 void AppListShowerDelegate::OnShown(aura::Window* root_window) { | |
215 is_visible_ = true; | |
216 // Update applist button status when app list visibility is changed. | |
217 Shelf::ForWindow(root_window)->GetAppListButtonView()->SchedulePaint(); | |
218 } | |
219 | |
220 void AppListShowerDelegate::OnDismissed() { | |
221 DCHECK(is_visible_); | |
222 DCHECK(view_); | |
223 | |
224 is_visible_ = false; | |
225 | |
226 // App list needs to know the new shelf layout in order to calculate its | |
227 // UI layout when AppListView visibility changes. | |
228 Shell::GetPrimaryRootWindowController() | |
229 ->GetShelfLayoutManager() | |
230 ->UpdateAutoHideState(); | |
231 | |
232 // Update applist button status when app list visibility is changed. | |
233 Shelf::ForWindow(view_->GetWidget()->GetNativeView()) | |
234 ->GetAppListButtonView() | |
235 ->SchedulePaint(); | |
236 } | |
237 | |
238 void AppListShowerDelegate::UpdateBounds() { | |
239 if (!view_ || !is_visible_) | |
240 return; | |
241 | |
242 view_->UpdateBounds(); | |
243 | |
244 if (is_centered_) { | |
245 view_->SetAnchorPoint(GetCenterOfDisplayForView( | |
246 view_, GetMinimumBoundsHeightForAppList(view_))); | |
247 } | |
248 } | |
249 | |
250 gfx::Vector2d AppListShowerDelegate::GetVisibilityAnimationOffset( | |
251 aura::Window* root_window) { | |
252 DCHECK(Shell::HasInstance()); | |
253 | |
254 // App list needs to know the new shelf layout in order to calculate its | |
255 // UI layout when AppListView visibility changes. | |
256 Shell::GetPrimaryRootWindowController() | |
257 ->GetShelfLayoutManager() | |
258 ->UpdateAutoHideState(); | |
259 | |
260 ShelfAlignment shelf_alignment = | |
261 Shell::GetInstance()->GetShelfAlignment(root_window); | |
262 switch (shelf_alignment) { | |
263 case SHELF_ALIGNMENT_BOTTOM: | |
264 return gfx::Vector2d(0, kAnimationOffset); | |
265 case SHELF_ALIGNMENT_LEFT: | |
266 return gfx::Vector2d(-kAnimationOffset, 0); | |
267 case SHELF_ALIGNMENT_RIGHT: | |
268 return gfx::Vector2d(kAnimationOffset, 0); | |
269 } | |
270 NOTREACHED(); | |
271 return gfx::Vector2d(); | |
272 } | |
273 | |
274 //////////////////////////////////////////////////////////////////////////////// | |
275 // AppListShowerDelegate, private: | |
276 | |
277 void AppListShowerDelegate::ProcessLocatedEvent(ui::LocatedEvent* event) { | |
278 if (!view_ || !is_visible_) | |
279 return; | |
280 | |
281 // If the event happened on a menu, then the event should not close the app | |
282 // list. | |
283 aura::Window* target = static_cast<aura::Window*>(event->target()); | |
284 if (target) { | |
285 RootWindowController* root_controller = | |
286 GetRootWindowController(target->GetRootWindow()); | |
287 if (root_controller) { | |
288 aura::Window* menu_container = | |
289 root_controller->GetContainer(kShellWindowId_MenuContainer); | |
290 if (menu_container->Contains(target)) | |
291 return; | |
292 aura::Window* keyboard_container = root_controller->GetContainer( | |
293 kShellWindowId_VirtualKeyboardContainer); | |
294 if (keyboard_container->Contains(target)) | |
295 return; | |
296 } | |
297 } | |
298 | |
299 aura::Window* window = view_->GetWidget()->GetNativeView()->parent(); | |
300 if (!window->Contains(target) && | |
301 !app_list::switches::ShouldNotDismissOnBlur()) { | |
302 shower_->Dismiss(); | |
303 } | |
304 } | |
305 | |
306 //////////////////////////////////////////////////////////////////////////////// | |
307 // AppListShowerDelegate, aura::EventFilter implementation: | |
308 | |
309 void AppListShowerDelegate::OnMouseEvent(ui::MouseEvent* event) { | |
310 if (event->type() == ui::ET_MOUSE_PRESSED) | |
311 ProcessLocatedEvent(event); | |
312 } | |
313 | |
314 void AppListShowerDelegate::OnGestureEvent(ui::GestureEvent* event) { | |
315 if (event->type() == ui::ET_GESTURE_TAP_DOWN) | |
316 ProcessLocatedEvent(event); | |
317 } | |
318 | |
319 //////////////////////////////////////////////////////////////////////////////// | |
320 // AppListShowerDelegate, keyboard::KeyboardControllerObserver implementation: | |
321 | |
322 void AppListShowerDelegate::OnKeyboardBoundsChanging( | |
323 const gfx::Rect& new_bounds) { | |
324 UpdateBounds(); | |
325 } | |
326 | |
327 //////////////////////////////////////////////////////////////////////////////// | |
328 // AppListShowerDelegate, ShellObserver implementation: | |
329 void AppListShowerDelegate::OnShelfAlignmentChanged(aura::Window* root_window) { | |
330 if (view_) | |
331 view_->SetBubbleArrow(GetBubbleArrow(view_->GetWidget()->GetNativeView())); | |
332 } | |
333 | |
334 void AppListShowerDelegate::OnMaximizeModeStarted() { | |
335 // The "fullscreen" app-list is initialized as in a different type of window, | |
336 // therefore we can't switch between the fullscreen status and the normal | |
337 // app-list bubble. App-list should be dismissed for the transition between | |
338 // maximize mode (touch-view mode) and non-maximize mode, otherwise the app | |
339 // list tries to behave as a bubble which leads to a crash. crbug.com/510062 | |
340 if (IsFullscreenAppListEnabled() && is_visible_) | |
341 shower_->Dismiss(); | |
342 } | |
343 | |
344 void AppListShowerDelegate::OnMaximizeModeEnded() { | |
345 // See the comments of OnMaximizeModeStarted(). | |
346 if (IsFullscreenAppListEnabled() && is_visible_) | |
347 shower_->Dismiss(); | |
348 } | |
349 | |
350 //////////////////////////////////////////////////////////////////////////////// | |
351 // AppListShowerDelegate, ShelfIconObserver implementation: | |
352 | |
353 void AppListShowerDelegate::OnShelfIconPositionsChanged() { | |
354 UpdateBounds(); | |
355 } | |
356 | |
357 } // namespace ash | |
OLD | NEW |