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.
5 #include "ash/wm/maximize_mode/maximize_mode_window_manager.h"
7 #include "ash/ash_switches.h"
8 #include "ash/root_window_controller.h"
10 #include "ash/shell_window_ids.h"
11 #include "ash/wm/maximize_mode/maximize_mode_window_state.h"
12 #include "ash/wm/maximize_mode/workspace_backdrop_delegate.h"
13 #include "ash/wm/mru_window_tracker.h"
14 #include "ash/wm/overview/window_selector_controller.h"
15 #include "ash/wm/window_state.h"
16 #include "ash/wm/window_util.h"
17 #include "ash/wm/wm_event.h"
18 #include "ash/wm/workspace_controller.h"
19 #include "base/command_line.h"
20 #include "ui/aura/client/aura_constants.h"
21 #include "ui/aura/window.h"
22 #include "ui/gfx/screen.h"
28 // The height of the area in which a touch operation leads to exiting the
30 const int kLeaveFullScreenAreaHeightInPixel
= 2;
32 // Exits overview mode if it is currently active.
33 void CancelOverview() {
34 WindowSelectorController
* controller
=
35 Shell::GetInstance()->window_selector_controller();
36 if (controller
&& controller
->IsSelecting())
37 controller
->OnSelectionEnded();
42 MaximizeModeWindowManager::~MaximizeModeWindowManager() {
43 // Overview mode needs to be ended before exiting maximize mode to prevent
44 // transforming windows which are currently in
45 // overview: http://crbug.com/366605
48 Shell::GetInstance()->RemovePreTargetHandler(this);
49 Shell::GetInstance()->RemoveShellObserver(this);
50 Shell::GetScreen()->RemoveObserver(this);
51 EnableBackdropBehindTopWindowOnEachDisplay(false);
52 RemoveWindowCreationObservers();
56 int MaximizeModeWindowManager::GetNumberOfManagedWindows() {
57 return window_state_map_
.size();
60 void MaximizeModeWindowManager::AddWindow(aura::Window
* window
) {
61 // Only add the window if it is a direct dependent of a container window
62 // and not yet tracked.
63 if (!ShouldHandleWindow(window
) ||
64 window_state_map_
.find(window
) != window_state_map_
.end() ||
65 !IsContainerWindow(window
->parent())) {
69 MaximizeAndTrackWindow(window
);
72 void MaximizeModeWindowManager::WindowStateDestroyed(aura::Window
* window
) {
73 // At this time ForgetWindow() should already have been called. If not,
74 // someone else must have replaced the "window manager's state object".
75 DCHECK(!window
->HasObserver(this));
77 WindowToState::iterator it
= window_state_map_
.find(window
);
78 DCHECK(it
!= window_state_map_
.end());
79 window_state_map_
.erase(it
);
82 void MaximizeModeWindowManager::OnOverviewModeStarting() {
83 if (backdrops_hidden_
)
86 EnableBackdropBehindTopWindowOnEachDisplay(false);
87 SetDeferBoundsUpdates(true);
88 backdrops_hidden_
= true;
91 void MaximizeModeWindowManager::OnOverviewModeEnded() {
92 if (!backdrops_hidden_
)
95 backdrops_hidden_
= false;
96 EnableBackdropBehindTopWindowOnEachDisplay(true);
97 SetDeferBoundsUpdates(false);
100 void MaximizeModeWindowManager::OnWindowDestroying(aura::Window
* window
) {
101 if (IsContainerWindow(window
)) {
102 // container window can be removed on display destruction.
103 window
->RemoveObserver(this);
104 observed_container_windows_
.erase(window
);
106 // If a known window gets destroyed we need to remove all knowledge about
108 ForgetWindow(window
);
112 void MaximizeModeWindowManager::OnWindowAdded(aura::Window
* window
) {
113 // A window can get removed and then re-added by a drag and drop operation.
114 if (IsContainerWindow(window
->parent()) &&
115 window_state_map_
.find(window
) == window_state_map_
.end()) {
116 MaximizeAndTrackWindow(window
);
117 // When the state got added, the "WM_EVENT_ADDED_TO_WORKSPACE" event got
118 // already sent and we have to notify our state again.
119 if (window_state_map_
.find(window
) != window_state_map_
.end()) {
120 wm::WMEvent
event(wm::WM_EVENT_ADDED_TO_WORKSPACE
);
121 wm::GetWindowState(window
)->OnWMEvent(&event
);
126 void MaximizeModeWindowManager::OnWindowPropertyChanged(aura::Window
* window
,
129 // Stop managing |window| if the always-on-top property is added.
130 if (key
== aura::client::kAlwaysOnTopKey
&&
131 window
->GetProperty(aura::client::kAlwaysOnTopKey
)) {
132 ForgetWindow(window
);
136 void MaximizeModeWindowManager::OnWindowBoundsChanged(
137 aura::Window
* window
,
138 const gfx::Rect
& old_bounds
,
139 const gfx::Rect
& new_bounds
) {
140 if (!IsContainerWindow(window
))
142 // Reposition all non maximizeable windows.
143 for (WindowToState::iterator it
= window_state_map_
.begin();
144 it
!= window_state_map_
.end();
146 it
->second
->UpdateWindowPosition(wm::GetWindowState(it
->first
));
150 void MaximizeModeWindowManager::OnDisplayAdded(const gfx::Display
& display
) {
151 DisplayConfigurationChanged();
154 void MaximizeModeWindowManager::OnDisplayRemoved(const gfx::Display
& display
) {
155 DisplayConfigurationChanged();
158 void MaximizeModeWindowManager::OnDisplayMetricsChanged(const gfx::Display
&,
160 // Nothing to do here.
163 void MaximizeModeWindowManager::OnTouchEvent(ui::TouchEvent
* event
) {
164 if (event
->type() != ui::ET_TOUCH_PRESSED
)
167 // Find the active window (from the primary screen) to un-fullscreen.
168 aura::Window
* window
= wm::GetActiveWindow();
172 wm::WindowState
* window_state
= wm::GetWindowState(window
);
173 if (!window_state
->IsFullscreen() || window_state
->in_immersive_fullscreen())
176 // Test that the touch happened in the top or bottom lines.
178 if (y
>= kLeaveFullScreenAreaHeightInPixel
&&
179 y
< (window
->bounds().height() - kLeaveFullScreenAreaHeightInPixel
)) {
183 // Leave full screen mode.
184 event
->StopPropagation();
185 wm::WMEvent
toggle_fullscreen(wm::WM_EVENT_TOGGLE_FULLSCREEN
);
186 window_state
->OnWMEvent(&toggle_fullscreen
);
189 MaximizeModeWindowManager::MaximizeModeWindowManager()
190 : backdrops_hidden_(false) {
191 // The overview mode needs to be ended before the maximize mode is started. To
192 // guarantee the proper order, it will be turned off from here.
195 MaximizeAllWindows();
196 AddWindowCreationObservers();
197 EnableBackdropBehindTopWindowOnEachDisplay(true);
198 Shell::GetScreen()->AddObserver(this);
199 Shell::GetInstance()->AddShellObserver(this);
200 Shell::GetInstance()->AddPreTargetHandler(this);
203 void MaximizeModeWindowManager::MaximizeAllWindows() {
204 MruWindowTracker::WindowList windows
= ash::Shell::GetInstance()->
205 mru_window_tracker()->BuildWindowListIgnoreModal();
206 // Add all existing Mru windows.
207 for (MruWindowTracker::WindowList::iterator window
= windows
.begin();
208 window
!= windows
.end(); ++window
) {
209 MaximizeAndTrackWindow(*window
);
213 void MaximizeModeWindowManager::RestoreAllWindows() {
214 while (window_state_map_
.size())
215 ForgetWindow(window_state_map_
.begin()->first
);
218 void MaximizeModeWindowManager::SetDeferBoundsUpdates(
219 bool defer_bounds_updates
) {
220 for (WindowToState::iterator it
= window_state_map_
.begin();
221 it
!= window_state_map_
.end();
223 it
->second
->SetDeferBoundsUpdates(defer_bounds_updates
);
227 void MaximizeModeWindowManager::MaximizeAndTrackWindow(
228 aura::Window
* window
) {
229 if (!ShouldHandleWindow(window
))
232 DCHECK(window_state_map_
.find(window
) == window_state_map_
.end());
233 window
->AddObserver(this);
235 // We create and remember a maximize mode state which will attach itself to
236 // the provided state object.
237 window_state_map_
[window
] = new MaximizeModeWindowState(window
, this);
240 void MaximizeModeWindowManager::ForgetWindow(aura::Window
* window
) {
241 WindowToState::iterator it
= window_state_map_
.find(window
);
243 // The following DCHECK could fail if our window state object was destroyed
244 // earlier by someone else. However - at this point there is no other client
245 // which replaces the state object and therefore this should not happen.
246 DCHECK(it
!= window_state_map_
.end());
247 window
->RemoveObserver(this);
249 // By telling the state object to revert, it will switch back the old
250 // State object and destroy itself, calling WindowStateDestroyed().
251 it
->second
->LeaveMaximizeMode(wm::GetWindowState(it
->first
));
252 DCHECK(window_state_map_
.find(window
) == window_state_map_
.end());
255 bool MaximizeModeWindowManager::ShouldHandleWindow(aura::Window
* window
) {
258 // Windows with the always-on-top property should be free-floating and thus
259 // not managed by us.
260 if (window
->GetProperty(aura::client::kAlwaysOnTopKey
))
263 // Windows in the dock should not be managed by us.
264 if (wm::GetWindowState(window
)->IsDocked())
267 return window
->type() == ui::wm::WINDOW_TYPE_NORMAL
;
270 void MaximizeModeWindowManager::AddWindowCreationObservers() {
271 DCHECK(observed_container_windows_
.empty());
272 // Observe window activations/creations in the default containers on all root
274 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
275 for (aura::Window::Windows::const_iterator iter
= root_windows
.begin();
276 iter
!= root_windows
.end(); ++iter
) {
277 aura::Window
* container
=
278 Shell::GetContainer(*iter
, kShellWindowId_DefaultContainer
);
279 DCHECK(observed_container_windows_
.find(container
) ==
280 observed_container_windows_
.end());
281 container
->AddObserver(this);
282 observed_container_windows_
.insert(container
);
286 void MaximizeModeWindowManager::RemoveWindowCreationObservers() {
287 for (std::set
<aura::Window
*>::iterator iter
=
288 observed_container_windows_
.begin();
289 iter
!= observed_container_windows_
.end(); ++iter
) {
290 (*iter
)->RemoveObserver(this);
292 observed_container_windows_
.clear();
295 void MaximizeModeWindowManager::DisplayConfigurationChanged() {
296 EnableBackdropBehindTopWindowOnEachDisplay(false);
297 RemoveWindowCreationObservers();
298 AddWindowCreationObservers();
299 EnableBackdropBehindTopWindowOnEachDisplay(true);
302 bool MaximizeModeWindowManager::IsContainerWindow(aura::Window
* window
) {
303 return observed_container_windows_
.find(window
) !=
304 observed_container_windows_
.end();
307 void MaximizeModeWindowManager::EnableBackdropBehindTopWindowOnEachDisplay(
309 // This function should be a no-op if backdrops have been disabled.
310 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
311 switches::kAshDisableMaximizeModeWindowBackdrop
)) {
315 if (backdrops_hidden_
)
318 // Inform the WorkspaceLayoutManager that we want to show a backdrop behind
319 // the topmost window of its container.
320 Shell::RootWindowControllerList controllers
=
321 Shell::GetAllRootWindowControllers();
322 for (Shell::RootWindowControllerList::iterator iter
= controllers
.begin();
323 iter
!= controllers
.end(); ++iter
) {
324 RootWindowController
* controller
= *iter
;
325 aura::Window
* container
= Shell::GetContainer(
326 controller
->GetRootWindow(), kShellWindowId_DefaultContainer
);
327 controller
->workspace_controller()->SetMaximizeBackdropDelegate(
328 scoped_ptr
<WorkspaceLayoutManagerDelegate
>(
329 enable
? new WorkspaceBackdropDelegate(container
) : NULL
));