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/top_controls_manager.h"
9 #include "base/logging.h"
10 #include "base/time.h"
11 #include "cc/keyframed_animation_curve.h"
12 #include "cc/layer_tree_impl.h"
13 #include "cc/timing_function.h"
14 #include "cc/top_controls_manager_client.h"
15 #include "ui/gfx/transform.h"
16 #include "ui/gfx/vector2d_f.h"
20 // These constants were chosen empirically for their visually pleasant behavior.
21 // Contact tedchoc@chromium.org for questions about changing these values.
22 const float kShowHideThreshold
= 0.5f
;
23 const int64 kShowHideMaxDurationMs
= 175;
27 scoped_ptr
<TopControlsManager
> TopControlsManager::Create(
28 TopControlsManagerClient
* client
, float top_controls_height
) {
29 return make_scoped_ptr(new TopControlsManager(client
, top_controls_height
));
32 TopControlsManager::TopControlsManager(TopControlsManagerClient
* client
,
33 float top_controls_height
)
35 animation_direction_(NO_ANIMATION
),
36 is_overlay_mode_(false),
37 in_scroll_gesture_(false),
38 top_controls_height_(top_controls_height
),
39 controls_top_offset_(0),
40 content_top_offset_(top_controls_height
),
41 previous_root_scroll_offset_(0.f
),
42 scroll_start_offset_(0.f
) {
46 TopControlsManager::~TopControlsManager() {
49 void TopControlsManager::UpdateDrawPositions() {
50 if (!client_
->haveRootScrollLayer())
53 // If the scroll position has changed underneath us (i.e. a javascript
54 // scroll), then simulate a scroll that covers the delta.
55 float scroll_total_y
= RootScrollLayerTotalScrollY();
56 if (!in_scroll_gesture_
57 && scroll_total_y
!= previous_root_scroll_offset_
) {
58 ScrollBy(gfx::Vector2dF(0, scroll_total_y
- previous_root_scroll_offset_
));
59 StartAnimationIfNecessary();
60 previous_root_scroll_offset_
= RootScrollLayerTotalScrollY();
64 void TopControlsManager::ScrollBegin() {
66 in_scroll_gesture_
= true;
67 scroll_start_offset_
= RootScrollLayerTotalScrollY() + controls_top_offset_
;
70 gfx::Vector2dF
TopControlsManager::ScrollBy(
71 const gfx::Vector2dF pending_delta
) {
72 if (pending_delta
.y() == 0)
75 float scroll_total_y
= RootScrollLayerTotalScrollY();
76 if (in_scroll_gesture_
&&
77 ((pending_delta
.y() > 0 && scroll_total_y
< scroll_start_offset_
) ||
78 (pending_delta
.y() < 0 &&
79 scroll_total_y
> scroll_start_offset_
+ top_controls_height_
))) {
84 return ScrollInternal(pending_delta
);
87 gfx::Vector2dF
TopControlsManager::ScrollInternal(
88 const gfx::Vector2dF pending_delta
) {
89 float scroll_total_y
= RootScrollLayerTotalScrollY();
90 float scroll_delta_y
= pending_delta
.y();
92 float previous_controls_offset
= controls_top_offset_
;
93 float previous_content_offset
= content_top_offset_
;
94 bool previous_was_overlay
= is_overlay_mode_
;
96 controls_top_offset_
-= scroll_delta_y
;
97 controls_top_offset_
= std::min(
98 std::max(controls_top_offset_
, -top_controls_height_
), 0.f
);
100 if (scroll_total_y
> 0 || (scroll_total_y
== 0
101 && content_top_offset_
< scroll_delta_y
)) {
102 is_overlay_mode_
= true;
104 // The first case is where the page applies a scroll (javascript) and is
105 // being re-adjusted in a call to UpdateDrawPositions. Instead of slamming
106 // the controls to the top, we adjust by the scroll delta until we reach
107 // zero as we expect.
108 if (scroll_total_y
> 0 && content_top_offset_
!= 0)
109 content_top_offset_
-= scroll_delta_y
;
111 content_top_offset_
= 0;
112 } else if (scroll_total_y
<= 0 && (scroll_delta_y
< 0
113 || (scroll_delta_y
> 0 && content_top_offset_
> 0))) {
114 is_overlay_mode_
= false;
115 content_top_offset_
-= scroll_delta_y
;
117 content_top_offset_
= std::max(
118 std::min(content_top_offset_
,
119 controls_top_offset_
+ top_controls_height_
), 0.f
);
121 gfx::Vector2dF applied_delta
;
122 if (!previous_was_overlay
)
123 applied_delta
.set_y(previous_content_offset
- content_top_offset_
);
125 if (is_overlay_mode_
!= previous_was_overlay
126 || previous_controls_offset
!= controls_top_offset_
127 || previous_content_offset
!= content_top_offset_
) {
128 client_
->setNeedsRedraw();
129 client_
->setActiveTreeNeedsUpdateDrawProperties();
132 return pending_delta
- applied_delta
;
135 void TopControlsManager::ScrollEnd() {
136 StartAnimationIfNecessary();
137 previous_root_scroll_offset_
= RootScrollLayerTotalScrollY();
138 in_scroll_gesture_
= false;
141 void TopControlsManager::Animate(base::TimeTicks monotonic_time
) {
142 if (!top_controls_animation_
|| !client_
->haveRootScrollLayer())
145 double time
= (monotonic_time
- base::TimeTicks()).InMillisecondsF();
146 float new_offset
= top_controls_animation_
->getValue(time
);
147 gfx::Vector2dF
scroll_vector(0.f
, -(new_offset
- controls_top_offset_
));
148 ScrollInternal(scroll_vector
);
149 client_
->setNeedsRedraw();
151 if (IsAnimationCompleteAtTime(monotonic_time
))
155 void TopControlsManager::ResetAnimations() {
156 if (top_controls_animation_
)
157 top_controls_animation_
.reset();
159 animation_direction_
= NO_ANIMATION
;
162 float TopControlsManager::RootScrollLayerTotalScrollY() {
163 return client_
->rootScrollLayerTotalScrollY();
166 void TopControlsManager::SetupAnimation(AnimationDirection direction
) {
167 top_controls_animation_
= KeyframedFloatAnimationCurve::create();
169 (base::TimeTicks::Now() - base::TimeTicks()).InMillisecondsF();
170 top_controls_animation_
->addKeyframe(
171 FloatKeyframe::create(start_time
, controls_top_offset_
,
172 scoped_ptr
<TimingFunction
>()));
173 float max_ending_offset
=
174 (direction
== SHOWING_CONTROLS
? 1 : -1) * top_controls_height_
;
175 top_controls_animation_
->addKeyframe(
176 FloatKeyframe::create(start_time
+ kShowHideMaxDurationMs
,
177 controls_top_offset_
+ max_ending_offset
,
178 EaseTimingFunction::create()));
179 animation_direction_
= direction
;
182 void TopControlsManager::StartAnimationIfNecessary() {
183 float scroll_total_y
= RootScrollLayerTotalScrollY();
185 if (controls_top_offset_
!= 0
186 && controls_top_offset_
!= -top_controls_height_
) {
187 AnimationDirection show_controls
=
188 controls_top_offset_
>= -(top_controls_height_
* kShowHideThreshold
) ?
189 SHOWING_CONTROLS
: HIDING_CONTROLS
;
190 if (!top_controls_animation_
|| animation_direction_
!= show_controls
) {
191 SetupAnimation(show_controls
);
192 client_
->setNeedsRedraw();
197 bool TopControlsManager::IsAnimationCompleteAtTime(base::TimeTicks time
) {
198 if (!top_controls_animation_
)
201 double time_ms
= (time
- base::TimeTicks()).InMillisecondsF();
202 float new_offset
= top_controls_animation_
->getValue(time_ms
);
204 if ((animation_direction_
== SHOWING_CONTROLS
&& new_offset
>= 0) ||
205 (animation_direction_
== HIDING_CONTROLS
206 && new_offset
<= -top_controls_height_
)) {