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