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.
5 #include "ash/wm/default_state.h"
7 #include "ash/display/window_tree_host_manager.h"
8 #include "ash/screen_util.h"
10 #include "ash/shell_window_ids.h"
11 #include "ash/wm/coordinate_conversion.h"
12 #include "ash/wm/dock/docked_window_layout_manager.h"
13 #include "ash/wm/window_animations.h"
14 #include "ash/wm/window_state.h"
15 #include "ash/wm/window_state_delegate.h"
16 #include "ash/wm/window_state_util.h"
17 #include "ash/wm/window_util.h"
18 #include "ash/wm/wm_event.h"
19 #include "ash/wm/workspace/workspace_window_resizer.h"
20 #include "ui/aura/client/aura_constants.h"
21 #include "ui/aura/window.h"
22 #include "ui/aura/window_delegate.h"
23 #include "ui/gfx/display.h"
24 #include "ui/gfx/geometry/rect.h"
30 // This specifies how much percent (30%) of a window rect
31 // must be visible when the window is added to the workspace.
32 const float kMinimumPercentOnScreenArea
= 0.3f
;
34 // When a window that has restore bounds at least as large as a work area is
35 // unmaximized, inset the bounds slightly so that they are not exactly the same.
36 // This makes it easier to resize the window.
37 const int kMaximizedWindowInset
= 10; // DIPs.
39 bool IsMinimizedWindowState(const WindowStateType state_type
) {
40 return state_type
== WINDOW_STATE_TYPE_MINIMIZED
||
41 state_type
== WINDOW_STATE_TYPE_DOCKED_MINIMIZED
;
44 void MoveToDisplayForRestore(WindowState
* window_state
) {
45 if (!window_state
->HasRestoreBounds())
47 const gfx::Rect
& restore_bounds
= window_state
->GetRestoreBoundsInScreen();
49 // Move only if the restore bounds is outside of
50 // the display. There is no information about in which
51 // display it should be restored, so this is best guess.
52 // TODO(oshima): Restore information should contain the
53 // work area information like WindowResizer does for the
54 // last window location.
55 gfx::Rect display_area
= Shell::GetScreen()->GetDisplayNearestWindow(
56 window_state
->window()).bounds();
58 if (!display_area
.Intersects(restore_bounds
)) {
59 const gfx::Display
& display
=
60 Shell::GetScreen()->GetDisplayMatching(restore_bounds
);
61 WindowTreeHostManager
* window_tree_host_manager
=
62 Shell::GetInstance()->window_tree_host_manager();
63 aura::Window
* new_root
=
64 window_tree_host_manager
->GetRootWindowForDisplayId(display
.id());
65 if (new_root
!= window_state
->window()->GetRootWindow()) {
66 aura::Window
* new_container
=
67 Shell::GetContainer(new_root
, window_state
->window()->parent()->id());
68 new_container
->AddChild(window_state
->window());
73 DockedWindowLayoutManager
* GetDockedWindowLayoutManager() {
74 aura::Window
* active_window
= ash::wm::GetActiveWindow();
76 aura::Window
* dock_container
= Shell::GetContainer(
77 active_window
->GetRootWindow(), kShellWindowId_DockedContainer
);
78 DockedWindowLayoutManager
* dock_layout
=
79 static_cast<DockedWindowLayoutManager
*>(
80 dock_container
->layout_manager());
86 class ScopedPreferredAlignmentResetter
{
88 ScopedPreferredAlignmentResetter(DockedAlignment dock_alignment
,
89 DockedWindowLayoutManager
* dock_layout
)
90 : docked_window_layout_manager_(dock_layout
) {
91 docked_window_layout_manager_
->set_preferred_alignment(dock_alignment
);
93 ~ScopedPreferredAlignmentResetter() {
94 docked_window_layout_manager_
->set_preferred_alignment(
95 DOCKED_ALIGNMENT_NONE
);
99 DockedWindowLayoutManager
* docked_window_layout_manager_
;
101 DISALLOW_COPY_AND_ASSIGN(ScopedPreferredAlignmentResetter
);
104 class ScopedDockedLayoutEventSourceResetter
{
106 ScopedDockedLayoutEventSourceResetter(DockedWindowLayoutManager
* dock_layout
)
107 : docked_window_layout_manager_(dock_layout
) {
108 docked_window_layout_manager_
->set_event_source(
109 DOCKED_ACTION_SOURCE_KEYBOARD
);
111 ~ScopedDockedLayoutEventSourceResetter() {
112 docked_window_layout_manager_
->set_event_source(
113 DOCKED_ACTION_SOURCE_UNKNOWN
);
117 DockedWindowLayoutManager
* docked_window_layout_manager_
;
119 DISALLOW_COPY_AND_ASSIGN(ScopedDockedLayoutEventSourceResetter
);
122 void CycleSnapDock(WindowState
* window_state
, WMEventType event
) {
123 DockedWindowLayoutManager
* dock_layout
= GetDockedWindowLayoutManager();
124 wm::WindowStateType desired_snap_state
= event
==
125 WM_EVENT_CYCLE_SNAP_DOCK_LEFT
? wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
:
126 wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
;
127 DockedAlignment desired_dock_alignment
= event
==
128 WM_EVENT_CYCLE_SNAP_DOCK_LEFT
?
129 DOCKED_ALIGNMENT_LEFT
: DOCKED_ALIGNMENT_RIGHT
;
130 DockedAlignment current_dock_alignment
= dock_layout
?
131 dock_layout
->CalculateAlignment() : DOCKED_ALIGNMENT_NONE
;
133 if (!window_state
->IsDocked() ||
134 (current_dock_alignment
!= DOCKED_ALIGNMENT_NONE
&&
135 current_dock_alignment
!= desired_dock_alignment
)) {
136 if (window_state
->CanSnap() &&
137 window_state
->GetStateType() != desired_snap_state
&&
138 window_state
->window()->type() != ui::wm::WINDOW_TYPE_PANEL
) {
139 const wm::WMEvent
event(desired_snap_state
==
140 wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
?
141 wm::WM_EVENT_SNAP_LEFT
: wm::WM_EVENT_SNAP_RIGHT
);
142 window_state
->OnWMEvent(&event
);
147 dock_layout
->CanDockWindow(window_state
->window(),
148 desired_dock_alignment
)) {
149 if (window_state
->IsDocked()) {
150 dock_layout
->MaybeSetDesiredDockedAlignment(desired_dock_alignment
);
154 ScopedDockedLayoutEventSourceResetter
event_source_resetter(dock_layout
);
155 ScopedPreferredAlignmentResetter
alignmentResetter(desired_dock_alignment
,
157 const wm::WMEvent
event(wm::WM_EVENT_DOCK
);
158 window_state
->OnWMEvent(&event
);
163 if (window_state
->IsDocked() || window_state
->IsSnapped()) {
164 ScopedDockedLayoutEventSourceResetter
event_source_resetter(dock_layout
);
165 window_state
->Restore();
168 ::wm::AnimateWindow(window_state
->window(),
169 ::wm::WINDOW_ANIMATION_TYPE_BOUNCE
);
174 DefaultState::DefaultState(WindowStateType initial_state_type
)
175 : state_type_(initial_state_type
), stored_window_state_(nullptr) {}
176 DefaultState::~DefaultState() {}
178 void DefaultState::OnWMEvent(WindowState
* window_state
,
179 const WMEvent
* event
) {
180 if (ProcessWorkspaceEvents(window_state
, event
))
183 if (ProcessCompoundEvents(window_state
, event
))
186 WindowStateType current_state_type
= window_state
->GetStateType();
187 WindowStateType next_state_type
= WINDOW_STATE_TYPE_NORMAL
;
188 switch (event
->type()) {
189 case WM_EVENT_NORMAL
:
191 current_state_type
== WINDOW_STATE_TYPE_DOCKED_MINIMIZED
?
192 WINDOW_STATE_TYPE_DOCKED
: WINDOW_STATE_TYPE_NORMAL
;
194 case WM_EVENT_MAXIMIZE
:
195 next_state_type
= WINDOW_STATE_TYPE_MAXIMIZED
;
197 case WM_EVENT_MINIMIZE
:
199 current_state_type
== WINDOW_STATE_TYPE_DOCKED
?
200 WINDOW_STATE_TYPE_DOCKED_MINIMIZED
: WINDOW_STATE_TYPE_MINIMIZED
;
202 case WM_EVENT_FULLSCREEN
:
203 next_state_type
= WINDOW_STATE_TYPE_FULLSCREEN
;
205 case WM_EVENT_SNAP_LEFT
:
206 next_state_type
= WINDOW_STATE_TYPE_LEFT_SNAPPED
;
208 case WM_EVENT_SNAP_RIGHT
:
209 next_state_type
= WINDOW_STATE_TYPE_RIGHT_SNAPPED
;
212 next_state_type
= WINDOW_STATE_TYPE_DOCKED
;
214 case WM_EVENT_SET_BOUNDS
:
215 SetBounds(window_state
, static_cast<const SetBoundsEvent
*>(event
));
217 case WM_EVENT_SHOW_INACTIVE
:
218 next_state_type
= WINDOW_STATE_TYPE_INACTIVE
;
220 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION
:
221 case WM_EVENT_TOGGLE_MAXIMIZE
:
222 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE
:
223 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE
:
224 case WM_EVENT_TOGGLE_FULLSCREEN
:
225 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT
:
226 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT
:
227 case WM_EVENT_CENTER
:
228 NOTREACHED() << "Compound event should not reach here:" << event
;
230 case WM_EVENT_ADDED_TO_WORKSPACE
:
231 case WM_EVENT_WORKAREA_BOUNDS_CHANGED
:
232 case WM_EVENT_DISPLAY_BOUNDS_CHANGED
:
233 NOTREACHED() << "Workspace event should not reach here:" << event
;
237 if (next_state_type
== current_state_type
&& window_state
->IsSnapped()) {
238 gfx::Rect snapped_bounds
= event
->type() == WM_EVENT_SNAP_LEFT
?
239 GetDefaultLeftSnappedWindowBoundsInParent(window_state
->window()) :
240 GetDefaultRightSnappedWindowBoundsInParent(window_state
->window());
241 window_state
->SetBoundsDirectAnimated(snapped_bounds
);
245 EnterToNextState(window_state
, next_state_type
);
248 WindowStateType
DefaultState::GetType() const {
252 void DefaultState::AttachState(
253 WindowState
* window_state
,
254 WindowState::State
* state_in_previous_mode
) {
255 DCHECK_EQ(stored_window_state_
, window_state
);
257 ReenterToCurrentState(window_state
, state_in_previous_mode
);
259 // If the display has changed while in the another mode,
260 // we need to let windows know the change.
261 gfx::Display current_display
= Shell::GetScreen()->
262 GetDisplayNearestWindow(window_state
->window());
263 if (stored_display_state_
.bounds() != current_display
.bounds()) {
264 const WMEvent
event(wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED
);
265 window_state
->OnWMEvent(&event
);
266 } else if (stored_display_state_
.work_area() != current_display
.work_area()) {
267 const WMEvent
event(wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED
);
268 window_state
->OnWMEvent(&event
);
272 void DefaultState::DetachState(WindowState
* window_state
) {
273 stored_window_state_
= window_state
;
274 aura::Window
* window
= window_state
->window();
275 stored_bounds_
= window
->bounds();
276 stored_restore_bounds_
= window_state
->HasRestoreBounds() ?
277 window_state
->GetRestoreBoundsInParent() : gfx::Rect();
278 // Remember the display state so that in case of the display change
279 // while in the other mode, we can perform necessary action to
280 // restore the window state to the proper state for the current
282 stored_display_state_
= Shell::GetScreen()->
283 GetDisplayNearestWindow(window_state
->window());
287 bool DefaultState::ProcessCompoundEvents(WindowState
* window_state
,
288 const WMEvent
* event
) {
289 aura::Window
* window
= window_state
->window();
291 switch (event
->type()) {
292 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION
:
293 if (window_state
->IsFullscreen()) {
294 const wm::WMEvent
event(wm::WM_EVENT_TOGGLE_FULLSCREEN
);
295 window_state
->OnWMEvent(&event
);
296 } else if (window_state
->IsMaximized()) {
297 window_state
->Restore();
298 } else if (window_state
->IsNormalOrSnapped()) {
299 if (window_state
->CanMaximize())
300 window_state
->Maximize();
303 case WM_EVENT_TOGGLE_MAXIMIZE
:
304 if (window_state
->IsFullscreen()) {
305 const wm::WMEvent
event(wm::WM_EVENT_TOGGLE_FULLSCREEN
);
306 window_state
->OnWMEvent(&event
);
307 } else if (window_state
->IsMaximized()) {
308 window_state
->Restore();
309 } else if (window_state
->CanMaximize()) {
310 window_state
->Maximize();
313 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE
: {
314 gfx::Rect work_area
=
315 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window
);
317 // Maximize vertically if:
318 // - The window does not have a max height defined.
319 // - The window has the normal state type. Snapped windows are excluded
320 // because they are already maximized vertically and reverting to the
321 // restored bounds looks weird.
322 if (window
->delegate()->GetMaximumSize().height() != 0 ||
323 !window_state
->IsNormalStateType()) {
326 if (window_state
->HasRestoreBounds() &&
327 (window
->bounds().height() == work_area
.height() &&
328 window
->bounds().y() == work_area
.y())) {
329 window_state
->SetAndClearRestoreBounds();
331 window_state
->SaveCurrentBoundsForRestore();
332 window
->SetBounds(gfx::Rect(window
->bounds().x(),
334 window
->bounds().width(),
335 work_area
.height()));
339 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE
: {
340 // Maximize horizontally if:
341 // - The window does not have a max width defined.
342 // - The window is snapped or has the normal state type.
343 if (window
->delegate()->GetMaximumSize().width() != 0)
345 if (!window_state
->IsNormalOrSnapped())
347 gfx::Rect work_area
=
348 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window
);
349 if (window_state
->IsNormalStateType() &&
350 window_state
->HasRestoreBounds() &&
351 (window
->bounds().width() == work_area
.width() &&
352 window
->bounds().x() == work_area
.x())) {
353 window_state
->SetAndClearRestoreBounds();
355 gfx::Rect
new_bounds(work_area
.x(),
356 window
->bounds().y(),
358 window
->bounds().height());
360 gfx::Rect restore_bounds
= window
->bounds();
361 if (window_state
->IsSnapped()) {
362 window_state
->SetRestoreBoundsInParent(new_bounds
);
363 window_state
->Restore();
365 // The restore logic prevents a window from being restored to bounds
366 // which match the workspace bounds exactly so it is necessary to set
367 // the bounds again below.
370 window_state
->SetRestoreBoundsInParent(restore_bounds
);
371 window
->SetBounds(new_bounds
);
375 case WM_EVENT_TOGGLE_FULLSCREEN
:
376 ToggleFullScreen(window_state
, window_state
->delegate());
378 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT
:
379 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT
:
380 CycleSnapDock(window_state
, event
->type());
382 case WM_EVENT_CENTER
:
383 CenterWindow(window_state
);
385 case WM_EVENT_NORMAL
:
386 case WM_EVENT_MAXIMIZE
:
387 case WM_EVENT_MINIMIZE
:
388 case WM_EVENT_FULLSCREEN
:
389 case WM_EVENT_SNAP_LEFT
:
390 case WM_EVENT_SNAP_RIGHT
:
391 case WM_EVENT_SET_BOUNDS
:
392 case WM_EVENT_SHOW_INACTIVE
:
395 case WM_EVENT_ADDED_TO_WORKSPACE
:
396 case WM_EVENT_WORKAREA_BOUNDS_CHANGED
:
397 case WM_EVENT_DISPLAY_BOUNDS_CHANGED
:
398 NOTREACHED() << "Workspace event should not reach here:" << event
;
404 bool DefaultState::ProcessWorkspaceEvents(WindowState
* window_state
,
405 const WMEvent
* event
) {
406 switch (event
->type()) {
407 case WM_EVENT_ADDED_TO_WORKSPACE
: {
408 // When a window is dragged and dropped onto a different
409 // root window, the bounds will be updated after they are added
410 // to the root window.
411 // If a window is opened as maximized or fullscreen, its bounds may be
412 // empty, so update the bounds now before checking empty.
413 if (window_state
->is_dragged() ||
414 SetMaximizedOrFullscreenBounds(window_state
)) {
418 aura::Window
* window
= window_state
->window();
419 gfx::Rect bounds
= window
->bounds();
421 // Don't adjust window bounds if the bounds are empty as this
422 // happens when a new views::Widget is created.
423 if (bounds
.IsEmpty())
426 // Only windows of type WINDOW_TYPE_NORMAL or WINDOW_TYPE_PANEL need to be
427 // adjusted to have minimum visibility, because they are positioned by the
428 // user and user should always be able to interact with them. Other
429 // windows are positioned programmatically.
430 if (!window_state
->IsUserPositionable())
433 // Use entire display instead of workarea because the workarea can
434 // be further shrunk by the docked area. The logic ensures 30%
435 // visibility which should be enough to see where the window gets
437 gfx::Rect display_area
= ScreenUtil::GetDisplayBoundsInParent(window
);
438 int min_width
= bounds
.width() * kMinimumPercentOnScreenArea
;
439 int min_height
= bounds
.height() * kMinimumPercentOnScreenArea
;
440 AdjustBoundsToEnsureWindowVisibility(
441 display_area
, min_width
, min_height
, &bounds
);
442 window_state
->AdjustSnappedBounds(&bounds
);
443 if (window
->bounds() != bounds
)
444 window_state
->SetBoundsConstrained(bounds
);
447 case WM_EVENT_DISPLAY_BOUNDS_CHANGED
: {
448 if (window_state
->is_dragged() ||
449 SetMaximizedOrFullscreenBounds(window_state
)) {
452 gfx::Rect work_area_in_parent
=
453 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state
->window());
454 gfx::Rect bounds
= window_state
->window()->bounds();
455 // When display bounds has changed, make sure the entire window is fully
457 bounds
.AdjustToFit(work_area_in_parent
);
458 window_state
->AdjustSnappedBounds(&bounds
);
459 if (window_state
->window()->bounds() != bounds
)
460 window_state
->SetBoundsDirectAnimated(bounds
);
463 case WM_EVENT_WORKAREA_BOUNDS_CHANGED
: {
464 if (window_state
->is_dragged() ||
465 SetMaximizedOrFullscreenBounds(window_state
)) {
468 gfx::Rect work_area_in_parent
=
469 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state
->window());
470 gfx::Rect bounds
= window_state
->window()->bounds();
471 AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent
, &bounds
);
472 window_state
->AdjustSnappedBounds(&bounds
);
473 if (window_state
->window()->bounds() != bounds
)
474 window_state
->SetBoundsDirectAnimated(bounds
);
477 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION
:
478 case WM_EVENT_TOGGLE_MAXIMIZE
:
479 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE
:
480 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE
:
481 case WM_EVENT_TOGGLE_FULLSCREEN
:
482 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT
:
483 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT
:
484 case WM_EVENT_CENTER
:
485 case WM_EVENT_NORMAL
:
486 case WM_EVENT_MAXIMIZE
:
487 case WM_EVENT_MINIMIZE
:
488 case WM_EVENT_FULLSCREEN
:
489 case WM_EVENT_SNAP_LEFT
:
490 case WM_EVENT_SNAP_RIGHT
:
491 case WM_EVENT_SET_BOUNDS
:
492 case WM_EVENT_SHOW_INACTIVE
:
500 bool DefaultState::SetMaximizedOrFullscreenBounds(WindowState
* window_state
) {
501 DCHECK(!window_state
->is_dragged());
502 if (window_state
->IsMaximized()) {
503 window_state
->SetBoundsDirect(
504 ScreenUtil::GetMaximizedWindowBoundsInParent(window_state
->window()));
507 if (window_state
->IsFullscreen()) {
508 window_state
->SetBoundsDirect(
509 ScreenUtil::GetDisplayBoundsInParent(window_state
->window()));
516 void DefaultState::SetBounds(WindowState
* window_state
,
517 const SetBoundsEvent
* event
) {
518 if (window_state
->is_dragged()) {
519 // TODO(oshima|varkha): This may be no longer needed, as the dragging
520 // happens in docked window container. crbug.com/485612.
521 window_state
->SetBoundsDirect(event
->requested_bounds());
522 } else if (window_state
->IsSnapped()) {
523 gfx::Rect work_area_in_parent
=
524 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state
->window());
525 gfx::Rect
child_bounds(event
->requested_bounds());
526 AdjustBoundsSmallerThan(work_area_in_parent
.size(), &child_bounds
);
527 window_state
->AdjustSnappedBounds(&child_bounds
);
528 window_state
->SetBoundsDirect(child_bounds
);
529 } else if (!SetMaximizedOrFullscreenBounds(window_state
)) {
530 window_state
->SetBoundsConstrained(event
->requested_bounds());
534 void DefaultState::EnterToNextState(WindowState
* window_state
,
535 WindowStateType next_state_type
) {
536 // Do nothing if we're already in the same state.
537 if (state_type_
== next_state_type
)
540 WindowStateType previous_state_type
= state_type_
;
541 state_type_
= next_state_type
;
543 window_state
->UpdateWindowShowStateFromStateType();
544 window_state
->NotifyPreStateTypeChange(previous_state_type
);
546 if (window_state
->window()->parent()) {
547 if (!window_state
->HasRestoreBounds() &&
548 (previous_state_type
== WINDOW_STATE_TYPE_DEFAULT
||
549 previous_state_type
== WINDOW_STATE_TYPE_NORMAL
) &&
550 !window_state
->IsMinimized() &&
551 !window_state
->IsNormalStateType()) {
552 window_state
->SaveCurrentBoundsForRestore();
555 // When restoring from a minimized state, we want to restore to the
556 // previous bounds. However, we want to maintain the restore bounds.
557 // (The restore bounds are set if a user maximized the window in one
558 // axis by double clicking the window border for example).
559 gfx::Rect restore_bounds_in_screen
;
560 if (previous_state_type
== WINDOW_STATE_TYPE_MINIMIZED
&&
561 window_state
->IsNormalStateType() &&
562 window_state
->HasRestoreBounds() &&
563 !window_state
->unminimize_to_restore_bounds()) {
564 restore_bounds_in_screen
= window_state
->GetRestoreBoundsInScreen();
565 window_state
->SaveCurrentBoundsForRestore();
568 if (window_state
->IsMaximizedOrFullscreen())
569 MoveToDisplayForRestore(window_state
);
571 UpdateBoundsFromState(window_state
, previous_state_type
);
573 // Normal state should have no restore bounds unless it's
575 if (!restore_bounds_in_screen
.IsEmpty())
576 window_state
->SetRestoreBoundsInScreen(restore_bounds_in_screen
);
577 else if (window_state
->IsNormalStateType())
578 window_state
->ClearRestoreBounds();
580 window_state
->NotifyPostStateTypeChange(previous_state_type
);
583 void DefaultState::ReenterToCurrentState(
584 WindowState
* window_state
,
585 WindowState::State
* state_in_previous_mode
) {
586 WindowStateType previous_state_type
= state_in_previous_mode
->GetType();
587 if (previous_state_type
== wm::WINDOW_STATE_TYPE_FULLSCREEN
) {
588 // A state change should not move a window out of full screen since full
589 // screen is a "special mode" the user wanted to be in and should be
590 // respected as such.
591 state_type_
= wm::WINDOW_STATE_TYPE_FULLSCREEN
;
593 window_state
->UpdateWindowShowStateFromStateType();
594 window_state
->NotifyPreStateTypeChange(previous_state_type
);
596 if ((state_type_
== wm::WINDOW_STATE_TYPE_NORMAL
||
597 state_type_
== wm::WINDOW_STATE_TYPE_DEFAULT
) &&
598 !stored_bounds_
.IsEmpty()) {
599 // Use the restore mechanism to set the bounds for
600 // the window in normal state. This also covers unminimize case.
601 window_state
->SetRestoreBoundsInParent(stored_bounds_
);
604 UpdateBoundsFromState(window_state
, state_in_previous_mode
->GetType());
606 // Then restore the restore bounds to their previous value.
607 if (!stored_restore_bounds_
.IsEmpty())
608 window_state
->SetRestoreBoundsInParent(stored_restore_bounds_
);
610 window_state
->ClearRestoreBounds();
612 window_state
->NotifyPostStateTypeChange(previous_state_type
);
615 void DefaultState::UpdateBoundsFromState(WindowState
* window_state
,
616 WindowStateType previous_state_type
) {
617 aura::Window
* window
= window_state
->window();
618 gfx::Rect bounds_in_parent
;
619 switch (state_type_
) {
620 case WINDOW_STATE_TYPE_LEFT_SNAPPED
:
621 case WINDOW_STATE_TYPE_RIGHT_SNAPPED
:
622 bounds_in_parent
= state_type_
== WINDOW_STATE_TYPE_LEFT_SNAPPED
?
623 GetDefaultLeftSnappedWindowBoundsInParent(window_state
->window()) :
624 GetDefaultRightSnappedWindowBoundsInParent(window_state
->window());
626 case WINDOW_STATE_TYPE_DOCKED
: {
627 if (window
->parent()->id() != kShellWindowId_DockedContainer
) {
628 aura::Window
* docked_container
= Shell::GetContainer(
629 window
->GetRootWindow(),
630 kShellWindowId_DockedContainer
);
631 wm::ReparentChildWithTransientChildren(window
,
635 // Return early because we don't want to update the bounds of the
636 // window below; as the bounds are managed by the dock layout.
639 case WINDOW_STATE_TYPE_DEFAULT
:
640 case WINDOW_STATE_TYPE_NORMAL
: {
641 gfx::Rect work_area_in_parent
=
642 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state
->window());
643 if (window_state
->HasRestoreBounds()) {
644 bounds_in_parent
= window_state
->GetRestoreBoundsInParent();
645 // Check if the |window|'s restored size is bigger than the working area
646 // This may happen if a window was resized to maximized bounds or if the
647 // display resolution changed while the window was maximized.
648 if (previous_state_type
== WINDOW_STATE_TYPE_MAXIMIZED
&&
649 bounds_in_parent
.width() >= work_area_in_parent
.width() &&
650 bounds_in_parent
.height() >= work_area_in_parent
.height()) {
651 bounds_in_parent
= work_area_in_parent
;
652 bounds_in_parent
.Inset(kMaximizedWindowInset
, kMaximizedWindowInset
,
653 kMaximizedWindowInset
, kMaximizedWindowInset
);
656 bounds_in_parent
= window
->bounds();
658 // Make sure that part of the window is always visible.
659 AdjustBoundsToEnsureMinimumWindowVisibility(
660 work_area_in_parent
, &bounds_in_parent
);
663 case WINDOW_STATE_TYPE_MAXIMIZED
:
664 bounds_in_parent
= ScreenUtil::GetMaximizedWindowBoundsInParent(window
);
667 case WINDOW_STATE_TYPE_FULLSCREEN
:
668 bounds_in_parent
= ScreenUtil::GetDisplayBoundsInParent(window
);
671 case WINDOW_STATE_TYPE_DOCKED_MINIMIZED
:
672 case WINDOW_STATE_TYPE_MINIMIZED
:
674 case WINDOW_STATE_TYPE_INACTIVE
:
675 case WINDOW_STATE_TYPE_END
:
676 case WINDOW_STATE_TYPE_AUTO_POSITIONED
:
680 if (!window_state
->IsMinimized()) {
681 if (IsMinimizedWindowState(previous_state_type
) ||
682 window_state
->IsFullscreen()) {
683 window_state
->SetBoundsDirect(bounds_in_parent
);
684 } else if (window_state
->IsMaximized() ||
685 IsMaximizedOrFullscreenWindowStateType(previous_state_type
)) {
686 window_state
->SetBoundsDirectCrossFade(bounds_in_parent
);
687 } else if (window_state
->is_dragged()) {
688 // SetBoundsDirectAnimated does not work when the window gets reparented.
689 // TODO(oshima): Consider fixing it and reenable the animation.
690 window_state
->SetBoundsDirect(bounds_in_parent
);
692 window_state
->SetBoundsDirectAnimated(bounds_in_parent
);
696 if (window_state
->IsMinimized()) {
697 // Save the previous show state so that we can correctly restore it.
698 window_state
->window()->SetProperty(aura::client::kRestoreShowStateKey
,
699 ToWindowShowState(previous_state_type
));
700 ::wm::SetWindowVisibilityAnimationType(
701 window_state
->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE
);
704 window_state
->window()->Hide();
705 // Activate another window.
706 if (window_state
->IsActive())
707 window_state
->Deactivate();
708 } else if ((window_state
->window()->TargetVisibility() ||
709 IsMinimizedWindowState(previous_state_type
)) &&
710 !window_state
->window()->layer()->visible()) {
711 // The layer may be hidden if the window was previously minimized. Make
712 // sure it's visible.
713 window_state
->window()->Show();
714 if (IsMinimizedWindowState(previous_state_type
) &&
715 !window_state
->IsMaximizedOrFullscreen()) {
716 window_state
->set_unminimize_to_restore_bounds(false);
722 void DefaultState::CenterWindow(WindowState
* window_state
) {
723 if (!window_state
->IsNormalOrSnapped())
725 aura::Window
* window
= window_state
->window();
726 if (window_state
->IsSnapped()) {
727 gfx::Rect center_in_screen
=
728 Shell::GetScreen()->GetDisplayNearestWindow(window
).work_area();
729 gfx::Size size
= window_state
->HasRestoreBounds() ?
730 window_state
->GetRestoreBoundsInScreen().size() :
731 window
->bounds().size();
732 center_in_screen
.ClampToCenteredSize(size
);
733 window_state
->SetRestoreBoundsInScreen(center_in_screen
);
734 window_state
->Restore();
736 gfx::Rect center_in_parent
=
737 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window
);
738 center_in_parent
.ClampToCenteredSize(window
->bounds().size());
739 window_state
->SetBoundsDirectAnimated(center_in_parent
);
741 // Centering window is treated as if a user moved and resized the window.
742 window_state
->set_bounds_changed_by_user(true);