123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- // Copyright 2017 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/client_controlled_state.h"
- #include "ash/public/cpp/shell_window_ids.h"
- #include "ash/public/cpp/window_animation_types.h"
- #include "ash/root_window_controller.h"
- #include "ash/screen_util.h"
- #include "ash/shell.h"
- #include "ash/wm/pip/pip_positioner.h"
- #include "ash/wm/screen_pinning_controller.h"
- #include "ash/wm/splitview/split_view_controller.h"
- #include "ash/wm/tablet_mode/tablet_mode_controller.h"
- #include "ash/wm/window_positioning_utils.h"
- #include "ash/wm/window_state.h"
- #include "ash/wm/window_state_delegate.h"
- #include "ash/wm/window_state_util.h"
- #include "ash/wm/wm_event.h"
- #include "chromeos/ui/base/window_state_type.h"
- #include "ui/aura/client/aura_constants.h"
- #include "ui/aura/window.h"
- #include "ui/aura/window_delegate.h"
- #include "ui/display/display.h"
- #include "ui/display/screen.h"
- #include "ui/wm/core/window_util.h"
- namespace ash {
- namespace {
- using ::chromeos::WindowStateType;
- // |kMinimumOnScreenArea + 1| is used to avoid adjusting loop.
- constexpr int kClientControlledWindowMinimumOnScreenArea =
- kMinimumOnScreenArea + 1;
- } // namespace
- // static
- void ClientControlledState::AdjustBoundsForMinimumWindowVisibility(
- const gfx::Rect& display_bounds,
- gfx::Rect* bounds) {
- AdjustBoundsToEnsureWindowVisibility(
- display_bounds, kClientControlledWindowMinimumOnScreenArea,
- kClientControlledWindowMinimumOnScreenArea, bounds);
- }
- ClientControlledState::ClientControlledState(std::unique_ptr<Delegate> delegate)
- : BaseState(WindowStateType::kDefault), delegate_(std::move(delegate)) {}
- ClientControlledState::~ClientControlledState() = default;
- void ClientControlledState::ResetDelegate() {
- delegate_.reset();
- }
- void ClientControlledState::HandleTransitionEvents(WindowState* window_state,
- const WMEvent* event) {
- if (!delegate_)
- return;
- const WMEventType event_type = event->type();
- bool pin_transition = window_state->IsTrustedPinned() ||
- window_state->IsPinned() || event->IsPinEvent();
- // Pinned State transition is handled on server side.
- if (pin_transition) {
- // Only one window can be pinned.
- if (event->IsPinEvent() &&
- Shell::Get()->screen_pinning_controller()->IsPinned()) {
- return;
- }
- WindowStateType next_state_type =
- GetStateForTransitionEvent(window_state, event);
- delegate_->HandleWindowStateRequest(window_state, next_state_type);
- WindowStateType old_state_type = state_type_;
- bool was_pinned = window_state->IsPinned();
- bool was_trusted_pinned = window_state->IsTrustedPinned();
- set_next_bounds_change_animation_type(
- WindowState::BoundsChangeAnimationType::kCrossFade);
- EnterNextState(window_state, next_state_type);
- VLOG(1) << "Processing Pinned Transition: event=" << event_type
- << ", state=" << old_state_type << "=>" << next_state_type
- << ", pinned=" << was_pinned << "=>" << window_state->IsPinned()
- << ", trusted pinned=" << was_trusted_pinned << "=>"
- << window_state->IsTrustedPinned();
- return;
- }
- switch (event_type) {
- case WM_EVENT_NORMAL:
- case WM_EVENT_MAXIMIZE:
- case WM_EVENT_MINIMIZE:
- case WM_EVENT_FULLSCREEN:
- case WM_EVENT_SNAP_PRIMARY:
- case WM_EVENT_SNAP_SECONDARY: {
- WindowStateType next_state =
- GetResolvedNextWindowStateType(window_state, event);
- UpdateWindowForTransitionEvents(window_state, next_state, event_type);
- break;
- }
- case WM_EVENT_FLOAT:
- // TODO(crbug.com/1346061): Implement this.
- break;
- case WM_EVENT_RESTORE:
- UpdateWindowForTransitionEvents(
- window_state, window_state->GetRestoreWindowState(), event_type);
- break;
- case WM_EVENT_SHOW_INACTIVE:
- NOTREACHED();
- break;
- default:
- NOTREACHED() << "Unknown event :" << event->type();
- }
- }
- void ClientControlledState::AttachState(
- WindowState* window_state,
- WindowState::State* state_in_previous_mode) {}
- void ClientControlledState::DetachState(WindowState* window_state) {}
- #if DCHECK_IS_ON()
- void ClientControlledState::CheckMaximizableCondition(
- const WindowState* window_state) const {
- // A client decides when the window should be maximizable.
- }
- #endif // DCHECK_IS_ON()
- void ClientControlledState::HandleWorkspaceEvents(WindowState* window_state,
- const WMEvent* event) {
- if (!delegate_)
- return;
- // Client is responsible for adjusting bounds after workspace bounds change.
- if (window_state->IsSnapped()) {
- gfx::Rect bounds = GetSnappedWindowBoundsInParent(
- window_state->window(), window_state->GetStateType());
- // Then ask delegate to set the desired bounds for the snap state.
- delegate_->HandleBoundsRequest(window_state, window_state->GetStateType(),
- bounds, window_state->GetDisplay().id());
- } else if (event->type() == WM_EVENT_DISPLAY_BOUNDS_CHANGED) {
- // Explicitly handle the primary change because it can change the display id
- // with no bounds change.
- if (event->AsDisplayMetricsChangedWMEvent()->primary_changed()) {
- const gfx::Rect bounds = window_state->window()->bounds();
- delegate_->HandleBoundsRequest(window_state, window_state->GetStateType(),
- bounds, window_state->GetDisplay().id());
- }
- } else if (event->type() == WM_EVENT_ADDED_TO_WORKSPACE) {
- aura::Window* window = window_state->window();
- gfx::Rect bounds = window->bounds();
- AdjustBoundsForMinimumWindowVisibility(window->GetRootWindow()->bounds(),
- &bounds);
- if (window->bounds() != bounds)
- window_state->SetBoundsConstrained(bounds);
- }
- }
- void ClientControlledState::HandleCompoundEvents(WindowState* window_state,
- const WMEvent* event) {
- if (!delegate_)
- return;
- switch (event->type()) {
- case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
- if (window_state->IsFullscreen()) {
- const WMEvent event(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();
- }
- break;
- case WM_EVENT_TOGGLE_MAXIMIZE:
- if (window_state->IsFullscreen()) {
- const WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN);
- window_state->OnWMEvent(&event);
- } else if (window_state->IsMaximized()) {
- window_state->Restore();
- } else if (window_state->CanMaximize()) {
- window_state->Maximize();
- }
- break;
- case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE:
- // TODO(oshima): Implement this.
- break;
- case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
- // TODO(oshima): Implement this.
- break;
- case WM_EVENT_TOGGLE_FULLSCREEN:
- ToggleFullScreen(window_state, window_state->delegate());
- break;
- case WM_EVENT_CYCLE_SNAP_PRIMARY:
- case WM_EVENT_CYCLE_SNAP_SECONDARY:
- CycleSnap(window_state, event->type());
- break;
- default:
- NOTREACHED() << "Invalid event :" << event->type();
- break;
- }
- }
- void ClientControlledState::HandleBoundsEvents(WindowState* window_state,
- const WMEvent* event) {
- if (!delegate_)
- return;
- switch (event->type()) {
- case WM_EVENT_SET_BOUNDS: {
- const auto* set_bounds_event =
- static_cast<const SetBoundsWMEvent*>(event);
- const gfx::Rect& bounds = set_bounds_event->requested_bounds();
- if (set_bounds_locally_) {
- switch (next_bounds_change_animation_type_) {
- case WindowState::BoundsChangeAnimationType::kNone:
- window_state->SetBoundsDirect(bounds);
- break;
- case WindowState::BoundsChangeAnimationType::kCrossFade:
- window_state->SetBoundsDirectCrossFade(bounds);
- break;
- case WindowState::BoundsChangeAnimationType::kAnimate:
- window_state->SetBoundsDirectAnimated(
- bounds, bounds_change_animation_duration_);
- break;
- case WindowState::BoundsChangeAnimationType::kAnimateZero:
- NOTREACHED();
- break;
- }
- next_bounds_change_animation_type_ =
- WindowState::BoundsChangeAnimationType::kNone;
- } else if (!window_state->IsPinned()) {
- // TODO(oshima): Define behavior for pinned app.
- bounds_change_animation_duration_ = set_bounds_event->duration();
- int64_t display_id = set_bounds_event->display_id();
- auto* window = window_state->window();
- if (display_id == display::kInvalidDisplayId) {
- display_id = display::Screen::GetScreen()
- ->GetDisplayNearestWindow(window)
- .id();
- }
- #if DCHECK_IS_ON()
- gfx::Rect bounds_in_display(bounds);
- // The coordinates of the WindowState's parent must be same as display
- // coordinates. The following code is only to verify this condition.
- const aura::Window* root = window->GetRootWindow();
- aura::Window::ConvertRectToTarget(window->parent(), root,
- &bounds_in_display);
- DCHECK_EQ(bounds_in_display.x(), bounds.x());
- DCHECK_EQ(bounds_in_display.y(), bounds.y());
- #endif
- delegate_->HandleBoundsRequest(
- window_state, window_state->GetStateType(), bounds, display_id);
- }
- break;
- }
- case WM_EVENT_CENTER:
- CenterWindow(window_state);
- break;
- default:
- NOTREACHED() << "Unknown event:" << event->type();
- }
- }
- void ClientControlledState::OnWindowDestroying(WindowState* window_state) {
- ResetDelegate();
- }
- bool ClientControlledState::EnterNextState(WindowState* window_state,
- WindowStateType next_state_type) {
- // Do nothing if we're already in the same state, or delegate has already
- // been deleted.
- if (state_type_ == next_state_type || !delegate_)
- return false;
- WindowStateType previous_state_type = state_type_;
- state_type_ = next_state_type;
- window_state->UpdateWindowPropertiesFromStateType();
- window_state->NotifyPreStateTypeChange(previous_state_type);
- // Don't update the window if the window is detached from parent.
- // This can happen during dragging.
- // TODO(oshima): This was added for DOCKED windows. Investigate if
- // we still need this.
- if (window_state->window()->parent())
- UpdateMinimizedState(window_state, previous_state_type);
- window_state->NotifyPostStateTypeChange(previous_state_type);
- if (IsPinnedWindowStateType(next_state_type) ||
- IsPinnedWindowStateType(previous_state_type)) {
- Shell::Get()->screen_pinning_controller()->SetPinnedWindow(
- window_state->window());
- }
- return true;
- }
- WindowStateType ClientControlledState::GetResolvedNextWindowStateType(
- WindowState* window_state,
- const WMEvent* event) {
- DCHECK(event->IsTransitionEvent());
- const WindowStateType next = GetStateForTransitionEvent(window_state, event);
- if (Shell::Get()->tablet_mode_controller()->InTabletMode() &&
- next == WindowStateType::kNormal && window_state->CanMaximize())
- return WindowStateType::kMaximized;
- return next;
- }
- void ClientControlledState::UpdateWindowForTransitionEvents(
- WindowState* window_state,
- chromeos::WindowStateType next_state_type,
- WMEventType event_type) {
- aura::Window* window = window_state->window();
- if (next_state_type == WindowStateType::kPrimarySnapped ||
- next_state_type == WindowStateType::kSecondarySnapped) {
- if (window_state->CanSnap()) {
- HandleWindowSnapping(window_state,
- next_state_type == WindowStateType::kPrimarySnapped
- ? WM_EVENT_SNAP_PRIMARY
- : WM_EVENT_SNAP_SECONDARY);
- if (event_type == WM_EVENT_RESTORE) {
- window_state->set_snap_action_source(
- WindowSnapActionSource::kSnapByWindowStateRestore);
- }
- window_state->RecordAndResetWindowSnapActionSource(
- window_state->GetStateType(), next_state_type);
- // Get the desired window bounds for the snap state.
- gfx::Rect bounds =
- GetSnappedWindowBoundsInParent(window, next_state_type);
- // We don't want Unminimize() to restore the pre-snapped state during the
- // transition. See crbug.com/1031313 for why we need this.
- // kRestoreShowStateKey property will be updated properly after the window
- // is snapped correctly.
- if (window_state->IsMinimized())
- window->ClearProperty(aura::client::kRestoreShowStateKey);
- window_state->UpdateWindowPropertiesFromStateType();
- VLOG(1) << "Processing State Transtion: event=" << event_type
- << ", state=" << state_type_
- << ", next_state=" << next_state_type;
- // Then ask delegate to set the desired bounds for the snap state.
- delegate_->HandleBoundsRequest(window_state, next_state_type, bounds,
- window_state->GetDisplay().id());
- }
- } else {
- // Clients handle a window state change asynchronously. So in the case
- // that the window is in a transitional state (already snapped but not
- // applied to its window state yet), we here skip to pass WM_EVENT.
- if (SplitViewController::Get(window)->IsWindowInTransitionalState(window))
- return;
- // Reset window state.
- window_state->UpdateWindowPropertiesFromStateType();
- VLOG(1) << "Processing State Transtion: event=" << event_type
- << ", state=" << state_type_ << ", next_state=" << next_state_type;
- // Then ask delegate to handle the window state change.
- delegate_->HandleWindowStateRequest(window_state, next_state_type);
- }
- }
- } // namespace ash
|