OLD | NEW |
(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 "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 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(1.f); |
| 71 ash::GetRootWindowController(root_window) |
| 72 ->GetContainer(ash::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 AutoclickRingHandler. 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 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 widget_->SetAlwaysOnTop(true); |
| 119 |
| 120 // We are owned by the AutoclickRing. |
| 121 set_owned_by_client(); |
| 122 SetNewLocation(event_location, root_window); |
| 123 } |
| 124 |
| 125 ~AutoclickRingView() override {} |
| 126 |
| 127 void SetNewLocation(const gfx::Point& new_event_location, |
| 128 aura::Window* root_window) { |
| 129 gfx::Point point = new_event_location; |
| 130 aura::client::GetScreenPositionClient(root_window) |
| 131 ->ConvertPointToScreen(root_window, &point); |
| 132 widget_->SetBounds(gfx::Rect( |
| 133 point.x() - (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth), |
| 134 point.y() - (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth), |
| 135 GetPreferredSize().width(), GetPreferredSize().height())); |
| 136 widget_->Show(); |
| 137 widget_->GetNativeView()->layer()->SetOpacity( |
| 138 kAutoclickRingOpacityStartValue); |
| 139 } |
| 140 |
| 141 void UpdateWithGrowAnimation(gfx::Animation* animation) { |
| 142 // Update the portion of the circle filled so far and re-draw. |
| 143 current_angle_ = animation->CurrentValueBetween( |
| 144 kAutoclickRingInnerStartAngle, kAutoclickRingAngleEndValue); |
| 145 current_scale_ = animation->CurrentValueBetween( |
| 146 kAutoclickRingScaleStartValue, kAutoclickRingScaleEndValue); |
| 147 widget_->GetNativeView()->layer()->SetOpacity( |
| 148 animation->CurrentValueBetween(kAutoclickRingOpacityStartValue, |
| 149 kAutoclickRingOpacityEndValue)); |
| 150 SchedulePaint(); |
| 151 } |
| 152 |
| 153 void UpdateWithShrinkAnimation(gfx::Animation* animation) { |
| 154 current_scale_ = animation->CurrentValueBetween( |
| 155 kAutoclickRingScaleEndValue, kAutoclickRingShrinkScaleEndValue); |
| 156 widget_->GetNativeView()->layer()->SetOpacity( |
| 157 animation->CurrentValueBetween(kAutoclickRingOpacityEndValue, |
| 158 kAutoclickRingOpacityStartValue)); |
| 159 SchedulePaint(); |
| 160 } |
| 161 |
| 162 private: |
| 163 // Overridden from views::View. |
| 164 gfx::Size GetPreferredSize() const override { |
| 165 return gfx::Size(2 * (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth), |
| 166 2 * (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth)); |
| 167 } |
| 168 |
| 169 void OnPaint(gfx::Canvas* canvas) override { |
| 170 gfx::Point center(GetPreferredSize().width() / 2, |
| 171 GetPreferredSize().height() / 2); |
| 172 canvas->Save(); |
| 173 |
| 174 gfx::Transform scale; |
| 175 scale.Scale(current_scale_, current_scale_); |
| 176 // We want to scale from the center. |
| 177 canvas->Translate(center.OffsetFromOrigin()); |
| 178 canvas->Transform(scale); |
| 179 canvas->Translate(-center.OffsetFromOrigin()); |
| 180 |
| 181 // Paint inner circle. |
| 182 PaintAutoclickRingArc(canvas, center, kAutoclickRingInnerRadius, |
| 183 kAutoclickRingInnerStartAngle, current_angle_); |
| 184 // Paint outer circle. |
| 185 PaintAutoclickRingCircle(canvas, center, kAutoclickRingOuterRadius); |
| 186 |
| 187 canvas->Restore(); |
| 188 } |
| 189 |
| 190 std::unique_ptr<views::Widget> widget_; |
| 191 int current_angle_; |
| 192 double current_scale_; |
| 193 |
| 194 DISALLOW_COPY_AND_ASSIGN(AutoclickRingView); |
| 195 }; |
| 196 |
| 197 //////////////////////////////////////////////////////////////////////////////// |
| 198 |
| 199 // AutoclickRingHandler, public |
| 200 AutoclickRingHandler::AutoclickRingHandler() |
| 201 : gfx::LinearAnimation(kAutoclickRingFrameRateHz, nullptr), |
| 202 tap_down_target_(nullptr), |
| 203 current_animation_type_(NONE) {} |
| 204 |
| 205 AutoclickRingHandler::~AutoclickRingHandler() { |
| 206 StopAutoclickRing(); |
| 207 } |
| 208 |
| 209 void AutoclickRingHandler::StartGesture(int duration_ms, 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_ = GROW_ANIMATION; |
| 217 StartAnimation(duration_ms); |
| 218 } |
| 219 |
| 220 void AutoclickRingHandler::StopGesture() { |
| 221 aura::Window* target = GetTargetWindow(); |
| 222 if (tap_down_target_ && tap_down_target_ != target) |
| 223 return; |
| 224 StopAutoclickRing(); |
| 225 } |
| 226 |
| 227 void AutoclickRingHandler::SetGestureCenter(gfx::Point center) { |
| 228 tap_down_location_ = center; |
| 229 } |
| 230 //////////////////////////////////////////////////////////////////////////////// |
| 231 // AutoclickRingHandler, private |
| 232 aura::Window* AutoclickRingHandler::GetTargetWindow() { |
| 233 aura::Window* target = ash::WmWindowAura::GetAuraWindow( |
| 234 ash::wm::GetRootWindowAt(tap_down_location_)); |
| 235 DCHECK(target) << "Root window not found while rendering autoclick circle;"; |
| 236 return target; |
| 237 } |
| 238 |
| 239 void AutoclickRingHandler::SetTapDownTarget() { |
| 240 aura::Window* target = GetTargetWindow(); |
| 241 SetTapDownTarget(target); |
| 242 } |
| 243 |
| 244 void AutoclickRingHandler::StartAnimation(int delay_ms) { |
| 245 switch (current_animation_type_) { |
| 246 case GROW_ANIMATION: { |
| 247 aura::Window* root_window = tap_down_target_->GetRootWindow(); |
| 248 if (!root_window) { |
| 249 StopAutoclickRing(); |
| 250 return; |
| 251 } |
| 252 view_.reset(new AutoclickRingView(tap_down_location_, root_window)); |
| 253 SetDuration(delay_ms); |
| 254 Start(); |
| 255 break; |
| 256 } |
| 257 case SHRINK_ANIMATION: |
| 258 SetDuration(delay_ms); |
| 259 Start(); |
| 260 break; |
| 261 default: |
| 262 NOTREACHED(); |
| 263 break; |
| 264 } |
| 265 } |
| 266 |
| 267 void AutoclickRingHandler::StopAutoclickRing() { |
| 268 // Since, Animation::Stop() calls AnimationStopped(), we need to reset the |
| 269 // |current_animation_type_| before Stop(), otherwise AnimationStopped() may |
| 270 // start the timer again. |
| 271 current_animation_type_ = NONE; |
| 272 Stop(); |
| 273 view_.reset(); |
| 274 SetTapDownTarget(nullptr); |
| 275 } |
| 276 |
| 277 void AutoclickRingHandler::SetTapDownTarget(aura::Window* target) { |
| 278 if (tap_down_target_ == target) |
| 279 return; |
| 280 |
| 281 if (tap_down_target_) |
| 282 tap_down_target_->RemoveObserver(this); |
| 283 tap_down_target_ = target; |
| 284 if (tap_down_target_) |
| 285 tap_down_target_->AddObserver(this); |
| 286 } |
| 287 |
| 288 void AutoclickRingHandler::AnimateToState(double state) { |
| 289 DCHECK(view_.get()); |
| 290 switch (current_animation_type_) { |
| 291 case GROW_ANIMATION: |
| 292 view_->SetNewLocation(tap_down_location_, GetTargetWindow()); |
| 293 view_->UpdateWithGrowAnimation(this); |
| 294 break; |
| 295 case SHRINK_ANIMATION: |
| 296 view_->SetNewLocation(tap_down_location_, GetTargetWindow()); |
| 297 view_->UpdateWithShrinkAnimation(this); |
| 298 break; |
| 299 default: |
| 300 NOTREACHED(); |
| 301 break; |
| 302 } |
| 303 } |
| 304 |
| 305 void AutoclickRingHandler::AnimationStopped() { |
| 306 switch (current_animation_type_) { |
| 307 case GROW_ANIMATION: |
| 308 current_animation_type_ = SHRINK_ANIMATION; |
| 309 StartAnimation(0); |
| 310 break; |
| 311 case SHRINK_ANIMATION: |
| 312 current_animation_type_ = NONE; |
| 313 // fall through to reset the view. |
| 314 default: |
| 315 view_.reset(); |
| 316 SetTapDownTarget(nullptr); |
| 317 break; |
| 318 } |
| 319 } |
| 320 |
| 321 void AutoclickRingHandler::OnWindowDestroying(aura::Window* window) { |
| 322 DCHECK_EQ(tap_down_target_, window); |
| 323 StopAutoclickRing(); |
| 324 } |
| 325 |
| 326 } // namespace chromeos |
OLD | NEW |