Roll src/third_party/skia fd0ecf4:d803cda
[chromium-blink-merge.git] / ash / wm / default_state.cc
blobf6e33152ceb7570890b30f86cd49c6843ee55377
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"
9 #include "ash/shell.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"
26 namespace ash {
27 namespace wm {
28 namespace {
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())
41 return;
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();
70 if (active_window) {
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());
76 return dock_layout;
78 return NULL;
81 class ScopedPreferredAlignmentResetter {
82 public:
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);
93 private:
94 DockedWindowLayoutManager* docked_window_layout_manager_;
96 DISALLOW_COPY_AND_ASSIGN(ScopedPreferredAlignmentResetter);
99 class ScopedDockedLayoutEventSourceResetter {
100 public:
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);
111 private:
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);
138 return;
141 if (dock_layout &&
142 dock_layout->CanDockWindow(window_state->window(),
143 desired_dock_alignment)) {
144 if (window_state->IsDocked()) {
145 dock_layout->MaybeSetDesiredDockedAlignment(desired_dock_alignment);
146 return;
149 ScopedDockedLayoutEventSourceResetter event_source_resetter(dock_layout);
150 ScopedPreferredAlignmentResetter alignmentResetter(desired_dock_alignment,
151 dock_layout);
152 const wm::WMEvent event(wm::WM_EVENT_DOCK);
153 window_state->OnWMEvent(&event);
154 return;
158 if (window_state->IsDocked() || window_state->IsSnapped()) {
159 ScopedDockedLayoutEventSourceResetter event_source_resetter(dock_layout);
160 window_state->Restore();
161 return;
163 ::wm::AnimateWindow(window_state->window(),
164 ::wm::WINDOW_ANIMATION_TYPE_BOUNCE);
167 } // namespace;
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))
176 return;
178 if (ProcessCompoundEvents(window_state, event))
179 return;
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:
185 next_state_type =
186 current_state_type == WINDOW_STATE_TYPE_DOCKED_MINIMIZED ?
187 WINDOW_STATE_TYPE_DOCKED : WINDOW_STATE_TYPE_NORMAL;
188 break;
189 case WM_EVENT_MAXIMIZE:
190 next_state_type = WINDOW_STATE_TYPE_MAXIMIZED;
191 break;
192 case WM_EVENT_MINIMIZE:
193 next_state_type =
194 current_state_type == WINDOW_STATE_TYPE_DOCKED ?
195 WINDOW_STATE_TYPE_DOCKED_MINIMIZED : WINDOW_STATE_TYPE_MINIMIZED;
196 break;
197 case WM_EVENT_FULLSCREEN:
198 next_state_type = WINDOW_STATE_TYPE_FULLSCREEN;
199 break;
200 case WM_EVENT_SNAP_LEFT:
201 next_state_type = WINDOW_STATE_TYPE_LEFT_SNAPPED;
202 break;
203 case WM_EVENT_SNAP_RIGHT:
204 next_state_type = WINDOW_STATE_TYPE_RIGHT_SNAPPED;
205 break;
206 case WM_EVENT_DOCK:
207 next_state_type = WINDOW_STATE_TYPE_DOCKED;
208 break;
209 case WM_EVENT_SET_BOUNDS:
210 SetBounds(window_state, static_cast<const SetBoundsEvent*>(event));
211 return;
212 case WM_EVENT_SHOW_INACTIVE:
213 next_state_type = WINDOW_STATE_TYPE_INACTIVE;
214 break;
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;
224 return;
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;
229 return;
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);
237 return;
240 EnterToNextState(window_state, next_state_type);
243 WindowStateType DefaultState::GetType() const {
244 return state_type_;
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
276 // display.
277 stored_display_state_ = Shell::GetScreen()->
278 GetDisplayNearestWindow(window_state->window());
281 // static
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();
297 return true;
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();
307 return true;
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()) {
319 return true;
321 if (window_state->HasRestoreBounds() &&
322 (window->bounds().height() == work_area.height() &&
323 window->bounds().y() == work_area.y())) {
324 window_state->SetAndClearRestoreBounds();
325 } else {
326 window_state->SaveCurrentBoundsForRestore();
327 window->SetBounds(gfx::Rect(window->bounds().x(),
328 work_area.y(),
329 window->bounds().width(),
330 work_area.height()));
332 return true;
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)
339 return true;
340 if (!window_state->IsNormalOrSnapped())
341 return true;
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();
349 } else {
350 gfx::Rect new_bounds(work_area.x(),
351 window->bounds().y(),
352 work_area.width(),
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);
368 return true;
370 case WM_EVENT_TOGGLE_FULLSCREEN:
371 ToggleFullScreen(window_state, window_state->delegate());
372 return true;
373 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT:
374 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT:
375 CycleSnapDock(window_state, event->type());
376 return true;
377 case WM_EVENT_CENTER:
378 CenterWindow(window_state);
379 return true;
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:
388 case WM_EVENT_DOCK:
389 break;
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;
394 break;
396 return false;
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)) {
410 return true;
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())
419 return true;
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) {
427 return true;
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
433 // moved.
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);
442 return true;
444 case WM_EVENT_DISPLAY_BOUNDS_CHANGED: {
445 if (window_state->is_dragged() ||
446 SetMaximizedOrFullscreenBounds(window_state)) {
447 return true;
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
453 // visible.
454 bounds.AdjustToFit(work_area_in_parent);
455 window_state->AdjustSnappedBounds(&bounds);
456 if (window_state->window()->bounds() != bounds)
457 window_state->SetBoundsDirectAnimated(bounds);
458 return true;
460 case WM_EVENT_WORKAREA_BOUNDS_CHANGED: {
461 if (window_state->is_dragged() ||
462 SetMaximizedOrFullscreenBounds(window_state)) {
463 return true;
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);
472 return true;
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:
490 case WM_EVENT_DOCK:
491 break;
493 return false;
496 // static
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()));
502 return true;
504 if (window_state->IsFullscreen()) {
505 window_state->SetBoundsDirect(
506 ScreenUtil::GetDisplayBoundsInParent(window_state->window()));
507 return true;
509 return false;
512 // static
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)
533 return;
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
569 // unminimized.
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_);
604 else
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());
620 break;
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,
627 window->parent(),
628 docked_container);
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.
632 return;
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);
651 } else {
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);
657 break;
659 case WINDOW_STATE_TYPE_MAXIMIZED:
660 bounds_in_parent = ScreenUtil::GetMaximizedWindowBoundsInParent(window);
661 break;
663 case WINDOW_STATE_TYPE_FULLSCREEN:
664 bounds_in_parent = ScreenUtil::GetDisplayBoundsInParent(window);
665 break;
667 case WINDOW_STATE_TYPE_DOCKED_MINIMIZED:
668 case WINDOW_STATE_TYPE_MINIMIZED:
669 break;
670 case WINDOW_STATE_TYPE_INACTIVE:
671 case WINDOW_STATE_TYPE_END:
672 case WINDOW_STATE_TYPE_AUTO_POSITIONED:
673 return;
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);
687 } else {
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);
699 // Hide the window.
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);
717 // static
718 void DefaultState::CenterWindow(WindowState* window_state) {
719 if (!window_state->IsNormalOrSnapped())
720 return;
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();
731 } else {
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);
741 } // namespace wm
742 } // namespace ash