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

Unified 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, 7 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 side-by-side diff with in-line comments
Download patch
Index: ash/autoclick/autoclick_ring_handler.cc
diff --git a/ash/autoclick/autoclick_ring_handler.cc b/ash/autoclick/autoclick_ring_handler.cc
new file mode 100644
index 0000000000000000000000000000000000000000..6f44d43d6d9021b70ec3985c9ae0a25dfd138153
--- /dev/null
+++ b/ash/autoclick/autoclick_ring_handler.cc
@@ -0,0 +1,337 @@
+// 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 "ash/autoclick/autoclick_ring_handler.h"
+
+#include <memory>
+
+#include "ash/root_window_controller.h"
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/wm/aura/wm_window_aura.h"
+#include "ash/wm/common/root_window_finder.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 ash {
+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(0xFF);
+ GetRootWindowController(root_window)
+ ->GetContainer(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, 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.
+ tap_down_target_(NULL),
+ current_animation_type_(NONE) {}
+
+LongPressAutoclickRingHandler::~LongPressAutoclickRingHandler() {
+ StopAutoclickRing();
+}
+
+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.
+ int delay_ms,
+ bool start_animation) {
+ aura::Window* target = GetTargetWindow();
+
+ if (tap_down_target_ && tap_down_target_ != target)
+ return;
+ if (start_animation) {
+ // Start timer that will start animation on "semi-long-press".
+ StopAutoclickRing();
+ SetTapDownLocationAndTarget();
+ current_animation_type_ = GROW_ANIMATION;
+ StartAnimation(delay_ms);
+ } else {
+ StopAutoclickRing();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// LongPressAutoclickRingHandler, private
+gfx::Point LongPressAutoclickRingHandler::GetLastMouseLocation() {
+ return aura::Env::GetInstance()->last_mouse_location();
+}
+
+aura::Window* LongPressAutoclickRingHandler::GetTargetWindow() {
+ gfx::Point screen_location = GetLastMouseLocation();
+ aura::Window* target =
+ wm::WmWindowAura::GetAuraWindow(wm::GetRootWindowAt(screen_location));
+ DCHECK(target) << "Root window not found while rendering autoclick circle;";
+ return target;
+}
+
+void LongPressAutoclickRingHandler::SetTapDownLocationAndTarget() {
+ gfx::Point screen_location = GetLastMouseLocation();
+ gfx::Point click_location(screen_location);
+ aura::Window* target = GetTargetWindow();
+ ::wm::ConvertPointFromScreen(target, &click_location);
+ aura::WindowTreeHost* host = target->GetHost();
+ host->ConvertPointToHost(&click_location);
+ tap_down_location_ = click_location;
+ 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(NULL);
+}
+
+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(GetLastMouseLocation(), GetTargetWindow());
+ view_->UpdateWithGrowAnimation(this);
+ break;
+ case SHRINK_ANIMATION:
+ view_->SetNewLocation(GetLastMouseLocation(), 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(NULL);
+ break;
+ }
+}
+
+void LongPressAutoclickRingHandler::OnWindowDestroying(aura::Window* window) {
+ DCHECK_EQ(tap_down_target_, window);
+ StopAutoclickRing();
+}
+
+} // namespace ash

Powered by Google App Engine
This is Rietveld 408576698