1// Copyright 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 "cc/input/top_controls_manager.h" 6 7#include <algorithm> 8 9#include "base/logging.h" 10#include "base/time/time.h" 11#include "cc/animation/keyframed_animation_curve.h" 12#include "cc/animation/timing_function.h" 13#include "cc/input/top_controls_manager_client.h" 14#include "cc/trees/layer_tree_impl.h" 15#include "ui/gfx/transform.h" 16#include "ui/gfx/vector2d_f.h" 17 18namespace cc { 19namespace { 20// These constants were chosen empirically for their visually pleasant behavior. 21// Contact tedchoc@chromium.org for questions about changing these values. 22const int64 kShowHideMaxDurationMs = 200; 23} 24 25// static 26scoped_ptr<TopControlsManager> TopControlsManager::Create( 27 TopControlsManagerClient* client, 28 float top_controls_height, 29 float top_controls_show_threshold, 30 float top_controls_hide_threshold) { 31 return make_scoped_ptr(new TopControlsManager(client, 32 top_controls_height, 33 top_controls_show_threshold, 34 top_controls_hide_threshold)); 35} 36 37TopControlsManager::TopControlsManager(TopControlsManagerClient* client, 38 float top_controls_height, 39 float top_controls_show_threshold, 40 float top_controls_hide_threshold) 41 : client_(client), 42 animation_direction_(NO_ANIMATION), 43 permitted_state_(BOTH), 44 controls_top_offset_(0.f), 45 top_controls_height_(top_controls_height), 46 current_scroll_delta_(0.f), 47 controls_scroll_begin_offset_(0.f), 48 top_controls_show_height_( 49 top_controls_height * top_controls_hide_threshold), 50 top_controls_hide_height_( 51 top_controls_height * (1.f - top_controls_show_threshold)) { 52 CHECK(client_); 53} 54 55TopControlsManager::~TopControlsManager() { 56} 57 58void TopControlsManager::UpdateTopControlsState(TopControlsState constraints, 59 TopControlsState current, 60 bool animate) { 61 DCHECK(!(constraints == SHOWN && current == HIDDEN)); 62 DCHECK(!(constraints == HIDDEN && current == SHOWN)); 63 64 permitted_state_ = constraints; 65 66 // Don't do anything if it doesn't matter which state the controls are in. 67 if (constraints == BOTH && current == BOTH) 68 return; 69 70 // Don't do anything if there is no change in offset. 71 float final_controls_position = 0.f; 72 if (constraints == HIDDEN || current == HIDDEN) { 73 final_controls_position = -top_controls_height_; 74 } 75 if (final_controls_position == controls_top_offset_) { 76 return; 77 } 78 79 AnimationDirection animation_direction = SHOWING_CONTROLS; 80 if (constraints == HIDDEN || current == HIDDEN) 81 animation_direction = HIDING_CONTROLS; 82 ResetAnimations(); 83 if (animate) { 84 SetupAnimation(animation_direction); 85 } else { 86 controls_top_offset_ = final_controls_position; 87 } 88 client_->DidChangeTopControlsPosition(); 89} 90 91void TopControlsManager::ScrollBegin() { 92 ResetAnimations(); 93 current_scroll_delta_ = 0.f; 94 controls_scroll_begin_offset_ = controls_top_offset_; 95} 96 97gfx::Vector2dF TopControlsManager::ScrollBy( 98 const gfx::Vector2dF pending_delta) { 99 if (permitted_state_ == SHOWN && pending_delta.y() > 0) 100 return pending_delta; 101 else if (permitted_state_ == HIDDEN && pending_delta.y() < 0) 102 return pending_delta; 103 104 current_scroll_delta_ += pending_delta.y(); 105 106 float old_offset = controls_top_offset_; 107 SetControlsTopOffset(controls_scroll_begin_offset_ - current_scroll_delta_); 108 109 // If the controls are fully visible, treat the current position as the 110 // new baseline even if the gesture didn't end. 111 if (controls_top_offset_ == 0.f) { 112 current_scroll_delta_ = 0.f; 113 controls_scroll_begin_offset_ = 0.f; 114 } 115 116 ResetAnimations(); 117 118 gfx::Vector2dF applied_delta(0.f, old_offset - controls_top_offset_); 119 return pending_delta - applied_delta; 120} 121 122void TopControlsManager::ScrollEnd() { 123 StartAnimationIfNecessary(); 124} 125 126void TopControlsManager::SetControlsTopOffset(float controls_top_offset) { 127 controls_top_offset = std::max(controls_top_offset, -top_controls_height_); 128 controls_top_offset = std::min(controls_top_offset, 0.f); 129 130 if (controls_top_offset_ == controls_top_offset) 131 return; 132 133 controls_top_offset_ = controls_top_offset; 134 135 client_->DidChangeTopControlsPosition(); 136} 137 138gfx::Vector2dF TopControlsManager::Animate(base::TimeTicks monotonic_time) { 139 if (!top_controls_animation_ || !client_->HaveRootScrollLayer()) 140 return gfx::Vector2dF(); 141 142 double time = (monotonic_time - base::TimeTicks()).InMillisecondsF(); 143 144 float old_offset = controls_top_offset_; 145 SetControlsTopOffset(top_controls_animation_->GetValue(time)); 146 147 if (IsAnimationCompleteAtTime(monotonic_time)) 148 ResetAnimations(); 149 150 gfx::Vector2dF scroll_delta(0.f, controls_top_offset_ - old_offset); 151 return scroll_delta; 152} 153 154void TopControlsManager::ResetAnimations() { 155 if (top_controls_animation_) 156 top_controls_animation_.reset(); 157 158 animation_direction_ = NO_ANIMATION; 159} 160 161void TopControlsManager::SetupAnimation(AnimationDirection direction) { 162 DCHECK(direction != NO_ANIMATION); 163 164 if (direction == SHOWING_CONTROLS && controls_top_offset_ == 0) 165 return; 166 167 if (direction == HIDING_CONTROLS && 168 controls_top_offset_ == -top_controls_height_) { 169 return; 170 } 171 172 if (top_controls_animation_ && animation_direction_ == direction) 173 return; 174 175 top_controls_animation_ = KeyframedFloatAnimationCurve::Create(); 176 double start_time = 177 (base::TimeTicks::Now() - base::TimeTicks()).InMillisecondsF(); 178 top_controls_animation_->AddKeyframe( 179 FloatKeyframe::Create(start_time, controls_top_offset_, 180 scoped_ptr<TimingFunction>())); 181 float max_ending_offset = 182 (direction == SHOWING_CONTROLS ? 1 : -1) * top_controls_height_; 183 top_controls_animation_->AddKeyframe( 184 FloatKeyframe::Create(start_time + kShowHideMaxDurationMs, 185 controls_top_offset_ + max_ending_offset, 186 EaseTimingFunction::Create())); 187 animation_direction_ = direction; 188 client_->DidChangeTopControlsPosition(); 189} 190 191void TopControlsManager::StartAnimationIfNecessary() { 192 if (controls_top_offset_ != 0 193 && controls_top_offset_ != -top_controls_height_) { 194 AnimationDirection show_controls = NO_ANIMATION; 195 196 if (controls_top_offset_ >= -top_controls_show_height_) { 197 // If we're showing so much that the hide threshold won't trigger, show. 198 show_controls = SHOWING_CONTROLS; 199 } else if (controls_top_offset_ <= -top_controls_hide_height_) { 200 // If we're showing so little that the show threshold won't trigger, hide. 201 show_controls = HIDING_CONTROLS; 202 } else { 203 // If we could be either showing or hiding, we determine which one to 204 // do based on whether or not the total scroll delta was moving up or 205 // down. 206 show_controls = current_scroll_delta_ <= 0.f ? 207 SHOWING_CONTROLS : HIDING_CONTROLS; 208 } 209 210 if (show_controls != NO_ANIMATION) 211 SetupAnimation(show_controls); 212 } 213} 214 215bool TopControlsManager::IsAnimationCompleteAtTime(base::TimeTicks time) { 216 if (!top_controls_animation_) 217 return true; 218 219 double time_ms = (time - base::TimeTicks()).InMillisecondsF(); 220 float new_offset = top_controls_animation_->GetValue(time_ms); 221 222 if ((animation_direction_ == SHOWING_CONTROLS && new_offset >= 0) || 223 (animation_direction_ == HIDING_CONTROLS 224 && new_offset <= -top_controls_height_)) { 225 return true; 226 } 227 return false; 228} 229 230} // namespace cc 231