blob: e8fdd3ab7fac12f13b889d38db1438cadb4f7c4a [file] [log] [blame]
// Copyright 2019 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/home_screen/drag_window_from_shelf_controller.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/home_screen/home_screen_controller.h"
#include "ash/home_screen/home_screen_delegate.h"
#include "ash/home_screen/window_scale_animation.h"
#include "ash/public/cpp/presentation_time_recorder.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/public/cpp/window_backdrop.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/root_window_controller.h"
#include "ash/scoped_animation_disabler.h"
#include "ash/screen_util.h"
#include "ash/shelf/hotseat_widget.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/wallpaper/wallpaper_property.h"
#include "ash/wallpaper/wallpaper_view.h"
#include "ash/wallpaper/wallpaper_widget_controller.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/overview/overview_constants.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/overview/overview_utils.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_drag_indicators.h"
#include "ash/wm/splitview/split_view_utils.h"
#include "ash/wm/window_properties.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_transient_descendant_iterator.h"
#include "ash/wm/window_util.h"
#include "base/bind_helpers.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/ranges.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/hit_test.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/transform_util.h"
#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
// The minimum window scale factor when dragging a window from shelf.
constexpr float kMinimumWindowScaleDuringDragging = 0.3f;
// The ratio in display height at which point the dragged window shrinks to its
// minimum scale kMinimumWindowScaleDuringDragging.
constexpr float kMinYDisplayHeightRatio = 0.125f;
// Amount of time to wait to show overview after the user slows down or stops
// window dragging.
constexpr base::TimeDelta kShowOverviewTimeWhenDragSuspend =
base::TimeDelta::FromMilliseconds(40);
// The scroll update threshold to restart the show overview timer.
constexpr float kScrollUpdateOverviewThreshold = 2.f;
// Presentation time histogram names.
constexpr char kDragWindowFromShelfHistogram[] =
"Ash.DragWindowFromShelf.PresentationTime";
constexpr char kDragWindowFromShelfMaxLatencyHistogram[] =
"Ash.DragWindowFromShelf.PresentationTime.MaxLatency";
} // namespace
// Hide all visible windows expect the dragged windows or the window showing in
// splitview during dragging.
class DragWindowFromShelfController::WindowsHider
: public aura::WindowObserver {
public:
explicit WindowsHider(aura::Window* dragged_window)
: dragged_window_(dragged_window) {
std::vector<aura::Window*> windows =
Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
for (auto* window : windows) {
if (window == dragged_window_)
continue;
if (::wm::HasTransientAncestor(window, dragged_window_))
continue;
if (!window->IsVisible())
continue;
if (SplitViewController::Get(window)->IsWindowInSplitView(window))
continue;
hidden_windows_.push_back(window);
window->AddObserver(this);
window->SetProperty(kHideDuringWindowDragging, true);
}
window_util::MinimizeAndHideWithoutAnimation(hidden_windows_);
}
~WindowsHider() override {
for (auto* window : hidden_windows_) {
window->RemoveObserver(this);
window->ClearProperty(kHideDuringWindowDragging);
}
hidden_windows_.clear();
}
void RestoreWindowsVisibility() {
for (auto* window : hidden_windows_) {
window->RemoveObserver(this);
ScopedAnimationDisabler disabler(window);
window->Show();
window->ClearProperty(kHideDuringWindowDragging);
}
hidden_windows_.clear();
}
// Even though we explicitly minimize the windows, some (i.e. ARC apps)
// minimize asynchronously so they may not be truly minimized after |this| is
// constructed.
bool WindowsMinimized() {
return std::all_of(hidden_windows_.begin(), hidden_windows_.end(),
[](const aura::Window* w) {
return WindowState::Get(w)->IsMinimized();
});
}
// aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override {
window->RemoveObserver(this);
hidden_windows_.erase(
std::find(hidden_windows_.begin(), hidden_windows_.end(), window));
}
private:
aura::Window* dragged_window_;
std::vector<aura::Window*> hidden_windows_;
DISALLOW_COPY_AND_ASSIGN(WindowsHider);
};
// static
float DragWindowFromShelfController::GetReturnToMaximizedThreshold() {
return Shell::GetPrimaryRootWindowController()
->shelf()
->hotseat_widget()
->GetHotseatFullDragAmount();
}
DragWindowFromShelfController::DragWindowFromShelfController(
aura::Window* window,
const gfx::PointF& location_in_screen)
: window_(window) {
window_->AddObserver(this);
OnDragStarted(location_in_screen);
presentation_time_recorder_ = CreatePresentationTimeHistogramRecorder(
window_->GetHost()->compositor(), kDragWindowFromShelfHistogram,
kDragWindowFromShelfMaxLatencyHistogram);
}
DragWindowFromShelfController::~DragWindowFromShelfController() {
CancelDrag();
if (window_)
window_->RemoveObserver(this);
}
void DragWindowFromShelfController::Drag(const gfx::PointF& location_in_screen,
float scroll_x,
float scroll_y) {
// |window_| might have been destroyed during dragging.
if (!window_)
return;
if (!drag_started_)
return;
presentation_time_recorder_->RequestNext();
UpdateDraggedWindow(location_in_screen);
// Open overview if the window has been dragged far enough and the scroll
// delta has decreased to kOpenOverviewThreshold. Wait until all windows are
// minimized or they will not show up in overview.
DCHECK(windows_hider_);
OverviewController* overview_controller = Shell::Get()->overview_controller();
if (std::abs(scroll_y) <= kOpenOverviewThreshold &&
!overview_controller->InOverviewSession() &&
windows_hider_->WindowsMinimized()) {
overview_controller->StartOverview(OverviewEnterExitType::kImmediateEnter);
OnWindowDragStartedInOverview();
}
// If overview is active, update its splitview indicator during dragging if
// splitview is allowed in current configuration.
if (overview_controller->InOverviewSession()) {
const SplitViewController::SnapPosition snap_position =
GetSnapPosition(location_in_screen);
const SplitViewDragIndicators::WindowDraggingState window_dragging_state =
SplitViewDragIndicators::ComputeWindowDraggingState(
/*is_dragging=*/true,
SplitViewDragIndicators::WindowDraggingState::kFromShelf,
snap_position);
OverviewSession* overview_session = overview_controller->overview_session();
overview_session->UpdateSplitViewDragIndicatorsWindowDraggingStates(
Shell::GetPrimaryRootWindow(), window_dragging_state);
overview_session->OnWindowDragContinued(window_, location_in_screen,
window_dragging_state);
if (snap_position != SplitViewController::NONE) {
// If the dragged window is in snap preview area, make sure overview is
// visible.
ShowOverviewDuringOrAfterDrag();
} else if (std::abs(scroll_x) > kShowOverviewThreshold ||
std::abs(scroll_y) > kShowOverviewThreshold) {
// If the dragging velocity is large enough, hide overview windows.
show_overview_timer_.Stop();
HideOverviewDuringDrag();
} else if (!show_overview_timer_.IsRunning() ||
std::abs(scroll_x) > kScrollUpdateOverviewThreshold ||
std::abs(scroll_y) > kScrollUpdateOverviewThreshold) {
// Otherwise start the |show_overview_timer_| to show and update
// overview when the dragging slows down or stops. Note if the window is
// still being dragged with scroll rate more than
// kScrollUpdateOverviewThreshold, we restart the show overview timer.
show_overview_timer_.Start(
FROM_HERE, kShowOverviewTimeWhenDragSuspend, this,
&DragWindowFromShelfController::ShowOverviewDuringOrAfterDrag);
}
}
previous_location_in_screen_ = location_in_screen;
}
base::Optional<ShelfWindowDragResult> DragWindowFromShelfController::EndDrag(
const gfx::PointF& location_in_screen,
base::Optional<float> velocity_y) {
if (!drag_started_)
return base::nullopt;
UpdateDraggedWindow(location_in_screen);
drag_started_ = false;
previous_location_in_screen_ = location_in_screen;
presentation_time_recorder_.reset();
OverviewController* overview_controller = Shell::Get()->overview_controller();
SplitViewController* split_view_controller =
SplitViewController::Get(Shell::GetPrimaryRootWindow());
const bool in_overview = overview_controller->InOverviewSession();
const bool in_splitview = split_view_controller->InSplitViewMode();
const bool drop_window_in_overview =
ShouldDropWindowInOverview(location_in_screen, velocity_y);
end_snap_position_ = GetSnapPositionOnDragEnd(location_in_screen, velocity_y);
window_drag_result_ = base::nullopt;
if (ShouldGoToHomeScreen(location_in_screen, velocity_y)) {
DCHECK(!in_splitview);
if (in_overview)
overview_controller->EndOverview(OverviewEnterExitType::kFadeOutExit);
window_drag_result_ = ShelfWindowDragResult::kGoToHomeScreen;
} else if (ShouldRestoreToOriginalBounds(location_in_screen)) {
window_drag_result_ = ShelfWindowDragResult::kRestoreToOriginalBounds;
} else if (!in_overview) {
// if overview is not active during the entire drag process, scale down the
// dragged window to go to home screen.
window_drag_result_ = ShelfWindowDragResult::kGoToHomeScreen;
} else {
if (drop_window_in_overview)
window_drag_result_ = ShelfWindowDragResult::kGoToOverviewMode;
else if (end_snap_position_ != SplitViewController::NONE)
window_drag_result_ = ShelfWindowDragResult::kGoToSplitviewMode;
// For window that may drop in overview or snap in split screen, restore its
// original backdrop mode.
WindowBackdrop::Get(window_)->RestoreBackdrop();
}
WindowState::Get(window_)->DeleteDragDetails();
if (window_drag_result_.has_value()) {
UMA_HISTOGRAM_ENUMERATION(kHandleDragWindowFromShelfHistogramName,
*window_drag_result_);
}
return window_drag_result_;
}
void DragWindowFromShelfController::CancelDrag() {
if (!drag_started_)
return;
UMA_HISTOGRAM_ENUMERATION(kHandleDragWindowFromShelfHistogramName,
ShelfWindowDragResult::kDragCanceled);
drag_started_ = false;
presentation_time_recorder_.reset();
// Reset the window's transform to identity transform.
window_->SetTransform(gfx::Transform());
WindowBackdrop::Get(window_)->RestoreBackdrop();
// End overview if it was opened during dragging.
OverviewController* overview_controller = Shell::Get()->overview_controller();
if (overview_controller->InOverviewSession())
overview_controller->EndOverview(OverviewEnterExitType::kImmediateExit);
ReshowHiddenWindowsOnDragEnd();
window_drag_result_ = ShelfWindowDragResult::kDragCanceled;
// When the drag is cancelled, the window should restore to its original snap
// position.
OnDragEnded(previous_location_in_screen_,
/*should_drop_window_in_overview=*/false,
/*snap_position=*/initial_snap_position_);
WindowState::Get(window_)->DeleteDragDetails();
}
bool DragWindowFromShelfController::IsDraggedWindowAnimating() const {
return window_ && window_->layer()->GetAnimator()->is_animating();
}
void DragWindowFromShelfController::FinalizeDraggedWindow() {
if (!window_drag_result_.has_value()) {
started_in_overview_ = false;
return;
}
DCHECK(!drag_started_);
DCHECK(window_);
OnDragEnded(previous_location_in_screen_,
*window_drag_result_ == ShelfWindowDragResult::kGoToOverviewMode,
end_snap_position_);
}
void DragWindowFromShelfController::OnWindowDestroying(aura::Window* window) {
DCHECK_EQ(window_, window);
CancelDrag();
window_->RemoveObserver(this);
window_ = nullptr;
}
void DragWindowFromShelfController::AddObserver(
DragWindowFromShelfController::Observer* observer) {
observers_.AddObserver(observer);
}
void DragWindowFromShelfController::RemoveObserver(
DragWindowFromShelfController::Observer* observer) {
observers_.RemoveObserver(observer);
}
void DragWindowFromShelfController::OnDragStarted(
const gfx::PointF& location_in_screen) {
drag_started_ = true;
started_in_overview_ =
Shell::Get()->overview_controller()->InOverviewSession();
initial_location_in_screen_ = location_in_screen;
previous_location_in_screen_ = location_in_screen;
WindowState::Get(window_)->CreateDragDetails(
initial_location_in_screen_, HTCLIENT, ::wm::WINDOW_MOVE_SOURCE_TOUCH);
// Disable the backdrop on the dragged window during dragging.
WindowBackdrop::Get(window_)->DisableBackdrop();
// Hide all visible windows behind the dragged window during dragging.
windows_hider_ = std::make_unique<WindowsHider>(window_);
// Hide the home launcher until it's eligible to show it.
Shell::Get()->home_screen_controller()->OnWindowDragStarted();
// Use the same dim and blur as in overview during dragging.
RootWindowController::ForWindow(window_->GetRootWindow())
->wallpaper_widget_controller()
->SetWallpaperProperty(wallpaper_constants::kOverviewInTabletState);
// If the dragged window is one of the snapped window in splitview, it needs
// to be detached from splitview before start dragging.
SplitViewController* split_view_controller =
SplitViewController::Get(Shell::GetPrimaryRootWindow());
// Preserve initial snap position
if (split_view_controller->IsWindowInSplitView(window_)) {
initial_snap_position_ =
split_view_controller->GetPositionOfSnappedWindow(window_);
}
split_view_controller->OnWindowDragStarted(window_);
// Note SplitViewController::OnWindowDragStarted() may open overview.
if (Shell::Get()->overview_controller()->InOverviewSession())
OnWindowDragStartedInOverview();
}
void DragWindowFromShelfController::OnDragEnded(
const gfx::PointF& location_in_screen,
bool should_drop_window_in_overview,
SplitViewController::SnapPosition snap_position) {
OverviewController* overview_controller = Shell::Get()->overview_controller();
if (overview_controller->InOverviewSession()) {
// Make sure overview is visible after drag ends.
ShowOverviewDuringOrAfterDrag();
OverviewSession* overview_session = overview_controller->overview_session();
overview_session->ResetSplitViewDragIndicatorsWindowDraggingStates();
// No need to reposition overview windows if we are not dropping the dragged
// window into overview. Overview will either be exited or unchanged, and
// the extra movement from existing window will just add unnecessary
// movement which will also slow down our dragged window animation.
if (!should_drop_window_in_overview)
overview_session->SuspendReposition();
overview_session->OnWindowDragEnded(
window_, location_in_screen, should_drop_window_in_overview,
/*snap=*/snap_position != SplitViewController::NONE);
overview_session->ResumeReposition();
}
SplitViewController* split_view_controller =
SplitViewController::Get(Shell::GetPrimaryRootWindow());
if (split_view_controller->InSplitViewMode() ||
snap_position != SplitViewController::NONE) {
split_view_controller->OnWindowDragEnded(
window_, snap_position, gfx::ToRoundedPoint(location_in_screen));
}
// Scale-in-to-show home screen if home screen should be shown after drag
// ends.
Shell::Get()->home_screen_controller()->OnWindowDragEnded(/*animate=*/true);
// Clear the wallpaper dim and blur if not in overview after drag ends.
// If in overview, the dim and blur will be cleared after overview ends.
if (!overview_controller->InOverviewSession()) {
RootWindowController::ForWindow(window_->GetRootWindow())
->wallpaper_widget_controller()
->SetWallpaperProperty(wallpaper_constants::kClear);
}
DCHECK(window_drag_result_.has_value());
switch (*window_drag_result_) {
case ShelfWindowDragResult::kGoToHomeScreen:
ScaleDownWindowAfterDrag();
break;
case ShelfWindowDragResult::kRestoreToOriginalBounds:
ScaleUpToRestoreWindowAfterDrag();
break;
case ShelfWindowDragResult::kGoToOverviewMode:
case ShelfWindowDragResult::kGoToSplitviewMode:
case ShelfWindowDragResult::kDragCanceled:
// No action is needed.
break;
}
window_drag_result_.reset();
started_in_overview_ = false;
}
void DragWindowFromShelfController::UpdateDraggedWindow(
const gfx::PointF& location_in_screen) {
gfx::Rect bounds = window_->bounds();
::wm::ConvertRectToScreen(window_->parent(), &bounds);
// Calculate the window's transform based on the location.
// For scale, at |initial_location_in_screen_| or bounds.bottom(), the scale
// is 1.0, and at the |min_y| position of its bounds, it reaches to its
// minimum scale |kMinimumWindowScaleDuringDragging|. Calculate the desired
// scale based on the current y position.
const gfx::Rect display_bounds =
display::Screen::GetScreen()
->GetDisplayNearestPoint(gfx::ToRoundedPoint(location_in_screen))
.bounds();
const float min_y = display_bounds.y() +
display_bounds.height() * kMinYDisplayHeightRatio +
kMinimumWindowScaleDuringDragging * bounds.height();
float y_full =
std::min(initial_location_in_screen_.y(), (float)bounds.bottom()) - min_y;
float y_diff = location_in_screen.y() - min_y;
float scale = (1.0f - kMinimumWindowScaleDuringDragging) * y_diff / y_full +
kMinimumWindowScaleDuringDragging;
scale = base::ClampToRange(scale, /*min=*/kMinimumWindowScaleDuringDragging,
/*max=*/1.f);
// Calculate the desired translation so that the dragged window stays under
// the finger during the dragging.
// Since vertical drag doesn't start until after passing the top of the shelf,
// the y calculations should be relative to the window bounds instead of
// |initial_location_in_screen| (which is on the shelf)
gfx::Transform transform;
transform.Translate(
(location_in_screen.x() - bounds.x()) -
(initial_location_in_screen_.x() - bounds.x()) * scale,
(location_in_screen.y() - bounds.y()) - bounds.height() * scale);
transform.Scale(scale, scale);
// The dragged window cannot exceed the top or bottom of the display. So
// calculate the expected transformed bounds and then adjust the transform if
// needed.
gfx::RectF transformed_bounds(window_->bounds());
gfx::Transform new_tranform = TransformAboutPivot(
gfx::ToRoundedPoint(transformed_bounds.origin()), transform);
new_tranform.TransformRect(&transformed_bounds);
::wm::TranslateRectToScreen(window_->parent(), &transformed_bounds);
if (transformed_bounds.y() < display_bounds.y()) {
transform.Translate(0,
(display_bounds.y() - transformed_bounds.y()) / scale);
} else if (transformed_bounds.bottom() > bounds.bottom()) {
DCHECK_EQ(1.f, scale);
transform.Translate(
0, (bounds.bottom() - transformed_bounds.bottom()) / scale);
}
SetTransform(window_, transform);
}
SplitViewController::SnapPosition
DragWindowFromShelfController::GetSnapPosition(
const gfx::PointF& location_in_screen) const {
// if |location_in_screen| is close to the bottom of the screen and is
// inside of GetReturnToMaximizedThreshold() threshold, we should not try to
// snap the window.
if (ShouldRestoreToOriginalBounds(location_in_screen))
return SplitViewController::NONE;
aura::Window* root_window = Shell::GetPrimaryRootWindow();
SplitViewController::SnapPosition snap_position = ::ash::GetSnapPosition(
root_window, window_, gfx::ToRoundedPoint(location_in_screen),
gfx::ToRoundedPoint(initial_location_in_screen_),
/*snap_distance_from_edge=*/kDistanceFromEdge,
/*minimum_drag_distance=*/kMinDragDistance,
/*horizontal_edge_inset=*/kScreenEdgeInsetForSnap,
/*vertical_edge_inset=*/kScreenEdgeInsetForSnap);
// For portrait mode, since the drag starts from the bottom of the screen,
// we should only allow the window to snap to the top of the screen.
const bool is_landscape = IsCurrentScreenOrientationLandscape();
const bool is_primary = IsCurrentScreenOrientationPrimary();
if (!is_landscape &&
((is_primary && snap_position == SplitViewController::RIGHT) ||
(!is_primary && snap_position == SplitViewController::LEFT))) {
snap_position = SplitViewController::NONE;
}
return snap_position;
}
bool DragWindowFromShelfController::ShouldRestoreToOriginalBounds(
const gfx::PointF& location_in_screen) const {
const gfx::Rect display_bounds =
display::Screen::GetScreen()
->GetDisplayNearestPoint(gfx::ToRoundedPoint(location_in_screen))
.bounds();
gfx::RectF transformed_window_bounds =
window_util::GetTransformedBounds(window_, /*top_inset=*/0);
return transformed_window_bounds.bottom() >
display_bounds.bottom() - GetReturnToMaximizedThreshold();
}
bool DragWindowFromShelfController::ShouldGoToHomeScreen(
const gfx::PointF& location_in_screen,
base::Optional<float> velocity_y) const {
// If the drag ends below the shelf, do not go to home screen (theoretically
// it may happen in kExtended hotseat case when drag can start and end below
// the shelf).
if (location_in_screen.y() >=
Shelf::ForWindow(window_)->GetIdealBoundsForWorkAreaCalculation().y()) {
return false;
}
// Do not go home if we're in split screen.
if (SplitViewController::Get(Shell::GetPrimaryRootWindow())
->InSplitViewMode()) {
return false;
}
// If overview is invisible when the drag ends, no matter what the velocity
// is, we should go to home screen.
if (Shell::Get()->overview_controller()->InOverviewSession() &&
!show_overview_windows_) {
return true;
}
// Otherwise go home if the velocity is large enough.
return velocity_y.has_value() && *velocity_y < 0 &&
std::abs(*velocity_y) >= kVelocityToHomeScreenThreshold;
}
SplitViewController::SnapPosition
DragWindowFromShelfController::GetSnapPositionOnDragEnd(
const gfx::PointF& location_in_screen,
base::Optional<float> velocity_y) const {
if (!Shell::Get()->overview_controller()->InOverviewSession() ||
ShouldGoToHomeScreen(location_in_screen, velocity_y)) {
return SplitViewController::NONE;
}
// When dragging ends but restore to original bounds, we should restore
// window's initial snap position
if (ShouldRestoreToOriginalBounds(location_in_screen))
return initial_snap_position_;
return GetSnapPosition(location_in_screen);
}
bool DragWindowFromShelfController::ShouldDropWindowInOverview(
const gfx::PointF& location_in_screen,
base::Optional<float> velocity_y) const {
if (!Shell::Get()->overview_controller()->InOverviewSession())
return false;
if (ShouldGoToHomeScreen(location_in_screen, velocity_y))
return false;
const bool in_splitview =
SplitViewController::Get(Shell::GetPrimaryRootWindow())
->InSplitViewMode();
if (!in_splitview && ShouldRestoreToOriginalBounds(location_in_screen)) {
return false;
}
if (in_splitview) {
if (velocity_y.has_value() && *velocity_y < 0 &&
std::abs(*velocity_y) >= kVelocityToOverviewThreshold) {
return true;
}
if (ShouldRestoreToOriginalBounds(location_in_screen))
return false;
}
return GetSnapPositionOnDragEnd(location_in_screen, velocity_y) ==
SplitViewController::NONE;
}
void DragWindowFromShelfController::ReshowHiddenWindowsOnDragEnd() {
windows_hider_->RestoreWindowsVisibility();
}
void DragWindowFromShelfController::ShowOverviewDuringOrAfterDrag() {
show_overview_timer_.Stop();
OverviewController* overview_controller = Shell::Get()->overview_controller();
if (!overview_controller->InOverviewSession())
return;
show_overview_windows_ = true;
overview_controller->overview_session()->SetVisibleDuringWindowDragging(
/*visible=*/true, /*animate=*/true);
for (Observer& observer : observers_)
observer.OnOverviewVisibilityChanged(true);
}
void DragWindowFromShelfController::HideOverviewDuringDrag() {
show_overview_windows_ = false;
OverviewController* overview_controller = Shell::Get()->overview_controller();
if (!overview_controller->InOverviewSession())
return;
overview_controller->overview_session()->SetVisibleDuringWindowDragging(
/*visible=*/false,
/*animate=*/false);
for (Observer& observer : observers_)
observer.OnOverviewVisibilityChanged(false);
}
void DragWindowFromShelfController::ScaleDownWindowAfterDrag() {
// Notify home screen controller that the home screen is about to be shown, so
// home screen and shelf start updating their state as the window is
// minimizing.
Shell::Get()
->home_screen_controller()
->delegate()
->OnHomeLauncherPositionChanged(
/*percent_shown=*/100,
display::Screen::GetScreen()->GetPrimaryDisplay().id());
// Do the scale-down transform for the entire transient tree.
for (auto* window : GetTransientTreeIterator(window_)) {
// self-destructed when window transform animation is done.
new WindowScaleAnimation(
window, WindowScaleAnimation::WindowScaleType::kScaleDownToShelf,
window == window_
? base::BindOnce(
&DragWindowFromShelfController::OnWindowScaledDownAfterDrag,
weak_ptr_factory_.GetWeakPtr())
: base::NullCallback());
}
}
void DragWindowFromShelfController::OnWindowScaledDownAfterDrag() {
HomeScreenController* home_screen_controller =
Shell::Get()->home_screen_controller();
if (!home_screen_controller || !home_screen_controller->delegate())
return;
home_screen_controller->delegate()->OnHomeLauncherAnimationComplete(
/*shown=*/true, display::Screen::GetScreen()->GetPrimaryDisplay().id());
}
void DragWindowFromShelfController::ScaleUpToRestoreWindowAfterDrag() {
// Do the scale up transform for the entire transient tee.
for (auto* window : GetTransientTreeIterator(window_)) {
new WindowScaleAnimation(
window, WindowScaleAnimation::WindowScaleType::kScaleUpToRestore,
base::BindOnce(
&DragWindowFromShelfController::OnWindowRestoredToOrignalBounds,
weak_ptr_factory_.GetWeakPtr(),
/*should_end_overview=*/!started_in_overview_));
}
}
void DragWindowFromShelfController::OnWindowRestoredToOrignalBounds(
bool end_overview) {
base::AutoReset<bool> auto_reset(&during_window_restoration_callback_, true);
if (end_overview) {
Shell::Get()->overview_controller()->EndOverview(
OverviewEnterExitType::kImmediateExit);
}
ReshowHiddenWindowsOnDragEnd();
}
void DragWindowFromShelfController::OnWindowDragStartedInOverview() {
OverviewSession* overview_session =
Shell::Get()->overview_controller()->overview_session();
DCHECK(overview_session);
overview_session->OnWindowDragStarted(window_, /*animate=*/false);
if (ShouldAllowSplitView())
overview_session->SetSplitViewDragIndicatorsDraggedWindow(window_);
// Hide overview windows first and fade in the windows after delaying
// kShowOverviewTimeWhenDragSuspend.
HideOverviewDuringDrag();
}
} // namespace ash