window_state.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright 2013 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/window_state.h" 6 7#include "ash/ash_switches.h" 8#include "ash/root_window_controller.h" 9#include "ash/screen_util.h" 10#include "ash/shell_window_ids.h" 11#include "ash/wm/default_state.h" 12#include "ash/wm/window_properties.h" 13#include "ash/wm/window_state_delegate.h" 14#include "ash/wm/window_state_observer.h" 15#include "ash/wm/window_util.h" 16#include "ash/wm/wm_types.h" 17#include "base/auto_reset.h" 18#include "base/command_line.h" 19#include "ui/aura/client/aura_constants.h" 20#include "ui/aura/layout_manager.h" 21#include "ui/aura/window.h" 22#include "ui/aura/window_delegate.h" 23#include "ui/compositor/scoped_layer_animation_settings.h" 24#include "ui/gfx/display.h" 25#include "ui/views/corewm/window_util.h" 26 27namespace ash { 28namespace wm { 29 30namespace { 31 32// A tentative class to set the bounds on the window. 33// TODO(oshima): Once all logic is cleaned up, move this to the real layout 34// manager with proper friendship. 35class BoundsSetter : public aura::LayoutManager { 36 public: 37 BoundsSetter() {} 38 virtual ~BoundsSetter() {} 39 40 // aura::LayoutManager overrides: 41 virtual void OnWindowResized() OVERRIDE {} 42 virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {} 43 virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {} 44 virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {} 45 virtual void OnChildWindowVisibilityChanged( 46 aura::Window* child, bool visible) OVERRIDE {} 47 virtual void SetChildBounds( 48 aura::Window* child, const gfx::Rect& requested_bounds) OVERRIDE {} 49 50 void SetBounds(aura::Window* window, const gfx::Rect& bounds) { 51 SetChildBoundsDirect(window, bounds); 52 } 53 54 private: 55 DISALLOW_COPY_AND_ASSIGN(BoundsSetter); 56}; 57 58WMEvent WMEventFromShowState(ui::WindowShowState requested_show_state) { 59 switch (requested_show_state) { 60 case ui::SHOW_STATE_DEFAULT: 61 case ui::SHOW_STATE_NORMAL: 62 return NORMAL; 63 case ui::SHOW_STATE_MINIMIZED: 64 return MINIMIZE; 65 case ui::SHOW_STATE_MAXIMIZED: 66 return MAXIMIZE; 67 case ui::SHOW_STATE_FULLSCREEN: 68 return FULLSCREEN; 69 case ui::SHOW_STATE_INACTIVE: 70 return SHOW_INACTIVE; 71 case ui::SHOW_STATE_DETACHED: 72 case ui::SHOW_STATE_END: 73 NOTREACHED() << "No WMEvent defined for the show type:" 74 << requested_show_state; 75 } 76 return NORMAL; 77} 78 79} // namespace 80 81WindowState::WindowState(aura::Window* window) 82 : window_(window), 83 window_position_managed_(false), 84 bounds_changed_by_user_(false), 85 panel_attached_(true), 86 continue_drag_after_reparent_(false), 87 ignored_by_shelf_(false), 88 can_consume_system_keys_(false), 89 top_row_keys_are_function_keys_(false), 90 unminimize_to_restore_bounds_(false), 91 hide_shelf_when_fullscreen_(true), 92 animate_to_fullscreen_(true), 93 minimum_visibility_(false), 94 ignore_property_change_(false), 95 window_show_type_(ToWindowShowType(GetShowState())), 96 current_state_(new DefaultState) { 97 window_->AddObserver(this); 98#if defined(OS_CHROMEOS) 99 // NOTE(pkotwicz): Animating to immersive fullscreen does not look good. When 100 // switches::UseImmersiveFullscreenForAllWindows() returns true, most windows 101 // can be put into immersive fullscreen. It is not worth the added complexity 102 // to only animate to fullscreen if the window is put into immersive 103 // fullscreen. 104 animate_to_fullscreen_ = !switches::UseImmersiveFullscreenForAllWindows(); 105#endif 106} 107 108WindowState::~WindowState() { 109} 110 111bool WindowState::HasDelegate() const { 112 return delegate_; 113} 114 115void WindowState::SetDelegate(scoped_ptr<WindowStateDelegate> delegate) { 116 DCHECK(!delegate_.get()); 117 delegate_ = delegate.Pass(); 118} 119 120ui::WindowShowState WindowState::GetShowState() const { 121 return window_->GetProperty(aura::client::kShowStateKey); 122} 123 124bool WindowState::IsMinimized() const { 125 return GetShowState() == ui::SHOW_STATE_MINIMIZED; 126} 127 128bool WindowState::IsMaximized() const { 129 return GetShowState() == ui::SHOW_STATE_MAXIMIZED; 130} 131 132bool WindowState::IsFullscreen() const { 133 return GetShowState() == ui::SHOW_STATE_FULLSCREEN; 134} 135 136bool WindowState::IsMaximizedOrFullscreen() const { 137 ui::WindowShowState show_state(GetShowState()); 138 return show_state == ui::SHOW_STATE_FULLSCREEN || 139 show_state == ui::SHOW_STATE_MAXIMIZED; 140} 141 142bool WindowState::IsNormalShowState() const { 143 ui::WindowShowState state = GetShowState(); 144 return state == ui::SHOW_STATE_NORMAL || state == ui::SHOW_STATE_DEFAULT; 145} 146 147bool WindowState::IsNormalShowType() const { 148 return window_show_type_ == SHOW_TYPE_NORMAL || 149 window_show_type_ == SHOW_TYPE_DEFAULT; 150} 151 152bool WindowState::IsActive() const { 153 return IsActiveWindow(window_); 154} 155 156bool WindowState::IsDocked() const { 157 return window_->parent() && 158 window_->parent()->id() == internal::kShellWindowId_DockedContainer; 159} 160 161bool WindowState::IsSnapped() const { 162 return window_show_type_ == SHOW_TYPE_LEFT_SNAPPED || 163 window_show_type_ == SHOW_TYPE_RIGHT_SNAPPED; 164} 165 166bool WindowState::CanMaximize() const { 167 return window_->GetProperty(aura::client::kCanMaximizeKey); 168} 169 170bool WindowState::CanMinimize() const { 171 internal::RootWindowController* controller = 172 internal::RootWindowController::ForWindow(window_); 173 if (!controller) 174 return false; 175 aura::Window* lockscreen = controller->GetContainer( 176 internal::kShellWindowId_LockScreenContainersContainer); 177 if (lockscreen->Contains(window_)) 178 return false; 179 180 return true; 181} 182 183bool WindowState::CanResize() const { 184 return window_->GetProperty(aura::client::kCanResizeKey); 185} 186 187bool WindowState::CanActivate() const { 188 return views::corewm::CanActivateWindow(window_); 189} 190 191bool WindowState::CanSnap() const { 192 if (!CanResize() || window_->type() == ui::wm::WINDOW_TYPE_PANEL || 193 views::corewm::GetTransientParent(window_)) 194 return false; 195 // If a window has a maximum size defined, snapping may make it too big. 196 return window_->delegate() ? window_->delegate()->GetMaximumSize().IsEmpty() : 197 true; 198} 199 200bool WindowState::HasRestoreBounds() const { 201 return window_->GetProperty(aura::client::kRestoreBoundsKey) != NULL; 202} 203 204void WindowState::Maximize() { 205 window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 206} 207 208void WindowState::SnapLeft(const gfx::Rect& bounds) { 209 SnapWindow(SHOW_TYPE_LEFT_SNAPPED, bounds); 210} 211 212void WindowState::SnapRight(const gfx::Rect& bounds) { 213 SnapWindow(SHOW_TYPE_RIGHT_SNAPPED, bounds); 214} 215 216void WindowState::Minimize() { 217 window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 218} 219 220void WindowState::Unminimize() { 221 window_->SetProperty( 222 aura::client::kShowStateKey, 223 window_->GetProperty(aura::client::kRestoreShowStateKey)); 224 window_->ClearProperty(aura::client::kRestoreShowStateKey); 225} 226 227void WindowState::Activate() { 228 ActivateWindow(window_); 229} 230 231void WindowState::Deactivate() { 232 DeactivateWindow(window_); 233} 234 235void WindowState::Restore() { 236 if (!IsNormalShowType()) 237 OnWMEvent(NORMAL); 238} 239 240void WindowState::ToggleFullscreen() { 241 OnWMEvent(TOGGLE_FULLSCREEN); 242} 243 244void WindowState::OnWMEvent(WMEvent event) { 245 current_state_->OnWMEvent(this, event); 246} 247 248void WindowState::SetBoundsInScreen( 249 const gfx::Rect& bounds_in_screen) { 250 gfx::Rect bounds_in_parent = 251 ScreenUtil::ConvertRectFromScreen(window_->parent(), 252 bounds_in_screen); 253 window_->SetBounds(bounds_in_parent); 254} 255 256void WindowState::SaveCurrentBoundsForRestore() { 257 gfx::Rect bounds_in_screen = 258 ScreenUtil::ConvertRectToScreen(window_->parent(), 259 window_->bounds()); 260 SetRestoreBoundsInScreen(bounds_in_screen); 261} 262 263gfx::Rect WindowState::GetRestoreBoundsInScreen() const { 264 return *window_->GetProperty(aura::client::kRestoreBoundsKey); 265} 266 267gfx::Rect WindowState::GetRestoreBoundsInParent() const { 268 return ScreenUtil::ConvertRectFromScreen(window_->parent(), 269 GetRestoreBoundsInScreen()); 270} 271 272void WindowState::SetRestoreBoundsInScreen(const gfx::Rect& bounds) { 273 window_->SetProperty(aura::client::kRestoreBoundsKey, new gfx::Rect(bounds)); 274} 275 276void WindowState::SetRestoreBoundsInParent(const gfx::Rect& bounds) { 277 SetRestoreBoundsInScreen( 278 ScreenUtil::ConvertRectToScreen(window_->parent(), bounds)); 279} 280 281void WindowState::ClearRestoreBounds() { 282 window_->ClearProperty(aura::client::kRestoreBoundsKey); 283} 284 285void WindowState::SetPreAutoManageWindowBounds( 286 const gfx::Rect& bounds) { 287 pre_auto_manage_window_bounds_.reset(new gfx::Rect(bounds)); 288} 289 290void WindowState::AddObserver(WindowStateObserver* observer) { 291 observer_list_.AddObserver(observer); 292} 293 294void WindowState::RemoveObserver(WindowStateObserver* observer) { 295 observer_list_.RemoveObserver(observer); 296} 297 298void WindowState::CreateDragDetails(aura::Window* window, 299 const gfx::Point& point_in_parent, 300 int window_component, 301 aura::client::WindowMoveSource source) { 302 drag_details_.reset( 303 new DragDetails(window, point_in_parent, window_component, source)); 304} 305 306void WindowState::DeleteDragDetails() { 307 drag_details_.reset(); 308} 309 310void WindowState::SetAndClearRestoreBounds() { 311 DCHECK(HasRestoreBounds()); 312 SetBoundsInScreen(GetRestoreBoundsInScreen()); 313 ClearRestoreBounds(); 314} 315 316void WindowState::AdjustSnappedBounds(gfx::Rect* bounds) { 317 if (is_dragged() || !IsSnapped()) 318 return; 319 gfx::Rect maximized_bounds = ScreenUtil::GetMaximizedWindowBoundsInParent( 320 window_); 321 if (window_show_type() == SHOW_TYPE_LEFT_SNAPPED) 322 bounds->set_x(maximized_bounds.x()); 323 else if (window_show_type() == SHOW_TYPE_RIGHT_SNAPPED) 324 bounds->set_x(maximized_bounds.right() - bounds->width()); 325 bounds->set_y(maximized_bounds.y()); 326 bounds->set_height(maximized_bounds.height()); 327} 328 329void WindowState::OnWindowPropertyChanged(aura::Window* window, 330 const void* key, 331 intptr_t old) { 332 DCHECK_EQ(window, window_); 333 if (key == aura::client::kShowStateKey && !ignore_property_change_) 334 OnWMEvent(WMEventFromShowState(GetShowState())); 335} 336 337void WindowState::SnapWindow(WindowShowType left_or_right, 338 const gfx::Rect& bounds) { 339 if (window_show_type_ == left_or_right) { 340 window_->SetBounds(bounds); 341 return; 342 } 343 344 // Compute the bounds that the window will restore to. If the window does not 345 // already have restore bounds, it will be restored (when un-snapped) to the 346 // last bounds that it had before getting snapped. 347 gfx::Rect restore_bounds_in_screen(HasRestoreBounds() ? 348 GetRestoreBoundsInScreen() : window_->GetBoundsInScreen()); 349 // Set the window's restore bounds so that WorkspaceLayoutManager knows 350 // which width to use when the snapped window is moved to the edge. 351 SetRestoreBoundsInParent(bounds); 352 353 DCHECK(left_or_right == SHOW_TYPE_LEFT_SNAPPED || 354 left_or_right == SHOW_TYPE_RIGHT_SNAPPED); 355 OnWMEvent(left_or_right == SHOW_TYPE_LEFT_SNAPPED ? 356 SNAP_LEFT : SNAP_RIGHT); 357 358 // TODO(varkha): Ideally the bounds should be changed in a LayoutManager upon 359 // observing the WindowShowType change. 360 // If the window is a child of kShellWindowId_DockedContainer such as during 361 // a drag, the window's bounds are not set in 362 // WorkspaceLayoutManager::OnWindowShowTypeChanged(). Set them here. Skip 363 // setting the bounds otherwise to avoid stopping the slide animation which 364 // was started as a result of OnWindowShowTypeChanged(). 365 if (IsDocked()) 366 window_->SetBounds(bounds); 367 SetRestoreBoundsInScreen(restore_bounds_in_screen); 368} 369 370void WindowState::UpdateWindowShowType(WindowShowType new_window_show_type) { 371 ui::WindowShowState new_window_state = 372 ToWindowShowState(new_window_show_type); 373 if (new_window_state != GetShowState()) { 374 base::AutoReset<bool> resetter(&ignore_property_change_, true); 375 window_->SetProperty(aura::client::kShowStateKey, new_window_state); 376 } 377 window_show_type_ = new_window_show_type; 378} 379 380void WindowState::NotifyPreShowTypeChange(WindowShowType old_window_show_type) { 381 FOR_EACH_OBSERVER(WindowStateObserver, observer_list_, 382 OnPreWindowShowTypeChange(this, old_window_show_type)); 383} 384 385void WindowState::NotifyPostShowTypeChange( 386 WindowShowType old_window_show_type) { 387 FOR_EACH_OBSERVER(WindowStateObserver, observer_list_, 388 OnPostWindowShowTypeChange(this, old_window_show_type)); 389} 390 391void WindowState::SetBoundsDirect(const gfx::Rect& bounds) { 392 BoundsSetter().SetBounds(window_, bounds); 393} 394 395void WindowState::SetBoundsDirectAnimated(const gfx::Rect& bounds) { 396 const int kBoundsChangeSlideDurationMs = 120; 397 398 ui::Layer* layer = window_->layer(); 399 ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator()); 400 slide_settings.SetPreemptionStrategy( 401 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 402 slide_settings.SetTransitionDuration( 403 base::TimeDelta::FromMilliseconds(kBoundsChangeSlideDurationMs)); 404 SetBoundsDirect(bounds); 405} 406 407WindowState* GetActiveWindowState() { 408 aura::Window* active = GetActiveWindow(); 409 return active ? GetWindowState(active) : NULL; 410} 411 412WindowState* GetWindowState(aura::Window* window) { 413 if (!window) 414 return NULL; 415 WindowState* settings = window->GetProperty(internal::kWindowStateKey); 416 if(!settings) { 417 settings = new WindowState(window); 418 window->SetProperty(internal::kWindowStateKey, settings); 419 } 420 return settings; 421} 422 423const WindowState* GetWindowState(const aura::Window* window) { 424 return GetWindowState(const_cast<aura::Window*>(window)); 425} 426 427} // namespace wm 428} // namespace ash 429