OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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 "chrome/browser/chromeos/ui/autoclick_ring_handler.h" | |
6 | |
7 #include <memory> | |
8 | |
9 #include "ash/aura/wm_window_aura.h" | |
10 #include "ash/common/shell_window_ids.h" | |
11 #include "ash/common/wm/root_window_finder.h" | |
12 #include "ash/root_window_controller.h" | |
13 #include "ash/shell.h" | |
14 #include "third_party/skia/include/core/SkColor.h" | |
15 #include "third_party/skia/include/core/SkPaint.h" | |
16 #include "third_party/skia/include/core/SkPath.h" | |
17 #include "third_party/skia/include/core/SkRect.h" | |
18 #include "ui/aura/client/screen_position_client.h" | |
19 #include "ui/aura/env.h" | |
20 #include "ui/aura/window.h" | |
21 #include "ui/aura/window_event_dispatcher.h" | |
22 #include "ui/compositor/layer.h" | |
23 #include "ui/gfx/canvas.h" | |
24 #include "ui/gfx/transform.h" | |
25 #include "ui/views/view.h" | |
26 #include "ui/views/widget/widget.h" | |
27 #include "ui/wm/core/coordinate_conversion.h" | |
28 | |
29 namespace chromeos { | |
30 namespace { | |
31 | |
32 const int kAutoclickRingOuterRadius = 30; | |
33 const int kAutoclickRingInnerRadius = 20; | |
34 | |
35 // Angles from x-axis at which the outer and inner circles start. | |
36 const int kAutoclickRingInnerStartAngle = -90; | |
37 | |
38 const int kAutoclickRingGlowWidth = 20; | |
39 // The following is half width to avoid division by 2. | |
40 const int kAutoclickRingArcWidth = 2; | |
41 | |
42 // Start and end values for various animations. | |
43 const double kAutoclickRingScaleStartValue = 1.0; | |
44 const double kAutoclickRingScaleEndValue = 1.0; | |
45 const double kAutoclickRingShrinkScaleEndValue = 0.5; | |
46 | |
47 const double kAutoclickRingOpacityStartValue = 0.1; | |
48 const double kAutoclickRingOpacityEndValue = 0.5; | |
49 const int kAutoclickRingAngleStartValue = -90; | |
50 // The sweep angle is a bit greater than 360 to make sure the circle | |
51 // completes at the end of the animation. | |
52 const int kAutoclickRingAngleEndValue = 360; | |
53 | |
54 // Visual constants. | |
55 const SkColor kAutoclickRingArcColor = SkColorSetARGB(255, 0, 255, 0); | |
56 const SkColor kAutoclickRingCircleColor = SkColorSetARGB(255, 0, 0, 255); | |
57 const int kAutoclickRingFrameRateHz = 60; | |
58 | |
59 views::Widget* CreateAutoclickRingWidget(aura::Window* root_window) { | |
60 views::Widget* widget = new views::Widget; | |
61 views::Widget::InitParams params; | |
62 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; | |
63 params.keep_on_top = true; | |
oshima
2016/06/15 21:26:28
nit: you don't need this.
sammiequon
2016/06/15 23:19:59
Done.
| |
64 params.accept_events = false; | |
65 params.activatable = views::Widget::InitParams::ACTIVATABLE_NO; | |
66 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
67 params.context = root_window; | |
68 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; | |
69 params.parent = ash::Shell::GetContainer( | |
70 root_window, ash::kShellWindowId_OverlayContainer); | |
71 | |
72 widget->Init(params); | |
73 widget->SetOpacity(1.f); | |
74 return widget; | |
75 } | |
76 | |
77 void PaintAutoclickRingCircle(gfx::Canvas* canvas, | |
78 gfx::Point& center, | |
79 int radius) { | |
80 SkPaint paint; | |
81 paint.setStyle(SkPaint::kStroke_Style); | |
82 paint.setStrokeWidth(2 * kAutoclickRingArcWidth); | |
83 paint.setColor(kAutoclickRingCircleColor); | |
84 paint.setAntiAlias(true); | |
85 | |
86 canvas->DrawCircle(center, radius, paint); | |
87 } | |
oshima
2016/06/15 21:26:28
nit: new line
sammiequon
2016/06/15 23:19:59
Done.
| |
88 void PaintAutoclickRingArc(gfx::Canvas* canvas, | |
89 gfx::Point& center, | |
oshima
2016/06/15 21:26:28
const gfx::Point&
sammiequon
2016/06/15 23:19:59
Done.
| |
90 int radius, | |
91 int start_angle, | |
92 int end_angle) { | |
93 SkPaint paint; | |
94 paint.setStyle(SkPaint::kStroke_Style); | |
95 paint.setStrokeWidth(2 * kAutoclickRingArcWidth); | |
96 paint.setColor(kAutoclickRingArcColor); | |
97 paint.setAntiAlias(true); | |
98 | |
99 SkPath arc_path; | |
100 arc_path.addArc(SkRect::MakeXYWH(center.x() - radius, center.y() - radius, | |
101 2 * radius, 2 * radius), | |
102 start_angle, end_angle - start_angle); | |
103 canvas->DrawPath(arc_path, paint); | |
104 } | |
105 } // namespace | |
106 | |
107 // View of the AutoclickRingHandler. Draws the actual contents and updates as | |
108 // the animation proceeds. It also maintains the views::Widget that the | |
109 // animation is shown in. | |
110 class AutoclickRingHandler::AutoclickRingView : public views::View { | |
111 public: | |
112 AutoclickRingView(const gfx::Point& event_location, aura::Window* root_window) | |
113 : views::View(), | |
114 widget_(CreateAutoclickRingWidget(root_window)), | |
115 current_angle_(kAutoclickRingAngleStartValue), | |
116 current_scale_(kAutoclickRingScaleStartValue) { | |
117 widget_->SetContentsView(this); | |
118 | |
119 // We are owned by the AutoclickRingHandler. | |
120 set_owned_by_client(); | |
121 SetNewLocation(event_location, root_window); | |
122 } | |
123 | |
124 ~AutoclickRingView() override {} | |
125 | |
126 void SetNewLocation(const gfx::Point& new_event_location, | |
127 aura::Window* root_window) { | |
128 gfx::Point point = new_event_location; | |
129 widget_->SetBounds(gfx::Rect( | |
130 point.x() - (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth), | |
131 point.y() - (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth), | |
132 GetPreferredSize().width(), GetPreferredSize().height())); | |
133 widget_->Show(); | |
134 widget_->GetNativeView()->layer()->SetOpacity( | |
135 kAutoclickRingOpacityStartValue); | |
136 } | |
137 | |
138 void UpdateWithGrowAnimation(gfx::Animation* animation) { | |
139 // Update the portion of the circle filled so far and re-draw. | |
140 current_angle_ = animation->CurrentValueBetween( | |
141 kAutoclickRingInnerStartAngle, kAutoclickRingAngleEndValue); | |
142 current_scale_ = animation->CurrentValueBetween( | |
143 kAutoclickRingScaleStartValue, kAutoclickRingScaleEndValue); | |
144 widget_->GetNativeView()->layer()->SetOpacity( | |
145 animation->CurrentValueBetween(kAutoclickRingOpacityStartValue, | |
146 kAutoclickRingOpacityEndValue)); | |
147 SchedulePaint(); | |
148 } | |
149 | |
150 void UpdateWithShrinkAnimation(gfx::Animation* animation) { | |
151 current_angle_ = animation->CurrentValueBetween( | |
152 kAutoclickRingInnerStartAngle, kAutoclickRingAngleEndValue); | |
153 current_scale_ = animation->CurrentValueBetween( | |
154 kAutoclickRingScaleEndValue, kAutoclickRingShrinkScaleEndValue); | |
155 widget_->GetNativeView()->layer()->SetOpacity( | |
156 animation->CurrentValueBetween(kAutoclickRingOpacityStartValue, | |
157 kAutoclickRingOpacityEndValue)); | |
158 SchedulePaint(); | |
159 } | |
160 | |
161 private: | |
162 // Overridden from views::View. | |
163 gfx::Size GetPreferredSize() const override { | |
164 return gfx::Size(2 * (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth), | |
165 2 * (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth)); | |
166 } | |
167 | |
168 void OnPaint(gfx::Canvas* canvas) override { | |
169 gfx::Point center(GetPreferredSize().width() / 2, | |
170 GetPreferredSize().height() / 2); | |
171 canvas->Save(); | |
172 | |
173 gfx::Transform scale; | |
174 scale.Scale(current_scale_, current_scale_); | |
175 // We want to scale from the center. | |
176 canvas->Translate(center.OffsetFromOrigin()); | |
177 canvas->Transform(scale); | |
178 canvas->Translate(-center.OffsetFromOrigin()); | |
179 | |
180 // Paint inner circle. | |
181 PaintAutoclickRingArc(canvas, center, kAutoclickRingInnerRadius, | |
182 kAutoclickRingInnerStartAngle, current_angle_); | |
183 // Paint outer circle. | |
184 PaintAutoclickRingCircle(canvas, center, kAutoclickRingOuterRadius); | |
185 | |
186 canvas->Restore(); | |
187 } | |
188 | |
189 std::unique_ptr<views::Widget> widget_; | |
190 int current_angle_; | |
191 double current_scale_; | |
192 | |
193 DISALLOW_COPY_AND_ASSIGN(AutoclickRingView); | |
194 }; | |
195 | |
196 //////////////////////////////////////////////////////////////////////////////// | |
197 | |
198 // AutoclickRingHandler, public | |
199 AutoclickRingHandler::AutoclickRingHandler() | |
200 : gfx::LinearAnimation(kAutoclickRingFrameRateHz, nullptr), | |
201 tap_down_target_(nullptr), | |
202 current_animation_type_(AnimationType::NONE) {} | |
203 | |
204 AutoclickRingHandler::~AutoclickRingHandler() { | |
205 StopAutoclickRing(); | |
206 } | |
207 | |
208 void AutoclickRingHandler::StartGesture(base::TimeDelta duration, | |
209 gfx::Point center) { | |
210 aura::Window* target = GetTargetWindow(); | |
211 if (tap_down_target_ && tap_down_target_ != target) | |
212 return; | |
213 StopAutoclickRing(); | |
214 SetTapDownTarget(); | |
215 tap_down_location_ = center; | |
216 current_animation_type_ = AnimationType::GROW_ANIMATION; | |
217 animation_duration_ = duration; | |
218 StartAnimation(base::TimeDelta()); | |
219 } | |
220 | |
221 void AutoclickRingHandler::StopGesture() { | |
222 aura::Window* target = GetTargetWindow(); | |
223 if (tap_down_target_ && tap_down_target_ != target) | |
224 return; | |
225 StopAutoclickRing(); | |
226 } | |
227 | |
228 void AutoclickRingHandler::SetGestureCenter(gfx::Point center) { | |
229 tap_down_location_ = center; | |
230 } | |
231 //////////////////////////////////////////////////////////////////////////////// | |
232 | |
233 // AutoclickRingHandler, private | |
234 aura::Window* AutoclickRingHandler::GetTargetWindow() { | |
235 aura::Window* target = ash::WmWindowAura::GetAuraWindow( | |
236 ash::wm::GetRootWindowAt(tap_down_location_)); | |
237 DCHECK(target) << "Root window not found while rendering autoclick circle;"; | |
238 return target; | |
239 } | |
240 | |
241 void AutoclickRingHandler::SetTapDownTarget() { | |
242 aura::Window* target = GetTargetWindow(); | |
243 SetTapDownTarget(target); | |
244 } | |
245 | |
246 void AutoclickRingHandler::StartAnimation(base::TimeDelta delay) { | |
247 int delay_ms = int{delay.InMilliseconds()}; | |
248 switch (current_animation_type_) { | |
249 case AnimationType::GROW_ANIMATION: { | |
250 aura::Window* root_window = tap_down_target_->GetRootWindow(); | |
251 view_.reset(new AutoclickRingView(tap_down_location_, root_window)); | |
252 SetDuration(delay_ms); | |
253 Start(); | |
254 break; | |
255 } | |
256 case AnimationType::SHRINK_ANIMATION: { | |
257 aura::Window* root_window = tap_down_target_->GetRootWindow(); | |
258 view_.reset(new AutoclickRingView(tap_down_location_, root_window)); | |
259 SetDuration(delay_ms); | |
260 Start(); | |
261 break; | |
262 } | |
263 case AnimationType::NONE: | |
264 NOTREACHED(); | |
265 break; | |
266 } | |
267 } | |
268 | |
269 void AutoclickRingHandler::StopAutoclickRing() { | |
270 // Since, Animation::Stop() calls AnimationStopped(), we need to reset the | |
271 // |current_animation_type_| before Stop(), otherwise AnimationStopped() may | |
272 // start the timer again. | |
273 current_animation_type_ = AnimationType::NONE; | |
274 Stop(); | |
275 view_.reset(); | |
276 SetTapDownTarget(nullptr); | |
277 } | |
278 | |
279 void AutoclickRingHandler::SetTapDownTarget(aura::Window* target) { | |
280 if (tap_down_target_ == target) | |
281 return; | |
282 | |
283 if (tap_down_target_) | |
284 tap_down_target_->RemoveObserver(this); | |
285 tap_down_target_ = target; | |
286 if (tap_down_target_) | |
287 tap_down_target_->AddObserver(this); | |
288 } | |
289 | |
290 void AutoclickRingHandler::AnimateToState(double state) { | |
291 DCHECK(view_.get()); | |
292 switch (current_animation_type_) { | |
293 case AnimationType::GROW_ANIMATION: | |
294 view_->SetNewLocation(tap_down_location_, GetTargetWindow()); | |
295 view_->UpdateWithGrowAnimation(this); | |
296 break; | |
297 case AnimationType::SHRINK_ANIMATION: | |
298 view_->SetNewLocation(tap_down_location_, GetTargetWindow()); | |
299 view_->UpdateWithShrinkAnimation(this); | |
300 break; | |
301 case AnimationType::NONE: | |
302 NOTREACHED(); | |
303 break; | |
304 } | |
305 } | |
306 | |
307 void AutoclickRingHandler::AnimationStopped() { | |
308 switch (current_animation_type_) { | |
309 case AnimationType::GROW_ANIMATION: | |
310 current_animation_type_ = AnimationType::SHRINK_ANIMATION; | |
311 StartAnimation(animation_duration_); | |
312 break; | |
313 case AnimationType::SHRINK_ANIMATION: | |
314 current_animation_type_ = AnimationType::NONE; | |
315 break; | |
316 case AnimationType::NONE: | |
317 // fall through to reset the view. | |
318 view_.reset(); | |
319 SetTapDownTarget(nullptr); | |
320 break; | |
321 } | |
322 } | |
323 | |
324 void AutoclickRingHandler::OnWindowDestroying(aura::Window* window) { | |
325 DCHECK_EQ(tap_down_target_, window); | |
326 StopAutoclickRing(); | |
327 } | |
328 | |
329 } // namespace chromeos | |
OLD | NEW |