Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(38)

Side by Side Diff: ash/autoclick/autoclick_ring_handler.cc

Issue 2016073004: Show a visual indicator for the progress of auto-click. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lkgr
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 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 "ash/autoclick/autoclick_ring_handler.h"
6
7 #include <memory>
8
9 #include "ash/root_window_controller.h"
10 #include "ash/shell.h"
11 #include "ash/shell_window_ids.h"
12 #include "ash/wm/aura/wm_window_aura.h"
13 #include "ash/wm/common/root_window_finder.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 ash {
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 kAutoclickRingOuterStartAngle = 0;
37 const int kAutoclickRingInnerStartAngle = -90;
38
39 const int kAutoclickRingGlowWidth = 20;
40 // The following is half width to avoid division by 2.
41 const int kAutoclickRingArcWidth = 2;
42
43 // Start and end values for various animations.
44 const double kAutoclickRingScaleStartValue = 1.0;
45 const double kAutoclickRingScaleEndValue = 0.5;
46 const double kAutoclickRingShrinkScaleEndValue = 0.5;
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;
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 widget->Init(params);
70 widget->SetOpacity(0xFF);
71 GetRootWindowController(root_window)
72 ->GetContainer(kShellWindowId_OverlayContainer)
73 ->AddChild(widget->GetNativeWindow());
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 }
88 void PaintAutoclickRingArc(gfx::Canvas* canvas,
89 gfx::Point& center,
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 LongPressAutoclickRingHandler. Draws the actual contents and
108 // updates as the animation proceeds. It also maintains the views::Widget that
109 // the animation is shown in.
110 class LongPressAutoclickRingHandler::LongPressAutoclickRingView
111 : public views::View {
112 public:
113 LongPressAutoclickRingView(const gfx::Point& event_location,
114 aura::Window* root_window)
115 : views::View(),
116 widget_(CreateAutoclickRingWidget(root_window)),
117 current_angle_(kAutoclickRingAngleStartValue),
118 current_scale_(kAutoclickRingScaleStartValue) {
119 widget_->SetContentsView(this);
120 widget_->SetAlwaysOnTop(true);
121
122 // We are owned by the LongPressAutoclickRing.
123 set_owned_by_client();
124 SetNewLocation(event_location, root_window);
125 }
126
127 ~LongPressAutoclickRingView() override {}
128
129 void SetNewLocation(const gfx::Point& new_event_location,
130 aura::Window* root_window) {
131 gfx::Point point = new_event_location;
132 aura::client::GetScreenPositionClient(root_window)
133 ->ConvertPointToScreen(root_window, &point);
134 widget_->SetBounds(gfx::Rect(
135 point.x() - (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth),
136 point.y() - (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth),
137 GetPreferredSize().width(), GetPreferredSize().height()));
138 widget_->Show();
139 widget_->GetNativeView()->layer()->SetOpacity(
140 kAutoclickRingOpacityStartValue);
141 }
142
143 void UpdateWithGrowAnimation(gfx::Animation* animation) {
144 // Update the portion of the circle filled so far and re-draw.
145 current_angle_ = animation->CurrentValueBetween(
146 kAutoclickRingInnerStartAngle, kAutoclickRingAngleEndValue);
147 current_scale_ = animation->CurrentValueBetween(
148 kAutoclickRingScaleStartValue, kAutoclickRingScaleEndValue);
149 widget_->GetNativeView()->layer()->SetOpacity(
150 animation->CurrentValueBetween(kAutoclickRingOpacityStartValue,
151 kAutoclickRingOpacityEndValue));
152 SchedulePaint();
153 }
154
155 void UpdateWithShrinkAnimation(gfx::Animation* animation) {
156 current_scale_ = animation->CurrentValueBetween(
157 kAutoclickRingScaleEndValue, kAutoclickRingShrinkScaleEndValue);
158 widget_->GetNativeView()->layer()->SetOpacity(
159 animation->CurrentValueBetween(kAutoclickRingOpacityEndValue,
160 kAutoclickRingOpacityStartValue));
161 SchedulePaint();
162 }
163
164 private:
165 // Overridden from views::View.
166 gfx::Size GetPreferredSize() const override {
167 return gfx::Size(2 * (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth),
168 2 * (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth));
169 }
170
171 void OnPaint(gfx::Canvas* canvas) override {
172 gfx::Point center(GetPreferredSize().width() / 2,
173 GetPreferredSize().height() / 2);
174 canvas->Save();
175
176 gfx::Transform scale;
177 scale.Scale(current_scale_, current_scale_);
178 // We want to scale from the center.
179 canvas->Translate(center.OffsetFromOrigin());
180 canvas->Transform(scale);
181 canvas->Translate(-center.OffsetFromOrigin());
182
183 // Paint inner circle.
184 PaintAutoclickRingArc(canvas, center, kAutoclickRingInnerRadius,
185 kAutoclickRingInnerStartAngle, current_angle_);
186 // Paint outer circle.
187 PaintAutoclickRingCircle(canvas, center, kAutoclickRingOuterRadius);
188
189 canvas->Restore();
190 }
191
192 std::unique_ptr<views::Widget> widget_;
193 int current_angle_;
194 double current_scale_;
195
196 DISALLOW_COPY_AND_ASSIGN(LongPressAutoclickRingView);
197 };
198
199 ////////////////////////////////////////////////////////////////////////////////
200 // LongPressAutoclickRingHandler, public
201
202 LongPressAutoclickRingHandler::LongPressAutoclickRingHandler()
203 : gfx::LinearAnimation(kAutoclickRingFrameRateHz, NULL),
jdufault 2016/05/31 19:06:15 Replace all NULL occurrences in this file with nul
sammiequon 2016/06/03 21:34:31 Done.
204 tap_down_target_(NULL),
205 current_animation_type_(NONE) {}
206
207 LongPressAutoclickRingHandler::~LongPressAutoclickRingHandler() {
208 StopAutoclickRing();
209 }
210
211 void LongPressAutoclickRingHandler::ProcessEvent(ui::LocatedEvent* event,
jdufault 2016/05/31 19:06:15 Does |event| ever get used? Can it be removed? It
sammiequon 2016/06/03 21:34:31 Done. Replaced with the suggested functions.
212 int delay_ms,
213 bool start_animation) {
214 aura::Window* target = GetTargetWindow();
215
216 if (tap_down_target_ && tap_down_target_ != target)
217 return;
218 if (start_animation) {
219 // Start timer that will start animation on "semi-long-press".
220 StopAutoclickRing();
221 SetTapDownLocationAndTarget();
222 current_animation_type_ = GROW_ANIMATION;
223 StartAnimation(delay_ms);
224 } else {
225 StopAutoclickRing();
226 }
227 }
228
229 ////////////////////////////////////////////////////////////////////////////////
230 // LongPressAutoclickRingHandler, private
231 gfx::Point LongPressAutoclickRingHandler::GetLastMouseLocation() {
232 return aura::Env::GetInstance()->last_mouse_location();
233 }
234
235 aura::Window* LongPressAutoclickRingHandler::GetTargetWindow() {
236 gfx::Point screen_location = GetLastMouseLocation();
237 aura::Window* target =
238 wm::WmWindowAura::GetAuraWindow(wm::GetRootWindowAt(screen_location));
239 DCHECK(target) << "Root window not found while rendering autoclick circle;";
240 return target;
241 }
242
243 void LongPressAutoclickRingHandler::SetTapDownLocationAndTarget() {
244 gfx::Point screen_location = GetLastMouseLocation();
245 gfx::Point click_location(screen_location);
246 aura::Window* target = GetTargetWindow();
247 ::wm::ConvertPointFromScreen(target, &click_location);
248 aura::WindowTreeHost* host = target->GetHost();
249 host->ConvertPointToHost(&click_location);
250 tap_down_location_ = click_location;
251 SetTapDownTarget(target);
252 }
253
254 void LongPressAutoclickRingHandler::StartAnimation(int delay_ms) {
255 switch (current_animation_type_) {
256 case GROW_ANIMATION: {
257 aura::Window* root_window = tap_down_target_->GetRootWindow();
258 if (!root_window) {
259 StopAutoclickRing();
260 return;
261 }
262 view_.reset(
263 new LongPressAutoclickRingView(tap_down_location_, root_window));
264 SetDuration(delay_ms);
265 Start();
266 break;
267 }
268 case SHRINK_ANIMATION:
269 SetDuration(delay_ms);
270 Start();
271 break;
272 default:
273 NOTREACHED();
274 break;
275 }
276 }
277
278 void LongPressAutoclickRingHandler::StopAutoclickRing() {
279 // Since, Animation::Stop() calls AnimationStopped(), we need to reset the
280 // |current_animation_type_| before Stop(), otherwise AnimationStopped() may
281 // start the timer again.
282 current_animation_type_ = NONE;
283 Stop();
284 view_.reset();
285 SetTapDownTarget(NULL);
286 }
287
288 void LongPressAutoclickRingHandler::SetTapDownTarget(aura::Window* target) {
289 if (tap_down_target_ == target)
290 return;
291
292 if (tap_down_target_)
293 tap_down_target_->RemoveObserver(this);
294 tap_down_target_ = target;
295 if (tap_down_target_)
296 tap_down_target_->AddObserver(this);
297 }
298
299 void LongPressAutoclickRingHandler::AnimateToState(double state) {
300 DCHECK(view_.get());
301 switch (current_animation_type_) {
302 case GROW_ANIMATION:
303 view_->SetNewLocation(GetLastMouseLocation(), GetTargetWindow());
304 view_->UpdateWithGrowAnimation(this);
305 break;
306 case SHRINK_ANIMATION:
307 view_->SetNewLocation(GetLastMouseLocation(), GetTargetWindow());
308 view_->UpdateWithShrinkAnimation(this);
309 break;
310 default:
311 NOTREACHED();
312 break;
313 }
314 }
315
316 void LongPressAutoclickRingHandler::AnimationStopped() {
317 switch (current_animation_type_) {
318 case GROW_ANIMATION:
319 current_animation_type_ = SHRINK_ANIMATION;
320 StartAnimation(0);
321 break;
322 case SHRINK_ANIMATION:
323 current_animation_type_ = NONE;
324 // fall through to reset the view.
325 default:
326 view_.reset();
327 SetTapDownTarget(NULL);
328 break;
329 }
330 }
331
332 void LongPressAutoclickRingHandler::OnWindowDestroying(aura::Window* window) {
333 DCHECK_EQ(tap_down_target_, window);
334 StopAutoclickRing();
335 }
336
337 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698