From dc51080328caa12d2b1fc02febc72cab70bb9f50 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 29 Apr 2020 04:25:49 +0200
Subject: Introduce `Cursor` type in `canvas`

---
 wgpu/src/widget/canvas.rs         | 38 +++++++++++++----------------
 wgpu/src/widget/canvas/cursor.rs  | 50 +++++++++++++++++++++++++++++++++++++++
 wgpu/src/widget/canvas/program.rs | 32 ++++++++++++++++---------
 3 files changed, 88 insertions(+), 32 deletions(-)
 create mode 100644 wgpu/src/widget/canvas/cursor.rs

(limited to 'wgpu')

diff --git a/wgpu/src/widget/canvas.rs b/wgpu/src/widget/canvas.rs
index 0006ca8d..a5834330 100644
--- a/wgpu/src/widget/canvas.rs
+++ b/wgpu/src/widget/canvas.rs
@@ -9,8 +9,8 @@
 use crate::{Defaults, Primitive, Renderer};
 
 use iced_native::{
-    input::mouse, layout, Clipboard, Element, Hasher, Layout, Length,
-    MouseCursor, Point, Size, Vector, Widget,
+    layout, Clipboard, Element, Hasher, Layout, Length, MouseCursor, Point,
+    Size, Vector, Widget,
 };
 use std::hash::Hash;
 use std::marker::PhantomData;
@@ -18,6 +18,7 @@ use std::marker::PhantomData;
 pub mod path;
 
 mod cache;
+mod cursor;
 mod event;
 mod fill;
 mod frame;
@@ -27,6 +28,7 @@ mod stroke;
 mod text;
 
 pub use cache::Cache;
+pub use cursor::Cursor;
 pub use event::Event;
 pub use fill::Fill;
 pub use frame::Frame;
@@ -59,10 +61,10 @@ pub use text::Text;
 /// ```no_run
 /// # mod iced {
 /// #     pub use iced_wgpu::canvas;
-/// #     pub use iced_native::{Color, Size};
+/// #     pub use iced_native::{Color, Rectangle};
 /// # }
-/// use iced::canvas::{self, Cache, Canvas, Fill, Frame, Geometry, Path, Program};
-/// use iced::{Color, Size};
+/// use iced::canvas::{self, Canvas, Cursor, Fill, Frame, Geometry, Path, Program};
+/// use iced::{Color, Rectangle};
 ///
 /// // First, we define the data we need for drawing
 /// #[derive(Debug)]
@@ -72,9 +74,9 @@ pub use text::Text;
 ///
 /// // Then, we implement the `Program` trait
 /// impl Program<()> for Circle {
-///     fn draw(&self, bounds: Size) -> Vec<Geometry>{
+///     fn draw(&self, bounds: Rectangle, _cursor: Cursor) -> Vec<Geometry>{
 ///         // We prepare a new `Frame`
-///         let mut frame = Frame::new(bounds);
+///         let mut frame = Frame::new(bounds.size());
 ///
 ///         // We create a `Path` representing a simple circle
 ///         let circle = Path::circle(frame.center(), self.radius);
@@ -165,22 +167,16 @@ impl<Message, P: Program<Message>> Widget<Message, Renderer>
 
         let canvas_event = match event {
             iced_native::Event::Mouse(mouse_event) => {
-                Some(Event::Mouse(match mouse_event {
-                    mouse::Event::CursorMoved { .. } => {
-                        mouse::Event::CursorMoved {
-                            x: cursor_position.x - bounds.x,
-                            y: cursor_position.y - bounds.y,
-                        }
-                    }
-                    _ => mouse_event,
-                }))
+                Some(Event::Mouse(mouse_event))
             }
             _ => None,
         };
 
+        let cursor = Cursor::from_window_position(cursor_position);
+
         if let Some(canvas_event) = canvas_event {
             if let Some(message) =
-                self.program.update(canvas_event, bounds.size())
+                self.program.update(canvas_event, bounds, cursor)
             {
                 messages.push(message);
             }
@@ -192,11 +188,11 @@ impl<Message, P: Program<Message>> Widget<Message, Renderer>
         _renderer: &mut Renderer,
         _defaults: &Defaults,
         layout: Layout<'_>,
-        _cursor_position: Point,
+        cursor_position: Point,
     ) -> (Primitive, MouseCursor) {
         let bounds = layout.bounds();
         let translation = Vector::new(bounds.x, bounds.y);
-        let size = Size::new(bounds.width, bounds.height);
+        let cursor = Cursor::from_window_position(cursor_position);
 
         (
             Primitive::Translate {
@@ -204,13 +200,13 @@ impl<Message, P: Program<Message>> Widget<Message, Renderer>
                 content: Box::new(Primitive::Group {
                     primitives: self
                         .program
-                        .draw(size)
+                        .draw(bounds, cursor)
                         .into_iter()
                         .map(Geometry::into_primitive)
                         .collect(),
                 }),
             },
-            self.program.mouse_cursor(size),
+            self.program.mouse_cursor(bounds, cursor),
         )
     }
 
diff --git a/wgpu/src/widget/canvas/cursor.rs b/wgpu/src/widget/canvas/cursor.rs
new file mode 100644
index 00000000..a559782a
--- /dev/null
+++ b/wgpu/src/widget/canvas/cursor.rs
@@ -0,0 +1,50 @@
+use iced_native::{Point, Rectangle};
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum Cursor {
+    Available(Point),
+    Unavailable,
+}
+
+impl Cursor {
+    // TODO: Remove this once this type is used in `iced_native` to encode
+    // proper cursor availability
+    pub(crate) fn from_window_position(position: Point) -> Self {
+        if position.x < 0.0 || position.y < 0.0 {
+            Cursor::Unavailable
+        } else {
+            Cursor::Available(position)
+        }
+    }
+
+    pub fn position(&self) -> Option<Point> {
+        match self {
+            Cursor::Available(position) => Some(*position),
+            Cursor::Unavailable => None,
+        }
+    }
+
+    pub fn relative_position(&self, bounds: &Rectangle) -> Option<Point> {
+        match self {
+            Cursor::Available(position) => {
+                Some(Point::new(position.x - bounds.x, position.y - bounds.y))
+            }
+            _ => None,
+        }
+    }
+
+    pub fn internal_position(&self, bounds: &Rectangle) -> Option<Point> {
+        if self.is_over(bounds) {
+            self.relative_position(bounds)
+        } else {
+            None
+        }
+    }
+
+    pub fn is_over(&self, bounds: &Rectangle) -> bool {
+        match self {
+            Cursor::Available(position) => bounds.contains(*position),
+            Cursor::Unavailable => false,
+        }
+    }
+}
diff --git a/wgpu/src/widget/canvas/program.rs b/wgpu/src/widget/canvas/program.rs
index 9e4aca89..f8e54514 100644
--- a/wgpu/src/widget/canvas/program.rs
+++ b/wgpu/src/widget/canvas/program.rs
@@ -1,14 +1,19 @@
-use crate::canvas::{Event, Geometry, Size};
-use iced_native::MouseCursor;
+use crate::canvas::{Cursor, Event, Geometry};
+use iced_native::{MouseCursor, Rectangle};
 
 pub trait Program<Message> {
-    fn update(&mut self, _event: Event, _bounds: Size) -> Option<Message> {
+    fn update(
+        &mut self,
+        _event: Event,
+        _bounds: Rectangle,
+        _cursor: Cursor,
+    ) -> Option<Message> {
         None
     }
 
-    fn draw(&self, bounds: Size) -> Vec<Geometry>;
+    fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Vec<Geometry>;
 
-    fn mouse_cursor(&self, _bounds: Size) -> MouseCursor {
+    fn mouse_cursor(&self, _bounds: Rectangle, _cursor: Cursor) -> MouseCursor {
         MouseCursor::default()
     }
 }
@@ -17,15 +22,20 @@ impl<T, Message> Program<Message> for &mut T
 where
     T: Program<Message>,
 {
-    fn update(&mut self, event: Event, bounds: Size) -> Option<Message> {
-        T::update(self, event, bounds)
+    fn update(
+        &mut self,
+        event: Event,
+        bounds: Rectangle,
+        cursor: Cursor,
+    ) -> Option<Message> {
+        T::update(self, event, bounds, cursor)
     }
 
-    fn draw(&self, bounds: Size) -> Vec<Geometry> {
-        T::draw(self, bounds)
+    fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Vec<Geometry> {
+        T::draw(self, bounds, cursor)
     }
 
-    fn mouse_cursor(&self, bounds: Size) -> MouseCursor {
-        T::mouse_cursor(self, bounds)
+    fn mouse_cursor(&self, bounds: Rectangle, cursor: Cursor) -> MouseCursor {
+        T::mouse_cursor(self, bounds, cursor)
     }
 }
-- 
cgit