Updating XTBs based on .GRDs from branch master
[chromium-blink-merge.git] / ash / wm / default_state.cc
blob380ccafe48ebd2439b729bd8e802e87e08b81a8f
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"
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 // 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())
46 return;
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();
75 if (active_window) {
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());
81 return dock_layout;
83 return NULL;
86 class ScopedPreferredAlignmentResetter {
87 public:
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);
98 private:
99 DockedWindowLayoutManager* docked_window_layout_manager_;
101 DISALLOW_COPY_AND_ASSIGN(ScopedPreferredAlignmentResetter);
104 class ScopedDockedLayoutEventSourceResetter {
105 public:
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);
116 private:
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);
143 return;
146 if (dock_layout &&
147 dock_layout->CanDockWindow(window_state->window(),
148 desired_dock_alignment)) {
149 if (window_state->IsDocked()) {
150 dock_layout->MaybeSetDesiredDockedAlignment(desired_dock_alignment);
151 return;
154 ScopedDockedLayoutEventSourceResetter event_source_resetter(dock_layout);
155 ScopedPreferredAlignmentResetter alignmentResetter(desired_dock_alignment,
156 dock_layout);
157 const wm::WMEvent event(wm::WM_EVENT_DOCK);
158 window_state->OnWMEvent(&event);
159 return;
163 if (window_state->IsDocked() || window_state->IsSnapped()) {
164 ScopedDockedLayoutEventSourceResetter event_source_resetter(dock_layout);
165 window_state->Restore();
166 return;
168 ::wm::AnimateWindow(window_state->window(),
169 ::wm::WINDOW_ANIMATION_TYPE_BOUNCE);
172 } // namespace;
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))
181 return;
183 if (ProcessCompoundEvents(window_state, event))
184 return;
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:
190 next_state_type =
191 current_state_type == WINDOW_STATE_TYPE_DOCKED_MINIMIZED ?
192 WINDOW_STATE_TYPE_DOCKED : WINDOW_STATE_TYPE_NORMAL;
193 break;
194 case WM_EVENT_MAXIMIZE:
195 next_state_type = WINDOW_STATE_TYPE_MAXIMIZED;
196 break;
197 case WM_EVENT_MINIMIZE:
198 next_state_type =
199 current_state_type == WINDOW_STATE_TYPE_DOCKED ?
200 WINDOW_STATE_TYPE_DOCKED_MINIMIZED : WINDOW_STATE_TYPE_MINIMIZED;
201 break;
202 case WM_EVENT_FULLSCREEN:
203 next_state_type = WINDOW_STATE_TYPE_FULLSCREEN;
204 break;
205 case WM_EVENT_SNAP_LEFT:
206 next_state_type = WINDOW_STATE_TYPE_LEFT_SNAPPED;
207 break;
208 case WM_EVENT_SNAP_RIGHT:
209 next_state_type = WINDOW_STATE_TYPE_RIGHT_SNAPPED;
210 break;
211 case WM_EVENT_DOCK:
212 next_state_type = WINDOW_STATE_TYPE_DOCKED;
213 break;
214 case WM_EVENT_SET_BOUNDS:
215 SetBounds(window_state, static_cast<const SetBoundsEvent*>(event));
216 return;
217 case WM_EVENT_SHOW_INACTIVE:
218 next_state_type = WINDOW_STATE_TYPE_INACTIVE;
219 break;
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;
229 return;
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;
234 return;
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);
242 return;
245 EnterToNextState(window_state, next_state_type);
248 WindowStateType DefaultState::GetType() const {
249 return state_type_;
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
281 // display.
282 stored_display_state_ = Shell::GetScreen()->
283 GetDisplayNearestWindow(window_state->window());
286 // static
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();
302 return true;
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();
312 return true;
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()) {
324 return true;
326 if (window_state->HasRestoreBounds() &&
327 (window->bounds().height() == work_area.height() &&
328 window->bounds().y() == work_area.y())) {
329 window_state->SetAndClearRestoreBounds();
330 } else {
331 window_state->SaveCurrentBoundsForRestore();
332 window->SetBounds(gfx::Rect(window->bounds().x(),
333 work_area.y(),
334 window->bounds().width(),
335 work_area.height()));
337 return true;
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)
344 return true;
345 if (!window_state->IsNormalOrSnapped())
346 return true;
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();
354 } else {
355 gfx::Rect new_bounds(work_area.x(),
356 window->bounds().y(),
357 work_area.width(),
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);
373 return true;
375 case WM_EVENT_TOGGLE_FULLSCREEN:
376 ToggleFullScreen(window_state, window_state->delegate());
377 return true;
378 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT:
379 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT:
380 CycleSnapDock(window_state, event->type());
381 return true;
382 case WM_EVENT_CENTER:
383 CenterWindow(window_state);
384 return true;
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:
393 case WM_EVENT_DOCK:
394 break;
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;
399 break;
401 return false;
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)) {
415 return true;
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())
424 return true;
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())
431 return true;
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
436 // moved.
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);
445 return true;
447 case WM_EVENT_DISPLAY_BOUNDS_CHANGED: {
448 if (window_state->is_dragged() ||
449 SetMaximizedOrFullscreenBounds(window_state)) {
450 return true;
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
456 // visible.
457 bounds.AdjustToFit(work_area_in_parent);
458 window_state->AdjustSnappedBounds(&bounds);
459 if (window_state->window()->bounds() != bounds)
460 window_state->SetBoundsDirectAnimated(bounds);
461 return true;
463 case WM_EVENT_WORKAREA_BOUNDS_CHANGED: {
464 if (window_state->is_dragged() ||
465 SetMaximizedOrFullscreenBounds(window_state)) {
466 return true;
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);
475 return true;
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:
493 case WM_EVENT_DOCK:
494 break;
496 return false;
499 // static
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()));
505 return true;
507 if (window_state->IsFullscreen()) {
508 window_state->SetBoundsDirect(
509 ScreenUtil::GetDisplayBoundsInParent(window_state->window()));
510 return true;
512 return false;
515 // static
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)
538 return;
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
574 // unminimized.
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_);
609 else
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());
625 break;
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,
632 window->parent(),
633 docked_container);
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.
637 return;
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);
655 } else {
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);
661 break;
663 case WINDOW_STATE_TYPE_MAXIMIZED:
664 bounds_in_parent = ScreenUtil::GetMaximizedWindowBoundsInParent(window);
665 break;
667 case WINDOW_STATE_TYPE_FULLSCREEN:
668 bounds_in_parent = ScreenUtil::GetDisplayBoundsInParent(window);
669 break;
671 case WINDOW_STATE_TYPE_DOCKED_MINIMIZED:
672 case WINDOW_STATE_TYPE_MINIMIZED:
673 break;
674 case WINDOW_STATE_TYPE_INACTIVE:
675 case WINDOW_STATE_TYPE_END:
676 case WINDOW_STATE_TYPE_AUTO_POSITIONED:
677 return;
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);
691 } else {
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);
703 // Hide the window.
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);
721 // static
722 void DefaultState::CenterWindow(WindowState* window_state) {
723 if (!window_state->IsNormalOrSnapped())
724 return;
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();
735 } else {
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);
745 } // namespace wm
746 } // namespace ash