Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / content / browser / android / overscroll_glow.cc
blob8985b6a5796fd4b5efc878ab7d2d37d7fa51963e
1 // Copyright (c) 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.
5 #include "content/browser/android/overscroll_glow.h"
7 #include "cc/layers/layer.h"
8 #include "content/browser/android/edge_effect_base.h"
10 using std::max;
11 using std::min;
13 namespace content {
15 namespace {
17 const float kEpsilon = 1e-3f;
19 bool IsApproxZero(float value) {
20 return std::abs(value) < kEpsilon;
23 gfx::Vector2dF ZeroSmallComponents(gfx::Vector2dF vector) {
24 if (IsApproxZero(vector.x()))
25 vector.set_x(0);
26 if (IsApproxZero(vector.y()))
27 vector.set_y(0);
28 return vector;
31 gfx::Transform ComputeTransform(OverscrollGlow::Edge edge,
32 const gfx::SizeF& window_size,
33 float offset) {
34 // Transforms assume the edge layers are anchored to their *top center point*.
35 switch (edge) {
36 case OverscrollGlow::EDGE_TOP:
37 return gfx::Transform(1, 0, 0, 1, 0, offset);
38 case OverscrollGlow::EDGE_LEFT:
39 return gfx::Transform(0,
41 -1,
43 -window_size.height() / 2.f + offset,
44 window_size.height() / 2.f);
45 case OverscrollGlow::EDGE_BOTTOM:
46 return gfx::Transform(-1, 0, 0, -1, 0, window_size.height() + offset);
47 case OverscrollGlow::EDGE_RIGHT:
48 return gfx::Transform(
50 -1,
53 -window_size.height() / 2.f + window_size.width() + offset,
54 window_size.height() / 2.f);
55 default:
56 NOTREACHED() << "Invalid edge: " << edge;
57 return gfx::Transform();
61 gfx::SizeF ComputeSize(OverscrollGlow::Edge edge,
62 const gfx::SizeF& window_size) {
63 switch (edge) {
64 case OverscrollGlow::EDGE_TOP:
65 case OverscrollGlow::EDGE_BOTTOM:
66 return window_size;
67 case OverscrollGlow::EDGE_LEFT:
68 case OverscrollGlow::EDGE_RIGHT:
69 return gfx::SizeF(window_size.height(), window_size.width());
70 default:
71 NOTREACHED() << "Invalid edge: " << edge;
72 return gfx::SizeF();
76 } // namespace
78 OverscrollGlow::OverscrollGlow(OverscrollGlowClient* client)
79 : client_(client), edge_offsets_(), initialized_(false) {
80 DCHECK(client);
83 OverscrollGlow::~OverscrollGlow() {
84 Detach();
87 void OverscrollGlow::Reset() {
88 if (!initialized_)
89 return;
90 Detach();
91 for (size_t i = 0; i < EDGE_COUNT; ++i)
92 edge_effects_[i]->Finish();
95 bool OverscrollGlow::OnOverscrolled(base::TimeTicks current_time,
96 gfx::Vector2dF accumulated_overscroll,
97 gfx::Vector2dF overscroll_delta,
98 gfx::Vector2dF velocity,
99 gfx::Vector2dF displacement) {
100 // The size of the glow determines the relative effect of the inputs; an
101 // empty-sized effect is effectively disabled.
102 if (viewport_size_.IsEmpty())
103 return false;
105 // Ignore sufficiently small values that won't meaningfuly affect animation.
106 overscroll_delta = ZeroSmallComponents(overscroll_delta);
107 if (overscroll_delta.IsZero()) {
108 if (initialized_)
109 Release(current_time);
110 return CheckNeedsAnimate();
113 if (!InitializeIfNecessary())
114 return false;
116 gfx::Vector2dF old_overscroll = accumulated_overscroll - overscroll_delta;
117 bool x_overscroll_started =
118 !IsApproxZero(overscroll_delta.x()) && IsApproxZero(old_overscroll.x());
119 bool y_overscroll_started =
120 !IsApproxZero(overscroll_delta.y()) && IsApproxZero(old_overscroll.y());
122 velocity = ZeroSmallComponents(velocity);
123 if (!velocity.IsZero())
124 Absorb(current_time, velocity, x_overscroll_started, y_overscroll_started);
125 else
126 Pull(current_time, overscroll_delta, displacement);
128 return CheckNeedsAnimate();
131 bool OverscrollGlow::Animate(base::TimeTicks current_time,
132 cc::Layer* parent_layer) {
133 DCHECK(parent_layer);
134 if (!CheckNeedsAnimate())
135 return false;
137 UpdateLayerAttachment(parent_layer);
139 for (size_t i = 0; i < EDGE_COUNT; ++i) {
140 if (edge_effects_[i]->Update(current_time)) {
141 Edge edge = static_cast<Edge>(i);
142 edge_effects_[i]->ApplyToLayers(
143 ComputeSize(edge, viewport_size_),
144 ComputeTransform(edge, viewport_size_, edge_offsets_[i]));
148 return CheckNeedsAnimate();
151 void OverscrollGlow::UpdateDisplay(
152 const gfx::SizeF& viewport_size,
153 const gfx::SizeF& content_size,
154 const gfx::Vector2dF& content_scroll_offset) {
155 viewport_size_ = viewport_size;
156 edge_offsets_[OverscrollGlow::EDGE_TOP] = -content_scroll_offset.y();
157 edge_offsets_[OverscrollGlow::EDGE_LEFT] = -content_scroll_offset.x();
158 edge_offsets_[OverscrollGlow::EDGE_BOTTOM] = content_size.height() -
159 content_scroll_offset.y() -
160 viewport_size.height();
161 edge_offsets_[OverscrollGlow::EDGE_RIGHT] =
162 content_size.width() - content_scroll_offset.x() - viewport_size.width();
165 bool OverscrollGlow::CheckNeedsAnimate() {
166 if (!initialized_) {
167 Detach();
168 return false;
171 for (size_t i = 0; i < EDGE_COUNT; ++i) {
172 if (!edge_effects_[i]->IsFinished())
173 return true;
176 Detach();
177 return false;
180 void OverscrollGlow::UpdateLayerAttachment(cc::Layer* parent) {
181 DCHECK(parent);
182 if (!root_layer_.get())
183 return;
185 if (!CheckNeedsAnimate())
186 return;
188 if (root_layer_->parent() != parent)
189 parent->AddChild(root_layer_);
191 for (size_t i = 0; i < EDGE_COUNT; ++i)
192 edge_effects_[i]->SetParent(root_layer_.get());
195 void OverscrollGlow::Detach() {
196 if (root_layer_.get())
197 root_layer_->RemoveFromParent();
200 bool OverscrollGlow::InitializeIfNecessary() {
201 if (initialized_)
202 return true;
204 DCHECK(!root_layer_.get());
205 root_layer_ = cc::Layer::Create();
206 for (size_t i = 0; i < EDGE_COUNT; ++i) {
207 edge_effects_[i] = client_->CreateEdgeEffect();
208 DCHECK(edge_effects_[i]);
211 initialized_ = true;
212 return true;
215 void OverscrollGlow::Pull(base::TimeTicks current_time,
216 const gfx::Vector2dF& overscroll_delta,
217 const gfx::Vector2dF& overscroll_location) {
218 DCHECK(initialized_);
219 DCHECK(!overscroll_delta.IsZero());
220 DCHECK(!viewport_size_.IsEmpty());
221 const float inv_width = 1.f / viewport_size_.width();
222 const float inv_height = 1.f / viewport_size_.height();
224 gfx::Vector2dF overscroll_pull =
225 gfx::ScaleVector2d(overscroll_delta, inv_width, inv_height);
226 const float edge_pull[EDGE_COUNT] = {
227 min(overscroll_pull.y(), 0.f), // Top
228 min(overscroll_pull.x(), 0.f), // Left
229 max(overscroll_pull.y(), 0.f), // Bottom
230 max(overscroll_pull.x(), 0.f) // Right
233 gfx::Vector2dF displacement =
234 gfx::ScaleVector2d(overscroll_location, inv_width, inv_height);
235 displacement.set_x(max(0.f, min(1.f, displacement.x())));
236 displacement.set_y(max(0.f, min(1.f, displacement.y())));
237 const float edge_displacement[EDGE_COUNT] = {
238 1.f - displacement.x(), // Top
239 displacement.y(), // Left
240 displacement.x(), // Bottom
241 1.f - displacement.y() // Right
244 for (size_t i = 0; i < EDGE_COUNT; ++i) {
245 if (!edge_pull[i])
246 continue;
248 edge_effects_[i]->Pull(
249 current_time, std::abs(edge_pull[i]), edge_displacement[i]);
250 GetOppositeEdge(i)->Release(current_time);
254 void OverscrollGlow::Absorb(base::TimeTicks current_time,
255 const gfx::Vector2dF& velocity,
256 bool x_overscroll_started,
257 bool y_overscroll_started) {
258 DCHECK(initialized_);
259 DCHECK(!velocity.IsZero());
261 // Only trigger on initial overscroll at a non-zero velocity
262 const float overscroll_velocities[EDGE_COUNT] = {
263 y_overscroll_started ? min(velocity.y(), 0.f) : 0, // Top
264 x_overscroll_started ? min(velocity.x(), 0.f) : 0, // Left
265 y_overscroll_started ? max(velocity.y(), 0.f) : 0, // Bottom
266 x_overscroll_started ? max(velocity.x(), 0.f) : 0 // Right
269 for (size_t i = 0; i < EDGE_COUNT; ++i) {
270 if (!overscroll_velocities[i])
271 continue;
273 edge_effects_[i]->Absorb(current_time, std::abs(overscroll_velocities[i]));
274 GetOppositeEdge(i)->Release(current_time);
278 void OverscrollGlow::Release(base::TimeTicks current_time) {
279 DCHECK(initialized_);
280 for (size_t i = 0; i < EDGE_COUNT; ++i)
281 edge_effects_[i]->Release(current_time);
284 EdgeEffectBase* OverscrollGlow::GetOppositeEdge(int edge_index) {
285 DCHECK(initialized_);
286 return edge_effects_[(edge_index + 2) % EDGE_COUNT].get();
289 } // namespace content