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