Home | History | Annotate | Download | only in touch
      1 // Copyright 2013 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/touch/touch_hud_projection.h"
      6 
      7 #include "ash/root_window_controller.h"
      8 #include "ash/shell.h"
      9 #include "third_party/skia/include/effects/SkGradientShader.h"
     10 #include "ui/events/event.h"
     11 #include "ui/gfx/animation/animation_delegate.h"
     12 #include "ui/gfx/animation/linear_animation.h"
     13 #include "ui/gfx/canvas.h"
     14 #include "ui/gfx/size.h"
     15 #include "ui/views/widget/widget.h"
     16 
     17 namespace ash {
     18 
     19 const int kPointRadius = 20;
     20 const SkColor kProjectionFillColor = SkColorSetRGB(0xF5, 0xF5, 0xDC);
     21 const SkColor kProjectionStrokeColor = SK_ColorGRAY;
     22 const int kProjectionAlpha = 0xB0;
     23 const int kFadeoutDurationInMs = 250;
     24 const int kFadeoutFrameRate = 60;
     25 
     26 // TouchPointView draws a single touch point. This object manages its own
     27 // lifetime and deletes itself upon fade-out completion or whenever |Remove()|
     28 // is explicitly called.
     29 class TouchPointView : public views::View,
     30                        public gfx::AnimationDelegate,
     31                        public views::WidgetObserver {
     32  public:
     33   explicit TouchPointView(views::Widget* parent_widget)
     34       : circle_center_(kPointRadius + 1, kPointRadius + 1),
     35         gradient_center_(SkPoint::Make(kPointRadius + 1,
     36                                        kPointRadius + 1)) {
     37     SetPaintToLayer(true);
     38     SetFillsBoundsOpaquely(false);
     39 
     40     SetSize(gfx::Size(2 * kPointRadius + 2, 2 * kPointRadius + 2));
     41 
     42     stroke_paint_.setStyle(SkPaint::kStroke_Style);
     43     stroke_paint_.setColor(kProjectionStrokeColor);
     44 
     45     gradient_colors_[0] = kProjectionFillColor;
     46     gradient_colors_[1] = kProjectionStrokeColor;
     47 
     48     gradient_pos_[0] = SkFloatToScalar(0.9f);
     49     gradient_pos_[1] = SkFloatToScalar(1.0f);
     50 
     51     parent_widget->GetContentsView()->AddChildView(this);
     52 
     53     parent_widget->AddObserver(this);
     54   }
     55 
     56   void UpdateTouch(const ui::TouchEvent& touch) {
     57     if (touch.type() == ui::ET_TOUCH_RELEASED ||
     58         touch.type() == ui::ET_TOUCH_CANCELLED) {
     59       fadeout_.reset(new gfx::LinearAnimation(kFadeoutDurationInMs,
     60                                              kFadeoutFrameRate,
     61                                              this));
     62       fadeout_->Start();
     63     } else {
     64       SetX(parent()->GetMirroredXInView(touch.root_location().x()) -
     65                kPointRadius - 1);
     66       SetY(touch.root_location().y() - kPointRadius - 1);
     67     }
     68   }
     69 
     70   void Remove() {
     71     delete this;
     72   }
     73 
     74  private:
     75   virtual ~TouchPointView() {
     76     GetWidget()->RemoveObserver(this);
     77     parent()->RemoveChildView(this);
     78   }
     79 
     80   // Overridden from views::View.
     81   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
     82     int alpha = kProjectionAlpha;
     83     if (fadeout_)
     84       alpha = static_cast<int>(fadeout_->CurrentValueBetween(alpha, 0));
     85     fill_paint_.setAlpha(alpha);
     86     stroke_paint_.setAlpha(alpha);
     87     SkShader* shader = SkGradientShader::CreateRadial(
     88         gradient_center_,
     89         SkIntToScalar(kPointRadius),
     90         gradient_colors_,
     91         gradient_pos_,
     92         arraysize(gradient_colors_),
     93         SkShader::kMirror_TileMode);
     94     fill_paint_.setShader(shader);
     95     shader->unref();
     96     canvas->DrawCircle(circle_center_, SkIntToScalar(kPointRadius),
     97                        fill_paint_);
     98     canvas->DrawCircle(circle_center_, SkIntToScalar(kPointRadius),
     99                        stroke_paint_);
    100   }
    101 
    102   // Overridden from gfx::AnimationDelegate.
    103   virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE {
    104     DCHECK_EQ(fadeout_.get(), animation);
    105     delete this;
    106   }
    107 
    108   virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
    109     DCHECK_EQ(fadeout_.get(), animation);
    110     SchedulePaint();
    111   }
    112 
    113   virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE {
    114     AnimationEnded(animation);
    115   }
    116 
    117   // Overridden from views::WidgetObserver.
    118   virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE {
    119     if (fadeout_)
    120       fadeout_->Stop();
    121     else
    122       Remove();
    123   }
    124 
    125   const gfx::Point circle_center_;
    126   const SkPoint gradient_center_;
    127 
    128   SkPaint fill_paint_;
    129   SkPaint stroke_paint_;
    130   SkColor gradient_colors_[2];
    131   SkScalar gradient_pos_[2];
    132 
    133   scoped_ptr<gfx::Animation> fadeout_;
    134 
    135   DISALLOW_COPY_AND_ASSIGN(TouchPointView);
    136 };
    137 
    138 TouchHudProjection::TouchHudProjection(aura::Window* initial_root)
    139     : TouchObserverHUD(initial_root) {
    140 }
    141 
    142 TouchHudProjection::~TouchHudProjection() {
    143 }
    144 
    145 void TouchHudProjection::Clear() {
    146   for (std::map<int, TouchPointView*>::iterator iter = points_.begin();
    147       iter != points_.end(); iter++)
    148     iter->second->Remove();
    149   points_.clear();
    150 }
    151 
    152 void TouchHudProjection::OnTouchEvent(ui::TouchEvent* event) {
    153   if (event->type() == ui::ET_TOUCH_PRESSED) {
    154     TouchPointView* point = new TouchPointView(widget());
    155     point->UpdateTouch(*event);
    156     std::pair<std::map<int, TouchPointView*>::iterator, bool> result =
    157         points_.insert(std::make_pair(event->touch_id(), point));
    158     // If a |TouchPointView| is already mapped to the touch id, remove it and
    159     // replace it with the new one.
    160     if (!result.second) {
    161       result.first->second->Remove();
    162       result.first->second = point;
    163     }
    164   } else {
    165     std::map<int, TouchPointView*>::iterator iter =
    166         points_.find(event->touch_id());
    167     if (iter != points_.end()) {
    168       iter->second->UpdateTouch(*event);
    169       if (event->type() == ui::ET_TOUCH_RELEASED ||
    170           event->type() == ui::ET_TOUCH_CANCELLED)
    171         points_.erase(iter);
    172     }
    173   }
    174 }
    175 
    176 void TouchHudProjection::SetHudForRootWindowController(
    177     RootWindowController* controller) {
    178   controller->set_touch_hud_projection(this);
    179 }
    180 
    181 void TouchHudProjection::UnsetHudForRootWindowController(
    182     RootWindowController* controller) {
    183   controller->set_touch_hud_projection(NULL);
    184 }
    185 
    186 }  // namespace ash
    187