OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ash/wm/default_state.h" | 5 #include "ash/wm/default_state.h" |
6 | 6 |
7 #include "ash/display/display_controller.h" | |
7 #include "ash/screen_util.h" | 8 #include "ash/screen_util.h" |
8 #include "ash/shell.h" | 9 #include "ash/shell.h" |
9 #include "ash/wm/coordinate_conversion.h" | 10 #include "ash/wm/coordinate_conversion.h" |
11 #include "ash/wm/window_animations.h" | |
10 #include "ash/wm/window_state.h" | 12 #include "ash/wm/window_state.h" |
13 #include "ash/wm/window_state_delegate.h" | |
14 #include "ash/wm/window_util.h" | |
15 #include "ash/wm/workspace/workspace_window_resizer.h" | |
16 #include "ui/aura/client/aura_constants.h" | |
11 #include "ui/aura/window.h" | 17 #include "ui/aura/window.h" |
12 #include "ui/aura/window_delegate.h" | 18 #include "ui/aura/window_delegate.h" |
13 #include "ui/gfx/display.h" | 19 #include "ui/gfx/display.h" |
14 #include "ui/gfx/rect.h" | 20 #include "ui/gfx/rect.h" |
15 | 21 |
16 namespace ash { | 22 namespace ash { |
17 namespace wm { | 23 namespace wm { |
24 namespace { | |
25 | |
26 gfx::Rect BoundsWithScreenEdgeVisible( | |
27 aura::Window* window, | |
28 const gfx::Rect& restore_bounds) { | |
29 gfx::Rect max_bounds = | |
30 ash::ScreenUtil::GetMaximizedWindowBoundsInParent(window); | |
31 // If the restore_bounds are more than 1 grid step away from the size the | |
32 // window would be when maximized, inset it. | |
33 max_bounds.Inset(ash::internal::WorkspaceWindowResizer::kScreenEdgeInset, | |
34 ash::internal::WorkspaceWindowResizer::kScreenEdgeInset); | |
35 if (restore_bounds.Contains(max_bounds)) | |
36 return max_bounds; | |
37 return restore_bounds; | |
38 } | |
39 | |
40 void MoveToDisplayForRestore(wm::WindowState* window_state) { | |
41 if (!window_state->HasRestoreBounds()) | |
42 return; | |
43 const gfx::Rect& restore_bounds = window_state->GetRestoreBoundsInScreen(); | |
44 | |
45 // Move only if the restore bounds is outside of | |
46 // the display. There is no information about in which | |
47 // display it should be restored, so this is best guess. | |
48 // TODO(oshima): Restore information should contain the | |
49 // work area information like WindowResizer does for the | |
50 // last window location. | |
51 gfx::Rect display_area = Shell::GetScreen()->GetDisplayNearestWindow( | |
52 window_state->window()).bounds(); | |
53 | |
54 if (!display_area.Intersects(restore_bounds)) { | |
55 const gfx::Display& display = | |
56 Shell::GetScreen()->GetDisplayMatching(restore_bounds); | |
57 DisplayController* display_controller = | |
58 Shell::GetInstance()->display_controller(); | |
59 aura::Window* new_root = | |
60 display_controller->GetRootWindowForDisplayId(display.id()); | |
61 if (new_root != window_state->window()->GetRootWindow()) { | |
62 aura::Window* new_container = | |
63 Shell::GetContainer(new_root, window_state->window()->parent()->id()); | |
64 new_container->AddChild(window_state->window()); | |
65 } | |
66 } | |
67 } | |
68 | |
69 } // namespace; | |
18 | 70 |
19 DefaultState::DefaultState() {} | 71 DefaultState::DefaultState() {} |
20 DefaultState::~DefaultState() {} | 72 DefaultState::~DefaultState() {} |
21 | 73 |
22 void DefaultState::OnWMEvent(WindowState* window_state, | 74 void DefaultState::OnWMEvent(WindowState* window_state, |
23 WMEvent event) { | 75 WMEvent event) { |
76 if (ProcessCompoundEvents(window_state, event)) | |
77 return; | |
78 | |
79 WindowShowType next_show_type = SHOW_TYPE_NORMAL; | |
80 switch (event) { | |
81 case NORMAL: | |
82 next_show_type = SHOW_TYPE_NORMAL; | |
83 break; | |
84 case MAXIMIZE: | |
85 next_show_type = SHOW_TYPE_MAXIMIZED; | |
86 break; | |
87 case MINIMIZE: | |
88 next_show_type = SHOW_TYPE_MINIMIZED; | |
89 break; | |
90 case FULLSCREEN: | |
91 next_show_type = SHOW_TYPE_FULLSCREEN; | |
92 break; | |
93 case SNAP_LEFT: | |
94 next_show_type = SHOW_TYPE_LEFT_SNAPPED; | |
95 break; | |
96 case SNAP_RIGHT: | |
97 next_show_type = SHOW_TYPE_RIGHT_SNAPPED; | |
98 break; | |
99 case TOGGLE_MAXIMIZE_CAPTION: | |
100 case TOGGLE_MAXIMIZE: | |
101 case TOGGLE_VERTICAL_MAXIMIZE: | |
102 case TOGGLE_HORIZONTAL_MAXIMIZE: | |
103 case TOGGLE_FULLSCREEN: | |
104 NOTREACHED() << "Compound event should not reach here:" << event; | |
105 return; | |
106 } | |
107 | |
108 WindowShowType current = window_state->window_show_type(); | |
109 if (current != next_show_type) { | |
110 window_state->UpdateWindowShowType(next_show_type); | |
111 window_state->NotifyPreShowTypeChange(current); | |
112 // TODO(oshima): Make docked window a state. | |
pkotwicz
2014/02/12 18:20:18
Is there a plan to make panel windows and docked w
oshima
2014/02/12 19:56:16
The plan is to replace DefaultState with state mac
| |
113 if (!window_state->IsDocked() && !window_state->IsPanel()) | |
114 UpdateBoundsFromShowType(window_state, current); | |
115 window_state->NotifyPostShowTypeChange(current); | |
116 } | |
117 }; | |
118 | |
119 // static | |
120 bool DefaultState::ProcessCompoundEvents(WindowState* window_state, | |
121 WMEvent event) { | |
24 aura::Window* window = window_state->window(); | 122 aura::Window* window = window_state->window(); |
25 | 123 |
26 switch (event) { | 124 switch (event) { |
27 case TOGGLE_MAXIMIZE_CAPTION: | 125 case TOGGLE_MAXIMIZE_CAPTION: |
28 if (window_state->IsFullscreen()) { | 126 if (window_state->IsFullscreen()) { |
29 window_state->ToggleFullscreen(); | 127 window_state->ToggleFullscreen(); |
30 } else if (window_state->IsMaximized()) { | 128 } else if (window_state->IsMaximized()) { |
31 window_state->Restore(); | 129 window_state->Restore(); |
32 } else if (window_state->IsNormalShowType() || | 130 } else if (window_state->IsNormalShowType() || |
33 window_state->IsSnapped()) { | 131 window_state->IsSnapped()) { |
34 if (window_state->CanMaximize()) | 132 if (window_state->CanMaximize()) |
35 window_state->Maximize(); | 133 window_state->Maximize(); |
36 } | 134 } |
37 break; | 135 return true; |
38 | |
39 case TOGGLE_MAXIMIZE: | 136 case TOGGLE_MAXIMIZE: |
40 if (window_state->IsFullscreen()) | 137 if (window_state->IsFullscreen()) |
41 window_state->ToggleFullscreen(); | 138 window_state->ToggleFullscreen(); |
42 else if (window_state->IsMaximized()) | 139 else if (window_state->IsMaximized()) |
43 window_state->Restore(); | 140 window_state->Restore(); |
44 else if (window_state->CanMaximize()) | 141 else if (window_state->CanMaximize()) |
45 window_state->Maximize(); | 142 window_state->Maximize(); |
46 break; | 143 return true; |
47 | |
48 case TOGGLE_VERTICAL_MAXIMIZE: { | 144 case TOGGLE_VERTICAL_MAXIMIZE: { |
49 gfx::Rect work_area = | 145 gfx::Rect work_area = |
50 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window); | 146 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window); |
51 | 147 |
52 // Maximize vertically if: | 148 // Maximize vertically if: |
53 // - The window does not have a max height defined. | 149 // - The window does not have a max height defined. |
54 // - The window has the normal show type. Snapped windows are excluded | 150 // - The window has the normal show type. Snapped windows are excluded |
55 // because they are already maximized vertically and reverting to the | 151 // because they are already maximized vertically and reverting to the |
56 // restored bounds looks weird. | 152 // restored bounds looks weird. |
57 if (window->delegate()->GetMaximumSize().height() != 0 || | 153 if (window->delegate()->GetMaximumSize().height() != 0 || |
58 !window_state->IsNormalShowType()) { | 154 !window_state->IsNormalShowType()) { |
59 return; | 155 return true; |
60 } | 156 } |
61 if (window_state->HasRestoreBounds() && | 157 if (window_state->HasRestoreBounds() && |
62 (window->bounds().height() == work_area.height() && | 158 (window->bounds().height() == work_area.height() && |
63 window->bounds().y() == work_area.y())) { | 159 window->bounds().y() == work_area.y())) { |
64 window_state->SetAndClearRestoreBounds(); | 160 window_state->SetAndClearRestoreBounds(); |
65 } else { | 161 } else { |
66 window_state->SaveCurrentBoundsForRestore(); | 162 window_state->SaveCurrentBoundsForRestore(); |
67 window->SetBounds(gfx::Rect(window->bounds().x(), | 163 window->SetBounds(gfx::Rect(window->bounds().x(), |
68 work_area.y(), | 164 work_area.y(), |
69 window->bounds().width(), | 165 window->bounds().width(), |
70 work_area.height())); | 166 work_area.height())); |
71 } | 167 } |
72 break; | 168 return true; |
73 } | 169 } |
74 case TOGGLE_HORIZONTAL_MAXIMIZE: { | 170 case TOGGLE_HORIZONTAL_MAXIMIZE: { |
75 // Maximize horizontally if: | 171 // Maximize horizontally if: |
76 // - The window does not have a max width defined. | 172 // - The window does not have a max width defined. |
77 // - The window is snapped or has the normal show type. | 173 // - The window is snapped or has the normal show type. |
78 if (window->delegate()->GetMaximumSize().width() != 0) | 174 if (window->delegate()->GetMaximumSize().width() != 0) |
79 return; | 175 return true; |
80 if (!window_state->IsNormalShowType() && !window_state->IsSnapped()) | 176 if (!window_state->IsNormalShowType() && !window_state->IsSnapped()) |
81 return; | 177 return true; |
82 | |
83 gfx::Rect work_area = | 178 gfx::Rect work_area = |
84 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window); | 179 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window); |
85 | |
86 if (window_state->IsNormalShowType() && | 180 if (window_state->IsNormalShowType() && |
87 window_state->HasRestoreBounds() && | 181 window_state->HasRestoreBounds() && |
88 (window->bounds().width() == work_area.width() && | 182 (window->bounds().width() == work_area.width() && |
89 window->bounds().x() == work_area.x())) { | 183 window->bounds().x() == work_area.x())) { |
90 window_state->SetAndClearRestoreBounds(); | 184 window_state->SetAndClearRestoreBounds(); |
91 } else { | 185 } else { |
92 gfx::Rect new_bounds(work_area.x(), | 186 gfx::Rect new_bounds(work_area.x(), |
93 window->bounds().y(), | 187 window->bounds().y(), |
94 work_area.width(), | 188 work_area.width(), |
95 window->bounds().height()); | 189 window->bounds().height()); |
96 | 190 |
97 gfx::Rect restore_bounds = window->bounds(); | 191 gfx::Rect restore_bounds = window->bounds(); |
98 if (window_state->IsSnapped()) { | 192 if (window_state->IsSnapped()) { |
99 window_state->SetRestoreBoundsInParent(new_bounds); | 193 window_state->SetRestoreBoundsInParent(new_bounds); |
100 window_state->Restore(); | 194 window_state->Restore(); |
101 | 195 |
102 // The restore logic prevents a window from being restored to bounds | 196 // The restore logic prevents a window from being restored to bounds |
103 // which match the workspace bounds exactly so it is necessary to set | 197 // which match the workspace bounds exactly so it is necessary to set |
104 // the bounds again below. | 198 // the bounds again below. |
105 } | 199 } |
106 | 200 |
107 window_state->SetRestoreBoundsInParent(restore_bounds); | 201 window_state->SetRestoreBoundsInParent(restore_bounds); |
108 window->SetBounds(new_bounds); | 202 window->SetBounds(new_bounds); |
109 } | 203 } |
204 return true; | |
205 } | |
206 case TOGGLE_FULLSCREEN: { | |
207 // Window which cannot be maximized should not be fullscreened. | |
208 // It can, however, be restored if it was fullscreened. | |
209 bool is_fullscreen = window_state->IsFullscreen(); | |
210 if (!is_fullscreen && !window_state->CanMaximize()) | |
211 return true; | |
212 if (window_state->delegate() && | |
213 window_state->delegate()->ToggleFullscreen(window_state)) { | |
214 return true; | |
215 } | |
216 if (is_fullscreen) { | |
217 window_state->Restore(); | |
218 } else { | |
219 // | |
220 window_state->window()->SetProperty(aura::client::kShowStateKey, | |
221 ui::SHOW_STATE_FULLSCREEN); | |
222 } | |
223 return true; | |
224 } | |
225 case NORMAL: | |
226 case MAXIMIZE: | |
227 case MINIMIZE: | |
228 case FULLSCREEN: | |
229 case SNAP_LEFT: | |
230 case SNAP_RIGHT: | |
231 break; | |
232 } | |
233 return false; | |
234 } | |
235 | |
236 // static | |
237 void DefaultState::UpdateBoundsFromShowType(wm::WindowState* window_state, | |
238 wm::WindowShowType old_show_type) { | |
239 aura::Window* window = window_state->window(); | |
240 // Do nothing If this is not yet added to the container. | |
241 if (!window->parent()) | |
242 return; | |
243 | |
244 if (old_show_type != wm::SHOW_TYPE_MINIMIZED && | |
245 !window_state->HasRestoreBounds() && | |
246 window_state->IsMaximizedOrFullscreen() && | |
247 !wm::IsMaximizedOrFullscreenWindowShowType(old_show_type)) { | |
248 window_state->SaveCurrentBoundsForRestore(); | |
249 } | |
250 | |
251 // When restoring from a minimized state, we want to restore to the previous | |
252 // bounds. However, we want to maintain the restore bounds. (The restore | |
253 // bounds are set if a user maximized the window in one axis by double | |
254 // clicking the window border for example). | |
255 gfx::Rect restore; | |
256 if (old_show_type == wm::SHOW_TYPE_MINIMIZED && | |
257 window_state->IsNormalShowState() && | |
258 window_state->HasRestoreBounds() && | |
259 !window_state->unminimize_to_restore_bounds()) { | |
260 restore = window_state->GetRestoreBoundsInScreen(); | |
261 window_state->SaveCurrentBoundsForRestore(); | |
262 } | |
263 | |
264 if (window_state->IsMaximizedOrFullscreen()) | |
265 MoveToDisplayForRestore(window_state); | |
266 | |
267 wm::WindowShowType show_type = window_state->window_show_type(); | |
268 gfx::Rect bounds_in_parent; | |
269 switch (show_type) { | |
270 case wm::SHOW_TYPE_DEFAULT: | |
271 case wm::SHOW_TYPE_NORMAL: | |
272 case wm::SHOW_TYPE_LEFT_SNAPPED: | |
273 case wm::SHOW_TYPE_RIGHT_SNAPPED: { | |
274 gfx::Rect work_area_in_parent = | |
275 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window()); | |
276 | |
277 if (window_state->HasRestoreBounds()) | |
278 bounds_in_parent = window_state->GetRestoreBoundsInParent(); | |
279 else | |
280 bounds_in_parent = window->bounds(); | |
281 // Make sure that part of the window is always visible. | |
282 wm::AdjustBoundsToEnsureMinimumWindowVisibility( | |
283 work_area_in_parent, &bounds_in_parent); | |
284 | |
285 if (show_type == wm::SHOW_TYPE_LEFT_SNAPPED || | |
286 show_type == wm::SHOW_TYPE_RIGHT_SNAPPED) { | |
287 window_state->AdjustSnappedBounds(&bounds_in_parent); | |
288 } else { | |
289 bounds_in_parent = BoundsWithScreenEdgeVisible( | |
290 window, | |
291 bounds_in_parent); | |
292 } | |
293 break; | |
294 } | |
295 case wm::SHOW_TYPE_MAXIMIZED: | |
296 bounds_in_parent = ScreenUtil::GetMaximizedWindowBoundsInParent(window); | |
297 break; | |
298 | |
299 case wm::SHOW_TYPE_FULLSCREEN: | |
300 bounds_in_parent = ScreenUtil::GetDisplayBoundsInParent(window); | |
301 break; | |
302 | |
303 case wm::SHOW_TYPE_MINIMIZED: | |
304 break; | |
305 case wm::SHOW_TYPE_INACTIVE: | |
306 case wm::SHOW_TYPE_DETACHED: | |
307 case wm::SHOW_TYPE_END: | |
308 case wm::SHOW_TYPE_AUTO_POSITIONED: | |
309 return; | |
310 } | |
311 | |
312 if (show_type != wm::SHOW_TYPE_MINIMIZED) { | |
313 if (old_show_type == wm::SHOW_TYPE_MINIMIZED || | |
314 (window_state->IsFullscreen() && | |
315 !window_state->animate_to_fullscreen())) { | |
316 window_state->SetBoundsDirect(bounds_in_parent); | |
317 } else if (window_state->IsMaximizedOrFullscreen() || | |
318 IsMaximizedOrFullscreenWindowShowType(old_show_type)) { | |
319 CrossFadeToBounds(window, bounds_in_parent); | |
320 } else { | |
321 window_state->SetBoundsDirectAnimated(bounds_in_parent); | |
110 } | 322 } |
111 } | 323 } |
112 }; | 324 |
325 if (window_state->IsMinimized()) { | |
326 if (old_show_type == wm::SHOW_TYPE_MINIMIZED) | |
327 return; | |
328 | |
329 // Save the previous show state so that we can correctly restore it. | |
330 window_state->window()->SetProperty(aura::client::kRestoreShowStateKey, | |
331 wm::ToWindowShowState(old_show_type)); | |
332 views::corewm::SetWindowVisibilityAnimationType( | |
333 window_state->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE); | |
334 | |
335 // Hide the window. | |
336 window_state->window()->Hide(); | |
337 // Activate another window. | |
338 if (window_state->IsActive()) | |
339 window_state->Deactivate(); | |
340 } else if ((window_state->window()->TargetVisibility() || | |
341 old_show_type == wm::SHOW_TYPE_MINIMIZED) && | |
342 !window_state->window()->layer()->visible()) { | |
343 // The layer may be hidden if the window was previously minimized. Make | |
344 // sure it's visible. | |
345 window_state->window()->Show(); | |
346 if (old_show_type == wm::SHOW_TYPE_MINIMIZED && | |
347 !window_state->IsMaximizedOrFullscreen()) { | |
348 window_state->set_unminimize_to_restore_bounds(false); | |
349 } | |
350 } | |
351 | |
352 if (window_state->IsNormalShowState()) | |
353 window_state->ClearRestoreBounds(); | |
354 | |
355 // Set the restore rectangle to the previously set restore rectangle. | |
356 if (!restore.IsEmpty()) | |
357 window_state->SetRestoreBoundsInScreen(restore); | |
358 } | |
113 | 359 |
114 } // namespace wm | 360 } // namespace wm |
115 } // namespace ash | 361 } // namespace ash |
OLD | NEW |