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/tablet_mode/tablet_mode_window_state.h" |
| 6 |
| 7 #include <utility> |
| 8 |
| 9 #include "ash/public/cpp/shell_window_ids.h" |
| 10 #include "ash/screen_util.h" |
| 11 #include "ash/shell.h" |
| 12 #include "ash/wm/screen_pinning_controller.h" |
| 13 #include "ash/wm/tablet_mode/tablet_mode_window_manager.h" |
| 14 #include "ash/wm/window_animation_types.h" |
| 15 #include "ash/wm/window_properties.h" |
| 16 #include "ash/wm/window_state_util.h" |
| 17 #include "ash/wm/wm_event.h" |
| 18 #include "ash/wm_window.h" |
| 19 #include "ui/aura/window.h" |
| 20 #include "ui/aura/window_delegate.h" |
| 21 #include "ui/compositor/layer.h" |
| 22 #include "ui/gfx/geometry/rect.h" |
| 23 |
| 24 namespace ash { |
| 25 namespace { |
| 26 |
| 27 // Sets the restore bounds and show state overrides. These values take |
| 28 // precedence over the restore bounds and restore show state (if set). |
| 29 // If |bounds_override| is empty the values are cleared. |
| 30 void SetWindowRestoreOverrides(aura::Window* window, |
| 31 const gfx::Rect& bounds_override, |
| 32 ui::WindowShowState window_state_override) { |
| 33 if (bounds_override.IsEmpty()) { |
| 34 window->ClearProperty(kRestoreShowStateOverrideKey); |
| 35 window->ClearProperty(kRestoreBoundsOverrideKey); |
| 36 return; |
| 37 } |
| 38 window->SetProperty(kRestoreShowStateOverrideKey, window_state_override); |
| 39 window->SetProperty(kRestoreBoundsOverrideKey, |
| 40 new gfx::Rect(bounds_override)); |
| 41 } |
| 42 |
| 43 // Returns the biggest possible size for a window which is about to be |
| 44 // maximized. |
| 45 gfx::Size GetMaximumSizeOfWindow(wm::WindowState* window_state) { |
| 46 DCHECK(window_state->CanMaximize() || window_state->CanResize()); |
| 47 |
| 48 gfx::Size workspace_size = |
| 49 ScreenUtil::GetMaximizedWindowBoundsInParent(window_state->window()) |
| 50 .size(); |
| 51 |
| 52 gfx::Size size = window_state->window()->delegate() |
| 53 ? window_state->window()->delegate()->GetMaximumSize() |
| 54 : gfx::Size(); |
| 55 if (size.IsEmpty()) |
| 56 return workspace_size; |
| 57 |
| 58 size.SetToMin(workspace_size); |
| 59 return size; |
| 60 } |
| 61 |
| 62 // Returns the centered bounds of the given bounds in the work area. |
| 63 gfx::Rect GetCenteredBounds(const gfx::Rect& bounds_in_parent, |
| 64 wm::WindowState* state_object) { |
| 65 gfx::Rect work_area_in_parent = |
| 66 ScreenUtil::GetDisplayWorkAreaBoundsInParent(state_object->window()); |
| 67 work_area_in_parent.ClampToCenteredSize(bounds_in_parent.size()); |
| 68 return work_area_in_parent; |
| 69 } |
| 70 |
| 71 // Returns the maximized/full screen and/or centered bounds of a window. |
| 72 gfx::Rect GetBoundsInMaximizedMode(wm::WindowState* state_object) { |
| 73 if (state_object->IsFullscreen() || state_object->IsPinned()) |
| 74 return ScreenUtil::GetDisplayBoundsInParent(state_object->window()); |
| 75 |
| 76 gfx::Rect bounds_in_parent; |
| 77 // Make the window as big as possible. |
| 78 if (state_object->CanMaximize() || state_object->CanResize()) { |
| 79 bounds_in_parent.set_size(GetMaximumSizeOfWindow(state_object)); |
| 80 } else { |
| 81 // We prefer the user given window dimensions over the current windows |
| 82 // dimensions since they are likely to be the result from some other state |
| 83 // object logic. |
| 84 if (state_object->HasRestoreBounds()) |
| 85 bounds_in_parent = state_object->GetRestoreBoundsInParent(); |
| 86 else |
| 87 bounds_in_parent = state_object->window()->bounds(); |
| 88 } |
| 89 return GetCenteredBounds(bounds_in_parent, state_object); |
| 90 } |
| 91 |
| 92 gfx::Rect GetRestoreBounds(wm::WindowState* window_state) { |
| 93 if (window_state->IsMinimized() || window_state->IsMaximized() || |
| 94 window_state->IsFullscreen()) { |
| 95 gfx::Rect restore_bounds = window_state->GetRestoreBoundsInScreen(); |
| 96 if (!restore_bounds.IsEmpty()) |
| 97 return restore_bounds; |
| 98 } |
| 99 return window_state->window()->GetBoundsInScreen(); |
| 100 } |
| 101 |
| 102 } // namespace |
| 103 |
| 104 // static |
| 105 void TabletModeWindowState::UpdateWindowPosition( |
| 106 wm::WindowState* window_state) { |
| 107 gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state); |
| 108 if (bounds_in_parent == window_state->window()->bounds()) |
| 109 return; |
| 110 window_state->SetBoundsDirect(bounds_in_parent); |
| 111 } |
| 112 |
| 113 TabletModeWindowState::TabletModeWindowState(aura::Window* window, |
| 114 TabletModeWindowManager* creator) |
| 115 : window_(window), |
| 116 creator_(creator), |
| 117 current_state_type_(wm::GetWindowState(window)->GetStateType()), |
| 118 defer_bounds_updates_(false) { |
| 119 old_state_.reset(wm::GetWindowState(window) |
| 120 ->SetStateObject(std::unique_ptr<State>(this)) |
| 121 .release()); |
| 122 } |
| 123 |
| 124 TabletModeWindowState::~TabletModeWindowState() { |
| 125 creator_->WindowStateDestroyed(window_); |
| 126 } |
| 127 |
| 128 void TabletModeWindowState::LeaveTabletMode(wm::WindowState* window_state) { |
| 129 // Note: When we return we will destroy ourselves with the |our_reference|. |
| 130 std::unique_ptr<wm::WindowState::State> our_reference = |
| 131 window_state->SetStateObject(std::move(old_state_)); |
| 132 } |
| 133 |
| 134 void TabletModeWindowState::SetDeferBoundsUpdates(bool defer_bounds_updates) { |
| 135 if (defer_bounds_updates_ == defer_bounds_updates) |
| 136 return; |
| 137 |
| 138 defer_bounds_updates_ = defer_bounds_updates; |
| 139 if (!defer_bounds_updates_) |
| 140 UpdateBounds(wm::GetWindowState(window_), true); |
| 141 } |
| 142 |
| 143 void TabletModeWindowState::OnWMEvent(wm::WindowState* window_state, |
| 144 const wm::WMEvent* event) { |
| 145 // Ignore events that are sent during the exit transition. |
| 146 if (ignore_wm_events_) { |
| 147 return; |
| 148 } |
| 149 |
| 150 switch (event->type()) { |
| 151 case wm::WM_EVENT_TOGGLE_FULLSCREEN: |
| 152 ToggleFullScreen(window_state, window_state->delegate()); |
| 153 break; |
| 154 case wm::WM_EVENT_FULLSCREEN: |
| 155 UpdateWindow(window_state, wm::WINDOW_STATE_TYPE_FULLSCREEN, true); |
| 156 break; |
| 157 case wm::WM_EVENT_PIN: |
| 158 if (!Shell::Get()->screen_pinning_controller()->IsPinned()) |
| 159 UpdateWindow(window_state, wm::WINDOW_STATE_TYPE_PINNED, true); |
| 160 break; |
| 161 case wm::WM_EVENT_TRUSTED_PIN: |
| 162 if (!Shell::Get()->screen_pinning_controller()->IsPinned()) |
| 163 UpdateWindow(window_state, wm::WINDOW_STATE_TYPE_TRUSTED_PINNED, true); |
| 164 break; |
| 165 case wm::WM_EVENT_TOGGLE_MAXIMIZE_CAPTION: |
| 166 case wm::WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: |
| 167 case wm::WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: |
| 168 case wm::WM_EVENT_TOGGLE_MAXIMIZE: |
| 169 case wm::WM_EVENT_CYCLE_SNAP_LEFT: |
| 170 case wm::WM_EVENT_CYCLE_SNAP_RIGHT: |
| 171 case wm::WM_EVENT_CENTER: |
| 172 case wm::WM_EVENT_SNAP_LEFT: |
| 173 case wm::WM_EVENT_SNAP_RIGHT: |
| 174 case wm::WM_EVENT_NORMAL: |
| 175 case wm::WM_EVENT_MAXIMIZE: |
| 176 UpdateWindow(window_state, GetMaximizedOrCenteredWindowType(window_state), |
| 177 true); |
| 178 return; |
| 179 case wm::WM_EVENT_MINIMIZE: |
| 180 UpdateWindow(window_state, wm::WINDOW_STATE_TYPE_MINIMIZED, true); |
| 181 return; |
| 182 case wm::WM_EVENT_SHOW_INACTIVE: |
| 183 return; |
| 184 case wm::WM_EVENT_SET_BOUNDS: |
| 185 if (current_state_type_ == wm::WINDOW_STATE_TYPE_MAXIMIZED) { |
| 186 // Having a maximized window, it could have been created with an empty |
| 187 // size and the caller should get his size upon leaving the maximized |
| 188 // mode. As such we set the restore bounds to the requested bounds. |
| 189 gfx::Rect bounds_in_parent = |
| 190 (static_cast<const wm::SetBoundsEvent*>(event))->requested_bounds(); |
| 191 if (!bounds_in_parent.IsEmpty()) |
| 192 window_state->SetRestoreBoundsInParent(bounds_in_parent); |
| 193 } else if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED && |
| 194 current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN && |
| 195 current_state_type_ != wm::WINDOW_STATE_TYPE_PINNED && |
| 196 current_state_type_ != wm::WINDOW_STATE_TYPE_TRUSTED_PINNED) { |
| 197 // In all other cases (except for minimized windows) we respect the |
| 198 // requested bounds and center it to a fully visible area on the screen. |
| 199 gfx::Rect bounds_in_parent = |
| 200 (static_cast<const wm::SetBoundsEvent*>(event))->requested_bounds(); |
| 201 bounds_in_parent = GetCenteredBounds(bounds_in_parent, window_state); |
| 202 if (bounds_in_parent != window_state->window()->bounds()) { |
| 203 if (window_state->window()->IsVisible()) |
| 204 window_state->SetBoundsDirectAnimated(bounds_in_parent); |
| 205 else |
| 206 window_state->SetBoundsDirect(bounds_in_parent); |
| 207 } |
| 208 } |
| 209 break; |
| 210 case wm::WM_EVENT_ADDED_TO_WORKSPACE: |
| 211 if (current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED && |
| 212 current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN && |
| 213 current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED) { |
| 214 wm::WindowStateType new_state = |
| 215 GetMaximizedOrCenteredWindowType(window_state); |
| 216 UpdateWindow(window_state, new_state, true); |
| 217 } |
| 218 break; |
| 219 case wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED: |
| 220 if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED) |
| 221 UpdateBounds(window_state, true); |
| 222 break; |
| 223 case wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED: |
| 224 // Don't animate on a screen rotation - just snap to new size. |
| 225 if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED) |
| 226 UpdateBounds(window_state, false); |
| 227 break; |
| 228 } |
| 229 } |
| 230 |
| 231 wm::WindowStateType TabletModeWindowState::GetType() const { |
| 232 return current_state_type_; |
| 233 } |
| 234 |
| 235 void TabletModeWindowState::AttachState( |
| 236 wm::WindowState* window_state, |
| 237 wm::WindowState::State* previous_state) { |
| 238 current_state_type_ = previous_state->GetType(); |
| 239 |
| 240 gfx::Rect restore_bounds = GetRestoreBounds(window_state); |
| 241 if (!restore_bounds.IsEmpty()) { |
| 242 // We do not want to do a session restore to our window states. Therefore |
| 243 // we tell the window to use the current default states instead. |
| 244 SetWindowRestoreOverrides(window_state->window(), restore_bounds, |
| 245 window_state->GetShowState()); |
| 246 } |
| 247 |
| 248 // Initialize the state to a good preset. |
| 249 if (current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED && |
| 250 current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED && |
| 251 current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN && |
| 252 current_state_type_ != wm::WINDOW_STATE_TYPE_PINNED && |
| 253 current_state_type_ != wm::WINDOW_STATE_TYPE_TRUSTED_PINNED) { |
| 254 UpdateWindow(window_state, GetMaximizedOrCenteredWindowType(window_state), |
| 255 true); |
| 256 } |
| 257 |
| 258 window_state->set_can_be_dragged(false); |
| 259 } |
| 260 |
| 261 void TabletModeWindowState::DetachState(wm::WindowState* window_state) { |
| 262 // From now on, we can use the default session restore mechanism again. |
| 263 SetWindowRestoreOverrides(window_state->window(), gfx::Rect(), |
| 264 ui::SHOW_STATE_NORMAL); |
| 265 window_state->set_can_be_dragged(true); |
| 266 } |
| 267 |
| 268 void TabletModeWindowState::UpdateWindow(wm::WindowState* window_state, |
| 269 wm::WindowStateType target_state, |
| 270 bool animated) { |
| 271 DCHECK(target_state == wm::WINDOW_STATE_TYPE_MINIMIZED || |
| 272 target_state == wm::WINDOW_STATE_TYPE_MAXIMIZED || |
| 273 target_state == wm::WINDOW_STATE_TYPE_PINNED || |
| 274 target_state == wm::WINDOW_STATE_TYPE_TRUSTED_PINNED || |
| 275 (target_state == wm::WINDOW_STATE_TYPE_NORMAL && |
| 276 !window_state->CanMaximize()) || |
| 277 target_state == wm::WINDOW_STATE_TYPE_FULLSCREEN); |
| 278 |
| 279 if (current_state_type_ == target_state) { |
| 280 if (target_state == wm::WINDOW_STATE_TYPE_MINIMIZED) |
| 281 return; |
| 282 // If the state type did not change, update it accordingly. |
| 283 UpdateBounds(window_state, animated); |
| 284 return; |
| 285 } |
| 286 |
| 287 const wm::WindowStateType old_state_type = current_state_type_; |
| 288 current_state_type_ = target_state; |
| 289 window_state->UpdateWindowPropertiesFromStateType(); |
| 290 window_state->NotifyPreStateTypeChange(old_state_type); |
| 291 |
| 292 if (target_state == wm::WINDOW_STATE_TYPE_MINIMIZED) { |
| 293 ::wm::SetWindowVisibilityAnimationType( |
| 294 window_state->window(), wm::WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE); |
| 295 window_state->window()->Hide(); |
| 296 if (window_state->IsActive()) |
| 297 window_state->Deactivate(); |
| 298 } else { |
| 299 UpdateBounds(window_state, animated); |
| 300 } |
| 301 |
| 302 window_state->NotifyPostStateTypeChange(old_state_type); |
| 303 |
| 304 if (old_state_type == wm::WINDOW_STATE_TYPE_PINNED || |
| 305 target_state == wm::WINDOW_STATE_TYPE_PINNED || |
| 306 old_state_type == wm::WINDOW_STATE_TYPE_TRUSTED_PINNED || |
| 307 target_state == wm::WINDOW_STATE_TYPE_TRUSTED_PINNED) { |
| 308 Shell::Get()->screen_pinning_controller()->SetPinnedWindow( |
| 309 WmWindow::Get(window_state->window())); |
| 310 } |
| 311 |
| 312 if ((window_state->window()->layer()->GetTargetVisibility() || |
| 313 old_state_type == wm::WINDOW_STATE_TYPE_MINIMIZED) && |
| 314 !window_state->window()->layer()->visible()) { |
| 315 // The layer may be hidden if the window was previously minimized. Make |
| 316 // sure it's visible. |
| 317 window_state->window()->Show(); |
| 318 } |
| 319 } |
| 320 |
| 321 wm::WindowStateType TabletModeWindowState::GetMaximizedOrCenteredWindowType( |
| 322 wm::WindowState* window_state) { |
| 323 return window_state->CanMaximize() ? wm::WINDOW_STATE_TYPE_MAXIMIZED |
| 324 : wm::WINDOW_STATE_TYPE_NORMAL; |
| 325 } |
| 326 |
| 327 void TabletModeWindowState::UpdateBounds(wm::WindowState* window_state, |
| 328 bool animated) { |
| 329 if (defer_bounds_updates_) |
| 330 return; |
| 331 gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state); |
| 332 // If we have a target bounds rectangle, we center it and set it |
| 333 // accordingly. |
| 334 if (!bounds_in_parent.IsEmpty() && |
| 335 bounds_in_parent != window_state->window()->bounds()) { |
| 336 if (current_state_type_ == wm::WINDOW_STATE_TYPE_MINIMIZED || |
| 337 !window_state->window()->IsVisible() || !animated) { |
| 338 window_state->SetBoundsDirect(bounds_in_parent); |
| 339 } else { |
| 340 // If we animate (to) tablet mode, we want to use the cross fade to |
| 341 // avoid flashing. |
| 342 if (window_state->IsMaximized()) |
| 343 window_state->SetBoundsDirectCrossFade(bounds_in_parent); |
| 344 else |
| 345 window_state->SetBoundsDirectAnimated(bounds_in_parent); |
| 346 } |
| 347 } |
| 348 } |
| 349 |
| 350 } // namespace ash |
OLD | NEW |