@@ -150,12 +150,6 @@ where
150150 let content = layout. children ( ) . next ( ) . unwrap ( ) ;
151151 let content_bounds = content. bounds ( ) ;
152152
153- let is_mouse_over_scrollbar = renderer. is_mouse_over_scrollbar (
154- bounds,
155- content_bounds,
156- cursor_position,
157- ) ;
158-
159153 // TODO: Event capture. Nested scrollables should capture scroll events.
160154 if is_mouse_over {
161155 match event {
@@ -174,49 +168,66 @@ where
174168 }
175169 }
176170
177- if self . state . is_scrollbar_grabbed ( ) || is_mouse_over_scrollbar {
171+ let offset = self . state . offset ( bounds, content_bounds) ;
172+ let scrollbar = renderer. scrollbar ( bounds, content_bounds, offset) ;
173+ let is_mouse_over_scrollbar = scrollbar
174+ . as_ref ( )
175+ . map ( |scrollbar| scrollbar. is_mouse_over ( cursor_position) )
176+ . unwrap_or ( false ) ;
177+
178+ if self . state . is_scroller_grabbed ( ) {
178179 match event {
179180 Event :: Mouse ( mouse:: Event :: Input {
180181 button : mouse:: Button :: Left ,
181- state,
182- } ) => match state {
183- ButtonState :: Pressed => {
184- self . state . scroll_to (
185- cursor_position. y / ( bounds. y + bounds. height ) ,
186- bounds,
187- content_bounds,
188- ) ;
189-
190- self . state . scrollbar_grabbed_at = Some ( cursor_position) ;
191- }
192- ButtonState :: Released => {
193- self . state . scrollbar_grabbed_at = None ;
194- }
195- } ,
182+ state : ButtonState :: Released ,
183+ } ) => {
184+ self . state . scroller_grabbed_at = None ;
185+ }
196186 Event :: Mouse ( mouse:: Event :: CursorMoved { .. } ) => {
197- if let Some ( scrollbar_grabbed_at ) =
198- self . state . scrollbar_grabbed_at
187+ if let ( Some ( scrollbar ) , Some ( scroller_grabbed_at ) ) =
188+ ( scrollbar , self . state . scroller_grabbed_at )
199189 {
200- let ratio = content_bounds . height / bounds . height ;
201- let delta = scrollbar_grabbed_at . y - cursor_position . y ;
202-
203- self . state . scroll (
204- delta * ratio ,
190+ self . state . scroll_to (
191+ scrollbar . scroll_percentage (
192+ scroller_grabbed_at ,
193+ cursor_position ,
194+ ) ,
205195 bounds,
206196 content_bounds,
207197 ) ;
208-
209- self . state . scrollbar_grabbed_at = Some ( cursor_position) ;
198+ }
199+ }
200+ _ => { }
201+ }
202+ } else if is_mouse_over_scrollbar {
203+ match event {
204+ Event :: Mouse ( mouse:: Event :: Input {
205+ button : mouse:: Button :: Left ,
206+ state : ButtonState :: Pressed ,
207+ } ) => {
208+ if let Some ( scrollbar) = scrollbar {
209+ if let Some ( scroller_grabbed_at) =
210+ scrollbar. grab_scroller ( cursor_position)
211+ {
212+ self . state . scroll_to (
213+ scrollbar. scroll_percentage (
214+ scroller_grabbed_at,
215+ cursor_position,
216+ ) ,
217+ bounds,
218+ content_bounds,
219+ ) ;
220+
221+ self . state . scroller_grabbed_at =
222+ Some ( scroller_grabbed_at) ;
223+ }
210224 }
211225 }
212226 _ => { }
213227 }
214228 }
215229
216- let cursor_position = if is_mouse_over
217- && !( is_mouse_over_scrollbar
218- || self . state . scrollbar_grabbed_at . is_some ( ) )
219- {
230+ let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar {
220231 Point :: new (
221232 cursor_position. x ,
222233 cursor_position. y
@@ -249,13 +260,13 @@ where
249260 let content_layout = layout. children ( ) . next ( ) . unwrap ( ) ;
250261 let content_bounds = content_layout. bounds ( ) ;
251262 let offset = self . state . offset ( bounds, content_bounds) ;
263+ let scrollbar = renderer. scrollbar ( bounds, content_bounds, offset) ;
252264
253265 let is_mouse_over = bounds. contains ( cursor_position) ;
254- let is_mouse_over_scrollbar = renderer. is_mouse_over_scrollbar (
255- bounds,
256- content_bounds,
257- cursor_position,
258- ) ;
266+ let is_mouse_over_scrollbar = scrollbar
267+ . as_ref ( )
268+ . map ( |scrollbar| scrollbar. is_mouse_over ( cursor_position) )
269+ . unwrap_or ( false ) ;
259270
260271 let content = {
261272 let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar {
@@ -274,6 +285,7 @@ where
274285 content_layout. bounds ( ) ,
275286 is_mouse_over,
276287 is_mouse_over_scrollbar,
288+ scrollbar,
277289 offset,
278290 content,
279291 )
@@ -294,7 +306,7 @@ where
294306/// [`Scrollable`]: struct.Scrollable.html
295307#[ derive( Debug , Clone , Copy , Default ) ]
296308pub struct State {
297- scrollbar_grabbed_at : Option < Point > ,
309+ scroller_grabbed_at : Option < f32 > ,
298310 offset : f32 ,
299311}
300312
@@ -356,12 +368,68 @@ impl State {
356368 self . offset . min ( hidden_content as f32 ) as u32
357369 }
358370
359- /// Returns whether the scrollbar is currently grabbed or not.
360- pub fn is_scrollbar_grabbed ( & self ) -> bool {
361- self . scrollbar_grabbed_at . is_some ( )
371+ /// Returns whether the scroller is currently grabbed or not.
372+ pub fn is_scroller_grabbed ( & self ) -> bool {
373+ self . scroller_grabbed_at . is_some ( )
362374 }
363375}
364376
377+ /// The scrollbar of a [`Scrollable`].
378+ ///
379+ /// [`Scrollable`]: struct.Scrollable.html
380+ #[ derive( Debug ) ]
381+ pub struct Scrollbar {
382+ /// The bounds of the [`Scrollbar`].
383+ ///
384+ /// [`Scrollbar`]: struct.Scrollbar.html
385+ pub bounds : Rectangle ,
386+
387+ /// The bounds of the [`Scroller`].
388+ ///
389+ /// [`Scroller`]: struct.Scroller.html
390+ pub scroller : Scroller ,
391+ }
392+
393+ impl Scrollbar {
394+ fn is_mouse_over ( & self , cursor_position : Point ) -> bool {
395+ self . bounds . contains ( cursor_position)
396+ }
397+
398+ fn grab_scroller ( & self , cursor_position : Point ) -> Option < f32 > {
399+ if self . bounds . contains ( cursor_position) {
400+ Some ( if self . scroller . bounds . contains ( cursor_position) {
401+ ( cursor_position. y - self . scroller . bounds . y )
402+ / self . scroller . bounds . height
403+ } else {
404+ 0.5
405+ } )
406+ } else {
407+ None
408+ }
409+ }
410+
411+ fn scroll_percentage (
412+ & self ,
413+ grabbed_at : f32 ,
414+ cursor_position : Point ,
415+ ) -> f32 {
416+ ( cursor_position. y + self . bounds . y
417+ - self . scroller . bounds . height * grabbed_at)
418+ / ( self . bounds . height - self . scroller . bounds . height )
419+ }
420+ }
421+
422+ /// The handle of a [`Scrollbar`].
423+ ///
424+ /// [`Scrollbar`]: struct.Scrollbar.html
425+ #[ derive( Debug , Clone , Copy ) ]
426+ pub struct Scroller {
427+ /// The bounds of the [`Scroller`].
428+ ///
429+ /// [`Scroller`]: struct.Scrollbar.html
430+ pub bounds : Rectangle ,
431+ }
432+
365433/// The renderer of a [`Scrollable`].
366434///
367435/// Your [renderer] will need to implement this trait before being
@@ -370,27 +438,31 @@ impl State {
370438/// [`Scrollable`]: struct.Scrollable.html
371439/// [renderer]: ../../renderer/index.html
372440pub trait Renderer : crate :: Renderer + Sized {
373- /// Returns whether the mouse is over the scrollbar given the bounds of
374- /// the [`Scrollable`] and its contents .
441+ /// Returns the [`Scrollbar`] given the bounds and content bounds of a
442+ /// [`Scrollable`].
375443 ///
444+ /// [`Scrollbar`]: struct.Scrollbar.html
376445 /// [`Scrollable`]: struct.Scrollable.html
377- fn is_mouse_over_scrollbar (
446+ fn scrollbar (
378447 & self ,
379448 bounds : Rectangle ,
380449 content_bounds : Rectangle ,
381- cursor_position : Point ,
382- ) -> bool ;
450+ offset : u32 ,
451+ ) -> Option < Scrollbar > ;
383452
384453 /// Draws the [`Scrollable`].
385454 ///
386455 /// It receives:
387456 /// - the [`State`] of the [`Scrollable`]
388- /// - the bounds of the [`Scrollable`]
457+ /// - the bounds of the [`Scrollable`] widget
458+ /// - the bounds of the [`Scrollable`] content
389459 /// - whether the mouse is over the [`Scrollable`] or not
390- /// - whether the mouse is over the scrollbar or not
460+ /// - whether the mouse is over the [`Scrollbar`] or not
461+ /// - a optional [`Scrollbar`] to be rendered
391462 /// - the scrolling offset
392463 /// - the drawn content
393464 ///
465+ /// [`Scrollbar`]: struct.Scrollbar.html
394466 /// [`Scrollable`]: struct.Scrollable.html
395467 /// [`State`]: struct.State.html
396468 fn draw (
@@ -400,6 +472,7 @@ pub trait Renderer: crate::Renderer + Sized {
400472 content_bounds : Rectangle ,
401473 is_mouse_over : bool ,
402474 is_mouse_over_scrollbar : bool ,
475+ scrollbar : Option < Scrollbar > ,
403476 offset : u32 ,
404477 content : Self :: Output ,
405478 ) -> Self :: Output ;
0 commit comments