Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / android / overscroll_glow.cc
blobedebea9e9e47fb96805bbd581160f4522fe3bd06
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"
9 #include "content/public/browser/android/compositor.h"
11 using std::max;
12 using std::min;
14 namespace content {
16 namespace {
18 const float kEpsilon = 1e-3f;
20 bool IsApproxZero(float value) {
21 return std::abs(value) < kEpsilon;
24 gfx::Vector2dF ZeroSmallComponents(gfx::Vector2dF vector) {
25 if (IsApproxZero(vector.x()))
26 vector.set_x(0);
27 if (IsApproxZero(vector.y()))
28 vector.set_y(0);
29 return vector;
32 } // namespace
34 OverscrollGlow::OverscrollGlow(OverscrollGlowClient* client)
35 : client_(client),
36 edge_offsets_(),
37 initialized_(false),
38 allow_horizontal_overscroll_(true),
39 allow_vertical_overscroll_(true) {
40 DCHECK(client);
43 OverscrollGlow::~OverscrollGlow() {
44 Detach();
47 void OverscrollGlow::Reset() {
48 if (!initialized_)
49 return;
50 Detach();
51 for (size_t i = 0; i < EDGE_COUNT; ++i)
52 edge_effects_[i]->Finish();
55 bool OverscrollGlow::IsActive() const {
56 if (!initialized_)
57 return false;
58 for (size_t i = 0; i < EDGE_COUNT; ++i) {
59 if (!edge_effects_[i]->IsFinished())
60 return true;
62 return false;
65 float OverscrollGlow::GetVisibleAlpha() const {
66 float max_alpha = 0;
67 for (size_t i = 0; i < EDGE_COUNT; ++i) {
68 if (!edge_effects_[i]->IsFinished())
69 max_alpha = std::max(max_alpha, edge_effects_[i]->GetAlpha());
71 return std::min(max_alpha, 1.f);
74 bool OverscrollGlow::OnOverscrolled(base::TimeTicks current_time,
75 const gfx::Vector2dF& accumulated_overscroll,
76 gfx::Vector2dF overscroll_delta,
77 gfx::Vector2dF velocity,
78 const gfx::Vector2dF& overscroll_location) {
79 // The size of the glow determines the relative effect of the inputs; an
80 // empty-sized effect is effectively disabled.
81 if (viewport_size_.IsEmpty())
82 return false;
84 if (!allow_horizontal_overscroll_) {
85 overscroll_delta.set_x(0);
86 velocity.set_x(0);
88 if (!allow_vertical_overscroll_) {
89 overscroll_delta.set_y(0);
90 velocity.set_y(0);
93 // Ignore sufficiently small values that won't meaningfuly affect animation.
94 overscroll_delta = ZeroSmallComponents(overscroll_delta);
95 if (overscroll_delta.IsZero()) {
96 if (initialized_)
97 Release(current_time);
98 return CheckNeedsAnimate();
101 if (!InitializeIfNecessary())
102 return false;
104 gfx::Vector2dF old_overscroll = accumulated_overscroll - overscroll_delta;
105 bool x_overscroll_started =
106 !IsApproxZero(overscroll_delta.x()) && IsApproxZero(old_overscroll.x());
107 bool y_overscroll_started =
108 !IsApproxZero(overscroll_delta.y()) && IsApproxZero(old_overscroll.y());
110 velocity = ZeroSmallComponents(velocity);
111 if (!velocity.IsZero())
112 Absorb(current_time, velocity, x_overscroll_started, y_overscroll_started);
113 else
114 Pull(current_time, overscroll_delta, overscroll_location);
116 return CheckNeedsAnimate();
119 bool OverscrollGlow::Animate(base::TimeTicks current_time,
120 cc::Layer* parent_layer) {
121 DCHECK(parent_layer);
122 if (!CheckNeedsAnimate())
123 return false;
125 UpdateLayerAttachment(parent_layer);
127 for (size_t i = 0; i < EDGE_COUNT; ++i) {
128 if (edge_effects_[i]->Update(current_time)) {
129 EdgeEffectBase::Edge edge = static_cast<EdgeEffectBase::Edge>(i);
130 edge_effects_[i]->ApplyToLayers(edge, viewport_size_, edge_offsets_[i]);
134 return CheckNeedsAnimate();
137 void OverscrollGlow::OnFrameUpdated(
138 const gfx::SizeF& viewport_size,
139 const gfx::SizeF& content_size,
140 const gfx::Vector2dF& content_scroll_offset) {
141 viewport_size_ = viewport_size;
142 edge_offsets_[EdgeEffectBase::EDGE_TOP] = -content_scroll_offset.y();
143 edge_offsets_[EdgeEffectBase::EDGE_LEFT] = -content_scroll_offset.x();
144 edge_offsets_[EdgeEffectBase::EDGE_BOTTOM] = content_size.height() -
145 content_scroll_offset.y() -
146 viewport_size.height();
147 edge_offsets_[EdgeEffectBase::EDGE_RIGHT] =
148 content_size.width() - content_scroll_offset.x() - viewport_size.width();
150 // Only allow overscroll on scrollable axes, matching platform behavior.
151 allow_horizontal_overscroll_ =
152 std::ceil(viewport_size_.width()) < std::floor(content_size.width());
153 allow_vertical_overscroll_ =
154 std::ceil(viewport_size_.height()) < std::floor(content_size.height());
157 bool OverscrollGlow::CheckNeedsAnimate() {
158 if (!initialized_) {
159 Detach();
160 return false;
163 if (IsActive())
164 return true;
166 Detach();
167 return false;
170 void OverscrollGlow::UpdateLayerAttachment(cc::Layer* parent) {
171 DCHECK(parent);
172 if (!root_layer_.get())
173 return;
175 if (!CheckNeedsAnimate())
176 return;
178 if (root_layer_->parent() != parent)
179 parent->AddChild(root_layer_);
181 for (size_t i = 0; i < EDGE_COUNT; ++i)
182 edge_effects_[i]->SetParent(root_layer_.get());
185 void OverscrollGlow::Detach() {
186 if (root_layer_.get())
187 root_layer_->RemoveFromParent();
190 bool OverscrollGlow::InitializeIfNecessary() {
191 if (initialized_)
192 return true;
194 DCHECK(!root_layer_.get());
195 root_layer_ = cc::Layer::Create(Compositor::LayerSettings());
196 for (size_t i = 0; i < EDGE_COUNT; ++i) {
197 edge_effects_[i] = client_->CreateEdgeEffect();
198 DCHECK(edge_effects_[i]);
201 initialized_ = true;
202 return true;
205 void OverscrollGlow::Pull(base::TimeTicks current_time,
206 const gfx::Vector2dF& overscroll_delta,
207 const gfx::Vector2dF& overscroll_location) {
208 DCHECK(initialized_);
209 DCHECK(!overscroll_delta.IsZero());
210 DCHECK(!viewport_size_.IsEmpty());
211 const float inv_width = 1.f / viewport_size_.width();
212 const float inv_height = 1.f / viewport_size_.height();
214 gfx::Vector2dF overscroll_pull =
215 gfx::ScaleVector2d(overscroll_delta, inv_width, inv_height);
216 const float edge_pull[EDGE_COUNT] = {
217 min(overscroll_pull.y(), 0.f), // Top
218 min(overscroll_pull.x(), 0.f), // Left
219 max(overscroll_pull.y(), 0.f), // Bottom
220 max(overscroll_pull.x(), 0.f) // Right
223 gfx::Vector2dF displacement =
224 gfx::ScaleVector2d(overscroll_location, inv_width, inv_height);
225 displacement.set_x(max(0.f, min(1.f, displacement.x())));
226 displacement.set_y(max(0.f, min(1.f, displacement.y())));
227 const float edge_displacement[EDGE_COUNT] = {
228 1.f - displacement.x(), // Top
229 displacement.y(), // Left
230 displacement.x(), // Bottom
231 1.f - displacement.y() // Right
234 for (size_t i = 0; i < EDGE_COUNT; ++i) {
235 if (!edge_pull[i])
236 continue;
238 edge_effects_[i]->Pull(
239 current_time, std::abs(edge_pull[i]), edge_displacement[i]);
240 GetOppositeEdge(i)->Release(current_time);
244 void OverscrollGlow::Absorb(base::TimeTicks current_time,
245 const gfx::Vector2dF& velocity,
246 bool x_overscroll_started,
247 bool y_overscroll_started) {
248 DCHECK(initialized_);
249 DCHECK(!velocity.IsZero());
251 // Only trigger on initial overscroll at a non-zero velocity
252 const float overscroll_velocities[EDGE_COUNT] = {
253 y_overscroll_started ? min(velocity.y(), 0.f) : 0, // Top
254 x_overscroll_started ? min(velocity.x(), 0.f) : 0, // Left
255 y_overscroll_started ? max(velocity.y(), 0.f) : 0, // Bottom
256 x_overscroll_started ? max(velocity.x(), 0.f) : 0 // Right
259 for (size_t i = 0; i < EDGE_COUNT; ++i) {
260 if (!overscroll_velocities[i])
261 continue;
263 edge_effects_[i]->Absorb(current_time, std::abs(overscroll_velocities[i]));
264 GetOppositeEdge(i)->Release(current_time);
268 void OverscrollGlow::Release(base::TimeTicks current_time) {
269 DCHECK(initialized_);
270 for (size_t i = 0; i < EDGE_COUNT; ++i)
271 edge_effects_[i]->Release(current_time);
274 EdgeEffectBase* OverscrollGlow::GetOppositeEdge(int edge_index) {
275 DCHECK(initialized_);
276 return edge_effects_[(edge_index + 2) % EDGE_COUNT].get();
279 } // namespace content