| 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 "ash/wm/default_state.h" | |
| 6 | |
| 7 #include "ash/shell_window_ids.h" | |
| 8 #include "ash/wm/common/window_animation_types.h" | |
| 9 #include "ash/wm/common/window_parenting_utils.h" | |
| 10 #include "ash/wm/common/window_positioning_utils.h" | |
| 11 #include "ash/wm/common/window_state_util.h" | |
| 12 #include "ash/wm/common/wm_event.h" | |
| 13 #include "ash/wm/common/wm_globals.h" | |
| 14 #include "ash/wm/common/wm_root_window_controller.h" | |
| 15 #include "ash/wm/common/wm_screen_util.h" | |
| 16 #include "ash/wm/common/wm_window.h" | |
| 17 #include "ash/wm/dock/docked_window_layout_manager.h" | |
| 18 #include "ash/wm/window_state.h" | |
| 19 #include "ash/wm/window_state_delegate.h" | |
| 20 #include "ui/gfx/display.h" | |
| 21 #include "ui/gfx/screen.h" | |
| 22 | |
| 23 namespace ash { | |
| 24 namespace wm { | |
| 25 namespace { | |
| 26 | |
| 27 // This specifies how much percent (30%) of a window rect | |
| 28 // must be visible when the window is added to the workspace. | |
| 29 const float kMinimumPercentOnScreenArea = 0.3f; | |
| 30 | |
| 31 // When a window that has restore bounds at least as large as a work area is | |
| 32 // unmaximized, inset the bounds slightly so that they are not exactly the same. | |
| 33 // This makes it easier to resize the window. | |
| 34 const int kMaximizedWindowInset = 10; // DIPs. | |
| 35 | |
| 36 bool IsMinimizedWindowState(const WindowStateType state_type) { | |
| 37 return state_type == WINDOW_STATE_TYPE_MINIMIZED || | |
| 38 state_type == WINDOW_STATE_TYPE_DOCKED_MINIMIZED; | |
| 39 } | |
| 40 | |
| 41 void MoveToDisplayForRestore(WindowState* window_state) { | |
| 42 if (!window_state->HasRestoreBounds()) | |
| 43 return; | |
| 44 const gfx::Rect restore_bounds = window_state->GetRestoreBoundsInScreen(); | |
| 45 | |
| 46 // Move only if the restore bounds is outside of | |
| 47 // the display. There is no information about in which | |
| 48 // display it should be restored, so this is best guess. | |
| 49 // TODO(oshima): Restore information should contain the | |
| 50 // work area information like WindowResizer does for the | |
| 51 // last window location. | |
| 52 gfx::Rect display_area = | |
| 53 window_state->window()->GetDisplayNearestWindow().bounds(); | |
| 54 | |
| 55 if (!display_area.Intersects(restore_bounds)) { | |
| 56 const gfx::Display& display = | |
| 57 gfx::Screen::GetScreen()->GetDisplayMatching(restore_bounds); | |
| 58 WmGlobals* globals = window_state->window()->GetGlobals(); | |
| 59 WmWindow* new_root = globals->GetRootWindowForDisplayId(display.id()); | |
| 60 if (new_root != window_state->window()->GetRootWindow()) { | |
| 61 WmWindow* new_container = new_root->GetChildByShellWindowId( | |
| 62 window_state->window()->GetParent()->GetShellWindowId()); | |
| 63 new_container->AddChild(window_state->window()); | |
| 64 } | |
| 65 } | |
| 66 } | |
| 67 | |
| 68 DockedWindowLayoutManager* GetDockedWindowLayoutManager(WmGlobals* globals) { | |
| 69 return DockedWindowLayoutManager::Get(globals->GetActiveWindow()); | |
| 70 } | |
| 71 | |
| 72 class ScopedPreferredAlignmentResetter { | |
| 73 public: | |
| 74 ScopedPreferredAlignmentResetter(DockedAlignment dock_alignment, | |
| 75 DockedWindowLayoutManager* dock_layout) | |
| 76 : docked_window_layout_manager_(dock_layout) { | |
| 77 docked_window_layout_manager_->set_preferred_alignment(dock_alignment); | |
| 78 } | |
| 79 ~ScopedPreferredAlignmentResetter() { | |
| 80 docked_window_layout_manager_->set_preferred_alignment( | |
| 81 DOCKED_ALIGNMENT_NONE); | |
| 82 } | |
| 83 | |
| 84 private: | |
| 85 DockedWindowLayoutManager* docked_window_layout_manager_; | |
| 86 | |
| 87 DISALLOW_COPY_AND_ASSIGN(ScopedPreferredAlignmentResetter); | |
| 88 }; | |
| 89 | |
| 90 class ScopedDockedLayoutEventSourceResetter { | |
| 91 public: | |
| 92 ScopedDockedLayoutEventSourceResetter(DockedWindowLayoutManager* dock_layout) | |
| 93 : docked_window_layout_manager_(dock_layout) { | |
| 94 docked_window_layout_manager_->set_event_source( | |
| 95 DOCKED_ACTION_SOURCE_KEYBOARD); | |
| 96 } | |
| 97 ~ScopedDockedLayoutEventSourceResetter() { | |
| 98 docked_window_layout_manager_->set_event_source( | |
| 99 DOCKED_ACTION_SOURCE_UNKNOWN); | |
| 100 } | |
| 101 | |
| 102 private: | |
| 103 DockedWindowLayoutManager* docked_window_layout_manager_; | |
| 104 | |
| 105 DISALLOW_COPY_AND_ASSIGN(ScopedDockedLayoutEventSourceResetter); | |
| 106 }; | |
| 107 | |
| 108 void CycleSnapDock(WindowState* window_state, WMEventType event) { | |
| 109 DockedWindowLayoutManager* dock_layout = | |
| 110 GetDockedWindowLayoutManager(window_state->window()->GetGlobals()); | |
| 111 wm::WindowStateType desired_snap_state = event == | |
| 112 WM_EVENT_CYCLE_SNAP_DOCK_LEFT ? wm::WINDOW_STATE_TYPE_LEFT_SNAPPED : | |
| 113 wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED; | |
| 114 DockedAlignment desired_dock_alignment = event == | |
| 115 WM_EVENT_CYCLE_SNAP_DOCK_LEFT ? | |
| 116 DOCKED_ALIGNMENT_LEFT : DOCKED_ALIGNMENT_RIGHT; | |
| 117 DockedAlignment current_dock_alignment = dock_layout ? | |
| 118 dock_layout->CalculateAlignment() : DOCKED_ALIGNMENT_NONE; | |
| 119 | |
| 120 if (!window_state->IsDocked() || | |
| 121 (current_dock_alignment != DOCKED_ALIGNMENT_NONE && | |
| 122 current_dock_alignment != desired_dock_alignment)) { | |
| 123 if (window_state->CanSnap() && | |
| 124 window_state->GetStateType() != desired_snap_state && | |
| 125 window_state->window()->GetType() != ui::wm::WINDOW_TYPE_PANEL) { | |
| 126 const wm::WMEvent event(desired_snap_state == | |
| 127 wm::WINDOW_STATE_TYPE_LEFT_SNAPPED ? | |
| 128 wm::WM_EVENT_SNAP_LEFT : wm::WM_EVENT_SNAP_RIGHT); | |
| 129 window_state->OnWMEvent(&event); | |
| 130 return; | |
| 131 } | |
| 132 | |
| 133 if (dock_layout && | |
| 134 dock_layout->CanDockWindow(window_state->window(), | |
| 135 desired_dock_alignment)) { | |
| 136 if (window_state->IsDocked()) { | |
| 137 dock_layout->MaybeSetDesiredDockedAlignment(desired_dock_alignment); | |
| 138 return; | |
| 139 } | |
| 140 | |
| 141 ScopedDockedLayoutEventSourceResetter event_source_resetter(dock_layout); | |
| 142 ScopedPreferredAlignmentResetter alignmentResetter(desired_dock_alignment, | |
| 143 dock_layout); | |
| 144 const wm::WMEvent event(wm::WM_EVENT_DOCK); | |
| 145 window_state->OnWMEvent(&event); | |
| 146 return; | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 if (window_state->IsDocked() || window_state->IsSnapped()) { | |
| 151 ScopedDockedLayoutEventSourceResetter event_source_resetter(dock_layout); | |
| 152 window_state->Restore(); | |
| 153 return; | |
| 154 } | |
| 155 window_state->window()->Animate(::wm::WINDOW_ANIMATION_TYPE_BOUNCE); | |
| 156 } | |
| 157 | |
| 158 } // namespace | |
| 159 | |
| 160 DefaultState::DefaultState(WindowStateType initial_state_type) | |
| 161 : state_type_(initial_state_type), stored_window_state_(nullptr) {} | |
| 162 DefaultState::~DefaultState() {} | |
| 163 | |
| 164 void DefaultState::OnWMEvent(WindowState* window_state, | |
| 165 const WMEvent* event) { | |
| 166 if (ProcessWorkspaceEvents(window_state, event)) | |
| 167 return; | |
| 168 | |
| 169 if (ProcessCompoundEvents(window_state, event)) | |
| 170 return; | |
| 171 | |
| 172 WindowStateType current_state_type = window_state->GetStateType(); | |
| 173 WindowStateType next_state_type = WINDOW_STATE_TYPE_NORMAL; | |
| 174 switch (event->type()) { | |
| 175 case WM_EVENT_NORMAL: | |
| 176 next_state_type = | |
| 177 current_state_type == WINDOW_STATE_TYPE_DOCKED_MINIMIZED ? | |
| 178 WINDOW_STATE_TYPE_DOCKED : WINDOW_STATE_TYPE_NORMAL; | |
| 179 break; | |
| 180 case WM_EVENT_MAXIMIZE: | |
| 181 next_state_type = WINDOW_STATE_TYPE_MAXIMIZED; | |
| 182 break; | |
| 183 case WM_EVENT_MINIMIZE: | |
| 184 next_state_type = | |
| 185 current_state_type == WINDOW_STATE_TYPE_DOCKED ? | |
| 186 WINDOW_STATE_TYPE_DOCKED_MINIMIZED : WINDOW_STATE_TYPE_MINIMIZED; | |
| 187 break; | |
| 188 case WM_EVENT_FULLSCREEN: | |
| 189 next_state_type = WINDOW_STATE_TYPE_FULLSCREEN; | |
| 190 break; | |
| 191 case WM_EVENT_SNAP_LEFT: | |
| 192 next_state_type = WINDOW_STATE_TYPE_LEFT_SNAPPED; | |
| 193 break; | |
| 194 case WM_EVENT_SNAP_RIGHT: | |
| 195 next_state_type = WINDOW_STATE_TYPE_RIGHT_SNAPPED; | |
| 196 break; | |
| 197 case WM_EVENT_DOCK: | |
| 198 next_state_type = WINDOW_STATE_TYPE_DOCKED; | |
| 199 break; | |
| 200 case WM_EVENT_SET_BOUNDS: | |
| 201 SetBounds(window_state, static_cast<const SetBoundsEvent*>(event)); | |
| 202 return; | |
| 203 case WM_EVENT_SHOW_INACTIVE: | |
| 204 next_state_type = WINDOW_STATE_TYPE_INACTIVE; | |
| 205 break; | |
| 206 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION: | |
| 207 case WM_EVENT_TOGGLE_MAXIMIZE: | |
| 208 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: | |
| 209 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: | |
| 210 case WM_EVENT_TOGGLE_FULLSCREEN: | |
| 211 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT: | |
| 212 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT: | |
| 213 case WM_EVENT_CENTER: | |
| 214 NOTREACHED() << "Compound event should not reach here:" << event; | |
| 215 return; | |
| 216 case WM_EVENT_ADDED_TO_WORKSPACE: | |
| 217 case WM_EVENT_WORKAREA_BOUNDS_CHANGED: | |
| 218 case WM_EVENT_DISPLAY_BOUNDS_CHANGED: | |
| 219 NOTREACHED() << "Workspace event should not reach here:" << event; | |
| 220 return; | |
| 221 } | |
| 222 | |
| 223 if (next_state_type == current_state_type && window_state->IsSnapped()) { | |
| 224 gfx::Rect snapped_bounds = event->type() == WM_EVENT_SNAP_LEFT ? | |
| 225 GetDefaultLeftSnappedWindowBoundsInParent(window_state->window()) : | |
| 226 GetDefaultRightSnappedWindowBoundsInParent(window_state->window()); | |
| 227 window_state->SetBoundsDirectAnimated(snapped_bounds); | |
| 228 return; | |
| 229 } | |
| 230 | |
| 231 EnterToNextState(window_state, next_state_type); | |
| 232 } | |
| 233 | |
| 234 WindowStateType DefaultState::GetType() const { | |
| 235 return state_type_; | |
| 236 } | |
| 237 | |
| 238 void DefaultState::AttachState( | |
| 239 WindowState* window_state, | |
| 240 WindowState::State* state_in_previous_mode) { | |
| 241 DCHECK_EQ(stored_window_state_, window_state); | |
| 242 | |
| 243 ReenterToCurrentState(window_state, state_in_previous_mode); | |
| 244 | |
| 245 // If the display has changed while in the another mode, | |
| 246 // we need to let windows know the change. | |
| 247 gfx::Display current_display = | |
| 248 window_state->window()->GetDisplayNearestWindow(); | |
| 249 if (stored_display_state_.bounds() != current_display.bounds()) { | |
| 250 const WMEvent event(wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED); | |
| 251 window_state->OnWMEvent(&event); | |
| 252 } else if (stored_display_state_.work_area() != current_display.work_area()) { | |
| 253 const WMEvent event(wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED); | |
| 254 window_state->OnWMEvent(&event); | |
| 255 } | |
| 256 } | |
| 257 | |
| 258 void DefaultState::DetachState(WindowState* window_state) { | |
| 259 stored_window_state_ = window_state; | |
| 260 stored_bounds_ = window_state->window()->GetBounds(); | |
| 261 stored_restore_bounds_ = window_state->HasRestoreBounds() ? | |
| 262 window_state->GetRestoreBoundsInParent() : gfx::Rect(); | |
| 263 // Remember the display state so that in case of the display change | |
| 264 // while in the other mode, we can perform necessary action to | |
| 265 // restore the window state to the proper state for the current | |
| 266 // display. | |
| 267 stored_display_state_ = window_state->window()->GetDisplayNearestWindow(); | |
| 268 } | |
| 269 | |
| 270 // static | |
| 271 bool DefaultState::ProcessCompoundEvents(WindowState* window_state, | |
| 272 const WMEvent* event) { | |
| 273 WmWindow* window = window_state->window(); | |
| 274 | |
| 275 switch (event->type()) { | |
| 276 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION: | |
| 277 if (window_state->IsFullscreen()) { | |
| 278 const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN); | |
| 279 window_state->OnWMEvent(&event); | |
| 280 } else if (window_state->IsMaximized()) { | |
| 281 window_state->Restore(); | |
| 282 } else if (window_state->IsNormalOrSnapped()) { | |
| 283 if (window_state->CanMaximize()) | |
| 284 window_state->Maximize(); | |
| 285 } | |
| 286 return true; | |
| 287 case WM_EVENT_TOGGLE_MAXIMIZE: | |
| 288 if (window_state->IsFullscreen()) { | |
| 289 const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN); | |
| 290 window_state->OnWMEvent(&event); | |
| 291 } else if (window_state->IsMaximized()) { | |
| 292 window_state->Restore(); | |
| 293 } else if (window_state->CanMaximize()) { | |
| 294 window_state->Maximize(); | |
| 295 } | |
| 296 return true; | |
| 297 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: { | |
| 298 gfx::Rect work_area = GetDisplayWorkAreaBoundsInParent(window); | |
| 299 | |
| 300 // Maximize vertically if: | |
| 301 // - The window does not have a max height defined. | |
| 302 // - The window has the normal state type. Snapped windows are excluded | |
| 303 // because they are already maximized vertically and reverting to the | |
| 304 // restored bounds looks weird. | |
| 305 if (window->GetMaximumSize().height() != 0 || | |
| 306 !window_state->IsNormalStateType()) { | |
| 307 return true; | |
| 308 } | |
| 309 if (window_state->HasRestoreBounds() && | |
| 310 (window->GetBounds().height() == work_area.height() && | |
| 311 window->GetBounds().y() == work_area.y())) { | |
| 312 window_state->SetAndClearRestoreBounds(); | |
| 313 } else { | |
| 314 window_state->SaveCurrentBoundsForRestore(); | |
| 315 window->SetBounds(gfx::Rect(window->GetBounds().x(), work_area.y(), | |
| 316 window->GetBounds().width(), | |
| 317 work_area.height())); | |
| 318 } | |
| 319 return true; | |
| 320 } | |
| 321 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: { | |
| 322 // Maximize horizontally if: | |
| 323 // - The window does not have a max width defined. | |
| 324 // - The window is snapped or has the normal state type. | |
| 325 if (window->GetMaximumSize().width() != 0) | |
| 326 return true; | |
| 327 if (!window_state->IsNormalOrSnapped()) | |
| 328 return true; | |
| 329 gfx::Rect work_area = GetDisplayWorkAreaBoundsInParent(window); | |
| 330 if (window_state->IsNormalStateType() && | |
| 331 window_state->HasRestoreBounds() && | |
| 332 (window->GetBounds().width() == work_area.width() && | |
| 333 window->GetBounds().x() == work_area.x())) { | |
| 334 window_state->SetAndClearRestoreBounds(); | |
| 335 } else { | |
| 336 gfx::Rect new_bounds(work_area.x(), window->GetBounds().y(), | |
| 337 work_area.width(), window->GetBounds().height()); | |
| 338 | |
| 339 gfx::Rect restore_bounds = window->GetBounds(); | |
| 340 if (window_state->IsSnapped()) { | |
| 341 window_state->SetRestoreBoundsInParent(new_bounds); | |
| 342 window_state->Restore(); | |
| 343 | |
| 344 // The restore logic prevents a window from being restored to bounds | |
| 345 // which match the workspace bounds exactly so it is necessary to set | |
| 346 // the bounds again below. | |
| 347 } | |
| 348 | |
| 349 window_state->SetRestoreBoundsInParent(restore_bounds); | |
| 350 window->SetBounds(new_bounds); | |
| 351 } | |
| 352 return true; | |
| 353 } | |
| 354 case WM_EVENT_TOGGLE_FULLSCREEN: | |
| 355 ToggleFullScreen(window_state, window_state->delegate()); | |
| 356 return true; | |
| 357 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT: | |
| 358 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT: | |
| 359 CycleSnapDock(window_state, event->type()); | |
| 360 return true; | |
| 361 case WM_EVENT_CENTER: | |
| 362 CenterWindow(window_state); | |
| 363 return true; | |
| 364 case WM_EVENT_NORMAL: | |
| 365 case WM_EVENT_MAXIMIZE: | |
| 366 case WM_EVENT_MINIMIZE: | |
| 367 case WM_EVENT_FULLSCREEN: | |
| 368 case WM_EVENT_SNAP_LEFT: | |
| 369 case WM_EVENT_SNAP_RIGHT: | |
| 370 case WM_EVENT_SET_BOUNDS: | |
| 371 case WM_EVENT_SHOW_INACTIVE: | |
| 372 case WM_EVENT_DOCK: | |
| 373 break; | |
| 374 case WM_EVENT_ADDED_TO_WORKSPACE: | |
| 375 case WM_EVENT_WORKAREA_BOUNDS_CHANGED: | |
| 376 case WM_EVENT_DISPLAY_BOUNDS_CHANGED: | |
| 377 NOTREACHED() << "Workspace event should not reach here:" << event; | |
| 378 break; | |
| 379 } | |
| 380 return false; | |
| 381 } | |
| 382 | |
| 383 bool DefaultState::ProcessWorkspaceEvents(WindowState* window_state, | |
| 384 const WMEvent* event) { | |
| 385 switch (event->type()) { | |
| 386 case WM_EVENT_ADDED_TO_WORKSPACE: { | |
| 387 // When a window is dragged and dropped onto a different | |
| 388 // root window, the bounds will be updated after they are added | |
| 389 // to the root window. | |
| 390 // If a window is opened as maximized or fullscreen, its bounds may be | |
| 391 // empty, so update the bounds now before checking empty. | |
| 392 if (window_state->is_dragged() || | |
| 393 SetMaximizedOrFullscreenBounds(window_state)) { | |
| 394 return true; | |
| 395 } | |
| 396 | |
| 397 WmWindow* window = window_state->window(); | |
| 398 gfx::Rect bounds = window->GetBounds(); | |
| 399 | |
| 400 // Don't adjust window bounds if the bounds are empty as this | |
| 401 // happens when a new views::Widget is created. | |
| 402 if (bounds.IsEmpty()) | |
| 403 return true; | |
| 404 | |
| 405 // Only windows of type WINDOW_TYPE_NORMAL or WINDOW_TYPE_PANEL need to be | |
| 406 // adjusted to have minimum visibility, because they are positioned by the | |
| 407 // user and user should always be able to interact with them. Other | |
| 408 // windows are positioned programmatically. | |
| 409 if (!window_state->IsUserPositionable()) | |
| 410 return true; | |
| 411 | |
| 412 // Use entire display instead of workarea because the workarea can | |
| 413 // be further shrunk by the docked area. The logic ensures 30% | |
| 414 // visibility which should be enough to see where the window gets | |
| 415 // moved. | |
| 416 gfx::Rect display_area = GetDisplayBoundsInParent(window); | |
| 417 int min_width = bounds.width() * wm::kMinimumPercentOnScreenArea; | |
| 418 int min_height = bounds.height() * wm::kMinimumPercentOnScreenArea; | |
| 419 wm::AdjustBoundsToEnsureWindowVisibility(display_area, min_width, | |
| 420 min_height, &bounds); | |
| 421 window_state->AdjustSnappedBounds(&bounds); | |
| 422 if (window->GetBounds() != bounds) | |
| 423 window_state->SetBoundsConstrained(bounds); | |
| 424 return true; | |
| 425 } | |
| 426 case WM_EVENT_DISPLAY_BOUNDS_CHANGED: { | |
| 427 if (window_state->is_dragged() || | |
| 428 SetMaximizedOrFullscreenBounds(window_state)) { | |
| 429 return true; | |
| 430 } | |
| 431 gfx::Rect work_area_in_parent = | |
| 432 GetDisplayWorkAreaBoundsInParent(window_state->window()); | |
| 433 gfx::Rect bounds = window_state->window()->GetBounds(); | |
| 434 // When display bounds has changed, make sure the entire window is fully | |
| 435 // visible. | |
| 436 bounds.AdjustToFit(work_area_in_parent); | |
| 437 window_state->AdjustSnappedBounds(&bounds); | |
| 438 if (window_state->window()->GetBounds() != bounds) | |
| 439 window_state->SetBoundsDirectAnimated(bounds); | |
| 440 return true; | |
| 441 } | |
| 442 case WM_EVENT_WORKAREA_BOUNDS_CHANGED: { | |
| 443 // Don't resize the maximized window when the desktop is covered | |
| 444 // by fullscreen window. crbug.com/504299. | |
| 445 bool in_fullscreen = | |
| 446 window_state->window() | |
| 447 ->GetRootWindowController() | |
| 448 ->GetWorkspaceWindowState() == WORKSPACE_WINDOW_STATE_FULL_SCREEN; | |
| 449 if (in_fullscreen && window_state->IsMaximized()) | |
| 450 return true; | |
| 451 | |
| 452 if (window_state->is_dragged() || | |
| 453 SetMaximizedOrFullscreenBounds(window_state)) { | |
| 454 return true; | |
| 455 } | |
| 456 gfx::Rect work_area_in_parent = | |
| 457 GetDisplayWorkAreaBoundsInParent(window_state->window()); | |
| 458 gfx::Rect bounds = window_state->window()->GetBounds(); | |
| 459 wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent, | |
| 460 &bounds); | |
| 461 window_state->AdjustSnappedBounds(&bounds); | |
| 462 if (window_state->window()->GetBounds() != bounds) | |
| 463 window_state->SetBoundsDirectAnimated(bounds); | |
| 464 return true; | |
| 465 } | |
| 466 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION: | |
| 467 case WM_EVENT_TOGGLE_MAXIMIZE: | |
| 468 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: | |
| 469 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: | |
| 470 case WM_EVENT_TOGGLE_FULLSCREEN: | |
| 471 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT: | |
| 472 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT: | |
| 473 case WM_EVENT_CENTER: | |
| 474 case WM_EVENT_NORMAL: | |
| 475 case WM_EVENT_MAXIMIZE: | |
| 476 case WM_EVENT_MINIMIZE: | |
| 477 case WM_EVENT_FULLSCREEN: | |
| 478 case WM_EVENT_SNAP_LEFT: | |
| 479 case WM_EVENT_SNAP_RIGHT: | |
| 480 case WM_EVENT_SET_BOUNDS: | |
| 481 case WM_EVENT_SHOW_INACTIVE: | |
| 482 case WM_EVENT_DOCK: | |
| 483 break; | |
| 484 } | |
| 485 return false; | |
| 486 } | |
| 487 | |
| 488 // static | |
| 489 bool DefaultState::SetMaximizedOrFullscreenBounds(WindowState* window_state) { | |
| 490 DCHECK(!window_state->is_dragged()); | |
| 491 if (window_state->IsMaximized()) { | |
| 492 window_state->SetBoundsDirect( | |
| 493 GetMaximizedWindowBoundsInParent(window_state->window())); | |
| 494 return true; | |
| 495 } | |
| 496 if (window_state->IsFullscreen()) { | |
| 497 window_state->SetBoundsDirect( | |
| 498 GetDisplayBoundsInParent(window_state->window())); | |
| 499 return true; | |
| 500 } | |
| 501 return false; | |
| 502 } | |
| 503 | |
| 504 // static | |
| 505 void DefaultState::SetBounds(WindowState* window_state, | |
| 506 const SetBoundsEvent* event) { | |
| 507 if (window_state->is_dragged()) { | |
| 508 // TODO(oshima|varkha): This may be no longer needed, as the dragging | |
| 509 // happens in docked window container. crbug.com/485612. | |
| 510 window_state->SetBoundsDirect(event->requested_bounds()); | |
| 511 } else if (window_state->IsSnapped()) { | |
| 512 gfx::Rect work_area_in_parent = | |
| 513 GetDisplayWorkAreaBoundsInParent(window_state->window()); | |
| 514 gfx::Rect child_bounds(event->requested_bounds()); | |
| 515 wm::AdjustBoundsSmallerThan(work_area_in_parent.size(), &child_bounds); | |
| 516 window_state->AdjustSnappedBounds(&child_bounds); | |
| 517 window_state->SetBoundsDirect(child_bounds); | |
| 518 } else if (!SetMaximizedOrFullscreenBounds(window_state)) { | |
| 519 window_state->SetBoundsConstrained(event->requested_bounds()); | |
| 520 } | |
| 521 } | |
| 522 | |
| 523 void DefaultState::EnterToNextState(WindowState* window_state, | |
| 524 WindowStateType next_state_type) { | |
| 525 // Do nothing if we're already in the same state. | |
| 526 if (state_type_ == next_state_type) | |
| 527 return; | |
| 528 | |
| 529 WindowStateType previous_state_type = state_type_; | |
| 530 state_type_ = next_state_type; | |
| 531 | |
| 532 window_state->UpdateWindowShowStateFromStateType(); | |
| 533 window_state->NotifyPreStateTypeChange(previous_state_type); | |
| 534 | |
| 535 if (window_state->window()->GetParent()) { | |
| 536 if (!window_state->HasRestoreBounds() && | |
| 537 (previous_state_type == WINDOW_STATE_TYPE_DEFAULT || | |
| 538 previous_state_type == WINDOW_STATE_TYPE_NORMAL) && | |
| 539 !window_state->IsMinimized() && | |
| 540 !window_state->IsNormalStateType()) { | |
| 541 window_state->SaveCurrentBoundsForRestore(); | |
| 542 } | |
| 543 | |
| 544 // When restoring from a minimized state, we want to restore to the | |
| 545 // previous bounds. However, we want to maintain the restore bounds. | |
| 546 // (The restore bounds are set if a user maximized the window in one | |
| 547 // axis by double clicking the window border for example). | |
| 548 gfx::Rect restore_bounds_in_screen; | |
| 549 if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED && | |
| 550 window_state->IsNormalStateType() && | |
| 551 window_state->HasRestoreBounds() && | |
| 552 !window_state->unminimize_to_restore_bounds()) { | |
| 553 restore_bounds_in_screen = window_state->GetRestoreBoundsInScreen(); | |
| 554 window_state->SaveCurrentBoundsForRestore(); | |
| 555 } | |
| 556 | |
| 557 if (window_state->IsMaximizedOrFullscreen()) | |
| 558 MoveToDisplayForRestore(window_state); | |
| 559 | |
| 560 UpdateBoundsFromState(window_state, previous_state_type); | |
| 561 | |
| 562 // Normal state should have no restore bounds unless it's | |
| 563 // unminimized. | |
| 564 if (!restore_bounds_in_screen.IsEmpty()) | |
| 565 window_state->SetRestoreBoundsInScreen(restore_bounds_in_screen); | |
| 566 else if (window_state->IsNormalStateType()) | |
| 567 window_state->ClearRestoreBounds(); | |
| 568 } | |
| 569 window_state->NotifyPostStateTypeChange(previous_state_type); | |
| 570 } | |
| 571 | |
| 572 void DefaultState::ReenterToCurrentState( | |
| 573 WindowState* window_state, | |
| 574 WindowState::State* state_in_previous_mode) { | |
| 575 WindowStateType previous_state_type = state_in_previous_mode->GetType(); | |
| 576 | |
| 577 // A state change should not move a window into or out of full screen since | |
| 578 // full screen is a "special mode" the user wanted to be in and should be | |
| 579 // respected as such. | |
| 580 if (previous_state_type == wm::WINDOW_STATE_TYPE_FULLSCREEN) | |
| 581 state_type_ = wm::WINDOW_STATE_TYPE_FULLSCREEN; | |
| 582 else if (state_type_ == wm::WINDOW_STATE_TYPE_FULLSCREEN) | |
| 583 state_type_ = previous_state_type; | |
| 584 | |
| 585 window_state->UpdateWindowShowStateFromStateType(); | |
| 586 window_state->NotifyPreStateTypeChange(previous_state_type); | |
| 587 | |
| 588 if ((state_type_ == wm::WINDOW_STATE_TYPE_NORMAL || | |
| 589 state_type_ == wm::WINDOW_STATE_TYPE_DEFAULT) && | |
| 590 !stored_bounds_.IsEmpty()) { | |
| 591 // Use the restore mechanism to set the bounds for | |
| 592 // the window in normal state. This also covers unminimize case. | |
| 593 window_state->SetRestoreBoundsInParent(stored_bounds_); | |
| 594 } | |
| 595 | |
| 596 UpdateBoundsFromState(window_state, state_in_previous_mode->GetType()); | |
| 597 | |
| 598 // Then restore the restore bounds to their previous value. | |
| 599 if (!stored_restore_bounds_.IsEmpty()) | |
| 600 window_state->SetRestoreBoundsInParent(stored_restore_bounds_); | |
| 601 else | |
| 602 window_state->ClearRestoreBounds(); | |
| 603 | |
| 604 window_state->NotifyPostStateTypeChange(previous_state_type); | |
| 605 } | |
| 606 | |
| 607 void DefaultState::UpdateBoundsFromState(WindowState* window_state, | |
| 608 WindowStateType previous_state_type) { | |
| 609 WmWindow* window = window_state->window(); | |
| 610 gfx::Rect bounds_in_parent; | |
| 611 switch (state_type_) { | |
| 612 case WINDOW_STATE_TYPE_LEFT_SNAPPED: | |
| 613 case WINDOW_STATE_TYPE_RIGHT_SNAPPED: | |
| 614 bounds_in_parent = | |
| 615 state_type_ == WINDOW_STATE_TYPE_LEFT_SNAPPED | |
| 616 ? GetDefaultLeftSnappedWindowBoundsInParent(window) | |
| 617 : GetDefaultRightSnappedWindowBoundsInParent(window); | |
| 618 break; | |
| 619 case WINDOW_STATE_TYPE_DOCKED: { | |
| 620 if (window->GetParent()->GetShellWindowId() != | |
| 621 kShellWindowId_DockedContainer) { | |
| 622 WmWindow* docked_container = | |
| 623 window->GetRootWindow()->GetChildByShellWindowId( | |
| 624 kShellWindowId_DockedContainer); | |
| 625 ReparentChildWithTransientChildren(window, window->GetParent(), | |
| 626 docked_container); | |
| 627 } | |
| 628 // Return early because we don't want to update the bounds of the | |
| 629 // window below; as the bounds are managed by the dock layout. | |
| 630 return; | |
| 631 } | |
| 632 case WINDOW_STATE_TYPE_DEFAULT: | |
| 633 case WINDOW_STATE_TYPE_NORMAL: { | |
| 634 gfx::Rect work_area_in_parent = GetDisplayWorkAreaBoundsInParent(window); | |
| 635 if (window_state->HasRestoreBounds()) { | |
| 636 bounds_in_parent = window_state->GetRestoreBoundsInParent(); | |
| 637 // Check if the |window|'s restored size is bigger than the working area | |
| 638 // This may happen if a window was resized to maximized bounds or if the | |
| 639 // display resolution changed while the window was maximized. | |
| 640 if (previous_state_type == WINDOW_STATE_TYPE_MAXIMIZED && | |
| 641 bounds_in_parent.width() >= work_area_in_parent.width() && | |
| 642 bounds_in_parent.height() >= work_area_in_parent.height()) { | |
| 643 bounds_in_parent = work_area_in_parent; | |
| 644 bounds_in_parent.Inset(kMaximizedWindowInset, kMaximizedWindowInset, | |
| 645 kMaximizedWindowInset, kMaximizedWindowInset); | |
| 646 } | |
| 647 } else { | |
| 648 bounds_in_parent = window->GetBounds(); | |
| 649 } | |
| 650 // Make sure that part of the window is always visible. | |
| 651 wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent, | |
| 652 &bounds_in_parent); | |
| 653 break; | |
| 654 } | |
| 655 case WINDOW_STATE_TYPE_MAXIMIZED: | |
| 656 bounds_in_parent = GetMaximizedWindowBoundsInParent(window); | |
| 657 break; | |
| 658 | |
| 659 case WINDOW_STATE_TYPE_FULLSCREEN: | |
| 660 bounds_in_parent = GetDisplayBoundsInParent(window); | |
| 661 break; | |
| 662 | |
| 663 case WINDOW_STATE_TYPE_DOCKED_MINIMIZED: | |
| 664 case WINDOW_STATE_TYPE_MINIMIZED: | |
| 665 break; | |
| 666 case WINDOW_STATE_TYPE_INACTIVE: | |
| 667 case WINDOW_STATE_TYPE_END: | |
| 668 case WINDOW_STATE_TYPE_AUTO_POSITIONED: | |
| 669 return; | |
| 670 } | |
| 671 | |
| 672 if (!window_state->IsMinimized()) { | |
| 673 if (IsMinimizedWindowState(previous_state_type) || | |
| 674 window_state->IsFullscreen()) { | |
| 675 window_state->SetBoundsDirect(bounds_in_parent); | |
| 676 } else if (window_state->IsMaximized() || | |
| 677 IsMaximizedOrFullscreenWindowStateType(previous_state_type)) { | |
| 678 window_state->SetBoundsDirectCrossFade(bounds_in_parent); | |
| 679 } else if (window_state->is_dragged()) { | |
| 680 // SetBoundsDirectAnimated does not work when the window gets reparented. | |
| 681 // TODO(oshima): Consider fixing it and reenable the animation. | |
| 682 window_state->SetBoundsDirect(bounds_in_parent); | |
| 683 } else { | |
| 684 window_state->SetBoundsDirectAnimated(bounds_in_parent); | |
| 685 } | |
| 686 } | |
| 687 | |
| 688 if (window_state->IsMinimized()) { | |
| 689 // Save the previous show state so that we can correctly restore it. | |
| 690 window->SetRestoreShowState(ToWindowShowState(previous_state_type)); | |
| 691 window->SetVisibilityAnimationType( | |
| 692 WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE); | |
| 693 | |
| 694 // Hide the window. | |
| 695 window->Hide(); | |
| 696 // Activate another window. | |
| 697 if (window_state->IsActive()) | |
| 698 window_state->Deactivate(); | |
| 699 } else if ((window->GetTargetVisibility() || | |
| 700 IsMinimizedWindowState(previous_state_type)) && | |
| 701 !window->GetLayer()->visible()) { | |
| 702 // The layer may be hidden if the window was previously minimized. Make | |
| 703 // sure it's visible. | |
| 704 window->Show(); | |
| 705 if (IsMinimizedWindowState(previous_state_type) && | |
| 706 !window_state->IsMaximizedOrFullscreen()) { | |
| 707 window_state->set_unminimize_to_restore_bounds(false); | |
| 708 } | |
| 709 } | |
| 710 } | |
| 711 | |
| 712 // static | |
| 713 void DefaultState::CenterWindow(WindowState* window_state) { | |
| 714 if (!window_state->IsNormalOrSnapped()) | |
| 715 return; | |
| 716 WmWindow* window = window_state->window(); | |
| 717 if (window_state->IsSnapped()) { | |
| 718 gfx::Rect center_in_screen = window->GetDisplayNearestWindow().work_area(); | |
| 719 gfx::Size size = window_state->HasRestoreBounds() | |
| 720 ? window_state->GetRestoreBoundsInScreen().size() | |
| 721 : window->GetBounds().size(); | |
| 722 center_in_screen.ClampToCenteredSize(size); | |
| 723 window_state->SetRestoreBoundsInScreen(center_in_screen); | |
| 724 window_state->Restore(); | |
| 725 } else { | |
| 726 gfx::Rect center_in_parent = GetDisplayWorkAreaBoundsInParent(window); | |
| 727 center_in_parent.ClampToCenteredSize(window->GetBounds().size()); | |
| 728 window_state->SetBoundsDirectAnimated(center_in_parent); | |
| 729 } | |
| 730 // Centering window is treated as if a user moved and resized the window. | |
| 731 window_state->set_bounds_changed_by_user(true); | |
| 732 } | |
| 733 | |
| 734 } // namespace wm | |
| 735 } // namespace ash | |
| OLD | NEW |