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.
5 #include "cc/input/top_controls_manager.h"
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/geometry/vector2d_f.h"
17 #include "ui/gfx/transform.h"
21 // These constants were chosen empirically for their visually pleasant behavior.
22 // Contact tedchoc@chromium.org for questions about changing these values.
23 const int64 kShowHideMaxDurationMs
= 200;
27 scoped_ptr
<TopControlsManager
> TopControlsManager::Create(
28 TopControlsManagerClient
* client
,
29 float top_controls_show_threshold
,
30 float top_controls_hide_threshold
) {
31 return make_scoped_ptr(new TopControlsManager(client
,
32 top_controls_show_threshold
,
33 top_controls_hide_threshold
));
36 TopControlsManager::TopControlsManager(TopControlsManagerClient
* client
,
37 float top_controls_show_threshold
,
38 float top_controls_hide_threshold
)
40 animation_direction_(NO_ANIMATION
),
41 permitted_state_(BOTH
),
42 accumulated_scroll_delta_(0.f
),
43 baseline_content_offset_(0.f
),
44 top_controls_show_threshold_(top_controls_hide_threshold
),
45 top_controls_hide_threshold_(top_controls_show_threshold
),
46 pinch_gesture_active_(false) {
50 TopControlsManager::~TopControlsManager() {
53 float TopControlsManager::ControlsTopOffset() const {
54 return ContentTopOffset() - TopControlsHeight();
57 float TopControlsManager::ContentTopOffset() const {
58 return TopControlsShownRatio() * TopControlsHeight();
61 float TopControlsManager::TopControlsShownRatio() const {
62 return client_
->CurrentTopControlsShownRatio();
65 float TopControlsManager::TopControlsHeight() const {
66 return client_
->TopControlsHeight();
69 void TopControlsManager::UpdateTopControlsState(TopControlsState constraints
,
70 TopControlsState current
,
72 DCHECK(!(constraints
== SHOWN
&& current
== HIDDEN
));
73 DCHECK(!(constraints
== HIDDEN
&& current
== SHOWN
));
75 permitted_state_
= constraints
;
77 // Don't do anything if it doesn't matter which state the controls are in.
78 if (constraints
== BOTH
&& current
== BOTH
)
81 // Don't do anything if there is no change in offset.
82 float final_shown_ratio
= 1.f
;
83 if (constraints
== HIDDEN
|| current
== HIDDEN
)
84 final_shown_ratio
= 0.f
;
85 if (final_shown_ratio
== TopControlsShownRatio())
89 SetupAnimation(final_shown_ratio
? SHOWING_CONTROLS
: HIDING_CONTROLS
);
92 client_
->SetCurrentTopControlsShownRatio(final_shown_ratio
);
96 void TopControlsManager::ScrollBegin() {
97 DCHECK(!pinch_gesture_active_
);
102 gfx::Vector2dF
TopControlsManager::ScrollBy(
103 const gfx::Vector2dF
& pending_delta
) {
104 if (!TopControlsHeight())
105 return pending_delta
;
107 if (pinch_gesture_active_
)
108 return pending_delta
;
110 if (!TopControlsHeight())
111 return pending_delta
;
113 if (permitted_state_
== SHOWN
&& pending_delta
.y() > 0)
114 return pending_delta
;
115 else if (permitted_state_
== HIDDEN
&& pending_delta
.y() < 0)
116 return pending_delta
;
118 accumulated_scroll_delta_
+= pending_delta
.y();
120 float old_offset
= ContentTopOffset();
121 client_
->SetCurrentTopControlsShownRatio(
122 (baseline_content_offset_
- accumulated_scroll_delta_
) /
123 TopControlsHeight());
125 // If the controls are fully visible, treat the current position as the
126 // new baseline even if the gesture didn't end.
127 if (TopControlsShownRatio() == 1.f
)
132 gfx::Vector2dF
applied_delta(0.f
, old_offset
- ContentTopOffset());
133 return pending_delta
- applied_delta
;
136 void TopControlsManager::ScrollEnd() {
137 DCHECK(!pinch_gesture_active_
);
138 StartAnimationIfNecessary();
141 void TopControlsManager::PinchBegin() {
142 DCHECK(!pinch_gesture_active_
);
143 pinch_gesture_active_
= true;
144 StartAnimationIfNecessary();
147 void TopControlsManager::PinchEnd() {
148 DCHECK(pinch_gesture_active_
);
149 // Pinch{Begin,End} will always occur within the scope of Scroll{Begin,End},
150 // so return to a state expected by the remaining scroll sequence.
151 pinch_gesture_active_
= false;
155 void TopControlsManager::MainThreadHasStoppedFlinging() {
156 StartAnimationIfNecessary();
159 gfx::Vector2dF
TopControlsManager::Animate(base::TimeTicks monotonic_time
) {
160 if (!top_controls_animation_
|| !client_
->HaveRootScrollLayer())
161 return gfx::Vector2dF();
163 base::TimeDelta time
= monotonic_time
- base::TimeTicks();
165 float old_offset
= ContentTopOffset();
166 client_
->SetCurrentTopControlsShownRatio(
167 top_controls_animation_
->GetValue(time
));
169 if (IsAnimationCompleteAtTime(monotonic_time
))
172 gfx::Vector2dF
scroll_delta(0.f
, ContentTopOffset() - old_offset
);
176 void TopControlsManager::ResetAnimations() {
177 top_controls_animation_
= nullptr;
178 animation_direction_
= NO_ANIMATION
;
181 void TopControlsManager::SetupAnimation(AnimationDirection direction
) {
182 DCHECK_NE(NO_ANIMATION
, direction
);
183 DCHECK_IMPLIES(direction
== HIDING_CONTROLS
, TopControlsShownRatio() > 0.f
);
184 DCHECK_IMPLIES(direction
== SHOWING_CONTROLS
, TopControlsShownRatio() < 1.f
);
186 if (top_controls_animation_
&& animation_direction_
== direction
)
189 if (!TopControlsHeight()) {
190 client_
->SetCurrentTopControlsShownRatio(
191 direction
== HIDING_CONTROLS
? 0.f
: 1.f
);
195 top_controls_animation_
= KeyframedFloatAnimationCurve::Create();
196 base::TimeDelta start_time
= gfx::FrameTime::Now() - base::TimeTicks();
197 top_controls_animation_
->AddKeyframe(
198 FloatKeyframe::Create(start_time
, TopControlsShownRatio(), nullptr));
199 float max_ending_ratio
= (direction
== SHOWING_CONTROLS
? 1 : -1);
200 top_controls_animation_
->AddKeyframe(FloatKeyframe::Create(
201 start_time
+ base::TimeDelta::FromMilliseconds(kShowHideMaxDurationMs
),
202 TopControlsShownRatio() + max_ending_ratio
,
203 EaseTimingFunction::Create()));
204 animation_direction_
= direction
;
205 client_
->DidChangeTopControlsPosition();
208 void TopControlsManager::StartAnimationIfNecessary() {
209 if (TopControlsShownRatio() == 0.f
|| TopControlsShownRatio() == 1.f
)
212 if (TopControlsShownRatio() >= 1.f
- top_controls_hide_threshold_
) {
213 // If we're showing so much that the hide threshold won't trigger, show.
214 SetupAnimation(SHOWING_CONTROLS
);
215 } else if (TopControlsShownRatio() <= top_controls_show_threshold_
) {
216 // If we're showing so little that the show threshold won't trigger, hide.
217 SetupAnimation(HIDING_CONTROLS
);
219 // If we could be either showing or hiding, we determine which one to
220 // do based on whether or not the total scroll delta was moving up or
222 SetupAnimation(accumulated_scroll_delta_
<= 0.f
? SHOWING_CONTROLS
227 bool TopControlsManager::IsAnimationCompleteAtTime(base::TimeTicks time
) {
228 if (!top_controls_animation_
)
231 base::TimeDelta animation_time
= time
- base::TimeTicks();
232 float new_ratio
= top_controls_animation_
->GetValue(animation_time
);
234 if ((animation_direction_
== SHOWING_CONTROLS
&& new_ratio
>= 1.f
) ||
235 (animation_direction_
== HIDING_CONTROLS
&& new_ratio
<= 0.f
)) {
241 void TopControlsManager::ResetBaseline() {
242 accumulated_scroll_delta_
= 0.f
;
243 baseline_content_offset_
= ContentTopOffset();