| // Copyright 2019 The Chromium Authors |
| // 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/style/ash_color_id.h" |
| #include "ash/wm/splitview/split_view_constants.h" |
| #include "ash/wm/splitview/split_view_utils.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/timer/timer.h" |
| #include "chromeos/constants/chromeos_features.h" |
| #include "ui/chromeos/styles/cros_tokens_color_mappings.h" |
| #include "ui/color/color_id.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/gfx/animation/animation_delegate.h" |
| #include "ui/gfx/animation/slide_animation.h" |
| #include "ui/views/background.h" |
| |
| namespace ash { |
| |
| 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(kSplitviewDividerSelectionStatusChangeDuration); |
| SetTweenType(gfx::Tween::EASE_IN); |
| } |
| |
| SelectionAnimation(const SelectionAnimation&) = delete; |
| SelectionAnimation& operator=(const SelectionAnimation&) = delete; |
| |
| ~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_->UpdateCornerRadius(CurrentValueBetween( |
| kSplitviewWhiteBarCornerRadius, kSplitviewWhiteBarRadius)); |
| } |
| |
| raw_ptr<SplitViewDividerHandlerView, ExperimentalAsh> white_handler_view_; |
| }; |
| |
| 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(kSplitviewDividerSpawnDuration); |
| SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN); |
| } |
| |
| SpawningAnimation(const SpawningAnimation&) = delete; |
| SpawningAnimation& operator=(const SpawningAnimation&) = delete; |
| |
| ~SpawningAnimation() override = default; |
| |
| void Activate() { |
| white_handler_view_->SetVisible(false); |
| delay_timer_.Start(FROM_HERE, kSplitviewDividerSpawnDelay, 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_->GetVisible()); |
| 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(); |
| } |
| |
| raw_ptr<SplitViewDividerHandlerView, ExperimentalAsh> white_handler_view_; |
| int spawn_signed_offset_; |
| base::OneShotTimer delay_timer_; |
| }; |
| |
| SplitViewDividerHandlerView::SplitViewDividerHandlerView() |
| : selection_animation_(std::make_unique<SelectionAnimation>(this)) { |
| SetPaintToLayer(); |
| SetBackground(views::CreateThemedRoundedRectBackground( |
| chromeos::features::IsJellyrollEnabled() |
| ? static_cast<ui::ColorId>(cros_tokens::kCrosSysOnSurface) |
| : kColorAshIconColorPrimary, |
| kSplitviewWhiteBarCornerRadius)); |
| } |
| |
| 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(bool is_resizing) { |
| spawning_animation_.reset(); |
| SetVisible(true); |
| selection_animation_->UpdateWhiteHandlerBounds(); |
| if (is_resizing) |
| selection_animation_->Show(); |
| else |
| selection_animation_->Hide(); |
| } |
| |
| void SplitViewDividerHandlerView::UpdateCornerRadius(float radius) { |
| layer()->SetRoundedCornerRadius(gfx::RoundedCornersF{radius}); |
| } |
| |
| 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); |
| views::View::OnPaint(canvas); |
| } |
| |
| } // namespace ash |