15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/wm/gestures/long_press_affordance_handler.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/display/display_controller.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/root_window_controller.h"
968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "ash/shell.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/shell_window_ids.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkColor.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkPaint.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkPath.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkRect.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/effects/SkGradientShader.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/client/screen_position_client.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/window.h"
18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/aura/window_event_dispatcher.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/compositor/layer.h"
2068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "ui/events/gestures/gesture_configuration.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/screen.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/transform.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/view.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/widget.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)namespace ash {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kAffordanceOuterRadius = 60;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kAffordanceInnerRadius = 50;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Angles from x-axis at which the outer and inner circles start.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kAffordanceOuterStartAngle = -109;
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kAffordanceInnerStartAngle = -65;
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kAffordanceGlowWidth = 20;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The following is half width to avoid division by 2.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kAffordanceArcWidth = 3;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Start and end values for various animations.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const double kAffordanceScaleStartValue = 0.8;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const double kAffordanceScaleEndValue = 1.0;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const double kAffordanceShrinkScaleEndValue = 0.5;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const double kAffordanceOpacityStartValue = 0.1;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const double kAffordanceOpacityEndValue = 0.5;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kAffordanceAngleStartValue = 0;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The end angle is a bit greater than 360 to make sure the circle completes at
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the end of the animation.
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kAffordanceAngleEndValue = 380;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kAffordanceDelayBeforeShrinkMs = 200;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kAffordanceShrinkAnimationDurationMs = 100;
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Visual constants.
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const SkColor kAffordanceGlowStartColor = SkColorSetARGB(24, 255, 255, 255);
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const SkColor kAffordanceGlowEndColor = SkColorSetARGB(0, 255, 255, 255);
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const SkColor kAffordanceArcColor = SkColorSetARGB(80, 0, 0, 0);
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kAffordanceFrameRateHz = 60;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)views::Widget* CreateAffordanceWidget(aura::Window* root_window) {
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::Widget* widget = new views::Widget;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::Widget::InitParams params;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  params.keep_on_top = true;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  params.accept_events = false;
66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  params.context = root_window;
69eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  widget->Init(params);
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  widget->SetOpacity(0xFF);
7258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  GetRootWindowController(root_window)->GetContainer(
7358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      kShellWindowId_OverlayContainer)->AddChild(widget->GetNativeWindow());
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return widget;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PaintAffordanceArc(gfx::Canvas* canvas,
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        gfx::Point& center,
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        int radius,
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        int start_angle,
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        int end_angle) {
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkPaint paint;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setStyle(SkPaint::kStroke_Style);
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setStrokeWidth(2 * kAffordanceArcWidth);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setColor(kAffordanceArcColor);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setAntiAlias(true);
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkPath arc_path;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  arc_path.addArc(SkRect::MakeXYWH(center.x() - radius,
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   center.y() - radius,
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   2 * radius,
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   2 * radius),
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  start_angle, end_angle);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  canvas->DrawPath(arc_path, paint);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PaintAffordanceGlow(gfx::Canvas* canvas,
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         gfx::Point& center,
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         int start_radius,
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         int end_radius,
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         SkColor* colors,
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         SkScalar* pos,
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         int num_colors) {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkPoint sk_center;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int radius = (end_radius + start_radius) / 2;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int glow_width = end_radius - start_radius;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sk_center.iset(center.x(), center.y());
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  skia::RefPtr<SkShader> shader = skia::AdoptRef(
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SkGradientShader::CreateTwoPointRadial(
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          sk_center,
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          SkIntToScalar(start_radius),
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          sk_center,
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          SkIntToScalar(end_radius),
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          colors,
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          pos,
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          num_colors,
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          SkShader::kClamp_TileMode));
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(shader);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkPaint paint;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setStyle(SkPaint::kStroke_Style);
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setStrokeWidth(glow_width);
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  paint.setShader(shader.get());
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setAntiAlias(true);
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkPath arc_path;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  arc_path.addArc(SkRect::MakeXYWH(center.x() - radius,
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   center.y() - radius,
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   2 * radius,
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   2 * radius),
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  0, 360);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  canvas->DrawPath(arc_path, paint);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// View of the LongPressAffordanceHandler. Draws the actual contents and
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// updates as the animation proceeds. It also maintains the views::Widget that
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the animation is shown in.
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class LongPressAffordanceHandler::LongPressAffordanceView
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : public views::View {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  LongPressAffordanceView(const gfx::Point& event_location,
1421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                          aura::Window* root_window)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : views::View(),
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        widget_(CreateAffordanceWidget(root_window)),
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_angle_(kAffordanceAngleStartValue),
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_scale_(kAffordanceScaleStartValue) {
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    widget_->SetContentsView(this);
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    widget_->SetAlwaysOnTop(true);
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We are owned by the LongPressAffordance.
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set_owned_by_client();
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Point point = event_location;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aura::client::GetScreenPositionClient(root_window)->ConvertPointToScreen(
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        root_window, &point);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    widget_->SetBounds(gfx::Rect(
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        point.x() - (kAffordanceOuterRadius + kAffordanceGlowWidth),
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        point.y() - (kAffordanceOuterRadius + kAffordanceGlowWidth),
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GetPreferredSize().width(),
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GetPreferredSize().height()));
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    widget_->Show();
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    widget_->GetNativeView()->layer()->SetOpacity(kAffordanceOpacityStartValue);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~LongPressAffordanceView() {
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
167d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  void UpdateWithGrowAnimation(gfx::Animation* animation) {
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Update the portion of the circle filled so far and re-draw.
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_angle_ = animation->CurrentValueBetween(kAffordanceAngleStartValue,
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kAffordanceAngleEndValue);
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_scale_ = animation->CurrentValueBetween(kAffordanceScaleStartValue,
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kAffordanceScaleEndValue);
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    widget_->GetNativeView()->layer()->SetOpacity(
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        animation->CurrentValueBetween(kAffordanceOpacityStartValue,
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            kAffordanceOpacityEndValue));
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SchedulePaint();
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
179d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  void UpdateWithShrinkAnimation(gfx::Animation* animation) {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_scale_ = animation->CurrentValueBetween(kAffordanceScaleEndValue,
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kAffordanceShrinkScaleEndValue);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    widget_->GetNativeView()->layer()->SetOpacity(
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        animation->CurrentValueBetween(kAffordanceOpacityEndValue,
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            kAffordanceOpacityStartValue));
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SchedulePaint();
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Overridden from views::View.
190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  virtual gfx::Size GetPreferredSize() const OVERRIDE {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return gfx::Size(2 * (kAffordanceOuterRadius + kAffordanceGlowWidth),
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        2 * (kAffordanceOuterRadius + kAffordanceGlowWidth));
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Point center(GetPreferredSize().width() / 2,
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      GetPreferredSize().height() / 2);
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Save();
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Transform scale;
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scale.Scale(current_scale_, current_scale_);
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We want to scale from the center.
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Translate(center.OffsetFromOrigin());
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Transform(scale);
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Translate(-center.OffsetFromOrigin());
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Paint affordance glow
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int start_radius = kAffordanceInnerRadius - kAffordanceGlowWidth;
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int end_radius = kAffordanceOuterRadius + kAffordanceGlowWidth;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int num_colors = 3;
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SkScalar pos[num_colors] = {0, 0.5, 1};
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SkColor colors[num_colors] = {kAffordanceGlowEndColor,
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kAffordanceGlowStartColor, kAffordanceGlowEndColor};
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PaintAffordanceGlow(canvas, center, start_radius, end_radius, colors, pos,
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        num_colors);
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Paint inner circle.
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PaintAffordanceArc(canvas, center, kAffordanceInnerRadius,
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kAffordanceInnerStartAngle, -current_angle_);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Paint outer circle.
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PaintAffordanceArc(canvas, center, kAffordanceOuterRadius,
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kAffordanceOuterStartAngle, current_angle_);
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Restore();
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<views::Widget> widget_;
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int current_angle_;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double current_scale_;
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(LongPressAffordanceView);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// LongPressAffordanceHandler, public
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LongPressAffordanceHandler::LongPressAffordanceHandler()
238f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : gfx::LinearAnimation(kAffordanceFrameRateHz, NULL),
239f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      tap_down_target_(NULL),
2407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      current_animation_type_(NONE) {}
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
242f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)LongPressAffordanceHandler::~LongPressAffordanceHandler() {
243f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  StopAffordance();
244f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void LongPressAffordanceHandler::ProcessEvent(aura::Window* target,
247f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                              ui::GestureEvent* event) {
248f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Once we have a target, we are only interested in events with that target.
249f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (tap_down_target_ && tap_down_target_ != target)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (event->type()) {
252f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case ui::ET_GESTURE_TAP_DOWN: {
253f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      // Start timer that will start animation on "semi-long-press".
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tap_down_location_ = event->root_location();
255f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      SetTapDownTarget(target);
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_animation_type_ = GROW_ANIMATION;
257f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      int64 timer_start_time_ms =
258f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          ui::GestureConfiguration::semi_long_press_time_in_seconds() * 1000;
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      timer_.Start(FROM_HERE,
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   base::TimeDelta::FromMilliseconds(timer_start_time_ms),
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   this,
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   &LongPressAffordanceHandler::StartAnimation);
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
264f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
265f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case ui::ET_GESTURE_TAP:
266f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case ui::ET_GESTURE_TAP_CANCEL:
267f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      StopAffordance();
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_GESTURE_LONG_PRESS:
270f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      End();
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// LongPressAffordanceHandler, private
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void LongPressAffordanceHandler::StartAnimation() {
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (current_animation_type_) {
282f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case GROW_ANIMATION: {
283f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      aura::Window* root_window = tap_down_target_->GetRootWindow();
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!root_window) {
285f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        StopAffordance();
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_.reset(new LongPressAffordanceView(tap_down_location_, root_window));
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetDuration(
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ui::GestureConfiguration::long_press_time_in_seconds() * 1000 -
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ui::GestureConfiguration::semi_long_press_time_in_seconds() * 1000 -
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          kAffordanceDelayBeforeShrinkMs);
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Start();
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
295f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SHRINK_ANIMATION:
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetDuration(kAffordanceShrinkAnimationDurationMs);
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Start();
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
306f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void LongPressAffordanceHandler::StopAffordance() {
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (timer_.IsRunning())
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    timer_.Stop();
309f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Since, Animation::Stop() calls AnimationStopped(), we need to reset the
310f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // |current_animation_type_| before Stop(), otherwise AnimationStopped() may
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // start the timer again.
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_animation_type_ = NONE;
313f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  Stop();
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  view_.reset();
315f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetTapDownTarget(NULL);
316f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
317f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
318f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void LongPressAffordanceHandler::SetTapDownTarget(aura::Window* target) {
319f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (tap_down_target_ == target)
320f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;
321f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
322f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (tap_down_target_)
323f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    tap_down_target_->RemoveObserver(this);
324f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  tap_down_target_ = target;
325f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (tap_down_target_)
326f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    tap_down_target_->AddObserver(this);
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void LongPressAffordanceHandler::AnimateToState(double state) {
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(view_.get());
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (current_animation_type_) {
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GROW_ANIMATION:
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->UpdateWithGrowAnimation(this);
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SHRINK_ANIMATION:
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->UpdateWithShrinkAnimation(this);
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
344f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void LongPressAffordanceHandler::AnimationStopped() {
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (current_animation_type_) {
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GROW_ANIMATION:
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_animation_type_ = SHRINK_ANIMATION;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      timer_.Start(FROM_HERE,
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::TimeDelta::FromMilliseconds(kAffordanceDelayBeforeShrinkMs),
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this, &LongPressAffordanceHandler::StartAnimation);
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SHRINK_ANIMATION:
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_animation_type_ = NONE;
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // fall through to reset the view.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_.reset();
357f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      SetTapDownTarget(NULL);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
362f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void LongPressAffordanceHandler::OnWindowDestroying(aura::Window* window) {
363f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK_EQ(tap_down_target_, window);
364f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  StopAffordance();
365f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
366f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace ash
368