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