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