| 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 |