OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/browser/android/overscroll_glow.h" | |
6 | |
7 #include "cc/layers/layer.h" | |
8 #include "content/browser/android/edge_effect_base.h" | |
9 #include "content/public/browser/android/compositor.h" | |
10 | |
11 using std::max; | |
12 using std::min; | |
13 | |
14 namespace content { | |
15 | |
16 namespace { | |
17 | |
18 const float kEpsilon = 1e-3f; | |
19 | |
20 bool IsApproxZero(float value) { | |
21 return std::abs(value) < kEpsilon; | |
22 } | |
23 | |
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; | |
30 } | |
31 | |
32 } // namespace | |
33 | |
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); | |
41 } | |
42 | |
43 OverscrollGlow::~OverscrollGlow() { | |
44 Detach(); | |
45 } | |
46 | |
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(); | |
53 } | |
54 | |
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; | |
61 } | |
62 return false; | |
63 } | |
64 | |
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()); | |
70 } | |
71 return std::min(max_alpha, 1.f); | |
72 } | |
73 | |
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; | |
83 | |
84 if (!allow_horizontal_overscroll_) { | |
85 overscroll_delta.set_x(0); | |
86 velocity.set_x(0); | |
87 } | |
88 if (!allow_vertical_overscroll_) { | |
89 overscroll_delta.set_y(0); | |
90 velocity.set_y(0); | |
91 } | |
92 | |
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(); | |
99 } | |
100 | |
101 if (!InitializeIfNecessary()) | |
102 return false; | |
103 | |
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()); | |
109 | |
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); | |
115 | |
116 return CheckNeedsAnimate(); | |
117 } | |
118 | |
119 bool OverscrollGlow::Animate(base::TimeTicks current_time, | |
120 cc::Layer* parent_layer) { | |
121 DCHECK(parent_layer); | |
122 if (!CheckNeedsAnimate()) | |
123 return false; | |
124 | |
125 UpdateLayerAttachment(parent_layer); | |
126 | |
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]); | |
131 } | |
132 } | |
133 | |
134 return CheckNeedsAnimate(); | |
135 } | |
136 | |
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(); | |
149 | |
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()); | |
155 } | |
156 | |
157 bool OverscrollGlow::CheckNeedsAnimate() { | |
158 if (!initialized_) { | |
159 Detach(); | |
160 return false; | |
161 } | |
162 | |
163 if (IsActive()) | |
164 return true; | |
165 | |
166 Detach(); | |
167 return false; | |
168 } | |
169 | |
170 void OverscrollGlow::UpdateLayerAttachment(cc::Layer* parent) { | |
171 DCHECK(parent); | |
172 if (!root_layer_.get()) | |
173 return; | |
174 | |
175 if (!CheckNeedsAnimate()) | |
176 return; | |
177 | |
178 if (root_layer_->parent() != parent) | |
179 parent->AddChild(root_layer_); | |
180 | |
181 for (size_t i = 0; i < EDGE_COUNT; ++i) | |
182 edge_effects_[i]->SetParent(root_layer_.get()); | |
183 } | |
184 | |
185 void OverscrollGlow::Detach() { | |
186 if (root_layer_.get()) | |
187 root_layer_->RemoveFromParent(); | |
188 } | |
189 | |
190 bool OverscrollGlow::InitializeIfNecessary() { | |
191 if (initialized_) | |
192 return true; | |
193 | |
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]); | |
199 } | |
200 | |
201 initialized_ = true; | |
202 return true; | |
203 } | |
204 | |
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(); | |
213 | |
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 | |
221 }; | |
222 | |
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 | |
232 }; | |
233 | |
234 for (size_t i = 0; i < EDGE_COUNT; ++i) { | |
235 if (!edge_pull[i]) | |
236 continue; | |
237 | |
238 edge_effects_[i]->Pull( | |
239 current_time, std::abs(edge_pull[i]), edge_displacement[i]); | |
240 GetOppositeEdge(i)->Release(current_time); | |
241 } | |
242 } | |
243 | |
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()); | |
250 | |
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 | |
257 }; | |
258 | |
259 for (size_t i = 0; i < EDGE_COUNT; ++i) { | |
260 if (!overscroll_velocities[i]) | |
261 continue; | |
262 | |
263 edge_effects_[i]->Absorb(current_time, std::abs(overscroll_velocities[i])); | |
264 GetOppositeEdge(i)->Release(current_time); | |
265 } | |
266 } | |
267 | |
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); | |
272 } | |
273 | |
274 EdgeEffectBase* OverscrollGlow::GetOppositeEdge(int edge_index) { | |
275 DCHECK(initialized_); | |
276 return edge_effects_[(edge_index + 2) % EDGE_COUNT].get(); | |
277 } | |
278 | |
279 } // namespace content | |
OLD | NEW |