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