Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / android / overscroll_glow.cc
blobf7eb4bb9b0d92ba5199bfc161e81cbba22512d86
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::IsActive() const {
96 if (!initialized_)
97 return false;
98 for (size_t i = 0; i < EDGE_COUNT; ++i) {
99 if (!edge_effects_[i]->IsFinished())
100 return true;
102 return false;
105 float OverscrollGlow::GetVisibleAlpha() const {
106 float max_alpha = 0;
107 for (size_t i = 0; i < EDGE_COUNT; ++i) {
108 if (!edge_effects_[i]->IsFinished())
109 max_alpha = std::max(max_alpha, edge_effects_[i]->GetAlpha());
111 return std::min(max_alpha, 1.f);
114 bool OverscrollGlow::OnOverscrolled(base::TimeTicks current_time,
115 gfx::Vector2dF accumulated_overscroll,
116 gfx::Vector2dF overscroll_delta,
117 gfx::Vector2dF velocity,
118 gfx::Vector2dF displacement) {
119 // The size of the glow determines the relative effect of the inputs; an
120 // empty-sized effect is effectively disabled.
121 if (viewport_size_.IsEmpty())
122 return false;
124 // Ignore sufficiently small values that won't meaningfuly affect animation.
125 overscroll_delta = ZeroSmallComponents(overscroll_delta);
126 if (overscroll_delta.IsZero()) {
127 if (initialized_)
128 Release(current_time);
129 return CheckNeedsAnimate();
132 if (!InitializeIfNecessary())
133 return false;
135 gfx::Vector2dF old_overscroll = accumulated_overscroll - overscroll_delta;
136 bool x_overscroll_started =
137 !IsApproxZero(overscroll_delta.x()) && IsApproxZero(old_overscroll.x());
138 bool y_overscroll_started =
139 !IsApproxZero(overscroll_delta.y()) && IsApproxZero(old_overscroll.y());
141 velocity = ZeroSmallComponents(velocity);
142 if (!velocity.IsZero())
143 Absorb(current_time, velocity, x_overscroll_started, y_overscroll_started);
144 else
145 Pull(current_time, overscroll_delta, displacement);
147 return CheckNeedsAnimate();
150 bool OverscrollGlow::Animate(base::TimeTicks current_time,
151 cc::Layer* parent_layer) {
152 DCHECK(parent_layer);
153 if (!CheckNeedsAnimate())
154 return false;
156 UpdateLayerAttachment(parent_layer);
158 for (size_t i = 0; i < EDGE_COUNT; ++i) {
159 if (edge_effects_[i]->Update(current_time)) {
160 Edge edge = static_cast<Edge>(i);
161 edge_effects_[i]->ApplyToLayers(
162 ComputeSize(edge, viewport_size_),
163 ComputeTransform(edge, viewport_size_, edge_offsets_[i]));
167 return CheckNeedsAnimate();
170 void OverscrollGlow::UpdateDisplay(
171 const gfx::SizeF& viewport_size,
172 const gfx::SizeF& content_size,
173 const gfx::Vector2dF& content_scroll_offset) {
174 viewport_size_ = viewport_size;
175 edge_offsets_[OverscrollGlow::EDGE_TOP] = -content_scroll_offset.y();
176 edge_offsets_[OverscrollGlow::EDGE_LEFT] = -content_scroll_offset.x();
177 edge_offsets_[OverscrollGlow::EDGE_BOTTOM] = content_size.height() -
178 content_scroll_offset.y() -
179 viewport_size.height();
180 edge_offsets_[OverscrollGlow::EDGE_RIGHT] =
181 content_size.width() - content_scroll_offset.x() - viewport_size.width();
184 bool OverscrollGlow::CheckNeedsAnimate() {
185 if (!initialized_) {
186 Detach();
187 return false;
190 if (IsActive())
191 return true;
193 Detach();
194 return false;
197 void OverscrollGlow::UpdateLayerAttachment(cc::Layer* parent) {
198 DCHECK(parent);
199 if (!root_layer_.get())
200 return;
202 if (!CheckNeedsAnimate())
203 return;
205 if (root_layer_->parent() != parent)
206 parent->AddChild(root_layer_);
208 for (size_t i = 0; i < EDGE_COUNT; ++i)
209 edge_effects_[i]->SetParent(root_layer_.get());
212 void OverscrollGlow::Detach() {
213 if (root_layer_.get())
214 root_layer_->RemoveFromParent();
217 bool OverscrollGlow::InitializeIfNecessary() {
218 if (initialized_)
219 return true;
221 DCHECK(!root_layer_.get());
222 root_layer_ = cc::Layer::Create();
223 for (size_t i = 0; i < EDGE_COUNT; ++i) {
224 edge_effects_[i] = client_->CreateEdgeEffect();
225 DCHECK(edge_effects_[i]);
228 initialized_ = true;
229 return true;
232 void OverscrollGlow::Pull(base::TimeTicks current_time,
233 const gfx::Vector2dF& overscroll_delta,
234 const gfx::Vector2dF& overscroll_location) {
235 DCHECK(initialized_);
236 DCHECK(!overscroll_delta.IsZero());
237 DCHECK(!viewport_size_.IsEmpty());
238 const float inv_width = 1.f / viewport_size_.width();
239 const float inv_height = 1.f / viewport_size_.height();
241 gfx::Vector2dF overscroll_pull =
242 gfx::ScaleVector2d(overscroll_delta, inv_width, inv_height);
243 const float edge_pull[EDGE_COUNT] = {
244 min(overscroll_pull.y(), 0.f), // Top
245 min(overscroll_pull.x(), 0.f), // Left
246 max(overscroll_pull.y(), 0.f), // Bottom
247 max(overscroll_pull.x(), 0.f) // Right
250 gfx::Vector2dF displacement =
251 gfx::ScaleVector2d(overscroll_location, inv_width, inv_height);
252 displacement.set_x(max(0.f, min(1.f, displacement.x())));
253 displacement.set_y(max(0.f, min(1.f, displacement.y())));
254 const float edge_displacement[EDGE_COUNT] = {
255 1.f - displacement.x(), // Top
256 displacement.y(), // Left
257 displacement.x(), // Bottom
258 1.f - displacement.y() // Right
261 for (size_t i = 0; i < EDGE_COUNT; ++i) {
262 if (!edge_pull[i])
263 continue;
265 edge_effects_[i]->Pull(
266 current_time, std::abs(edge_pull[i]), edge_displacement[i]);
267 GetOppositeEdge(i)->Release(current_time);
271 void OverscrollGlow::Absorb(base::TimeTicks current_time,
272 const gfx::Vector2dF& velocity,
273 bool x_overscroll_started,
274 bool y_overscroll_started) {
275 DCHECK(initialized_);
276 DCHECK(!velocity.IsZero());
278 // Only trigger on initial overscroll at a non-zero velocity
279 const float overscroll_velocities[EDGE_COUNT] = {
280 y_overscroll_started ? min(velocity.y(), 0.f) : 0, // Top
281 x_overscroll_started ? min(velocity.x(), 0.f) : 0, // Left
282 y_overscroll_started ? max(velocity.y(), 0.f) : 0, // Bottom
283 x_overscroll_started ? max(velocity.x(), 0.f) : 0 // Right
286 for (size_t i = 0; i < EDGE_COUNT; ++i) {
287 if (!overscroll_velocities[i])
288 continue;
290 edge_effects_[i]->Absorb(current_time, std::abs(overscroll_velocities[i]));
291 GetOppositeEdge(i)->Release(current_time);
295 void OverscrollGlow::Release(base::TimeTicks current_time) {
296 DCHECK(initialized_);
297 for (size_t i = 0; i < EDGE_COUNT; ++i)
298 edge_effects_[i]->Release(current_time);
301 EdgeEffectBase* OverscrollGlow::GetOppositeEdge(int edge_index) {
302 DCHECK(initialized_);
303 return edge_effects_[(edge_index + 2) % EDGE_COUNT].get();
306 } // namespace content