This source file includes following definitions.
- IsPanel
- MoveToDisplayForRestore
- OnWMEvent
- GetType
- AttachState
- DetachState
- ProcessCompoundEvents
- ProcessWorkspaceEvents
- SetMaximizedOrFullscreenBounds
- SetBounds
- EnterToNextState
- ReenterToCurrentState
- UpdateBoundsFromState
- CenterWindow
#include "ash/wm/default_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/window_animations.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_state_delegate.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"
namespace ash {
namespace wm {
namespace {
const float kMinimumPercentOnScreenArea = 0.3f;
bool IsPanel(aura::Window* window) {
return window->parent() &&
window->parent()->id() == kShellWindowId_PanelContainer;
}
void MoveToDisplayForRestore(WindowState* window_state) {
if (!window_state->HasRestoreBounds())
return;
const gfx::Rect& restore_bounds = window_state->GetRestoreBoundsInScreen();
gfx::Rect display_area = Shell::GetScreen()->GetDisplayNearestWindow(
window_state->window()).bounds();
if (!display_area.Intersects(restore_bounds)) {
const gfx::Display& display =
Shell::GetScreen()->GetDisplayMatching(restore_bounds);
DisplayController* display_controller =
Shell::GetInstance()->display_controller();
aura::Window* new_root =
display_controller->GetRootWindowForDisplayId(display.id());
if (new_root != window_state->window()->GetRootWindow()) {
aura::Window* new_container =
Shell::GetContainer(new_root, window_state->window()->parent()->id());
new_container->AddChild(window_state->window());
}
}
}
}
DefaultState::DefaultState(WindowStateType initial_state_type)
: state_type_(initial_state_type) {}
DefaultState::~DefaultState() {}
void DefaultState::OnWMEvent(WindowState* window_state,
const WMEvent* event) {
if (ProcessWorkspaceEvents(window_state, event))
return;
if (ProcessCompoundEvents(window_state, event))
return;
WindowStateType next_state_type = WINDOW_STATE_TYPE_NORMAL;
switch (event->type()) {
case WM_EVENT_NORMAL:
next_state_type = WINDOW_STATE_TYPE_NORMAL;
break;
case WM_EVENT_MAXIMIZE:
next_state_type = WINDOW_STATE_TYPE_MAXIMIZED;
break;
case WM_EVENT_MINIMIZE:
next_state_type = WINDOW_STATE_TYPE_MINIMIZED;
break;
case WM_EVENT_FULLSCREEN:
next_state_type = WINDOW_STATE_TYPE_FULLSCREEN;
break;
case WM_EVENT_SNAP_LEFT:
next_state_type = WINDOW_STATE_TYPE_LEFT_SNAPPED;
break;
case WM_EVENT_SNAP_RIGHT:
next_state_type = WINDOW_STATE_TYPE_RIGHT_SNAPPED;
break;
case WM_EVENT_SET_BOUNDS:
SetBounds(window_state, static_cast<const SetBoundsEvent*>(event));
return;
case WM_EVENT_SHOW_INACTIVE:
next_state_type = WINDOW_STATE_TYPE_INACTIVE;
break;
case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
case WM_EVENT_TOGGLE_MAXIMIZE:
case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE:
case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
case WM_EVENT_TOGGLE_FULLSCREEN:
case WM_EVENT_CENTER:
NOTREACHED() << "Compound event should not reach here:" << event;
return;
case WM_EVENT_ADDED_TO_WORKSPACE:
case WM_EVENT_WORKAREA_BOUNDS_CHANGED:
case WM_EVENT_DISPLAY_BOUNDS_CHANGED:
NOTREACHED() << "Workspace event should not reach here:" << event;
return;
}
WindowStateType current = window_state->GetStateType();
if (next_state_type == current && window_state->IsSnapped()) {
gfx::Rect snapped_bounds = event->type() == WM_EVENT_SNAP_LEFT ?
GetDefaultLeftSnappedWindowBoundsInParent(window_state->window()) :
GetDefaultRightSnappedWindowBoundsInParent(window_state->window());
window_state->SetBoundsDirectAnimated(snapped_bounds);
return;
}
EnterToNextState(window_state, next_state_type);
}
WindowStateType DefaultState::GetType() const {
return state_type_;
}
void DefaultState::AttachState(
WindowState* window_state,
WindowState::State* state_in_previous_mode) {
DCHECK_EQ(stored_window_state_, window_state);
ReenterToCurrentState(window_state, state_in_previous_mode);
gfx::Display current_display = Shell::GetScreen()->
GetDisplayNearestWindow(window_state->window());
if (stored_display_state_.bounds() != current_display.bounds()) {
const WMEvent event(wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED);
window_state->OnWMEvent(&event);
} else if (stored_display_state_.work_area() != current_display.work_area()) {
const WMEvent event(wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED);
window_state->OnWMEvent(&event);
}
}
void DefaultState::DetachState(WindowState* window_state) {
stored_window_state_ = window_state;
aura::Window* window = window_state->window();
stored_bounds_ = window->bounds();
stored_restore_bounds_ = window_state->HasRestoreBounds() ?
window_state->GetRestoreBoundsInParent() : gfx::Rect();
stored_display_state_ = Shell::GetScreen()->
GetDisplayNearestWindow(window_state->window());
}
bool DefaultState::ProcessCompoundEvents(WindowState* window_state,
const WMEvent* event) {
aura::Window* window = window_state->window();
switch (event->type()) {
case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
if (window_state->IsFullscreen()) {
const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
window_state->OnWMEvent(&event);
} else if (window_state->IsMaximized()) {
window_state->Restore();
} else if (window_state->IsNormalOrSnapped()) {
if (window_state->CanMaximize())
window_state->Maximize();
}
return true;
case WM_EVENT_TOGGLE_MAXIMIZE:
if (window_state->IsFullscreen()) {
const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
window_state->OnWMEvent(&event);
} else if (window_state->IsMaximized()) {
window_state->Restore();
} else if (window_state->CanMaximize()) {
window_state->Maximize();
}
return true;
case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: {
gfx::Rect work_area =
ScreenUtil::GetDisplayWorkAreaBoundsInParent(window);
if (window->delegate()->GetMaximumSize().height() != 0 ||
!window_state->IsNormalStateType()) {
return true;
}
if (window_state->HasRestoreBounds() &&
(window->bounds().height() == work_area.height() &&
window->bounds().y() == work_area.y())) {
window_state->SetAndClearRestoreBounds();
} else {
window_state->SaveCurrentBoundsForRestore();
window->SetBounds(gfx::Rect(window->bounds().x(),
work_area.y(),
window->bounds().width(),
work_area.height()));
}
return true;
}
case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: {
if (window->delegate()->GetMaximumSize().width() != 0)
return true;
if (!window_state->IsNormalOrSnapped())
return true;
gfx::Rect work_area =
ScreenUtil::GetDisplayWorkAreaBoundsInParent(window);
if (window_state->IsNormalStateType() &&
window_state->HasRestoreBounds() &&
(window->bounds().width() == work_area.width() &&
window->bounds().x() == work_area.x())) {
window_state->SetAndClearRestoreBounds();
} else {
gfx::Rect new_bounds(work_area.x(),
window->bounds().y(),
work_area.width(),
window->bounds().height());
gfx::Rect restore_bounds = window->bounds();
if (window_state->IsSnapped()) {
window_state->SetRestoreBoundsInParent(new_bounds);
window_state->Restore();
}
window_state->SetRestoreBoundsInParent(restore_bounds);
window->SetBounds(new_bounds);
}
return true;
}
case WM_EVENT_TOGGLE_FULLSCREEN: {
bool is_fullscreen = window_state->IsFullscreen();
if (!is_fullscreen && !window_state->CanMaximize())
return true;
if (window_state->delegate() &&
window_state->delegate()->ToggleFullscreen(window_state)) {
return true;
}
if (is_fullscreen) {
window_state->Restore();
} else {
window_state->window()->SetProperty(aura::client::kShowStateKey,
ui::SHOW_STATE_FULLSCREEN);
}
return true;
}
case WM_EVENT_CENTER:
CenterWindow(window_state);
return true;
case WM_EVENT_NORMAL:
case WM_EVENT_MAXIMIZE:
case WM_EVENT_MINIMIZE:
case WM_EVENT_FULLSCREEN:
case WM_EVENT_SNAP_LEFT:
case WM_EVENT_SNAP_RIGHT:
case WM_EVENT_SET_BOUNDS:
case WM_EVENT_SHOW_INACTIVE:
break;
case WM_EVENT_ADDED_TO_WORKSPACE:
case WM_EVENT_WORKAREA_BOUNDS_CHANGED:
case WM_EVENT_DISPLAY_BOUNDS_CHANGED:
NOTREACHED() << "Workspace event should not reach here:" << event;
break;
}
return false;
}
bool DefaultState::ProcessWorkspaceEvents(WindowState* window_state,
const WMEvent* event) {
switch (event->type()) {
case WM_EVENT_ADDED_TO_WORKSPACE: {
if (window_state->is_dragged() ||
SetMaximizedOrFullscreenBounds(window_state)) {
return true;
}
aura::Window* window = window_state->window();
gfx::Rect bounds = window->bounds();
if (bounds.IsEmpty())
return true;
gfx::Rect display_area = ScreenUtil::GetDisplayBoundsInParent(window);
int min_width = bounds.width() * kMinimumPercentOnScreenArea;
int min_height = bounds.height() * kMinimumPercentOnScreenArea;
AdjustBoundsToEnsureWindowVisibility(
display_area, min_width, min_height, &bounds);
window_state->AdjustSnappedBounds(&bounds);
if (window->bounds() != bounds)
window_state->SetBoundsConstrained(bounds);
return true;
}
case WM_EVENT_DISPLAY_BOUNDS_CHANGED: {
if (window_state->is_dragged() ||
SetMaximizedOrFullscreenBounds(window_state)) {
return true;
}
gfx::Rect work_area_in_parent =
ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
gfx::Rect bounds = window_state->window()->bounds();
bounds.AdjustToFit(work_area_in_parent);
window_state->AdjustSnappedBounds(&bounds);
if (window_state->window()->bounds() != bounds)
window_state->SetBoundsDirectAnimated(bounds);
return true;
}
case WM_EVENT_WORKAREA_BOUNDS_CHANGED: {
if (window_state->is_dragged() ||
SetMaximizedOrFullscreenBounds(window_state)) {
return true;
}
gfx::Rect work_area_in_parent =
ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
gfx::Rect bounds = window_state->window()->bounds();
AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent, &bounds);
window_state->AdjustSnappedBounds(&bounds);
if (window_state->window()->bounds() != bounds)
window_state->SetBoundsDirectAnimated(bounds);
return true;
}
case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
case WM_EVENT_TOGGLE_MAXIMIZE:
case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE:
case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
case WM_EVENT_TOGGLE_FULLSCREEN:
case WM_EVENT_CENTER:
case WM_EVENT_NORMAL:
case WM_EVENT_MAXIMIZE:
case WM_EVENT_MINIMIZE:
case WM_EVENT_FULLSCREEN:
case WM_EVENT_SNAP_LEFT:
case WM_EVENT_SNAP_RIGHT:
case WM_EVENT_SET_BOUNDS:
case WM_EVENT_SHOW_INACTIVE:
break;
}
return false;
}
bool DefaultState::SetMaximizedOrFullscreenBounds(WindowState* window_state) {
DCHECK(!window_state->is_dragged());
if (window_state->IsMaximized()) {
window_state->SetBoundsDirect(
ScreenUtil::GetMaximizedWindowBoundsInParent(window_state->window()));
return true;
}
if (window_state->IsFullscreen()) {
window_state->SetBoundsDirect(
ScreenUtil::GetDisplayBoundsInParent(window_state->window()));
return true;
}
return false;
}
void DefaultState::SetBounds(WindowState* window_state,
const SetBoundsEvent* event) {
if (window_state->is_dragged()) {
window_state->SetBoundsDirect(event->requested_bounds());
} else if (window_state->IsSnapped()) {
gfx::Rect work_area_in_parent =
ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
gfx::Rect child_bounds(event->requested_bounds());
AdjustBoundsSmallerThan(work_area_in_parent.size(), &child_bounds);
window_state->AdjustSnappedBounds(&child_bounds);
window_state->SetBoundsDirect(child_bounds);
} else if (!SetMaximizedOrFullscreenBounds(window_state)) {
window_state->SetBoundsConstrained(event->requested_bounds());
}
}
void DefaultState::EnterToNextState(WindowState* window_state,
WindowStateType next_state_type) {
if (state_type_ == next_state_type)
return;
WindowStateType previous_state_type = state_type_;
state_type_ = next_state_type;
window_state->UpdateWindowShowStateFromStateType();
window_state->NotifyPreStateTypeChange(previous_state_type);
if (window_state->window()->parent() &&
(window_state->IsSnapped() ||
(!window_state->IsDocked() && !IsPanel(window_state->window())))) {
if (!window_state->HasRestoreBounds() &&
(previous_state_type == WINDOW_STATE_TYPE_DEFAULT ||
previous_state_type == WINDOW_STATE_TYPE_NORMAL) &&
!window_state->IsMinimized() &&
!window_state->IsNormalStateType()) {
window_state->SaveCurrentBoundsForRestore();
}
gfx::Rect restore_bounds_in_screen;
if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED &&
window_state->IsNormalStateType() &&
window_state->HasRestoreBounds() &&
!window_state->unminimize_to_restore_bounds()) {
restore_bounds_in_screen = window_state->GetRestoreBoundsInScreen();
window_state->SaveCurrentBoundsForRestore();
}
if (window_state->IsMaximizedOrFullscreen())
MoveToDisplayForRestore(window_state);
UpdateBoundsFromState(window_state, previous_state_type);
if (!restore_bounds_in_screen.IsEmpty())
window_state->SetRestoreBoundsInScreen(restore_bounds_in_screen);
else if (window_state->IsNormalStateType())
window_state->ClearRestoreBounds();
}
window_state->NotifyPostStateTypeChange(previous_state_type);
}
void DefaultState::ReenterToCurrentState(
WindowState* window_state,
WindowState::State* state_in_previous_mode) {
WindowStateType previous_state_type = state_in_previous_mode->GetType();
window_state->UpdateWindowShowStateFromStateType();
window_state->NotifyPreStateTypeChange(previous_state_type);
if (state_type_ == wm::WINDOW_STATE_TYPE_NORMAL ||
state_type_ == wm::WINDOW_STATE_TYPE_DEFAULT) {
window_state->SetRestoreBoundsInParent(stored_bounds_);
}
UpdateBoundsFromState(window_state, state_in_previous_mode->GetType());
if (!stored_restore_bounds_.IsEmpty())
window_state->SetRestoreBoundsInParent(stored_restore_bounds_);
else
window_state->ClearRestoreBounds();
window_state->NotifyPostStateTypeChange(previous_state_type);
}
void DefaultState::UpdateBoundsFromState(WindowState* window_state,
WindowStateType previous_state_type) {
aura::Window* window = window_state->window();
gfx::Rect bounds_in_parent;
switch (state_type_) {
case WINDOW_STATE_TYPE_LEFT_SNAPPED:
case WINDOW_STATE_TYPE_RIGHT_SNAPPED:
bounds_in_parent = state_type_ == WINDOW_STATE_TYPE_LEFT_SNAPPED ?
GetDefaultLeftSnappedWindowBoundsInParent(window_state->window()) :
GetDefaultRightSnappedWindowBoundsInParent(window_state->window());
break;
case WINDOW_STATE_TYPE_DEFAULT:
case WINDOW_STATE_TYPE_NORMAL: {
gfx::Rect work_area_in_parent =
ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
if (window_state->HasRestoreBounds())
bounds_in_parent = window_state->GetRestoreBoundsInParent();
else
bounds_in_parent = window->bounds();
AdjustBoundsToEnsureMinimumWindowVisibility(
work_area_in_parent, &bounds_in_parent);
break;
}
case WINDOW_STATE_TYPE_MAXIMIZED:
bounds_in_parent = ScreenUtil::GetMaximizedWindowBoundsInParent(window);
break;
case WINDOW_STATE_TYPE_FULLSCREEN:
bounds_in_parent = ScreenUtil::GetDisplayBoundsInParent(window);
break;
case WINDOW_STATE_TYPE_MINIMIZED:
break;
case WINDOW_STATE_TYPE_INACTIVE:
case WINDOW_STATE_TYPE_DETACHED:
case WINDOW_STATE_TYPE_END:
case WINDOW_STATE_TYPE_AUTO_POSITIONED:
return;
}
if (state_type_ != WINDOW_STATE_TYPE_MINIMIZED) {
if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED ||
window_state->IsFullscreen()) {
window_state->SetBoundsDirect(bounds_in_parent);
} else if (window_state->IsMaximized() ||
IsMaximizedOrFullscreenWindowStateType(previous_state_type)) {
window_state->SetBoundsDirectCrossFade(bounds_in_parent);
} else if (window_state->is_dragged()) {
window_state->SetBoundsDirect(bounds_in_parent);
} else {
window_state->SetBoundsDirectAnimated(bounds_in_parent);
}
}
if (window_state->IsMinimized()) {
window_state->window()->SetProperty(aura::client::kRestoreShowStateKey,
ToWindowShowState(previous_state_type));
::wm::SetWindowVisibilityAnimationType(
window_state->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
window_state->window()->Hide();
if (window_state->IsActive())
window_state->Deactivate();
} else if ((window_state->window()->TargetVisibility() ||
previous_state_type == WINDOW_STATE_TYPE_MINIMIZED) &&
!window_state->window()->layer()->visible()) {
window_state->window()->Show();
if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED &&
!window_state->IsMaximizedOrFullscreen()) {
window_state->set_unminimize_to_restore_bounds(false);
}
}
}
void DefaultState::CenterWindow(WindowState* window_state) {
if (!window_state->IsNormalOrSnapped())
return;
aura::Window* window = window_state->window();
if (window_state->IsSnapped()) {
gfx::Rect center_in_screen =
Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
gfx::Size size = window_state->HasRestoreBounds() ?
window_state->GetRestoreBoundsInScreen().size() :
window->bounds().size();
center_in_screen.ClampToCenteredSize(size);
window_state->SetRestoreBoundsInScreen(center_in_screen);
window_state->Restore();
} else {
gfx::Rect center_in_parent =
ScreenUtil::GetDisplayWorkAreaBoundsInParent(window);
center_in_parent.ClampToCenteredSize(window->bounds().size());
window_state->SetBoundsDirectAnimated(center_in_parent);
}
window_state->set_bounds_changed_by_user(true);
}
}
}