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 // TODO(oshima|varkha): This may be no longer needed, as the dragging
517 // happens in docked window container. crbug.com/485612.
518 window_state
->SetBoundsDirect(event
->requested_bounds());
519 } else if (window_state
->IsSnapped()) {
520 gfx::Rect work_area_in_parent
=
521 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state
->window());
522 gfx::Rect
child_bounds(event
->requested_bounds());
523 AdjustBoundsSmallerThan(work_area_in_parent
.size(), &child_bounds
);
524 window_state
->AdjustSnappedBounds(&child_bounds
);
525 window_state
->SetBoundsDirect(child_bounds
);
526 } else if (!SetMaximizedOrFullscreenBounds(window_state
)) {
527 window_state
->SetBoundsConstrained(event
->requested_bounds());
531 void DefaultState::EnterToNextState(WindowState
* window_state
,
532 WindowStateType next_state_type
) {
533 // Do nothing if we're already in the same state.
534 if (state_type_
== next_state_type
)
537 WindowStateType previous_state_type
= state_type_
;
538 state_type_
= next_state_type
;
540 window_state
->UpdateWindowShowStateFromStateType();
541 window_state
->NotifyPreStateTypeChange(previous_state_type
);
543 if (window_state
->window()->parent()) {
544 if (!window_state
->HasRestoreBounds() &&
545 (previous_state_type
== WINDOW_STATE_TYPE_DEFAULT
||
546 previous_state_type
== WINDOW_STATE_TYPE_NORMAL
) &&
547 !window_state
->IsMinimized() &&
548 !window_state
->IsNormalStateType()) {
549 window_state
->SaveCurrentBoundsForRestore();
552 // When restoring from a minimized state, we want to restore to the
553 // previous bounds. However, we want to maintain the restore bounds.
554 // (The restore bounds are set if a user maximized the window in one
555 // axis by double clicking the window border for example).
556 gfx::Rect restore_bounds_in_screen
;
557 if (previous_state_type
== WINDOW_STATE_TYPE_MINIMIZED
&&
558 window_state
->IsNormalStateType() &&
559 window_state
->HasRestoreBounds() &&
560 !window_state
->unminimize_to_restore_bounds()) {
561 restore_bounds_in_screen
= window_state
->GetRestoreBoundsInScreen();
562 window_state
->SaveCurrentBoundsForRestore();
565 if (window_state
->IsMaximizedOrFullscreen())
566 MoveToDisplayForRestore(window_state
);
568 UpdateBoundsFromState(window_state
, previous_state_type
);
570 // Normal state should have no restore bounds unless it's
572 if (!restore_bounds_in_screen
.IsEmpty())
573 window_state
->SetRestoreBoundsInScreen(restore_bounds_in_screen
);
574 else if (window_state
->IsNormalStateType())
575 window_state
->ClearRestoreBounds();
577 window_state
->NotifyPostStateTypeChange(previous_state_type
);
580 void DefaultState::ReenterToCurrentState(
581 WindowState
* window_state
,
582 WindowState::State
* state_in_previous_mode
) {
583 WindowStateType previous_state_type
= state_in_previous_mode
->GetType();
584 if (previous_state_type
== wm::WINDOW_STATE_TYPE_FULLSCREEN
) {
585 // A state change should not move a window out of full screen since full
586 // screen is a "special mode" the user wanted to be in and should be
587 // respected as such.
588 state_type_
= wm::WINDOW_STATE_TYPE_FULLSCREEN
;
590 window_state
->UpdateWindowShowStateFromStateType();
591 window_state
->NotifyPreStateTypeChange(previous_state_type
);
593 if ((state_type_
== wm::WINDOW_STATE_TYPE_NORMAL
||
594 state_type_
== wm::WINDOW_STATE_TYPE_DEFAULT
) &&
595 !stored_bounds_
.IsEmpty()) {
596 // Use the restore mechanism to set the bounds for
597 // the window in normal state. This also covers unminimize case.
598 window_state
->SetRestoreBoundsInParent(stored_bounds_
);
601 UpdateBoundsFromState(window_state
, state_in_previous_mode
->GetType());
603 // Then restore the restore bounds to their previous value.
604 if (!stored_restore_bounds_
.IsEmpty())
605 window_state
->SetRestoreBoundsInParent(stored_restore_bounds_
);
607 window_state
->ClearRestoreBounds();
609 window_state
->NotifyPostStateTypeChange(previous_state_type
);
612 void DefaultState::UpdateBoundsFromState(WindowState
* window_state
,
613 WindowStateType previous_state_type
) {
614 aura::Window
* window
= window_state
->window();
615 gfx::Rect bounds_in_parent
;
616 switch (state_type_
) {
617 case WINDOW_STATE_TYPE_LEFT_SNAPPED
:
618 case WINDOW_STATE_TYPE_RIGHT_SNAPPED
:
619 bounds_in_parent
= state_type_
== WINDOW_STATE_TYPE_LEFT_SNAPPED
?
620 GetDefaultLeftSnappedWindowBoundsInParent(window_state
->window()) :
621 GetDefaultRightSnappedWindowBoundsInParent(window_state
->window());
623 case WINDOW_STATE_TYPE_DOCKED
: {
624 if (window
->parent()->id() != kShellWindowId_DockedContainer
) {
625 aura::Window
* docked_container
= Shell::GetContainer(
626 window
->GetRootWindow(),
627 kShellWindowId_DockedContainer
);
628 wm::ReparentChildWithTransientChildren(window
,
632 // Return early because we don't want to update the bounds of the
633 // window below; as the bounds are managed by the dock layout.
636 case WINDOW_STATE_TYPE_DEFAULT
:
637 case WINDOW_STATE_TYPE_NORMAL
: {
638 gfx::Rect work_area_in_parent
=
639 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state
->window());
640 if (window_state
->HasRestoreBounds()) {
641 bounds_in_parent
= window_state
->GetRestoreBoundsInParent();
642 // Check if the |window|'s restored size is bigger than the working area
643 // This may happen if a window was resized to maximized bounds or if the
644 // display resolution changed while the window was maximized.
645 if (previous_state_type
== WINDOW_STATE_TYPE_MAXIMIZED
&&
646 bounds_in_parent
.width() >= work_area_in_parent
.width() &&
647 bounds_in_parent
.height() >= work_area_in_parent
.height()) {
648 // Inset the bounds slightly so that they are not exactly same as
649 // the work area bounds and it is easier to resize the window.
650 bounds_in_parent
= work_area_in_parent
;
651 bounds_in_parent
.Inset(10, 10, 10, 10);
654 bounds_in_parent
= window
->bounds();
656 // Make sure that part of the window is always visible.
657 AdjustBoundsToEnsureMinimumWindowVisibility(
658 work_area_in_parent
, &bounds_in_parent
);
661 case WINDOW_STATE_TYPE_MAXIMIZED
:
662 bounds_in_parent
= ScreenUtil::GetMaximizedWindowBoundsInParent(window
);
665 case WINDOW_STATE_TYPE_FULLSCREEN
:
666 bounds_in_parent
= ScreenUtil::GetDisplayBoundsInParent(window
);
669 case WINDOW_STATE_TYPE_DOCKED_MINIMIZED
:
670 case WINDOW_STATE_TYPE_MINIMIZED
:
672 case WINDOW_STATE_TYPE_INACTIVE
:
673 case WINDOW_STATE_TYPE_END
:
674 case WINDOW_STATE_TYPE_AUTO_POSITIONED
:
678 if (!window_state
->IsMinimized()) {
679 if (IsMinimizedWindowState(previous_state_type
) ||
680 window_state
->IsFullscreen()) {
681 window_state
->SetBoundsDirect(bounds_in_parent
);
682 } else if (window_state
->IsMaximized() ||
683 IsMaximizedOrFullscreenWindowStateType(previous_state_type
)) {
684 window_state
->SetBoundsDirectCrossFade(bounds_in_parent
);
685 } else if (window_state
->is_dragged()) {
686 // SetBoundsDirectAnimated does not work when the window gets reparented.
687 // TODO(oshima): Consider fixing it and reenable the animation.
688 window_state
->SetBoundsDirect(bounds_in_parent
);
690 window_state
->SetBoundsDirectAnimated(bounds_in_parent
);
694 if (window_state
->IsMinimized()) {
695 // Save the previous show state so that we can correctly restore it.
696 window_state
->window()->SetProperty(aura::client::kRestoreShowStateKey
,
697 ToWindowShowState(previous_state_type
));
698 ::wm::SetWindowVisibilityAnimationType(
699 window_state
->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE
);
702 window_state
->window()->Hide();
703 // Activate another window.
704 if (window_state
->IsActive())
705 window_state
->Deactivate();
706 } else if ((window_state
->window()->TargetVisibility() ||
707 IsMinimizedWindowState(previous_state_type
)) &&
708 !window_state
->window()->layer()->visible()) {
709 // The layer may be hidden if the window was previously minimized. Make
710 // sure it's visible.
711 window_state
->window()->Show();
712 if (IsMinimizedWindowState(previous_state_type
) &&
713 !window_state
->IsMaximizedOrFullscreen()) {
714 window_state
->set_unminimize_to_restore_bounds(false);
720 void DefaultState::CenterWindow(WindowState
* window_state
) {
721 if (!window_state
->IsNormalOrSnapped())
723 aura::Window
* window
= window_state
->window();
724 if (window_state
->IsSnapped()) {
725 gfx::Rect center_in_screen
=
726 Shell::GetScreen()->GetDisplayNearestWindow(window
).work_area();
727 gfx::Size size
= window_state
->HasRestoreBounds() ?
728 window_state
->GetRestoreBoundsInScreen().size() :
729 window
->bounds().size();
730 center_in_screen
.ClampToCenteredSize(size
);
731 window_state
->SetRestoreBoundsInScreen(center_in_screen
);
732 window_state
->Restore();
734 gfx::Rect center_in_parent
=
735 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window
);
736 center_in_parent
.ClampToCenteredSize(window
->bounds().size());
737 window_state
->SetBoundsDirectAnimated(center_in_parent
);
739 // Centering window is treated as if a user moved and resized the window.
740 window_state
->set_bounds_changed_by_user(true);