Home | History | Annotate | Download | only in maximize_mode
      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/maximize_mode/maximize_mode_window_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/maximize_mode/maximize_mode_window_manager.h"
     13 #include "ash/wm/window_animations.h"
     14 #include "ash/wm/window_properties.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 #include "ui/views/view_constants_aura.h"
     26 #include "ui/views/widget/widget.h"
     27 
     28 namespace ash {
     29 namespace {
     30 
     31 // Returns the biggest possible size for a window which is about to be
     32 // maximized.
     33 gfx::Size GetMaximumSizeOfWindow(wm::WindowState* window_state) {
     34   DCHECK(window_state->CanMaximize() || window_state->CanResize());
     35 
     36   gfx::Size workspace_size = ScreenUtil::GetMaximizedWindowBoundsInParent(
     37       window_state->window()).size();
     38 
     39   aura::WindowDelegate* delegate = window_state->window()->delegate();
     40   if (!delegate)
     41     return workspace_size;
     42 
     43   gfx::Size size = delegate->GetMaximumSize();
     44   if (size.IsEmpty())
     45     return workspace_size;
     46 
     47   size.SetToMin(workspace_size);
     48   return size;
     49 }
     50 
     51 // Returns the centered bounds of the given bounds in the work area.
     52 gfx::Rect GetCenteredBounds(const gfx::Rect& bounds_in_parent,
     53                             wm::WindowState* state_object) {
     54   gfx::Rect work_area_in_parent =
     55       ScreenUtil::GetDisplayWorkAreaBoundsInParent(state_object->window());
     56   work_area_in_parent.ClampToCenteredSize(bounds_in_parent.size());
     57   return work_area_in_parent;
     58 }
     59 
     60 // Returns the maximized/full screen and/or centered bounds of a window.
     61 gfx::Rect GetBoundsInMaximizedMode(wm::WindowState* state_object) {
     62   if (state_object->IsFullscreen())
     63     return ScreenUtil::GetDisplayBoundsInParent(state_object->window());
     64 
     65   gfx::Rect bounds_in_parent;
     66   // Make the window as big as possible.
     67   if (state_object->CanMaximize() || state_object->CanResize()) {
     68     bounds_in_parent.set_size(GetMaximumSizeOfWindow(state_object));
     69   } else {
     70     // We prefer the user given window dimensions over the current windows
     71     // dimensions since they are likely to be the result from some other state
     72     // object logic.
     73     if (state_object->HasRestoreBounds())
     74       bounds_in_parent = state_object->GetRestoreBoundsInParent();
     75     else
     76       bounds_in_parent = state_object->window()->bounds();
     77   }
     78   return GetCenteredBounds(bounds_in_parent, state_object);
     79 }
     80 
     81 }  // namespace
     82 
     83 // static
     84 void MaximizeModeWindowState::UpdateWindowPosition(
     85     wm::WindowState* window_state, bool animated) {
     86   gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state);
     87 
     88   if (bounds_in_parent == window_state->window()->bounds())
     89     return;
     90 
     91   if (animated)
     92     window_state->SetBoundsDirect(bounds_in_parent);
     93   else
     94     window_state->SetBoundsDirectAnimated(bounds_in_parent);
     95 }
     96 
     97 MaximizeModeWindowState::MaximizeModeWindowState(
     98     aura::Window* window, MaximizeModeWindowManager* creator)
     99     : window_(window),
    100       creator_(creator),
    101       current_state_type_(wm::GetWindowState(window)->GetStateType()) {
    102   old_state_.reset(
    103       wm::GetWindowState(window)->SetStateObject(
    104           scoped_ptr<State>(this).Pass()).release());
    105 }
    106 
    107 MaximizeModeWindowState::~MaximizeModeWindowState() {
    108   creator_->WindowStateDestroyed(window_);
    109 }
    110 
    111 void MaximizeModeWindowState::LeaveMaximizeMode(wm::WindowState* window_state) {
    112   // Note: When we return we will destroy ourselves with the |our_reference|.
    113   scoped_ptr<wm::WindowState::State> our_reference =
    114       window_state->SetStateObject(old_state_.Pass());
    115 }
    116 
    117 void MaximizeModeWindowState::OnWMEvent(wm::WindowState* window_state,
    118                                         const wm::WMEvent* event) {
    119   switch (event->type()) {
    120     case wm::WM_EVENT_TOGGLE_FULLSCREEN:
    121       ToggleFullScreen(window_state, window_state->delegate());
    122       break;
    123     case wm::WM_EVENT_FULLSCREEN:
    124       UpdateWindow(window_state, wm::WINDOW_STATE_TYPE_FULLSCREEN, true);
    125       break;
    126     case wm::WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
    127     case wm::WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE:
    128     case wm::WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
    129     case wm::WM_EVENT_TOGGLE_MAXIMIZE:
    130     case wm::WM_EVENT_CENTER:
    131     case wm::WM_EVENT_SNAP_LEFT:
    132     case wm::WM_EVENT_SNAP_RIGHT:
    133     case wm::WM_EVENT_NORMAL:
    134     case wm::WM_EVENT_MAXIMIZE:
    135       UpdateWindow(window_state,
    136                    GetMaximizedOrCenteredWindowType(window_state),
    137                    true);
    138       return;
    139     case wm::WM_EVENT_MINIMIZE:
    140       UpdateWindow(window_state, wm::WINDOW_STATE_TYPE_MINIMIZED, true);
    141       return;
    142     case wm::WM_EVENT_SHOW_INACTIVE:
    143       return;
    144     case wm::WM_EVENT_SET_BOUNDS:
    145       if (current_state_type_ == wm::WINDOW_STATE_TYPE_MAXIMIZED) {
    146         // Having a maximized window, it could have been created with an empty
    147         // size and the caller should get his size upon leaving the maximized
    148         // mode. As such we set the restore bounds to the requested bounds.
    149         gfx::Rect bounds_in_parent =
    150             (static_cast<const wm::SetBoundsEvent*>(event))->requested_bounds();
    151         if (!bounds_in_parent.IsEmpty())
    152           window_state->SetRestoreBoundsInParent(bounds_in_parent);
    153       } else if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED &&
    154                  current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED &&
    155                  current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN) {
    156         // In all other cases (except for minimized windows) we respect the
    157         // requested bounds and center it to a fully visible area on the screen.
    158         gfx::Rect bounds_in_parent =
    159             (static_cast<const wm::SetBoundsEvent*>(event))->requested_bounds();
    160         bounds_in_parent = GetCenteredBounds(bounds_in_parent, window_state);
    161         if (bounds_in_parent != window_state->window()->bounds()) {
    162           if (window_state->window()->IsVisible())
    163             window_state->SetBoundsDirectAnimated(bounds_in_parent);
    164           else
    165             window_state->SetBoundsDirect(bounds_in_parent);
    166         }
    167       }
    168       break;
    169     case wm::WM_EVENT_ADDED_TO_WORKSPACE:
    170       if (current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED &&
    171           current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN &&
    172           current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED) {
    173         wm::WindowStateType new_state =
    174             GetMaximizedOrCenteredWindowType(window_state);
    175         UpdateWindow(window_state, new_state, true);
    176       }
    177       break;
    178     case wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED:
    179       if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED)
    180         UpdateBounds(window_state, true);
    181       break;
    182     case wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED:
    183       // Don't animate on a screen rotation - just snap to new size.
    184       if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED)
    185         UpdateBounds(window_state, false);
    186       break;
    187   }
    188 }
    189 
    190 wm::WindowStateType MaximizeModeWindowState::GetType() const {
    191   return current_state_type_;
    192 }
    193 
    194 void MaximizeModeWindowState::AttachState(
    195     wm::WindowState* window_state,
    196     wm::WindowState::State* previous_state) {
    197   current_state_type_ = previous_state->GetType();
    198 
    199   views::Widget* widget =
    200       views::Widget::GetWidgetForNativeWindow(window_state->window());
    201   if (widget) {
    202     gfx::Rect bounds = widget->GetRestoredBounds();
    203     if (!bounds.IsEmpty()) {
    204       // We do not want to do a session restore to our window states. Therefore
    205       // we tell the window to use the current default states instead.
    206       window_state->window()->SetProperty(ash::kRestoreShowStateOverrideKey,
    207                                           window_state->GetShowState());
    208       window_state->window()->SetProperty(ash::kRestoreBoundsOverrideKey,
    209           new gfx::Rect(widget->GetRestoredBounds()));
    210     }
    211   }
    212 
    213   // Initialize the state to a good preset.
    214   if (current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED &&
    215       current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED &&
    216       current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN) {
    217     UpdateWindow(window_state,
    218                  GetMaximizedOrCenteredWindowType(window_state),
    219                  true);
    220   }
    221 
    222   window_state->set_can_be_dragged(false);
    223 }
    224 
    225 void MaximizeModeWindowState::DetachState(wm::WindowState* window_state) {
    226   // From now on, we can use the default session restore mechanism again.
    227   window_state->window()->ClearProperty(ash::kRestoreBoundsOverrideKey);
    228   window_state->set_can_be_dragged(true);
    229 }
    230 
    231 void MaximizeModeWindowState::UpdateWindow(wm::WindowState* window_state,
    232                                            wm::WindowStateType target_state,
    233                                            bool animated) {
    234   DCHECK(target_state == wm::WINDOW_STATE_TYPE_MINIMIZED ||
    235          target_state == wm::WINDOW_STATE_TYPE_MAXIMIZED ||
    236          (target_state == wm::WINDOW_STATE_TYPE_NORMAL &&
    237               !window_state->CanMaximize()) ||
    238          target_state == wm::WINDOW_STATE_TYPE_FULLSCREEN);
    239 
    240   if (target_state == wm::WINDOW_STATE_TYPE_MINIMIZED) {
    241     if (current_state_type_ == wm::WINDOW_STATE_TYPE_MINIMIZED)
    242       return;
    243 
    244     current_state_type_ = target_state;
    245     ::wm::SetWindowVisibilityAnimationType(
    246         window_state->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
    247     window_state->window()->Hide();
    248     if (window_state->IsActive())
    249       window_state->Deactivate();
    250     return;
    251   }
    252 
    253   if (current_state_type_ == target_state) {
    254     // If the state type did not change, update it accordingly.
    255     UpdateBounds(window_state, animated);
    256     return;
    257   }
    258 
    259   const wm::WindowStateType old_state_type = current_state_type_;
    260   current_state_type_ = target_state;
    261   window_state->UpdateWindowShowStateFromStateType();
    262   window_state->NotifyPreStateTypeChange(old_state_type);
    263   UpdateBounds(window_state, animated);
    264   window_state->NotifyPostStateTypeChange(old_state_type);
    265 
    266   if ((window_state->window()->TargetVisibility() ||
    267       old_state_type == wm::WINDOW_STATE_TYPE_MINIMIZED) &&
    268       !window_state->window()->layer()->visible()) {
    269     // The layer may be hidden if the window was previously minimized. Make
    270     // sure it's visible.
    271     window_state->window()->Show();
    272   }
    273 }
    274 
    275 wm::WindowStateType MaximizeModeWindowState::GetMaximizedOrCenteredWindowType(
    276       wm::WindowState* window_state) {
    277   return window_state->CanMaximize() ? wm::WINDOW_STATE_TYPE_MAXIMIZED :
    278                                        wm::WINDOW_STATE_TYPE_NORMAL;
    279 }
    280 
    281 void MaximizeModeWindowState::UpdateBounds(wm::WindowState* window_state,
    282                                            bool animated) {
    283   gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state);
    284   // If we have a target bounds rectangle, we center it and set it
    285   // accordingly.
    286   if (!bounds_in_parent.IsEmpty() &&
    287       bounds_in_parent != window_state->window()->bounds()) {
    288     if (current_state_type_ == wm::WINDOW_STATE_TYPE_MINIMIZED ||
    289         !window_state->window()->IsVisible() ||
    290         !animated) {
    291       window_state->SetBoundsDirect(bounds_in_parent);
    292     } else {
    293       // If we animate (to) maximized mode, we want to use the cross fade to
    294       // avoid flashing.
    295       if (window_state->IsMaximized())
    296         window_state->SetBoundsDirectCrossFade(bounds_in_parent);
    297       else
    298         window_state->SetBoundsDirectAnimated(bounds_in_parent);
    299     }
    300   }
    301 }
    302 
    303 }  // namespace ash
    304