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