OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 "athena/wm/window_manager_impl.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "athena/screen/public/screen_manager.h" | |
10 #include "athena/util/container_priorities.h" | |
11 #include "athena/wm/public/window_manager_observer.h" | |
12 #include "athena/wm/split_view_controller.h" | |
13 #include "athena/wm/title_drag_controller.h" | |
14 #include "athena/wm/window_list_provider_impl.h" | |
15 #include "athena/wm/window_overview_mode.h" | |
16 #include "base/bind.h" | |
17 #include "base/logging.h" | |
18 #include "ui/aura/client/aura_constants.h" | |
19 #include "ui/aura/layout_manager.h" | |
20 #include "ui/aura/window.h" | |
21 #include "ui/aura/window_delegate.h" | |
22 #include "ui/compositor/closure_animation_observer.h" | |
23 #include "ui/compositor/scoped_layer_animation_settings.h" | |
24 #include "ui/gfx/display.h" | |
25 #include "ui/gfx/screen.h" | |
26 #include "ui/wm/core/shadow_controller.h" | |
27 #include "ui/wm/core/transient_window_manager.h" | |
28 #include "ui/wm/core/window_util.h" | |
29 #include "ui/wm/core/wm_state.h" | |
30 #include "ui/wm/public/activation_client.h" | |
31 #include "ui/wm/public/window_types.h" | |
32 | |
33 namespace athena { | |
34 namespace { | |
35 class WindowManagerImpl* instance = nullptr; | |
36 | |
37 void SetWindowState(aura::Window* window, | |
38 const gfx::Rect& bounds, | |
39 const gfx::Transform& transform) { | |
40 window->SetBounds(bounds); | |
41 window->SetTransform(transform); | |
42 } | |
43 | |
44 // Tests whether the given window can be maximized | |
45 bool CanWindowMaximize(const aura::Window* const window) { | |
46 const aura::WindowDelegate* delegate = window->delegate(); | |
47 const bool no_max_size = | |
48 !delegate || delegate->GetMaximumSize().IsEmpty(); | |
49 return no_max_size && | |
50 window->GetProperty(aura::client::kCanMaximizeKey) && | |
51 window->GetProperty(aura::client::kCanResizeKey); | |
52 } | |
53 | |
54 } // namespace | |
55 | |
56 class AthenaContainerLayoutManager : public aura::LayoutManager { | |
57 public: | |
58 AthenaContainerLayoutManager(); | |
59 ~AthenaContainerLayoutManager() override; | |
60 | |
61 private: | |
62 // aura::LayoutManager: | |
63 void OnWindowResized() override; | |
64 void OnWindowAddedToLayout(aura::Window* child) override; | |
65 void OnWillRemoveWindowFromLayout(aura::Window* child) override; | |
66 void OnWindowRemovedFromLayout(aura::Window* child) override; | |
67 void OnChildWindowVisibilityChanged(aura::Window* child, | |
68 bool visible) override; | |
69 void SetChildBounds(aura::Window* child, | |
70 const gfx::Rect& requested_bounds) override; | |
71 | |
72 DISALLOW_COPY_AND_ASSIGN(AthenaContainerLayoutManager); | |
73 }; | |
74 | |
75 AthenaContainerLayoutManager::AthenaContainerLayoutManager() { | |
76 } | |
77 | |
78 AthenaContainerLayoutManager::~AthenaContainerLayoutManager() { | |
79 } | |
80 | |
81 void AthenaContainerLayoutManager::OnWindowResized() { | |
82 // Resize all the existing windows. | |
83 const aura::Window::Windows& list = | |
84 instance->window_list_provider_->GetWindowList(); | |
85 const gfx::Size work_area = | |
86 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().size(); | |
87 bool is_splitview = instance->split_view_controller_->IsSplitViewModeActive(); | |
88 gfx::Size split_size; | |
89 if (is_splitview) { | |
90 CHECK(instance->split_view_controller_->left_window()); | |
91 split_size = | |
92 instance->split_view_controller_->left_window()->bounds().size(); | |
93 } | |
94 | |
95 for (aura::Window::Windows::const_iterator iter = list.begin(); | |
96 iter != list.end(); | |
97 ++iter) { | |
98 aura::Window* window = *iter; | |
99 if (is_splitview) { | |
100 if (window == instance->split_view_controller_->left_window()) | |
101 window->SetBounds(gfx::Rect(split_size)); | |
102 else if (window == instance->split_view_controller_->right_window()) { | |
103 window->SetBounds( | |
104 gfx::Rect(gfx::Point(split_size.width(), 0), split_size)); | |
105 } else if (CanWindowMaximize(window)) | |
106 window->SetBounds(gfx::Rect(work_area)); | |
107 } else if (CanWindowMaximize(window)) { | |
108 window->SetBounds(gfx::Rect(work_area)); | |
109 } | |
110 } | |
111 } | |
112 | |
113 void AthenaContainerLayoutManager::OnWindowAddedToLayout(aura::Window* child) { | |
114 // TODO(oshima): Split view modes needs to take the transient window into | |
115 // account. | |
116 if (wm::GetTransientParent(child)) { | |
117 wm::TransientWindowManager::Get(child) | |
118 ->set_parent_controls_visibility(true); | |
119 } | |
120 } | |
121 | |
122 void AthenaContainerLayoutManager::OnWillRemoveWindowFromLayout( | |
123 aura::Window* child) { | |
124 } | |
125 | |
126 void AthenaContainerLayoutManager::OnWindowRemovedFromLayout( | |
127 aura::Window* child) { | |
128 } | |
129 | |
130 void AthenaContainerLayoutManager::OnChildWindowVisibilityChanged( | |
131 aura::Window* child, | |
132 bool visible) { | |
133 if (visible && CanWindowMaximize(child)) { | |
134 // Make sure we're resizing a window that actually exists in the window list | |
135 // to avoid resizing the divider in the split mode. | |
136 if(instance->window_list_provider_->IsWindowInList(child)) { | |
137 child->SetBounds( | |
138 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area()); | |
139 } | |
140 } | |
141 } | |
142 | |
143 void AthenaContainerLayoutManager::SetChildBounds( | |
144 aura::Window* child, | |
145 const gfx::Rect& requested_bounds) { | |
146 if (!requested_bounds.IsEmpty()) | |
147 SetChildBoundsDirect(child, requested_bounds); | |
148 } | |
149 | |
150 WindowManagerImpl::WindowManagerImpl() { | |
151 ScreenManager::ContainerParams params("DefaultContainer", CP_DEFAULT); | |
152 params.can_activate_children = true; | |
153 params.default_parent = true; | |
154 params.modal_container_priority = CP_SYSTEM_MODAL; | |
155 container_.reset(ScreenManager::Get()->CreateContainer(params)); | |
156 container_->SetLayoutManager(new AthenaContainerLayoutManager); | |
157 container_->AddObserver(this); | |
158 window_list_provider_.reset(new WindowListProviderImpl(container_.get())); | |
159 window_list_provider_->AddObserver(this); | |
160 split_view_controller_.reset( | |
161 new SplitViewController(container_.get(), window_list_provider_.get())); | |
162 AddObserver(split_view_controller_.get()); | |
163 title_drag_controller_.reset(new TitleDragController(container_.get(), this)); | |
164 wm_state_.reset(new wm::WMState()); | |
165 aura::client::ActivationClient* activation_client = | |
166 aura::client::GetActivationClient(container_->GetRootWindow()); | |
167 DCHECK(container_->GetRootWindow()); | |
168 DCHECK(activation_client); | |
169 shadow_controller_.reset(new wm::ShadowController(activation_client)); | |
170 instance = this; | |
171 InstallAccelerators(); | |
172 } | |
173 | |
174 WindowManagerImpl::~WindowManagerImpl() { | |
175 window_list_provider_->RemoveObserver(this); | |
176 overview_.reset(); | |
177 RemoveObserver(split_view_controller_.get()); | |
178 split_view_controller_.reset(); | |
179 window_list_provider_.reset(); | |
180 if (container_) | |
181 container_->RemoveObserver(this); | |
182 | |
183 // |title_drag_controller_| needs to be reset before |container_|. | |
184 title_drag_controller_.reset(); | |
185 container_.reset(); | |
186 instance = nullptr; | |
187 } | |
188 | |
189 void WindowManagerImpl::ToggleSplitView() { | |
190 if (IsOverviewModeActive()) | |
191 return; | |
192 | |
193 if (split_view_controller_->IsSplitViewModeActive()) { | |
194 split_view_controller_->DeactivateSplitMode(); | |
195 FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnSplitViewModeExit()); | |
196 // Relayout so that windows are maximzied. | |
197 container_->layout_manager()->OnWindowResized(); | |
198 } else if (split_view_controller_->CanActivateSplitViewMode()) { | |
199 FOR_EACH_OBSERVER(WindowManagerObserver, | |
200 observers_, | |
201 OnSplitViewModeEnter()); | |
202 split_view_controller_->ActivateSplitMode(nullptr, nullptr, nullptr); | |
203 } | |
204 } | |
205 | |
206 void WindowManagerImpl::EnterOverview() { | |
207 if (IsOverviewModeActive()) | |
208 return; | |
209 | |
210 FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnOverviewModeEnter()); | |
211 | |
212 // Note: The window_list_provider_ resembles the exact window list of the | |
213 // container, so no re-stacking is required before showing the OverviewMode. | |
214 overview_ = WindowOverviewMode::Create( | |
215 container_.get(), window_list_provider_.get(), | |
216 split_view_controller_.get(), this); | |
217 AcceleratorManager::Get()->RegisterAccelerator(kEscAcceleratorData, this); | |
218 } | |
219 | |
220 void WindowManagerImpl::ExitOverview() { | |
221 if (!IsOverviewModeActive()) | |
222 return; | |
223 | |
224 ExitOverviewNoActivate(); | |
225 | |
226 // Activate the window which was active prior to entering overview. | |
227 const aura::Window::Windows windows = window_list_provider_->GetWindowList(); | |
228 if (!windows.empty()) { | |
229 aura::Window* window_to_activate = windows.back(); | |
230 | |
231 // Show the window in case the exit overview animation has finished and | |
232 // |window| was hidden. | |
233 window_to_activate->Show(); | |
234 wm::ActivateWindow(window_to_activate); | |
235 } | |
236 } | |
237 | |
238 bool WindowManagerImpl::IsOverviewModeActive() { | |
239 return overview_; | |
240 } | |
241 | |
242 void WindowManagerImpl::ExitOverviewNoActivate() { | |
243 if (!IsOverviewModeActive()) | |
244 return; | |
245 | |
246 overview_.reset(); | |
247 FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnOverviewModeExit()); | |
248 AcceleratorManager::Get()->UnregisterAccelerator(kEscAcceleratorData, this); | |
249 } | |
250 | |
251 void WindowManagerImpl::InstallAccelerators() { | |
252 const AcceleratorData accelerator_data[] = { | |
253 {TRIGGER_ON_PRESS, | |
254 ui::VKEY_F6, | |
255 ui::EF_NONE, | |
256 CMD_TOGGLE_OVERVIEW, | |
257 AF_NONE}, | |
258 {TRIGGER_ON_PRESS, | |
259 ui::VKEY_F6, | |
260 ui::EF_CONTROL_DOWN, | |
261 CMD_TOGGLE_SPLIT_VIEW, | |
262 AF_NONE}, | |
263 // Debug | |
264 {TRIGGER_ON_PRESS, | |
265 ui::VKEY_6, | |
266 ui::EF_NONE, | |
267 CMD_TOGGLE_OVERVIEW, | |
268 AF_NONE | AF_DEBUG}, | |
269 }; | |
270 AcceleratorManager::Get()->RegisterAccelerators( | |
271 accelerator_data, arraysize(accelerator_data), this); | |
272 } | |
273 | |
274 void WindowManagerImpl::AddObserver(WindowManagerObserver* observer) { | |
275 observers_.AddObserver(observer); | |
276 } | |
277 | |
278 void WindowManagerImpl::RemoveObserver(WindowManagerObserver* observer) { | |
279 observers_.RemoveObserver(observer); | |
280 } | |
281 | |
282 void WindowManagerImpl::ToggleSplitViewForTest() { | |
283 ToggleSplitView(); | |
284 } | |
285 | |
286 WindowListProvider* WindowManagerImpl::GetWindowListProvider() { | |
287 return window_list_provider_.get(); | |
288 } | |
289 | |
290 void WindowManagerImpl::OnSelectWindow(aura::Window* window) { | |
291 ExitOverviewNoActivate(); | |
292 | |
293 // Show the window in case the exit overview animation has finished and | |
294 // |window| was hidden. | |
295 window->Show(); | |
296 | |
297 wm::ActivateWindow(window); | |
298 | |
299 if (split_view_controller_->IsSplitViewModeActive()) { | |
300 split_view_controller_->DeactivateSplitMode(); | |
301 FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnSplitViewModeExit()); | |
302 } | |
303 // If |window| does not have the size of the work-area, then make sure it is | |
304 // resized. | |
305 const gfx::Size work_area = | |
306 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().size(); | |
307 | |
308 // Resize to the screen bounds only if the window is maximize-able, and | |
309 // is not already maximized | |
310 if (window->GetTargetBounds().size() != work_area && | |
311 CanWindowMaximize(window)) { | |
312 const gfx::Rect& window_bounds = window->bounds(); | |
313 const gfx::Rect desired_bounds(work_area); | |
314 gfx::Transform transform; | |
315 transform.Translate(desired_bounds.x() - window_bounds.x(), | |
316 desired_bounds.y() - window_bounds.y()); | |
317 transform.Scale(desired_bounds.width() / window_bounds.width(), | |
318 desired_bounds.height() / window_bounds.height()); | |
319 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); | |
320 settings.SetPreemptionStrategy( | |
321 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | |
322 settings.AddObserver( | |
323 new ui::ClosureAnimationObserver(base::Bind(&SetWindowState, | |
324 base::Unretained(window), | |
325 desired_bounds, | |
326 gfx::Transform()))); | |
327 window->SetTransform(transform); | |
328 } | |
329 } | |
330 | |
331 void WindowManagerImpl::OnSelectSplitViewWindow(aura::Window* left, | |
332 aura::Window* right, | |
333 aura::Window* to_activate) { | |
334 ExitOverviewNoActivate(); | |
335 FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnSplitViewModeEnter()); | |
336 split_view_controller_->ActivateSplitMode(left, right, to_activate); | |
337 } | |
338 | |
339 void WindowManagerImpl::OnWindowStackingChangedInList() { | |
340 } | |
341 | |
342 void WindowManagerImpl::OnWindowAddedToList(aura::Window* child) { | |
343 if (instance->split_view_controller_->IsSplitViewModeActive() && | |
344 !instance->IsOverviewModeActive()) { | |
345 instance->split_view_controller_->ReplaceWindow( | |
346 instance->split_view_controller_->left_window(), child); | |
347 } else { | |
348 gfx::Size size = | |
349 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().size(); | |
350 child->SetBounds(gfx::Rect(size)); | |
351 } | |
352 | |
353 if (instance->IsOverviewModeActive()) { | |
354 // TODO(pkotwicz|oshima). Creating a new window should only exit overview | |
355 // mode if the new window is activated. crbug.com/415266 | |
356 instance->OnSelectWindow(child); | |
357 } | |
358 } | |
359 | |
360 void WindowManagerImpl::OnWindowRemovedFromList(aura::Window* removed_window, | |
361 int index) { | |
362 aura::Window::Windows windows = window_list_provider_->GetWindowList(); | |
363 DCHECK(!window_list_provider_->IsWindowInList(removed_window)); | |
364 DCHECK_LE(index, static_cast<int>(windows.size())); | |
365 | |
366 // Splitted windows are handled in SplitViewController. | |
367 if (split_view_controller_->IsSplitViewModeActive()) | |
368 return; | |
369 | |
370 // In overview mode, windows are handled in WindowOverviewMode class. | |
371 if (!IsOverviewModeActive()) | |
372 return; | |
373 | |
374 // Shows the next window if the removed window was top. | |
375 if (!windows.empty() && index == static_cast<int>(windows.size())) { | |
376 aura::Window* next_window = windows.back(); | |
377 next_window->Show(); | |
378 | |
379 // Don't activate the window here, since it should be done in focus manager. | |
380 } | |
381 } | |
382 | |
383 void WindowManagerImpl::OnWindowDestroying(aura::Window* window) { | |
384 if (window == container_) | |
385 container_.reset(); | |
386 } | |
387 | |
388 bool WindowManagerImpl::IsCommandEnabled(int command_id) const { | |
389 return true; | |
390 } | |
391 | |
392 bool WindowManagerImpl::OnAcceleratorFired(int command_id, | |
393 const ui::Accelerator& accelerator) { | |
394 switch (command_id) { | |
395 case CMD_EXIT_OVERVIEW: | |
396 ExitOverview(); | |
397 break; | |
398 case CMD_TOGGLE_OVERVIEW: | |
399 if (IsOverviewModeActive()) | |
400 ExitOverview(); | |
401 else | |
402 EnterOverview(); | |
403 break; | |
404 case CMD_TOGGLE_SPLIT_VIEW: | |
405 ToggleSplitView(); | |
406 break; | |
407 } | |
408 return true; | |
409 } | |
410 | |
411 aura::Window* WindowManagerImpl::GetWindowBehind(aura::Window* window) { | |
412 const aura::Window::Windows& windows = window_list_provider_->GetWindowList(); | |
413 aura::Window::Windows::const_reverse_iterator iter = | |
414 std::find(windows.rbegin(), windows.rend(), window); | |
415 CHECK(iter != windows.rend()); | |
416 ++iter; | |
417 aura::Window* behind = nullptr; | |
418 if (iter != windows.rend()) | |
419 behind = *iter++; | |
420 | |
421 if (split_view_controller_->IsSplitViewModeActive()) { | |
422 aura::Window* left = split_view_controller_->left_window(); | |
423 aura::Window* right = split_view_controller_->right_window(); | |
424 CHECK(window == left || window == right); | |
425 if (behind == left || behind == right) | |
426 behind = (iter == windows.rend()) ? nullptr : *iter; | |
427 } | |
428 | |
429 return behind; | |
430 } | |
431 | |
432 void WindowManagerImpl::OnTitleDragStarted(aura::Window* window) { | |
433 aura::Window* next_window = GetWindowBehind(window); | |
434 if (!next_window) | |
435 return; | |
436 // Make sure |window| is active. | |
437 wm::ActivateWindow(window); | |
438 | |
439 // Make sure |next_window| is visibile. | |
440 next_window->Show(); | |
441 | |
442 // Position |next_window| correctly (left aligned if it's larger than | |
443 // |window|, and center aligned otherwise). | |
444 int dx = window->bounds().x() - next_window->bounds().x(); | |
445 if (next_window->bounds().width() < window->bounds().width()) | |
446 dx -= (next_window->bounds().width() - window->bounds().width()) / 2; | |
447 | |
448 if (dx) { | |
449 gfx::Transform transform; | |
450 transform.Translate(dx, 0); | |
451 next_window->SetTransform(transform); | |
452 } | |
453 } | |
454 | |
455 void WindowManagerImpl::OnTitleDragCompleted(aura::Window* window) { | |
456 aura::Window* next_window = GetWindowBehind(window); | |
457 if (!next_window) | |
458 return; | |
459 if (split_view_controller_->IsSplitViewModeActive()) { | |
460 split_view_controller_->ReplaceWindow(window, next_window); | |
461 } else { | |
462 ui::ScopedLayerAnimationSettings | |
463 settings(next_window->layer()->GetAnimator()); | |
464 settings.AddObserver(new ui::ClosureAnimationObserver( | |
465 base::Bind(&SetWindowState, | |
466 base::Unretained(next_window), | |
467 window->bounds(), | |
468 gfx::Transform()))); | |
469 | |
470 gfx::Transform transform; | |
471 transform.Scale(window->bounds().width() / next_window->bounds().width(), | |
472 window->bounds().height() / next_window->bounds().height()); | |
473 transform.Translate(window->bounds().x() - next_window->bounds().x(), 0); | |
474 next_window->SetTransform(transform); | |
475 | |
476 wm::ActivateWindow(next_window); | |
477 } | |
478 window->Hide(); | |
479 } | |
480 | |
481 void WindowManagerImpl::OnTitleDragCanceled(aura::Window* window) { | |
482 aura::Window* next_window = GetWindowBehind(window); | |
483 if (!next_window) | |
484 return; | |
485 next_window->SetTransform(gfx::Transform()); | |
486 next_window->Hide(); | |
487 } | |
488 | |
489 // static | |
490 WindowManager* WindowManager::Create() { | |
491 DCHECK(!instance); | |
492 new WindowManagerImpl; | |
493 DCHECK(instance); | |
494 return instance; | |
495 } | |
496 | |
497 // static | |
498 void WindowManager::Shutdown() { | |
499 DCHECK(instance); | |
500 delete instance; | |
501 DCHECK(!instance); | |
502 } | |
503 | |
504 // static | |
505 WindowManager* WindowManager::Get() { | |
506 DCHECK(instance); | |
507 return instance; | |
508 } | |
509 | |
510 } // namespace athena | |
OLD | NEW |