Commit ded3461c authored by Sammie Quon's avatar Sammie Quon Committed by Commit Bot

splitview: Change highlights to use a single solid color layer.

Replaces the old three view setup which was used before we could
apply rounded corners directly on layers. Tested manually on
LTR and RTL on all 4 orientations. Visually no difference.

Ran ui.OverviewDragWindowPerf.drag_to_snap. Not noticeable
improvements on caroline, but input latency goes from 95ms to 80ms
on krane.

Test: added tests
Bug: 1047778
Change-Id: Idff6345fb5778d2e898bc51cd76bd7eac3c1741d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2033564
Commit-Queue: Sammie Quon <sammiequon@chromium.org>
Reviewed-by: default avatarXiaoqian Dai <xdai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#738262}
parent d7b8a9d1
......@@ -2353,8 +2353,6 @@ static_library("test_support") {
"wm/lock_state_controller_test_api.h",
"wm/splitview/multi_display_overview_and_split_view_test.cc",
"wm/splitview/multi_display_overview_and_split_view_test.h",
"wm/splitview/split_view_highlight_view_test_api.cc",
"wm/splitview/split_view_highlight_view_test_api.h",
"wm/tablet_mode/tablet_mode_controller_test_api.cc",
"wm/tablet_mode/tablet_mode_controller_test_api.h",
"wm/test_activation_delegate.cc",
......
......@@ -236,11 +236,6 @@ class SplitViewDragIndicators::SplitViewDragIndicatorsView
right_highlight_view_ = AddChildView(
std::make_unique<SplitViewHighlightView>(/*is_right_or_bottom=*/true));
left_highlight_view_->SetPaintToLayer();
right_highlight_view_->SetPaintToLayer();
left_highlight_view_->layer()->SetFillsBoundsOpaquely(false);
right_highlight_view_->layer()->SetFillsBoundsOpaquely(false);
left_rotated_view_ = AddChildView(
std::make_unique<RotatedImageLabelView>(/*is_right_or_bottom=*/false));
right_rotated_view_ = AddChildView(
......@@ -464,9 +459,8 @@ class SplitViewDragIndicators::SplitViewDragIndicatorsView
}
left_highlight_view_->SetBounds(GetMirroredRect(left_highlight_bounds),
horizontal, left_highlight_animation_type);
left_highlight_animation_type);
right_highlight_view_->SetBounds(GetMirroredRect(right_highlight_bounds),
horizontal,
right_highlight_animation_type);
// Calculate the bounds of the views which contain the guidance text and
......
......@@ -6,176 +6,116 @@
#include "ash/display/screen_orientation_controller.h"
#include "ash/shell.h"
#include "ash/wm/overview/rounded_rect_view.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_utils.h"
#include "ui/gfx/canvas.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/view_observer.h"
namespace ash {
namespace {
// The amount of round applied to the corners of the highlight views.
constexpr int kHighlightScreenRoundRectRadiusDp = 4;
constexpr int kRoundRectPaddingDp = 10;
gfx::Transform CalculateTransformFromRects(const gfx::Rect& src,
const gfx::Rect& dst,
bool landscape) {
// In portrait, rtl will have no effect on this view.
const bool is_rtl = base::i18n::IsRTL() && landscape;
const bool should_scale =
src.width() != dst.width() || src.height() != dst.height();
constexpr gfx::RoundedCornersF kHighlightScreenRoundRectRadii(4.f);
// Self deleting animation observer that removes clipping on View's layer and
// optionally sets bounds after the animation ends.
class ClippingObserver : public ui::ImplicitAnimationObserver,
public views::ViewObserver {
public:
ClippingObserver(views::View* view, base::Optional<gfx::Rect> bounds)
: view_(view), bounds_(bounds) {
view_->AddObserver(this);
}
~ClippingObserver() override { view_->RemoveObserver(this); }
// ui::ImplicitAnimationObserver:
void OnImplicitAnimationsCompleted() override {
view_->layer()->SetClipRect(gfx::Rect());
if (bounds_)
view_->SetBoundsRect(*bounds_);
delete this;
}
// Add a translatation. In rtl, translate in the opposite direction to account
// for the flip.
gfx::Transform transform;
transform.Translate(is_rtl && !should_scale ? src.origin() - dst.origin()
: dst.origin() - src.origin());
if (should_scale) {
// In rtl a extra translation needs to be added to account for the flipped
// scaling.
if (is_rtl) {
int x_translation = 0;
if ((src.x() > dst.x() && src.width() < dst.width()) ||
(src.x() == dst.x() && src.width() > dst.width())) {
x_translation = std::abs(dst.width() - src.width());
} else {
x_translation = -std::abs(dst.width() - src.width());
}
transform.Translate(gfx::Vector2d(x_translation, 0));
}
transform.Scale(
static_cast<float>(dst.width()) / static_cast<float>(src.width()),
static_cast<float>(dst.height()) / static_cast<float>(src.height()));
// views::ViewObserver:
void OnViewIsDeleting(views::View* observed_view) override {
DCHECK_EQ(view_, observed_view);
delete this;
}
return transform;
}
private:
views::View* const view_;
base::Optional<gfx::Rect> bounds_;
};
} // namespace
SplitViewHighlightView::SplitViewHighlightView(bool is_right_or_bottom)
: is_right_or_bottom_(is_right_or_bottom) {
left_top_ =
new RoundedRectView(kHighlightScreenRoundRectRadiusDp, SK_ColorWHITE);
right_bottom_ =
new RoundedRectView(kHighlightScreenRoundRectRadiusDp, SK_ColorWHITE);
left_top_->SetPaintToLayer();
left_top_->layer()->SetFillsBoundsOpaquely(false);
right_bottom_->SetPaintToLayer();
right_bottom_->layer()->SetFillsBoundsOpaquely(false);
middle_ = new views::View();
middle_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
middle_->layer()->SetColor(SK_ColorWHITE);
AddChildView(left_top_);
AddChildView(right_bottom_);
AddChildView(middle_);
SetPaintToLayer(ui::LAYER_SOLID_COLOR);
layer()->SetFillsBoundsOpaquely(false);
layer()->SetColor(SK_ColorWHITE);
layer()->SetRoundedCornerRadius(kHighlightScreenRoundRectRadii);
layer()->SetIsFastRoundedCorner(true);
}
SplitViewHighlightView::~SplitViewHighlightView() = default;
void SplitViewHighlightView::SetBounds(
const gfx::Rect& bounds,
bool landscape,
const base::Optional<SplitviewAnimationType>& animation_type) {
if (bounds == this->bounds() && landscape == landscape_)
if (bounds == this->bounds())
return;
landscape_ = landscape;
const gfx::Rect old_bounds = this->bounds();
const gfx::Vector2d offset = old_bounds.origin() - bounds.origin();
SetBoundsRect(bounds);
// Shift the bounds of the right bottom view if needed before applying the
// transform.
const bool slides_from_right = base::i18n::IsRTL() && landscape
? !is_right_or_bottom_
: is_right_or_bottom_;
if (!offset.IsZero() && animation_type &&
(slides_from_right ||
*animation_type == SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET)) {
gfx::Rect old_left_top_bounds = left_top_->bounds();
gfx::Rect old_right_middle_bounds = right_bottom_->bounds();
gfx::Rect old_middle_bounds = middle_->bounds();
old_left_top_bounds.Offset(offset);
old_right_middle_bounds.Offset(offset);
old_middle_bounds.Offset(offset);
left_top_->SetBoundsRect(old_left_top_bounds);
right_bottom_->SetBoundsRect(old_right_middle_bounds);
middle_->SetBoundsRect(old_middle_bounds);
}
// Calculate the new bounds. The middle should take as much space as possible,
// and the other two should take just enough space so they can display rounded
// corners.
gfx::Rect left_top_bounds, right_bottom_bounds;
gfx::Rect middle_bounds = bounds;
// The thickness of the two outer views should be the amount of rounding, plus
// a little padding. There will be some overlap to simply the code (we use a
// rectangle that is rounded on all sides, but cover half the sides instead of
// creating a new class that is only rounded on half the sides).
const int thickness = kHighlightScreenRoundRectRadiusDp + kRoundRectPaddingDp;
if (landscape) {
left_top_bounds = gfx::Rect(0, 0, thickness, bounds.height());
right_bottom_bounds = left_top_bounds;
right_bottom_bounds.Offset(bounds.width() - thickness, 0);
middle_bounds.Offset(-bounds.x(), -bounds.y());
middle_bounds.Inset(kHighlightScreenRoundRectRadiusDp, 0);
} else {
left_top_bounds = gfx::Rect(0, 0, bounds.width(), thickness);
right_bottom_bounds = left_top_bounds;
right_bottom_bounds.Offset(0, bounds.height() - thickness);
middle_bounds.Offset(-bounds.x(), -bounds.y());
middle_bounds.Inset(0, kHighlightScreenRoundRectRadiusDp);
if (!animation_type) {
SetBoundsRect(bounds);
return;
}
left_top_bounds = GetMirroredRect(left_top_bounds);
right_bottom_bounds = GetMirroredRect(right_bottom_bounds);
middle_bounds = GetMirroredRect(middle_bounds);
// If |animation_type| has a value, calculate the needed transform from old
// bounds to new bounds and apply it. Otherwise set the new bounds and reset
// the transforms on all items.
if (animation_type) {
DoSplitviewTransformAnimation(
middle_->layer(), *animation_type,
CalculateTransformFromRects(middle_->bounds(), middle_bounds,
landscape),
/*animation_observer=*/nullptr);
DoSplitviewTransformAnimation(
left_top_->layer(), *animation_type,
CalculateTransformFromRects(left_top_->bounds(), left_top_bounds,
landscape),
/*animation_observer=*/nullptr);
DoSplitviewTransformAnimation(
right_bottom_->layer(), *animation_type,
CalculateTransformFromRects(right_bottom_->bounds(),
right_bottom_bounds, landscape),
/*animation_observer=*/nullptr);
} else {
left_top_->layer()->SetTransform(gfx::Transform());
right_bottom_->layer()->SetTransform(gfx::Transform());
middle_->layer()->SetTransform(gfx::Transform());
const gfx::Rect old_bounds = this->bounds();
// Note: This is passed on the assumption that the highlights either.
// 1) Slide out - x or y increases and other dimension stays the same.
// 2) Slide in - x or y decreases and other dimension stays the same.
// 3) Expands(Nix inset) - x and y both increase by a small amount.
const bool grows = bounds.size().GetArea() > old_bounds.size().GetArea();
// If the highlight grows, set the final bounds and clip the rect to the
// current bounds and animate. Otherwise, start the clip animation and set the
// bounds after the animation is complete.
if (grows)
SetBoundsRect(bounds);
// The origin of the clip rect needs to be shifted depending on whether we are
// growing or shrinking for right/bottom views since their animations are
// mirrored.
gfx::Point start_origin, end_origin;
const bool nix_animation =
*animation_type == SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET;
if (is_right_or_bottom_ || nix_animation) {
gfx::Vector2d clip_offset = bounds.origin() - old_bounds.origin();
// RTL is a special case since for the right highlight we will receive a
// mirrored rect whose origin will not change. In this case the clip rect
// offset should be the change in width. Portrait mode does not care since
// it is unaffected by RTL and the nix inset animation will supply the
// current bounds offset.
if (base::i18n::IsRTL() && SplitViewController::IsLayoutHorizontal() &&
!nix_animation) {
clip_offset = gfx::Vector2d(bounds.width() - old_bounds.width(), 0);
}
left_top_->SetBoundsRect(left_top_bounds);
right_bottom_->SetBoundsRect(right_bottom_bounds);
middle_->SetBoundsRect(middle_bounds);
clip_offset.set_x(std::abs(clip_offset.x()));
clip_offset.set_y(std::abs(clip_offset.y()));
if (grows)
start_origin += clip_offset;
else
end_origin += clip_offset;
}
}
void SplitViewHighlightView::SetColor(SkColor color) {
left_top_->SetBackgroundColor(color);
right_bottom_->SetBackgroundColor(color);
middle_->layer()->SetColor(color);
layer()->SetClipRect(gfx::Rect(start_origin, old_bounds.size()));
DoSplitviewClipRectAnimation(
layer(), *animation_type, gfx::Rect(end_origin, bounds.size()),
std::make_unique<ClippingObserver>(
this, grows ? base::nullopt : base::make_optional(bounds)));
}
void SplitViewHighlightView::OnWindowDraggingStateChanged(
......@@ -217,8 +157,8 @@ void SplitViewHighlightView::OnWindowDraggingStateChanged(
return;
}
// Set the color according to |can_dragged_window_be_snapped|.
SetColor(can_dragged_window_be_snapped ? SK_ColorWHITE : SK_ColorBLACK);
layer()->SetColor(can_dragged_window_be_snapped ? SK_ColorWHITE
: SK_ColorBLACK);
if (preview_position != SplitViewController::NONE) {
DoSplitviewOpacityAnimation(
......
......@@ -13,37 +13,23 @@
namespace ash {
class RoundedRectView;
class SplitViewHighlightViewTestApi;
// View that is used for displaying and animating the highlights which tell
// users where to drag windows to enter splitview, and previews the space which
// a snapped window will occupy. It is a view consisting of a rectangle with
// rounded corners on the left or top, a rectangle in the middle and a rectangle
// with rounded corners on the right or bottom. It is done this way to ensure
// rounded corners remain the same during the duration of an animation.
// (Transforming a rounded rect will stretch the corners, and having to repaint
// every animation tick is expensive.)
//
// Although rounded corners are prevented from stretching along one axis, there
// is one animation where the rounded corners will stretch along the
// perpendicular axis. Specifically, the preview area has a small inset (on all
// four sides) until you actually snap the window, and then the preview area
// animates to nix that inset while fading out. So the rounded corners will
// stretch by an amount depending on the dimensions of the work area, but it is
// unlikely to be noticeable under normal circumstances.
// a snapped window will occupy. It is a view consists of one solid color layer
// with rounded corners. If animations are needed, they are performed by
// animating the layer's clip rect.
class ASH_EXPORT SplitViewHighlightView : public views::View {
public:
explicit SplitViewHighlightView(bool is_right_or_bottom);
~SplitViewHighlightView() override;
SplitViewHighlightView(const SplitViewHighlightView&) = delete;
SplitViewHighlightView& operator=(const SplitViewHighlightView&) = delete;
// Updates bounds, animating if |animation_type| has a value.
void SetBounds(const gfx::Rect& bounds,
bool landscape,
const base::Optional<SplitviewAnimationType>& animation_type);
void SetColor(SkColor color);
// Called to update the opacity of the highlights view on transition from
// |previous_window_dragging_state| to |window_dragging_state|. If
// |previews_only|, then there shall be no visible drag indicators except for
......@@ -57,21 +43,11 @@ class ASH_EXPORT SplitViewHighlightView : public views::View {
bool can_dragged_window_be_snapped);
private:
friend class SplitViewHighlightViewTestApi;
// The three components of this view.
RoundedRectView* left_top_ = nullptr;
RoundedRectView* right_bottom_ = nullptr;
views::View* middle_ = nullptr;
bool landscape_ = true;
// Determines whether this particular highlight view is located at the right
// or bottom of the screen. These highlights animate in the opposite direction
// as left or top highlights, so when we use SetBounds extra calucations have
// to be done to ensure the animation is correct.
// as left or top highlights, so when we use |SetBounds()| extra calucations
// have to be done to ensure the animation is correct.
const bool is_right_or_bottom_;
DISALLOW_COPY_AND_ASSIGN(SplitViewHighlightView);
};
} // namespace ash
......
// 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/wm/splitview/split_view_highlight_view_test_api.h"
#include "ash/wm/overview/rounded_rect_view.h"
#include "ui/views/view.h"
namespace ash {
SplitViewHighlightViewTestApi::SplitViewHighlightViewTestApi(
SplitViewHighlightView* highlight_view)
: highlight_view_(highlight_view) {}
SplitViewHighlightViewTestApi::~SplitViewHighlightViewTestApi() = default;
views::View* SplitViewHighlightViewTestApi::GetLeftTopView() {
return static_cast<views::View*>(highlight_view_->left_top_);
}
views::View* SplitViewHighlightViewTestApi::GetRightBottomView() {
return static_cast<views::View*>(highlight_view_->right_bottom_);
}
views::View* SplitViewHighlightViewTestApi::GetMiddleView() {
return highlight_view_->middle_;
}
} // namespace ash
// 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.
#ifndef ASH_WM_SPLITVIEW_SPLIT_VIEW_HIGHLIGHT_VIEW_TEST_API_H_
#define ASH_WM_SPLITVIEW_SPLIT_VIEW_HIGHLIGHT_VIEW_TEST_API_H_
#include "ash/wm/splitview/split_view_highlight_view.h"
#include "base/macros.h"
namespace views {
class View;
} // namespace views
namespace ash {
// Use the api in this class to test SplitViewHighlightView.
class SplitViewHighlightViewTestApi {
public:
explicit SplitViewHighlightViewTestApi(
SplitViewHighlightView* highlight_view);
~SplitViewHighlightViewTestApi();
views::View* GetLeftTopView();
views::View* GetRightBottomView();
views::View* GetMiddleView();
private:
SplitViewHighlightView* highlight_view_;
DISALLOW_COPY_AND_ASSIGN(SplitViewHighlightViewTestApi);
};
} // namespace ash
#endif // ASH_WM_SPLITVIEW_SPLIT_VIEW_HIGHLIGHT_VIEW_TEST_API_H_
......@@ -4,32 +4,29 @@
#include "ash/wm/splitview/split_view_highlight_view.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/display/screen_orientation_controller_test_api.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/splitview/split_view_constants.h"
#include "ash/wm/splitview/split_view_highlight_view_test_api.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/test/icu_test_util.h"
#include "ui/gfx/transform.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/display/test/display_manager_test_api.h"
namespace ash {
namespace {
gfx::Transform GetTransform(views::View* view) {
DCHECK(view && view->layer());
return view->layer()->transform();
}
} // namespace
class SplitViewHighlightViewTest : public AshTestBase {
public:
SplitViewHighlightViewTest() = default;
~SplitViewHighlightViewTest() override = default;
SplitViewHighlightView* left_highlight() { return left_highlight_.get(); }
SplitViewHighlightView* right_highlight() { return right_highlight_.get(); }
SplitViewHighlightViewTest(const SplitViewHighlightViewTest&) = delete;
SplitViewHighlightViewTest& operator=(const SplitViewHighlightViewTest&) =
delete;
// test::AshTestBase:
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
......@@ -37,174 +34,150 @@ class SplitViewHighlightViewTest : public AshTestBase {
right_highlight_ = std::make_unique<SplitViewHighlightView>(true);
}
private:
void SetLeftBounds(const gfx::Rect& bounds, bool animate) {
SetBounds(bounds, /*is_left=*/true, animate);
}
void SetRightBounds(const gfx::Rect& bounds, bool animate) {
SetBounds(bounds, /*is_left=*/false, animate);
}
protected:
std::unique_ptr<SplitViewHighlightView> left_highlight_;
std::unique_ptr<SplitViewHighlightView> right_highlight_;
DISALLOW_COPY_AND_ASSIGN(SplitViewHighlightViewTest);
private:
void SetBounds(const gfx::Rect& bounds, bool is_left, bool animate) {
// The animation type only determines the duration and tween. For testing,
// any valid animation type would work.
auto animation_type =
animate ? base::make_optional(SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN)
: base::nullopt;
auto* highlight_view =
is_left ? left_highlight_.get() : right_highlight_.get();
highlight_view->SetBounds(bounds, animation_type);
}
};
// Tests setting and animating bounds for the split view highlight view in
// landscape mode.
TEST_F(SplitViewHighlightViewTest, LandscapeBounds) {
const gfx::Rect bounds(0, 0, 100, 100);
left_highlight()->SetBounds(bounds, /*landscape=*/true,
/*animation_type=*/base::nullopt);
// Tests that setting bounds without animations in landscape mode will set the
// bounds of the components correctly, without any transforms.
SplitViewHighlightViewTestApi test_api(left_highlight());
EXPECT_EQ(gfx::Rect(0, 0, 14, 100), test_api.GetLeftTopView()->bounds());
EXPECT_EQ(gfx::Rect(4, 0, 92, 100), test_api.GetMiddleView()->bounds());
EXPECT_EQ(gfx::Rect(86, 0, 14, 100), test_api.GetRightBottomView()->bounds());
EXPECT_TRUE(GetTransform(test_api.GetLeftTopView()).IsIdentity());
EXPECT_TRUE(GetTransform(test_api.GetMiddleView()).IsIdentity());
EXPECT_TRUE(GetTransform(test_api.GetRightBottomView()).IsIdentity());
// Tests that after animating to new bounds, the components have the same
// bounds, but have transforms.
const gfx::Rect new_bounds(0, 0, 200, 100);
left_highlight()->SetBounds(
new_bounds, /*landscape=*/true, /*animation_type=*/
base::make_optional(SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(gfx::Rect(0, 0, 14, 100), test_api.GetLeftTopView()->bounds());
EXPECT_EQ(gfx::Rect(4, 0, 92, 100), test_api.GetMiddleView()->bounds());
EXPECT_EQ(gfx::Rect(86, 0, 14, 100), test_api.GetRightBottomView()->bounds());
EXPECT_TRUE(GetTransform(test_api.GetLeftTopView()).IsIdentity());
gfx::Transform expected_middle_transform;
expected_middle_transform.Scale(2.16, 1);
EXPECT_TRUE(GetTransform(test_api.GetMiddleView())
.ApproximatelyEqual(expected_middle_transform));
gfx::Transform expected_end_transform;
expected_end_transform.Translate(100, 0);
EXPECT_TRUE(GetTransform(test_api.GetRightBottomView())
.ApproximatelyEqual(expected_end_transform));
TEST_F(SplitViewHighlightViewTest, HighlightGrows) {
ui::ScopedAnimationDurationScaleMode scoped_animation_duration(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Tests that before animating, we set the bounds to the desired bounds and
// clip the rect to the size of the old bounds.
gfx::Rect start_bounds(100, 100);
gfx::Rect end_bounds(200, 100);
SetLeftBounds(start_bounds, /*animate=*/false);
SetLeftBounds(end_bounds, /*animate=*/true);
EXPECT_EQ(end_bounds, left_highlight_->bounds());
EXPECT_EQ(start_bounds, left_highlight_->layer()->clip_rect());
// After the animation is finished the clip rect should be removed.
left_highlight_->layer()->GetAnimator()->StopAnimating();
EXPECT_EQ(gfx::Rect(), left_highlight_->layer()->clip_rect());
// Tests that for right highlights, the clip is shifted as the animation is
// mirrored.
start_bounds = gfx::Rect(100, 0, 100, 100);
end_bounds = gfx::Rect(200, 100);
SetRightBounds(start_bounds, /*animate=*/false);
SetRightBounds(end_bounds, /*animate=*/true);
EXPECT_EQ(end_bounds, right_highlight_->bounds());
EXPECT_EQ(start_bounds, right_highlight_->layer()->clip_rect());
// After the animation is finished the clip rect should be removed.
right_highlight_->layer()->GetAnimator()->StopAnimating();
EXPECT_EQ(gfx::Rect(), right_highlight_->layer()->clip_rect());
}
// Tests setting and animating bounds for the split view highlight view in
// landscape mode for rtl languages.
TEST_F(SplitViewHighlightViewTest, LandscapeBoundsInRtl) {
base::test::ScopedRestoreICUDefaultLocale scoped_locale("he");
const gfx::Rect bounds(0, 0, 100, 100);
left_highlight()->SetBounds(bounds, /*landscape=*/true,
/*animation_type=*/base::nullopt);
// Tests that setting bounds without animations in landscape mode will set the
// bounds of the components correctly, without any transforms. In rtl, the
// bounds of the outer components are swapped.
SplitViewHighlightViewTestApi test_api(left_highlight());
EXPECT_EQ(gfx::Rect(86, 0, 14, 100), test_api.GetLeftTopView()->bounds());
EXPECT_EQ(gfx::Rect(4, 0, 92, 100), test_api.GetMiddleView()->bounds());
EXPECT_EQ(gfx::Rect(0, 0, 14, 100), test_api.GetRightBottomView()->bounds());
EXPECT_TRUE(GetTransform(test_api.GetLeftTopView()).IsIdentity());
EXPECT_TRUE(GetTransform(test_api.GetMiddleView()).IsIdentity());
EXPECT_TRUE(GetTransform(test_api.GetRightBottomView()).IsIdentity());
// Tests that after animating to new bounds, the components have the same
// bounds, but have transforms. In rtl the beginning element is the one that
// is translated instead. The middle element has a extra translation in its
// transform to account for the flipped scaling.
const gfx::Rect new_bounds(0, 0, 200, 100);
left_highlight()->SetBounds(
new_bounds, /*landscape=*/true, /*animation_type=*/
base::make_optional(SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(gfx::Rect(86, 0, 14, 100), test_api.GetLeftTopView()->bounds());
EXPECT_EQ(gfx::Rect(4, 0, 92, 100), test_api.GetMiddleView()->bounds());
EXPECT_EQ(gfx::Rect(0, 0, 14, 100), test_api.GetRightBottomView()->bounds());
gfx::Transform expected_begin_transform;
expected_begin_transform.Translate(-100, 0);
EXPECT_TRUE(GetTransform(test_api.GetLeftTopView())
.ApproximatelyEqual(expected_begin_transform));
gfx::Transform expected_middle_transform;
expected_middle_transform.Translate(-100, 0);
expected_middle_transform.Scale(2.16, 1);
EXPECT_TRUE(GetTransform(test_api.GetMiddleView())
.ApproximatelyEqual(expected_middle_transform));
EXPECT_TRUE(GetTransform(test_api.GetRightBottomView()).IsIdentity());
TEST_F(SplitViewHighlightViewTest, HighlightShrinks) {
ui::ScopedAnimationDurationScaleMode scoped_animation_duration(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Tests that when the highlight shrinks, the bounds do not get set until the
// animation is complete.
gfx::Rect start_bounds(200, 100);
gfx::Rect end_bounds(100, 100);
SetLeftBounds(start_bounds, /*animate=*/false);
SetLeftBounds(end_bounds, /*animate=*/true);
EXPECT_EQ(start_bounds, left_highlight_->bounds());
EXPECT_EQ(start_bounds, left_highlight_->layer()->clip_rect());
// After the animation is finished the clip rect should be removed and the
// bounds should be set.
left_highlight_->layer()->GetAnimator()->StopAnimating();
EXPECT_EQ(end_bounds, left_highlight_->bounds());
EXPECT_EQ(gfx::Rect(), left_highlight_->layer()->clip_rect());
}
class SplitViewHighlightViewPortraitTest
: public SplitViewHighlightViewTest,
public testing::WithParamInterface<bool> {
public:
SplitViewHighlightViewPortraitTest()
: scoped_locale_(GetParam() ? "he" : "") {}
~SplitViewHighlightViewPortraitTest() override = default;
private:
// Restores locale to the default when destructor is called.
base::test::ScopedRestoreICUDefaultLocale scoped_locale_;
DISALLOW_COPY_AND_ASSIGN(SplitViewHighlightViewPortraitTest);
};
// Tests setting and animating bounds for the split view highlight view in
// portrait mode. The bounds should remain the same in ltr or rtl.
TEST_P(SplitViewHighlightViewPortraitTest, Bounds) {
const gfx::Rect bounds(0, 0, 100, 100);
left_highlight()->SetBounds(bounds, /*landscape=*/false,
/*animation_type=*/base::nullopt);
SplitViewHighlightViewTestApi test_api(left_highlight());
EXPECT_EQ(gfx::Rect(0, 0, 100, 14), test_api.GetLeftTopView()->bounds());
EXPECT_EQ(gfx::Rect(0, 4, 100, 92), test_api.GetMiddleView()->bounds());
EXPECT_EQ(gfx::Rect(0, 86, 100, 14), test_api.GetRightBottomView()->bounds());
const gfx::Rect new_bounds(0, 0, 100, 200);
left_highlight()->SetBounds(
new_bounds, /*landscape=*/false, /*animation_type=*/
base::make_optional(SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(gfx::Rect(0, 0, 100, 14), test_api.GetLeftTopView()->bounds());
EXPECT_EQ(gfx::Rect(0, 4, 100, 92), test_api.GetMiddleView()->bounds());
EXPECT_EQ(gfx::Rect(0, 86, 100, 14), test_api.GetRightBottomView()->bounds());
EXPECT_TRUE(GetTransform(test_api.GetLeftTopView()).IsIdentity());
gfx::Transform expected_middle_transform;
expected_middle_transform.Scale(1, 2.16);
EXPECT_TRUE(GetTransform(test_api.GetMiddleView())
.ApproximatelyEqual(expected_middle_transform));
gfx::Transform expected_end_transform;
expected_end_transform.Translate(0, 100);
EXPECT_TRUE(GetTransform(test_api.GetRightBottomView())
.ApproximatelyEqual(expected_end_transform));
TEST_F(SplitViewHighlightViewTest, PortraitMode) {
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
// Set display to portrait mode.
int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
display::DisplayManager* display_manager = Shell::Get()->display_manager();
display::test::ScopedSetInternalDisplayId set_internal(display_manager,
display_id);
ScreenOrientationControllerTestApi test_api(
Shell::Get()->screen_orientation_controller());
test_api.SetDisplayRotation(display::Display::ROTATE_90,
display::Display::RotationSource::ACTIVE);
ui::ScopedAnimationDurationScaleMode scoped_animation_duration(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
for (bool is_rtl : {false, true}) {
// RTL should not affect portrait highlights.
base::test::ScopedRestoreICUDefaultLocale scoped_locale(is_rtl ? "he"
: "en_US");
SCOPED_TRACE(is_rtl ? "RTL" : "LTR");
// Tests that before animating, we set the bounds to the desired bounds and
// clip the rect to the size of the old bounds.
gfx::Rect start_bounds(100, 100);
gfx::Rect end_bounds(100, 200);
SetLeftBounds(start_bounds, /*animate=*/false);
SetLeftBounds(end_bounds, /*animate=*/true);
EXPECT_EQ(end_bounds, left_highlight_->bounds());
EXPECT_EQ(start_bounds, left_highlight_->layer()->clip_rect());
// After the animation is finished the clip rect should be removed.
left_highlight_->layer()->GetAnimator()->StopAnimating();
EXPECT_EQ(gfx::Rect(), left_highlight_->layer()->clip_rect());
// Tests that for bottom highlights, the clip is shifted as the animation is
// comes from bottom up instead of top down.
start_bounds = gfx::Rect(0, 100, 100, 100);
end_bounds = gfx::Rect(200, 100);
SetRightBounds(start_bounds, /*animate=*/false);
SetRightBounds(end_bounds, /*animate=*/true);
EXPECT_EQ(end_bounds, right_highlight_->bounds());
EXPECT_EQ(start_bounds, right_highlight_->layer()->clip_rect());
// After the animation is finished the clip rect should be removed.
right_highlight_->layer()->GetAnimator()->StopAnimating();
EXPECT_EQ(gfx::Rect(), right_highlight_->layer()->clip_rect());
}
}
INSTANTIATE_TEST_SUITE_P(Bounds,
SplitViewHighlightViewPortraitTest,
testing::Bool());
TEST_F(SplitViewHighlightViewTest, RightBounds) {
const gfx::Rect bounds(100, 0, 100, 100);
right_highlight()->SetBounds(bounds, /*landscape=*/true,
/*animation_type=*/base::nullopt);
SplitViewHighlightViewTestApi test_api(right_highlight());
EXPECT_EQ(gfx::Rect(0, 0, 14, 100), test_api.GetLeftTopView()->bounds());
EXPECT_EQ(gfx::Rect(4, 0, 92, 100), test_api.GetMiddleView()->bounds());
EXPECT_EQ(gfx::Rect(86, 0, 14, 100), test_api.GetRightBottomView()->bounds());
const gfx::Rect new_bounds(0, 0, 200, 100);
right_highlight()->SetBounds(
new_bounds, /*landscape=*/true, /*animation_type=*/
base::make_optional(SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(gfx::Rect(100, 0, 14, 100), test_api.GetLeftTopView()->bounds());
EXPECT_EQ(gfx::Rect(104, 0, 92, 100), test_api.GetMiddleView()->bounds());
EXPECT_EQ(gfx::Rect(186, 0, 14, 100),
test_api.GetRightBottomView()->bounds());
gfx::Transform expected_begin_transform;
expected_begin_transform.Translate(-100, 0);
EXPECT_TRUE(GetTransform(test_api.GetLeftTopView())
.ApproximatelyEqual(expected_begin_transform));
gfx::Transform expected_middle_transform;
expected_middle_transform.Translate(-100, 0);
expected_middle_transform.Scale(2.16, 1);
EXPECT_TRUE(GetTransform(test_api.GetMiddleView())
.ApproximatelyEqual(expected_middle_transform));
EXPECT_TRUE(GetTransform(test_api.GetRightBottomView()).IsIdentity());
// Tests that the highlights work as in expected in RTL.
TEST_F(SplitViewHighlightViewTest, HighlightInRtl) {
base::test::ScopedRestoreICUDefaultLocale scoped_locale("he");
ui::ScopedAnimationDurationScaleMode scoped_animation_duration(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// In RTL, the right highlight gets mirrored bounds, so its start and end
// bounds will have the same origin.
const gfx::Rect start_bounds(0, 0, 100, 100);
const gfx::Rect end_bounds(0, 0, 200, 100);
SetRightBounds(start_bounds, /*animate=*/false);
SetRightBounds(end_bounds, /*animate=*/true);
EXPECT_EQ(end_bounds, right_highlight_->bounds());
EXPECT_EQ(gfx::Rect(100, 0, 100, 100),
right_highlight_->layer()->clip_rect());
right_highlight_->layer()->GetAnimator()->StopAnimating();
EXPECT_EQ(gfx::Rect(), right_highlight_->layer()->clip_rect());
}
} // namespace ash
......@@ -243,13 +243,9 @@ void DoSplitviewTransformAnimation(
return;
switch (type) {
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_IN:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_OUT:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_IN:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_OUT:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_OUT:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_IN:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_OUT:
case SPLITVIEW_ANIMATION_SET_WINDOW_TRANSFORM:
......@@ -276,6 +272,42 @@ void DoSplitviewTransformAnimation(
layer->SetTransform(target_transform);
}
void DoSplitviewClipRectAnimation(
ui::Layer* layer,
SplitviewAnimationType type,
const gfx::Rect& target_clip_rect,
std::unique_ptr<ui::ImplicitAnimationObserver> animation_observer) {
ui::LayerAnimator* animator = layer->GetAnimator();
if (animator->GetTargetClipRect() == target_clip_rect)
return;
switch (type) {
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_IN:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_OUT:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_OUT:
break;
default:
NOTREACHED() << "Not a valid split view clip rect type.";
return;
}
base::TimeDelta duration;
gfx::Tween::Type tween;
ui::LayerAnimator::PreemptionStrategy preemption_strategy;
base::TimeDelta delay;
GetAnimationValuesForType(type, &duration, &tween, &preemption_strategy,
&delay);
ui::ScopedLayerAnimationSettings settings(animator);
if (animation_observer.get())
settings.AddObserver(animation_observer.release());
ApplyAnimationSettings(&settings, animator, ui::LayerAnimationElement::CLIP,
duration, tween, preemption_strategy, delay);
layer->SetClipRect(target_clip_rect);
}
void MaybeRestoreSplitView(bool refresh_snapped_windows) {
if (!ShouldAllowSplitView() ||
!Shell::Get()->tablet_mode_controller()->InTabletMode()) {
......
......@@ -112,6 +112,13 @@ void DoSplitviewTransformAnimation(
const gfx::Transform& target_transform,
std::unique_ptr<ui::ImplicitAnimationObserver> animation_observer);
// Animates |layer|'s clip rect based on |type|.
void DoSplitviewClipRectAnimation(
ui::Layer* layer,
SplitviewAnimationType type,
const gfx::Rect& target_clip_rect,
std::unique_ptr<ui::ImplicitAnimationObserver> animation_observer);
// Restores split view and overview based on the current split view's state.
// If |refresh_snapped_windows| is true, it will update the left and right
// snapped windows based on the MRU windows snapped states.
......
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