@@ -14,7 +14,7 @@ use crate::input::{
14
14
FocusEvent , FocusEventResult , InputEventFilterResult , InputEventResult , KeyEvent , MouseEvent ,
15
15
} ;
16
16
use crate :: item_rendering:: CachedRenderingData ;
17
- use crate :: items:: PropertyAnimation ;
17
+ use crate :: items:: { AnimationDirection , PropertyAnimation } ;
18
18
use crate :: layout:: { LayoutInfo , Orientation } ;
19
19
use crate :: lengths:: {
20
20
LogicalBorderRadius , LogicalLength , LogicalPoint , LogicalRect , LogicalSize , LogicalVector ,
@@ -92,6 +92,26 @@ impl Item for Flickable {
92
92
}
93
93
} ,
94
94
) ;
95
+
96
+ self . data . viewport_change_handler . init_delayed (
97
+ self_rc. downgrade ( ) ,
98
+ |self_weak| {
99
+ let Some ( flick_rc) = self_weak. upgrade ( ) else { return Default :: default ( ) } ;
100
+ let Some ( flick) = flick_rc. downcast :: < Flickable > ( ) else {
101
+ return Default :: default ( ) ;
102
+ } ;
103
+ let flick = flick. as_pin_ref ( ) ;
104
+
105
+ ( flick. viewport_x ( ) . get ( ) , flick. viewport_y ( ) . get ( ) )
106
+ } ,
107
+ |self_weak, _| {
108
+ let Some ( flick_rc) = self_weak. upgrade ( ) else { return } ;
109
+ let Some ( flick) = flick_rc. downcast :: < Flickable > ( ) else { return } ;
110
+ let flick = flick. as_pin_ref ( ) ;
111
+
112
+ flick. flicked . call ( & ( ) )
113
+ } ,
114
+ ) ;
95
115
}
96
116
97
117
fn layout_info (
@@ -242,6 +262,15 @@ pub(super) const DURATION_THRESHOLD: Duration = Duration::from_millis(500);
242
262
/// The delay to which press are forwarded to the inner item
243
263
pub ( super ) const FORWARD_DELAY : Duration = Duration :: from_millis ( 100 ) ;
244
264
265
+ const SMOOTH_SCROLL_DURATION : i32 = 250 ;
266
+ const SMOOTH_SCROLL_ANIM : PropertyAnimation = PropertyAnimation {
267
+ duration : SMOOTH_SCROLL_DURATION ,
268
+ easing : EasingCurve :: CubicBezier ( [ 0.0 , 0.0 , 0.58 , 1.0 ] ) ,
269
+ delay : 0 ,
270
+ iteration_count : 1. ,
271
+ direction : AnimationDirection :: Normal ,
272
+ } ;
273
+
245
274
#[ derive( Default , Debug ) ]
246
275
struct FlickableDataInner {
247
276
/// The position in which the press was made
@@ -250,13 +279,17 @@ struct FlickableDataInner {
250
279
pressed_viewport_pos : LogicalPoint ,
251
280
/// Set to true if the flickable is flicking and capturing all mouse event, not forwarding back to the children
252
281
capture_events : bool ,
282
+ smooth_scroll_time : Option < Instant > ,
283
+ smooth_scroll_target : LogicalPoint ,
253
284
}
254
285
255
286
#[ derive( Default , Debug ) ]
256
287
pub struct FlickableData {
257
288
inner : RefCell < FlickableDataInner > ,
258
289
/// Tracker that tracks the property to make sure that the flickable is in bounds
259
290
in_bound_change_handler : crate :: properties:: ChangeTracker ,
291
+ // Scroll trackers for flicked callback
292
+ viewport_change_handler : crate :: properties:: ChangeTracker ,
260
293
}
261
294
262
295
impl FlickableData {
@@ -372,12 +405,8 @@ impl FlickableData {
372
405
if inner. capture_events || should_capture ( ) {
373
406
let new_pos = ensure_in_bound ( flick, new_pos, flick_rc) ;
374
407
375
- let old_pos = ( x. get ( ) , y. get ( ) ) ;
376
408
x. set ( new_pos. x_length ( ) ) ;
377
409
y. set ( new_pos. y_length ( ) ) ;
378
- if old_pos. 0 != new_pos. x_length ( ) || old_pos. 1 != new_pos. y_length ( ) {
379
- ( Flickable :: FIELD_OFFSETS . flicked ) . apply_pin ( flick) . call ( & ( ) ) ;
380
- }
381
410
382
411
inner. capture_events = true ;
383
412
InputEventResult :: GrabMouse
@@ -395,7 +424,7 @@ impl FlickableData {
395
424
}
396
425
}
397
426
MouseEvent :: Wheel { delta_x, delta_y, .. } => {
398
- let delta = if window_adapter. window ( ) . 0 . modifiers . get ( ) . shift ( )
427
+ let mut delta = if window_adapter. window ( ) . 0 . modifiers . get ( ) . shift ( )
399
428
&& !cfg ! ( target_os = "macos" )
400
429
{
401
430
// Shift invert coordinate for the purpose of scrolling. But not on macOs because there the OS already take care of the change
@@ -413,20 +442,36 @@ impl FlickableData {
413
442
return InputEventResult :: EventIgnored ;
414
443
}
415
444
416
- let old_pos = LogicalPoint :: from_lengths (
417
- ( Flickable :: FIELD_OFFSETS . viewport_x ) . apply_pin ( flick) . get ( ) ,
418
- ( Flickable :: FIELD_OFFSETS . viewport_y ) . apply_pin ( flick) . get ( ) ,
419
- ) ;
420
- let new_pos = ensure_in_bound ( flick, old_pos + delta, flick_rc) ;
421
-
422
445
let viewport_x = ( Flickable :: FIELD_OFFSETS . viewport_x ) . apply_pin ( flick) ;
423
446
let viewport_y = ( Flickable :: FIELD_OFFSETS . viewport_y ) . apply_pin ( flick) ;
424
- let old_pos = ( viewport_x. get ( ) , viewport_y. get ( ) ) ;
425
- viewport_x. set ( new_pos. x_length ( ) ) ;
426
- viewport_y. set ( new_pos. y_length ( ) ) ;
427
- if old_pos. 0 != new_pos. x_length ( ) || old_pos. 1 != new_pos. y_length ( ) {
428
- ( Flickable :: FIELD_OFFSETS . flicked ) . apply_pin ( flick) . call ( & ( ) ) ;
447
+
448
+ let old_pos = LogicalPoint :: from_lengths ( viewport_x. get ( ) , viewport_y. get ( ) ) ;
449
+
450
+ // Accumulate scroll delta
451
+ if let Some ( smooth_scroll_time) = inner. smooth_scroll_time . take ( ) {
452
+ let millis =
453
+ ( crate :: animations:: current_tick ( ) - smooth_scroll_time) . as_millis ( ) as i32 ;
454
+
455
+ if millis < SMOOTH_SCROLL_DURATION {
456
+ let remaining_delta = inner. smooth_scroll_target - old_pos;
457
+
458
+ // Only if is in the same direction.
459
+ // `Default` is because `dot` returns `i32` in embedded
460
+ // but it returns `f32` in any other platform
461
+ if delta. dot ( remaining_delta) > Default :: default ( ) {
462
+ delta += remaining_delta;
463
+ }
464
+ }
429
465
}
466
+
467
+ let new_pos = ensure_in_bound ( flick, old_pos + delta, flick_rc) ;
468
+
469
+ inner. smooth_scroll_target = new_pos;
470
+ inner. smooth_scroll_time = Some ( Instant :: now ( ) ) ;
471
+
472
+ viewport_y. set_animated_value ( new_pos. y_length ( ) , SMOOTH_SCROLL_ANIM ) ;
473
+ viewport_x. set_animated_value ( new_pos. x_length ( ) , SMOOTH_SCROLL_ANIM ) ;
474
+
430
475
InputEventResult :: EventAccepted
431
476
}
432
477
MouseEvent :: DragMove ( ..) | MouseEvent :: Drop ( ..) => InputEventResult :: EventIgnored ,
@@ -449,26 +494,19 @@ impl FlickableData {
449
494
{
450
495
let speed = dist / ( millis as f32 ) ;
451
496
452
- let duration = 250 ;
453
497
let final_pos = ensure_in_bound (
454
498
flick,
455
- ( inner. pressed_viewport_pos . cast ( ) + dist + speed * ( duration as f32 ) ) . cast ( ) ,
499
+ ( inner. pressed_viewport_pos . cast ( )
500
+ + dist
501
+ + speed * ( SMOOTH_SCROLL_DURATION as f32 ) )
502
+ . cast ( ) ,
456
503
flick_rc,
457
504
) ;
458
- let anim = PropertyAnimation {
459
- duration,
460
- easing : EasingCurve :: CubicBezier ( [ 0.0 , 0.0 , 0.58 , 1.0 ] ) ,
461
- ..PropertyAnimation :: default ( )
462
- } ;
463
505
464
506
let viewport_x = ( Flickable :: FIELD_OFFSETS . viewport_x ) . apply_pin ( flick) ;
465
507
let viewport_y = ( Flickable :: FIELD_OFFSETS . viewport_y ) . apply_pin ( flick) ;
466
- let old_pos = ( viewport_x. get ( ) , viewport_y. get ( ) ) ;
467
- viewport_x. set_animated_value ( final_pos. x_length ( ) , anim. clone ( ) ) ;
468
- viewport_y. set_animated_value ( final_pos. y_length ( ) , anim) ;
469
- if old_pos. 0 != final_pos. x_length ( ) || old_pos. 1 != final_pos. y_length ( ) {
470
- ( Flickable :: FIELD_OFFSETS . flicked ) . apply_pin ( flick) . call ( & ( ) ) ;
471
- }
508
+ viewport_x. set_animated_value ( final_pos. x_length ( ) , SMOOTH_SCROLL_ANIM ) ;
509
+ viewport_y. set_animated_value ( final_pos. y_length ( ) , SMOOTH_SCROLL_ANIM ) ;
472
510
}
473
511
}
474
512
inner. capture_events = false ; // FIXME: should only be set to false once the flick animation is over
0 commit comments