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 top_controls_height_(0.f
),
43 current_scroll_delta_(0.f
),
44 controls_scroll_begin_offset_(0.f
),
45 top_controls_show_threshold_(top_controls_hide_threshold
),
46 top_controls_hide_threshold_(top_controls_show_threshold
),
47 pinch_gesture_active_(false) {
51 TopControlsManager::~TopControlsManager() {
54 float TopControlsManager::ControlsTopOffset() {
55 return client_
->ControlsTopOffset();
58 float TopControlsManager::ContentTopOffset() {
59 return client_
->ControlsTopOffset() + top_controls_height_
;
62 void TopControlsManager::UpdateTopControlsState(TopControlsState constraints
,
63 TopControlsState current
,
65 DCHECK(!(constraints
== SHOWN
&& current
== HIDDEN
));
66 DCHECK(!(constraints
== HIDDEN
&& current
== SHOWN
));
68 permitted_state_
= constraints
;
70 // Don't do anything if it doesn't matter which state the controls are in.
71 if (constraints
== BOTH
&& current
== BOTH
)
74 // Don't do anything if there is no change in offset.
75 float final_controls_position
= 0.f
;
76 if (constraints
== HIDDEN
|| current
== HIDDEN
) {
77 final_controls_position
= -top_controls_height_
;
79 if (final_controls_position
== client_
->ControlsTopOffset()) {
83 AnimationDirection animation_direction
= SHOWING_CONTROLS
;
84 if (constraints
== HIDDEN
|| current
== HIDDEN
)
85 animation_direction
= HIDING_CONTROLS
;
88 SetupAnimation(animation_direction
);
90 client_
->SetControlsTopOffset(final_controls_position
);
92 client_
->DidChangeTopControlsPosition();
95 void TopControlsManager::ScrollBegin() {
96 DCHECK(!pinch_gesture_active_
);
98 current_scroll_delta_
= 0.f
;
99 controls_scroll_begin_offset_
= client_
->ControlsTopOffset();
102 gfx::Vector2dF
TopControlsManager::ScrollBy(
103 const gfx::Vector2dF
& pending_delta
) {
104 if (pinch_gesture_active_
)
105 return pending_delta
;
107 if (permitted_state_
== SHOWN
&& pending_delta
.y() > 0)
108 return pending_delta
;
109 else if (permitted_state_
== HIDDEN
&& pending_delta
.y() < 0)
110 return pending_delta
;
112 current_scroll_delta_
+= pending_delta
.y();
114 float old_offset
= client_
->ControlsTopOffset();
115 SetControlsTopOffset(controls_scroll_begin_offset_
- current_scroll_delta_
);
117 // If the controls are fully visible, treat the current position as the
118 // new baseline even if the gesture didn't end.
119 if (client_
->ControlsTopOffset() == 0.f
) {
120 current_scroll_delta_
= 0.f
;
121 controls_scroll_begin_offset_
= 0.f
;
126 gfx::Vector2dF
applied_delta(0.f
, old_offset
- client_
->ControlsTopOffset());
127 return pending_delta
- applied_delta
;
130 void TopControlsManager::ScrollEnd() {
131 DCHECK(!pinch_gesture_active_
);
132 StartAnimationIfNecessary();
135 void TopControlsManager::PinchBegin() {
136 DCHECK(!pinch_gesture_active_
);
137 pinch_gesture_active_
= true;
138 StartAnimationIfNecessary();
141 void TopControlsManager::PinchEnd() {
142 DCHECK(pinch_gesture_active_
);
143 // Pinch{Begin,End} will always occur within the scope of Scroll{Begin,End},
144 // so return to a state expected by the remaining scroll sequence.
145 pinch_gesture_active_
= false;
149 void TopControlsManager::SetControlsTopOffset(float controls_top_offset
) {
150 controls_top_offset
= std::max(controls_top_offset
, -top_controls_height_
);
151 controls_top_offset
= std::min(controls_top_offset
, 0.f
);
153 if (client_
->ControlsTopOffset() == controls_top_offset
)
156 client_
->SetControlsTopOffset(controls_top_offset
);
158 client_
->DidChangeTopControlsPosition();
161 void TopControlsManager::SetTopControlsHeight(float top_controls_height
) {
162 DCHECK_GE(top_controls_height
, 0);
164 if (top_controls_height
== top_controls_height_
)
168 float top_controls_offset
= client_
->ControlsTopOffset();
169 top_controls_height_
= top_controls_height
;
170 SetControlsTopOffset(top_controls_offset
);
171 StartAnimationIfNecessary();
174 gfx::Vector2dF
TopControlsManager::Animate(base::TimeTicks monotonic_time
) {
175 if (!top_controls_animation_
|| !client_
->HaveRootScrollLayer())
176 return gfx::Vector2dF();
178 base::TimeDelta time
= monotonic_time
- base::TimeTicks();
180 float old_offset
= client_
->ControlsTopOffset();
181 SetControlsTopOffset(top_controls_animation_
->GetValue(time
));
183 if (IsAnimationCompleteAtTime(monotonic_time
))
186 gfx::Vector2dF
scroll_delta(0.f
, client_
->ControlsTopOffset() - old_offset
);
190 void TopControlsManager::ResetAnimations() {
191 top_controls_animation_
= nullptr;
192 animation_direction_
= NO_ANIMATION
;
195 void TopControlsManager::SetupAnimation(AnimationDirection direction
) {
196 DCHECK(direction
!= NO_ANIMATION
);
198 if (direction
== SHOWING_CONTROLS
&& client_
->ControlsTopOffset() == 0)
201 if (direction
== HIDING_CONTROLS
&&
202 client_
->ControlsTopOffset() == -top_controls_height_
) {
206 if (top_controls_animation_
&& animation_direction_
== direction
)
209 top_controls_animation_
= KeyframedFloatAnimationCurve::Create();
210 base::TimeDelta start_time
= gfx::FrameTime::Now() - base::TimeTicks();
211 top_controls_animation_
->AddKeyframe(
212 FloatKeyframe::Create(start_time
, client_
->ControlsTopOffset(), nullptr));
213 float max_ending_offset
=
214 (direction
== SHOWING_CONTROLS
? 1 : -1) * top_controls_height_
;
215 top_controls_animation_
->AddKeyframe(FloatKeyframe::Create(
216 start_time
+ base::TimeDelta::FromMilliseconds(kShowHideMaxDurationMs
),
217 client_
->ControlsTopOffset() + max_ending_offset
,
218 EaseTimingFunction::Create()));
219 animation_direction_
= direction
;
220 client_
->DidChangeTopControlsPosition();
223 void TopControlsManager::StartAnimationIfNecessary() {
224 if (client_
->ControlsTopOffset() != 0
225 && client_
->ControlsTopOffset() != -top_controls_height_
) {
226 AnimationDirection show_controls
= NO_ANIMATION
;
228 float top_controls_show_height
=
229 top_controls_height_
* top_controls_hide_threshold_
;
230 float top_controls_hide_height
=
231 top_controls_height_
* (1.f
- top_controls_show_threshold_
);
232 if (client_
->ControlsTopOffset() >= -top_controls_show_height
) {
233 // If we're showing so much that the hide threshold won't trigger, show.
234 show_controls
= SHOWING_CONTROLS
;
235 } else if (client_
->ControlsTopOffset() <= -top_controls_hide_height
) {
236 // If we're showing so little that the show threshold won't trigger, hide.
237 show_controls
= HIDING_CONTROLS
;
239 // If we could be either showing or hiding, we determine which one to
240 // do based on whether or not the total scroll delta was moving up or
242 show_controls
= current_scroll_delta_
<= 0.f
?
243 SHOWING_CONTROLS
: HIDING_CONTROLS
;
246 if (show_controls
!= NO_ANIMATION
)
247 SetupAnimation(show_controls
);
251 bool TopControlsManager::IsAnimationCompleteAtTime(base::TimeTicks time
) {
252 if (!top_controls_animation_
)
255 base::TimeDelta animation_time
= time
- base::TimeTicks();
256 float new_offset
= top_controls_animation_
->GetValue(animation_time
);
258 if ((animation_direction_
== SHOWING_CONTROLS
&& new_offset
>= 0) ||
259 (animation_direction_
== HIDING_CONTROLS
260 && new_offset
<= -top_controls_height_
)) {