@@ -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 ,
@@ -29,7 +29,9 @@ use alloc::rc::Rc;
2929use const_field_offset:: FieldOffsets ;
3030use core:: cell:: RefCell ;
3131use core:: pin:: Pin ;
32+ use core:: ptr:: NonNull ;
3233use core:: time:: Duration ;
34+ use std:: dbg;
3335#[ allow( unused) ]
3436use euclid:: num:: Ceil ;
3537use euclid:: num:: Zero ;
@@ -92,6 +94,26 @@ impl Item for Flickable {
9294 }
9395 } ,
9496 ) ;
97+
98+ self . data . viewport_change_handler . init_delayed (
99+ self_rc. downgrade ( ) ,
100+ |self_weak| {
101+ let Some ( flick_rc) = self_weak. upgrade ( ) else { return Default :: default ( ) } ;
102+ let Some ( flick) = flick_rc. downcast :: < Flickable > ( ) else {
103+ return Default :: default ( ) ;
104+ } ;
105+ let flick = flick. as_pin_ref ( ) ;
106+
107+ ( flick. viewport_x ( ) . get ( ) , flick. viewport_y ( ) . get ( ) )
108+ } ,
109+ |self_weak, _| {
110+ let Some ( flick_rc) = self_weak. upgrade ( ) else { return } ;
111+ let Some ( flick) = flick_rc. downcast :: < Flickable > ( ) else { return } ;
112+ let flick = flick. as_pin_ref ( ) ;
113+
114+ flick. flicked . call ( & ( ) )
115+ } ,
116+ ) ;
95117 }
96118
97119 fn layout_info (
@@ -242,6 +264,15 @@ pub(super) const DURATION_THRESHOLD: Duration = Duration::from_millis(500);
242264/// The delay to which press are forwarded to the inner item
243265pub ( super ) const FORWARD_DELAY : Duration = Duration :: from_millis ( 100 ) ;
244266
267+ const SMOOTH_SCROLL_DURATION : i32 = 250 ;
268+ const SMOOTH_SCROLL_ANIM : PropertyAnimation = PropertyAnimation {
269+ duration : SMOOTH_SCROLL_DURATION ,
270+ easing : EasingCurve :: CubicBezier ( [ 0.0 , 0.0 , 0.58 , 1.0 ] ) ,
271+ delay : 0 ,
272+ iteration_count : 1. ,
273+ direction : AnimationDirection :: Normal ,
274+ } ;
275+
245276#[ derive( Default , Debug ) ]
246277struct FlickableDataInner {
247278 /// The position in which the press was made
@@ -250,13 +281,17 @@ struct FlickableDataInner {
250281 pressed_viewport_pos : LogicalPoint ,
251282 /// Set to true if the flickable is flicking and capturing all mouse event, not forwarding back to the children
252283 capture_events : bool ,
284+ smooth_scroll_time : Option < Instant > ,
285+ smooth_scroll_target : LogicalPoint ,
253286}
254287
255288#[ derive( Default , Debug ) ]
256289pub struct FlickableData {
257290 inner : RefCell < FlickableDataInner > ,
258291 /// Tracker that tracks the property to make sure that the flickable is in bounds
259292 in_bound_change_handler : crate :: properties:: ChangeTracker ,
293+ // Scroll trackers for flicked callback
294+ viewport_change_handler : crate :: properties:: ChangeTracker ,
260295}
261296
262297impl FlickableData {
@@ -372,12 +407,8 @@ impl FlickableData {
372407 if inner. capture_events || should_capture ( ) {
373408 let new_pos = ensure_in_bound ( flick, new_pos, flick_rc) ;
374409
375- let old_pos = ( x. get ( ) , y. get ( ) ) ;
376410 x. set ( new_pos. x_length ( ) ) ;
377411 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- }
381412
382413 inner. capture_events = true ;
383414 InputEventResult :: GrabMouse
@@ -395,7 +426,7 @@ impl FlickableData {
395426 }
396427 }
397428 MouseEvent :: Wheel { delta_x, delta_y, .. } => {
398- let delta = if window_adapter. window ( ) . 0 . modifiers . get ( ) . shift ( )
429+ let mut delta = if window_adapter. window ( ) . 0 . modifiers . get ( ) . shift ( )
399430 && !cfg ! ( target_os = "macos" )
400431 {
401432 // Shift invert coordinate for the purpose of scrolling. But not on macOs because there the OS already take care of the change
@@ -413,20 +444,36 @@ impl FlickableData {
413444 return InputEventResult :: EventIgnored ;
414445 }
415446
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-
422447 let viewport_x = ( Flickable :: FIELD_OFFSETS . viewport_x ) . apply_pin ( flick) ;
423448 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 ( & ( ) ) ;
449+
450+ let old_pos = LogicalPoint :: from_lengths ( viewport_x. get ( ) , viewport_y. get ( ) ) ;
451+
452+ // Accumulate scroll delta
453+ if let Some ( smooth_scroll_time) = inner. smooth_scroll_time . take ( ) {
454+ let millis =
455+ ( crate :: animations:: current_tick ( ) - smooth_scroll_time) . as_millis ( ) as i32 ;
456+
457+ if millis < SMOOTH_SCROLL_DURATION {
458+ let remaining_delta = inner. smooth_scroll_target - old_pos;
459+
460+ // Only if is in the same direction.
461+ // `Default` is because `dot` returns `i32` in embedded
462+ // but it returns `f32` in any other platform
463+ if delta. dot ( remaining_delta) > Default :: default ( ) {
464+ delta += remaining_delta;
465+ }
466+ }
429467 }
468+
469+ let new_pos = ensure_in_bound ( flick, old_pos + delta, flick_rc) ;
470+
471+ inner. smooth_scroll_target = new_pos;
472+ inner. smooth_scroll_time = Some ( Instant :: now ( ) ) ;
473+
474+ viewport_y. set_animated_value ( new_pos. y_length ( ) , SMOOTH_SCROLL_ANIM ) ;
475+ viewport_x. set_animated_value ( new_pos. x_length ( ) , SMOOTH_SCROLL_ANIM ) ;
476+
430477 InputEventResult :: EventAccepted
431478 }
432479 MouseEvent :: DragMove ( ..) | MouseEvent :: Drop ( ..) => InputEventResult :: EventIgnored ,
@@ -449,26 +496,19 @@ impl FlickableData {
449496 {
450497 let speed = dist / ( millis as f32 ) ;
451498
452- let duration = 250 ;
453499 let final_pos = ensure_in_bound (
454500 flick,
455- ( inner. pressed_viewport_pos . cast ( ) + dist + speed * ( duration as f32 ) ) . cast ( ) ,
501+ ( inner. pressed_viewport_pos . cast ( )
502+ + dist
503+ + speed * ( SMOOTH_SCROLL_DURATION as f32 ) )
504+ . cast ( ) ,
456505 flick_rc,
457506 ) ;
458- let anim = PropertyAnimation {
459- duration,
460- easing : EasingCurve :: CubicBezier ( [ 0.0 , 0.0 , 0.58 , 1.0 ] ) ,
461- ..PropertyAnimation :: default ( )
462- } ;
463507
464508 let viewport_x = ( Flickable :: FIELD_OFFSETS . viewport_x ) . apply_pin ( flick) ;
465509 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- }
510+ viewport_x. set_animated_value ( final_pos. x_length ( ) , SMOOTH_SCROLL_ANIM ) ;
511+ viewport_y. set_animated_value ( final_pos. y_length ( ) , SMOOTH_SCROLL_ANIM ) ;
472512 }
473513 }
474514 inner. capture_events = false ; // FIXME: should only be set to false once the flick animation is over
0 commit comments