pulsing_block_view.cc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/app_list/views/pulsing_block_view.h"
  5. #include <stddef.h>
  6. #include <memory>
  7. #include <vector>
  8. #include "ash/constants/ash_features.h"
  9. #include "ash/style/ash_color_provider.h"
  10. #include "ash/style/dark_light_mode_controller_impl.h"
  11. #include "base/check_op.h"
  12. #include "base/rand_util.h"
  13. #include "third_party/skia/include/core/SkColor.h"
  14. #include "ui/compositor/layer.h"
  15. #include "ui/compositor/layer_animation_element.h"
  16. #include "ui/compositor/layer_animation_sequence.h"
  17. #include "ui/compositor/layer_animator.h"
  18. #include "ui/gfx/canvas.h"
  19. #include "ui/gfx/geometry/transform.h"
  20. #include "ui/gfx/geometry/transform_util.h"
  21. #include "ui/views/animation/animation_builder.h"
  22. #include "ui/views/animation/animation_sequence_block.h"
  23. #include "ui/views/background.h"
  24. #include "ui/views/layout/box_layout.h"
  25. #include "ui/views/layout/fill_layout.h"
  26. namespace {
  27. const base::TimeDelta kPulsingDuration = base::Milliseconds(500);
  28. const SkColor kBlockColor = SkColorSetRGB(225, 225, 225);
  29. const int kBlockSize = 64;
  30. const int kAnimationDurationInMs = 600;
  31. const float kAnimationOpacity[] = {0.4f, 0.8f, 0.4f};
  32. const float kAnimationScale[] = {0.8f, 1.0f, 0.8f};
  33. void SchedulePulsingAnimation(ui::Layer* layer) {
  34. DCHECK(layer);
  35. DCHECK_EQ(std::size(kAnimationOpacity), std::size(kAnimationScale));
  36. const gfx::Rect local_bounds(layer->bounds().size());
  37. views::AnimationBuilder builder;
  38. builder.Repeatedly();
  39. for (size_t i = 0; i < std::size(kAnimationOpacity); ++i) {
  40. builder.GetCurrentSequence()
  41. .SetDuration(base::Milliseconds(kAnimationDurationInMs))
  42. .SetOpacity(layer, kAnimationOpacity[i])
  43. .SetTransform(layer, gfx::GetScaleTransform(local_bounds.CenterPoint(),
  44. kAnimationScale[i]))
  45. .Then();
  46. }
  47. builder.GetCurrentSequence().SetDuration(
  48. base::Milliseconds(kAnimationDurationInMs));
  49. }
  50. void ScheduleNewPulsingAnimation(ui::Layer* layer) {
  51. DCHECK(layer);
  52. views::AnimationBuilder()
  53. .SetPreemptionStrategy(
  54. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
  55. .Repeatedly()
  56. .SetDuration(kPulsingDuration)
  57. .SetOpacity(layer, 0.5f, gfx::Tween::FAST_OUT_LINEAR_IN)
  58. .At(kPulsingDuration)
  59. .SetDuration(kPulsingDuration)
  60. .SetOpacity(layer, 1.0f, gfx::Tween::LINEAR);
  61. }
  62. } // namespace
  63. namespace ash {
  64. PulsingBlockView::PulsingBlockView(const gfx::Size& size,
  65. base::TimeDelta animation_delay)
  66. : block_size_(size) {
  67. if (ash::features::IsProductivityLauncherEnabled()) {
  68. views::BoxLayout* layout_manager =
  69. SetLayoutManager(std::make_unique<views::BoxLayout>(
  70. views::BoxLayout::Orientation::kHorizontal));
  71. layout_manager->set_cross_axis_alignment(
  72. views::BoxLayout::CrossAxisAlignment::kCenter);
  73. layout_manager->set_main_axis_alignment(
  74. views::BoxLayout::MainAxisAlignment::kCenter);
  75. // Stack two views for the same block. The bottom view contains the
  76. // background blur, which will be visible all the time. The top view
  77. // contains the color of the block, which will animate its opacity.
  78. views::View* stacked_views = AddChildView(
  79. views::Builder<views::View>()
  80. .SetVisible(true)
  81. .SetLayoutManager(std::make_unique<views::FillLayout>())
  82. .AddChild(
  83. views::Builder<views::View>()
  84. .CopyAddressTo(&background_color_view_)
  85. .SetEnabled(false)
  86. .SetBackground(views::CreateSolidBackground(
  87. DarkLightModeControllerImpl::Get()->IsDarkModeEnabled()
  88. ? SkColorSetA(SK_ColorWHITE, 0x4D)
  89. : SkColorSetA(SK_ColorBLACK, 0x33)))
  90. .SetPreferredSize(block_size_))
  91. .SetPreferredSize(block_size_)
  92. .SetPaintToLayer()
  93. .Build());
  94. stacked_views->layer()->SetMasksToBounds(true);
  95. stacked_views->layer()->SetBackgroundBlur(
  96. ColorProvider::kBackgroundBlurSigma);
  97. stacked_views->layer()->SetBackdropFilterQuality(
  98. ColorProvider::kBackgroundBlurQuality);
  99. const float radii = block_size_.height() / 2.0f;
  100. stacked_views->layer()->SetRoundedCornerRadius(
  101. {radii, radii, radii, radii});
  102. start_delay_timer_.Start(FROM_HERE, animation_delay, this,
  103. &PulsingBlockView::OnStartDelayTimer);
  104. } else {
  105. SetPaintToLayer();
  106. layer()->SetFillsBoundsOpaquely(false);
  107. const int max_delay = kAnimationDurationInMs * std::size(kAnimationOpacity);
  108. const int delay = base::RandInt(0, max_delay);
  109. start_delay_timer_.Start(FROM_HERE, base::Milliseconds(delay), this,
  110. &PulsingBlockView::OnStartDelayTimer);
  111. }
  112. }
  113. PulsingBlockView::~PulsingBlockView() {}
  114. const char* PulsingBlockView::GetClassName() const {
  115. return "PulsingBlockView";
  116. }
  117. void PulsingBlockView::OnStartDelayTimer() {
  118. if (!ash::features::IsProductivityLauncherEnabled()) {
  119. SchedulePulsingAnimation(layer());
  120. return;
  121. }
  122. background_color_view_->SetPaintToLayer();
  123. background_color_view_->layer()->SetFillsBoundsOpaquely(false);
  124. ScheduleNewPulsingAnimation(background_color_view_->layer());
  125. }
  126. void PulsingBlockView::OnThemeChanged() {
  127. views::View::OnThemeChanged();
  128. if (!ash::features::IsProductivityLauncherEnabled())
  129. return;
  130. if (background_color_view_) {
  131. background_color_view_->SetBackground(views::CreateSolidBackground(
  132. DarkLightModeControllerImpl::Get()->IsDarkModeEnabled()
  133. ? SkColorSetA(SK_ColorWHITE, 0x4D)
  134. : SkColorSetA(SK_ColorBLACK, 0x33)));
  135. }
  136. }
  137. void PulsingBlockView::OnPaint(gfx::Canvas* canvas) {
  138. if (ash::features::IsProductivityLauncherEnabled()) {
  139. views::View::OnPaint(canvas);
  140. return;
  141. }
  142. gfx::Rect rect(GetContentsBounds());
  143. rect.ClampToCenteredSize(gfx::Size(kBlockSize, kBlockSize));
  144. canvas->FillRect(rect, kBlockColor);
  145. }
  146. bool PulsingBlockView::IsAnimating() {
  147. views::View* animating_view = ash::features::IsProductivityLauncherEnabled()
  148. ? background_color_view_
  149. : this;
  150. return animating_view->layer()
  151. ? animating_view->layer()->GetAnimator()->is_animating()
  152. : false;
  153. }
  154. bool PulsingBlockView::FireAnimationTimerForTest() {
  155. if (!start_delay_timer_.IsRunning())
  156. return false;
  157. start_delay_timer_.FireNow();
  158. return true;
  159. }
  160. } // namespace ash