• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IsPanel(aura::Window * window)33 bool IsPanel(aura::Window* window) {
34   return window->parent() &&
35          window->parent()->id() == kShellWindowId_PanelContainer;
36 }
37 
MoveToDisplayForRestore(WindowState * window_state)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 
DefaultState(WindowStateType initial_state_type)69 DefaultState::DefaultState(WindowStateType initial_state_type)
70     : state_type_(initial_state_type) {}
~DefaultState()71 DefaultState::~DefaultState() {}
72 
OnWMEvent(WindowState * window_state,const WMEvent * event)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 
GetType() const135 WindowStateType DefaultState::GetType() const {
136   return state_type_;
137 }
138 
AttachState(WindowState * window_state,WindowState::State * state_in_previous_mode)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 
DetachState(WindowState * window_state)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
ProcessCompoundEvents(WindowState * window_state,const WMEvent * event)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 
ProcessWorkspaceEvents(WindowState * window_state,const WMEvent * event)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
SetMaximizedOrFullscreenBounds(WindowState * window_state)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
SetBounds(WindowState * window_state,const SetBoundsEvent * event)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 
EnterToNextState(WindowState * window_state,WindowStateType next_state_type)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 
ReenterToCurrentState(WindowState * window_state,WindowState::State * state_in_previous_mode)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 
UpdateBoundsFromState(WindowState * window_state,WindowStateType previous_state_type)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
CenterWindow(WindowState * window_state)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