Only fsync leveldb's directory when the manifest is being updated.
[chromium-blink-merge.git] / ash / wm / gestures / long_press_affordance_handler.cc
blob723e3923cda5d58d356253d2e0f6f4fd8cb2b8c4
1 // Copyright (c) 2012 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.
5 #include "ash/wm/gestures/long_press_affordance_handler.h"
7 #include "ash/display/display_controller.h"
8 #include "ash/shell.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/shell_window_ids.h"
11 #include "ash/wm/property_util.h"
12 #include "third_party/skia/include/core/SkColor.h"
13 #include "third_party/skia/include/core/SkPaint.h"
14 #include "third_party/skia/include/core/SkPath.h"
15 #include "third_party/skia/include/core/SkRect.h"
16 #include "third_party/skia/include/effects/SkGradientShader.h"
17 #include "ui/aura/client/screen_position_client.h"
18 #include "ui/aura/root_window.h"
19 #include "ui/aura/window.h"
20 #include "ui/base/gestures/gesture_configuration.h"
21 #include "ui/base/gestures/gesture_util.h"
22 #include "ui/compositor/layer.h"
23 #include "ui/gfx/canvas.h"
24 #include "ui/gfx/screen.h"
25 #include "ui/gfx/transform.h"
26 #include "ui/views/view.h"
27 #include "ui/views/widget/widget.h"
29 namespace {
31 const int kAffordanceOuterRadius = 60;
32 const int kAffordanceInnerRadius = 50;
34 // Angles from x-axis at which the outer and inner circles start.
35 const int kAffordanceOuterStartAngle = -109;
36 const int kAffordanceInnerStartAngle = -65;
38 const int kAffordanceGlowWidth = 20;
39 // The following is half width to avoid division by 2.
40 const int kAffordanceArcWidth = 3;
42 // Start and end values for various animations.
43 const double kAffordanceScaleStartValue = 0.8;
44 const double kAffordanceScaleEndValue = 1.0;
45 const double kAffordanceShrinkScaleEndValue = 0.5;
46 const double kAffordanceOpacityStartValue = 0.1;
47 const double kAffordanceOpacityEndValue = 0.5;
48 const int kAffordanceAngleStartValue = 0;
49 // The end angle is a bit greater than 360 to make sure the circle completes at
50 // the end of the animation.
51 const int kAffordanceAngleEndValue = 380;
52 const int kAffordanceDelayBeforeShrinkMs = 200;
53 const int kAffordanceShrinkAnimationDurationMs = 100;
55 // Visual constants.
56 const SkColor kAffordanceGlowStartColor = SkColorSetARGB(24, 255, 255, 255);
57 const SkColor kAffordanceGlowEndColor = SkColorSetARGB(0, 255, 255, 255);
58 const SkColor kAffordanceArcColor = SkColorSetARGB(80, 0, 0, 0);
59 const int kAffordanceFrameRateHz = 60;
61 views::Widget* CreateAffordanceWidget(aura::RootWindow* root_window) {
62 views::Widget* widget = new views::Widget;
63 views::Widget::InitParams params;
64 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
65 params.keep_on_top = true;
66 params.accept_events = false;
67 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
68 params.context = root_window;
69 params.transparent = true;
70 widget->Init(params);
71 widget->SetOpacity(0xFF);
72 ash::GetRootWindowController(root_window)->GetContainer(
73 ash::internal::kShellWindowId_OverlayContainer)->AddChild(
74 widget->GetNativeWindow());
75 return widget;
78 void PaintAffordanceArc(gfx::Canvas* canvas,
79 gfx::Point& center,
80 int radius,
81 int start_angle,
82 int end_angle) {
83 SkPaint paint;
84 paint.setStyle(SkPaint::kStroke_Style);
85 paint.setStrokeWidth(2 * kAffordanceArcWidth);
86 paint.setColor(kAffordanceArcColor);
87 paint.setAntiAlias(true);
89 SkPath arc_path;
90 arc_path.addArc(SkRect::MakeXYWH(center.x() - radius,
91 center.y() - radius,
92 2 * radius,
93 2 * radius),
94 start_angle, end_angle);
95 canvas->DrawPath(arc_path, paint);
98 void PaintAffordanceGlow(gfx::Canvas* canvas,
99 gfx::Point& center,
100 int start_radius,
101 int end_radius,
102 SkColor* colors,
103 SkScalar* pos,
104 int num_colors) {
105 SkPoint sk_center;
106 int radius = (end_radius + start_radius) / 2;
107 int glow_width = end_radius - start_radius;
108 sk_center.iset(center.x(), center.y());
109 skia::RefPtr<SkShader> shader = skia::AdoptRef(
110 SkGradientShader::CreateTwoPointRadial(
111 sk_center,
112 SkIntToScalar(start_radius),
113 sk_center,
114 SkIntToScalar(end_radius),
115 colors,
116 pos,
117 num_colors,
118 SkShader::kClamp_TileMode));
119 DCHECK(shader);
120 SkPaint paint;
121 paint.setStyle(SkPaint::kStroke_Style);
122 paint.setStrokeWidth(glow_width);
123 paint.setShader(shader.get());
124 paint.setAntiAlias(true);
125 SkPath arc_path;
126 arc_path.addArc(SkRect::MakeXYWH(center.x() - radius,
127 center.y() - radius,
128 2 * radius,
129 2 * radius),
130 0, 360);
131 canvas->DrawPath(arc_path, paint);
134 } // namespace
136 namespace ash {
137 namespace internal {
139 // View of the LongPressAffordanceHandler. Draws the actual contents and
140 // updates as the animation proceeds. It also maintains the views::Widget that
141 // the animation is shown in.
142 class LongPressAffordanceHandler::LongPressAffordanceView
143 : public views::View {
144 public:
145 LongPressAffordanceView(const gfx::Point& event_location,
146 aura::RootWindow* root_window)
147 : views::View(),
148 widget_(CreateAffordanceWidget(root_window)),
149 current_angle_(kAffordanceAngleStartValue),
150 current_scale_(kAffordanceScaleStartValue) {
151 widget_->SetContentsView(this);
152 widget_->SetAlwaysOnTop(true);
154 // We are owned by the LongPressAffordance.
155 set_owned_by_client();
156 gfx::Point point = event_location;
157 aura::client::GetScreenPositionClient(root_window)->ConvertPointToScreen(
158 root_window, &point);
159 widget_->SetBounds(gfx::Rect(
160 point.x() - (kAffordanceOuterRadius + kAffordanceGlowWidth),
161 point.y() - (kAffordanceOuterRadius + kAffordanceGlowWidth),
162 GetPreferredSize().width(),
163 GetPreferredSize().height()));
164 widget_->Show();
165 widget_->GetNativeView()->layer()->SetOpacity(kAffordanceOpacityStartValue);
168 virtual ~LongPressAffordanceView() {
171 void UpdateWithGrowAnimation(ui::Animation* animation) {
172 // Update the portion of the circle filled so far and re-draw.
173 current_angle_ = animation->CurrentValueBetween(kAffordanceAngleStartValue,
174 kAffordanceAngleEndValue);
175 current_scale_ = animation->CurrentValueBetween(kAffordanceScaleStartValue,
176 kAffordanceScaleEndValue);
177 widget_->GetNativeView()->layer()->SetOpacity(
178 animation->CurrentValueBetween(kAffordanceOpacityStartValue,
179 kAffordanceOpacityEndValue));
180 SchedulePaint();
183 void UpdateWithShrinkAnimation(ui::Animation* animation) {
184 current_scale_ = animation->CurrentValueBetween(kAffordanceScaleEndValue,
185 kAffordanceShrinkScaleEndValue);
186 widget_->GetNativeView()->layer()->SetOpacity(
187 animation->CurrentValueBetween(kAffordanceOpacityEndValue,
188 kAffordanceOpacityStartValue));
189 SchedulePaint();
192 private:
193 // Overridden from views::View.
194 virtual gfx::Size GetPreferredSize() OVERRIDE {
195 return gfx::Size(2 * (kAffordanceOuterRadius + kAffordanceGlowWidth),
196 2 * (kAffordanceOuterRadius + kAffordanceGlowWidth));
199 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
200 gfx::Point center(GetPreferredSize().width() / 2,
201 GetPreferredSize().height() / 2);
202 canvas->Save();
204 gfx::Transform scale;
205 scale.Scale(current_scale_, current_scale_);
206 // We want to scale from the center.
207 canvas->Translate(center.OffsetFromOrigin());
208 canvas->Transform(scale);
209 canvas->Translate(-center.OffsetFromOrigin());
211 // Paint affordance glow
212 int start_radius = kAffordanceInnerRadius - kAffordanceGlowWidth;
213 int end_radius = kAffordanceOuterRadius + kAffordanceGlowWidth;
214 const int num_colors = 3;
215 SkScalar pos[num_colors] = {0, 0.5, 1};
216 SkColor colors[num_colors] = {kAffordanceGlowEndColor,
217 kAffordanceGlowStartColor, kAffordanceGlowEndColor};
218 PaintAffordanceGlow(canvas, center, start_radius, end_radius, colors, pos,
219 num_colors);
221 // Paint inner circle.
222 PaintAffordanceArc(canvas, center, kAffordanceInnerRadius,
223 kAffordanceInnerStartAngle, -current_angle_);
224 // Paint outer circle.
225 PaintAffordanceArc(canvas, center, kAffordanceOuterRadius,
226 kAffordanceOuterStartAngle, current_angle_);
228 canvas->Restore();
231 scoped_ptr<views::Widget> widget_;
232 int current_angle_;
233 double current_scale_;
235 DISALLOW_COPY_AND_ASSIGN(LongPressAffordanceView);
238 ////////////////////////////////////////////////////////////////////////////////
239 // LongPressAffordanceHandler, public
241 LongPressAffordanceHandler::LongPressAffordanceHandler()
242 : ui::LinearAnimation(kAffordanceFrameRateHz, this),
243 view_(NULL),
244 tap_down_touch_id_(-1),
245 tap_down_display_id_(0),
246 current_animation_type_(NONE) {
249 LongPressAffordanceHandler::~LongPressAffordanceHandler() {}
251 void LongPressAffordanceHandler::ProcessEvent(aura::Window* target,
252 ui::LocatedEvent* event,
253 int touch_id) {
254 // Once we have a touch id, we are only interested in event of that touch id.
255 if (tap_down_touch_id_ != -1 && tap_down_touch_id_ != touch_id)
256 return;
257 int64 timer_start_time_ms =
258 ui::GestureConfiguration::semi_long_press_time_in_seconds() * 1000;
259 switch (event->type()) {
260 case ui::ET_GESTURE_TAP_DOWN:
261 // Start animation.
262 tap_down_location_ = event->root_location();
263 tap_down_touch_id_ = touch_id;
264 current_animation_type_ = GROW_ANIMATION;
265 tap_down_display_id_ =
266 Shell::GetScreen()->GetDisplayNearestWindow(target).id();
267 timer_.Start(FROM_HERE,
268 base::TimeDelta::FromMilliseconds(timer_start_time_ms),
269 this,
270 &LongPressAffordanceHandler::StartAnimation);
271 break;
272 case ui::ET_TOUCH_MOVED:
273 // If animation is running, We want it to be robust to small finger
274 // movements. So we stop the animation only when the finger moves a
275 // certain distance.
276 if (!ui::gestures::IsInsideManhattanSquare(
277 event->root_location(), tap_down_location_))
278 StopAnimation();
279 break;
280 case ui::ET_TOUCH_CANCELLED:
281 case ui::ET_GESTURE_END:
282 // We will stop the animation on TOUCH_RELEASED.
283 break;
284 case ui::ET_GESTURE_LONG_PRESS:
285 if (is_animating())
286 End();
287 break;
288 default:
289 // On all other touch and gesture events, we hide the animation.
290 StopAnimation();
291 break;
295 ////////////////////////////////////////////////////////////////////////////////
296 // LongPressAffordanceHandler, private
298 void LongPressAffordanceHandler::StartAnimation() {
299 aura::RootWindow* root_window = NULL;
300 switch (current_animation_type_) {
301 case GROW_ANIMATION:
302 root_window = ash::Shell::GetInstance()->display_controller()->
303 GetRootWindowForDisplayId(tap_down_display_id_);
304 if (!root_window) {
305 StopAnimation();
306 return;
308 view_.reset(new LongPressAffordanceView(tap_down_location_, root_window));
309 SetDuration(
310 ui::GestureConfiguration::long_press_time_in_seconds() * 1000 -
311 ui::GestureConfiguration::semi_long_press_time_in_seconds() * 1000 -
312 kAffordanceDelayBeforeShrinkMs);
313 Start();
314 break;
315 case SHRINK_ANIMATION:
316 SetDuration(kAffordanceShrinkAnimationDurationMs);
317 Start();
318 break;
319 default:
320 NOTREACHED();
321 break;
325 void LongPressAffordanceHandler::StopAnimation() {
326 if (timer_.IsRunning())
327 timer_.Stop();
328 // Since, Animation::Stop() calls AnimationEnded(), we need to reset the
329 // |current_animation_type_| before Stop(), otherwise AnimationEnded() may
330 // start the timer again.
331 current_animation_type_ = NONE;
332 if (is_animating())
333 Stop();
334 view_.reset();
335 tap_down_touch_id_ = -1;
336 tap_down_display_id_ = 0;
339 void LongPressAffordanceHandler::AnimateToState(double state) {
340 DCHECK(view_.get());
341 switch (current_animation_type_) {
342 case GROW_ANIMATION:
343 view_->UpdateWithGrowAnimation(this);
344 break;
345 case SHRINK_ANIMATION:
346 view_->UpdateWithShrinkAnimation(this);
347 break;
348 default:
349 NOTREACHED();
350 break;
354 bool LongPressAffordanceHandler::ShouldSendCanceledFromStop() {
355 return false;
358 void LongPressAffordanceHandler::AnimationEnded(
359 const ui::Animation* animation) {
360 switch (current_animation_type_) {
361 case GROW_ANIMATION:
362 current_animation_type_ = SHRINK_ANIMATION;
363 timer_.Start(FROM_HERE,
364 base::TimeDelta::FromMilliseconds(kAffordanceDelayBeforeShrinkMs),
365 this, &LongPressAffordanceHandler::StartAnimation);
366 break;
367 case SHRINK_ANIMATION:
368 current_animation_type_ = NONE;
369 // fall through to reset the view.
370 default:
371 view_.reset();
372 tap_down_touch_id_ = -1;
373 tap_down_display_id_ = 0;
374 break;
378 } // namespace internal
379 } // namespace ash