Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ash / wm / default_state.cc
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.
4
5 #include "ash/wm/default_state.h"
6
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/rect.h"
25
26 namespace ash {
27 namespace wm {
28 namespace {
29
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;
33
34 bool IsMinimizedWindowState(const WindowStateType state_type) {
35   return state_type == WINDOW_STATE_TYPE_MINIMIZED ||
36          state_type == WINDOW_STATE_TYPE_DOCKED_MINIMIZED;
37 }
38
39 void MoveToDisplayForRestore(WindowState* window_state) {
40   if (!window_state->HasRestoreBounds())
41     return;
42   const gfx::Rect& restore_bounds = window_state->GetRestoreBoundsInScreen();
43
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();
52
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());
64     }
65   }
66 }
67
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;
77   }
78   return NULL;
79 }
80
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);
87   }
88   ~ScopedPreferredAlignmentResetter() {
89     docked_window_layout_manager_->set_preferred_alignment(
90         DOCKED_ALIGNMENT_NONE);
91   }
92
93  private:
94   DockedWindowLayoutManager* docked_window_layout_manager_;
95
96   DISALLOW_COPY_AND_ASSIGN(ScopedPreferredAlignmentResetter);
97 };
98
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);
105   }
106   ~ScopedDockedLayoutEventSourceResetter() {
107     docked_window_layout_manager_->set_event_source(
108         DOCKED_ACTION_SOURCE_UNKNOWN);
109   }
110
111  private:
112   DockedWindowLayoutManager* docked_window_layout_manager_;
113
114   DISALLOW_COPY_AND_ASSIGN(ScopedDockedLayoutEventSourceResetter);
115 };
116
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;
127
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;
139     }
140
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;
147       }
148
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;
155     }
156   }
157
158   if (window_state->IsDocked() || window_state->IsSnapped()) {
159     ScopedDockedLayoutEventSourceResetter event_source_resetter(dock_layout);
160     window_state->Restore();
161     return;
162   }
163   ::wm::AnimateWindow(window_state->window(),
164                       ::wm::WINDOW_ANIMATION_TYPE_BOUNCE);
165 }
166
167 }  // namespace;
168
169 DefaultState::DefaultState(WindowStateType initial_state_type)
170     : state_type_(initial_state_type) {}
171 DefaultState::~DefaultState() {}
172
173 void DefaultState::OnWMEvent(WindowState* window_state,
174                              const WMEvent* event) {
175   if (ProcessWorkspaceEvents(window_state, event))
176     return;
177
178   if (ProcessCompoundEvents(window_state, event))
179     return;
180
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;
230   }
231
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;
238   }
239
240   EnterToNextState(window_state, next_state_type);
241 }
242
243 WindowStateType DefaultState::GetType() const {
244   return state_type_;
245 }
246
247 void DefaultState::AttachState(
248     WindowState* window_state,
249     WindowState::State* state_in_previous_mode) {
250   DCHECK_EQ(stored_window_state_, window_state);
251
252   ReenterToCurrentState(window_state, state_in_previous_mode);
253
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);
264   }
265 }
266
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());
279 }
280
281 // static
282 bool DefaultState::ProcessCompoundEvents(WindowState* window_state,
283                                          const WMEvent* event) {
284   aura::Window* window = window_state->window();
285
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();
296       }
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();
306       }
307       return true;
308     case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: {
309       gfx::Rect work_area =
310           ScreenUtil::GetDisplayWorkAreaBoundsInParent(window);
311
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;
320       }
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()));
331       }
332       return true;
333     }
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());
354
355         gfx::Rect restore_bounds = window->bounds();
356         if (window_state->IsSnapped()) {
357           window_state->SetRestoreBoundsInParent(new_bounds);
358           window_state->Restore();
359
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.
363         }
364
365         window_state->SetRestoreBoundsInParent(restore_bounds);
366         window->SetBounds(new_bounds);
367       }
368       return true;
369     }
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;
395   }
396   return false;
397 }
398
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;
411       }
412
413       aura::Window* window = window_state->window();
414       gfx::Rect bounds = window->bounds();
415
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;
420
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;
428       }
429
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;
443     }
444     case WM_EVENT_DISPLAY_BOUNDS_CHANGED: {
445       if (window_state->is_dragged() ||
446           SetMaximizedOrFullscreenBounds(window_state)) {
447         return true;
448       }
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;
459     }
460     case WM_EVENT_WORKAREA_BOUNDS_CHANGED: {
461       if (window_state->is_dragged() ||
462           SetMaximizedOrFullscreenBounds(window_state)) {
463         return true;
464       }
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;
473     }
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;
492   }
493   return false;
494 }
495
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;
503   }
504   if (window_state->IsFullscreen()) {
505     window_state->SetBoundsDirect(
506         ScreenUtil::GetDisplayBoundsInParent(window_state->window()));
507     return true;
508   }
509   return false;
510 }
511
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());
526   }
527 }
528
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;
534
535   WindowStateType previous_state_type = state_type_;
536   state_type_ = next_state_type;
537
538   window_state->UpdateWindowShowStateFromStateType();
539   window_state->NotifyPreStateTypeChange(previous_state_type);
540
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();
548     }
549
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();
561     }
562
563     if (window_state->IsMaximizedOrFullscreen())
564       MoveToDisplayForRestore(window_state);
565
566     UpdateBoundsFromState(window_state, previous_state_type);
567
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();
574   }
575   window_state->NotifyPostStateTypeChange(previous_state_type);
576 }
577
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;
587   }
588   window_state->UpdateWindowShowStateFromStateType();
589   window_state->NotifyPreStateTypeChange(previous_state_type);
590
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_);
597   }
598
599   UpdateBoundsFromState(window_state, state_in_previous_mode->GetType());
600
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();
606
607   window_state->NotifyPostStateTypeChange(previous_state_type);
608 }
609
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);
629       }
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;
633     }
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);
650         }
651       } else {
652         bounds_in_parent = window->bounds();
653       }
654       // Make sure that part of the window is always visible.
655       AdjustBoundsToEnsureMinimumWindowVisibility(
656           work_area_in_parent, &bounds_in_parent);
657       break;
658     }
659     case WINDOW_STATE_TYPE_MAXIMIZED:
660       bounds_in_parent = ScreenUtil::GetMaximizedWindowBoundsInParent(window);
661       break;
662
663     case WINDOW_STATE_TYPE_FULLSCREEN:
664       bounds_in_parent = ScreenUtil::GetDisplayBoundsInParent(window);
665       break;
666
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;
674   }
675
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);
689     }
690   }
691
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);
698
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);
713     }
714   }
715 }
716
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);
736   }
737   // Centering window is treated as if a user moved and resized the window.
738   window_state->set_bounds_changed_by_user(true);
739 }
740
741 }  // namespace wm
742 }  // namespace ash