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/display_controller.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 bool IsMinimizedWindowState(const WindowStateType state_type
) {
35 return state_type
== WINDOW_STATE_TYPE_MINIMIZED
||
36 state_type
== WINDOW_STATE_TYPE_DOCKED_MINIMIZED
;
39 void MoveToDisplayForRestore(WindowState
* window_state
) {
40 if (!window_state
->HasRestoreBounds())
42 const gfx::Rect
& restore_bounds
= window_state
->GetRestoreBoundsInScreen();
44 // Move only if the restore bounds is outside of
45 // the display. There is no information about in which
46 // display it should be restored, so this is best guess.
47 // TODO(oshima): Restore information should contain the
48 // work area information like WindowResizer does for the
49 // last window location.
50 gfx::Rect display_area
= Shell::GetScreen()->GetDisplayNearestWindow(
51 window_state
->window()).bounds();
53 if (!display_area
.Intersects(restore_bounds
)) {
54 const gfx::Display
& display
=
55 Shell::GetScreen()->GetDisplayMatching(restore_bounds
);
56 DisplayController
* display_controller
=
57 Shell::GetInstance()->display_controller();
58 aura::Window
* new_root
=
59 display_controller
->GetRootWindowForDisplayId(display
.id());
60 if (new_root
!= window_state
->window()->GetRootWindow()) {
61 aura::Window
* new_container
=
62 Shell::GetContainer(new_root
, window_state
->window()->parent()->id());
63 new_container
->AddChild(window_state
->window());
68 DockedWindowLayoutManager
* GetDockedWindowLayoutManager() {
69 aura::Window
* active_window
= ash::wm::GetActiveWindow();
71 aura::Window
* dock_container
= Shell::GetContainer(
72 active_window
->GetRootWindow(), kShellWindowId_DockedContainer
);
73 DockedWindowLayoutManager
* dock_layout
=
74 static_cast<DockedWindowLayoutManager
*>(
75 dock_container
->layout_manager());
81 class ScopedPreferredAlignmentResetter
{
83 ScopedPreferredAlignmentResetter(DockedAlignment dock_alignment
,
84 DockedWindowLayoutManager
* dock_layout
)
85 : docked_window_layout_manager_(dock_layout
) {
86 docked_window_layout_manager_
->set_preferred_alignment(dock_alignment
);
88 ~ScopedPreferredAlignmentResetter() {
89 docked_window_layout_manager_
->set_preferred_alignment(
90 DOCKED_ALIGNMENT_NONE
);
94 DockedWindowLayoutManager
* docked_window_layout_manager_
;
96 DISALLOW_COPY_AND_ASSIGN(ScopedPreferredAlignmentResetter
);
99 class ScopedDockedLayoutEventSourceResetter
{
101 ScopedDockedLayoutEventSourceResetter(DockedWindowLayoutManager
* dock_layout
)
102 : docked_window_layout_manager_(dock_layout
) {
103 docked_window_layout_manager_
->set_event_source(
104 DOCKED_ACTION_SOURCE_KEYBOARD
);
106 ~ScopedDockedLayoutEventSourceResetter() {
107 docked_window_layout_manager_
->set_event_source(
108 DOCKED_ACTION_SOURCE_UNKNOWN
);
112 DockedWindowLayoutManager
* docked_window_layout_manager_
;
114 DISALLOW_COPY_AND_ASSIGN(ScopedDockedLayoutEventSourceResetter
);
117 void CycleSnapDock(WindowState
* window_state
, WMEventType event
) {
118 DockedWindowLayoutManager
* dock_layout
= GetDockedWindowLayoutManager();
119 wm::WindowStateType desired_snap_state
= event
==
120 WM_EVENT_CYCLE_SNAP_DOCK_LEFT
? wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
:
121 wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED
;
122 DockedAlignment desired_dock_alignment
= event
==
123 WM_EVENT_CYCLE_SNAP_DOCK_LEFT
?
124 DOCKED_ALIGNMENT_LEFT
: DOCKED_ALIGNMENT_RIGHT
;
125 DockedAlignment current_dock_alignment
= dock_layout
?
126 dock_layout
->CalculateAlignment() : DOCKED_ALIGNMENT_NONE
;
128 if (!window_state
->IsDocked() ||
129 (current_dock_alignment
!= DOCKED_ALIGNMENT_NONE
&&
130 current_dock_alignment
!= desired_dock_alignment
)) {
131 if (window_state
->CanSnap() &&
132 window_state
->GetStateType() != desired_snap_state
&&
133 window_state
->window()->type() != ui::wm::WINDOW_TYPE_PANEL
) {
134 const wm::WMEvent
event(desired_snap_state
==
135 wm::WINDOW_STATE_TYPE_LEFT_SNAPPED
?
136 wm::WM_EVENT_SNAP_LEFT
: wm::WM_EVENT_SNAP_RIGHT
);
137 window_state
->OnWMEvent(&event
);
142 dock_layout
->CanDockWindow(window_state
->window(),
143 desired_dock_alignment
)) {
144 if (window_state
->IsDocked()) {
145 dock_layout
->MaybeSetDesiredDockedAlignment(desired_dock_alignment
);
149 ScopedDockedLayoutEventSourceResetter
event_source_resetter(dock_layout
);
150 ScopedPreferredAlignmentResetter
alignmentResetter(desired_dock_alignment
,
152 const wm::WMEvent
event(wm::WM_EVENT_DOCK
);
153 window_state
->OnWMEvent(&event
);
158 if (window_state
->IsDocked() || window_state
->IsSnapped()) {
159 ScopedDockedLayoutEventSourceResetter
event_source_resetter(dock_layout
);
160 window_state
->Restore();
163 ::wm::AnimateWindow(window_state
->window(),
164 ::wm::WINDOW_ANIMATION_TYPE_BOUNCE
);
169 DefaultState::DefaultState(WindowStateType initial_state_type
)
170 : state_type_(initial_state_type
) {}
171 DefaultState::~DefaultState() {}
173 void DefaultState::OnWMEvent(WindowState
* window_state
,
174 const WMEvent
* event
) {
175 if (ProcessWorkspaceEvents(window_state
, event
))
178 if (ProcessCompoundEvents(window_state
, event
))
181 WindowStateType current_state_type
= window_state
->GetStateType();
182 WindowStateType next_state_type
= WINDOW_STATE_TYPE_NORMAL
;
183 switch (event
->type()) {
184 case WM_EVENT_NORMAL
:
186 current_state_type
== WINDOW_STATE_TYPE_DOCKED_MINIMIZED
?
187 WINDOW_STATE_TYPE_DOCKED
: WINDOW_STATE_TYPE_NORMAL
;
189 case WM_EVENT_MAXIMIZE
:
190 next_state_type
= WINDOW_STATE_TYPE_MAXIMIZED
;
192 case WM_EVENT_MINIMIZE
:
194 current_state_type
== WINDOW_STATE_TYPE_DOCKED
?
195 WINDOW_STATE_TYPE_DOCKED_MINIMIZED
: WINDOW_STATE_TYPE_MINIMIZED
;
197 case WM_EVENT_FULLSCREEN
:
198 next_state_type
= WINDOW_STATE_TYPE_FULLSCREEN
;
200 case WM_EVENT_SNAP_LEFT
:
201 next_state_type
= WINDOW_STATE_TYPE_LEFT_SNAPPED
;
203 case WM_EVENT_SNAP_RIGHT
:
204 next_state_type
= WINDOW_STATE_TYPE_RIGHT_SNAPPED
;
207 next_state_type
= WINDOW_STATE_TYPE_DOCKED
;
209 case WM_EVENT_SET_BOUNDS
:
210 SetBounds(window_state
, static_cast<const SetBoundsEvent
*>(event
));
212 case WM_EVENT_SHOW_INACTIVE
:
213 next_state_type
= WINDOW_STATE_TYPE_INACTIVE
;
215 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION
:
216 case WM_EVENT_TOGGLE_MAXIMIZE
:
217 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE
:
218 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE
:
219 case WM_EVENT_TOGGLE_FULLSCREEN
:
220 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT
:
221 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT
:
222 case WM_EVENT_CENTER
:
223 NOTREACHED() << "Compound event should not reach here:" << event
;
225 case WM_EVENT_ADDED_TO_WORKSPACE
:
226 case WM_EVENT_WORKAREA_BOUNDS_CHANGED
:
227 case WM_EVENT_DISPLAY_BOUNDS_CHANGED
:
228 NOTREACHED() << "Workspace event should not reach here:" << event
;
232 if (next_state_type
== current_state_type
&& window_state
->IsSnapped()) {
233 gfx::Rect snapped_bounds
= event
->type() == WM_EVENT_SNAP_LEFT
?
234 GetDefaultLeftSnappedWindowBoundsInParent(window_state
->window()) :
235 GetDefaultRightSnappedWindowBoundsInParent(window_state
->window());
236 window_state
->SetBoundsDirectAnimated(snapped_bounds
);
240 EnterToNextState(window_state
, next_state_type
);
243 WindowStateType
DefaultState::GetType() const {
247 void DefaultState::AttachState(
248 WindowState
* window_state
,
249 WindowState::State
* state_in_previous_mode
) {
250 DCHECK_EQ(stored_window_state_
, window_state
);
252 ReenterToCurrentState(window_state
, state_in_previous_mode
);
254 // If the display has changed while in the another mode,
255 // we need to let windows know the change.
256 gfx::Display current_display
= Shell::GetScreen()->
257 GetDisplayNearestWindow(window_state
->window());
258 if (stored_display_state_
.bounds() != current_display
.bounds()) {
259 const WMEvent
event(wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED
);
260 window_state
->OnWMEvent(&event
);
261 } else if (stored_display_state_
.work_area() != current_display
.work_area()) {
262 const WMEvent
event(wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED
);
263 window_state
->OnWMEvent(&event
);
267 void DefaultState::DetachState(WindowState
* window_state
) {
268 stored_window_state_
= window_state
;
269 aura::Window
* window
= window_state
->window();
270 stored_bounds_
= window
->bounds();
271 stored_restore_bounds_
= window_state
->HasRestoreBounds() ?
272 window_state
->GetRestoreBoundsInParent() : gfx::Rect();
273 // Remember the display state so that in case of the display change
274 // while in the other mode, we can perform necessary action to
275 // restore the window state to the proper state for the current
277 stored_display_state_
= Shell::GetScreen()->
278 GetDisplayNearestWindow(window_state
->window());
282 bool DefaultState::ProcessCompoundEvents(WindowState
* window_state
,
283 const WMEvent
* event
) {
284 aura::Window
* window
= window_state
->window();
286 switch (event
->type()) {
287 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION
:
288 if (window_state
->IsFullscreen()) {
289 const wm::WMEvent
event(wm::WM_EVENT_TOGGLE_FULLSCREEN
);
290 window_state
->OnWMEvent(&event
);
291 } else if (window_state
->IsMaximized()) {
292 window_state
->Restore();
293 } else if (window_state
->IsNormalOrSnapped()) {
294 if (window_state
->CanMaximize())
295 window_state
->Maximize();
298 case WM_EVENT_TOGGLE_MAXIMIZE
:
299 if (window_state
->IsFullscreen()) {
300 const wm::WMEvent
event(wm::WM_EVENT_TOGGLE_FULLSCREEN
);
301 window_state
->OnWMEvent(&event
);
302 } else if (window_state
->IsMaximized()) {
303 window_state
->Restore();
304 } else if (window_state
->CanMaximize()) {
305 window_state
->Maximize();
308 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE
: {
309 gfx::Rect work_area
=
310 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window
);
312 // Maximize vertically if:
313 // - The window does not have a max height defined.
314 // - The window has the normal state type. Snapped windows are excluded
315 // because they are already maximized vertically and reverting to the
316 // restored bounds looks weird.
317 if (window
->delegate()->GetMaximumSize().height() != 0 ||
318 !window_state
->IsNormalStateType()) {
321 if (window_state
->HasRestoreBounds() &&
322 (window
->bounds().height() == work_area
.height() &&
323 window
->bounds().y() == work_area
.y())) {
324 window_state
->SetAndClearRestoreBounds();
326 window_state
->SaveCurrentBoundsForRestore();
327 window
->SetBounds(gfx::Rect(window
->bounds().x(),
329 window
->bounds().width(),
330 work_area
.height()));
334 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE
: {
335 // Maximize horizontally if:
336 // - The window does not have a max width defined.
337 // - The window is snapped or has the normal state type.
338 if (window
->delegate()->GetMaximumSize().width() != 0)
340 if (!window_state
->IsNormalOrSnapped())
342 gfx::Rect work_area
=
343 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window
);
344 if (window_state
->IsNormalStateType() &&
345 window_state
->HasRestoreBounds() &&
346 (window
->bounds().width() == work_area
.width() &&
347 window
->bounds().x() == work_area
.x())) {
348 window_state
->SetAndClearRestoreBounds();
350 gfx::Rect
new_bounds(work_area
.x(),
351 window
->bounds().y(),
353 window
->bounds().height());
355 gfx::Rect restore_bounds
= window
->bounds();
356 if (window_state
->IsSnapped()) {
357 window_state
->SetRestoreBoundsInParent(new_bounds
);
358 window_state
->Restore();
360 // The restore logic prevents a window from being restored to bounds
361 // which match the workspace bounds exactly so it is necessary to set
362 // the bounds again below.
365 window_state
->SetRestoreBoundsInParent(restore_bounds
);
366 window
->SetBounds(new_bounds
);
370 case WM_EVENT_TOGGLE_FULLSCREEN
:
371 ToggleFullScreen(window_state
, window_state
->delegate());
373 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT
:
374 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT
:
375 CycleSnapDock(window_state
, event
->type());
377 case WM_EVENT_CENTER
:
378 CenterWindow(window_state
);
380 case WM_EVENT_NORMAL
:
381 case WM_EVENT_MAXIMIZE
:
382 case WM_EVENT_MINIMIZE
:
383 case WM_EVENT_FULLSCREEN
:
384 case WM_EVENT_SNAP_LEFT
:
385 case WM_EVENT_SNAP_RIGHT
:
386 case WM_EVENT_SET_BOUNDS
:
387 case WM_EVENT_SHOW_INACTIVE
:
390 case WM_EVENT_ADDED_TO_WORKSPACE
:
391 case WM_EVENT_WORKAREA_BOUNDS_CHANGED
:
392 case WM_EVENT_DISPLAY_BOUNDS_CHANGED
:
393 NOTREACHED() << "Workspace event should not reach here:" << event
;
399 bool DefaultState::ProcessWorkspaceEvents(WindowState
* window_state
,
400 const WMEvent
* event
) {
401 switch (event
->type()) {
402 case WM_EVENT_ADDED_TO_WORKSPACE
: {
403 // When a window is dragged and dropped onto a different
404 // root window, the bounds will be updated after they are added
405 // to the root window.
406 // If a window is opened as maximized or fullscreen, its bounds may be
407 // empty, so update the bounds now before checking empty.
408 if (window_state
->is_dragged() ||
409 SetMaximizedOrFullscreenBounds(window_state
)) {
413 aura::Window
* window
= window_state
->window();
414 gfx::Rect bounds
= window
->bounds();
416 // Don't adjust window bounds if the bounds are empty as this
417 // happens when a new views::Widget is created.
418 if (bounds
.IsEmpty())
421 // Only windows of type WINDOW_TYPE_NORMAL or WINDOW_TYPE_PANEL need to be
422 // adjusted to have minimum visibility, because they are positioned by the
423 // user and user should always be able to interact with them. Other
424 // windows are positioned programmatically.
425 if (window_state
->window()->type() != ui::wm::WINDOW_TYPE_NORMAL
&&
426 window_state
->window()->type() != ui::wm::WINDOW_TYPE_PANEL
) {
430 // Use entire display instead of workarea because the workarea can
431 // be further shrunk by the docked area. The logic ensures 30%
432 // visibility which should be enough to see where the window gets
434 gfx::Rect display_area
= ScreenUtil::GetDisplayBoundsInParent(window
);
435 int min_width
= bounds
.width() * kMinimumPercentOnScreenArea
;
436 int min_height
= bounds
.height() * kMinimumPercentOnScreenArea
;
437 AdjustBoundsToEnsureWindowVisibility(
438 display_area
, min_width
, min_height
, &bounds
);
439 window_state
->AdjustSnappedBounds(&bounds
);
440 if (window
->bounds() != bounds
)
441 window_state
->SetBoundsConstrained(bounds
);
444 case WM_EVENT_DISPLAY_BOUNDS_CHANGED
: {
445 if (window_state
->is_dragged() ||
446 SetMaximizedOrFullscreenBounds(window_state
)) {
449 gfx::Rect work_area_in_parent
=
450 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state
->window());
451 gfx::Rect bounds
= window_state
->window()->bounds();
452 // When display bounds has changed, make sure the entire window is fully
454 bounds
.AdjustToFit(work_area_in_parent
);
455 window_state
->AdjustSnappedBounds(&bounds
);
456 if (window_state
->window()->bounds() != bounds
)
457 window_state
->SetBoundsDirectAnimated(bounds
);
460 case WM_EVENT_WORKAREA_BOUNDS_CHANGED
: {
461 if (window_state
->is_dragged() ||
462 SetMaximizedOrFullscreenBounds(window_state
)) {
465 gfx::Rect work_area_in_parent
=
466 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state
->window());
467 gfx::Rect bounds
= window_state
->window()->bounds();
468 AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent
, &bounds
);
469 window_state
->AdjustSnappedBounds(&bounds
);
470 if (window_state
->window()->bounds() != bounds
)
471 window_state
->SetBoundsDirectAnimated(bounds
);
474 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION
:
475 case WM_EVENT_TOGGLE_MAXIMIZE
:
476 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE
:
477 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE
:
478 case WM_EVENT_TOGGLE_FULLSCREEN
:
479 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT
:
480 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT
:
481 case WM_EVENT_CENTER
:
482 case WM_EVENT_NORMAL
:
483 case WM_EVENT_MAXIMIZE
:
484 case WM_EVENT_MINIMIZE
:
485 case WM_EVENT_FULLSCREEN
:
486 case WM_EVENT_SNAP_LEFT
:
487 case WM_EVENT_SNAP_RIGHT
:
488 case WM_EVENT_SET_BOUNDS
:
489 case WM_EVENT_SHOW_INACTIVE
:
497 bool DefaultState::SetMaximizedOrFullscreenBounds(WindowState
* window_state
) {
498 DCHECK(!window_state
->is_dragged());
499 if (window_state
->IsMaximized()) {
500 window_state
->SetBoundsDirect(
501 ScreenUtil::GetMaximizedWindowBoundsInParent(window_state
->window()));
504 if (window_state
->IsFullscreen()) {
505 window_state
->SetBoundsDirect(
506 ScreenUtil::GetDisplayBoundsInParent(window_state
->window()));
513 void DefaultState::SetBounds(WindowState
* window_state
,
514 const SetBoundsEvent
* event
) {
515 if (window_state
->is_dragged()) {
516 window_state
->SetBoundsDirect(event
->requested_bounds());
517 } else if (window_state
->IsSnapped()) {
518 gfx::Rect work_area_in_parent
=
519 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state
->window());
520 gfx::Rect
child_bounds(event
->requested_bounds());
521 AdjustBoundsSmallerThan(work_area_in_parent
.size(), &child_bounds
);
522 window_state
->AdjustSnappedBounds(&child_bounds
);
523 window_state
->SetBoundsDirect(child_bounds
);
524 } else if (!SetMaximizedOrFullscreenBounds(window_state
)) {
525 window_state
->SetBoundsConstrained(event
->requested_bounds());
529 void DefaultState::EnterToNextState(WindowState
* window_state
,
530 WindowStateType next_state_type
) {
531 // Do nothing if we're already in the same state.
532 if (state_type_
== next_state_type
)
535 WindowStateType previous_state_type
= state_type_
;
536 state_type_
= next_state_type
;
538 window_state
->UpdateWindowShowStateFromStateType();
539 window_state
->NotifyPreStateTypeChange(previous_state_type
);
541 if (window_state
->window()->parent()) {
542 if (!window_state
->HasRestoreBounds() &&
543 (previous_state_type
== WINDOW_STATE_TYPE_DEFAULT
||
544 previous_state_type
== WINDOW_STATE_TYPE_NORMAL
) &&
545 !window_state
->IsMinimized() &&
546 !window_state
->IsNormalStateType()) {
547 window_state
->SaveCurrentBoundsForRestore();
550 // When restoring from a minimized state, we want to restore to the
551 // previous bounds. However, we want to maintain the restore bounds.
552 // (The restore bounds are set if a user maximized the window in one
553 // axis by double clicking the window border for example).
554 gfx::Rect restore_bounds_in_screen
;
555 if (previous_state_type
== WINDOW_STATE_TYPE_MINIMIZED
&&
556 window_state
->IsNormalStateType() &&
557 window_state
->HasRestoreBounds() &&
558 !window_state
->unminimize_to_restore_bounds()) {
559 restore_bounds_in_screen
= window_state
->GetRestoreBoundsInScreen();
560 window_state
->SaveCurrentBoundsForRestore();
563 if (window_state
->IsMaximizedOrFullscreen())
564 MoveToDisplayForRestore(window_state
);
566 UpdateBoundsFromState(window_state
, previous_state_type
);
568 // Normal state should have no restore bounds unless it's
570 if (!restore_bounds_in_screen
.IsEmpty())
571 window_state
->SetRestoreBoundsInScreen(restore_bounds_in_screen
);
572 else if (window_state
->IsNormalStateType())
573 window_state
->ClearRestoreBounds();
575 window_state
->NotifyPostStateTypeChange(previous_state_type
);
578 void DefaultState::ReenterToCurrentState(
579 WindowState
* window_state
,
580 WindowState::State
* state_in_previous_mode
) {
581 WindowStateType previous_state_type
= state_in_previous_mode
->GetType();
582 if (previous_state_type
== wm::WINDOW_STATE_TYPE_FULLSCREEN
) {
583 // A state change should not move a window out of full screen since full
584 // screen is a "special mode" the user wanted to be in and should be
585 // respected as such.
586 state_type_
= wm::WINDOW_STATE_TYPE_FULLSCREEN
;
588 window_state
->UpdateWindowShowStateFromStateType();
589 window_state
->NotifyPreStateTypeChange(previous_state_type
);
591 if ((state_type_
== wm::WINDOW_STATE_TYPE_NORMAL
||
592 state_type_
== wm::WINDOW_STATE_TYPE_DEFAULT
) &&
593 !stored_bounds_
.IsEmpty()) {
594 // Use the restore mechanism to set the bounds for
595 // the window in normal state. This also covers unminimize case.
596 window_state
->SetRestoreBoundsInParent(stored_bounds_
);
599 UpdateBoundsFromState(window_state
, state_in_previous_mode
->GetType());
601 // Then restore the restore bounds to their previous value.
602 if (!stored_restore_bounds_
.IsEmpty())
603 window_state
->SetRestoreBoundsInParent(stored_restore_bounds_
);
605 window_state
->ClearRestoreBounds();
607 window_state
->NotifyPostStateTypeChange(previous_state_type
);
610 void DefaultState::UpdateBoundsFromState(WindowState
* window_state
,
611 WindowStateType previous_state_type
) {
612 aura::Window
* window
= window_state
->window();
613 gfx::Rect bounds_in_parent
;
614 switch (state_type_
) {
615 case WINDOW_STATE_TYPE_LEFT_SNAPPED
:
616 case WINDOW_STATE_TYPE_RIGHT_SNAPPED
:
617 bounds_in_parent
= state_type_
== WINDOW_STATE_TYPE_LEFT_SNAPPED
?
618 GetDefaultLeftSnappedWindowBoundsInParent(window_state
->window()) :
619 GetDefaultRightSnappedWindowBoundsInParent(window_state
->window());
621 case WINDOW_STATE_TYPE_DOCKED
: {
622 if (window
->parent()->id() != kShellWindowId_DockedContainer
) {
623 aura::Window
* docked_container
= Shell::GetContainer(
624 window
->GetRootWindow(),
625 kShellWindowId_DockedContainer
);
626 wm::ReparentChildWithTransientChildren(window
,
630 // Return early because we don't want to update the bounds of the
631 // window below; as the bounds are managed by the dock layout.
634 case WINDOW_STATE_TYPE_DEFAULT
:
635 case WINDOW_STATE_TYPE_NORMAL
: {
636 gfx::Rect work_area_in_parent
=
637 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state
->window());
638 if (window_state
->HasRestoreBounds()) {
639 bounds_in_parent
= window_state
->GetRestoreBoundsInParent();
640 // Check if the |window|'s restored size is bigger than the working area
641 // This may happen if a window was resized to maximized bounds or if the
642 // display resolution changed while the window was maximized.
643 if (previous_state_type
== WINDOW_STATE_TYPE_MAXIMIZED
&&
644 bounds_in_parent
.width() >= work_area_in_parent
.width() &&
645 bounds_in_parent
.height() >= work_area_in_parent
.height()) {
646 // Inset the bounds slightly so that they are not exactly same as
647 // the work area bounds and it is easier to resize the window.
648 bounds_in_parent
= work_area_in_parent
;
649 bounds_in_parent
.Inset(10, 10, 10, 10);
652 bounds_in_parent
= window
->bounds();
654 // Make sure that part of the window is always visible.
655 AdjustBoundsToEnsureMinimumWindowVisibility(
656 work_area_in_parent
, &bounds_in_parent
);
659 case WINDOW_STATE_TYPE_MAXIMIZED
:
660 bounds_in_parent
= ScreenUtil::GetMaximizedWindowBoundsInParent(window
);
663 case WINDOW_STATE_TYPE_FULLSCREEN
:
664 bounds_in_parent
= ScreenUtil::GetDisplayBoundsInParent(window
);
667 case WINDOW_STATE_TYPE_DOCKED_MINIMIZED
:
668 case WINDOW_STATE_TYPE_MINIMIZED
:
670 case WINDOW_STATE_TYPE_INACTIVE
:
671 case WINDOW_STATE_TYPE_END
:
672 case WINDOW_STATE_TYPE_AUTO_POSITIONED
:
676 if (!window_state
->IsMinimized()) {
677 if (IsMinimizedWindowState(previous_state_type
) ||
678 window_state
->IsFullscreen()) {
679 window_state
->SetBoundsDirect(bounds_in_parent
);
680 } else if (window_state
->IsMaximized() ||
681 IsMaximizedOrFullscreenWindowStateType(previous_state_type
)) {
682 window_state
->SetBoundsDirectCrossFade(bounds_in_parent
);
683 } else if (window_state
->is_dragged()) {
684 // SetBoundsDirectAnimated does not work when the window gets reparented.
685 // TODO(oshima): Consider fixing it and reenable the animation.
686 window_state
->SetBoundsDirect(bounds_in_parent
);
688 window_state
->SetBoundsDirectAnimated(bounds_in_parent
);
692 if (window_state
->IsMinimized()) {
693 // Save the previous show state so that we can correctly restore it.
694 window_state
->window()->SetProperty(aura::client::kRestoreShowStateKey
,
695 ToWindowShowState(previous_state_type
));
696 ::wm::SetWindowVisibilityAnimationType(
697 window_state
->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE
);
700 window_state
->window()->Hide();
701 // Activate another window.
702 if (window_state
->IsActive())
703 window_state
->Deactivate();
704 } else if ((window_state
->window()->TargetVisibility() ||
705 IsMinimizedWindowState(previous_state_type
)) &&
706 !window_state
->window()->layer()->visible()) {
707 // The layer may be hidden if the window was previously minimized. Make
708 // sure it's visible.
709 window_state
->window()->Show();
710 if (IsMinimizedWindowState(previous_state_type
) &&
711 !window_state
->IsMaximizedOrFullscreen()) {
712 window_state
->set_unminimize_to_restore_bounds(false);
718 void DefaultState::CenterWindow(WindowState
* window_state
) {
719 if (!window_state
->IsNormalOrSnapped())
721 aura::Window
* window
= window_state
->window();
722 if (window_state
->IsSnapped()) {
723 gfx::Rect center_in_screen
=
724 Shell::GetScreen()->GetDisplayNearestWindow(window
).work_area();
725 gfx::Size size
= window_state
->HasRestoreBounds() ?
726 window_state
->GetRestoreBoundsInScreen().size() :
727 window
->bounds().size();
728 center_in_screen
.ClampToCenteredSize(size
);
729 window_state
->SetRestoreBoundsInScreen(center_in_screen
);
730 window_state
->Restore();
732 gfx::Rect center_in_parent
=
733 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window
);
734 center_in_parent
.ClampToCenteredSize(window
->bounds().size());
735 window_state
->SetBoundsDirectAnimated(center_in_parent
);
737 // Centering window is treated as if a user moved and resized the window.
738 window_state
->set_bounds_changed_by_user(true);