default_state.cc revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
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/window_animations.h"
13#include "ash/wm/window_state.h"
14#include "ash/wm/window_state_delegate.h"
15#include "ash/wm/window_util.h"
16#include "ash/wm/wm_event.h"
17#include "ash/wm/workspace/workspace_window_resizer.h"
18#include "ui/aura/client/aura_constants.h"
19#include "ui/aura/window.h"
20#include "ui/aura/window_delegate.h"
21#include "ui/gfx/display.h"
22#include "ui/gfx/rect.h"
23
24namespace ash {
25namespace wm {
26namespace {
27
28// This specifies how much percent (30%) of a window rect
29// must be visible when the window is added to the workspace.
30const float kMinimumPercentOnScreenArea = 0.3f;
31
32bool IsPanel(aura::Window* window) {
33  return window->parent() &&
34      window->parent()->id() == internal::kShellWindowId_DockedContainer;
35}
36
37void MoveToDisplayForRestore(WindowState* window_state) {
38  if (!window_state->HasRestoreBounds())
39    return;
40  const gfx::Rect& restore_bounds = window_state->GetRestoreBoundsInScreen();
41
42  // Move only if the restore bounds is outside of
43  // the display. There is no information about in which
44  // display it should be restored, so this is best guess.
45  // TODO(oshima): Restore information should contain the
46  // work area information like WindowResizer does for the
47  // last window location.
48  gfx::Rect display_area = Shell::GetScreen()->GetDisplayNearestWindow(
49      window_state->window()).bounds();
50
51  if (!display_area.Intersects(restore_bounds)) {
52    const gfx::Display& display =
53        Shell::GetScreen()->GetDisplayMatching(restore_bounds);
54    DisplayController* display_controller =
55        Shell::GetInstance()->display_controller();
56    aura::Window* new_root =
57        display_controller->GetRootWindowForDisplayId(display.id());
58    if (new_root != window_state->window()->GetRootWindow()) {
59      aura::Window* new_container =
60          Shell::GetContainer(new_root, window_state->window()->parent()->id());
61      new_container->AddChild(window_state->window());
62    }
63  }
64}
65
66}  // namespace;
67
68DefaultState::DefaultState(WindowStateType initial_state_type)
69    : state_type_(initial_state_type) {}
70DefaultState::~DefaultState() {}
71
72void DefaultState::OnWMEvent(WindowState* window_state,
73                             const WMEvent* event) {
74  if (ProcessWorkspaceEvents(window_state, event))
75    return;
76
77  if (ProcessCompoundEvents(window_state, event))
78    return;
79
80  WindowStateType next_state_type = WINDOW_STATE_TYPE_NORMAL;
81  switch (event->type()) {
82    case WM_EVENT_NORMAL:
83      next_state_type = WINDOW_STATE_TYPE_NORMAL;
84      break;
85    case WM_EVENT_MAXIMIZE:
86      next_state_type = WINDOW_STATE_TYPE_MAXIMIZED;
87      break;
88    case WM_EVENT_MINIMIZE:
89      next_state_type = WINDOW_STATE_TYPE_MINIMIZED;
90      break;
91    case WM_EVENT_FULLSCREEN:
92      next_state_type = WINDOW_STATE_TYPE_FULLSCREEN;
93      break;
94    case WM_EVENT_SNAP_LEFT:
95      next_state_type = WINDOW_STATE_TYPE_LEFT_SNAPPED;
96      break;
97    case WM_EVENT_SNAP_RIGHT:
98      next_state_type = WINDOW_STATE_TYPE_RIGHT_SNAPPED;
99      break;
100    case WM_EVENT_SET_BOUNDS:
101      SetBounds(window_state, static_cast<const SetBoundsEvent*>(event));
102      return;
103    case WM_EVENT_SHOW_INACTIVE:
104      next_state_type = WINDOW_STATE_TYPE_INACTIVE;
105      break;
106    case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
107    case WM_EVENT_TOGGLE_MAXIMIZE:
108    case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE:
109    case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
110    case WM_EVENT_TOGGLE_FULLSCREEN:
111    case WM_EVENT_CENTER:
112      NOTREACHED() << "Compound event should not reach here:" << event;
113      return;
114    case WM_EVENT_ADDED_TO_WORKSPACE:
115    case WM_EVENT_WORKAREA_BOUNDS_CHANGED:
116    case WM_EVENT_DISPLAY_BOUNDS_CHANGED:
117      NOTREACHED() << "Workspace event should not reach here:" << event;
118      return;
119  }
120
121  WindowStateType current = window_state->GetStateType();
122
123  if (next_state_type == current && window_state->IsSnapped()) {
124    gfx::Rect snapped_bounds = event->type() == WM_EVENT_SNAP_LEFT ?
125        GetDefaultLeftSnappedWindowBoundsInParent(window_state->window()) :
126        GetDefaultRightSnappedWindowBoundsInParent(window_state->window());
127    window_state->SetBoundsDirectAnimated(snapped_bounds);
128    return;
129  }
130
131  if (current != next_state_type) {
132    state_type_ = next_state_type;
133    window_state->UpdateWindowShowStateFromStateType();
134    window_state->NotifyPreStateTypeChange(current);
135    // TODO(oshima): Make docked window a state.
136    if (window_state->IsSnapped() ||
137        (!window_state->IsDocked() && !IsPanel(window_state->window()))) {
138      UpdateBounds(window_state, current);
139    }
140    window_state->NotifyPostStateTypeChange(current);
141  }
142}
143
144WindowStateType DefaultState::GetType() const {
145  return state_type_;
146}
147
148void DefaultState::AttachState(WindowState* window_state,
149                               WindowState::State* previous_state) {
150  DCHECK_EQ(stored_window_state_, window_state);
151  WindowStateType old_state_type = state_type_;
152  state_type_ = previous_state->GetType();
153
154  // Forget our restore sizes when the workspace size has changed.
155  bool workspace_unchanged = stored_workspace_size_ ==
156      window_state->window()->parent()->bounds().size();
157
158  // Set the restore bounds to be the previous bounds - this might be required
159  // for some state transitions like restore, so that the animations are sound.
160  if (!stored_bounds_.IsEmpty() && workspace_unchanged)
161    window_state->SetRestoreBoundsInParent(stored_bounds_);
162  else
163    window_state->ClearRestoreBounds();
164
165  if (old_state_type != state_type_) {
166    wm::WMEventType type = wm::WM_EVENT_NORMAL;
167    switch (old_state_type) {
168      case wm::WINDOW_STATE_TYPE_DEFAULT:
169      case wm::WINDOW_STATE_TYPE_AUTO_POSITIONED:
170      case wm::WINDOW_STATE_TYPE_NORMAL:
171      case wm::WINDOW_STATE_TYPE_DETACHED:
172      case wm::WINDOW_STATE_TYPE_END:
173        break;
174      case wm::WINDOW_STATE_TYPE_MINIMIZED:
175        type = wm::WM_EVENT_MINIMIZE;
176        break;
177      case wm::WINDOW_STATE_TYPE_MAXIMIZED:
178        type = wm::WM_EVENT_MAXIMIZE;
179        break;
180      case wm::WINDOW_STATE_TYPE_INACTIVE:
181        type = wm::WM_EVENT_SHOW_INACTIVE;
182        break;
183      case wm::WINDOW_STATE_TYPE_FULLSCREEN:
184        type = wm::WM_EVENT_TOGGLE_FULLSCREEN;
185        break;
186      case wm::WINDOW_STATE_TYPE_LEFT_SNAPPED:
187        type = wm::WM_EVENT_SNAP_LEFT;
188        break;
189      case wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED:
190        type = wm::WM_EVENT_SNAP_RIGHT;
191        break;
192    }
193    wm::WMEvent event(type);
194    window_state->OnWMEvent(&event);
195  }
196
197  if (workspace_unchanged) {
198    // If the bounds are not yet set and valid we restore them.
199    if (!stored_bounds_.IsEmpty() &&
200        stored_bounds_ != window_state->window()->bounds()) {
201      if (state_type_ == wm::WINDOW_STATE_TYPE_MINIMIZED)
202        window_state->SetBoundsDirect(stored_bounds_);
203      else
204        window_state->SetBoundsDirectAnimated(stored_bounds_);
205    }
206
207    // Then restore the restore bounds to their previous value.
208    if (!stored_restore_bounds_.IsEmpty())
209      window_state->SetRestoreBoundsInParent(stored_restore_bounds_);
210    else
211      window_state->ClearRestoreBounds();
212  }
213}
214
215void DefaultState::DetachState(WindowState* window_state) {
216  stored_window_state_ = window_state;
217  aura::Window* window = window_state->window();
218  stored_bounds_ = window->bounds();
219  stored_restore_bounds_ = window_state->HasRestoreBounds() ?
220      window_state->GetRestoreBoundsInParent() : gfx::Rect();
221  // If the container size for this window has changed we need to restore the
222  // proper location of the window within the container. Note that this might
223  // not be the same as the screen resolution.
224  stored_workspace_size_ = window_state->window()->parent()->bounds().size();
225}
226
227// static
228bool DefaultState::ProcessCompoundEvents(WindowState* window_state,
229                                         const WMEvent* event) {
230  aura::Window* window = window_state->window();
231
232  switch (event->type()) {
233    case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
234      if (window_state->IsFullscreen()) {
235        const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
236        window_state->OnWMEvent(&event);
237      } else if (window_state->IsMaximized()) {
238        window_state->Restore();
239      } else if (window_state->IsNormalOrSnapped()) {
240        if (window_state->CanMaximize())
241          window_state->Maximize();
242      }
243      return true;
244    case WM_EVENT_TOGGLE_MAXIMIZE:
245      if (window_state->IsFullscreen()) {
246        const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
247        window_state->OnWMEvent(&event);
248      } else if (window_state->IsMaximized()) {
249        window_state->Restore();
250      } else if (window_state->CanMaximize()) {
251        window_state->Maximize();
252      }
253      return true;
254    case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: {
255      gfx::Rect work_area =
256          ScreenUtil::GetDisplayWorkAreaBoundsInParent(window);
257
258      // Maximize vertically if:
259      // - The window does not have a max height defined.
260      // - The window has the normal state type. Snapped windows are excluded
261      //   because they are already maximized vertically and reverting to the
262      //   restored bounds looks weird.
263      if (window->delegate()->GetMaximumSize().height() != 0 ||
264          !window_state->IsNormalStateType()) {
265        return true;
266      }
267      if (window_state->HasRestoreBounds() &&
268          (window->bounds().height() == work_area.height() &&
269           window->bounds().y() == work_area.y())) {
270        window_state->SetAndClearRestoreBounds();
271      } else {
272        window_state->SaveCurrentBoundsForRestore();
273        window->SetBounds(gfx::Rect(window->bounds().x(),
274                                    work_area.y(),
275                                    window->bounds().width(),
276                                    work_area.height()));
277      }
278      return true;
279    }
280    case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: {
281      // Maximize horizontally if:
282      // - The window does not have a max width defined.
283      // - The window is snapped or has the normal state type.
284      if (window->delegate()->GetMaximumSize().width() != 0)
285        return true;
286      if (!window_state->IsNormalOrSnapped())
287        return true;
288      gfx::Rect work_area =
289          ScreenUtil::GetDisplayWorkAreaBoundsInParent(window);
290      if (window_state->IsNormalStateType() &&
291          window_state->HasRestoreBounds() &&
292          (window->bounds().width() == work_area.width() &&
293           window->bounds().x() == work_area.x())) {
294        window_state->SetAndClearRestoreBounds();
295      } else {
296        gfx::Rect new_bounds(work_area.x(),
297                             window->bounds().y(),
298                             work_area.width(),
299                             window->bounds().height());
300
301        gfx::Rect restore_bounds = window->bounds();
302        if (window_state->IsSnapped()) {
303          window_state->SetRestoreBoundsInParent(new_bounds);
304          window_state->Restore();
305
306          // The restore logic prevents a window from being restored to bounds
307          // which match the workspace bounds exactly so it is necessary to set
308          // the bounds again below.
309        }
310
311        window_state->SetRestoreBoundsInParent(restore_bounds);
312        window->SetBounds(new_bounds);
313      }
314      return true;
315    }
316    case WM_EVENT_TOGGLE_FULLSCREEN: {
317      // Window which cannot be maximized should not be fullscreened.
318      // It can, however, be restored if it was fullscreened.
319      bool is_fullscreen = window_state->IsFullscreen();
320      if (!is_fullscreen && !window_state->CanMaximize())
321        return true;
322      if (window_state->delegate() &&
323          window_state->delegate()->ToggleFullscreen(window_state)) {
324        return true;
325      }
326      if (is_fullscreen) {
327        window_state->Restore();
328      } else {
329        //
330        window_state->window()->SetProperty(aura::client::kShowStateKey,
331                                            ui::SHOW_STATE_FULLSCREEN);
332      }
333      return true;
334    }
335    case WM_EVENT_CENTER:
336      CenterWindow(window_state);
337      return true;
338    case WM_EVENT_NORMAL:
339    case WM_EVENT_MAXIMIZE:
340    case WM_EVENT_MINIMIZE:
341    case WM_EVENT_FULLSCREEN:
342    case WM_EVENT_SNAP_LEFT:
343    case WM_EVENT_SNAP_RIGHT:
344    case WM_EVENT_SET_BOUNDS:
345    case WM_EVENT_SHOW_INACTIVE:
346      break;
347    case WM_EVENT_ADDED_TO_WORKSPACE:
348    case WM_EVENT_WORKAREA_BOUNDS_CHANGED:
349    case WM_EVENT_DISPLAY_BOUNDS_CHANGED:
350      NOTREACHED() << "Workspace event should not reach here:" << event;
351      break;
352  }
353  return false;
354}
355
356bool DefaultState::ProcessWorkspaceEvents(WindowState* window_state,
357                                          const WMEvent* event) {
358  switch (event->type()) {
359    case WM_EVENT_ADDED_TO_WORKSPACE: {
360      // When a window is dragged and dropped onto a different
361      // root window, the bounds will be updated after they are added
362      // to the root window.
363      // If a window is opened as maximized or fullscreen, its bounds may be
364      // empty, so update the bounds now before checking empty.
365      if (window_state->is_dragged() ||
366          SetMaximizedOrFullscreenBounds(window_state)) {
367        return true;
368      }
369
370      aura::Window* window = window_state->window();
371      gfx::Rect bounds = window->bounds();
372
373      // Don't adjust window bounds if the bounds are empty as this
374      // happens when a new views::Widget is created.
375      if (bounds.IsEmpty())
376        return true;
377
378      // Use entire display instead of workarea because the workarea can
379      // be further shrunk by the docked area. The logic ensures 30%
380      // visibility which should be enough to see where the window gets
381      // moved.
382      gfx::Rect display_area = ScreenUtil::GetDisplayBoundsInParent(window);
383      int min_width = bounds.width() * kMinimumPercentOnScreenArea;
384      int min_height = bounds.height() * kMinimumPercentOnScreenArea;
385      AdjustBoundsToEnsureWindowVisibility(
386          display_area, min_width, min_height, &bounds);
387      window_state->AdjustSnappedBounds(&bounds);
388      if (window->bounds() != bounds)
389        window_state->SetBoundsConstrained(bounds);
390      return true;
391    }
392    case WM_EVENT_DISPLAY_BOUNDS_CHANGED: {
393      if (window_state->is_dragged() ||
394          SetMaximizedOrFullscreenBounds(window_state)) {
395        return true;
396      }
397      gfx::Rect work_area_in_parent =
398          ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
399      gfx::Rect bounds = window_state->window()->bounds();
400      // When display bounds has changed, make sure the entire window is fully
401      // visible.
402      bounds.AdjustToFit(work_area_in_parent);
403      window_state->AdjustSnappedBounds(&bounds);
404      if (window_state->window()->bounds() != bounds)
405        window_state->SetBoundsDirectAnimated(bounds);
406      return true;
407    }
408    case WM_EVENT_WORKAREA_BOUNDS_CHANGED: {
409      if (window_state->is_dragged() ||
410          SetMaximizedOrFullscreenBounds(window_state)) {
411        return true;
412      }
413      gfx::Rect work_area_in_parent =
414          ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
415      gfx::Rect bounds = window_state->window()->bounds();
416      AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent, &bounds);
417      window_state->AdjustSnappedBounds(&bounds);
418      if (window_state->window()->bounds() != bounds)
419        window_state->SetBoundsDirectAnimated(bounds);
420      return true;
421    }
422    case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
423    case WM_EVENT_TOGGLE_MAXIMIZE:
424    case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE:
425    case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
426    case WM_EVENT_TOGGLE_FULLSCREEN:
427    case WM_EVENT_CENTER:
428    case WM_EVENT_NORMAL:
429    case WM_EVENT_MAXIMIZE:
430    case WM_EVENT_MINIMIZE:
431    case WM_EVENT_FULLSCREEN:
432    case WM_EVENT_SNAP_LEFT:
433    case WM_EVENT_SNAP_RIGHT:
434    case WM_EVENT_SET_BOUNDS:
435    case WM_EVENT_SHOW_INACTIVE:
436      break;
437  }
438  return false;
439}
440
441// static
442void DefaultState::UpdateBounds(WindowState* window_state,
443                                WindowStateType old_state_type) {
444  aura::Window* window = window_state->window();
445  // Do nothing If this is not yet added to the container.
446  if (!window->parent())
447    return;
448
449  if (!window_state->HasRestoreBounds() &&
450      (old_state_type == WINDOW_STATE_TYPE_DEFAULT ||
451       old_state_type == WINDOW_STATE_TYPE_NORMAL) &&
452      !window_state->IsMinimized() &&
453      !window_state->IsNormalStateType()) {
454    window_state->SaveCurrentBoundsForRestore();
455  }
456
457  // When restoring from a minimized state, we want to restore to the previous
458  // bounds. However, we want to maintain the restore bounds. (The restore
459  // bounds are set if a user maximized the window in one axis by double
460  // clicking the window border for example).
461  gfx::Rect restore_bounds_in_screen;
462  if (old_state_type == WINDOW_STATE_TYPE_MINIMIZED &&
463      window_state->IsNormalStateType() &&
464      window_state->HasRestoreBounds() &&
465      !window_state->unminimize_to_restore_bounds()) {
466    restore_bounds_in_screen = window_state->GetRestoreBoundsInScreen();
467    window_state->SaveCurrentBoundsForRestore();
468  }
469
470  if (window_state->IsMaximizedOrFullscreen())
471    MoveToDisplayForRestore(window_state);
472
473  WindowStateType state_type = window_state->GetStateType();
474  gfx::Rect bounds_in_parent;
475
476  switch (state_type) {
477    case WINDOW_STATE_TYPE_LEFT_SNAPPED:
478    case WINDOW_STATE_TYPE_RIGHT_SNAPPED:
479      bounds_in_parent = state_type == WINDOW_STATE_TYPE_LEFT_SNAPPED ?
480          GetDefaultLeftSnappedWindowBoundsInParent(window_state->window()) :
481          GetDefaultRightSnappedWindowBoundsInParent(window_state->window());
482      break;
483    case WINDOW_STATE_TYPE_DEFAULT:
484    case WINDOW_STATE_TYPE_NORMAL: {
485      gfx::Rect work_area_in_parent =
486          ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
487      if (window_state->HasRestoreBounds())
488        bounds_in_parent = window_state->GetRestoreBoundsInParent();
489      else
490        bounds_in_parent = window->bounds();
491      // Make sure that part of the window is always visible.
492      AdjustBoundsToEnsureMinimumWindowVisibility(
493          work_area_in_parent, &bounds_in_parent);
494      break;
495    }
496    case WINDOW_STATE_TYPE_MAXIMIZED:
497      bounds_in_parent = ScreenUtil::GetMaximizedWindowBoundsInParent(window);
498      break;
499
500    case WINDOW_STATE_TYPE_FULLSCREEN:
501      bounds_in_parent = ScreenUtil::GetDisplayBoundsInParent(window);
502      break;
503
504    case WINDOW_STATE_TYPE_MINIMIZED:
505      break;
506    case WINDOW_STATE_TYPE_INACTIVE:
507    case WINDOW_STATE_TYPE_DETACHED:
508    case WINDOW_STATE_TYPE_END:
509    case WINDOW_STATE_TYPE_AUTO_POSITIONED:
510      return;
511  }
512
513  if (state_type != WINDOW_STATE_TYPE_MINIMIZED) {
514    if (old_state_type == WINDOW_STATE_TYPE_MINIMIZED ||
515        window_state->IsFullscreen()) {
516      window_state->SetBoundsDirect(bounds_in_parent);
517    } else if (window_state->IsMaximized() ||
518               IsMaximizedOrFullscreenWindowStateType(old_state_type)) {
519      window_state->SetBoundsDirectCrossFade(bounds_in_parent);
520    } else if (window_state->is_dragged()) {
521      // SetBoundsDirectAnimated does not work when the window gets reparented.
522      // TODO(oshima): Consider fixing it and reenable the animation.
523      window_state->SetBoundsDirect(bounds_in_parent);
524    } else {
525      window_state->SetBoundsDirectAnimated(bounds_in_parent);
526    }
527  }
528
529  if (window_state->IsMinimized()) {
530    // Save the previous show state so that we can correctly restore it.
531    window_state->window()->SetProperty(aura::client::kRestoreShowStateKey,
532                                        ToWindowShowState(old_state_type));
533    ::wm::SetWindowVisibilityAnimationType(
534        window_state->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
535
536    // Hide the window.
537    window_state->window()->Hide();
538    // Activate another window.
539    if (window_state->IsActive())
540      window_state->Deactivate();
541  } else if ((window_state->window()->TargetVisibility() ||
542              old_state_type == WINDOW_STATE_TYPE_MINIMIZED) &&
543             !window_state->window()->layer()->visible()) {
544    // The layer may be hidden if the window was previously minimized. Make
545    // sure it's visible.
546    window_state->window()->Show();
547    if (old_state_type == WINDOW_STATE_TYPE_MINIMIZED &&
548        !window_state->IsMaximizedOrFullscreen()) {
549      window_state->set_unminimize_to_restore_bounds(false);
550    }
551  }
552
553  if (window_state->IsNormalStateType())
554    window_state->ClearRestoreBounds();
555
556  // Set the restore rectangle to the previously set restore rectangle.
557  if (!restore_bounds_in_screen.IsEmpty())
558    window_state->SetRestoreBoundsInScreen(restore_bounds_in_screen);
559}
560
561// static
562bool DefaultState::SetMaximizedOrFullscreenBounds(WindowState* window_state) {
563  DCHECK(!window_state->is_dragged());
564  if (window_state->IsMaximized()) {
565    window_state->SetBoundsDirect(
566        ScreenUtil::GetMaximizedWindowBoundsInParent(window_state->window()));
567    return true;
568  }
569  if (window_state->IsFullscreen()) {
570    window_state->SetBoundsDirect(
571        ScreenUtil::GetDisplayBoundsInParent(window_state->window()));
572    return true;
573  }
574  return false;
575}
576
577// static
578void DefaultState::SetBounds(WindowState* window_state,
579                             const SetBoundsEvent* event) {
580
581  if (window_state->is_dragged()) {
582    window_state->SetBoundsDirect(event->requested_bounds());
583  } else if (window_state->IsSnapped()) {
584    gfx::Rect work_area_in_parent =
585        ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
586    gfx::Rect child_bounds(event->requested_bounds());
587    AdjustBoundsSmallerThan(work_area_in_parent.size(), &child_bounds);
588    window_state->AdjustSnappedBounds(&child_bounds);
589    window_state->SetBoundsDirect(child_bounds);
590  } else if (!SetMaximizedOrFullscreenBounds(window_state)) {
591    window_state->SetBoundsConstrained(event->requested_bounds());
592  }
593}
594
595// static
596void DefaultState::CenterWindow(WindowState* window_state) {
597  if (!window_state->IsNormalOrSnapped())
598    return;
599  aura::Window* window = window_state->window();
600  if (window_state->IsSnapped()) {
601    gfx::Rect center_in_screen =
602        Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
603    gfx::Size size = window_state->HasRestoreBounds() ?
604        window_state->GetRestoreBoundsInScreen().size() :
605        window->bounds().size();
606    center_in_screen.ClampToCenteredSize(size);
607    window_state->SetRestoreBoundsInScreen(center_in_screen);
608    window_state->Restore();
609  } else {
610    gfx::Rect center_in_parent =
611        ScreenUtil::GetDisplayWorkAreaBoundsInParent(window);
612    center_in_parent.ClampToCenteredSize(window->bounds().size());
613    window_state->SetBoundsDirectAnimated(center_in_parent);
614  }
615}
616
617}  // namespace wm
618}  // namespace ash
619