| // Copyright 2014 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/maximize_mode/maximize_mode_window_state.h" |
| |
| #include "ash/display/display_controller.h" |
| #include "ash/screen_util.h" |
| #include "ash/shell.h" |
| #include "ash/shell_window_ids.h" |
| #include "ash/wm/coordinate_conversion.h" |
| #include "ash/wm/maximize_mode/maximize_mode_window_manager.h" |
| #include "ash/wm/window_animations.h" |
| #include "ash/wm/window_properties.h" |
| #include "ash/wm/window_state_delegate.h" |
| #include "ash/wm/window_state_util.h" |
| #include "ash/wm/window_util.h" |
| #include "ash/wm/wm_event.h" |
| #include "ash/wm/workspace/workspace_window_resizer.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/gfx/rect.h" |
| #include "ui/views/view_constants_aura.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace ash { |
| namespace { |
| |
| // Returns the biggest possible size for a window which is about to be |
| // maximized. |
| gfx::Size GetMaximumSizeOfWindow(wm::WindowState* window_state) { |
| DCHECK(window_state->CanMaximize() || window_state->CanResize()); |
| |
| gfx::Size workspace_size = ScreenUtil::GetMaximizedWindowBoundsInParent( |
| window_state->window()).size(); |
| |
| aura::WindowDelegate* delegate = window_state->window()->delegate(); |
| if (!delegate) |
| return workspace_size; |
| |
| gfx::Size size = delegate->GetMaximumSize(); |
| if (size.IsEmpty()) |
| return workspace_size; |
| |
| size.SetToMin(workspace_size); |
| return size; |
| } |
| |
| // Returns the centered bounds of the given bounds in the work area. |
| gfx::Rect GetCenteredBounds(const gfx::Rect& bounds_in_parent, |
| wm::WindowState* state_object) { |
| gfx::Rect work_area_in_parent = |
| ScreenUtil::GetDisplayWorkAreaBoundsInParent(state_object->window()); |
| work_area_in_parent.ClampToCenteredSize(bounds_in_parent.size()); |
| return work_area_in_parent; |
| } |
| |
| // Returns the maximized/full screen and/or centered bounds of a window. |
| gfx::Rect GetBoundsInMaximizedMode(wm::WindowState* state_object) { |
| if (state_object->IsFullscreen()) |
| return ScreenUtil::GetDisplayBoundsInParent(state_object->window()); |
| |
| gfx::Rect bounds_in_parent; |
| // Make the window as big as possible. |
| if (state_object->CanMaximize() || state_object->CanResize()) { |
| bounds_in_parent.set_size(GetMaximumSizeOfWindow(state_object)); |
| } else { |
| // We prefer the user given window dimensions over the current windows |
| // dimensions since they are likely to be the result from some other state |
| // object logic. |
| if (state_object->HasRestoreBounds()) |
| bounds_in_parent = state_object->GetRestoreBoundsInParent(); |
| else |
| bounds_in_parent = state_object->window()->bounds(); |
| } |
| return GetCenteredBounds(bounds_in_parent, state_object); |
| } |
| |
| } // namespace |
| |
| // static |
| void MaximizeModeWindowState::UpdateWindowPosition( |
| wm::WindowState* window_state, bool animated) { |
| gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state); |
| |
| if (bounds_in_parent == window_state->window()->bounds()) |
| return; |
| |
| if (animated) |
| window_state->SetBoundsDirect(bounds_in_parent); |
| else |
| window_state->SetBoundsDirectAnimated(bounds_in_parent); |
| } |
| |
| MaximizeModeWindowState::MaximizeModeWindowState( |
| aura::Window* window, MaximizeModeWindowManager* creator) |
| : window_(window), |
| creator_(creator), |
| current_state_type_(wm::GetWindowState(window)->GetStateType()) { |
| old_state_.reset( |
| wm::GetWindowState(window)->SetStateObject( |
| scoped_ptr<State>(this).Pass()).release()); |
| } |
| |
| MaximizeModeWindowState::~MaximizeModeWindowState() { |
| creator_->WindowStateDestroyed(window_); |
| } |
| |
| void MaximizeModeWindowState::LeaveMaximizeMode(wm::WindowState* window_state) { |
| // Note: When we return we will destroy ourselves with the |our_reference|. |
| scoped_ptr<wm::WindowState::State> our_reference = |
| window_state->SetStateObject(old_state_.Pass()); |
| } |
| |
| void MaximizeModeWindowState::OnWMEvent(wm::WindowState* window_state, |
| const wm::WMEvent* event) { |
| switch (event->type()) { |
| case wm::WM_EVENT_TOGGLE_FULLSCREEN: |
| ToggleFullScreen(window_state, window_state->delegate()); |
| break; |
| case wm::WM_EVENT_FULLSCREEN: |
| UpdateWindow(window_state, wm::WINDOW_STATE_TYPE_FULLSCREEN, true); |
| break; |
| case wm::WM_EVENT_TOGGLE_MAXIMIZE_CAPTION: |
| case wm::WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: |
| case wm::WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: |
| case wm::WM_EVENT_TOGGLE_MAXIMIZE: |
| case wm::WM_EVENT_CENTER: |
| case wm::WM_EVENT_SNAP_LEFT: |
| case wm::WM_EVENT_SNAP_RIGHT: |
| case wm::WM_EVENT_NORMAL: |
| case wm::WM_EVENT_MAXIMIZE: |
| UpdateWindow(window_state, |
| GetMaximizedOrCenteredWindowType(window_state), |
| true); |
| return; |
| case wm::WM_EVENT_MINIMIZE: |
| UpdateWindow(window_state, wm::WINDOW_STATE_TYPE_MINIMIZED, true); |
| return; |
| case wm::WM_EVENT_SHOW_INACTIVE: |
| return; |
| case wm::WM_EVENT_SET_BOUNDS: |
| if (current_state_type_ == wm::WINDOW_STATE_TYPE_MAXIMIZED) { |
| // Having a maximized window, it could have been created with an empty |
| // size and the caller should get his size upon leaving the maximized |
| // mode. As such we set the restore bounds to the requested bounds. |
| gfx::Rect bounds_in_parent = |
| (static_cast<const wm::SetBoundsEvent*>(event))->requested_bounds(); |
| if (!bounds_in_parent.IsEmpty()) |
| window_state->SetRestoreBoundsInParent(bounds_in_parent); |
| } else if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED && |
| current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED && |
| current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN) { |
| // In all other cases (except for minimized windows) we respect the |
| // requested bounds and center it to a fully visible area on the screen. |
| gfx::Rect bounds_in_parent = |
| (static_cast<const wm::SetBoundsEvent*>(event))->requested_bounds(); |
| bounds_in_parent = GetCenteredBounds(bounds_in_parent, window_state); |
| if (bounds_in_parent != window_state->window()->bounds()) { |
| if (window_state->window()->IsVisible()) |
| window_state->SetBoundsDirectAnimated(bounds_in_parent); |
| else |
| window_state->SetBoundsDirect(bounds_in_parent); |
| } |
| } |
| break; |
| case wm::WM_EVENT_ADDED_TO_WORKSPACE: |
| if (current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED && |
| current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN && |
| current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED) { |
| wm::WindowStateType new_state = |
| GetMaximizedOrCenteredWindowType(window_state); |
| UpdateWindow(window_state, new_state, true); |
| } |
| break; |
| case wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED: |
| if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED) |
| UpdateBounds(window_state, true); |
| break; |
| case wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED: |
| // Don't animate on a screen rotation - just snap to new size. |
| if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED) |
| UpdateBounds(window_state, false); |
| break; |
| } |
| } |
| |
| wm::WindowStateType MaximizeModeWindowState::GetType() const { |
| return current_state_type_; |
| } |
| |
| void MaximizeModeWindowState::AttachState( |
| wm::WindowState* window_state, |
| wm::WindowState::State* previous_state) { |
| current_state_type_ = previous_state->GetType(); |
| |
| views::Widget* widget = |
| views::Widget::GetWidgetForNativeWindow(window_state->window()); |
| if (widget) { |
| gfx::Rect bounds = widget->GetRestoredBounds(); |
| if (!bounds.IsEmpty()) { |
| // We do not want to do a session restore to our window states. Therefore |
| // we tell the window to use the current default states instead. |
| window_state->window()->SetProperty(ash::kRestoreShowStateOverrideKey, |
| window_state->GetShowState()); |
| window_state->window()->SetProperty(ash::kRestoreBoundsOverrideKey, |
| new gfx::Rect(widget->GetRestoredBounds())); |
| } |
| } |
| |
| // Initialize the state to a good preset. |
| if (current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED && |
| current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED && |
| current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN) { |
| UpdateWindow(window_state, |
| GetMaximizedOrCenteredWindowType(window_state), |
| true); |
| } |
| |
| window_state->set_can_be_dragged(false); |
| } |
| |
| void MaximizeModeWindowState::DetachState(wm::WindowState* window_state) { |
| // From now on, we can use the default session restore mechanism again. |
| window_state->window()->ClearProperty(ash::kRestoreBoundsOverrideKey); |
| window_state->set_can_be_dragged(true); |
| } |
| |
| void MaximizeModeWindowState::UpdateWindow(wm::WindowState* window_state, |
| wm::WindowStateType target_state, |
| bool animated) { |
| DCHECK(target_state == wm::WINDOW_STATE_TYPE_MINIMIZED || |
| target_state == wm::WINDOW_STATE_TYPE_MAXIMIZED || |
| (target_state == wm::WINDOW_STATE_TYPE_NORMAL && |
| !window_state->CanMaximize()) || |
| target_state == wm::WINDOW_STATE_TYPE_FULLSCREEN); |
| |
| if (target_state == wm::WINDOW_STATE_TYPE_MINIMIZED) { |
| if (current_state_type_ == wm::WINDOW_STATE_TYPE_MINIMIZED) |
| return; |
| |
| current_state_type_ = target_state; |
| ::wm::SetWindowVisibilityAnimationType( |
| window_state->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE); |
| window_state->window()->Hide(); |
| if (window_state->IsActive()) |
| window_state->Deactivate(); |
| return; |
| } |
| |
| if (current_state_type_ == target_state) { |
| // If the state type did not change, update it accordingly. |
| UpdateBounds(window_state, animated); |
| return; |
| } |
| |
| const wm::WindowStateType old_state_type = current_state_type_; |
| current_state_type_ = target_state; |
| window_state->UpdateWindowShowStateFromStateType(); |
| window_state->NotifyPreStateTypeChange(old_state_type); |
| UpdateBounds(window_state, animated); |
| window_state->NotifyPostStateTypeChange(old_state_type); |
| |
| if ((window_state->window()->TargetVisibility() || |
| old_state_type == wm::WINDOW_STATE_TYPE_MINIMIZED) && |
| !window_state->window()->layer()->visible()) { |
| // The layer may be hidden if the window was previously minimized. Make |
| // sure it's visible. |
| window_state->window()->Show(); |
| } |
| } |
| |
| wm::WindowStateType MaximizeModeWindowState::GetMaximizedOrCenteredWindowType( |
| wm::WindowState* window_state) { |
| return window_state->CanMaximize() ? wm::WINDOW_STATE_TYPE_MAXIMIZED : |
| wm::WINDOW_STATE_TYPE_NORMAL; |
| } |
| |
| void MaximizeModeWindowState::UpdateBounds(wm::WindowState* window_state, |
| bool animated) { |
| gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state); |
| // If we have a target bounds rectangle, we center it and set it |
| // accordingly. |
| if (!bounds_in_parent.IsEmpty() && |
| bounds_in_parent != window_state->window()->bounds()) { |
| if (current_state_type_ == wm::WINDOW_STATE_TYPE_MINIMIZED || |
| !window_state->window()->IsVisible() || |
| !animated) { |
| window_state->SetBoundsDirect(bounds_in_parent); |
| } else { |
| // If we animate (to) maximized mode, we want to use the cross fade to |
| // avoid flashing. |
| if (window_state->IsMaximized()) |
| window_state->SetBoundsDirectCrossFade(bounds_in_parent); |
| else |
| window_state->SetBoundsDirectAnimated(bounds_in_parent); |
| } |
| } |
| } |
| |
| } // namespace ash |