2
* Copyright (C) 2012 - Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU Lesser General Public License, as
6
* published by the Free Software Foundation; either version 2.1 or 3.0
9
* This program is distributed in the hope that it will be useful, but
10
* WITHOUT ANY WARRANTY; without even the implied warranties of
11
* MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
12
* PURPOSE. See the applicable version of the GNU Lesser General Public
13
* License for more details.
15
* You should have received a copy of both the GNU Lesser General Public
16
* License along with this program. If not, see <http://www.gnu.org/licenses/>
18
* Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com>
22
#include "KineticAxisScroller.h"
23
#include "AxisDecelerationAnimation.h"
24
#include "VelocityCalculator.h"
26
#include "KineticScrollingTickSource.h"
30
/*****************************************************************************
32
*****************************************************************************/
34
class KineticAxisScroller::Private : public sigc::trackable
40
void CalculateContentPosLimits();
42
void ProcessFingerDown();
44
void ProcessFingerUp();
46
void ProcessFingerDrag(int mov);
47
void ProcessFingerDrag_Pressed(int mov);
48
void ProcessFingerDrag_FollowingFinger(int mov);
50
void UpdateAnimations(int time);
52
int LimitOutOfBoundsMovement(int movement, int position,
53
int min_position) const;
55
float viewport_length_;
56
float content_length_;
58
float min_content_pos_;
60
BoundsBehavior bounds_behavior_;
62
KineticScrollerAxisState state_;
64
VelocityCalculator velocity_calculator_;
65
AxisDecelerationAnimation deceleration_animation_;
66
int accumulated_movement_;
69
KineticAxisScroller::Private::Private()
71
viewport_length_ = 0.0f;
72
content_length_ = 0.0f;
74
min_content_pos_ = 0.0f;
75
bounds_behavior_ = DragAndOvershootBounds;
76
state_ = KineticScrollerAxisStateIdle;
79
void KineticAxisScroller::Private::CalculateContentPosLimits()
81
if (content_length_ > viewport_length_)
82
min_content_pos_ = -(content_length_ - viewport_length_);
84
min_content_pos_ = 0.0f;
87
void KineticAxisScroller::Private::ProcessFingerDown()
91
case KineticScrollerAxisStateIdle:
92
state_ = KineticScrollerAxisStatePressed;
93
accumulated_movement_ = 0;
95
case KineticScrollerAxisStateMovingByInertia:
96
state_ = KineticScrollerAxisStatePressed;
97
accumulated_movement_ = 0;
105
void KineticAxisScroller::Private::ProcessFingerUp()
107
// consider the elapsed time between the last drag update and now, that the
108
// finger has been lifted (it could have been still for a while).
109
velocity_calculator_.ProcessMovement(0);
110
float velocity = velocity_calculator_.CalculateVelocity();
112
if (fabs(velocity) < MINIMUM_FLICK_SPEED)
115
velocity *= FLICK_BOOST;
117
/* Always run the animation, even if there's no starting speed,
118
as it will pull the content back to a valid position if it
119
was dragged beyond valid boundaries */
120
deceleration_animation_.SetMinimumPosition(min_content_pos_);
121
deceleration_animation_.SetMaximumPosition(0.0f);
122
deceleration_animation_.SetStartPosition(content_pos_);
123
deceleration_animation_.SetStartVelocity(velocity);
124
deceleration_animation_.Start();
126
state_ = KineticScrollerAxisStateMovingByInertia;
129
void KineticAxisScroller::Private::ProcessFingerDrag(int movement)
131
movement = LimitOutOfBoundsMovement(movement, content_pos_, min_content_pos_);
136
if (state_ == KineticScrollerAxisStatePressed)
138
ProcessFingerDrag_Pressed(movement);
140
else // KineticScrollerAxisState::FollowingFinger
142
ProcessFingerDrag_FollowingFinger(movement);
146
void KineticAxisScroller::Private::ProcessFingerDrag_Pressed(int movement)
148
accumulated_movement_ += movement;
150
if (abs(accumulated_movement_) > KineticAxisScroller::MOVEMENT_THRESHOLD)
152
state_ = KineticScrollerAxisStateFollowingFinger;
153
velocity_calculator_.Reset();
155
ProcessFingerDrag_FollowingFinger(movement);
159
void KineticAxisScroller::Private::ProcessFingerDrag_FollowingFinger(int movement)
161
/* TODO: Filter the input for a smoother movement.
162
E.g.: make the content follow the finger with a small delay instead of
163
exactly following it. That will remove the inherent jitter in input coming
164
from a touchscreen. */
166
content_pos_ += movement;
168
velocity_calculator_.ProcessMovement(movement);
171
void KineticAxisScroller::Private::UpdateAnimations(int delta_time)
173
if (deceleration_animation_.IsActive())
175
deceleration_animation_.Update(delta_time);
176
content_pos_ = deceleration_animation_.GetPosition();
178
if (!deceleration_animation_.IsActive())
179
state_ = KineticScrollerAxisStateIdle;
183
state_ = KineticScrollerAxisStateIdle;
187
int KineticAxisScroller::Private::LimitOutOfBoundsMovement(int movement,
189
int min_position) const
191
int new_position = position + movement;
193
if (new_position > 0)
195
if (bounds_behavior_ == StopAtBounds)
201
/* hinder movement to hint user that he's dragging content beyound its
206
else if (new_position < min_position)
208
if (bounds_behavior_ == StopAtBounds)
210
return min_position - position;
219
/* new_position is still within bounds */
224
/*****************************************************************************
226
*****************************************************************************/
228
const float KineticAxisScroller::MINIMUM_FLICK_SPEED = 0.003f;
229
const float KineticAxisScroller::FLICK_BOOST = 2.0f;
231
KineticAxisScroller::KineticAxisScroller()
236
KineticAxisScroller::~KineticAxisScroller()
241
void KineticAxisScroller::SetViewportLength(int length)
243
p->viewport_length_ = length;
244
p->CalculateContentPosLimits();
247
int KineticAxisScroller::GetViewportLength() const
249
return p->viewport_length_;
252
void KineticAxisScroller::SetContentLength(int length)
254
p->content_length_ = length;
255
p->CalculateContentPosLimits();
258
int KineticAxisScroller::GetContentLength() const
260
return p->content_length_;
263
void KineticAxisScroller::SetContentPosition(int pos)
265
p->content_pos_ = pos;
268
void KineticAxisScroller::SetBoundsBehavior(BoundsBehavior bounds_behavior)
270
p->bounds_behavior_ = bounds_behavior;
272
p->deceleration_animation_.SetOvershootBoundsEnabled(
273
bounds_behavior != BoundsBehavior::StopAtBounds);
276
void KineticAxisScroller::ProcessFingerDown()
278
p->ProcessFingerDown();
281
void KineticAxisScroller::ProcessFingerUp()
283
p->ProcessFingerUp();
286
void KineticAxisScroller::ProcessFingerDrag(int movement)
288
p->ProcessFingerDrag(movement);
291
void KineticAxisScroller::UpdateTime(int delta_time)
293
p->UpdateAnimations(delta_time);
296
bool KineticAxisScroller::NeedTimeUpdates() const
298
return p->state_ == KineticScrollerAxisStateMovingByInertia
299
&& p->deceleration_animation_.IsActive();
302
KineticScrollerAxisState KineticAxisScroller::GetState() const
307
int KineticAxisScroller::GetContentPosition() const
309
return p->content_pos_;