Commit 57dccc17 authored by Avery Musbach's avatar Avery Musbach Committed by Commit Bot

split view: Implement the divider spawning animation

The present CL is for the animation of the divider and its white handle,
when split view is activated by dragging a window, if the window is not
minimized and the dragging is not tab dragging. The animation is based
on:
https://mccanny.users.x20web.corp.google.com/www/splitscreen-motion/index.html#window-drop

The present CL deviates from the specification in an edge case as shown
in the video linked in Comment 14 here:
https://bugs.chromium.org/p/chromium/issues/detail?id=934977#c14

See how the divider moves toward the side of the screen where the window
is snapped, whereas the mockup shows the divider moving away from that
side of the screen. The specification says that the white handle shall
spawn from "2dp off centerline of divider (in direction of drop)," which
in this case is 2dp to the left of where the divider spawns from. Then
the divider would pass through the point where the handle spawns.
Because that seems wrong, where the specification says "in direction of
drop," I instead use the direction from which the divider spawns, as you
see in the video.

chrome://flags/#ui-slow-animations affects the divider but not the white
handle, because the white handle uses SlideAnimation. The flag therefore
desynchronizes the spawning animations, but fortunately, the video
referenced above opens in a player where playback speed can be adjusted.

Test: manual
Bug: 934977
Change-Id: Ide325c36c939fbf4af07b878811207c79afc206c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1601368Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarXiaoqian Dai <xdai@chromium.org>
Commit-Queue: Avery Musbach <amusbach@chromium.org>
Cr-Commit-Position: refs/heads/master@{#659279}
parent 3e9d5d0c
......@@ -1174,6 +1174,8 @@ component("ash") {
"wm/splitview/split_view_controller.cc",
"wm/splitview/split_view_divider.cc",
"wm/splitview/split_view_divider.h",
"wm/splitview/split_view_divider_handler_view.cc",
"wm/splitview/split_view_divider_handler_view.h",
"wm/splitview/split_view_drag_indicators.cc",
"wm/splitview/split_view_drag_indicators.h",
"wm/splitview/split_view_highlight_view.cc",
......
......@@ -482,7 +482,8 @@ void OverviewWindowDragController::SnapWindow(
DCHECK_NE(snap_position, SplitViewController::NONE);
// |item_| will be deleted after SplitViewController::SnapWindow().
split_view_controller_->SnapWindow(item_->GetWindow(), snap_position);
split_view_controller_->SnapWindow(item_->GetWindow(), snap_position,
/*use_divider_spawn_animation=*/true);
item_ = nullptr;
}
......
......@@ -35,6 +35,48 @@ constexpr SkColor kSplitviewLabelEnabledColor = SK_ColorWHITE;
constexpr SkColor kSplitviewLabelBackgroundColor =
SkColorSetA(SK_ColorBLACK, 0xDE);
// The color of the divider.
constexpr SkColor kSplitviewDividerColor = SK_ColorBLACK;
// The color of the divider's handler.
constexpr SkColor kSplitviewWhiteBarColor = SK_ColorWHITE;
// The thickness of the divider when it is not being dragged.
constexpr int kSplitviewDividerShortSideLength = 8;
// The thickness of the divider during dragging.
constexpr int kSplitviewDividerEnlargedShortSideLength = 16;
// The time duration for the window transformation animations.
constexpr int kSplitviewWindowTransformMs = 250;
// The time duration for the divider animations when dragging starts and ends.
constexpr int kSplitviewDividerSelectionStatusChangeDurationMs = 250;
// The time duration for the divider spawning animation.
constexpr int kSplitviewDividerSpawnDurationMs = 100;
// The delay before the divider spawning animation.
constexpr int kSplitviewDividerSpawnDelayMs = 183;
// The thickness of the divider's handler.
constexpr int kSplitviewWhiteBarShortSideLength = 2;
// The length of the divider's handler.
constexpr int kSplitviewWhiteBarLongSideLength = 16;
// The corner radius of the divider's handler.
constexpr int kSplitviewWhiteBarCornerRadius = 1;
// The radius of the circular handler when the divider is being dragged.
constexpr int kSplitviewWhiteBarRadius = 4;
// The length of the divider's handler when it spawns.
constexpr int kSplitviewWhiteBarSpawnLongSideLength = 2;
// The distance from the divider to where its handler spawns.
constexpr int kSplitviewWhiteBarSpawnUnsignedOffset = 2;
} // namespace ash
#endif // ASH_WM_SPLITVIEW_SPLIT_VIEW_CONSTANTS_H_
......@@ -22,6 +22,7 @@
#include "ash/wm/overview/overview_grid.h"
#include "ash/wm/overview/overview_item.h"
#include "ash/wm/overview/overview_utils.h"
#include "ash/wm/splitview/split_view_constants.h"
#include "ash/wm/splitview/split_view_divider.h"
#include "ash/wm/splitview/split_view_utils.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
......@@ -308,7 +309,8 @@ bool SplitViewController::InTabletSplitViewMode() const {
}
void SplitViewController::SnapWindow(aura::Window* window,
SnapPosition snap_position) {
SnapPosition snap_position,
bool use_divider_spawn_animation) {
DCHECK(window && CanSnapInSplitview(window));
DCHECK_NE(snap_position, NONE);
DCHECK(!is_resizing_);
......@@ -323,6 +325,7 @@ void SplitViewController::SnapWindow(aura::Window* window,
UpdateSnappingWindowTransformedBounds(window);
RemoveWindowFromOverviewIfApplicable(window);
bool do_divider_spawn_animation = false;
if (state_ == SplitViewState::kNoSnap) {
// Add observers when the split view mode starts.
Shell::Get()->AddShellObserver(this);
......@@ -337,6 +340,20 @@ void SplitViewController::SnapWindow(aura::Window* window,
if (split_view_type_ == SplitViewType::kTabletType) {
split_view_divider_ =
std::make_unique<SplitViewDivider>(this, window->GetRootWindow());
// The divider spawn animation adds a finishing touch to the |window|
// animation that generally accommodates snapping by dragging, but if
// |window| is currently minimized then it will undergo the unminimizing
// animation instead. Therefore skip the divider spawn animation if
// |window| is minimized.
if (use_divider_spawn_animation &&
!wm::GetWindowState(window)->IsMinimized()) {
// For the divider spawn animation, at the end of the delay, the divider
// shall be visually aligned with an edge of |window|. This effect will
// be more easily achieved after |window| has been snapped and the
// corresponding transform animation has begun. So for now, just set a
// flag to indicate that the divider spawn animation should be done.
do_divider_spawn_animation = true;
}
}
splitview_start_time_ = base::Time::Now();
}
......@@ -408,6 +425,28 @@ void SplitViewController::SnapWindow(aura::Window* window,
wm::GetWindowState(window)->OnWMEvent(&event);
}
if (do_divider_spawn_animation) {
DCHECK(window->layer()->GetAnimator()->GetTargetTransform().IsIdentity());
const gfx::Rect bounds =
GetSnappedWindowBoundsInScreen(window, snap_position);
// Get one of the two corners of |window| that meet the divider.
gfx::Point p = IsPhysicalLeftOrTop(snap_position) ? bounds.bottom_right()
: bounds.origin();
// Apply the transform that |window| will undergo when the divider spawns.
static const double value = gfx::Tween::CalculateValue(
gfx::Tween::FAST_OUT_SLOW_IN,
static_cast<double>(kSplitviewDividerSpawnDelayMs) /
static_cast<double>(kSplitviewWindowTransformMs));
gfx::TransformAboutPivot(bounds.origin(),
gfx::Tween::TransformValueBetween(
value, window->transform(), gfx::Transform()))
.TransformPoint(&p);
// Use a coordinate of the transformed |window| corner for spawn_position.
split_view_divider_->DoSpawningAnimation(
IsCurrentScreenOrientationLandscape() ? p.x() : p.y());
}
base::RecordAction(base::UserMetricsAction("SplitView_SnapWindow"));
}
......@@ -1851,9 +1890,12 @@ void SplitViewController::EndWindowDragImpl(
/*animate=*/true);
}
} else {
aura::Window* initiator_window =
window->GetProperty(ash::kTabDraggingSourceWindowKey);
// Note SnapWindow() might put the previous window that was snapped at the
// |desired_snap_position| in overview.
SnapWindow(window, desired_snap_position);
SnapWindow(window, desired_snap_position,
/*use_divider_spawn_animation=*/!initiator_window);
// Reapply the bounds update because the bounds might have been
// modified by dragging operation.
// TODO(oshima): WindowState already gets notified when drag ends. Refactor
......@@ -1868,8 +1910,6 @@ void SplitViewController::EndWindowDragImpl(
// If splitview mode was not active before snapping the dragged
// window, snap the initiator window to the other side of the screen
// if it's not the same window as the dragged window.
aura::Window* initiator_window =
window->GetProperty(ash::kTabDraggingSourceWindowKey);
if (initiator_window && initiator_window != window) {
SnapWindow(initiator_window,
(desired_snap_position == SplitViewController::LEFT)
......
......@@ -98,8 +98,14 @@ class ASH_EXPORT SplitViewController : public SplitViewNotifier,
// Snaps window to left/right. It will try to remove |window| from the
// overview window grid first before snapping it if |window| is currently
// showing in the overview window grid.
void SnapWindow(aura::Window* window, SnapPosition snap_position);
// showing in the overview window grid. If split view mode is not already
// active, and if |window| is not minimized, |use_divider_spawn_animation|
// causes the divider to show up with an animation that adds a finishing touch
// to the snap animation of |window|. Use true when |window| is snapped by
// dragging, except for tab dragging.
void SnapWindow(aura::Window* window,
SnapPosition snap_position,
bool use_divider_spawn_animation = false);
// Swaps the left and right windows. This will do nothing if one of the
// windows is not snapped.
......
......@@ -7,12 +7,12 @@
#include <memory>
#include "ash/display/screen_orientation_controller.h"
#include "ash/public/cpp/ash_constants.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/screen_util.h"
#include "ash/shell.h"
#include "ash/wm/overview/rounded_rect_view.h"
#include "ash/wm/splitview/split_view_constants.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_divider_handler_view.h"
#include "ash/wm/splitview/split_view_utils.h"
#include "ash/wm/window_util.h"
#include "base/sequenced_task_runner.h"
......@@ -21,10 +21,6 @@
#include "ui/aura/window_targeter.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/canvas.h"
#include "ui/views/view.h"
#include "ui/views/view_targeter_delegate.h"
#include "ui/views/widget/widget.h"
......@@ -38,20 +34,13 @@ namespace ash {
namespace {
// The length of short side of the black bar of the divider in dp.
constexpr int kDividerShortSideLength = 8;
constexpr int kDividerEnlargedShortSideLength = 16;
// The length of the white bar of the divider in dp.
constexpr int kWhiteBarShortSideLength = 2;
constexpr int kWhiteBarLongSideLength = 16;
constexpr int kWhiteBarRadius = 4;
constexpr int kWhiteBarCornerRadius = 1;
constexpr SkColor kDividerBackgroundColor = SK_ColorBLACK;
constexpr SkColor kWhiteBarBackgroundColor = SK_ColorWHITE;
constexpr int kDividerBoundsChangeDurationMs = 250;
constexpr int kWhiteBarBoundsChangeDurationMs = 250;
constexpr base::TimeDelta kDividerSelectionStatusChangeDuration =
base::TimeDelta::FromMilliseconds(
kSplitviewDividerSelectionStatusChangeDurationMs);
constexpr base::TimeDelta kDividerSpawnDuration =
base::TimeDelta::FromMilliseconds(kSplitviewDividerSpawnDurationMs);
constexpr base::TimeDelta kDividerSpawnDelay =
base::TimeDelta::FromMilliseconds(kSplitviewDividerSpawnDelayMs);
// The distance to the divider edge in which a touch gesture will be considered
// as a valid event on the divider.
......@@ -84,56 +73,62 @@ class AlwaysOnTopWindowTargeter : public aura::WindowTargeter {
DISALLOW_COPY_AND_ASSIGN(AlwaysOnTopWindowTargeter);
};
// The white handler bar in the middle of the divider.
class DividerHandlerView : public RoundedRectView {
public:
DividerHandlerView(int corner_radius, SkColor background_color)
: RoundedRectView(corner_radius, background_color) {}
~DividerHandlerView() override = default;
// views::View:
void OnPaint(gfx::Canvas* canvas) override {
views::View::OnPaint(canvas);
// It's needed to avoid artifacts when tapping on the divider quickly.
canvas->DrawColor(SK_ColorTRANSPARENT, SkBlendMode::kSrc);
RoundedRectView::OnPaint(canvas);
}
private:
DISALLOW_COPY_AND_ASSIGN(DividerHandlerView);
};
// The divider view class. Passes the mouse/gesture events to the controller.
class DividerView : public views::View,
public views::ViewTargeterDelegate,
public gfx::AnimationDelegate {
// Has two child views, one for the divider and one for its white handler. The
// bounds and transforms of these two child views can be affected by the
// spawning animation and by dragging, but regardless, the controller receives
// mouse/gesture events in the bounds of the |DividerView| object itself.
class DividerView : public views::View, public views::ViewTargeterDelegate {
public:
explicit DividerView(SplitViewDivider* divider)
: controller_(Shell::Get()->split_view_controller()),
divider_(divider),
white_bar_animation_(this) {
: controller_(Shell::Get()->split_view_controller()), divider_(divider) {
divider_view_ = new views::View();
divider_view_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
divider_view_->layer()->SetColor(kDividerBackgroundColor);
divider_view_->layer()->SetColor(kSplitviewDividerColor);
divider_handler_view_ =
new DividerHandlerView(kWhiteBarCornerRadius, kWhiteBarBackgroundColor);
divider_handler_view_->SetPaintToLayer();
divider_handler_view_ = new SplitViewDividerHandlerView();
AddChildView(divider_view_);
AddChildView(divider_handler_view_);
SetEventTargeter(
std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
white_bar_animation_.SetSlideDuration(kWhiteBarBoundsChangeDurationMs);
white_bar_animation_.SetTweenType(gfx::Tween::EASE_IN);
}
~DividerView() override { white_bar_animation_.Stop(); }
~DividerView() override = default;
void DoSpawningAnimation(int spawn_position) {
const gfx::Rect bounds = GetBoundsInScreen();
int divider_signed_offset;
// To animate the divider scaling up from nothing, animate its bounds rather
// than its transform, mostly because a transform that scales by zero would
// be singular. For that bounds animation, express |spawn_position| in local
// coordinates by subtracting a coordinate of the origin. Compute
// |divider_signed_offset| as described in the comment for
// |SplitViewDividerHandlerView::DoSpawningAnimation|.
if (IsCurrentScreenOrientationLandscape()) {
divider_view_->SetBounds(spawn_position - bounds.x(), 0, 0,
bounds.height());
divider_signed_offset = spawn_position - bounds.CenterPoint().x();
} else {
divider_view_->SetBounds(0, spawn_position - bounds.y(), bounds.width(),
0);
divider_signed_offset = spawn_position - bounds.CenterPoint().y();
}
ui::LayerAnimator* divider_animator = divider_view_->layer()->GetAnimator();
ui::ScopedLayerAnimationSettings settings(divider_animator);
settings.SetTransitionDuration(kDividerSpawnDuration);
settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
settings.SetPreemptionStrategy(ui::LayerAnimator::ENQUEUE_NEW_ANIMATION);
divider_animator->SchedulePauseForProperties(
kDividerSpawnDelay, ui::LayerAnimationElement::BOUNDS);
divider_view_->SetBounds(0, 0, bounds.width(), bounds.height());
divider_handler_view_->DoSpawningAnimation(divider_signed_offset);
}
// views::View:
void Layout() override {
divider_view_->SetBoundsRect(GetLocalBounds());
UpdateWhiteHandlerBounds();
divider_handler_view_->Refresh();
}
bool OnMousePressed(const ui::MouseEvent& event) override {
......@@ -193,19 +188,6 @@ class DividerView : public views::View,
return true;
}
// gfx::AnimationDelegate:
void AnimationEnded(const gfx::Animation* animation) override {
UpdateWhiteHandlerBounds();
}
void AnimationProgressed(const gfx::Animation* animation) override {
UpdateWhiteHandlerBounds();
}
void AnimationCanceled(const gfx::Animation* animation) override {
UpdateWhiteHandlerBounds();
}
private:
void OnResizeStatusChanged() {
// It's possible that when this function is called, split view mode has
......@@ -214,12 +196,10 @@ class DividerView : public views::View,
if (!controller_->InSplitViewMode())
return;
// Do the white handler bar enlarge/shrink animation when starting/ending
// dragging.
if (controller_->is_resizing())
white_bar_animation_.Show();
else
white_bar_animation_.Hide();
// If |divider_view_|'s bounds are animating, it is for the divider spawning
// animation. Stop that before animating |divider_view_|'s transform.
ui::LayerAnimator* divider_animator = divider_view_->layer()->GetAnimator();
divider_animator->StopAnimatingProperty(ui::LayerAnimationElement::BOUNDS);
// Do the divider enlarge/shrink animation when starting/ending dragging.
divider_view_->SetBoundsRect(GetLocalBounds());
......@@ -233,56 +213,20 @@ class DividerView : public views::View,
transform.Scale(
static_cast<float>(new_bounds.width()) / old_bounds.width(),
static_cast<float>(new_bounds.height()) / old_bounds.height());
ui::ScopedLayerAnimationSettings settings(
divider_view_->layer()->GetAnimator());
settings.SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kDividerBoundsChangeDurationMs));
ui::ScopedLayerAnimationSettings settings(divider_animator);
settings.SetTransitionDuration(kDividerSelectionStatusChangeDuration);
settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
divider_view_->SetTransform(transform);
}
// Returns the expected bounds of the white divider handler.
void UpdateWhiteHandlerBounds() {
// Calculate the width/height/radius for the rounded rectangle.
int width, height, radius;
const int expected_width_unselected = IsCurrentScreenOrientationLandscape()
? kWhiteBarShortSideLength
: kWhiteBarLongSideLength;
const int expected_height_unselected = IsCurrentScreenOrientationLandscape()
? kWhiteBarLongSideLength
: kWhiteBarShortSideLength;
if (white_bar_animation_.is_animating()) {
width = white_bar_animation_.CurrentValueBetween(
expected_width_unselected, kWhiteBarRadius * 2);
height = white_bar_animation_.CurrentValueBetween(
expected_height_unselected, kWhiteBarRadius * 2);
radius = white_bar_animation_.CurrentValueBetween(kWhiteBarCornerRadius,
kWhiteBarRadius);
} else {
if (controller_->is_resizing()) {
width = kWhiteBarRadius * 2;
height = kWhiteBarRadius * 2;
radius = kWhiteBarRadius;
} else {
width = expected_width_unselected;
height = expected_height_unselected;
radius = kWhiteBarCornerRadius;
}
}
gfx::Rect white_bar_bounds(GetLocalBounds());
white_bar_bounds.ClampToCenteredSize(gfx::Size(width, height));
divider_handler_view_->SetCornerRadius(radius);
divider_handler_view_->SetBoundsRect(white_bar_bounds);
divider_handler_view_->Refresh();
}
views::View* divider_view_ = nullptr;
DividerHandlerView* divider_handler_view_ = nullptr;
SplitViewDividerHandlerView* divider_handler_view_ = nullptr;
SplitViewController* controller_;
SplitViewDivider* divider_;
gfx::SlideAnimation white_bar_animation_;
DISALLOW_COPY_AND_ASSIGN(DividerView);
};
......@@ -317,15 +261,15 @@ gfx::Size SplitViewDivider::GetDividerSize(
OrientationLockType screen_orientation,
bool is_dragging) {
if (IsLandscapeOrientation(screen_orientation)) {
return is_dragging
? gfx::Size(kDividerEnlargedShortSideLength,
return is_dragging ? gfx::Size(kSplitviewDividerEnlargedShortSideLength,
work_area_bounds.height())
: gfx::Size(kDividerShortSideLength, work_area_bounds.height());
: gfx::Size(kSplitviewDividerShortSideLength,
work_area_bounds.height());
} else {
return is_dragging
? gfx::Size(work_area_bounds.width(),
kDividerEnlargedShortSideLength)
: gfx::Size(work_area_bounds.width(), kDividerShortSideLength);
return is_dragging ? gfx::Size(work_area_bounds.width(),
kSplitviewDividerEnlargedShortSideLength)
: gfx::Size(work_area_bounds.width(),
kSplitviewDividerShortSideLength);
}
}
......@@ -337,8 +281,9 @@ gfx::Rect SplitViewDivider::GetDividerBoundsInScreen(
bool is_dragging) {
const gfx::Size divider_size = GetDividerSize(
work_area_bounds_in_screen, screen_orientation, is_dragging);
int dragging_diff =
(kDividerEnlargedShortSideLength - kDividerShortSideLength) / 2;
int dragging_diff = (kSplitviewDividerEnlargedShortSideLength -
kSplitviewDividerShortSideLength) /
2;
switch (screen_orientation) {
case OrientationLockType::kLandscapePrimary:
case OrientationLockType::kLandscapeSecondary:
......@@ -355,8 +300,8 @@ gfx::Rect SplitViewDivider::GetDividerBoundsInScreen(
return is_dragging
? gfx::Rect(work_area_bounds_in_screen.x(),
work_area_bounds_in_screen.y() + divider_position -
(kDividerEnlargedShortSideLength -
kDividerShortSideLength) /
(kSplitviewDividerEnlargedShortSideLength -
kSplitviewDividerShortSideLength) /
2,
divider_size.width(), divider_size.height())
: gfx::Rect(work_area_bounds_in_screen.x(),
......@@ -368,6 +313,11 @@ gfx::Rect SplitViewDivider::GetDividerBoundsInScreen(
}
}
void SplitViewDivider::DoSpawningAnimation(int spawning_position) {
static_cast<DividerView*>(divider_widget_->GetContentsView())
->DoSpawningAnimation(spawning_position);
}
void SplitViewDivider::UpdateDividerBounds() {
divider_widget_->SetBounds(GetDividerBoundsInScreen(/*is_dragging=*/false));
}
......
......@@ -56,6 +56,10 @@ class ASH_EXPORT SplitViewDivider : public aura::WindowObserver,
int divider_position,
bool is_dragging);
// Do the divider spawning animation that adds a finishing touch to the
// snapping animation of a window.
void DoSpawningAnimation(int spawn_position);
// Updates |divider_widget_|'s bounds.
void UpdateDividerBounds();
......
// 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/wm/splitview/split_view_divider_handler_view.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/shell.h"
#include "ash/wm/splitview/split_view_constants.h"
#include "ash/wm/splitview/split_view_utils.h"
#include "base/timer/timer.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/animation/slide_animation.h"
namespace ash {
namespace {
constexpr base::TimeDelta kWhiteBarSpawnDelay =
base::TimeDelta::FromMilliseconds(kSplitviewDividerSpawnDelayMs);
} // namespace
class SplitViewDividerHandlerView::SelectionAnimation
: public gfx::SlideAnimation,
public gfx::AnimationDelegate {
public:
SelectionAnimation(SplitViewDividerHandlerView* white_handler_view)
: gfx::SlideAnimation(this), white_handler_view_(white_handler_view) {
SetSlideDuration(kSplitviewDividerSelectionStatusChangeDurationMs);
SetTweenType(gfx::Tween::EASE_IN);
}
~SelectionAnimation() override = default;
void UpdateWhiteHandlerBounds() {
white_handler_view_->SetBounds(
CurrentValueBetween(kSplitviewWhiteBarShortSideLength,
kSplitviewWhiteBarRadius * 2),
CurrentValueBetween(kSplitviewWhiteBarLongSideLength,
kSplitviewWhiteBarRadius * 2),
/*signed_offset=*/0);
}
private:
// gfx::AnimationDelegate:
void AnimationProgressed(const gfx::Animation* animation) override {
UpdateWhiteHandlerBounds();
white_handler_view_->SetCornerRadius(CurrentValueBetween(
kSplitviewWhiteBarCornerRadius, kSplitviewWhiteBarRadius));
}
SplitViewDividerHandlerView* white_handler_view_;
DISALLOW_COPY_AND_ASSIGN(SelectionAnimation);
};
class SplitViewDividerHandlerView::SpawningAnimation
: public gfx::SlideAnimation,
public gfx::AnimationDelegate {
public:
SpawningAnimation(SplitViewDividerHandlerView* white_handler_view,
int divider_signed_offset)
: gfx::SlideAnimation(this),
white_handler_view_(white_handler_view),
spawn_signed_offset_(divider_signed_offset +
(divider_signed_offset > 0
? kSplitviewWhiteBarSpawnUnsignedOffset
: -kSplitviewWhiteBarSpawnUnsignedOffset)) {
SetSlideDuration(kSplitviewDividerSpawnDurationMs);
SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
}
~SpawningAnimation() override = default;
void Activate() {
white_handler_view_->SetVisible(false);
delay_timer_.Start(FROM_HERE, kWhiteBarSpawnDelay, this,
&SpawningAnimation::StartAnimation);
}
bool IsActive() const { return delay_timer_.IsRunning() || is_animating(); }
void UpdateWhiteHandlerBounds() {
DCHECK(IsActive());
white_handler_view_->SetBounds(
kSplitviewWhiteBarShortSideLength,
CurrentValueBetween(kSplitviewWhiteBarSpawnLongSideLength,
kSplitviewWhiteBarLongSideLength),
CurrentValueBetween(spawn_signed_offset_, 0));
}
private:
void StartAnimation() {
DCHECK(!white_handler_view_->visible());
white_handler_view_->SetVisible(true);
DCHECK(!is_animating());
Show();
DCHECK_EQ(0.0, GetCurrentValue());
UpdateWhiteHandlerBounds();
}
// gfx::AnimationDelegate:
void AnimationProgressed(const gfx::Animation* animation) override {
UpdateWhiteHandlerBounds();
}
SplitViewDividerHandlerView* white_handler_view_;
int spawn_signed_offset_;
base::OneShotTimer delay_timer_;
DISALLOW_COPY_AND_ASSIGN(SpawningAnimation);
};
SplitViewDividerHandlerView::SplitViewDividerHandlerView()
: RoundedRectView(kSplitviewWhiteBarCornerRadius, kSplitviewWhiteBarColor),
selection_animation_(std::make_unique<SelectionAnimation>(this)) {
SetPaintToLayer();
}
SplitViewDividerHandlerView::~SplitViewDividerHandlerView() = default;
void SplitViewDividerHandlerView::DoSpawningAnimation(
int divider_signed_offset) {
spawning_animation_ =
std::make_unique<SpawningAnimation>(this, divider_signed_offset);
spawning_animation_->Activate();
}
void SplitViewDividerHandlerView::Refresh() {
spawning_animation_.reset();
SetVisible(true);
selection_animation_->UpdateWhiteHandlerBounds();
if (Shell::Get()->split_view_controller()->is_resizing())
selection_animation_->Show();
else
selection_animation_->Hide();
}
void SplitViewDividerHandlerView::SetBounds(int short_length,
int long_length,
int signed_offset) {
const bool landscape = IsCurrentScreenOrientationLandscape();
gfx::Rect bounds = landscape ? gfx::Rect(short_length, long_length)
: gfx::Rect(long_length, short_length);
bounds.Offset(parent()->GetLocalBounds().CenterPoint() -
bounds.CenterPoint() +
(landscape ? gfx::Vector2d(signed_offset, 0)
: gfx::Vector2d(0, signed_offset)));
SetBoundsRect(bounds);
}
void SplitViewDividerHandlerView::OnPaint(gfx::Canvas* canvas) {
views::View::OnPaint(canvas);
// It's needed to avoid artifacts when tapping on the divider quickly.
canvas->DrawColor(SK_ColorTRANSPARENT, SkBlendMode::kSrc);
RoundedRectView::OnPaint(canvas);
}
} // namespace ash
// 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.
#ifndef ASH_WM_SPLITVIEW_SPLIT_VIEW_DIVIDER_HANDLER_VIEW_H_
#define ASH_WM_SPLITVIEW_SPLIT_VIEW_DIVIDER_HANDLER_VIEW_H_
#include <memory>
#include "ash/ash_export.h"
#include "ash/public/cpp/ash_constants.h"
#include "ash/wm/overview/rounded_rect_view.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "base/macros.h"
#include "ui/gfx/canvas.h"
#include "ui/views/view.h"
namespace ash {
// The white handler bar in the middle of the divider.
class ASH_EXPORT SplitViewDividerHandlerView : public RoundedRectView {
public:
SplitViewDividerHandlerView();
~SplitViewDividerHandlerView() override;
// Play the white handler's part in the divider spawn animation.
// |divider_signed_offset| represents the motion of the center of the divider
// during the spawning animation. This parameter is used to make the white
// handler move with the center of the divider, as the two views are siblings
// because if the white handler view were a child of the divider view, then
// the transform that enlarges the divider during dragging would distort the
// white handler.
void DoSpawningAnimation(int divider_signed_offset);
// If the spawning animation is running, stop it and show the white handler.
// Update bounds. Do the enlarge/shrink animation when starting/ending
// dragging.
void Refresh();
private:
class SelectionAnimation;
class SpawningAnimation;
void SetBounds(int short_length, int long_length, int signed_offset);
// views::View:
void OnPaint(gfx::Canvas* canvas) override;
// Handles the animations for starting and ending dragging.
std::unique_ptr<SelectionAnimation> selection_animation_;
// Handles the spawning animation.
std::unique_ptr<SpawningAnimation> spawning_animation_;
DISALLOW_COPY_AND_ASSIGN(SplitViewDividerHandlerView);
};
} // namespace ash
#endif // ASH_WM_SPLITVIEW_SPLIT_VIEW_DIVIDER_HANDLER_VIEW_H_
......@@ -5,6 +5,7 @@
#include "ash/wm/splitview/split_view_utils.h"
#include "ash/accessibility/accessibility_controller.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/ash_switches.h"
#include "ash/screen_util.h"
......@@ -46,7 +47,7 @@ constexpr base::TimeDelta kLabelAnimationDelay =
base::TimeDelta::FromMilliseconds(167);
// The time duration for the window transformation animations.
constexpr base::TimeDelta kWindowTransform =
base::TimeDelta::FromMilliseconds(250);
base::TimeDelta::FromMilliseconds(kSplitviewWindowTransformMs);
constexpr float kHighlightOpacity = 0.3f;
constexpr float kPreviewAreaHighlightOpacity = 0.18f;
......@@ -267,4 +268,11 @@ bool CanSnapInSplitview(aura::Window* window) {
return true;
}
bool IsPhysicalLeftOrTop(SplitViewController::SnapPosition position) {
DCHECK_NE(SplitViewController::NONE, position);
return position == (IsCurrentScreenOrientationPrimary()
? SplitViewController::LEFT
: SplitViewController::RIGHT);
}
} // namespace ash
......@@ -7,6 +7,7 @@
#include "ash/ash_export.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/transform.h"
......@@ -90,6 +91,8 @@ ASH_EXPORT bool ShouldAllowSplitView();
// tablet mode.
ASH_EXPORT bool CanSnapInSplitview(aura::Window* window);
ASH_EXPORT bool IsPhysicalLeftOrTop(SplitViewController::SnapPosition position);
} // namespace ash
#endif // ASH_WM_SPLITVIEW_SPLIT_VIEW_UTILS_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment