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/transform.h"
17 #include "ui/gfx/vector2d_f.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_height
,
30 float top_controls_show_threshold
,
31 float top_controls_hide_threshold
) {
32 return make_scoped_ptr(new TopControlsManager(client
,
34 top_controls_show_threshold
,
35 top_controls_hide_threshold
));
38 TopControlsManager::TopControlsManager(TopControlsManagerClient
* client
,
39 float top_controls_height
,
40 float top_controls_show_threshold
,
41 float top_controls_hide_threshold
)
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) {
57 TopControlsManager::~TopControlsManager() {
60 void TopControlsManager::UpdateTopControlsState(TopControlsState constraints
,
61 TopControlsState current
,
63 DCHECK(!(constraints
== SHOWN
&& current
== HIDDEN
));
64 DCHECK(!(constraints
== HIDDEN
&& current
== SHOWN
));
66 permitted_state_
= constraints
;
68 // Don't do anything if it doesn't matter which state the controls are in.
69 if (constraints
== BOTH
&& current
== BOTH
)
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_
;
77 if (final_controls_position
== controls_top_offset_
) {
81 AnimationDirection animation_direction
= SHOWING_CONTROLS
;
82 if (constraints
== HIDDEN
|| current
== HIDDEN
)
83 animation_direction
= HIDING_CONTROLS
;
86 SetupAnimation(animation_direction
);
88 controls_top_offset_
= final_controls_position
;
90 client_
->DidChangeTopControlsPosition();
93 void TopControlsManager::ScrollBegin() {
94 DCHECK(!pinch_gesture_active_
);
96 current_scroll_delta_
= 0.f
;
97 controls_scroll_begin_offset_
= controls_top_offset_
;
100 gfx::Vector2dF
TopControlsManager::ScrollBy(
101 const gfx::Vector2dF
& pending_delta
) {
102 if (pinch_gesture_active_
)
103 return pending_delta
;
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
;
110 current_scroll_delta_
+= pending_delta
.y();
112 float old_offset
= controls_top_offset_
;
113 SetControlsTopOffset(controls_scroll_begin_offset_
- current_scroll_delta_
);
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
;
124 gfx::Vector2dF
applied_delta(0.f
, old_offset
- controls_top_offset_
);
125 return pending_delta
- applied_delta
;
128 void TopControlsManager::ScrollEnd() {
129 DCHECK(!pinch_gesture_active_
);
130 StartAnimationIfNecessary();
133 void TopControlsManager::PinchBegin() {
134 DCHECK(!pinch_gesture_active_
);
135 pinch_gesture_active_
= true;
136 StartAnimationIfNecessary();
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;
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
);
151 if (controls_top_offset_
== controls_top_offset
)
154 controls_top_offset_
= controls_top_offset
;
156 client_
->DidChangeTopControlsPosition();
159 gfx::Vector2dF
TopControlsManager::Animate(base::TimeTicks monotonic_time
) {
160 if (!top_controls_animation_
|| !client_
->HaveRootScrollLayer())
161 return gfx::Vector2dF();
163 double time
= (monotonic_time
- base::TimeTicks()).InMillisecondsF();
165 float old_offset
= controls_top_offset_
;
166 SetControlsTopOffset(top_controls_animation_
->GetValue(time
));
168 if (IsAnimationCompleteAtTime(monotonic_time
))
171 gfx::Vector2dF
scroll_delta(0.f
, controls_top_offset_
- old_offset
);
175 void TopControlsManager::ResetAnimations() {
176 if (top_controls_animation_
)
177 top_controls_animation_
.reset();
179 animation_direction_
= NO_ANIMATION
;
182 void TopControlsManager::SetupAnimation(AnimationDirection direction
) {
183 DCHECK(direction
!= NO_ANIMATION
);
185 if (direction
== SHOWING_CONTROLS
&& controls_top_offset_
== 0)
188 if (direction
== HIDING_CONTROLS
&&
189 controls_top_offset_
== -top_controls_height_
) {
193 if (top_controls_animation_
&& animation_direction_
== direction
)
196 top_controls_animation_
= KeyframedFloatAnimationCurve::Create();
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();
212 void TopControlsManager::StartAnimationIfNecessary() {
213 if (controls_top_offset_
!= 0
214 && controls_top_offset_
!= -top_controls_height_
) {
215 AnimationDirection show_controls
= NO_ANIMATION
;
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
;
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
227 show_controls
= current_scroll_delta_
<= 0.f
?
228 SHOWING_CONTROLS
: HIDING_CONTROLS
;
231 if (show_controls
!= NO_ANIMATION
)
232 SetupAnimation(show_controls
);
236 bool TopControlsManager::IsAnimationCompleteAtTime(base::TimeTicks time
) {
237 if (!top_controls_animation_
)
240 double time_ms
= (time
- base::TimeTicks()).InMillisecondsF();
241 float new_offset
= top_controls_animation_
->GetValue(time_ms
);
243 if ((animation_direction_
== SHOWING_CONTROLS
&& new_offset
>= 0) ||
244 (animation_direction_
== HIDING_CONTROLS
245 && new_offset
<= -top_controls_height_
)) {