@@ -14,7 +14,7 @@ use crate::input::{
1414 FocusEvent , FocusEventResult , InputEventFilterResult , InputEventResult , KeyEvent , MouseEvent ,
1515} ;
1616use crate :: item_rendering:: CachedRenderingData ;
17- use crate :: items:: PropertyAnimation ;
17+ use crate :: items:: { AnimationDirection , PropertyAnimation } ;
1818use crate :: layout:: { LayoutInfo , Orientation } ;
1919use crate :: lengths:: {
2020 LogicalBorderRadius , LogicalLength , LogicalPoint , LogicalRect , LogicalSize , LogicalVector ,
@@ -48,6 +48,7 @@ pub struct Flickable {
4848 pub viewport_height : Property < LogicalLength > ,
4949
5050 pub interactive : Property < bool > ,
51+ pub smooth_scroll : Property < bool > ,
5152
5253 pub flicked : Callback < VoidArg > ,
5354
@@ -242,6 +243,15 @@ pub(super) const DURATION_THRESHOLD: Duration = Duration::from_millis(500);
242243/// The delay to which press are forwarded to the inner item
243244pub ( super ) const FORWARD_DELAY : Duration = Duration :: from_millis ( 100 ) ;
244245
246+ const SMOOTH_SCROLL_DURATION : i32 = 250 ;
247+ const SMOOTH_SCROLL_ANIM : PropertyAnimation = PropertyAnimation {
248+ duration : SMOOTH_SCROLL_DURATION ,
249+ easing : EasingCurve :: CubicBezier ( [ 0.0 , 0.0 , 0.58 , 1.0 ] ) ,
250+ delay : 0 ,
251+ iteration_count : 1. ,
252+ direction : AnimationDirection :: Normal ,
253+ } ;
254+
245255#[ derive( Default , Debug ) ]
246256struct FlickableDataInner {
247257 /// The position in which the press was made
@@ -250,6 +260,8 @@ struct FlickableDataInner {
250260 pressed_viewport_pos : LogicalPoint ,
251261 /// Set to true if the flickable is flicking and capturing all mouse event, not forwarding back to the children
252262 capture_events : bool ,
263+ smooth_scroll_time : Option < Instant > ,
264+ smooth_scroll_target : LogicalPoint ,
253265}
254266
255267#[ derive( Default , Debug ) ]
@@ -395,7 +407,7 @@ impl FlickableData {
395407 }
396408 }
397409 MouseEvent :: Wheel { delta_x, delta_y, .. } => {
398- let delta = if window_adapter. window ( ) . 0 . modifiers . get ( ) . shift ( )
410+ let mut delta = if window_adapter. window ( ) . 0 . modifiers . get ( ) . shift ( )
399411 && !cfg ! ( target_os = "macos" )
400412 {
401413 // Shift invert coordinate for the purpose of scrolling. But not on macOs because there the OS already take care of the change
@@ -413,18 +425,50 @@ impl FlickableData {
413425 return InputEventResult :: EventIgnored ;
414426 }
415427
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-
422428 let viewport_x = ( Flickable :: FIELD_OFFSETS . viewport_x ) . apply_pin ( flick) ;
423429 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 ( ) {
430+
431+ let old_pos = LogicalPoint :: from_lengths ( viewport_x. get ( ) , viewport_y. get ( ) ) ;
432+
433+ let smooth_scroll = ( Flickable :: FIELD_OFFSETS . smooth_scroll ) . apply_pin ( flick) . get ( ) ;
434+
435+ if smooth_scroll {
436+ // Accumulate scroll delta
437+ if let Some ( smooth_scroll_time) = inner. smooth_scroll_time . take ( ) {
438+ let millis = ( crate :: animations:: current_tick ( ) - smooth_scroll_time)
439+ . as_millis ( ) as i32 ;
440+
441+ if millis < SMOOTH_SCROLL_DURATION {
442+ let remaining_delta = inner. smooth_scroll_target - old_pos;
443+
444+ // Only if is in the same direction.
445+ // `Default` is because in embedded `dot` returns `i32`
446+ // but in any other platform it returns `f32`
447+ if delta. dot ( remaining_delta) > Default :: default ( ) {
448+ delta += remaining_delta;
449+ }
450+ }
451+ }
452+ }
453+
454+ let new_pos = ensure_in_bound ( flick, old_pos + delta, flick_rc) ;
455+
456+ if smooth_scroll {
457+ inner. smooth_scroll_target = new_pos;
458+ inner. smooth_scroll_time = Some ( Instant :: now ( ) ) ;
459+
460+ // Discard previous animation
461+ viewport_x. set_binding ( || euclid:: Length :: default ( ) ) ;
462+ viewport_y. set_binding ( || euclid:: Length :: default ( ) ) ;
463+
464+ viewport_x. set_animated_value ( new_pos. x_length ( ) , SMOOTH_SCROLL_ANIM ) ;
465+ viewport_y. set_animated_value ( new_pos. y_length ( ) , SMOOTH_SCROLL_ANIM ) ;
466+ } else {
467+ viewport_x. set ( new_pos. x_length ( ) ) ;
468+ viewport_y. set ( new_pos. y_length ( ) ) ;
469+ }
470+
471+ if old_pos != new_pos {
428472 ( Flickable :: FIELD_OFFSETS . flicked ) . apply_pin ( flick) . call ( & ( ) ) ;
429473 }
430474 InputEventResult :: EventAccepted
@@ -449,24 +493,21 @@ impl FlickableData {
449493 {
450494 let speed = dist / ( millis as f32 ) ;
451495
452- let duration = 250 ;
453496 let final_pos = ensure_in_bound (
454497 flick,
455- ( inner. pressed_viewport_pos . cast ( ) + dist + speed * ( duration as f32 ) ) . cast ( ) ,
498+ ( inner. pressed_viewport_pos . cast ( )
499+ + dist
500+ + speed * ( SMOOTH_SCROLL_DURATION as f32 ) )
501+ . cast ( ) ,
456502 flick_rc,
457503 ) ;
458- let anim = PropertyAnimation {
459- duration,
460- easing : EasingCurve :: CubicBezier ( [ 0.0 , 0.0 , 0.58 , 1.0 ] ) ,
461- ..PropertyAnimation :: default ( )
462- } ;
463504
464505 let viewport_x = ( Flickable :: FIELD_OFFSETS . viewport_x ) . apply_pin ( flick) ;
465506 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 ( ) {
507+ let old_pos = LogicalPoint :: from_lengths ( viewport_x. get ( ) , viewport_y. get ( ) ) ;
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 ) ;
510+ if old_pos != final_pos {
470511 ( Flickable :: FIELD_OFFSETS . flicked ) . apply_pin ( flick) . call ( & ( ) ) ;
471512 }
472513 }
0 commit comments