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