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