default_state.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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      else
517        bounds_in_parent = window->bounds();
518      // Make sure that part of the window is always visible.
519      AdjustBoundsToEnsureMinimumWindowVisibility(
520          work_area_in_parent, &bounds_in_parent);
521      break;
522    }
523    case WINDOW_STATE_TYPE_MAXIMIZED:
524      bounds_in_parent = ScreenUtil::GetMaximizedWindowBoundsInParent(window);
525      break;
526
527    case WINDOW_STATE_TYPE_FULLSCREEN:
528      bounds_in_parent = ScreenUtil::GetDisplayBoundsInParent(window);
529      break;
530
531    case WINDOW_STATE_TYPE_MINIMIZED:
532      break;
533    case WINDOW_STATE_TYPE_INACTIVE:
534    case WINDOW_STATE_TYPE_DETACHED:
535    case WINDOW_STATE_TYPE_END:
536    case WINDOW_STATE_TYPE_AUTO_POSITIONED:
537      return;
538  }
539
540  if (state_type_ != WINDOW_STATE_TYPE_MINIMIZED) {
541    if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED ||
542        window_state->IsFullscreen()) {
543      window_state->SetBoundsDirect(bounds_in_parent);
544    } else if (window_state->IsMaximized() ||
545               IsMaximizedOrFullscreenWindowStateType(previous_state_type)) {
546      window_state->SetBoundsDirectCrossFade(bounds_in_parent);
547    } else if (window_state->is_dragged()) {
548      // SetBoundsDirectAnimated does not work when the window gets reparented.
549      // TODO(oshima): Consider fixing it and reenable the animation.
550      window_state->SetBoundsDirect(bounds_in_parent);
551    } else {
552      window_state->SetBoundsDirectAnimated(bounds_in_parent);
553    }
554  }
555
556  if (window_state->IsMinimized()) {
557    // Save the previous show state so that we can correctly restore it.
558    window_state->window()->SetProperty(aura::client::kRestoreShowStateKey,
559                                        ToWindowShowState(previous_state_type));
560    ::wm::SetWindowVisibilityAnimationType(
561        window_state->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
562
563    // Hide the window.
564    window_state->window()->Hide();
565    // Activate another window.
566    if (window_state->IsActive())
567      window_state->Deactivate();
568  } else if ((window_state->window()->TargetVisibility() ||
569              previous_state_type == WINDOW_STATE_TYPE_MINIMIZED) &&
570             !window_state->window()->layer()->visible()) {
571    // The layer may be hidden if the window was previously minimized. Make
572    // sure it's visible.
573    window_state->window()->Show();
574    if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED &&
575        !window_state->IsMaximizedOrFullscreen()) {
576      window_state->set_unminimize_to_restore_bounds(false);
577    }
578  }
579}
580
581// static
582void DefaultState::CenterWindow(WindowState* window_state) {
583  if (!window_state->IsNormalOrSnapped())
584    return;
585  aura::Window* window = window_state->window();
586  if (window_state->IsSnapped()) {
587    gfx::Rect center_in_screen =
588        Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
589    gfx::Size size = window_state->HasRestoreBounds() ?
590        window_state->GetRestoreBoundsInScreen().size() :
591        window->bounds().size();
592    center_in_screen.ClampToCenteredSize(size);
593    window_state->SetRestoreBoundsInScreen(center_in_screen);
594    window_state->Restore();
595  } else {
596    gfx::Rect center_in_parent =
597        ScreenUtil::GetDisplayWorkAreaBoundsInParent(window);
598    center_in_parent.ClampToCenteredSize(window->bounds().size());
599    window_state->SetBoundsDirectAnimated(center_in_parent);
600  }
601  // Centering window is treated as if a user moved and resized the window.
602  window_state->set_bounds_changed_by_user(true);
603}
604
605}  // namespace wm
606}  // namespace ash
607