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