touch_hud_renderer.cc 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // Copyright 2013 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/touch/touch_hud_renderer.h"
  5. #include <memory>
  6. #include "base/scoped_observation.h"
  7. #include "base/time/time.h"
  8. #include "third_party/skia/include/effects/SkGradientShader.h"
  9. #include "ui/compositor/layer.h"
  10. #include "ui/compositor/layer_owner.h"
  11. #include "ui/events/event.h"
  12. #include "ui/gfx/animation/linear_animation.h"
  13. #include "ui/gfx/canvas.h"
  14. #include "ui/gfx/geometry/size.h"
  15. #include "ui/gfx/geometry/skia_conversions.h"
  16. #include "ui/views/animation/animation_delegate_views.h"
  17. #include "ui/views/view.h"
  18. #include "ui/views/widget/widget.h"
  19. #include "ui/views/widget/widget_observer.h"
  20. namespace ash {
  21. constexpr int kPointRadius = 20;
  22. constexpr SkColor4f kProjectionFillColor{0.96f, 0.96f, 0.86f, 1.0f};
  23. constexpr SkColor4f kProjectionStrokeColor = SkColors::kGray;
  24. constexpr int kProjectionAlpha = 0xB0;
  25. constexpr base::TimeDelta kFadeoutDuration = base::Milliseconds(250);
  26. constexpr int kFadeoutFrameRate = 60;
  27. // TouchPointView draws a single touch point.
  28. class TouchPointView : public views::View,
  29. public views::AnimationDelegateViews,
  30. public views::WidgetObserver {
  31. public:
  32. explicit TouchPointView(views::Widget* parent_widget)
  33. : views::AnimationDelegateViews(this) {
  34. SetPaintToLayer();
  35. layer()->SetFillsBoundsOpaquely(false);
  36. SetSize(gfx::Size(2 * kPointRadius + 2, 2 * kPointRadius + 2));
  37. widget_observation_.Observe(parent_widget);
  38. }
  39. TouchPointView(const TouchPointView&) = delete;
  40. TouchPointView& operator=(const TouchPointView&) = delete;
  41. ~TouchPointView() override = default;
  42. // Begins fadeout animation. After this is called, |this| owns itself and is
  43. // responsible for deleting itself when the animation ends or the host widget
  44. // is destroyed.
  45. void FadeOut(std::unique_ptr<TouchPointView> self) {
  46. DCHECK_EQ(this, self.get());
  47. owned_self_reference_ = std::move(self);
  48. fadeout_ = std::make_unique<gfx::LinearAnimation>(kFadeoutDuration,
  49. kFadeoutFrameRate, this);
  50. fadeout_->Start();
  51. }
  52. void UpdateLocation(const ui::LocatedEvent& touch) {
  53. SetPosition(
  54. gfx::Point(parent()->GetMirroredXInView(touch.root_location().x()) -
  55. kPointRadius - 1,
  56. touch.root_location().y() - kPointRadius - 1));
  57. }
  58. private:
  59. // views::View:
  60. void OnPaint(gfx::Canvas* canvas) override {
  61. int alpha = kProjectionAlpha;
  62. if (fadeout_)
  63. alpha = static_cast<int>(fadeout_->CurrentValueBetween(alpha, 0));
  64. cc::PaintFlags fill_flags;
  65. fill_flags.setAlpha(alpha);
  66. constexpr SkColor4f gradient_colors[2] = {kProjectionFillColor,
  67. kProjectionStrokeColor};
  68. constexpr SkScalar gradient_pos[2] = {SkFloatToScalar(0.9f),
  69. SkFloatToScalar(1.0f)};
  70. constexpr gfx::Point center(kPointRadius + 1, kPointRadius + 1);
  71. fill_flags.setShader(cc::PaintShader::MakeRadialGradient(
  72. gfx::PointToSkPoint(center), SkIntToScalar(kPointRadius),
  73. gradient_colors, gradient_pos, std::size(gradient_colors),
  74. SkTileMode::kMirror));
  75. canvas->DrawCircle(center, SkIntToScalar(kPointRadius), fill_flags);
  76. cc::PaintFlags stroke_flags;
  77. stroke_flags.setStyle(cc::PaintFlags::kStroke_Style);
  78. stroke_flags.setColor(kProjectionStrokeColor);
  79. stroke_flags.setAlpha(alpha);
  80. canvas->DrawCircle(center, SkIntToScalar(kPointRadius), stroke_flags);
  81. }
  82. // views::AnimationDelegateViews:
  83. void AnimationEnded(const gfx::Animation* animation) override {
  84. DCHECK_EQ(fadeout_.get(), animation);
  85. owned_self_reference_.reset();
  86. }
  87. void AnimationProgressed(const gfx::Animation* animation) override {
  88. DCHECK_EQ(fadeout_.get(), animation);
  89. SchedulePaint();
  90. }
  91. void AnimationCanceled(const gfx::Animation* animation) override {
  92. AnimationEnded(animation);
  93. }
  94. // views::WidgetObserver:
  95. void OnWidgetDestroying(views::Widget* widget) override {
  96. owned_self_reference_.reset();
  97. }
  98. std::unique_ptr<gfx::Animation> fadeout_;
  99. // When non-null, |owned_self_reference_| refers to |this|, and |this| owns
  100. // itself. This should be non-null when fading out, and null otherwise.
  101. std::unique_ptr<TouchPointView> owned_self_reference_;
  102. base::ScopedObservation<views::Widget, views::WidgetObserver>
  103. widget_observation_{this};
  104. };
  105. TouchHudRenderer::TouchHudRenderer(views::Widget* parent_widget)
  106. : parent_widget_(parent_widget) {
  107. parent_widget_->AddObserver(this);
  108. }
  109. TouchHudRenderer::~TouchHudRenderer() {
  110. if (parent_widget_)
  111. parent_widget_->RemoveObserver(this);
  112. CHECK(!IsInObserverList());
  113. }
  114. void TouchHudRenderer::Clear() {
  115. points_.clear();
  116. }
  117. void TouchHudRenderer::HandleTouchEvent(const ui::TouchEvent& event) {
  118. int id = event.pointer_details().id;
  119. auto iter = points_.find(id);
  120. if (event.type() == ui::ET_TOUCH_PRESSED) {
  121. if (iter != points_.end()) {
  122. TouchPointView* view = iter->second;
  123. view->parent()->RemoveChildViewT(view);
  124. points_.erase(iter);
  125. }
  126. TouchPointView* point = parent_widget_->GetContentsView()->AddChildView(
  127. std::make_unique<TouchPointView>(parent_widget_));
  128. point->UpdateLocation(event);
  129. auto result = points_.insert(std::make_pair(id, point));
  130. DCHECK(result.second);
  131. return;
  132. }
  133. if (iter == points_.end())
  134. return;
  135. if (event.type() == ui::ET_TOUCH_RELEASED ||
  136. event.type() == ui::ET_TOUCH_CANCELLED) {
  137. TouchPointView* view = iter->second;
  138. view->FadeOut(view->parent()->RemoveChildViewT(view));
  139. points_.erase(iter);
  140. } else {
  141. iter->second->UpdateLocation(event);
  142. }
  143. }
  144. void TouchHudRenderer::OnWidgetDestroying(views::Widget* widget) {
  145. DCHECK_EQ(widget, parent_widget_);
  146. parent_widget_->RemoveObserver(this);
  147. parent_widget_ = nullptr;
  148. Clear();
  149. }
  150. } // namespace ash