From 5eec3a8867f4fbd54e2e28e5d984c2ca7ec0fea4 Mon Sep 17 00:00:00 2001
From: Friz64 <friz64@arch.localdomain>
Date: Mon, 25 Nov 2019 20:27:15 +0100
Subject: First implementation

---
 native/src/renderer/null.rs     |  9 ++---
 native/src/widget.rs            |  2 +-
 native/src/widget/scrollable.rs | 73 +++++++++++++++++++++++++++++------------
 3 files changed, 58 insertions(+), 26 deletions(-)

(limited to 'native')

diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs
index 182f033a..c8852ad1 100644
--- a/native/src/renderer/null.rs
+++ b/native/src/renderer/null.rs
@@ -1,7 +1,7 @@
 use crate::{
     button, checkbox, column, radio, row, scrollable, text, text_input,
     Background, Color, Element, Font, HorizontalAlignment, Layout, Point,
-    Rectangle, Renderer, Size, VerticalAlignment,
+    Rectangle, Renderer, ScrollbarGrab, Size, VerticalAlignment,
 };
 
 /// A renderer that does nothing.
@@ -61,13 +61,14 @@ impl text::Renderer for Null {
 }
 
 impl scrollable::Renderer for Null {
-    fn is_mouse_over_scrollbar(
+    fn scrollbar_grab(
         &self,
         _bounds: Rectangle,
         _content_bounds: Rectangle,
+        _offset: u32,
         _cursor_position: Point,
-    ) -> bool {
-        false
+    ) -> Option<ScrollbarGrab> {
+        None
     }
 
     fn draw(
diff --git a/native/src/widget.rs b/native/src/widget.rs
index 71dcdc0d..a2d3aa12 100644
--- a/native/src/widget.rs
+++ b/native/src/widget.rs
@@ -47,7 +47,7 @@ pub use radio::Radio;
 #[doc(no_inline)]
 pub use row::Row;
 #[doc(no_inline)]
-pub use scrollable::Scrollable;
+pub use scrollable::{Scrollable, ScrollbarGrab};
 #[doc(no_inline)]
 pub use slider::Slider;
 #[doc(no_inline)]
diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs
index 678d837a..fed7f54e 100644
--- a/native/src/widget/scrollable.rs
+++ b/native/src/widget/scrollable.rs
@@ -150,9 +150,11 @@ where
         let content = layout.children().next().unwrap();
         let content_bounds = content.bounds();
 
-        let is_mouse_over_scrollbar = renderer.is_mouse_over_scrollbar(
+        let offset = self.state.offset(bounds, content_bounds);
+        let scrollbar_grab = renderer.scrollbar_grab(
             bounds,
             content_bounds,
+            offset,
             cursor_position,
         );
 
@@ -174,25 +176,45 @@ where
             }
         }
 
-        if self.state.is_scrollbar_grabbed() || is_mouse_over_scrollbar {
+        if self.state.currently_grabbed() || scrollbar_grab.is_some() {
             match event {
                 Event::Mouse(mouse::Event::Input {
                     button: mouse::Button::Left,
                     state,
                 }) => match state {
                     ButtonState::Pressed => {
-                        self.state.scroll_to(
-                            cursor_position.y / (bounds.y + bounds.height),
+                        let (scrollbar_grab, scroller_bounds) =
+                            scrollbar_grab.unwrap();
+
+                        let scroller_grabbed_at = match scrollbar_grab {
+                            ScrollbarGrab::Background => 0.5,
+                            ScrollbarGrab::Scroller => {
+                                (cursor_position.y - scroller_bounds.y)
+                                    / scroller_bounds.height
+                            }
+                        };
+
+                        let scroll_percentage = (cursor_position.y
+                            - (scroller_bounds.height * scroller_grabbed_at))
+                            / (bounds.height
+                                - (scroller_bounds.height
+                                    * scroller_grabbed_at));
+
+                        dbg!((scroll_percentage, scroller_grabbed_at));
+                        /*self.state.scroll_to(
+                            scroll_percentage,
                             bounds,
                             content_bounds,
-                        );
+                        );*/
 
-                        self.state.scrollbar_grabbed_at = Some(cursor_position);
+                        self.state.scroller_grabbed_at =
+                            Some(scroller_grabbed_at);
                     }
                     ButtonState::Released => {
-                        self.state.scrollbar_grabbed_at = None;
+                        self.state.scroller_grabbed_at = None;
                     }
                 },
+                /* TODO: Implement dragging to scroll
                 Event::Mouse(mouse::Event::CursorMoved { .. }) => {
                     if let Some(scrollbar_grabbed_at) =
                         self.state.scrollbar_grabbed_at
@@ -209,13 +231,13 @@ where
                         self.state.scrollbar_grabbed_at = Some(cursor_position);
                     }
                 }
+                */
                 _ => {}
             }
         }
 
         let cursor_position = if is_mouse_over
-            && !(is_mouse_over_scrollbar
-                || self.state.scrollbar_grabbed_at.is_some())
+            && !(scrollbar_grab.is_some() || self.state.currently_grabbed())
         {
             Point::new(
                 cursor_position.x,
@@ -251,11 +273,9 @@ where
         let offset = self.state.offset(bounds, content_bounds);
 
         let is_mouse_over = bounds.contains(cursor_position);
-        let is_mouse_over_scrollbar = renderer.is_mouse_over_scrollbar(
-            bounds,
-            content_bounds,
-            cursor_position,
-        );
+        let is_mouse_over_scrollbar = renderer
+            .scrollbar_grab(bounds, content_bounds, offset, cursor_position)
+            .is_some();
 
         let content = {
             let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar {
@@ -294,7 +314,7 @@ where
 /// [`Scrollable`]: struct.Scrollable.html
 #[derive(Debug, Clone, Copy, Default)]
 pub struct State {
-    scrollbar_grabbed_at: Option<Point>,
+    scroller_grabbed_at: Option<f32>,
     offset: f32,
 }
 
@@ -357,11 +377,20 @@ impl State {
     }
 
     /// Returns whether the scrollbar is currently grabbed or not.
-    pub fn is_scrollbar_grabbed(&self) -> bool {
-        self.scrollbar_grabbed_at.is_some()
+    pub fn currently_grabbed(&self) -> bool {
+        self.scroller_grabbed_at.is_some()
     }
 }
 
+#[derive(Debug, Clone, Copy)]
+/// What the mouse is grabbing on the scrollbar
+pub enum ScrollbarGrab {
+    /// The mouse is grabbing the background
+    Background,
+    /// The mouse is grabbing the scroller
+    Scroller,
+}
+
 /// The renderer of a [`Scrollable`].
 ///
 /// Your [renderer] will need to implement this trait before being
@@ -370,16 +399,18 @@ impl State {
 /// [`Scrollable`]: struct.Scrollable.html
 /// [renderer]: ../../renderer/index.html
 pub trait Renderer: crate::Renderer + Sized {
-    /// Returns whether the mouse is over the scrollbar given the bounds of
-    /// the [`Scrollable`] and its contents.
+    /// Returns what part of the scrollbar is being grabbed by the mouse
+    /// given the bounds of the [`Scrollable`] and its contents together
+    /// with the current scroller bounds.
     ///
     /// [`Scrollable`]: struct.Scrollable.html
-    fn is_mouse_over_scrollbar(
+    fn scrollbar_grab(
         &self,
         bounds: Rectangle,
         content_bounds: Rectangle,
+        offset: u32,
         cursor_position: Point,
-    ) -> bool;
+    ) -> Option<(ScrollbarGrab, Rectangle)>;
 
     /// Draws the [`Scrollable`].
     ///
-- 
cgit 


From f8fac432c665e57267243a9ee3920208b2724e6e Mon Sep 17 00:00:00 2001
From: Friz64 <friz64mc@gmail.com>
Date: Sat, 30 Nov 2019 17:30:42 +0100
Subject: Finalize work

---
 native/src/widget/scrollable.rs | 69 ++++++++++++++++++++++++-----------------
 1 file changed, 41 insertions(+), 28 deletions(-)

(limited to 'native')

diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs
index fed7f54e..17a1363e 100644
--- a/native/src/widget/scrollable.rs
+++ b/native/src/widget/scrollable.rs
@@ -151,10 +151,13 @@ where
         let content_bounds = content.bounds();
 
         let offset = self.state.offset(bounds, content_bounds);
+        let (background_bounds, scroller_bounds) =
+            renderer.scrollbar_bounds(bounds, content_bounds, offset);
         let scrollbar_grab = renderer.scrollbar_grab(
             bounds,
             content_bounds,
-            offset,
+            background_bounds,
+            scroller_bounds,
             cursor_position,
         );
 
@@ -183,10 +186,8 @@ where
                     state,
                 }) => match state {
                     ButtonState::Pressed => {
-                        let (scrollbar_grab, scroller_bounds) =
-                            scrollbar_grab.unwrap();
-
-                        let scroller_grabbed_at = match scrollbar_grab {
+                        let scroller_grabbed_at = match scrollbar_grab.unwrap()
+                        {
                             ScrollbarGrab::Background => 0.5,
                             ScrollbarGrab::Scroller => {
                                 (cursor_position.y - scroller_bounds.y)
@@ -194,18 +195,15 @@ where
                             }
                         };
 
-                        let scroll_percentage = (cursor_position.y
-                            - (scroller_bounds.height * scroller_grabbed_at))
-                            / (bounds.height
-                                - (scroller_bounds.height
-                                    * scroller_grabbed_at));
+                        let scroll_percentage = (cursor_position.y + bounds.y
+                            - scroller_bounds.height * scroller_grabbed_at)
+                            / (bounds.height - scroller_bounds.height);
 
-                        dbg!((scroll_percentage, scroller_grabbed_at));
-                        /*self.state.scroll_to(
+                        self.state.scroll_to(
                             scroll_percentage,
                             bounds,
                             content_bounds,
-                        );*/
+                        );
 
                         self.state.scroller_grabbed_at =
                             Some(scroller_grabbed_at);
@@ -214,24 +212,21 @@ where
                         self.state.scroller_grabbed_at = None;
                     }
                 },
-                /* TODO: Implement dragging to scroll
                 Event::Mouse(mouse::Event::CursorMoved { .. }) => {
-                    if let Some(scrollbar_grabbed_at) =
-                        self.state.scrollbar_grabbed_at
+                    if let Some(scroller_grabbed_at) =
+                        self.state.scroller_grabbed_at
                     {
-                        let ratio = content_bounds.height / bounds.height;
-                        let delta = scrollbar_grabbed_at.y - cursor_position.y;
+                        let scroll_percentage = (cursor_position.y + bounds.y
+                            - scroller_bounds.height * scroller_grabbed_at)
+                            / (bounds.height - scroller_bounds.height);
 
-                        self.state.scroll(
-                            delta * ratio,
+                        self.state.scroll_to(
+                            scroll_percentage,
                             bounds,
                             content_bounds,
                         );
-
-                        self.state.scrollbar_grabbed_at = Some(cursor_position);
                     }
                 }
-                */
                 _ => {}
             }
         }
@@ -273,8 +268,16 @@ where
         let offset = self.state.offset(bounds, content_bounds);
 
         let is_mouse_over = bounds.contains(cursor_position);
+        let (background_bounds, scroller_bounds) =
+            renderer.scrollbar_bounds(bounds, content_bounds, offset);
         let is_mouse_over_scrollbar = renderer
-            .scrollbar_grab(bounds, content_bounds, offset, cursor_position)
+            .scrollbar_grab(
+                bounds,
+                content_bounds,
+                background_bounds,
+                scroller_bounds,
+                cursor_position,
+            )
             .is_some();
 
         let content = {
@@ -399,18 +402,28 @@ pub enum ScrollbarGrab {
 /// [`Scrollable`]: struct.Scrollable.html
 /// [renderer]: ../../renderer/index.html
 pub trait Renderer: crate::Renderer + Sized {
+    /// Returns the bounds of the scrollbar
+    /// - Background
+    /// - Movable Scroller
+    fn scrollbar_bounds(
+        &self,
+        bounds: Rectangle,
+        content_bounds: Rectangle,
+        offset: u32,
+    ) -> (Rectangle, Rectangle);
+
     /// Returns what part of the scrollbar is being grabbed by the mouse
-    /// given the bounds of the [`Scrollable`] and its contents together
-    /// with the current scroller bounds.
+    /// given the bounds of the [`Scrollable`] and its contents.
     ///
     /// [`Scrollable`]: struct.Scrollable.html
     fn scrollbar_grab(
         &self,
         bounds: Rectangle,
         content_bounds: Rectangle,
-        offset: u32,
+        background_bounds: Rectangle,
+        scroller_bounds: Rectangle,
         cursor_position: Point,
-    ) -> Option<(ScrollbarGrab, Rectangle)>;
+    ) -> Option<ScrollbarGrab>;
 
     /// Draws the [`Scrollable`].
     ///
-- 
cgit 


From c12ba14ddad40e8f0e656ae0f8a43c87f0b5907f Mon Sep 17 00:00:00 2001
From: Friz64 <friz64mc@gmail.com>
Date: Sat, 30 Nov 2019 17:49:42 +0100
Subject: Move scrolling percentage logic to separate function

---
 native/src/widget/scrollable.rs | 33 +++++++++++++++++++++++----------
 1 file changed, 23 insertions(+), 10 deletions(-)

(limited to 'native')

diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs
index 17a1363e..ab1a203b 100644
--- a/native/src/widget/scrollable.rs
+++ b/native/src/widget/scrollable.rs
@@ -102,6 +102,17 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> {
     }
 }
 
+fn scroll_percentage(
+    bounds: Rectangle,
+    scroller_bounds: Rectangle,
+    scroller_grabbed_at: f32,
+    cursor_position: Point,
+) -> f32 {
+    (cursor_position.y + bounds.y
+        - scroller_bounds.height * scroller_grabbed_at)
+        / (bounds.height - scroller_bounds.height)
+}
+
 impl<'a, Message, Renderer> Widget<Message, Renderer>
     for Scrollable<'a, Message, Renderer>
 where
@@ -195,12 +206,13 @@ where
                             }
                         };
 
-                        let scroll_percentage = (cursor_position.y + bounds.y
-                            - scroller_bounds.height * scroller_grabbed_at)
-                            / (bounds.height - scroller_bounds.height);
-
                         self.state.scroll_to(
-                            scroll_percentage,
+                            scroll_percentage(
+                                bounds,
+                                scroller_bounds,
+                                scroller_grabbed_at,
+                                cursor_position,
+                            ),
                             bounds,
                             content_bounds,
                         );
@@ -216,12 +228,13 @@ where
                     if let Some(scroller_grabbed_at) =
                         self.state.scroller_grabbed_at
                     {
-                        let scroll_percentage = (cursor_position.y + bounds.y
-                            - scroller_bounds.height * scroller_grabbed_at)
-                            / (bounds.height - scroller_bounds.height);
-
                         self.state.scroll_to(
-                            scroll_percentage,
+                            scroll_percentage(
+                                bounds,
+                                scroller_bounds,
+                                scroller_grabbed_at,
+                                cursor_position,
+                            ),
                             bounds,
                             content_bounds,
                         );
-- 
cgit 


From 9a733bb3c89e15ada05fc90efb71eeae12b8c9c1 Mon Sep 17 00:00:00 2001
From: Friz64 <friz64mc@gmail.com>
Date: Sat, 30 Nov 2019 18:16:01 +0100
Subject: Support null renderer

---
 native/src/renderer/null.rs | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

(limited to 'native')

diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs
index c8852ad1..c06d1dfa 100644
--- a/native/src/renderer/null.rs
+++ b/native/src/renderer/null.rs
@@ -61,11 +61,21 @@ impl text::Renderer for Null {
 }
 
 impl scrollable::Renderer for Null {
-    fn scrollbar_grab(
+    fn scrollbar_bounds(
         &self,
         _bounds: Rectangle,
         _content_bounds: Rectangle,
         _offset: u32,
+    ) -> (Rectangle, Rectangle) {
+        Default::default()
+    }
+
+    fn scrollbar_grab(
+        &self,
+        _bounds: Rectangle,
+        _content_bounds: Rectangle,
+        _background_bounds: Rectangle,
+        _scroller_bounds: Rectangle,
         _cursor_position: Point,
     ) -> Option<ScrollbarGrab> {
         None
-- 
cgit 


From 6943041e0fad65927a741e316509bf07bdaa2f61 Mon Sep 17 00:00:00 2001
From: Friz64 <friz64mc@gmail.com>
Date: Mon, 2 Dec 2019 18:51:34 +0100
Subject: Address suggestions

---
 native/src/renderer/null.rs     |  23 ++++-----
 native/src/widget.rs            |   2 +-
 native/src/widget/scrollable.rs | 108 ++++++++++++++++++++++++----------------
 3 files changed, 75 insertions(+), 58 deletions(-)

(limited to 'native')

diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs
index c06d1dfa..2ce150c0 100644
--- a/native/src/renderer/null.rs
+++ b/native/src/renderer/null.rs
@@ -1,7 +1,7 @@
 use crate::{
     button, checkbox, column, radio, row, scrollable, text, text_input,
     Background, Color, Element, Font, HorizontalAlignment, Layout, Point,
-    Rectangle, Renderer, ScrollbarGrab, Size, VerticalAlignment,
+    Rectangle, Renderer, Size, VerticalAlignment,
 };
 
 /// A renderer that does nothing.
@@ -61,24 +61,17 @@ impl text::Renderer for Null {
 }
 
 impl scrollable::Renderer for Null {
-    fn scrollbar_bounds(
-        &self,
-        _bounds: Rectangle,
-        _content_bounds: Rectangle,
-        _offset: u32,
-    ) -> (Rectangle, Rectangle) {
+    fn scrollbar_bounds(_bounds: Rectangle) -> Rectangle {
         Default::default()
     }
 
-    fn scrollbar_grab(
-        &self,
+    fn scroller_bounds(
         _bounds: Rectangle,
         _content_bounds: Rectangle,
-        _background_bounds: Rectangle,
-        _scroller_bounds: Rectangle,
-        _cursor_position: Point,
-    ) -> Option<ScrollbarGrab> {
-        None
+        _scrollbar_bounds: Rectangle,
+        _offset: u32,
+    ) -> Rectangle {
+        Default::default()
     }
 
     fn draw(
@@ -88,6 +81,8 @@ impl scrollable::Renderer for Null {
         _content_bounds: Rectangle,
         _is_mouse_over: bool,
         _is_mouse_over_scrollbar: bool,
+        _scrollbar_bounds: Rectangle,
+        _scroller_bounds: Rectangle,
         _offset: u32,
         _content: Self::Output,
     ) {
diff --git a/native/src/widget.rs b/native/src/widget.rs
index a2d3aa12..71dcdc0d 100644
--- a/native/src/widget.rs
+++ b/native/src/widget.rs
@@ -47,7 +47,7 @@ pub use radio::Radio;
 #[doc(no_inline)]
 pub use row::Row;
 #[doc(no_inline)]
-pub use scrollable::{Scrollable, ScrollbarGrab};
+pub use scrollable::Scrollable;
 #[doc(no_inline)]
 pub use slider::Slider;
 #[doc(no_inline)]
diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs
index ab1a203b..b48ae492 100644
--- a/native/src/widget/scrollable.rs
+++ b/native/src/widget/scrollable.rs
@@ -162,12 +162,17 @@ where
         let content_bounds = content.bounds();
 
         let offset = self.state.offset(bounds, content_bounds);
-        let (background_bounds, scroller_bounds) =
-            renderer.scrollbar_bounds(bounds, content_bounds, offset);
-        let scrollbar_grab = renderer.scrollbar_grab(
+        let scrollbar_bounds = Renderer::scrollbar_bounds(bounds);
+        let scroller_bounds = Renderer::scroller_bounds(
             bounds,
             content_bounds,
-            background_bounds,
+            scrollbar_bounds,
+            offset,
+        );
+        let scrollbar_grab = ScrollbarItem::from_cursor_position(
+            bounds,
+            content_bounds,
+            scrollbar_bounds,
             scroller_bounds,
             cursor_position,
         );
@@ -190,7 +195,7 @@ where
             }
         }
 
-        if self.state.currently_grabbed() || scrollbar_grab.is_some() {
+        if self.state.is_scroller_grabbed() || scrollbar_grab.is_some() {
             match event {
                 Event::Mouse(mouse::Event::Input {
                     button: mouse::Button::Left,
@@ -199,8 +204,8 @@ where
                     ButtonState::Pressed => {
                         let scroller_grabbed_at = match scrollbar_grab.unwrap()
                         {
-                            ScrollbarGrab::Background => 0.5,
-                            ScrollbarGrab::Scroller => {
+                            ScrollbarItem::Background => 0.5,
+                            ScrollbarItem::Scroller => {
                                 (cursor_position.y - scroller_bounds.y)
                                     / scroller_bounds.height
                             }
@@ -245,7 +250,7 @@ where
         }
 
         let cursor_position = if is_mouse_over
-            && !(scrollbar_grab.is_some() || self.state.currently_grabbed())
+            && !(scrollbar_grab.is_some() || self.state.is_scroller_grabbed())
         {
             Point::new(
                 cursor_position.x,
@@ -281,17 +286,21 @@ where
         let offset = self.state.offset(bounds, content_bounds);
 
         let is_mouse_over = bounds.contains(cursor_position);
-        let (background_bounds, scroller_bounds) =
-            renderer.scrollbar_bounds(bounds, content_bounds, offset);
-        let is_mouse_over_scrollbar = renderer
-            .scrollbar_grab(
-                bounds,
-                content_bounds,
-                background_bounds,
-                scroller_bounds,
-                cursor_position,
-            )
-            .is_some();
+        let scrollbar_bounds = Renderer::scrollbar_bounds(bounds);
+        let scroller_bounds = Renderer::scroller_bounds(
+            bounds,
+            content_bounds,
+            scrollbar_bounds,
+            offset,
+        );
+        let is_mouse_over_scrollbar = ScrollbarItem::from_cursor_position(
+            bounds,
+            content_bounds,
+            scrollbar_bounds,
+            scroller_bounds,
+            cursor_position,
+        )
+        .is_some();
 
         let content = {
             let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar {
@@ -310,6 +319,8 @@ where
             content_layout.bounds(),
             is_mouse_over,
             is_mouse_over_scrollbar,
+            scrollbar_bounds,
+            scroller_bounds,
             offset,
             content,
         )
@@ -392,21 +403,41 @@ impl State {
         self.offset.min(hidden_content as f32) as u32
     }
 
-    /// Returns whether the scrollbar is currently grabbed or not.
-    pub fn currently_grabbed(&self) -> bool {
+    /// Returns whether the scroller is currently grabbed or not.
+    pub fn is_scroller_grabbed(&self) -> bool {
         self.scroller_grabbed_at.is_some()
     }
 }
 
 #[derive(Debug, Clone, Copy)]
-/// What the mouse is grabbing on the scrollbar
-pub enum ScrollbarGrab {
-    /// The mouse is grabbing the background
+enum ScrollbarItem {
     Background,
-    /// The mouse is grabbing the scroller
     Scroller,
 }
 
+impl ScrollbarItem {
+    /// `None` means the cursor is not over any item
+    fn from_cursor_position(
+        bounds: Rectangle,
+        content_bounds: Rectangle,
+        scrollbar_bounds: Rectangle,
+        scroller_bounds: Rectangle,
+        cursor_position: Point,
+    ) -> Option<ScrollbarItem> {
+        if content_bounds.height > bounds.height
+            && scrollbar_bounds.contains(cursor_position)
+        {
+            Some(if scroller_bounds.contains(cursor_position) {
+                ScrollbarItem::Scroller
+            } else {
+                ScrollbarItem::Background
+            })
+        } else {
+            None
+        }
+    }
+}
+
 /// The renderer of a [`Scrollable`].
 ///
 /// Your [renderer] will need to implement this trait before being
@@ -416,27 +447,16 @@ pub enum ScrollbarGrab {
 /// [renderer]: ../../renderer/index.html
 pub trait Renderer: crate::Renderer + Sized {
     /// Returns the bounds of the scrollbar
-    /// - Background
-    /// - Movable Scroller
-    fn scrollbar_bounds(
-        &self,
-        bounds: Rectangle,
-        content_bounds: Rectangle,
-        offset: u32,
-    ) -> (Rectangle, Rectangle);
+    fn scrollbar_bounds(bounds: Rectangle) -> Rectangle;
 
-    /// Returns what part of the scrollbar is being grabbed by the mouse
-    /// given the bounds of the [`Scrollable`] and its contents.
-    ///
-    /// [`Scrollable`]: struct.Scrollable.html
-    fn scrollbar_grab(
-        &self,
+    /// Returns the bounds of the scroller
+    /// "The part that you can drag around with your mouse to scroll"
+    fn scroller_bounds(
         bounds: Rectangle,
         content_bounds: Rectangle,
-        background_bounds: Rectangle,
-        scroller_bounds: Rectangle,
-        cursor_position: Point,
-    ) -> Option<ScrollbarGrab>;
+        scrollbar_bounds: Rectangle,
+        offset: u32,
+    ) -> Rectangle;
 
     /// Draws the [`Scrollable`].
     ///
@@ -457,6 +477,8 @@ pub trait Renderer: crate::Renderer + Sized {
         content_bounds: Rectangle,
         is_mouse_over: bool,
         is_mouse_over_scrollbar: bool,
+        scrollbar_bounds: Rectangle,
+        scroller_bounds: Rectangle,
         offset: u32,
         content: Self::Output,
     ) -> Self::Output;
-- 
cgit 


From 31e3b6fbcb06bd5e1e5773a7c2febd0cb0092819 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Tue, 3 Dec 2019 06:48:29 +0100
Subject: Unify logic by introducing `scrollable::Scrollbar`

---
 native/src/renderer/null.rs     |  15 +--
 native/src/widget/scrollable.rs | 215 +++++++++++++++++++---------------------
 2 files changed, 108 insertions(+), 122 deletions(-)

(limited to 'native')

diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs
index 2ce150c0..da0e5159 100644
--- a/native/src/renderer/null.rs
+++ b/native/src/renderer/null.rs
@@ -61,17 +61,13 @@ impl text::Renderer for Null {
 }
 
 impl scrollable::Renderer for Null {
-    fn scrollbar_bounds(_bounds: Rectangle) -> Rectangle {
-        Default::default()
-    }
-
-    fn scroller_bounds(
+    fn scrollbar(
+        &self,
         _bounds: Rectangle,
         _content_bounds: Rectangle,
-        _scrollbar_bounds: Rectangle,
         _offset: u32,
-    ) -> Rectangle {
-        Default::default()
+    ) -> Option<scrollable::Scrollbar> {
+        None
     }
 
     fn draw(
@@ -81,8 +77,7 @@ impl scrollable::Renderer for Null {
         _content_bounds: Rectangle,
         _is_mouse_over: bool,
         _is_mouse_over_scrollbar: bool,
-        _scrollbar_bounds: Rectangle,
-        _scroller_bounds: Rectangle,
+        _scrollbar: Option<scrollable::Scrollbar>,
         _offset: u32,
         _content: Self::Output,
     ) {
diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs
index b48ae492..7753c2d2 100644
--- a/native/src/widget/scrollable.rs
+++ b/native/src/widget/scrollable.rs
@@ -102,17 +102,6 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> {
     }
 }
 
-fn scroll_percentage(
-    bounds: Rectangle,
-    scroller_bounds: Rectangle,
-    scroller_grabbed_at: f32,
-    cursor_position: Point,
-) -> f32 {
-    (cursor_position.y + bounds.y
-        - scroller_bounds.height * scroller_grabbed_at)
-        / (bounds.height - scroller_bounds.height)
-}
-
 impl<'a, Message, Renderer> Widget<Message, Renderer>
     for Scrollable<'a, Message, Renderer>
 where
@@ -161,22 +150,6 @@ where
         let content = layout.children().next().unwrap();
         let content_bounds = content.bounds();
 
-        let offset = self.state.offset(bounds, content_bounds);
-        let scrollbar_bounds = Renderer::scrollbar_bounds(bounds);
-        let scroller_bounds = Renderer::scroller_bounds(
-            bounds,
-            content_bounds,
-            scrollbar_bounds,
-            offset,
-        );
-        let scrollbar_grab = ScrollbarItem::from_cursor_position(
-            bounds,
-            content_bounds,
-            scrollbar_bounds,
-            scroller_bounds,
-            cursor_position,
-        );
-
         // TODO: Event capture. Nested scrollables should capture scroll events.
         if is_mouse_over {
             match event {
@@ -195,48 +168,27 @@ where
             }
         }
 
-        if self.state.is_scroller_grabbed() || scrollbar_grab.is_some() {
+        let offset = self.state.offset(bounds, content_bounds);
+        let scrollbar = renderer.scrollbar(bounds, content_bounds, offset);
+        let is_mouse_over_scrollbar = scrollbar
+            .as_ref()
+            .map(|scrollbar| scrollbar.is_mouse_over(cursor_position))
+            .unwrap_or(false);
+
+        if self.state.is_scroller_grabbed() {
             match event {
                 Event::Mouse(mouse::Event::Input {
                     button: mouse::Button::Left,
-                    state,
-                }) => match state {
-                    ButtonState::Pressed => {
-                        let scroller_grabbed_at = match scrollbar_grab.unwrap()
-                        {
-                            ScrollbarItem::Background => 0.5,
-                            ScrollbarItem::Scroller => {
-                                (cursor_position.y - scroller_bounds.y)
-                                    / scroller_bounds.height
-                            }
-                        };
-
-                        self.state.scroll_to(
-                            scroll_percentage(
-                                bounds,
-                                scroller_bounds,
-                                scroller_grabbed_at,
-                                cursor_position,
-                            ),
-                            bounds,
-                            content_bounds,
-                        );
-
-                        self.state.scroller_grabbed_at =
-                            Some(scroller_grabbed_at);
-                    }
-                    ButtonState::Released => {
-                        self.state.scroller_grabbed_at = None;
-                    }
-                },
+                    state: ButtonState::Released,
+                }) => {
+                    self.state.scroller_grabbed_at = None;
+                }
                 Event::Mouse(mouse::Event::CursorMoved { .. }) => {
-                    if let Some(scroller_grabbed_at) =
-                        self.state.scroller_grabbed_at
+                    if let (Some(scrollbar), Some(scroller_grabbed_at)) =
+                        (scrollbar, self.state.scroller_grabbed_at)
                     {
                         self.state.scroll_to(
-                            scroll_percentage(
-                                bounds,
-                                scroller_bounds,
+                            scrollbar.scroll_percentage(
                                 scroller_grabbed_at,
                                 cursor_position,
                             ),
@@ -247,11 +199,35 @@ where
                 }
                 _ => {}
             }
+        } else if is_mouse_over_scrollbar {
+            match event {
+                Event::Mouse(mouse::Event::Input {
+                    button: mouse::Button::Left,
+                    state: ButtonState::Pressed,
+                }) => {
+                    if let Some(scrollbar) = scrollbar {
+                        if let Some(scroller_grabbed_at) =
+                            scrollbar.grab_scroller(cursor_position)
+                        {
+                            self.state.scroll_to(
+                                scrollbar.scroll_percentage(
+                                    scroller_grabbed_at,
+                                    cursor_position,
+                                ),
+                                bounds,
+                                content_bounds,
+                            );
+
+                            self.state.scroller_grabbed_at =
+                                Some(scroller_grabbed_at);
+                        }
+                    }
+                }
+                _ => {}
+            }
         }
 
-        let cursor_position = if is_mouse_over
-            && !(scrollbar_grab.is_some() || self.state.is_scroller_grabbed())
-        {
+        let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar {
             Point::new(
                 cursor_position.x,
                 cursor_position.y
@@ -284,23 +260,13 @@ where
         let content_layout = layout.children().next().unwrap();
         let content_bounds = content_layout.bounds();
         let offset = self.state.offset(bounds, content_bounds);
+        let scrollbar = renderer.scrollbar(bounds, content_bounds, offset);
 
         let is_mouse_over = bounds.contains(cursor_position);
-        let scrollbar_bounds = Renderer::scrollbar_bounds(bounds);
-        let scroller_bounds = Renderer::scroller_bounds(
-            bounds,
-            content_bounds,
-            scrollbar_bounds,
-            offset,
-        );
-        let is_mouse_over_scrollbar = ScrollbarItem::from_cursor_position(
-            bounds,
-            content_bounds,
-            scrollbar_bounds,
-            scroller_bounds,
-            cursor_position,
-        )
-        .is_some();
+        let is_mouse_over_scrollbar = scrollbar
+            .as_ref()
+            .map(|scrollbar| scrollbar.is_mouse_over(cursor_position))
+            .unwrap_or(false);
 
         let content = {
             let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar {
@@ -319,8 +285,7 @@ where
             content_layout.bounds(),
             is_mouse_over,
             is_mouse_over_scrollbar,
-            scrollbar_bounds,
-            scroller_bounds,
+            scrollbar,
             offset,
             content,
         )
@@ -409,33 +374,60 @@ impl State {
     }
 }
 
-#[derive(Debug, Clone, Copy)]
-enum ScrollbarItem {
-    Background,
-    Scroller,
+/// The scrollbar of a [`Scrollable`].
+///
+/// [`Scrollable`]: struct.Scrollable.html
+#[derive(Debug)]
+pub struct Scrollbar {
+    /// The bounds of the [`Scrollbar`].
+    ///
+    /// [`Scrollbar`]: struct.Scrollbar.html
+    pub bounds: Rectangle,
+
+    /// The bounds of the [`Scroller`].
+    ///
+    /// [`Scroller`]: struct.Scroller.html
+    pub scroller: Scroller,
 }
 
-impl ScrollbarItem {
-    /// `None` means the cursor is not over any item
-    fn from_cursor_position(
-        bounds: Rectangle,
-        content_bounds: Rectangle,
-        scrollbar_bounds: Rectangle,
-        scroller_bounds: Rectangle,
-        cursor_position: Point,
-    ) -> Option<ScrollbarItem> {
-        if content_bounds.height > bounds.height
-            && scrollbar_bounds.contains(cursor_position)
-        {
-            Some(if scroller_bounds.contains(cursor_position) {
-                ScrollbarItem::Scroller
+impl Scrollbar {
+    fn is_mouse_over(&self, cursor_position: Point) -> bool {
+        self.bounds.contains(cursor_position)
+    }
+
+    fn grab_scroller(&self, cursor_position: Point) -> Option<f32> {
+        if self.bounds.contains(cursor_position) {
+            Some(if self.scroller.bounds.contains(cursor_position) {
+                (cursor_position.y - self.scroller.bounds.y)
+                    / self.scroller.bounds.height
             } else {
-                ScrollbarItem::Background
+                0.5
             })
         } else {
             None
         }
     }
+
+    fn scroll_percentage(
+        &self,
+        grabbed_at: f32,
+        cursor_position: Point,
+    ) -> f32 {
+        (cursor_position.y + self.bounds.y
+            - self.scroller.bounds.height * grabbed_at)
+            / (self.bounds.height - self.scroller.bounds.height)
+    }
+}
+
+/// The handle of a [`Scrollbar`].
+///
+/// [`Scrollbar`]: struct.Scrollbar.html
+#[derive(Debug, Clone, Copy)]
+pub struct Scroller {
+    /// The bounds of the [`Scroller`].
+    ///
+    /// [`Scroller`]: struct.Scrollbar.html
+    pub bounds: Rectangle,
 }
 
 /// The renderer of a [`Scrollable`].
@@ -446,17 +438,17 @@ impl ScrollbarItem {
 /// [`Scrollable`]: struct.Scrollable.html
 /// [renderer]: ../../renderer/index.html
 pub trait Renderer: crate::Renderer + Sized {
-    /// Returns the bounds of the scrollbar
-    fn scrollbar_bounds(bounds: Rectangle) -> Rectangle;
-
-    /// Returns the bounds of the scroller
-    /// "The part that you can drag around with your mouse to scroll"
-    fn scroller_bounds(
+    /// Returns the [`Scrollbar`] given the bounds and content bounds of a
+    /// [`Scrollable`].
+    ///
+    /// [`Scrollbar`]: struct.Scrollbar.html
+    /// [`Scrollable`]: struct.Scrollable.html
+    fn scrollbar(
+        &self,
         bounds: Rectangle,
         content_bounds: Rectangle,
-        scrollbar_bounds: Rectangle,
         offset: u32,
-    ) -> Rectangle;
+    ) -> Option<Scrollbar>;
 
     /// Draws the [`Scrollable`].
     ///
@@ -477,8 +469,7 @@ pub trait Renderer: crate::Renderer + Sized {
         content_bounds: Rectangle,
         is_mouse_over: bool,
         is_mouse_over_scrollbar: bool,
-        scrollbar_bounds: Rectangle,
-        scroller_bounds: Rectangle,
+        scrollbar: Option<Scrollbar>,
         offset: u32,
         content: Self::Output,
     ) -> Self::Output;
-- 
cgit 


From 2cd517c09998f2dd4c8b15ba3b014d9904f957fd Mon Sep 17 00:00:00 2001
From: Friz64 <friz64mc@gmail.com>
Date: Tue, 3 Dec 2019 15:19:59 +0100
Subject: Correct documentation oversight

---
 native/src/widget/scrollable.rs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

(limited to 'native')

diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs
index 7753c2d2..3c2625b7 100644
--- a/native/src/widget/scrollable.rs
+++ b/native/src/widget/scrollable.rs
@@ -454,12 +454,15 @@ pub trait Renderer: crate::Renderer + Sized {
     ///
     /// It receives:
     /// - the [`State`] of the [`Scrollable`]
-    /// - the bounds of the [`Scrollable`]
+    /// - the bounds of the [`Scrollable`] widget
+    /// - the bounds of the [`Scrollable`] content
     /// - whether the mouse is over the [`Scrollable`] or not
-    /// - whether the mouse is over the scrollbar or not
+    /// - whether the mouse is over the [`Scrollbar`] or not
+    /// - a optional [`Scrollbar`] to be rendered
     /// - the scrolling offset
     /// - the drawn content
     ///
+    /// [`Scrollbar`]: struct.Scrollbar.html
     /// [`Scrollable`]: struct.Scrollable.html
     /// [`State`]: struct.State.html
     fn draw(
-- 
cgit