@@ -15,7 +15,7 @@ use crate::{
1515} ;
1616
1717pub use iced_style:: scrollable:: StyleSheet ;
18- pub use operation:: scrollable:: RelativeOffset ;
18+ pub use operation:: scrollable:: { AbsoluteOffset , RelativeOffset } ;
1919
2020pub mod style {
2121 //! The styles of a [`Scrollable`].
3838 vertical : Properties ,
3939 horizontal : Option < Properties > ,
4040 content : Element < ' a , Message , Renderer > ,
41- on_scroll : Option < Box < dyn Fn ( RelativeOffset ) -> Message + ' a > > ,
41+ on_scroll : Option < Box < dyn Fn ( Viewport ) -> Message + ' a > > ,
4242 style : <Renderer :: Theme as StyleSheet >:: Style ,
4343}
4444
9393
9494 /// Sets a function to call when the [`Scrollable`] is scrolled.
9595 ///
96- /// The function takes the new relative x & y offset of the [`Scrollable`]
97- /// (e.g. `0` means beginning, while `1` means end).
98- pub fn on_scroll (
99- mut self ,
100- f : impl Fn ( RelativeOffset ) -> Message + ' a ,
101- ) -> Self {
96+ /// The function takes the [`Viewport`] of the [`Scrollable`]
97+ pub fn on_scroll ( mut self , f : impl Fn ( Viewport ) -> Message + ' a ) -> Self {
10298 self . on_scroll = Some ( Box :: new ( f) ) ;
10399 self
104100 }
@@ -436,7 +432,7 @@ pub fn update<Message>(
436432 shell : & mut Shell < ' _ , Message > ,
437433 vertical : & Properties ,
438434 horizontal : Option < & Properties > ,
439- on_scroll : & Option < Box < dyn Fn ( RelativeOffset ) -> Message + ' _ > > ,
435+ on_scroll : & Option < Box < dyn Fn ( Viewport ) -> Message + ' _ > > ,
440436 update_content : impl FnOnce (
441437 Event ,
442438 Layout < ' _ > ,
@@ -896,7 +892,7 @@ pub fn draw<Renderer>(
896892
897893fn notify_on_scroll < Message > (
898894 state : & mut State ,
899- on_scroll : & Option < Box < dyn Fn ( RelativeOffset ) -> Message + ' _ > > ,
895+ on_scroll : & Option < Box < dyn Fn ( Viewport ) -> Message + ' _ > > ,
900896 bounds : Rectangle ,
901897 content_bounds : Rectangle ,
902898 shell : & mut Shell < ' _ , Message > ,
@@ -908,31 +904,36 @@ fn notify_on_scroll<Message>(
908904 return ;
909905 }
910906
911- let x = state. offset_x . absolute ( bounds. width , content_bounds. width )
912- / ( content_bounds. width - bounds. width ) ;
907+ let viewport = Viewport {
908+ offset_x : state. offset_x ,
909+ offset_y : state. offset_y ,
910+ bounds,
911+ content_bounds,
912+ } ;
913913
914- let y = state
915- . offset_y
916- . absolute ( bounds . height , content_bounds . height )
917- / ( content_bounds . height - bounds . height ) ;
914+ // Don't publish redundant viewports to shell
915+ if let Some ( last_notified ) = state . last_notified {
916+ let last_relative_offset = last_notified . relative_offset ( ) ;
917+ let current_relative_offset = viewport . relative_offset ( ) ;
918918
919- let new_offset = RelativeOffset { x, y } ;
919+ let last_absolute_offset = last_notified. absolute_offset ( ) ;
920+ let current_absolute_offset = viewport. absolute_offset ( ) ;
920921
921- // Don't publish redundant offsets to shell
922- if let Some ( prev_offset) = state. last_notified {
923922 let unchanged = |a : f32 , b : f32 | {
924923 ( a - b) . abs ( ) <= f32:: EPSILON || ( a. is_nan ( ) && b. is_nan ( ) )
925924 } ;
926925
927- if unchanged ( prev_offset. x , new_offset. x )
928- && unchanged ( prev_offset. y , new_offset. y )
926+ if unchanged ( last_relative_offset. x , current_relative_offset. x )
927+ && unchanged ( last_relative_offset. y , current_relative_offset. y )
928+ && unchanged ( last_absolute_offset. x , current_absolute_offset. x )
929+ && unchanged ( last_absolute_offset. y , current_absolute_offset. y )
929930 {
930931 return ;
931932 }
932933 }
933934
934- shell. publish ( on_scroll ( new_offset ) ) ;
935- state. last_notified = Some ( new_offset ) ;
935+ shell. publish ( on_scroll ( viewport ) ) ;
936+ state. last_notified = Some ( viewport ) ;
936937 }
937938}
938939
@@ -945,7 +946,7 @@ pub struct State {
945946 offset_x : Offset ,
946947 x_scroller_grabbed_at : Option < f32 > ,
947948 keyboard_modifiers : keyboard:: Modifiers ,
948- last_notified : Option < RelativeOffset > ,
949+ last_notified : Option < Viewport > ,
949950}
950951
951952impl Default for State {
@@ -966,6 +967,10 @@ impl operation::Scrollable for State {
966967 fn snap_to ( & mut self , offset : RelativeOffset ) {
967968 State :: snap_to ( self , offset) ;
968969 }
970+
971+ fn scroll_to ( & mut self , offset : AbsoluteOffset ) {
972+ State :: scroll_to ( self , offset)
973+ }
969974}
970975
971976#[ derive( Debug , Clone , Copy ) ]
@@ -975,18 +980,51 @@ enum Offset {
975980}
976981
977982impl Offset {
978- fn absolute ( self , window : f32 , content : f32 ) -> f32 {
983+ fn absolute ( self , viewport : f32 , content : f32 ) -> f32 {
979984 match self {
980985 Offset :: Absolute ( absolute) => {
981- absolute. min ( ( content - window ) . max ( 0.0 ) )
986+ absolute. min ( ( content - viewport ) . max ( 0.0 ) )
982987 }
983988 Offset :: Relative ( percentage) => {
984- ( ( content - window ) * percentage) . max ( 0.0 )
989+ ( ( content - viewport ) * percentage) . max ( 0.0 )
985990 }
986991 }
987992 }
988993}
989994
995+ /// The current [`Viewport`] of the [`Scrollable`].
996+ #[ derive( Debug , Clone , Copy ) ]
997+ pub struct Viewport {
998+ offset_x : Offset ,
999+ offset_y : Offset ,
1000+ bounds : Rectangle ,
1001+ content_bounds : Rectangle ,
1002+ }
1003+
1004+ impl Viewport {
1005+ /// Returns the [`AbsoluteOffset`] of the current [`Viewport`].
1006+ pub fn absolute_offset ( & self ) -> AbsoluteOffset {
1007+ let x = self
1008+ . offset_x
1009+ . absolute ( self . bounds . width , self . content_bounds . width ) ;
1010+ let y = self
1011+ . offset_y
1012+ . absolute ( self . bounds . height , self . content_bounds . height ) ;
1013+
1014+ AbsoluteOffset { x, y }
1015+ }
1016+
1017+ /// Returns the [`RelativeOffset`] of the current [`Viewport`].
1018+ pub fn relative_offset ( & self ) -> RelativeOffset {
1019+ let AbsoluteOffset { x, y } = self . absolute_offset ( ) ;
1020+
1021+ let x = x / ( self . content_bounds . width - self . bounds . width ) ;
1022+ let y = y / ( self . content_bounds . height - self . bounds . height ) ;
1023+
1024+ RelativeOffset { x, y }
1025+ }
1026+ }
1027+
9901028impl State {
9911029 /// Creates a new [`State`] with the scrollbar(s) at the beginning.
9921030 pub fn new ( ) -> Self {
@@ -1052,6 +1090,12 @@ impl State {
10521090 self . offset_y = Offset :: Relative ( offset. y . clamp ( 0.0 , 1.0 ) ) ;
10531091 }
10541092
1093+ /// Scroll to the provided [`AbsoluteOffset`].
1094+ pub fn scroll_to ( & mut self , offset : AbsoluteOffset ) {
1095+ self . offset_x = Offset :: Absolute ( offset. x . max ( 0.0 ) ) ;
1096+ self . offset_y = Offset :: Absolute ( offset. y . max ( 0.0 ) ) ;
1097+ }
1098+
10551099 /// Unsnaps the current scroll position, if snapped, given the bounds of the
10561100 /// [`Scrollable`] and its contents.
10571101 pub fn unsnap ( & mut self , bounds : Rectangle , content_bounds : Rectangle ) {
0 commit comments