Add ICU message format support
[chromium-blink-merge.git] / content / browser / android / overscroll_glow.cc
blob8638be5efc5a9b7c5c1e9560a00856669de8b472
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 gfx::Transform ComputeTransform(OverscrollGlow::Edge edge,
33 const gfx::SizeF& window_size,
34 float offset) {
35 // Transforms assume the edge layers are anchored to their *top center point*.
36 switch (edge) {
37 case OverscrollGlow::EDGE_TOP:
38 return gfx::Transform(1, 0, 0, 1, 0, offset);
39 case OverscrollGlow::EDGE_LEFT:
40 return gfx::Transform(0,
42 -1,
44 -window_size.height() / 2.f + offset,
45 window_size.height() / 2.f);
46 case OverscrollGlow::EDGE_BOTTOM:
47 return gfx::Transform(-1, 0, 0, -1, 0, window_size.height() + offset);
48 case OverscrollGlow::EDGE_RIGHT:
49 return gfx::Transform(
51 -1,
54 -window_size.height() / 2.f + window_size.width() + offset,
55 window_size.height() / 2.f);
56 default:
57 NOTREACHED() << "Invalid edge: " << edge;
58 return gfx::Transform();
62 gfx::SizeF ComputeSize(OverscrollGlow::Edge edge,
63 const gfx::SizeF& window_size) {
64 switch (edge) {
65 case OverscrollGlow::EDGE_TOP:
66 case OverscrollGlow::EDGE_BOTTOM:
67 return window_size;
68 case OverscrollGlow::EDGE_LEFT:
69 case OverscrollGlow::EDGE_RIGHT:
70 return gfx::SizeF(window_size.height(), window_size.width());
71 default:
72 NOTREACHED() << "Invalid edge: " << edge;
73 return gfx::SizeF();
77 } // namespace
79 OverscrollGlow::OverscrollGlow(OverscrollGlowClient* client)
80 : client_(client),
81 edge_offsets_(),
82 initialized_(false),
83 allow_horizontal_overscroll_(true),
84 allow_vertical_overscroll_(true) {
85 DCHECK(client);
88 OverscrollGlow::~OverscrollGlow() {
89 Detach();
92 void OverscrollGlow::Reset() {
93 if (!initialized_)
94 return;
95 Detach();
96 for (size_t i = 0; i < EDGE_COUNT; ++i)
97 edge_effects_[i]->Finish();
100 bool OverscrollGlow::IsActive() const {
101 if (!initialized_)
102 return false;
103 for (size_t i = 0; i < EDGE_COUNT; ++i) {
104 if (!edge_effects_[i]->IsFinished())
105 return true;
107 return false;
110 float OverscrollGlow::GetVisibleAlpha() const {
111 float max_alpha = 0;
112 for (size_t i = 0; i < EDGE_COUNT; ++i) {
113 if (!edge_effects_[i]->IsFinished())
114 max_alpha = std::max(max_alpha, edge_effects_[i]->GetAlpha());
116 return std::min(max_alpha, 1.f);
119 bool OverscrollGlow::OnOverscrolled(base::TimeTicks current_time,
120 const gfx::Vector2dF& accumulated_overscroll,
121 gfx::Vector2dF overscroll_delta,
122 gfx::Vector2dF velocity,
123 const gfx::Vector2dF& overscroll_location) {
124 // The size of the glow determines the relative effect of the inputs; an
125 // empty-sized effect is effectively disabled.
126 if (viewport_size_.IsEmpty())
127 return false;
129 if (!allow_horizontal_overscroll_) {
130 overscroll_delta.set_x(0);
131 velocity.set_x(0);
133 if (!allow_vertical_overscroll_) {
134 overscroll_delta.set_y(0);
135 velocity.set_y(0);
138 // Ignore sufficiently small values that won't meaningfuly affect animation.
139 overscroll_delta = ZeroSmallComponents(overscroll_delta);
140 if (overscroll_delta.IsZero()) {
141 if (initialized_)
142 Release(current_time);
143 return CheckNeedsAnimate();
146 if (!InitializeIfNecessary())
147 return false;
149 gfx::Vector2dF old_overscroll = accumulated_overscroll - overscroll_delta;
150 bool x_overscroll_started =
151 !IsApproxZero(overscroll_delta.x()) && IsApproxZero(old_overscroll.x());
152 bool y_overscroll_started =
153 !IsApproxZero(overscroll_delta.y()) && IsApproxZero(old_overscroll.y());
155 velocity = ZeroSmallComponents(velocity);
156 if (!velocity.IsZero())
157 Absorb(current_time, velocity, x_overscroll_started, y_overscroll_started);
158 else
159 Pull(current_time, overscroll_delta, overscroll_location);
161 return CheckNeedsAnimate();
164 bool OverscrollGlow::Animate(base::TimeTicks current_time,
165 cc::Layer* parent_layer) {
166 DCHECK(parent_layer);
167 if (!CheckNeedsAnimate())
168 return false;
170 UpdateLayerAttachment(parent_layer);
172 for (size_t i = 0; i < EDGE_COUNT; ++i) {
173 if (edge_effects_[i]->Update(current_time)) {
174 Edge edge = static_cast<Edge>(i);
175 edge_effects_[i]->ApplyToLayers(
176 ComputeSize(edge, viewport_size_),
177 ComputeTransform(edge, viewport_size_, edge_offsets_[i]));
181 return CheckNeedsAnimate();
184 void OverscrollGlow::OnFrameUpdated(
185 const gfx::SizeF& viewport_size,
186 const gfx::SizeF& content_size,
187 const gfx::Vector2dF& content_scroll_offset) {
188 viewport_size_ = viewport_size;
189 edge_offsets_[OverscrollGlow::EDGE_TOP] = -content_scroll_offset.y();
190 edge_offsets_[OverscrollGlow::EDGE_LEFT] = -content_scroll_offset.x();
191 edge_offsets_[OverscrollGlow::EDGE_BOTTOM] = content_size.height() -
192 content_scroll_offset.y() -
193 viewport_size.height();
194 edge_offsets_[OverscrollGlow::EDGE_RIGHT] =
195 content_size.width() - content_scroll_offset.x() - viewport_size.width();
197 // Only allow overscroll on scrollable axes, matching platform behavior.
198 allow_horizontal_overscroll_ =
199 std::ceil(viewport_size_.width()) < std::floor(content_size.width());
200 allow_vertical_overscroll_ =
201 std::ceil(viewport_size_.height()) < std::floor(content_size.height());
204 bool OverscrollGlow::CheckNeedsAnimate() {
205 if (!initialized_) {
206 Detach();
207 return false;
210 if (IsActive())
211 return true;
213 Detach();
214 return false;
217 void OverscrollGlow::UpdateLayerAttachment(cc::Layer* parent) {
218 DCHECK(parent);
219 if (!root_layer_.get())
220 return;
222 if (!CheckNeedsAnimate())
223 return;
225 if (root_layer_->parent() != parent)
226 parent->AddChild(root_layer_);
228 for (size_t i = 0; i < EDGE_COUNT; ++i)
229 edge_effects_[i]->SetParent(root_layer_.get());
232 void OverscrollGlow::Detach() {
233 if (root_layer_.get())
234 root_layer_->RemoveFromParent();
237 bool OverscrollGlow::InitializeIfNecessary() {
238 if (initialized_)
239 return true;
241 DCHECK(!root_layer_.get());
242 root_layer_ = cc::Layer::Create(Compositor::LayerSettings());
243 for (size_t i = 0; i < EDGE_COUNT; ++i) {
244 edge_effects_[i] = client_->CreateEdgeEffect();
245 DCHECK(edge_effects_[i]);
248 initialized_ = true;
249 return true;
252 void OverscrollGlow::Pull(base::TimeTicks current_time,
253 const gfx::Vector2dF& overscroll_delta,
254 const gfx::Vector2dF& overscroll_location) {
255 DCHECK(initialized_);
256 DCHECK(!overscroll_delta.IsZero());
257 DCHECK(!viewport_size_.IsEmpty());
258 const float inv_width = 1.f / viewport_size_.width();
259 const float inv_height = 1.f / viewport_size_.height();
261 gfx::Vector2dF overscroll_pull =
262 gfx::ScaleVector2d(overscroll_delta, inv_width, inv_height);
263 const float edge_pull[EDGE_COUNT] = {
264 min(overscroll_pull.y(), 0.f), // Top
265 min(overscroll_pull.x(), 0.f), // Left
266 max(overscroll_pull.y(), 0.f), // Bottom
267 max(overscroll_pull.x(), 0.f) // Right
270 gfx::Vector2dF displacement =
271 gfx::ScaleVector2d(overscroll_location, inv_width, inv_height);
272 displacement.set_x(max(0.f, min(1.f, displacement.x())));
273 displacement.set_y(max(0.f, min(1.f, displacement.y())));
274 const float edge_displacement[EDGE_COUNT] = {
275 1.f - displacement.x(), // Top
276 displacement.y(), // Left
277 displacement.x(), // Bottom
278 1.f - displacement.y() // Right
281 for (size_t i = 0; i < EDGE_COUNT; ++i) {
282 if (!edge_pull[i])
283 continue;
285 edge_effects_[i]->Pull(
286 current_time, std::abs(edge_pull[i]), edge_displacement[i]);
287 GetOppositeEdge(i)->Release(current_time);
291 void OverscrollGlow::Absorb(base::TimeTicks current_time,
292 const gfx::Vector2dF& velocity,
293 bool x_overscroll_started,
294 bool y_overscroll_started) {
295 DCHECK(initialized_);
296 DCHECK(!velocity.IsZero());
298 // Only trigger on initial overscroll at a non-zero velocity
299 const float overscroll_velocities[EDGE_COUNT] = {
300 y_overscroll_started ? min(velocity.y(), 0.f) : 0, // Top
301 x_overscroll_started ? min(velocity.x(), 0.f) : 0, // Left
302 y_overscroll_started ? max(velocity.y(), 0.f) : 0, // Bottom
303 x_overscroll_started ? max(velocity.x(), 0.f) : 0 // Right
306 for (size_t i = 0; i < EDGE_COUNT; ++i) {
307 if (!overscroll_velocities[i])
308 continue;
310 edge_effects_[i]->Absorb(current_time, std::abs(overscroll_velocities[i]));
311 GetOppositeEdge(i)->Release(current_time);
315 void OverscrollGlow::Release(base::TimeTicks current_time) {
316 DCHECK(initialized_);
317 for (size_t i = 0; i < EDGE_COUNT; ++i)
318 edge_effects_[i]->Release(current_time);
321 EdgeEffectBase* OverscrollGlow::GetOppositeEdge(int edge_index) {
322 DCHECK(initialized_);
323 return edge_effects_[(edge_index + 2) % EDGE_COUNT].get();
326 } // namespace content