blob: becb851762322dd6c07fbfffe323ec5d1215d4e2 [file] [log] [blame]
// Copyright 2018 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/app_list/home_launcher_gesture_handler.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/app_list/model/app_list_view_state.h"
#include "ash/root_window_controller.h"
#include "ash/scoped_animation_disabler.h"
#include "ash/screen_util.h"
#include "ash/shell.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_divider.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_transient_descendant_iterator.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace/backdrop_controller.h"
#include "ash/wm/workspace/workspace_layout_manager.h"
#include "ash/wm/workspace_controller.h"
#include "base/metrics/user_metrics.h"
#include "base/numerics/ranges.h"
#include "ui/aura/client/window_types.h"
#include "ui/aura/null_window_targeter.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/transient_window_manager.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
// The animation speed at which the window moves when the gesture is released.
constexpr base::TimeDelta kAnimationDurationMs =
base::TimeDelta::FromMilliseconds(250);
// The animation speed at which the window moves when a window is acitvated from
// the shelf, or deacitvated via home launcher button minimize.
constexpr base::TimeDelta kActivationChangedAnimationDurationMs =
base::TimeDelta::FromMilliseconds(350);
// The velocity the app list or shelf must be dragged in order to transition to
// the next state regardless of where the gesture ends, measured in DIPs/event.
constexpr int kScrollVelocityThreshold = 6;
// The width of the target of screen bounds will be the work area width times
// this ratio.
constexpr float kWidthRatio = 0.8f;
bool IsTabletMode() {
return Shell::Get()
->tablet_mode_controller()
->IsTabletModeWindowManagerEnabled();
}
// Checks if |window| can be hidden or shown with a gesture.
bool CanProcessWindow(aura::Window* window,
HomeLauncherGestureHandler::Mode mode) {
if (!window)
return false;
if (!window->IsVisible() &&
mode == HomeLauncherGestureHandler::Mode::kSlideUpToShow) {
return false;
}
if (window->IsVisible() &&
mode == HomeLauncherGestureHandler::Mode::kSlideDownToHide) {
return false;
}
if (!IsTabletMode())
return false;
if (window->type() == aura::client::WINDOW_TYPE_POPUP)
return false;
// Do not process if |window| is not the root of a transient tree.
if (::wm::GetTransientParent(window))
return false;
return true;
}
// Find the transform that will convert |src| to |dst|.
gfx::Transform CalculateTransform(const gfx::RectF& src,
const gfx::RectF& dst) {
return gfx::Transform(dst.width() / src.width(), 0, 0,
dst.height() / src.height(), dst.x() - src.x(),
dst.y() - src.y());
}
// Get the target offscreen workspace bounds.
gfx::RectF GetOffscreenWorkspaceBounds(const gfx::RectF& work_area) {
gfx::RectF new_work_area;
new_work_area.set_x(((1.f - kWidthRatio) / 2.f) * work_area.width() +
work_area.x());
new_work_area.set_width(kWidthRatio * work_area.width());
new_work_area.set_height(kWidthRatio * work_area.height());
new_work_area.set_y(work_area.y() - work_area.height());
return new_work_area;
}
// Get the target bounds of a window. It should maintain the same ratios
// relative the work area.
gfx::RectF GetOffscreenWindowBounds(aura::Window* window,
const gfx::RectF& src_work_area,
const gfx::RectF& dst_work_area) {
gfx::RectF bounds = gfx::RectF(window->GetTargetBounds());
float ratio = dst_work_area.width() / src_work_area.width();
gfx::RectF dst_bounds;
dst_bounds.set_x(bounds.x() * ratio + dst_work_area.x());
dst_bounds.set_y(bounds.y() * ratio + dst_work_area.y());
dst_bounds.set_width(bounds.width() * ratio);
dst_bounds.set_height(bounds.height() * ratio);
return dst_bounds;
}
// Given a |location_in_screen|, find out where it lies as a ratio in the
// work area, where the top of the work area is 0.f and the bottom is 1.f.
double GetHeightInWorkAreaAsRatio(const gfx::Point& location_in_screen,
const gfx::Rect& work_area) {
int clamped_y = base::ClampToRange(location_in_screen.y(), work_area.y(),
work_area.bottom());
double ratio =
static_cast<double>(clamped_y) / static_cast<double>(work_area.height());
return 1.0 - ratio;
}
bool IsLastEventInTopHalf(const gfx::Point& location_in_screen,
const gfx::Rect& work_area) {
return GetHeightInWorkAreaAsRatio(location_in_screen, work_area) > 0.5;
}
// Returns the window of the widget which contains the workspace backdrop. May
// be nullptr if the backdrop is not shown.
aura::Window* GetBackdropWindow(aura::Window* window) {
WorkspaceLayoutManager* layout_manager =
RootWindowController::ForWindow(window->GetRootWindow())
->workspace_controller()
->layout_manager();
return layout_manager
? layout_manager->backdrop_controller()->backdrop_window()
: nullptr;
}
// Returns the window of the widget of the split view divider. May be nullptr if
// split view is not active.
aura::Window* GetDividerWindow() {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
if (!split_view_controller->IsSplitViewModeActive())
return nullptr;
return split_view_controller->split_view_divider()
->divider_widget()
->GetNativeWindow();
}
} // namespace
// Class which allows us to make modifications to a window, and removes those
// modifications on destruction.
// TODO(sammiequon): Move to separate file and add test for
// ComputeWindowValues.
class HomeLauncherGestureHandler::ScopedWindowModifier
: public aura::WindowObserver {
public:
explicit ScopedWindowModifier(aura::Window* window) : window_(window) {
DCHECK(window_);
original_targeter_ =
window_->SetEventTargeter(std::make_unique<aura::NullWindowTargeter>());
}
~ScopedWindowModifier() override {
for (const auto& descendant : transient_descendants_values_)
descendant.first->RemoveObserver(this);
ResetOpacityAndTransform();
window_->SetEventTargeter(std::move(original_targeter_));
}
bool IsAnimating() const {
if (window_->layer()->GetAnimator()->is_animating())
return true;
for (const auto& descendant : transient_descendants_values_) {
if (descendant.first->layer()->GetAnimator()->is_animating())
return true;
}
return false;
}
void StopAnimating() {
window_->layer()->GetAnimator()->StopAnimating();
for (const auto& descendant : transient_descendants_values_)
descendant.first->layer()->GetAnimator()->StopAnimating();
}
void ResetOpacityAndTransform() {
window_->SetTransform(window_values_.initial_transform);
window_->layer()->SetOpacity(window_values_.initial_opacity);
for (const auto& descendant : transient_descendants_values_) {
descendant.first->SetTransform(descendant.second.initial_transform);
descendant.first->layer()->SetOpacity(descendant.second.initial_opacity);
}
}
// Calculates the values for |window_| and its transient descendants.
void ComputeWindowValues(const gfx::RectF& work_area,
const gfx::RectF& target_work_area) {
transient_descendants_values_.clear();
for (auto* window : wm::GetTransientTreeIterator(window_)) {
WindowValues values;
values.initial_opacity = window->layer()->opacity();
values.initial_transform = window->transform();
values.target_opacity = 0.f;
values.target_transform = CalculateTransform(
gfx::RectF(window->GetTargetBounds()),
GetOffscreenWindowBounds(window, work_area, target_work_area));
if (window == window_) {
window_values_ = values;
continue;
}
window->AddObserver(this);
transient_descendants_values_[window] = values;
}
}
// aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override {
auto it = transient_descendants_values_.find(window);
DCHECK(it != transient_descendants_values_.end());
window->RemoveObserver(this);
transient_descendants_values_.erase(it);
}
aura::Window* window() { return window_; }
WindowValues window_values() const { return window_values_; }
const std::map<aura::Window*, WindowValues>& transient_descendants_values()
const {
return transient_descendants_values_;
}
private:
aura::Window* window_;
// Original and target transform and opacity of |window_|.
WindowValues window_values_;
// Tracks the transient descendants of |window_| and their initial and
// target opacities and transforms.
std::map<aura::Window*, WindowValues> transient_descendants_values_;
std::unique_ptr<aura::WindowTargeter> original_targeter_;
DISALLOW_COPY_AND_ASSIGN(ScopedWindowModifier);
};
HomeLauncherGestureHandler::HomeLauncherGestureHandler(
AppListControllerImpl* app_list_controller)
: app_list_controller_(app_list_controller) {
tablet_mode_observer_.Add(Shell::Get()->tablet_mode_controller());
}
HomeLauncherGestureHandler::~HomeLauncherGestureHandler() {
StopObservingImplicitAnimations();
}
bool HomeLauncherGestureHandler::OnPressEvent(Mode mode,
const gfx::Point& location) {
// Do not start a new session if a window is currently being processed.
if (!IsIdle())
return false;
display_ = display::Screen::GetScreen()->GetDisplayNearestPoint(location);
if (!display_.is_valid())
return false;
if (!SetUpWindows(mode, /*window=*/nullptr))
return false;
mode_ = mode;
last_event_location_ = base::make_optional(location);
if (mode != Mode::kNone) {
app_list_controller_->NotifyHomeLauncherTargetPositionChanged(
mode == Mode::kSlideUpToShow /*showing*/, display_.id());
}
UpdateWindows(0.0, /*animate=*/false);
return true;
}
bool HomeLauncherGestureHandler::OnScrollEvent(const gfx::Point& location,
float scroll_y) {
if (IsAnimating())
return false;
if (!IsDragInProgress())
return false;
last_event_location_ = base::make_optional(location);
last_scroll_y_ = scroll_y;
DCHECK(display_.is_valid());
UpdateWindows(GetHeightInWorkAreaAsRatio(location, display_.work_area()),
/*animate=*/false);
return true;
}
bool HomeLauncherGestureHandler::OnReleaseEvent(const gfx::Point& location) {
if (IsAnimating())
return false;
if (!IsDragInProgress()) {
if (GetWindow1()) {
// |window1_| may not be nullptr when this release event is triggered
// by opening |window1_| with modal dialog in OnPressEvent(). In that
// case, just leave the |window1_| in show state and stop tracking.
AnimateToFinalState();
RemoveObserversAndStopTracking();
return true;
}
return false;
}
last_event_location_ = base::make_optional(location);
AnimateToFinalState();
return true;
}
void HomeLauncherGestureHandler::Cancel() {
if (!IsDragInProgress())
return;
AnimateToFinalState();
return;
}
bool HomeLauncherGestureHandler::ShowHomeLauncher(
const display::Display& display) {
if (!IsIdle())
return false;
if (!display.is_valid())
return false;
if (!SetUpWindows(Mode::kSlideUpToShow, /*window=*/nullptr))
return false;
display_ = display;
mode_ = Mode::kSlideUpToShow;
UpdateWindows(0.0, /*animate=*/false);
AnimateToFinalState();
return true;
}
bool HomeLauncherGestureHandler::HideHomeLauncherForWindow(
const display::Display& display,
aura::Window* window) {
if (!IsIdle())
return false;
if (!display.is_valid())
return false;
if (!SetUpWindows(Mode::kSlideDownToHide, window))
return false;
display_ = display;
mode_ = Mode::kSlideDownToHide;
UpdateWindows(1.0, /*animate=*/false);
AnimateToFinalState();
return true;
}
aura::Window* HomeLauncherGestureHandler::GetWindow1() {
if (!window1_)
return nullptr;
return window1_->window();
}
aura::Window* HomeLauncherGestureHandler::GetWindow2() {
if (!window2_)
return nullptr;
return window2_->window();
}
void HomeLauncherGestureHandler::OnWindowDestroying(aura::Window* window) {
if (window1_ && window == GetWindow1()) {
for (auto* hidden_window : hidden_windows_)
hidden_window->Show();
RemoveObserversAndStopTracking();
return;
}
if (window2_ && window == GetWindow2()) {
DCHECK(window1_);
window->RemoveObserver(this);
window2_.reset();
return;
}
DCHECK(base::ContainsValue(hidden_windows_, window));
window->RemoveObserver(this);
hidden_windows_.erase(
std::find(hidden_windows_.begin(), hidden_windows_.end(), window));
}
void HomeLauncherGestureHandler::OnTabletModeEnded() {
if (IsIdle())
return;
// When leaving tablet mode advance to the end of the in progress scroll
// session or animation.
StopObservingImplicitAnimations();
if (window1_)
window1_->StopAnimating();
if (window2_)
window2_->StopAnimating();
UpdateWindows(IsFinalStateShow() ? 1.0 : 0.0, /*animate=*/false);
OnImplicitAnimationsCompleted();
}
void HomeLauncherGestureHandler::OnImplicitAnimationsCompleted() {
float app_list_opacity = 1.f;
const bool is_final_state_show = IsFinalStateShow();
app_list_controller_->NotifyHomeLauncherAnimationComplete(
is_final_state_show /*shown*/, display_.id());
if (Shell::Get()->overview_controller()->IsSelecting()) {
if (overview_active_on_gesture_start_ && is_final_state_show) {
// Exit overview if event is released on the top half. This will also
// end splitview if it is active as SplitViewController observes
// overview mode ends.
Shell::Get()->overview_controller()->ToggleOverview(
OverviewSession::EnterExitOverviewType::kSwipeFromShelf);
} else {
app_list_opacity = 0.f;
}
}
// Return the app list to its original opacity and transform without
// animation.
DCHECK(display_.is_valid());
app_list_controller_->presenter()->UpdateYPositionAndOpacityForHomeLauncher(
display_.work_area().y(), app_list_opacity, base::NullCallback());
if (!window1_) {
RemoveObserversAndStopTracking();
return;
}
// Explicitly exit split view if two windows are snapped.
if (is_final_state_show && Shell::Get()->split_view_controller()->state() ==
SplitViewController::BOTH_SNAPPED) {
Shell::Get()->split_view_controller()->EndSplitView();
}
window1_->ResetOpacityAndTransform();
if (window2_)
window2_->ResetOpacityAndTransform();
if (is_final_state_show) {
wm::HideAndMinimizeWithoutAnimation(GetWindow1());
if (window2_)
wm::HideAndMinimizeWithoutAnimation(GetWindow2());
// Minimize the hidden windows so they can be used normally with alt+tab
// and overview. Minimize in reverse order to preserve mru ordering.
std::reverse(hidden_windows_.begin(), hidden_windows_.end());
for (auto* window : hidden_windows_)
wm::HideAndMinimizeWithoutAnimation(window);
} else {
// Reshow all windows previously hidden.
for (auto* window : hidden_windows_) {
ScopedAnimationDisabler disable(window);
window->Show();
}
}
// Update the backdrop last as the backdrop controller listens for some
// state changes like minimizing above which may also alter the backdrop.
aura::Window* backdrop_window = GetBackdropWindow(GetWindow1());
if (backdrop_window) {
backdrop_window->SetTransform(gfx::Transform());
backdrop_window->layer()->SetOpacity(1.f);
}
RemoveObserversAndStopTracking();
}
void HomeLauncherGestureHandler::AnimateToFinalState() {
const bool is_final_state_show = IsFinalStateShow();
UpdateWindows(is_final_state_show ? 1.0 : 0.0, /*animate=*/true);
if (!is_final_state_show && mode_ == Mode::kSlideDownToHide) {
app_list_controller_->NotifyHomeLauncherTargetPositionChanged(
false /*showing*/, display_.id());
base::RecordAction(
base::UserMetricsAction("AppList_HomeLauncherToMRUWindow"));
} else if (is_final_state_show && mode_ == Mode::kSlideUpToShow) {
app_list_controller_->NotifyHomeLauncherTargetPositionChanged(
true /*showing*/, display_.id());
base::RecordAction(
base::UserMetricsAction("AppList_CurrentWindowToHomeLauncher"));
}
}
void HomeLauncherGestureHandler::UpdateSettings(
ui::ScopedLayerAnimationSettings* settings,
bool observe) {
settings->SetTransitionDuration(IsDragInProgress()
? kAnimationDurationMs
: kActivationChangedAnimationDurationMs);
settings->SetTweenType(IsDragInProgress() ? gfx::Tween::LINEAR
: gfx::Tween::FAST_OUT_SLOW_IN);
settings->SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
if (observe)
settings->AddObserver(this);
}
void HomeLauncherGestureHandler::UpdateWindows(double progress, bool animate) {
// Update full screen applist.
DCHECK(display_.is_valid());
const gfx::Rect work_area = display_.work_area();
const int y_position =
gfx::Tween::IntValueBetween(progress, work_area.bottom(), work_area.y());
const float opacity = gfx::Tween::FloatValueBetween(progress, 0.f, 1.f);
app_list_controller_->presenter()->UpdateYPositionAndOpacityForHomeLauncher(
y_position, opacity,
animate ? base::BindRepeating(&HomeLauncherGestureHandler::UpdateSettings,
base::Unretained(this))
: base::NullCallback());
// Update the overview grid if needed.
OverviewController* controller = Shell::Get()->overview_controller();
if (overview_active_on_gesture_start_ && controller->IsSelecting()) {
DCHECK_EQ(mode_, Mode::kSlideUpToShow);
controller->overview_session()->UpdateGridAtLocationYPositionAndOpacity(
display_.id(), y_position - work_area.height(), 1.f - opacity,
work_area,
animate
? base::BindRepeating(&HomeLauncherGestureHandler::UpdateSettings,
base::Unretained(this))
: base::NullCallback());
}
if (!window1_)
return;
// Helper to update a single windows opacity and transform based on by
// calculating the in between values using |value| and |values|.
auto update_windows_helper = [this](double progress, bool animate,
aura::Window* window,
const WindowValues& values) {
float opacity = gfx::Tween::FloatValueBetween(
progress, values.initial_opacity, values.target_opacity);
gfx::Transform transform = gfx::Tween::TransformValueBetween(
progress, values.initial_transform, values.target_transform);
std::unique_ptr<ui::ScopedLayerAnimationSettings> settings;
if (animate) {
settings = std::make_unique<ui::ScopedLayerAnimationSettings>(
window->layer()->GetAnimator());
// There are multiple animations run on a release event (app list,
// overview and the stored windows). We only want to act on one
// animation end, so only observe one of the animations. If overview
// is active, observe the shield widget of the grid, else observe
// |window1_|.
UpdateSettings(settings.get(),
this->GetWindow1() == window &&
!(overview_active_on_gesture_start_ &&
Shell::Get()->overview_controller()->IsSelecting()));
}
window->layer()->SetOpacity(opacity);
window->SetTransform(transform);
};
aura::Window* backdrop_window = GetBackdropWindow(GetWindow1());
if (backdrop_window && backdrop_values_) {
update_windows_helper(progress, animate, backdrop_window,
*backdrop_values_);
}
aura::Window* divider_window = GetDividerWindow();
if (divider_window && divider_values_) {
update_windows_helper(progress, animate, divider_window, *divider_values_);
}
if (window2_) {
for (const auto& descendant : window2_->transient_descendants_values()) {
update_windows_helper(progress, animate, descendant.first,
descendant.second);
}
update_windows_helper(progress, animate, GetWindow2(),
window2_->window_values());
}
for (const auto& descendant : window1_->transient_descendants_values()) {
update_windows_helper(progress, animate, descendant.first,
descendant.second);
}
update_windows_helper(progress, animate, GetWindow1(),
window1_->window_values());
}
void HomeLauncherGestureHandler::RemoveObserversAndStopTracking() {
display_.set_id(display::kInvalidDisplayId);
backdrop_values_ = base::nullopt;
divider_values_ = base::nullopt;
last_event_location_ = base::nullopt;
last_scroll_y_ = 0.f;
mode_ = Mode::kNone;
for (auto* window : hidden_windows_)
window->RemoveObserver(this);
hidden_windows_.clear();
if (window1_)
GetWindow1()->RemoveObserver(this);
window1_.reset();
if (window2_)
GetWindow2()->RemoveObserver(this);
window2_.reset();
}
bool HomeLauncherGestureHandler::IsIdle() {
return !IsDragInProgress() && !IsAnimating();
}
bool HomeLauncherGestureHandler::IsAnimating() {
if (window1_ && window1_->IsAnimating())
return true;
if (window2_ && window2_->IsAnimating())
return true;
if (overview_active_on_gesture_start_ &&
Shell::Get()->overview_controller()->IsSelecting() &&
Shell::Get()
->overview_controller()
->overview_session()
->IsOverviewGridAnimating()) {
return true;
}
return false;
}
bool HomeLauncherGestureHandler::IsFinalStateShow() {
DCHECK_NE(Mode::kNone, mode_);
DCHECK(display_.is_valid());
// If fling velocity is greater than the threshold, show the launcher if
// sliding up, or hide the launcher if sliding down, irregardless of
// |last_event_location_|.
if (mode_ == Mode::kSlideUpToShow &&
last_scroll_y_ < -kScrollVelocityThreshold) {
return true;
}
if (mode_ == Mode::kSlideDownToHide &&
last_scroll_y_ > kScrollVelocityThreshold) {
return false;
}
return last_event_location_
? IsLastEventInTopHalf(*last_event_location_, display_.work_area())
: mode_ == Mode::kSlideUpToShow;
}
bool HomeLauncherGestureHandler::SetUpWindows(Mode mode, aura::Window* window) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
overview_active_on_gesture_start_ =
Shell::Get()->overview_controller()->IsSelecting();
const bool split_view_active = split_view_controller->IsSplitViewModeActive();
auto windows = Shell::Get()->mru_window_tracker()->BuildWindowForCycleList();
if (window && (mode != Mode::kSlideDownToHide ||
overview_active_on_gesture_start_ || split_view_active)) {
window1_.reset();
return false;
}
if (window && !windows.empty() && windows[0] != window &&
windows[0]->IsVisible()) {
// Do not run slide down animation for the |window| if another active
// window in mru list exists. Windows minimized in clamshell mode may
// have opacity of 0, so set them to 1 to ensure visibility.
if (wm::GetWindowState(window)->IsMinimized())
window->layer()->SetOpacity(1.f);
window1_.reset();
return false;
}
if (IsTabletMode() && overview_active_on_gesture_start_ &&
!split_view_active) {
DCHECK_EQ(Mode::kSlideUpToShow, mode);
window1_.reset();
return true;
}
// Always hide split view windows if they exist. Otherwise, hide the
// specified window if it is not null. If none of above is true, we want
// the first window in the mru list, if it exists and is usable.
aura::Window* first_window =
split_view_active
? split_view_controller->GetDefaultSnappedWindow()
: (window ? window : (windows.empty() ? nullptr : windows[0]));
if (!CanProcessWindow(first_window, mode)) {
window1_.reset();
return false;
}
DCHECK(base::ContainsValue(windows, first_window));
DCHECK_NE(Mode::kNone, mode);
base::RecordAction(base::UserMetricsAction(
mode == Mode::kSlideDownToHide
? "AppList_HomeLauncherToMRUWindowAttempt"
: "AppList_CurrentWindowToHomeLauncherAttempt"));
window1_ = std::make_unique<ScopedWindowModifier>(first_window);
GetWindow1()->AddObserver(this);
base::EraseIf(windows, [this](aura::Window* elem) {
return elem == this->GetWindow1();
});
// Alter a second window if we are in split view mode with two windows
// snapped.
if (mode == Mode::kSlideUpToShow &&
split_view_controller->state() == SplitViewController::BOTH_SNAPPED) {
DCHECK_GT(windows.size(), 0u);
aura::Window* second_window =
split_view_controller->default_snap_position() ==
SplitViewController::LEFT
? split_view_controller->right_window()
: split_view_controller->left_window();
DCHECK(base::ContainsValue(windows, second_window));
window2_ = std::make_unique<ScopedWindowModifier>(second_window);
GetWindow2()->AddObserver(this);
base::EraseIf(windows, [this](aura::Window* elem) {
return elem == this->GetWindow2();
});
}
// Show |window1_| if we are swiping down to hide.
if (mode == Mode::kSlideDownToHide) {
ScopedAnimationDisabler disable(GetWindow1());
GetWindow1()->Show();
// When |window1_| has a modal dialog child, window1_->Show() above would
// cancel the current gesture and trigger OnReleaseEvent() to reset
// |window1_|.
if (!window1_ || !GetWindow1())
return false;
wm::ActivateWindow(GetWindow1());
GetWindow1()->layer()->SetOpacity(1.f);
}
const gfx::RectF work_area =
gfx::RectF(screen_util::GetDisplayWorkAreaBoundsInParent(GetWindow1()));
const gfx::RectF target_work_area = GetOffscreenWorkspaceBounds(work_area);
window1_->ComputeWindowValues(work_area, target_work_area);
if (window2_)
window2_->ComputeWindowValues(work_area, target_work_area);
aura::Window* backdrop_window = GetBackdropWindow(GetWindow1());
if (backdrop_window) {
// Store the values needed to transform the backdrop. The backdrop
// actually covers the area behind the shelf as well, so initially
// transform it to be sized to the work area. Without the transform
// tweak, there is an extra shelf sized black area under |window1_|. Go
// to 0.01 opacity instead of 0 opacity otherwise animation end code
// will attempt to update the backdrop which will try to show a 0
// opacity window which causes a crash.
backdrop_values_ = base::make_optional(WindowValues());
backdrop_values_->initial_opacity = 1.f;
backdrop_values_->initial_transform = gfx::Transform(
1.f, 0.f, 0.f,
work_area.height() /
static_cast<float>(backdrop_window->bounds().height()),
0.f, 0.f);
backdrop_values_->target_opacity = 0.01f;
backdrop_values_->target_transform = CalculateTransform(
gfx::RectF(backdrop_window->bounds()), target_work_area);
}
// Stores values needed to transform the split view divider if it exists.
aura::Window* divider_window = GetDividerWindow();
if (divider_window) {
divider_values_ = base::make_optional(WindowValues());
divider_values_->initial_opacity = 1.f;
divider_values_->initial_transform = gfx::Transform();
divider_values_->target_opacity = 0.f;
divider_values_->target_transform = CalculateTransform(
gfx::RectF(divider_window->bounds()),
GetOffscreenWindowBounds(divider_window, work_area, target_work_area));
}
// Hide all visible windows which are behind our window so that when we
// scroll, the home launcher will be visible. This is only needed when
// swiping up, and not when overview mode is active.
hidden_windows_.clear();
if (mode == Mode::kSlideUpToShow && !overview_active_on_gesture_start_) {
for (auto* window : windows) {
if (window->IsVisible()) {
hidden_windows_.push_back(window);
window->AddObserver(this);
wm::HideWithoutAnimation(window);
}
}
}
return true;
}
} // namespace ash