// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/wm/window_state.h" #include "ash/ash_switches.h" #include "ash/root_window_controller.h" #include "ash/screen_ash.h" #include "ash/shell_window_ids.h" #include "ash/wm/window_properties.h" #include "ash/wm/window_state_delegate.h" #include "ash/wm/window_state_observer.h" #include "ash/wm/window_util.h" #include "ash/wm/wm_types.h" #include "base/auto_reset.h" #include "base/command_line.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" #include "ui/gfx/display.h" #include "ui/views/corewm/window_util.h" namespace ash { namespace wm { // static bool WindowState::IsMaximizedOrFullscreenState(ui::WindowShowState show_state) { return show_state == ui::SHOW_STATE_FULLSCREEN || show_state == ui::SHOW_STATE_MAXIMIZED; } WindowState::WindowState(aura::Window* window) : window_(window), window_position_managed_(false), bounds_changed_by_user_(false), panel_attached_(true), continue_drag_after_reparent_(false), ignored_by_shelf_(false), can_consume_system_keys_(false), top_row_keys_are_function_keys_(false), window_resizer_(NULL), always_restores_to_restore_bounds_(false), hide_shelf_when_fullscreen_(true), animate_to_fullscreen_(true), minimum_visibility_(false), in_set_window_show_type_(false), window_show_type_(ToWindowShowType(GetShowState())) { window_->AddObserver(this); #if defined(OS_CHROMEOS) // NOTE(pkotwicz): Animating to immersive fullscreen does not look good. When // the kAshEnableImmersiveFullscreenForAllWindows flag is set most windows // can be put into immersive fullscreen. It is not worth the added complexity // to only animate to fullscreen if the window is put into immersive // fullscreen. animate_to_fullscreen_ = !CommandLine::ForCurrentProcess()->HasSwitch( switches::kAshEnableImmersiveFullscreenForAllWindows); #endif } WindowState::~WindowState() { } bool WindowState::HasDelegate() const { return delegate_; } void WindowState::SetDelegate(scoped_ptr<WindowStateDelegate> delegate) { DCHECK(!delegate_.get()); delegate_ = delegate.Pass(); } ui::WindowShowState WindowState::GetShowState() const { return window_->GetProperty(aura::client::kShowStateKey); } bool WindowState::IsMinimized() const { return GetShowState() == ui::SHOW_STATE_MINIMIZED; } bool WindowState::IsMaximized() const { return GetShowState() == ui::SHOW_STATE_MAXIMIZED; } bool WindowState::IsFullscreen() const { return GetShowState() == ui::SHOW_STATE_FULLSCREEN; } bool WindowState::IsMaximizedOrFullscreen() const { return IsMaximizedOrFullscreenState(GetShowState()); } bool WindowState::IsNormalShowState() const { ui::WindowShowState state = window_->GetProperty(aura::client::kShowStateKey); return state == ui::SHOW_STATE_NORMAL || state == ui::SHOW_STATE_DEFAULT; } bool WindowState::IsActive() const { return IsActiveWindow(window_); } bool WindowState::IsDocked() const { return window_->parent() && window_->parent()->id() == internal::kShellWindowId_DockedContainer; } bool WindowState::IsSnapped() const { return window_show_type_ == SHOW_TYPE_LEFT_SNAPPED || window_show_type_ == SHOW_TYPE_RIGHT_SNAPPED; } bool WindowState::CanMaximize() const { return window_->GetProperty(aura::client::kCanMaximizeKey); } bool WindowState::CanMinimize() const { internal::RootWindowController* controller = internal::RootWindowController::ForWindow(window_); if (!controller) return false; aura::Window* lockscreen = controller->GetContainer( internal::kShellWindowId_LockScreenContainersContainer); if (lockscreen->Contains(window_)) return false; return true; } bool WindowState::CanResize() const { return window_->GetProperty(aura::client::kCanResizeKey); } bool WindowState::CanActivate() const { return views::corewm::CanActivateWindow(window_); } bool WindowState::CanSnap() const { if (!CanResize() || window_->type() == aura::client::WINDOW_TYPE_PANEL || window_->transient_parent()) return false; // If a window has a maximum size defined, snapping may make it too big. return window_->delegate() ? window_->delegate()->GetMaximumSize().IsEmpty() : true; } bool WindowState::HasRestoreBounds() const { return window_->GetProperty(aura::client::kRestoreBoundsKey) != NULL; } void WindowState::Maximize() { window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); } void WindowState::SnapLeft(const gfx::Rect& bounds) { SnapWindow(SHOW_TYPE_LEFT_SNAPPED, bounds); } void WindowState::SnapRight(const gfx::Rect& bounds) { SnapWindow(SHOW_TYPE_RIGHT_SNAPPED, bounds); } void WindowState::Minimize() { window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); } void WindowState::Unminimize() { window_->SetProperty( aura::client::kShowStateKey, window_->GetProperty(aura::client::kRestoreShowStateKey)); window_->ClearProperty(aura::client::kRestoreShowStateKey); } void WindowState::Activate() { ActivateWindow(window_); } void WindowState::Deactivate() { DeactivateWindow(window_); } void WindowState::Restore() { window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); } void WindowState::ToggleMaximized() { if (IsMaximized()) Restore(); else if (CanMaximize()) Maximize(); } void WindowState::ToggleFullscreen() { // Window which cannot be maximized should not be fullscreened. // It can, however, be restored if it was fullscreened. bool is_fullscreen = IsFullscreen(); if (!is_fullscreen && !CanMaximize()) return; if (delegate_ && delegate_->ToggleFullscreen(this)) return; if (is_fullscreen) { Restore(); } else { window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); } } void WindowState::SetBoundsInScreen( const gfx::Rect& bounds_in_screen) { gfx::Rect bounds_in_parent = ScreenAsh::ConvertRectFromScreen(window_->parent(), bounds_in_screen); window_->SetBounds(bounds_in_parent); } void WindowState::SaveCurrentBoundsForRestore() { gfx::Rect bounds_in_screen = ScreenAsh::ConvertRectToScreen(window_->parent(), window_->bounds()); SetRestoreBoundsInScreen(bounds_in_screen); } gfx::Rect WindowState::GetRestoreBoundsInScreen() const { return *window_->GetProperty(aura::client::kRestoreBoundsKey); } gfx::Rect WindowState::GetRestoreBoundsInParent() const { return ScreenAsh::ConvertRectFromScreen(window_->parent(), GetRestoreBoundsInScreen()); } void WindowState::SetRestoreBoundsInScreen(const gfx::Rect& bounds) { window_->SetProperty(aura::client::kRestoreBoundsKey, new gfx::Rect(bounds)); } void WindowState::SetRestoreBoundsInParent(const gfx::Rect& bounds) { SetRestoreBoundsInScreen( ScreenAsh::ConvertRectToScreen(window_->parent(), bounds)); } void WindowState::ClearRestoreBounds() { window_->ClearProperty(aura::client::kRestoreBoundsKey); } void WindowState::SetPreAutoManageWindowBounds( const gfx::Rect& bounds) { pre_auto_manage_window_bounds_.reset(new gfx::Rect(bounds)); } void WindowState::AddObserver(WindowStateObserver* observer) { observer_list_.AddObserver(observer); } void WindowState::RemoveObserver(WindowStateObserver* observer) { observer_list_.RemoveObserver(observer); } void WindowState::OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old) { DCHECK_EQ(window, window_); if (key == aura::client::kShowStateKey) SetWindowShowType(ToWindowShowType(GetShowState())); } void WindowState::SnapWindow(WindowShowType left_or_right, const gfx::Rect& bounds) { if (window_show_type_ == left_or_right) { window_->SetBounds(bounds); return; } // Compute the bounds that the window will restore to. If the window does not // already have restore bounds, it will be restored (when un-snapped) to the // last bounds that it had before getting snapped. gfx::Rect restore_bounds_in_screen(HasRestoreBounds() ? GetRestoreBoundsInScreen() : window_->GetBoundsInScreen()); // Set the window's restore bounds so that WorkspaceLayoutManager knows // which width to use when the snapped window is moved to the edge. SetRestoreBoundsInParent(bounds); bool was_maximized = IsMaximizedOrFullscreen(); // Before we can set the bounds we need to restore the window. // Restoring the window will set the window to its restored bounds set above. // Restore will cause OnWindowPropertyChanged() so it needs to be done // before notifying that the WindowShowType has changed to |left_or_right|. if (was_maximized) Restore(); DCHECK(left_or_right == SHOW_TYPE_LEFT_SNAPPED || left_or_right == SHOW_TYPE_RIGHT_SNAPPED); SetWindowShowType(left_or_right); // TODO(varkha): Ideally the bounds should be changed in a LayoutManager upon // observing the WindowShowType change. // If the window is a child of kShellWindowId_DockedContainer such as during // a drag, the window's bounds are not set in // WorkspaceLayoutManager::OnWindowShowTypeChanged(). Set them here. Skip // setting the bounds otherwise to avoid stopping the slide animation which // was started as a result of OnWindowShowTypeChanged(). if (IsDocked()) window_->SetBounds(bounds); SetRestoreBoundsInScreen(restore_bounds_in_screen); } void WindowState::SetWindowShowType(WindowShowType new_window_show_type) { if (in_set_window_show_type_) return; base::AutoReset<bool> resetter(&in_set_window_show_type_, true); ui::WindowShowState new_window_state = ToWindowShowState(new_window_show_type); if (new_window_state != GetShowState()) window_->SetProperty(aura::client::kShowStateKey, new_window_state); WindowShowType old_window_show_type = window_show_type_; window_show_type_ = new_window_show_type; if (old_window_show_type != window_show_type_) { FOR_EACH_OBSERVER(WindowStateObserver, observer_list_, OnWindowShowTypeChanged(this, old_window_show_type)); } } WindowState* GetActiveWindowState() { aura::Window* active = GetActiveWindow(); return active ? GetWindowState(active) : NULL; } WindowState* GetWindowState(aura::Window* window) { if (!window) return NULL; WindowState* settings = window->GetProperty(internal::kWindowStateKey); if(!settings) { settings = new WindowState(window); window->SetProperty(internal::kWindowStateKey, settings); } return settings; } const WindowState* GetWindowState(const aura::Window* window) { return GetWindowState(const_cast<aura::Window*>(window)); } } // namespace wm } // namespace ash