Chromium Code Reviews| 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 "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 | |
| OLD | NEW |