default_state.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/workspace/workspace_window_resizer.h" 17#include "ui/aura/client/aura_constants.h" 18#include "ui/aura/window.h" 19#include "ui/aura/window_delegate.h" 20#include "ui/gfx/display.h" 21#include "ui/gfx/rect.h" 22 23namespace ash { 24namespace wm { 25namespace { 26 27bool IsPanel(aura::Window* window) { 28 return window->parent() && 29 window->parent()->id() == internal::kShellWindowId_DockedContainer; 30} 31 32gfx::Rect BoundsWithScreenEdgeVisible( 33 aura::Window* window, 34 const gfx::Rect& restore_bounds) { 35 gfx::Rect max_bounds = 36 ash::ScreenUtil::GetMaximizedWindowBoundsInParent(window); 37 // If the restore_bounds are more than 1 grid step away from the size the 38 // window would be when maximized, inset it. 39 max_bounds.Inset(ash::internal::WorkspaceWindowResizer::kScreenEdgeInset, 40 ash::internal::WorkspaceWindowResizer::kScreenEdgeInset); 41 if (restore_bounds.Contains(max_bounds)) 42 return max_bounds; 43 return restore_bounds; 44} 45 46void MoveToDisplayForRestore(WindowState* window_state) { 47 if (!window_state->HasRestoreBounds()) 48 return; 49 const gfx::Rect& restore_bounds = window_state->GetRestoreBoundsInScreen(); 50 51 // Move only if the restore bounds is outside of 52 // the display. There is no information about in which 53 // display it should be restored, so this is best guess. 54 // TODO(oshima): Restore information should contain the 55 // work area information like WindowResizer does for the 56 // last window location. 57 gfx::Rect display_area = Shell::GetScreen()->GetDisplayNearestWindow( 58 window_state->window()).bounds(); 59 60 if (!display_area.Intersects(restore_bounds)) { 61 const gfx::Display& display = 62 Shell::GetScreen()->GetDisplayMatching(restore_bounds); 63 DisplayController* display_controller = 64 Shell::GetInstance()->display_controller(); 65 aura::Window* new_root = 66 display_controller->GetRootWindowForDisplayId(display.id()); 67 if (new_root != window_state->window()->GetRootWindow()) { 68 aura::Window* new_container = 69 Shell::GetContainer(new_root, window_state->window()->parent()->id()); 70 new_container->AddChild(window_state->window()); 71 } 72 } 73} 74 75} // namespace; 76 77DefaultState::DefaultState() {} 78DefaultState::~DefaultState() {} 79 80void DefaultState::OnWMEvent(WindowState* window_state, 81 WMEvent event) { 82 if (ProcessCompoundEvents(window_state, event)) 83 return; 84 85 WindowShowType next_show_type = SHOW_TYPE_NORMAL; 86 switch (event) { 87 case NORMAL: 88 next_show_type = SHOW_TYPE_NORMAL; 89 break; 90 case MAXIMIZE: 91 next_show_type = SHOW_TYPE_MAXIMIZED; 92 break; 93 case MINIMIZE: 94 next_show_type = SHOW_TYPE_MINIMIZED; 95 break; 96 case FULLSCREEN: 97 next_show_type = SHOW_TYPE_FULLSCREEN; 98 break; 99 case SNAP_LEFT: 100 next_show_type = SHOW_TYPE_LEFT_SNAPPED; 101 break; 102 case SNAP_RIGHT: 103 next_show_type = SHOW_TYPE_RIGHT_SNAPPED; 104 break; 105 case SHOW_INACTIVE: 106 next_show_type = SHOW_TYPE_INACTIVE; 107 break; 108 case TOGGLE_MAXIMIZE_CAPTION: 109 case TOGGLE_MAXIMIZE: 110 case TOGGLE_VERTICAL_MAXIMIZE: 111 case TOGGLE_HORIZONTAL_MAXIMIZE: 112 case TOGGLE_FULLSCREEN: 113 NOTREACHED() << "Compound event should not reach here:" << event; 114 return; 115 } 116 117 WindowShowType current = window_state->window_show_type(); 118 if (current != next_show_type) { 119 window_state->UpdateWindowShowType(next_show_type); 120 window_state->NotifyPreShowTypeChange(current); 121 // TODO(oshima): Make docked window a state. 122 if (!window_state->IsDocked() && !IsPanel(window_state->window())) 123 UpdateBoundsFromShowType(window_state, current); 124 window_state->NotifyPostShowTypeChange(current); 125 } 126}; 127 128// static 129bool DefaultState::ProcessCompoundEvents(WindowState* window_state, 130 WMEvent event) { 131 aura::Window* window = window_state->window(); 132 133 switch (event) { 134 case TOGGLE_MAXIMIZE_CAPTION: 135 if (window_state->IsFullscreen()) { 136 window_state->ToggleFullscreen(); 137 } else if (window_state->IsMaximized()) { 138 window_state->Restore(); 139 } else if (window_state->IsNormalShowType() || 140 window_state->IsSnapped()) { 141 if (window_state->CanMaximize()) 142 window_state->Maximize(); 143 } 144 return true; 145 case TOGGLE_MAXIMIZE: 146 if (window_state->IsFullscreen()) 147 window_state->ToggleFullscreen(); 148 else if (window_state->IsMaximized()) 149 window_state->Restore(); 150 else if (window_state->CanMaximize()) 151 window_state->Maximize(); 152 return true; 153 case TOGGLE_VERTICAL_MAXIMIZE: { 154 gfx::Rect work_area = 155 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window); 156 157 // Maximize vertically if: 158 // - The window does not have a max height defined. 159 // - The window has the normal show type. Snapped windows are excluded 160 // because they are already maximized vertically and reverting to the 161 // restored bounds looks weird. 162 if (window->delegate()->GetMaximumSize().height() != 0 || 163 !window_state->IsNormalShowType()) { 164 return true; 165 } 166 if (window_state->HasRestoreBounds() && 167 (window->bounds().height() == work_area.height() && 168 window->bounds().y() == work_area.y())) { 169 window_state->SetAndClearRestoreBounds(); 170 } else { 171 window_state->SaveCurrentBoundsForRestore(); 172 window->SetBounds(gfx::Rect(window->bounds().x(), 173 work_area.y(), 174 window->bounds().width(), 175 work_area.height())); 176 } 177 return true; 178 } 179 case TOGGLE_HORIZONTAL_MAXIMIZE: { 180 // Maximize horizontally if: 181 // - The window does not have a max width defined. 182 // - The window is snapped or has the normal show type. 183 if (window->delegate()->GetMaximumSize().width() != 0) 184 return true; 185 if (!window_state->IsNormalShowType() && !window_state->IsSnapped()) 186 return true; 187 gfx::Rect work_area = 188 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window); 189 if (window_state->IsNormalShowType() && 190 window_state->HasRestoreBounds() && 191 (window->bounds().width() == work_area.width() && 192 window->bounds().x() == work_area.x())) { 193 window_state->SetAndClearRestoreBounds(); 194 } else { 195 gfx::Rect new_bounds(work_area.x(), 196 window->bounds().y(), 197 work_area.width(), 198 window->bounds().height()); 199 200 gfx::Rect restore_bounds = window->bounds(); 201 if (window_state->IsSnapped()) { 202 window_state->SetRestoreBoundsInParent(new_bounds); 203 window_state->Restore(); 204 205 // The restore logic prevents a window from being restored to bounds 206 // which match the workspace bounds exactly so it is necessary to set 207 // the bounds again below. 208 } 209 210 window_state->SetRestoreBoundsInParent(restore_bounds); 211 window->SetBounds(new_bounds); 212 } 213 return true; 214 } 215 case TOGGLE_FULLSCREEN: { 216 // Window which cannot be maximized should not be fullscreened. 217 // It can, however, be restored if it was fullscreened. 218 bool is_fullscreen = window_state->IsFullscreen(); 219 if (!is_fullscreen && !window_state->CanMaximize()) 220 return true; 221 if (window_state->delegate() && 222 window_state->delegate()->ToggleFullscreen(window_state)) { 223 return true; 224 } 225 if (is_fullscreen) { 226 window_state->Restore(); 227 } else { 228 // 229 window_state->window()->SetProperty(aura::client::kShowStateKey, 230 ui::SHOW_STATE_FULLSCREEN); 231 } 232 return true; 233 } 234 case NORMAL: 235 case MAXIMIZE: 236 case MINIMIZE: 237 case FULLSCREEN: 238 case SNAP_LEFT: 239 case SNAP_RIGHT: 240 case SHOW_INACTIVE: 241 break; 242 } 243 return false; 244} 245 246// static 247void DefaultState::UpdateBoundsFromShowType(WindowState* window_state, 248 WindowShowType old_show_type) { 249 aura::Window* window = window_state->window(); 250 // Do nothing If this is not yet added to the container. 251 if (!window->parent()) 252 return; 253 254 if (old_show_type != SHOW_TYPE_MINIMIZED && 255 !window_state->HasRestoreBounds() && 256 window_state->IsMaximizedOrFullscreen() && 257 !IsMaximizedOrFullscreenWindowShowType(old_show_type)) { 258 window_state->SaveCurrentBoundsForRestore(); 259 } 260 261 // When restoring from a minimized state, we want to restore to the previous 262 // bounds. However, we want to maintain the restore bounds. (The restore 263 // bounds are set if a user maximized the window in one axis by double 264 // clicking the window border for example). 265 gfx::Rect restore; 266 if (old_show_type == SHOW_TYPE_MINIMIZED && 267 window_state->IsNormalShowState() && 268 window_state->HasRestoreBounds() && 269 !window_state->unminimize_to_restore_bounds()) { 270 restore = window_state->GetRestoreBoundsInScreen(); 271 window_state->SaveCurrentBoundsForRestore(); 272 } 273 274 if (window_state->IsMaximizedOrFullscreen()) 275 MoveToDisplayForRestore(window_state); 276 277 WindowShowType show_type = window_state->window_show_type(); 278 gfx::Rect bounds_in_parent; 279 switch (show_type) { 280 case SHOW_TYPE_DEFAULT: 281 case SHOW_TYPE_NORMAL: 282 case SHOW_TYPE_LEFT_SNAPPED: 283 case SHOW_TYPE_RIGHT_SNAPPED: { 284 gfx::Rect work_area_in_parent = 285 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window()); 286 287 if (window_state->HasRestoreBounds()) 288 bounds_in_parent = window_state->GetRestoreBoundsInParent(); 289 else 290 bounds_in_parent = window->bounds(); 291 // Make sure that part of the window is always visible. 292 AdjustBoundsToEnsureMinimumWindowVisibility( 293 work_area_in_parent, &bounds_in_parent); 294 295 if (show_type == SHOW_TYPE_LEFT_SNAPPED || 296 show_type == SHOW_TYPE_RIGHT_SNAPPED) { 297 window_state->AdjustSnappedBounds(&bounds_in_parent); 298 } else { 299 bounds_in_parent = BoundsWithScreenEdgeVisible( 300 window, 301 bounds_in_parent); 302 } 303 break; 304 } 305 case SHOW_TYPE_MAXIMIZED: 306 bounds_in_parent = ScreenUtil::GetMaximizedWindowBoundsInParent(window); 307 break; 308 309 case SHOW_TYPE_FULLSCREEN: 310 bounds_in_parent = ScreenUtil::GetDisplayBoundsInParent(window); 311 break; 312 313 case SHOW_TYPE_MINIMIZED: 314 break; 315 case SHOW_TYPE_INACTIVE: 316 case SHOW_TYPE_DETACHED: 317 case SHOW_TYPE_END: 318 case SHOW_TYPE_AUTO_POSITIONED: 319 return; 320 } 321 322 if (show_type != SHOW_TYPE_MINIMIZED) { 323 if (old_show_type == SHOW_TYPE_MINIMIZED || 324 (window_state->IsFullscreen() && 325 !window_state->animate_to_fullscreen())) { 326 window_state->SetBoundsDirect(bounds_in_parent); 327 } else if (window_state->IsMaximizedOrFullscreen() || 328 IsMaximizedOrFullscreenWindowShowType(old_show_type)) { 329 CrossFadeToBounds(window, bounds_in_parent); 330 } else { 331 window_state->SetBoundsDirectAnimated(bounds_in_parent); 332 } 333 } 334 335 if (window_state->IsMinimized()) { 336 // Save the previous show state so that we can correctly restore it. 337 window_state->window()->SetProperty(aura::client::kRestoreShowStateKey, 338 ToWindowShowState(old_show_type)); 339 views::corewm::SetWindowVisibilityAnimationType( 340 window_state->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE); 341 342 // Hide the window. 343 window_state->window()->Hide(); 344 // Activate another window. 345 if (window_state->IsActive()) 346 window_state->Deactivate(); 347 } else if ((window_state->window()->TargetVisibility() || 348 old_show_type == SHOW_TYPE_MINIMIZED) && 349 !window_state->window()->layer()->visible()) { 350 // The layer may be hidden if the window was previously minimized. Make 351 // sure it's visible. 352 window_state->window()->Show(); 353 if (old_show_type == SHOW_TYPE_MINIMIZED && 354 !window_state->IsMaximizedOrFullscreen()) { 355 window_state->set_unminimize_to_restore_bounds(false); 356 } 357 } 358 359 if (window_state->IsNormalShowState()) 360 window_state->ClearRestoreBounds(); 361 362 // Set the restore rectangle to the previously set restore rectangle. 363 if (!restore.IsEmpty()) 364 window_state->SetRestoreBoundsInScreen(restore); 365} 366 367} // namespace wm 368} // namespace ash 369