Home | History | Annotate | Download | only in wm
      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 
     25 namespace ash {
     26 namespace wm {
     27 namespace {
     28 
     29 // This specifies how much percent (30%) of a window rect
     30 // must be visible when the window is added to the workspace.
     31 const float kMinimumPercentOnScreenArea = 0.3f;
     32 
     33 bool IsPanel(aura::Window* window) {
     34   return window->parent() &&
     35          window->parent()->id() == kShellWindowId_PanelContainer;
     36 }
     37 
     38 void 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 
     69 DefaultState::DefaultState(WindowStateType initial_state_type)
     70     : state_type_(initial_state_type) {}
     71 DefaultState::~DefaultState() {}
     72 
     73 void 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 
    135 WindowStateType DefaultState::GetType() const {
    136   return state_type_;
    137 }
    138 
    139 void 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 
    159 void 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
    174 bool 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 
    286 bool 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       // Use entire display instead of workarea because the workarea can
    309       // be further shrunk by the docked area. The logic ensures 30%
    310       // visibility which should be enough to see where the window gets
    311       // moved.
    312       gfx::Rect display_area = ScreenUtil::GetDisplayBoundsInParent(window);
    313       int min_width = bounds.width() * kMinimumPercentOnScreenArea;
    314       int min_height = bounds.height() * kMinimumPercentOnScreenArea;
    315       AdjustBoundsToEnsureWindowVisibility(
    316           display_area, min_width, min_height, &bounds);
    317       window_state->AdjustSnappedBounds(&bounds);
    318       if (window->bounds() != bounds)
    319         window_state->SetBoundsConstrained(bounds);
    320       return true;
    321     }
    322     case WM_EVENT_DISPLAY_BOUNDS_CHANGED: {
    323       if (window_state->is_dragged() ||
    324           SetMaximizedOrFullscreenBounds(window_state)) {
    325         return true;
    326       }
    327       gfx::Rect work_area_in_parent =
    328           ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
    329       gfx::Rect bounds = window_state->window()->bounds();
    330       // When display bounds has changed, make sure the entire window is fully
    331       // visible.
    332       bounds.AdjustToFit(work_area_in_parent);
    333       window_state->AdjustSnappedBounds(&bounds);
    334       if (window_state->window()->bounds() != bounds)
    335         window_state->SetBoundsDirectAnimated(bounds);
    336       return true;
    337     }
    338     case WM_EVENT_WORKAREA_BOUNDS_CHANGED: {
    339       if (window_state->is_dragged() ||
    340           SetMaximizedOrFullscreenBounds(window_state)) {
    341         return true;
    342       }
    343       gfx::Rect work_area_in_parent =
    344           ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
    345       gfx::Rect bounds = window_state->window()->bounds();
    346       AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent, &bounds);
    347       window_state->AdjustSnappedBounds(&bounds);
    348       if (window_state->window()->bounds() != bounds)
    349         window_state->SetBoundsDirectAnimated(bounds);
    350       return true;
    351     }
    352     case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
    353     case WM_EVENT_TOGGLE_MAXIMIZE:
    354     case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE:
    355     case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
    356     case WM_EVENT_TOGGLE_FULLSCREEN:
    357     case WM_EVENT_CENTER:
    358     case WM_EVENT_NORMAL:
    359     case WM_EVENT_MAXIMIZE:
    360     case WM_EVENT_MINIMIZE:
    361     case WM_EVENT_FULLSCREEN:
    362     case WM_EVENT_SNAP_LEFT:
    363     case WM_EVENT_SNAP_RIGHT:
    364     case WM_EVENT_SET_BOUNDS:
    365     case WM_EVENT_SHOW_INACTIVE:
    366       break;
    367   }
    368   return false;
    369 }
    370 
    371 // static
    372 bool DefaultState::SetMaximizedOrFullscreenBounds(WindowState* window_state) {
    373   DCHECK(!window_state->is_dragged());
    374   if (window_state->IsMaximized()) {
    375     window_state->SetBoundsDirect(
    376         ScreenUtil::GetMaximizedWindowBoundsInParent(window_state->window()));
    377     return true;
    378   }
    379   if (window_state->IsFullscreen()) {
    380     window_state->SetBoundsDirect(
    381         ScreenUtil::GetDisplayBoundsInParent(window_state->window()));
    382     return true;
    383   }
    384   return false;
    385 }
    386 
    387 // static
    388 void DefaultState::SetBounds(WindowState* window_state,
    389                              const SetBoundsEvent* event) {
    390   if (window_state->is_dragged()) {
    391     window_state->SetBoundsDirect(event->requested_bounds());
    392   } else if (window_state->IsSnapped()) {
    393     gfx::Rect work_area_in_parent =
    394         ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
    395     gfx::Rect child_bounds(event->requested_bounds());
    396     AdjustBoundsSmallerThan(work_area_in_parent.size(), &child_bounds);
    397     window_state->AdjustSnappedBounds(&child_bounds);
    398     window_state->SetBoundsDirect(child_bounds);
    399   } else if (!SetMaximizedOrFullscreenBounds(window_state)) {
    400     window_state->SetBoundsConstrained(event->requested_bounds());
    401   }
    402 }
    403 
    404 void DefaultState::EnterToNextState(WindowState* window_state,
    405                                     WindowStateType next_state_type) {
    406   // Do nothing if  we're already in the same state.
    407   if (state_type_ == next_state_type)
    408     return;
    409 
    410   WindowStateType previous_state_type = state_type_;
    411   state_type_ = next_state_type;
    412 
    413   window_state->UpdateWindowShowStateFromStateType();
    414   window_state->NotifyPreStateTypeChange(previous_state_type);
    415 
    416   // This Docked/Snapped hack is due to the issue that IsDocked returns
    417   // true for dragging window.  TODO(oshima): Make docked window a state
    418   // and remove this hack.
    419   if (window_state->window()->parent() &&
    420       (window_state->IsSnapped() ||
    421        (!window_state->IsDocked() && !IsPanel(window_state->window())))) {
    422     if (!window_state->HasRestoreBounds() &&
    423         (previous_state_type == WINDOW_STATE_TYPE_DEFAULT ||
    424          previous_state_type == WINDOW_STATE_TYPE_NORMAL) &&
    425         !window_state->IsMinimized() &&
    426         !window_state->IsNormalStateType()) {
    427       window_state->SaveCurrentBoundsForRestore();
    428     }
    429 
    430     // When restoring from a minimized state, we want to restore to the previous
    431     // bounds. However, we want to maintain the restore bounds. (The restore
    432     // bounds are set if a user maximized the window in one axis by double
    433     // clicking the window border for example).
    434     gfx::Rect restore_bounds_in_screen;
    435     if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED &&
    436         window_state->IsNormalStateType() &&
    437         window_state->HasRestoreBounds() &&
    438         !window_state->unminimize_to_restore_bounds()) {
    439       restore_bounds_in_screen = window_state->GetRestoreBoundsInScreen();
    440       window_state->SaveCurrentBoundsForRestore();
    441     }
    442 
    443     if (window_state->IsMaximizedOrFullscreen())
    444       MoveToDisplayForRestore(window_state);
    445 
    446     UpdateBoundsFromState(window_state, previous_state_type);
    447 
    448     // Normal state should have no restore bounds unless it's
    449     // unminimzied.
    450     if (!restore_bounds_in_screen.IsEmpty())
    451       window_state->SetRestoreBoundsInScreen(restore_bounds_in_screen);
    452     else if (window_state->IsNormalStateType())
    453       window_state->ClearRestoreBounds();
    454   }
    455   window_state->NotifyPostStateTypeChange(previous_state_type);
    456 }
    457 
    458 void DefaultState::ReenterToCurrentState(
    459     WindowState* window_state,
    460     WindowState::State* state_in_previous_mode) {
    461   WindowStateType previous_state_type = state_in_previous_mode->GetType();
    462   if (previous_state_type == wm::WINDOW_STATE_TYPE_FULLSCREEN) {
    463     // A state change should not move a window out of full screen since full
    464     // screen is a "special mode" the user wanted to be in and should be
    465     // respected as such.
    466     state_type_ = wm::WINDOW_STATE_TYPE_FULLSCREEN;
    467   }
    468   window_state->UpdateWindowShowStateFromStateType();
    469   window_state->NotifyPreStateTypeChange(previous_state_type);
    470 
    471   if ((state_type_ == wm::WINDOW_STATE_TYPE_NORMAL ||
    472        state_type_ == wm::WINDOW_STATE_TYPE_DEFAULT) &&
    473       !stored_bounds_.IsEmpty()) {
    474     // Use the restore mechanism to set the bounds for
    475     // the window in normal state. This also covers unminimize case.
    476     window_state->SetRestoreBoundsInParent(stored_bounds_);
    477   }
    478 
    479   UpdateBoundsFromState(window_state, state_in_previous_mode->GetType());
    480 
    481   // Then restore the restore bounds to their previous value.
    482   if (!stored_restore_bounds_.IsEmpty())
    483     window_state->SetRestoreBoundsInParent(stored_restore_bounds_);
    484   else
    485     window_state->ClearRestoreBounds();
    486 
    487   window_state->NotifyPostStateTypeChange(previous_state_type);
    488 }
    489 
    490 void DefaultState::UpdateBoundsFromState(WindowState* window_state,
    491                                          WindowStateType previous_state_type) {
    492   aura::Window* window = window_state->window();
    493   gfx::Rect bounds_in_parent;
    494   switch (state_type_) {
    495     case WINDOW_STATE_TYPE_LEFT_SNAPPED:
    496     case WINDOW_STATE_TYPE_RIGHT_SNAPPED:
    497       bounds_in_parent = state_type_ == WINDOW_STATE_TYPE_LEFT_SNAPPED ?
    498           GetDefaultLeftSnappedWindowBoundsInParent(window_state->window()) :
    499           GetDefaultRightSnappedWindowBoundsInParent(window_state->window());
    500       break;
    501     case WINDOW_STATE_TYPE_DEFAULT:
    502     case WINDOW_STATE_TYPE_NORMAL: {
    503       gfx::Rect work_area_in_parent =
    504           ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
    505       if (window_state->HasRestoreBounds())
    506         bounds_in_parent = window_state->GetRestoreBoundsInParent();
    507       else
    508         bounds_in_parent = window->bounds();
    509       // Make sure that part of the window is always visible.
    510       AdjustBoundsToEnsureMinimumWindowVisibility(
    511           work_area_in_parent, &bounds_in_parent);
    512       break;
    513     }
    514     case WINDOW_STATE_TYPE_MAXIMIZED:
    515       bounds_in_parent = ScreenUtil::GetMaximizedWindowBoundsInParent(window);
    516       break;
    517 
    518     case WINDOW_STATE_TYPE_FULLSCREEN:
    519       bounds_in_parent = ScreenUtil::GetDisplayBoundsInParent(window);
    520       break;
    521 
    522     case WINDOW_STATE_TYPE_MINIMIZED:
    523       break;
    524     case WINDOW_STATE_TYPE_INACTIVE:
    525     case WINDOW_STATE_TYPE_DETACHED:
    526     case WINDOW_STATE_TYPE_END:
    527     case WINDOW_STATE_TYPE_AUTO_POSITIONED:
    528       return;
    529   }
    530 
    531   if (state_type_ != WINDOW_STATE_TYPE_MINIMIZED) {
    532     if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED ||
    533         window_state->IsFullscreen()) {
    534       window_state->SetBoundsDirect(bounds_in_parent);
    535     } else if (window_state->IsMaximized() ||
    536                IsMaximizedOrFullscreenWindowStateType(previous_state_type)) {
    537       window_state->SetBoundsDirectCrossFade(bounds_in_parent);
    538     } else if (window_state->is_dragged()) {
    539       // SetBoundsDirectAnimated does not work when the window gets reparented.
    540       // TODO(oshima): Consider fixing it and reenable the animation.
    541       window_state->SetBoundsDirect(bounds_in_parent);
    542     } else {
    543       window_state->SetBoundsDirectAnimated(bounds_in_parent);
    544     }
    545   }
    546 
    547   if (window_state->IsMinimized()) {
    548     // Save the previous show state so that we can correctly restore it.
    549     window_state->window()->SetProperty(aura::client::kRestoreShowStateKey,
    550                                         ToWindowShowState(previous_state_type));
    551     ::wm::SetWindowVisibilityAnimationType(
    552         window_state->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
    553 
    554     // Hide the window.
    555     window_state->window()->Hide();
    556     // Activate another window.
    557     if (window_state->IsActive())
    558       window_state->Deactivate();
    559   } else if ((window_state->window()->TargetVisibility() ||
    560               previous_state_type == WINDOW_STATE_TYPE_MINIMIZED) &&
    561              !window_state->window()->layer()->visible()) {
    562     // The layer may be hidden if the window was previously minimized. Make
    563     // sure it's visible.
    564     window_state->window()->Show();
    565     if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED &&
    566         !window_state->IsMaximizedOrFullscreen()) {
    567       window_state->set_unminimize_to_restore_bounds(false);
    568     }
    569   }
    570 }
    571 
    572 // static
    573 void DefaultState::CenterWindow(WindowState* window_state) {
    574   if (!window_state->IsNormalOrSnapped())
    575     return;
    576   aura::Window* window = window_state->window();
    577   if (window_state->IsSnapped()) {
    578     gfx::Rect center_in_screen =
    579         Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
    580     gfx::Size size = window_state->HasRestoreBounds() ?
    581         window_state->GetRestoreBoundsInScreen().size() :
    582         window->bounds().size();
    583     center_in_screen.ClampToCenteredSize(size);
    584     window_state->SetRestoreBoundsInScreen(center_in_screen);
    585     window_state->Restore();
    586   } else {
    587     gfx::Rect center_in_parent =
    588         ScreenUtil::GetDisplayWorkAreaBoundsInParent(window);
    589     center_in_parent.ClampToCenteredSize(window->bounds().size());
    590     window_state->SetBoundsDirectAnimated(center_in_parent);
    591   }
    592   // Centering window is treated as if a user moved and resized the window.
    593   window_state->set_bounds_changed_by_user(true);
    594 }
    595 
    596 }  // namespace wm
    597 }  // namespace ash
    598