| OLD | NEW |
| (Empty) |
| 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/common/default_state.h" | |
| 6 | |
| 7 #include "ash/wm/common/dock/docked_window_layout_manager.h" | |
| 8 #include "ash/wm/common/window_animation_types.h" | |
| 9 #include "ash/wm/common/window_parenting_utils.h" | |
| 10 #include "ash/wm/common/window_positioning_utils.h" | |
| 11 #include "ash/wm/common/window_state.h" | |
| 12 #include "ash/wm/common/window_state_delegate.h" | |
| 13 #include "ash/wm/common/window_state_util.h" | |
| 14 #include "ash/wm/common/wm_event.h" | |
| 15 #include "ash/wm/common/wm_globals.h" | |
| 16 #include "ash/wm/common/wm_root_window_controller.h" | |
| 17 #include "ash/wm/common/wm_screen_util.h" | |
| 18 #include "ash/wm/common/wm_shell_window_ids.h" | |
| 19 #include "ash/wm/common/wm_window.h" | |
| 20 #include "ui/display/display.h" | |
| 21 #include "ui/display/screen.h" | |
| 22 | |
| 23 namespace ash { | |
| 24 namespace wm { | |
| 25 namespace { | |
| 26 | |
| 27 // This specifies how much percent (30%) of a window rect | |
| 28 // must be visible when the window is added to the workspace. | |
| 29 const float kMinimumPercentOnScreenArea = 0.3f; | |
| 30 | |
| 31 // When a window that has restore bounds at least as large as a work area is | |
| 32 // unmaximized, inset the bounds slightly so that they are not exactly the same. | |
| 33 // This makes it easier to resize the window. | |
| 34 const int kMaximizedWindowInset = 10; // DIPs. | |
| 35 | |
| 36 bool IsMinimizedWindowState(const WindowStateType state_type) { | |
| 37 return state_type == WINDOW_STATE_TYPE_MINIMIZED || | |
| 38 state_type == WINDOW_STATE_TYPE_DOCKED_MINIMIZED; | |
| 39 } | |
| 40 | |
| 41 void MoveToDisplayForRestore(WindowState* window_state) { | |
| 42 if (!window_state->HasRestoreBounds()) | |
| 43 return; | |
| 44 const gfx::Rect restore_bounds = window_state->GetRestoreBoundsInScreen(); | |
| 45 | |
| 46 // Move only if the restore bounds is outside of | |
| 47 // the display. There is no information about in which | |
| 48 // display it should be restored, so this is best guess. | |
| 49 // TODO(oshima): Restore information should contain the | |
| 50 // work area information like WindowResizer does for the | |
| 51 // last window location. | |
| 52 gfx::Rect display_area = | |
| 53 window_state->window()->GetDisplayNearestWindow().bounds(); | |
| 54 | |
| 55 if (!display_area.Intersects(restore_bounds)) { | |
| 56 const display::Display& display = | |
| 57 display::Screen::GetScreen()->GetDisplayMatching(restore_bounds); | |
| 58 WmGlobals* globals = window_state->window()->GetGlobals(); | |
| 59 WmWindow* new_root = globals->GetRootWindowForDisplayId(display.id()); | |
| 60 if (new_root != window_state->window()->GetRootWindow()) { | |
| 61 WmWindow* new_container = new_root->GetChildByShellWindowId( | |
| 62 window_state->window()->GetParent()->GetShellWindowId()); | |
| 63 new_container->AddChild(window_state->window()); | |
| 64 } | |
| 65 } | |
| 66 } | |
| 67 | |
| 68 DockedWindowLayoutManager* GetDockedWindowLayoutManager(WmGlobals* globals) { | |
| 69 return DockedWindowLayoutManager::Get(globals->GetActiveWindow()); | |
| 70 } | |
| 71 | |
| 72 class ScopedPreferredAlignmentResetter { | |
| 73 public: | |
| 74 ScopedPreferredAlignmentResetter(DockedAlignment dock_alignment, | |
| 75 DockedWindowLayoutManager* dock_layout) | |
| 76 : docked_window_layout_manager_(dock_layout) { | |
| 77 docked_window_layout_manager_->set_preferred_alignment(dock_alignment); | |
| 78 } | |
| 79 ~ScopedPreferredAlignmentResetter() { | |
| 80 docked_window_layout_manager_->set_preferred_alignment( | |
| 81 DOCKED_ALIGNMENT_NONE); | |
| 82 } | |
| 83 | |
| 84 private: | |
| 85 DockedWindowLayoutManager* docked_window_layout_manager_; | |
| 86 | |
| 87 DISALLOW_COPY_AND_ASSIGN(ScopedPreferredAlignmentResetter); | |
| 88 }; | |
| 89 | |
| 90 class ScopedDockedLayoutEventSourceResetter { | |
| 91 public: | |
| 92 ScopedDockedLayoutEventSourceResetter(DockedWindowLayoutManager* dock_layout) | |
| 93 : docked_window_layout_manager_(dock_layout) { | |
| 94 docked_window_layout_manager_->set_event_source( | |
| 95 DOCKED_ACTION_SOURCE_KEYBOARD); | |
| 96 } | |
| 97 ~ScopedDockedLayoutEventSourceResetter() { | |
| 98 docked_window_layout_manager_->set_event_source( | |
| 99 DOCKED_ACTION_SOURCE_UNKNOWN); | |
| 100 } | |
| 101 | |
| 102 private: | |
| 103 DockedWindowLayoutManager* docked_window_layout_manager_; | |
| 104 | |
| 105 DISALLOW_COPY_AND_ASSIGN(ScopedDockedLayoutEventSourceResetter); | |
| 106 }; | |
| 107 | |
| 108 void CycleSnapDock(WindowState* window_state, WMEventType event) { | |
| 109 DockedWindowLayoutManager* dock_layout = | |
| 110 GetDockedWindowLayoutManager(window_state->window()->GetGlobals()); | |
| 111 wm::WindowStateType desired_snap_state = | |
| 112 event == WM_EVENT_CYCLE_SNAP_DOCK_LEFT | |
| 113 ? wm::WINDOW_STATE_TYPE_LEFT_SNAPPED | |
| 114 : wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED; | |
| 115 DockedAlignment desired_dock_alignment = | |
| 116 event == WM_EVENT_CYCLE_SNAP_DOCK_LEFT ? DOCKED_ALIGNMENT_LEFT | |
| 117 : DOCKED_ALIGNMENT_RIGHT; | |
| 118 DockedAlignment current_dock_alignment = | |
| 119 dock_layout ? dock_layout->CalculateAlignment() : DOCKED_ALIGNMENT_NONE; | |
| 120 | |
| 121 if (!window_state->IsDocked() || | |
| 122 (current_dock_alignment != DOCKED_ALIGNMENT_NONE && | |
| 123 current_dock_alignment != desired_dock_alignment)) { | |
| 124 if (window_state->CanSnap() && | |
| 125 window_state->GetStateType() != desired_snap_state && | |
| 126 window_state->window()->GetType() != ui::wm::WINDOW_TYPE_PANEL) { | |
| 127 const wm::WMEvent event(desired_snap_state == | |
| 128 wm::WINDOW_STATE_TYPE_LEFT_SNAPPED | |
| 129 ? wm::WM_EVENT_SNAP_LEFT | |
| 130 : wm::WM_EVENT_SNAP_RIGHT); | |
| 131 window_state->OnWMEvent(&event); | |
| 132 return; | |
| 133 } | |
| 134 | |
| 135 if (dock_layout && | |
| 136 dock_layout->CanDockWindow(window_state->window(), | |
| 137 desired_dock_alignment)) { | |
| 138 if (window_state->IsDocked()) { | |
| 139 dock_layout->MaybeSetDesiredDockedAlignment(desired_dock_alignment); | |
| 140 return; | |
| 141 } | |
| 142 | |
| 143 ScopedDockedLayoutEventSourceResetter event_source_resetter(dock_layout); | |
| 144 ScopedPreferredAlignmentResetter alignmentResetter(desired_dock_alignment, | |
| 145 dock_layout); | |
| 146 const wm::WMEvent event(wm::WM_EVENT_DOCK); | |
| 147 window_state->OnWMEvent(&event); | |
| 148 return; | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 if (window_state->IsDocked() || window_state->IsSnapped()) { | |
| 153 ScopedDockedLayoutEventSourceResetter event_source_resetter(dock_layout); | |
| 154 window_state->Restore(); | |
| 155 return; | |
| 156 } | |
| 157 window_state->window()->Animate(::wm::WINDOW_ANIMATION_TYPE_BOUNCE); | |
| 158 } | |
| 159 | |
| 160 } // namespace | |
| 161 | |
| 162 DefaultState::DefaultState(WindowStateType initial_state_type) | |
| 163 : state_type_(initial_state_type), stored_window_state_(nullptr) {} | |
| 164 DefaultState::~DefaultState() {} | |
| 165 | |
| 166 void DefaultState::OnWMEvent(WindowState* window_state, const WMEvent* event) { | |
| 167 if (ProcessWorkspaceEvents(window_state, event)) | |
| 168 return; | |
| 169 | |
| 170 if (ProcessCompoundEvents(window_state, event)) | |
| 171 return; | |
| 172 | |
| 173 WindowStateType current_state_type = window_state->GetStateType(); | |
| 174 WindowStateType next_state_type = WINDOW_STATE_TYPE_NORMAL; | |
| 175 switch (event->type()) { | |
| 176 case WM_EVENT_NORMAL: | |
| 177 next_state_type = current_state_type == WINDOW_STATE_TYPE_DOCKED_MINIMIZED | |
| 178 ? WINDOW_STATE_TYPE_DOCKED | |
| 179 : WINDOW_STATE_TYPE_NORMAL; | |
| 180 break; | |
| 181 case WM_EVENT_MAXIMIZE: | |
| 182 next_state_type = WINDOW_STATE_TYPE_MAXIMIZED; | |
| 183 break; | |
| 184 case WM_EVENT_MINIMIZE: | |
| 185 next_state_type = current_state_type == WINDOW_STATE_TYPE_DOCKED | |
| 186 ? WINDOW_STATE_TYPE_DOCKED_MINIMIZED | |
| 187 : WINDOW_STATE_TYPE_MINIMIZED; | |
| 188 break; | |
| 189 case WM_EVENT_FULLSCREEN: | |
| 190 next_state_type = WINDOW_STATE_TYPE_FULLSCREEN; | |
| 191 break; | |
| 192 case WM_EVENT_SNAP_LEFT: | |
| 193 next_state_type = WINDOW_STATE_TYPE_LEFT_SNAPPED; | |
| 194 break; | |
| 195 case WM_EVENT_SNAP_RIGHT: | |
| 196 next_state_type = WINDOW_STATE_TYPE_RIGHT_SNAPPED; | |
| 197 break; | |
| 198 case WM_EVENT_DOCK: | |
| 199 next_state_type = WINDOW_STATE_TYPE_DOCKED; | |
| 200 break; | |
| 201 case WM_EVENT_SET_BOUNDS: | |
| 202 SetBounds(window_state, static_cast<const SetBoundsEvent*>(event)); | |
| 203 return; | |
| 204 case WM_EVENT_SHOW_INACTIVE: | |
| 205 next_state_type = WINDOW_STATE_TYPE_INACTIVE; | |
| 206 break; | |
| 207 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION: | |
| 208 case WM_EVENT_TOGGLE_MAXIMIZE: | |
| 209 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: | |
| 210 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: | |
| 211 case WM_EVENT_TOGGLE_FULLSCREEN: | |
| 212 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT: | |
| 213 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT: | |
| 214 case WM_EVENT_CENTER: | |
| 215 NOTREACHED() << "Compound event should not reach here:" << event; | |
| 216 return; | |
| 217 case WM_EVENT_ADDED_TO_WORKSPACE: | |
| 218 case WM_EVENT_WORKAREA_BOUNDS_CHANGED: | |
| 219 case WM_EVENT_DISPLAY_BOUNDS_CHANGED: | |
| 220 NOTREACHED() << "Workspace event should not reach here:" << event; | |
| 221 return; | |
| 222 } | |
| 223 | |
| 224 if (next_state_type == current_state_type && window_state->IsSnapped()) { | |
| 225 gfx::Rect snapped_bounds = | |
| 226 event->type() == WM_EVENT_SNAP_LEFT | |
| 227 ? GetDefaultLeftSnappedWindowBoundsInParent(window_state->window()) | |
| 228 : GetDefaultRightSnappedWindowBoundsInParent( | |
| 229 window_state->window()); | |
| 230 window_state->SetBoundsDirectAnimated(snapped_bounds); | |
| 231 return; | |
| 232 } | |
| 233 | |
| 234 EnterToNextState(window_state, next_state_type); | |
| 235 } | |
| 236 | |
| 237 WindowStateType DefaultState::GetType() const { | |
| 238 return state_type_; | |
| 239 } | |
| 240 | |
| 241 void DefaultState::AttachState(WindowState* window_state, | |
| 242 WindowState::State* state_in_previous_mode) { | |
| 243 DCHECK_EQ(stored_window_state_, window_state); | |
| 244 | |
| 245 ReenterToCurrentState(window_state, state_in_previous_mode); | |
| 246 | |
| 247 // If the display has changed while in the another mode, | |
| 248 // we need to let windows know the change. | |
| 249 display::Display current_display = | |
| 250 window_state->window()->GetDisplayNearestWindow(); | |
| 251 if (stored_display_state_.bounds() != current_display.bounds()) { | |
| 252 const WMEvent event(wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED); | |
| 253 window_state->OnWMEvent(&event); | |
| 254 } else if (stored_display_state_.work_area() != current_display.work_area()) { | |
| 255 const WMEvent event(wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED); | |
| 256 window_state->OnWMEvent(&event); | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 void DefaultState::DetachState(WindowState* window_state) { | |
| 261 stored_window_state_ = window_state; | |
| 262 stored_bounds_ = window_state->window()->GetBounds(); | |
| 263 stored_restore_bounds_ = window_state->HasRestoreBounds() | |
| 264 ? window_state->GetRestoreBoundsInParent() | |
| 265 : gfx::Rect(); | |
| 266 // Remember the display state so that in case of the display change | |
| 267 // while in the other mode, we can perform necessary action to | |
| 268 // restore the window state to the proper state for the current | |
| 269 // display. | |
| 270 stored_display_state_ = window_state->window()->GetDisplayNearestWindow(); | |
| 271 } | |
| 272 | |
| 273 // static | |
| 274 bool DefaultState::ProcessCompoundEvents(WindowState* window_state, | |
| 275 const WMEvent* event) { | |
| 276 WmWindow* window = window_state->window(); | |
| 277 | |
| 278 switch (event->type()) { | |
| 279 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION: | |
| 280 if (window_state->IsFullscreen()) { | |
| 281 const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN); | |
| 282 window_state->OnWMEvent(&event); | |
| 283 } else if (window_state->IsMaximized()) { | |
| 284 window_state->Restore(); | |
| 285 } else if (window_state->IsNormalOrSnapped()) { | |
| 286 if (window_state->CanMaximize()) | |
| 287 window_state->Maximize(); | |
| 288 } | |
| 289 return true; | |
| 290 case WM_EVENT_TOGGLE_MAXIMIZE: | |
| 291 if (window_state->IsFullscreen()) { | |
| 292 const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN); | |
| 293 window_state->OnWMEvent(&event); | |
| 294 } else if (window_state->IsMaximized()) { | |
| 295 window_state->Restore(); | |
| 296 } else if (window_state->CanMaximize()) { | |
| 297 window_state->Maximize(); | |
| 298 } | |
| 299 return true; | |
| 300 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: { | |
| 301 gfx::Rect work_area = GetDisplayWorkAreaBoundsInParent(window); | |
| 302 | |
| 303 // Maximize vertically if: | |
| 304 // - The window does not have a max height defined. | |
| 305 // - The window has the normal state type. Snapped windows are excluded | |
| 306 // because they are already maximized vertically and reverting to the | |
| 307 // restored bounds looks weird. | |
| 308 if (window->GetMaximumSize().height() != 0 || | |
| 309 !window_state->IsNormalStateType()) { | |
| 310 return true; | |
| 311 } | |
| 312 if (window_state->HasRestoreBounds() && | |
| 313 (window->GetBounds().height() == work_area.height() && | |
| 314 window->GetBounds().y() == work_area.y())) { | |
| 315 window_state->SetAndClearRestoreBounds(); | |
| 316 } else { | |
| 317 window_state->SaveCurrentBoundsForRestore(); | |
| 318 window->SetBounds(gfx::Rect(window->GetBounds().x(), work_area.y(), | |
| 319 window->GetBounds().width(), | |
| 320 work_area.height())); | |
| 321 } | |
| 322 return true; | |
| 323 } | |
| 324 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: { | |
| 325 // Maximize horizontally if: | |
| 326 // - The window does not have a max width defined. | |
| 327 // - The window is snapped or has the normal state type. | |
| 328 if (window->GetMaximumSize().width() != 0) | |
| 329 return true; | |
| 330 if (!window_state->IsNormalOrSnapped()) | |
| 331 return true; | |
| 332 gfx::Rect work_area = GetDisplayWorkAreaBoundsInParent(window); | |
| 333 if (window_state->IsNormalStateType() && | |
| 334 window_state->HasRestoreBounds() && | |
| 335 (window->GetBounds().width() == work_area.width() && | |
| 336 window->GetBounds().x() == work_area.x())) { | |
| 337 window_state->SetAndClearRestoreBounds(); | |
| 338 } else { | |
| 339 gfx::Rect new_bounds(work_area.x(), window->GetBounds().y(), | |
| 340 work_area.width(), window->GetBounds().height()); | |
| 341 | |
| 342 gfx::Rect restore_bounds = window->GetBounds(); | |
| 343 if (window_state->IsSnapped()) { | |
| 344 window_state->SetRestoreBoundsInParent(new_bounds); | |
| 345 window_state->Restore(); | |
| 346 | |
| 347 // The restore logic prevents a window from being restored to bounds | |
| 348 // which match the workspace bounds exactly so it is necessary to set | |
| 349 // the bounds again below. | |
| 350 } | |
| 351 | |
| 352 window_state->SetRestoreBoundsInParent(restore_bounds); | |
| 353 window->SetBounds(new_bounds); | |
| 354 } | |
| 355 return true; | |
| 356 } | |
| 357 case WM_EVENT_TOGGLE_FULLSCREEN: | |
| 358 ToggleFullScreen(window_state, window_state->delegate()); | |
| 359 return true; | |
| 360 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT: | |
| 361 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT: | |
| 362 CycleSnapDock(window_state, event->type()); | |
| 363 return true; | |
| 364 case WM_EVENT_CENTER: | |
| 365 CenterWindow(window_state); | |
| 366 return true; | |
| 367 case WM_EVENT_NORMAL: | |
| 368 case WM_EVENT_MAXIMIZE: | |
| 369 case WM_EVENT_MINIMIZE: | |
| 370 case WM_EVENT_FULLSCREEN: | |
| 371 case WM_EVENT_SNAP_LEFT: | |
| 372 case WM_EVENT_SNAP_RIGHT: | |
| 373 case WM_EVENT_SET_BOUNDS: | |
| 374 case WM_EVENT_SHOW_INACTIVE: | |
| 375 case WM_EVENT_DOCK: | |
| 376 break; | |
| 377 case WM_EVENT_ADDED_TO_WORKSPACE: | |
| 378 case WM_EVENT_WORKAREA_BOUNDS_CHANGED: | |
| 379 case WM_EVENT_DISPLAY_BOUNDS_CHANGED: | |
| 380 NOTREACHED() << "Workspace event should not reach here:" << event; | |
| 381 break; | |
| 382 } | |
| 383 return false; | |
| 384 } | |
| 385 | |
| 386 bool DefaultState::ProcessWorkspaceEvents(WindowState* window_state, | |
| 387 const WMEvent* event) { | |
| 388 switch (event->type()) { | |
| 389 case WM_EVENT_ADDED_TO_WORKSPACE: { | |
| 390 // When a window is dragged and dropped onto a different | |
| 391 // root window, the bounds will be updated after they are added | |
| 392 // to the root window. | |
| 393 // If a window is opened as maximized or fullscreen, its bounds may be | |
| 394 // empty, so update the bounds now before checking empty. | |
| 395 if (window_state->is_dragged() || | |
| 396 SetMaximizedOrFullscreenBounds(window_state)) { | |
| 397 return true; | |
| 398 } | |
| 399 | |
| 400 WmWindow* window = window_state->window(); | |
| 401 gfx::Rect bounds = window->GetBounds(); | |
| 402 | |
| 403 // Don't adjust window bounds if the bounds are empty as this | |
| 404 // happens when a new views::Widget is created. | |
| 405 if (bounds.IsEmpty()) | |
| 406 return true; | |
| 407 | |
| 408 // Only windows of type WINDOW_TYPE_NORMAL or WINDOW_TYPE_PANEL need to be | |
| 409 // adjusted to have minimum visibility, because they are positioned by the | |
| 410 // user and user should always be able to interact with them. Other | |
| 411 // windows are positioned programmatically. | |
| 412 if (!window_state->IsUserPositionable()) | |
| 413 return true; | |
| 414 | |
| 415 // Use entire display instead of workarea because the workarea can | |
| 416 // be further shrunk by the docked area. The logic ensures 30% | |
| 417 // visibility which should be enough to see where the window gets | |
| 418 // moved. | |
| 419 gfx::Rect display_area = GetDisplayBoundsInParent(window); | |
| 420 int min_width = bounds.width() * wm::kMinimumPercentOnScreenArea; | |
| 421 int min_height = bounds.height() * wm::kMinimumPercentOnScreenArea; | |
| 422 wm::AdjustBoundsToEnsureWindowVisibility(display_area, min_width, | |
| 423 min_height, &bounds); | |
| 424 window_state->AdjustSnappedBounds(&bounds); | |
| 425 if (window->GetBounds() != bounds) | |
| 426 window_state->SetBoundsConstrained(bounds); | |
| 427 return true; | |
| 428 } | |
| 429 case WM_EVENT_DISPLAY_BOUNDS_CHANGED: { | |
| 430 if (window_state->is_dragged() || | |
| 431 SetMaximizedOrFullscreenBounds(window_state)) { | |
| 432 return true; | |
| 433 } | |
| 434 gfx::Rect work_area_in_parent = | |
| 435 GetDisplayWorkAreaBoundsInParent(window_state->window()); | |
| 436 gfx::Rect bounds = window_state->window()->GetBounds(); | |
| 437 // When display bounds has changed, make sure the entire window is fully | |
| 438 // visible. | |
| 439 bounds.AdjustToFit(work_area_in_parent); | |
| 440 window_state->AdjustSnappedBounds(&bounds); | |
| 441 if (window_state->window()->GetBounds() != bounds) | |
| 442 window_state->SetBoundsDirectAnimated(bounds); | |
| 443 return true; | |
| 444 } | |
| 445 case WM_EVENT_WORKAREA_BOUNDS_CHANGED: { | |
| 446 // Don't resize the maximized window when the desktop is covered | |
| 447 // by fullscreen window. crbug.com/504299. | |
| 448 bool in_fullscreen = | |
| 449 window_state->window() | |
| 450 ->GetRootWindowController() | |
| 451 ->GetWorkspaceWindowState() == WORKSPACE_WINDOW_STATE_FULL_SCREEN; | |
| 452 if (in_fullscreen && window_state->IsMaximized()) | |
| 453 return true; | |
| 454 | |
| 455 if (window_state->is_dragged() || | |
| 456 SetMaximizedOrFullscreenBounds(window_state)) { | |
| 457 return true; | |
| 458 } | |
| 459 gfx::Rect work_area_in_parent = | |
| 460 GetDisplayWorkAreaBoundsInParent(window_state->window()); | |
| 461 gfx::Rect bounds = window_state->window()->GetBounds(); | |
| 462 wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent, | |
| 463 &bounds); | |
| 464 window_state->AdjustSnappedBounds(&bounds); | |
| 465 if (window_state->window()->GetBounds() != bounds) | |
| 466 window_state->SetBoundsDirectAnimated(bounds); | |
| 467 return true; | |
| 468 } | |
| 469 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION: | |
| 470 case WM_EVENT_TOGGLE_MAXIMIZE: | |
| 471 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: | |
| 472 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: | |
| 473 case WM_EVENT_TOGGLE_FULLSCREEN: | |
| 474 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT: | |
| 475 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT: | |
| 476 case WM_EVENT_CENTER: | |
| 477 case WM_EVENT_NORMAL: | |
| 478 case WM_EVENT_MAXIMIZE: | |
| 479 case WM_EVENT_MINIMIZE: | |
| 480 case WM_EVENT_FULLSCREEN: | |
| 481 case WM_EVENT_SNAP_LEFT: | |
| 482 case WM_EVENT_SNAP_RIGHT: | |
| 483 case WM_EVENT_SET_BOUNDS: | |
| 484 case WM_EVENT_SHOW_INACTIVE: | |
| 485 case WM_EVENT_DOCK: | |
| 486 break; | |
| 487 } | |
| 488 return false; | |
| 489 } | |
| 490 | |
| 491 // static | |
| 492 bool DefaultState::SetMaximizedOrFullscreenBounds(WindowState* window_state) { | |
| 493 DCHECK(!window_state->is_dragged()); | |
| 494 if (window_state->IsMaximized()) { | |
| 495 window_state->SetBoundsDirect( | |
| 496 GetMaximizedWindowBoundsInParent(window_state->window())); | |
| 497 return true; | |
| 498 } | |
| 499 if (window_state->IsFullscreen()) { | |
| 500 window_state->SetBoundsDirect( | |
| 501 GetDisplayBoundsInParent(window_state->window())); | |
| 502 return true; | |
| 503 } | |
| 504 return false; | |
| 505 } | |
| 506 | |
| 507 // static | |
| 508 void DefaultState::SetBounds(WindowState* window_state, | |
| 509 const SetBoundsEvent* event) { | |
| 510 if (window_state->is_dragged()) { | |
| 511 // TODO(oshima|varkha): This may be no longer needed, as the dragging | |
| 512 // happens in docked window container. crbug.com/485612. | |
| 513 window_state->SetBoundsDirect(event->requested_bounds()); | |
| 514 } else if (window_state->IsSnapped()) { | |
| 515 gfx::Rect work_area_in_parent = | |
| 516 GetDisplayWorkAreaBoundsInParent(window_state->window()); | |
| 517 gfx::Rect child_bounds(event->requested_bounds()); | |
| 518 wm::AdjustBoundsSmallerThan(work_area_in_parent.size(), &child_bounds); | |
| 519 window_state->AdjustSnappedBounds(&child_bounds); | |
| 520 window_state->SetBoundsDirect(child_bounds); | |
| 521 } else if (!SetMaximizedOrFullscreenBounds(window_state)) { | |
| 522 window_state->SetBoundsConstrained(event->requested_bounds()); | |
| 523 } | |
| 524 } | |
| 525 | |
| 526 void DefaultState::EnterToNextState(WindowState* window_state, | |
| 527 WindowStateType next_state_type) { | |
| 528 // Do nothing if we're already in the same state. | |
| 529 if (state_type_ == next_state_type) | |
| 530 return; | |
| 531 | |
| 532 WindowStateType previous_state_type = state_type_; | |
| 533 state_type_ = next_state_type; | |
| 534 | |
| 535 window_state->UpdateWindowShowStateFromStateType(); | |
| 536 window_state->NotifyPreStateTypeChange(previous_state_type); | |
| 537 | |
| 538 if (window_state->window()->GetParent()) { | |
| 539 if (!window_state->HasRestoreBounds() && | |
| 540 (previous_state_type == WINDOW_STATE_TYPE_DEFAULT || | |
| 541 previous_state_type == WINDOW_STATE_TYPE_NORMAL) && | |
| 542 !window_state->IsMinimized() && !window_state->IsNormalStateType()) { | |
| 543 window_state->SaveCurrentBoundsForRestore(); | |
| 544 } | |
| 545 | |
| 546 // When restoring from a minimized state, we want to restore to the | |
| 547 // previous bounds. However, we want to maintain the restore bounds. | |
| 548 // (The restore bounds are set if a user maximized the window in one | |
| 549 // axis by double clicking the window border for example). | |
| 550 gfx::Rect restore_bounds_in_screen; | |
| 551 if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED && | |
| 552 window_state->IsNormalStateType() && window_state->HasRestoreBounds() && | |
| 553 !window_state->unminimize_to_restore_bounds()) { | |
| 554 restore_bounds_in_screen = window_state->GetRestoreBoundsInScreen(); | |
| 555 window_state->SaveCurrentBoundsForRestore(); | |
| 556 } | |
| 557 | |
| 558 if (window_state->IsMaximizedOrFullscreen()) | |
| 559 MoveToDisplayForRestore(window_state); | |
| 560 | |
| 561 UpdateBoundsFromState(window_state, previous_state_type); | |
| 562 | |
| 563 // Normal state should have no restore bounds unless it's | |
| 564 // unminimized. | |
| 565 if (!restore_bounds_in_screen.IsEmpty()) | |
| 566 window_state->SetRestoreBoundsInScreen(restore_bounds_in_screen); | |
| 567 else if (window_state->IsNormalStateType()) | |
| 568 window_state->ClearRestoreBounds(); | |
| 569 } | |
| 570 window_state->NotifyPostStateTypeChange(previous_state_type); | |
| 571 } | |
| 572 | |
| 573 void DefaultState::ReenterToCurrentState( | |
| 574 WindowState* window_state, | |
| 575 WindowState::State* state_in_previous_mode) { | |
| 576 WindowStateType previous_state_type = state_in_previous_mode->GetType(); | |
| 577 | |
| 578 // A state change should not move a window into or out of full screen since | |
| 579 // full screen is a "special mode" the user wanted to be in and should be | |
| 580 // respected as such. | |
| 581 if (previous_state_type == wm::WINDOW_STATE_TYPE_FULLSCREEN) | |
| 582 state_type_ = wm::WINDOW_STATE_TYPE_FULLSCREEN; | |
| 583 else if (state_type_ == wm::WINDOW_STATE_TYPE_FULLSCREEN) | |
| 584 state_type_ = previous_state_type; | |
| 585 | |
| 586 window_state->UpdateWindowShowStateFromStateType(); | |
| 587 window_state->NotifyPreStateTypeChange(previous_state_type); | |
| 588 | |
| 589 if ((state_type_ == wm::WINDOW_STATE_TYPE_NORMAL || | |
| 590 state_type_ == wm::WINDOW_STATE_TYPE_DEFAULT) && | |
| 591 !stored_bounds_.IsEmpty()) { | |
| 592 // Use the restore mechanism to set the bounds for | |
| 593 // the window in normal state. This also covers unminimize case. | |
| 594 window_state->SetRestoreBoundsInParent(stored_bounds_); | |
| 595 } | |
| 596 | |
| 597 UpdateBoundsFromState(window_state, state_in_previous_mode->GetType()); | |
| 598 | |
| 599 // Then restore the restore bounds to their previous value. | |
| 600 if (!stored_restore_bounds_.IsEmpty()) | |
| 601 window_state->SetRestoreBoundsInParent(stored_restore_bounds_); | |
| 602 else | |
| 603 window_state->ClearRestoreBounds(); | |
| 604 | |
| 605 window_state->NotifyPostStateTypeChange(previous_state_type); | |
| 606 } | |
| 607 | |
| 608 void DefaultState::UpdateBoundsFromState(WindowState* window_state, | |
| 609 WindowStateType previous_state_type) { | |
| 610 WmWindow* window = window_state->window(); | |
| 611 gfx::Rect bounds_in_parent; | |
| 612 switch (state_type_) { | |
| 613 case WINDOW_STATE_TYPE_LEFT_SNAPPED: | |
| 614 case WINDOW_STATE_TYPE_RIGHT_SNAPPED: | |
| 615 bounds_in_parent = | |
| 616 state_type_ == WINDOW_STATE_TYPE_LEFT_SNAPPED | |
| 617 ? GetDefaultLeftSnappedWindowBoundsInParent(window) | |
| 618 : GetDefaultRightSnappedWindowBoundsInParent(window); | |
| 619 break; | |
| 620 case WINDOW_STATE_TYPE_DOCKED: { | |
| 621 if (window->GetParent()->GetShellWindowId() != | |
| 622 kShellWindowId_DockedContainer) { | |
| 623 WmWindow* docked_container = | |
| 624 window->GetRootWindow()->GetChildByShellWindowId( | |
| 625 kShellWindowId_DockedContainer); | |
| 626 ReparentChildWithTransientChildren(window, window->GetParent(), | |
| 627 docked_container); | |
| 628 } | |
| 629 // Return early because we don't want to update the bounds of the | |
| 630 // window below; as the bounds are managed by the dock layout. | |
| 631 return; | |
| 632 } | |
| 633 case WINDOW_STATE_TYPE_DEFAULT: | |
| 634 case WINDOW_STATE_TYPE_NORMAL: { | |
| 635 gfx::Rect work_area_in_parent = GetDisplayWorkAreaBoundsInParent(window); | |
| 636 if (window_state->HasRestoreBounds()) { | |
| 637 bounds_in_parent = window_state->GetRestoreBoundsInParent(); | |
| 638 // Check if the |window|'s restored size is bigger than the working area | |
| 639 // This may happen if a window was resized to maximized bounds or if the | |
| 640 // display resolution changed while the window was maximized. | |
| 641 if (previous_state_type == WINDOW_STATE_TYPE_MAXIMIZED && | |
| 642 bounds_in_parent.width() >= work_area_in_parent.width() && | |
| 643 bounds_in_parent.height() >= work_area_in_parent.height()) { | |
| 644 bounds_in_parent = work_area_in_parent; | |
| 645 bounds_in_parent.Inset(kMaximizedWindowInset, kMaximizedWindowInset, | |
| 646 kMaximizedWindowInset, kMaximizedWindowInset); | |
| 647 } | |
| 648 } else { | |
| 649 bounds_in_parent = window->GetBounds(); | |
| 650 } | |
| 651 // Make sure that part of the window is always visible. | |
| 652 wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent, | |
| 653 &bounds_in_parent); | |
| 654 break; | |
| 655 } | |
| 656 case WINDOW_STATE_TYPE_MAXIMIZED: | |
| 657 bounds_in_parent = GetMaximizedWindowBoundsInParent(window); | |
| 658 break; | |
| 659 | |
| 660 case WINDOW_STATE_TYPE_FULLSCREEN: | |
| 661 bounds_in_parent = GetDisplayBoundsInParent(window); | |
| 662 break; | |
| 663 | |
| 664 case WINDOW_STATE_TYPE_DOCKED_MINIMIZED: | |
| 665 case WINDOW_STATE_TYPE_MINIMIZED: | |
| 666 break; | |
| 667 case WINDOW_STATE_TYPE_INACTIVE: | |
| 668 case WINDOW_STATE_TYPE_END: | |
| 669 case WINDOW_STATE_TYPE_AUTO_POSITIONED: | |
| 670 return; | |
| 671 } | |
| 672 | |
| 673 if (!window_state->IsMinimized()) { | |
| 674 if (IsMinimizedWindowState(previous_state_type) || | |
| 675 window_state->IsFullscreen()) { | |
| 676 window_state->SetBoundsDirect(bounds_in_parent); | |
| 677 } else if (window_state->IsMaximized() || | |
| 678 IsMaximizedOrFullscreenWindowStateType(previous_state_type)) { | |
| 679 window_state->SetBoundsDirectCrossFade(bounds_in_parent); | |
| 680 } else if (window_state->is_dragged()) { | |
| 681 // SetBoundsDirectAnimated does not work when the window gets reparented. | |
| 682 // TODO(oshima): Consider fixing it and reenable the animation. | |
| 683 window_state->SetBoundsDirect(bounds_in_parent); | |
| 684 } else { | |
| 685 window_state->SetBoundsDirectAnimated(bounds_in_parent); | |
| 686 } | |
| 687 } | |
| 688 | |
| 689 if (window_state->IsMinimized()) { | |
| 690 // Save the previous show state so that we can correctly restore it. | |
| 691 window->SetRestoreShowState(ToWindowShowState(previous_state_type)); | |
| 692 window->SetVisibilityAnimationType( | |
| 693 WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE); | |
| 694 | |
| 695 // Hide the window. | |
| 696 window->Hide(); | |
| 697 // Activate another window. | |
| 698 if (window_state->IsActive()) | |
| 699 window_state->Deactivate(); | |
| 700 } else if ((window->GetTargetVisibility() || | |
| 701 IsMinimizedWindowState(previous_state_type)) && | |
| 702 !window->GetLayer()->visible()) { | |
| 703 // The layer may be hidden if the window was previously minimized. Make | |
| 704 // sure it's visible. | |
| 705 window->Show(); | |
| 706 if (IsMinimizedWindowState(previous_state_type) && | |
| 707 !window_state->IsMaximizedOrFullscreen()) { | |
| 708 window_state->set_unminimize_to_restore_bounds(false); | |
| 709 } | |
| 710 } | |
| 711 } | |
| 712 | |
| 713 // static | |
| 714 void DefaultState::CenterWindow(WindowState* window_state) { | |
| 715 if (!window_state->IsNormalOrSnapped()) | |
| 716 return; | |
| 717 WmWindow* window = window_state->window(); | |
| 718 if (window_state->IsSnapped()) { | |
| 719 gfx::Rect center_in_screen = window->GetDisplayNearestWindow().work_area(); | |
| 720 gfx::Size size = window_state->HasRestoreBounds() | |
| 721 ? window_state->GetRestoreBoundsInScreen().size() | |
| 722 : window->GetBounds().size(); | |
| 723 center_in_screen.ClampToCenteredSize(size); | |
| 724 window_state->SetRestoreBoundsInScreen(center_in_screen); | |
| 725 window_state->Restore(); | |
| 726 } else { | |
| 727 gfx::Rect center_in_parent = GetDisplayWorkAreaBoundsInParent(window); | |
| 728 center_in_parent.ClampToCenteredSize(window->GetBounds().size()); | |
| 729 window_state->SetBoundsDirectAnimated(center_in_parent); | |
| 730 } | |
| 731 // Centering window is treated as if a user moved and resized the window. | |
| 732 window_state->set_bounds_changed_by_user(true); | |
| 733 } | |
| 734 | |
| 735 } // namespace wm | |
| 736 } // namespace ash | |
| OLD | NEW |