maximize_mode_window_state.cc revision 010d83a9304c5a91596085d917d248abff47903a
1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file. 4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/wm/maximize_mode/maximize_mode_window_state.h" 6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/display/display_controller.h" 8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/screen_util.h" 9effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "ash/shell.h" 10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/shell_window_ids.h" 11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/wm/coordinate_conversion.h" 12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/wm/maximize_mode/maximize_mode_window_manager.h" 13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/wm/window_animations.h" 145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/window_properties.h" 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/window_state_delegate.h" 165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/window_state_util.h" 175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/window_util.h" 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/wm_event.h" 195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/workspace/workspace_window_resizer.h" 205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/aura/client/aura_constants.h" 215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/aura/window.h" 225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/aura/window_delegate.h" 235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/display.h" 24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/rect.h" 25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/views/view_constants_aura.h" 26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/views/widget/widget.h" 277d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)namespace ash { 297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)namespace { 30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Returns the biggest possible size for a window which is about to be 32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// maximized. 33effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochgfx::Size GetMaximumSizeOfWindow(wm::WindowState* window_state) { 34effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch DCHECK(window_state->CanMaximize() || window_state->CanResize()); 35effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 36effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch gfx::Size workspace_size = ScreenUtil::GetMaximizedWindowBoundsInParent( 37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) window_state->window()).size(); 38effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 39effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch aura::WindowDelegate* delegate = window_state->window()->delegate(); 40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!delegate) 41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return workspace_size; 42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::Size size = delegate->GetMaximumSize(); 44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (size.IsEmpty()) 45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return workspace_size; 46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) size.SetToMin(workspace_size); 48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return size; 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Returns the centered bounds of the given bounds in the work area. 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)gfx::Rect GetCenteredBounds(const gfx::Rect& bounds_in_parent, 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) wm::WindowState* state_object) { 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::Rect work_area_in_parent = 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ScreenUtil::GetDisplayWorkAreaBoundsInParent(state_object->window()); 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) work_area_in_parent.ClampToCenteredSize(bounds_in_parent.size()); 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return work_area_in_parent; 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Returns the maximized/full screen and/or centered bounds of a window. 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)gfx::Rect GetBoundsInMaximizedMode(wm::WindowState* state_object) { 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (state_object->IsFullscreen()) 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return ScreenUtil::GetDisplayBoundsInParent(state_object->window()); 645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::Rect bounds_in_parent; 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Make the window as big as possible. 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (state_object->CanMaximize() || state_object->CanResize()) { 685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bounds_in_parent.set_size(GetMaximumSizeOfWindow(state_object)); 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else { 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // We prefer the user given window dimensions over the current windows 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // dimensions since they are likely to be the result from some other state 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // object logic. 735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (state_object->HasRestoreBounds()) 745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bounds_in_parent = state_object->GetRestoreBoundsInParent(); 755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) else 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bounds_in_parent = state_object->window()->bounds(); 775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return GetCenteredBounds(bounds_in_parent, state_object); 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} // namespace 825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static 845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MaximizeModeWindowState::UpdateWindowPosition( 855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) wm::WindowState* window_state, bool animated) { 865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state); 875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (bounds_in_parent == window_state->window()->bounds()) 895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (animated) 925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) window_state->SetBoundsDirect(bounds_in_parent); 935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) else 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) window_state->SetBoundsDirectAnimated(bounds_in_parent); 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MaximizeModeWindowState::MaximizeModeWindowState( 985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) aura::Window* window, MaximizeModeWindowManager* creator) 99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) : 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 107MaximizeModeWindowState::~MaximizeModeWindowState() { 108 creator_->WindowStateDestroyed(window_); 109} 110 111void 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 117void 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 case wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED: 180 if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED) 181 UpdateBounds(window_state, true); 182 break; 183 } 184} 185 186wm::WindowStateType MaximizeModeWindowState::GetType() const { 187 return current_state_type_; 188} 189 190void MaximizeModeWindowState::AttachState( 191 wm::WindowState* window_state, 192 wm::WindowState::State* previous_state) { 193 current_state_type_ = previous_state->GetType(); 194 195 views::Widget* widget = 196 views::Widget::GetWidgetForNativeWindow(window_state->window()); 197 if (widget) { 198 gfx::Rect bounds = widget->GetRestoredBounds(); 199 if (!bounds.IsEmpty()) { 200 // We do not want to do a session restore to our window states. Therefore 201 // we tell the window to use the current default states instead. 202 window_state->window()->SetProperty(ash::kRestoreShowStateOverrideKey, 203 window_state->GetShowState()); 204 window_state->window()->SetProperty(ash::kRestoreBoundsOverrideKey, 205 new gfx::Rect(widget->GetRestoredBounds())); 206 } 207 } 208 209 // Initialize the state to a good preset. 210 if (current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED && 211 current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED && 212 current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN) { 213 UpdateWindow(window_state, 214 GetMaximizedOrCenteredWindowType(window_state), 215 true); 216 } 217 218 window_state->set_can_be_dragged(false); 219} 220 221void MaximizeModeWindowState::DetachState(wm::WindowState* window_state) { 222 // From now on, we can use the default session restore mechanism again. 223 window_state->window()->ClearProperty(ash::kRestoreBoundsOverrideKey); 224 window_state->set_can_be_dragged(true); 225} 226 227void MaximizeModeWindowState::UpdateWindow(wm::WindowState* window_state, 228 wm::WindowStateType target_state, 229 bool animated) { 230 DCHECK(target_state == wm::WINDOW_STATE_TYPE_MINIMIZED || 231 target_state == wm::WINDOW_STATE_TYPE_MAXIMIZED || 232 (target_state == wm::WINDOW_STATE_TYPE_NORMAL && 233 !window_state->CanMaximize()) || 234 target_state == wm::WINDOW_STATE_TYPE_FULLSCREEN); 235 236 if (target_state == wm::WINDOW_STATE_TYPE_MINIMIZED) { 237 if (current_state_type_ == wm::WINDOW_STATE_TYPE_MINIMIZED) 238 return; 239 240 current_state_type_ = target_state; 241 ::wm::SetWindowVisibilityAnimationType( 242 window_state->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE); 243 window_state->window()->Hide(); 244 if (window_state->IsActive()) 245 window_state->Deactivate(); 246 return; 247 } 248 249 if (current_state_type_ == target_state) { 250 // If the state type did not change, update it accordingly. 251 UpdateBounds(window_state, animated); 252 return; 253 } 254 255 const wm::WindowStateType old_state_type = current_state_type_; 256 current_state_type_ = target_state; 257 window_state->UpdateWindowShowStateFromStateType(); 258 window_state->NotifyPreStateTypeChange(old_state_type); 259 UpdateBounds(window_state, animated); 260 window_state->NotifyPostStateTypeChange(old_state_type); 261 262 if ((window_state->window()->TargetVisibility() || 263 old_state_type == wm::WINDOW_STATE_TYPE_MINIMIZED) && 264 !window_state->window()->layer()->visible()) { 265 // The layer may be hidden if the window was previously minimized. Make 266 // sure it's visible. 267 window_state->window()->Show(); 268 } 269} 270 271wm::WindowStateType MaximizeModeWindowState::GetMaximizedOrCenteredWindowType( 272 wm::WindowState* window_state) { 273 return window_state->CanMaximize() ? wm::WINDOW_STATE_TYPE_MAXIMIZED : 274 wm::WINDOW_STATE_TYPE_NORMAL; 275} 276 277void MaximizeModeWindowState::UpdateBounds(wm::WindowState* window_state, 278 bool animated) { 279 gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state); 280 // If we have a target bounds rectangle, we center it and set it 281 // accordingly. 282 if (!bounds_in_parent.IsEmpty() && 283 bounds_in_parent != window_state->window()->bounds()) { 284 if (current_state_type_ == wm::WINDOW_STATE_TYPE_MINIMIZED || 285 !window_state->window()->IsVisible() || 286 !animated) { 287 window_state->SetBoundsDirect(bounds_in_parent); 288 } else { 289 // If we animate (to) maximized mode, we want to use the cross fade to 290 // avoid flashing. 291 if (window_state->IsMaximized()) 292 window_state->SetBoundsDirectCrossFade(bounds_in_parent); 293 else 294 window_state->SetBoundsDirectAnimated(bounds_in_parent); 295 } 296 } 297} 298 299} // namespace ash 300