8 static constexpr
float ScrollStep = 40.0f;
9 static constexpr
float SmoothingFactor = 0.5f;
10 static constexpr
float DragActivationThresholdDistancePx = 5.0f;
11 static constexpr
float SmoothingDeactivationThreshold_Px = 5.0f;
13 static constexpr
float BorderOverreachPx = 10.0f;
14 static constexpr
float DelayUntilFullClamp_frames = 8;
16 static constexpr
float ResidualSpeedInitialFactor = 0.575f;
17 static constexpr
float ResidualSpeedDropOffMultiplier = 0.945f;
18 static constexpr
float ResidualSpeedMinimumThreshold = 0.1f;
26 attr_position( &this->cm, this, 0 ),
27 attr_outsize( &this->cm, this, 0 ),
28 attr_insize( &this->cm, this, 0 ),
29 attr_overlapTop( &this->cm, this, 0 ),
30 attr_overlapBottom( &this->cm, this, 0 ),
32 smoothedPosition( 0 ),
35 _candidate( std::nullopt ),
36 _active( std::nullopt ),
39 _full_clamp_countdown( 0 )
53 if( this->_candidate.has_value() )
55 auto key = this->_candidate.value();
56 float2 pos = this->input_position( key );
58 float dist =
std::abs( pos.y - this->_candidate_pos );
59 if( dist > DragActivationThresholdDistancePx )
61 this->accept_candidate();
65 if( this->_active.has_value() )
67 this->refresh_clamp_countdown();
68 auto key = this->_active.value();
69 float2 pos = this->input_position( key );
71 auto pos_prev = this->position.
Get();
72 this->position.
Set( pos_prev - ( pos.y - this->_candidate_pos ) );
73 this->clamp_position();
75 this->_candidate_pos = pos.y;
81 for(
size_t i = 0; i < steps; i++ )
84 if( this->_residual_speed != 0.0f )
85 this->refresh_clamp_countdown();
86 else if( this->_full_clamp_countdown > 0 )
87 this->_full_clamp_countdown--;
90 this->position.
Set( this->position.
Get() + this->_residual_speed );
91 this->clamp_position();
92 this->_residual_speed *= ResidualSpeedDropOffMultiplier;
95 float direction = this->position.
Get() - this->smoothedPosition.
Get();
96 this->smoothedPosition.
Set( this->smoothedPosition.
Get() + direction * SmoothingFactor );
99 if(
std::abs( this->_residual_speed ) < ResidualSpeedMinimumThreshold ||
std::abs( direction ) < SmoothingDeactivationThreshold_Px )
100 this->_residual_speed = 0.0f;
103 if( this->_residual_speed == 0.0f && this->_full_clamp_countdown <= 0 &&
std::abs( direction ) < SmoothingDeactivationThreshold_Px )
105 this->smoothedPosition.
Set( this->position.
Get() );
112 void Scroller::accept_candidate()
114 auto key = this->_candidate.value();
115 this->_candidate = std::nullopt;
120 root->reserve_key( key,
true );
121 this->reserve_hover_keys( root,
true );
128 auto input_pos = this->iq.
input_position( key.first, key.second );
135 this->_input_id = id;
142 return this->_input_id;
150 if( !this->ibq.
IsBound( this->_input_id, key ) )
159 float2 input_pos = this->input_position( key );
162 if( !this->_active.has_value() && !this->_candidate.has_value() && press && real && hit && !offspace )
164 this->_candidate = key;
165 this->_candidate_pos = input_pos.y;
167 this->_residual_speed = 0.0f;
169 else if( this->_candidate == key && !press )
171 this->_candidate = std::nullopt;
173 else if( this->_active == key && !press )
176 this->_active = std::nullopt;
177 this->reserve_hover_keys( root,
false );
179 this->_residual_speed = ( this->position.
Get() - this->smoothedPosition.
Get() ) * ResidualSpeedInitialFactor;
180 if( this->_residual_speed != 0.0f )
185 void Scroller::refresh_clamp_countdown()
187 this->_full_clamp_countdown = DelayUntilFullClamp_frames;
191 void Scroller::clamp_position()
195 if( this->_full_clamp_countdown > 0 )
196 overreach = BorderOverreachPx;
204 auto outsize = this->
size.
Get().y;
207 if( insize > outsize )
208 this->position.
Set(
std::clamp( this->position.
Get(), 0.0f - overreach, insize - outsize + overreach ) );
210 this->position.
Set( 0 );
214 this->position.
Set( 0 );
225 this->refresh_clamp_countdown();
226 this->position.
Set( this->position.
Get() - ScrollStep );
227 this->clamp_position();
232 this->refresh_clamp_countdown();
233 this->position.
Set( this->position.
Get() + ScrollStep );
234 this->clamp_position();
249 if( this->
child.
dirty() || this->expectedSize.dirty() )
257 if( this->
child.
dirty() || this->child.Get()->preferredSize.dirty() )
262 if( this->position.
dirty() )
265 if( this->position.
Get() != this->smoothedPosition.Get() )
269 if( this->
child.
dirty() || this->child.Get()->preferredSize.dirty() || this->smoothedPosition.dirty() || this->scissor.dirty() )
278 if( this->
child.
dirty() || this->size.dirty() || this->modelTransform.dirty() || this->child.Get()->preferredSize.dirty() || this->smoothedPosition.dirty() || this->scissor.dirty() )
290 auto outsize = this->
size.
Get().y;
291 auto position_max = ( insize > outsize ) ? ( insize - outsize ) : 0.0f;
304 this->clamp_position();
306 auto smoothedPosition =
std::clamp( this->smoothedPosition.
Get(), 0.0f, std::max( 0.0f, insize - outsize ) );
323 void Scroller::reserve_hover_keys(
InputRoot * root,
bool reserve )
328 [ root, reserve ](
Input::Key key,
int device_id )