Index: chrome/browser/chromeos/ui/autoclick_ring_handler.cc |
diff --git a/chrome/browser/chromeos/ui/autoclick_ring_handler.cc b/chrome/browser/chromeos/ui/autoclick_ring_handler.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4ab856d7e3e71b4bdb61c9273b2e0ba05c55dc1b |
--- /dev/null |
+++ b/chrome/browser/chromeos/ui/autoclick_ring_handler.cc |
@@ -0,0 +1,329 @@ |
+// Copyright (c) 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/chromeos/ui/autoclick_ring_handler.h" |
+ |
+#include <memory> |
+ |
+#include "ash/common/shell_window_ids.h" |
+#include "ash/common/wm/root_window_finder.h" |
+#include "ash/root_window_controller.h" |
+#include "ash/shell.h" |
+#include "ash/wm/aura/wm_window_aura.h" |
+#include "third_party/skia/include/core/SkColor.h" |
+#include "third_party/skia/include/core/SkPaint.h" |
+#include "third_party/skia/include/core/SkPath.h" |
+#include "third_party/skia/include/core/SkRect.h" |
+#include "ui/aura/client/screen_position_client.h" |
+#include "ui/aura/env.h" |
+#include "ui/aura/window.h" |
+#include "ui/aura/window_event_dispatcher.h" |
+#include "ui/compositor/layer.h" |
+#include "ui/gfx/canvas.h" |
+#include "ui/gfx/transform.h" |
+#include "ui/views/view.h" |
+#include "ui/views/widget/widget.h" |
+#include "ui/wm/core/coordinate_conversion.h" |
+ |
+namespace chromeos { |
+namespace { |
+ |
+const int kAutoclickRingOuterRadius = 30; |
+const int kAutoclickRingInnerRadius = 20; |
+ |
+// Angles from x-axis at which the outer and inner circles start. |
+// const int kAutoclickRingOuterStartAngle = 0; |
+const int kAutoclickRingInnerStartAngle = -90; |
+ |
+const int kAutoclickRingGlowWidth = 20; |
+// The following is half width to avoid division by 2. |
+const int kAutoclickRingArcWidth = 2; |
+ |
+// Start and end values for various animations. |
+const double kAutoclickRingScaleStartValue = 1.0; |
+const double kAutoclickRingScaleEndValue = 0.5; |
+const double kAutoclickRingShrinkScaleEndValue = 0.5; |
+const double kAutoclickRingOpacityStartValue = 0.1; |
+const double kAutoclickRingOpacityEndValue = 0.5; |
+const int kAutoclickRingAngleStartValue = -90; |
+// The sweep angle is a bit greater than 360 to make sure the circle |
+// completes at the end of the animation. |
+const int kAutoclickRingAngleEndValue = 360; |
+ |
+// Visual constants. |
+const SkColor kAutoclickRingArcColor = SkColorSetARGB(255, 0, 255, 0); |
+const SkColor kAutoclickRingCircleColor = SkColorSetARGB(255, 0, 0, 255); |
+const int kAutoclickRingFrameRateHz = 60; |
+ |
+views::Widget* CreateAutoclickRingWidget(aura::Window* root_window) { |
+ views::Widget* widget = new views::Widget; |
+ views::Widget::InitParams params; |
+ params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; |
+ params.keep_on_top = true; |
+ params.accept_events = false; |
+ params.activatable = views::Widget::InitParams::ACTIVATABLE_NO; |
+ params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
+ params.context = root_window; |
+ params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; |
+ widget->Init(params); |
+ widget->SetOpacity(1.f); |
+ ash::GetRootWindowController(root_window) |
+ ->GetContainer(ash::kShellWindowId_OverlayContainer) |
+ ->AddChild(widget->GetNativeWindow()); |
+ return widget; |
+} |
+ |
+void PaintAutoclickRingCircle(gfx::Canvas* canvas, |
+ gfx::Point& center, |
+ int radius) { |
+ SkPaint paint; |
+ paint.setStyle(SkPaint::kStroke_Style); |
+ paint.setStrokeWidth(2 * kAutoclickRingArcWidth); |
+ paint.setColor(kAutoclickRingCircleColor); |
+ paint.setAntiAlias(true); |
+ |
+ canvas->DrawCircle(center, radius, paint); |
+} |
+void PaintAutoclickRingArc(gfx::Canvas* canvas, |
+ gfx::Point& center, |
+ int radius, |
+ int start_angle, |
+ int end_angle) { |
+ SkPaint paint; |
+ paint.setStyle(SkPaint::kStroke_Style); |
+ paint.setStrokeWidth(2 * kAutoclickRingArcWidth); |
+ paint.setColor(kAutoclickRingArcColor); |
+ paint.setAntiAlias(true); |
+ |
+ SkPath arc_path; |
+ arc_path.addArc(SkRect::MakeXYWH(center.x() - radius, center.y() - radius, |
+ 2 * radius, 2 * radius), |
+ start_angle, end_angle - start_angle); |
+ canvas->DrawPath(arc_path, paint); |
+} |
+} // namespace |
+ |
+// View of the LongPressAutoclickRingHandler. Draws the actual contents and |
+// updates as the animation proceeds. It also maintains the views::Widget that |
+// the animation is shown in. |
+class LongPressAutoclickRingHandler::LongPressAutoclickRingView |
+ : public views::View { |
+ public: |
+ LongPressAutoclickRingView(const gfx::Point& event_location, |
+ aura::Window* root_window) |
+ : views::View(), |
+ widget_(CreateAutoclickRingWidget(root_window)), |
+ current_angle_(kAutoclickRingAngleStartValue), |
+ current_scale_(kAutoclickRingScaleStartValue) { |
+ widget_->SetContentsView(this); |
+ widget_->SetAlwaysOnTop(true); |
+ |
+ // We are owned by the LongPressAutoclickRing. |
+ set_owned_by_client(); |
+ SetNewLocation(event_location, root_window); |
+ } |
+ |
+ ~LongPressAutoclickRingView() override {} |
+ |
+ void SetNewLocation(const gfx::Point& new_event_location, |
+ aura::Window* root_window) { |
+ gfx::Point point = new_event_location; |
+ aura::client::GetScreenPositionClient(root_window) |
+ ->ConvertPointToScreen(root_window, &point); |
+ widget_->SetBounds(gfx::Rect( |
+ point.x() - (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth), |
+ point.y() - (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth), |
+ GetPreferredSize().width(), GetPreferredSize().height())); |
+ widget_->Show(); |
+ widget_->GetNativeView()->layer()->SetOpacity( |
+ kAutoclickRingOpacityStartValue); |
+ } |
+ |
+ void UpdateWithGrowAnimation(gfx::Animation* animation) { |
+ // Update the portion of the circle filled so far and re-draw. |
+ current_angle_ = animation->CurrentValueBetween( |
+ kAutoclickRingInnerStartAngle, kAutoclickRingAngleEndValue); |
+ current_scale_ = animation->CurrentValueBetween( |
+ kAutoclickRingScaleStartValue, kAutoclickRingScaleEndValue); |
+ widget_->GetNativeView()->layer()->SetOpacity( |
+ animation->CurrentValueBetween(kAutoclickRingOpacityStartValue, |
+ kAutoclickRingOpacityEndValue)); |
+ SchedulePaint(); |
+ } |
+ |
+ void UpdateWithShrinkAnimation(gfx::Animation* animation) { |
+ current_scale_ = animation->CurrentValueBetween( |
+ kAutoclickRingScaleEndValue, kAutoclickRingShrinkScaleEndValue); |
+ widget_->GetNativeView()->layer()->SetOpacity( |
+ animation->CurrentValueBetween(kAutoclickRingOpacityEndValue, |
+ kAutoclickRingOpacityStartValue)); |
+ SchedulePaint(); |
+ } |
+ |
+ private: |
+ // Overridden from views::View. |
+ gfx::Size GetPreferredSize() const override { |
+ return gfx::Size(2 * (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth), |
+ 2 * (kAutoclickRingOuterRadius + kAutoclickRingGlowWidth)); |
+ } |
+ |
+ void OnPaint(gfx::Canvas* canvas) override { |
+ gfx::Point center(GetPreferredSize().width() / 2, |
+ GetPreferredSize().height() / 2); |
+ canvas->Save(); |
+ |
+ gfx::Transform scale; |
+ scale.Scale(current_scale_, current_scale_); |
+ // We want to scale from the center. |
+ canvas->Translate(center.OffsetFromOrigin()); |
+ canvas->Transform(scale); |
+ canvas->Translate(-center.OffsetFromOrigin()); |
+ |
+ // Paint inner circle. |
+ PaintAutoclickRingArc(canvas, center, kAutoclickRingInnerRadius, |
+ kAutoclickRingInnerStartAngle, current_angle_); |
+ // Paint outer circle. |
+ PaintAutoclickRingCircle(canvas, center, kAutoclickRingOuterRadius); |
+ |
+ canvas->Restore(); |
+ } |
+ |
+ std::unique_ptr<views::Widget> widget_; |
+ int current_angle_; |
+ double current_scale_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(LongPressAutoclickRingView); |
+}; |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// LongPressAutoclickRingHandler, public |
+ |
+LongPressAutoclickRingHandler::LongPressAutoclickRingHandler() |
+ : gfx::LinearAnimation(kAutoclickRingFrameRateHz, nullptr), |
+ tap_down_target_(nullptr), |
+ current_animation_type_(NONE) {} |
+LongPressAutoclickRingHandler::~LongPressAutoclickRingHandler() { |
+ StopAutoclickRing(); |
+} |
+ |
+void LongPressAutoclickRingHandler::StartGesture(int delay_ms, |
+ gfx::Point center) { |
+ aura::Window* target = GetTargetWindow(); |
+ if (tap_down_target_ && tap_down_target_ != target) |
+ return; |
+ StopAutoclickRing(); |
+ SetTapDownTarget(); |
+ tap_down_location_ = center; |
+ current_animation_type_ = GROW_ANIMATION; |
+ StartAnimation(delay_ms); |
+} |
+ |
+void LongPressAutoclickRingHandler::StopGesture() { |
+ aura::Window* target = GetTargetWindow(); |
+ if (tap_down_target_ && tap_down_target_ != target) |
+ return; |
+ StopAutoclickRing(); |
+} |
+ |
+void LongPressAutoclickRingHandler::SetGestureCenter(gfx::Point center) { |
+ tap_down_location_ = center; |
+} |
+//////////////////////////////////////////////////////////////////////////////// |
jdufault
2016/06/03 22:02:44
Newline between the end of the function and the st
|
+// LongPressAutoclickRingHandler, private |
+aura::Window* LongPressAutoclickRingHandler::GetTargetWindow() { |
+ aura::Window* target = ash::wm::WmWindowAura::GetAuraWindow( |
+ ash::wm::GetRootWindowAt(tap_down_location_)); |
+ DCHECK(target) << "Root window not found while rendering autoclick circle;"; |
+ return target; |
+} |
+ |
+void LongPressAutoclickRingHandler::SetTapDownTarget() { |
+ aura::Window* target = GetTargetWindow(); |
+ SetTapDownTarget(target); |
+} |
+ |
+void LongPressAutoclickRingHandler::StartAnimation(int delay_ms) { |
+ switch (current_animation_type_) { |
+ case GROW_ANIMATION: { |
+ aura::Window* root_window = tap_down_target_->GetRootWindow(); |
+ if (!root_window) { |
+ StopAutoclickRing(); |
+ return; |
+ } |
+ view_.reset( |
+ new LongPressAutoclickRingView(tap_down_location_, root_window)); |
+ SetDuration(delay_ms); |
+ Start(); |
+ break; |
+ } |
+ case SHRINK_ANIMATION: |
+ SetDuration(delay_ms); |
+ Start(); |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+} |
+ |
+void LongPressAutoclickRingHandler::StopAutoclickRing() { |
+ // Since, Animation::Stop() calls AnimationStopped(), we need to reset the |
+ // |current_animation_type_| before Stop(), otherwise AnimationStopped() may |
+ // start the timer again. |
+ current_animation_type_ = NONE; |
+ Stop(); |
+ view_.reset(); |
+ SetTapDownTarget(nullptr); |
+} |
+ |
+void LongPressAutoclickRingHandler::SetTapDownTarget(aura::Window* target) { |
+ if (tap_down_target_ == target) |
+ return; |
+ |
+ if (tap_down_target_) |
+ tap_down_target_->RemoveObserver(this); |
+ tap_down_target_ = target; |
+ if (tap_down_target_) |
+ tap_down_target_->AddObserver(this); |
+} |
+ |
+void LongPressAutoclickRingHandler::AnimateToState(double state) { |
+ DCHECK(view_.get()); |
+ switch (current_animation_type_) { |
+ case GROW_ANIMATION: |
+ view_->SetNewLocation(tap_down_location_, GetTargetWindow()); |
+ view_->UpdateWithGrowAnimation(this); |
+ break; |
+ case SHRINK_ANIMATION: |
+ view_->SetNewLocation(tap_down_location_, GetTargetWindow()); |
+ view_->UpdateWithShrinkAnimation(this); |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+} |
+ |
+void LongPressAutoclickRingHandler::AnimationStopped() { |
+ switch (current_animation_type_) { |
+ case GROW_ANIMATION: |
+ current_animation_type_ = SHRINK_ANIMATION; |
+ StartAnimation(0); |
+ break; |
+ case SHRINK_ANIMATION: |
+ current_animation_type_ = NONE; |
+ // fall through to reset the view. |
+ default: |
+ view_.reset(); |
+ SetTapDownTarget(nullptr); |
+ break; |
+ } |
+} |
+ |
+void LongPressAutoclickRingHandler::OnWindowDestroying(aura::Window* window) { |
+ DCHECK_EQ(tap_down_target_, window); |
+ StopAutoclickRing(); |
+} |
+ |
+} // namespace chromeos |