diff options
Diffstat (limited to 'wgpu/src/widget/canvas')
-rw-r--r-- | wgpu/src/widget/canvas/cache.rs | 96 | ||||
-rw-r--r-- | wgpu/src/widget/canvas/cursor.rs | 50 | ||||
-rw-r--r-- | wgpu/src/widget/canvas/drawable.rs | 12 | ||||
-rw-r--r-- | wgpu/src/widget/canvas/event.rs | 6 | ||||
-rw-r--r-- | wgpu/src/widget/canvas/frame.rs | 29 | ||||
-rw-r--r-- | wgpu/src/widget/canvas/geometry.rs | 20 | ||||
-rw-r--r-- | wgpu/src/widget/canvas/layer.rs | 25 | ||||
-rw-r--r-- | wgpu/src/widget/canvas/layer/cache.rs | 128 | ||||
-rw-r--r-- | wgpu/src/widget/canvas/program.rs | 41 | ||||
-rw-r--r-- | wgpu/src/widget/canvas/stroke.rs | 18 |
10 files changed, 245 insertions, 180 deletions
diff --git a/wgpu/src/widget/canvas/cache.rs b/wgpu/src/widget/canvas/cache.rs new file mode 100644 index 00000000..03643f74 --- /dev/null +++ b/wgpu/src/widget/canvas/cache.rs @@ -0,0 +1,96 @@ +use crate::{ + canvas::{Frame, Geometry}, + Primitive, +}; + +use iced_native::Size; +use std::{cell::RefCell, sync::Arc}; + +enum State { + Empty, + Filled { + bounds: Size, + primitive: Arc<Primitive>, + }, +} + +impl Default for State { + fn default() -> Self { + State::Empty + } +} +/// A simple cache that stores generated geometry to avoid recomputation. +/// +/// A [`Cache`] will not redraw its geometry unless the dimensions of its layer +/// change or it is explicitly cleared. +/// +/// [`Layer`]: ../trait.Layer.html +/// [`Cache`]: struct.Cache.html +#[derive(Debug, Default)] +pub struct Cache { + state: RefCell<State>, +} + +impl Cache { + /// Creates a new empty [`Cache`]. + /// + /// [`Cache`]: struct.Cache.html + pub fn new() -> Self { + Cache { + state: Default::default(), + } + } + + /// Clears the cache, forcing a redraw the next time it is used. + /// + /// [`Cached`]: struct.Cached.html + pub fn clear(&mut self) { + *self.state.borrow_mut() = State::Empty; + } + + pub fn draw( + &self, + new_bounds: Size, + draw_fn: impl Fn(&mut Frame), + ) -> Geometry { + use std::ops::Deref; + + if let State::Filled { bounds, primitive } = self.state.borrow().deref() + { + if *bounds == new_bounds { + return Geometry::from_primitive(Primitive::Cached { + cache: primitive.clone(), + }); + } + } + + let mut frame = Frame::new(new_bounds); + draw_fn(&mut frame); + + let primitive = { + let geometry = frame.into_geometry(); + + Arc::new(geometry.into_primitive()) + }; + + *self.state.borrow_mut() = State::Filled { + bounds: new_bounds, + primitive: primitive.clone(), + }; + + Geometry::from_primitive(Primitive::Cached { cache: primitive }) + } +} + +impl std::fmt::Debug for State { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + State::Empty => write!(f, "Empty"), + State::Filled { primitive, bounds } => f + .debug_struct("Filled") + .field("primitive", primitive) + .field("bounds", bounds) + .finish(), + } + } +} 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/drawable.rs b/wgpu/src/widget/canvas/drawable.rs deleted file mode 100644 index 6c74071c..00000000 --- a/wgpu/src/widget/canvas/drawable.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::canvas::Frame; - -/// A type that can be drawn on a [`Frame`]. -/// -/// [`Frame`]: struct.Frame.html -pub trait Drawable { - /// Draws the [`Drawable`] on the given [`Frame`]. - /// - /// [`Drawable`]: trait.Drawable.html - /// [`Frame`]: struct.Frame.html - fn draw(&self, frame: &mut Frame); -} diff --git a/wgpu/src/widget/canvas/event.rs b/wgpu/src/widget/canvas/event.rs new file mode 100644 index 00000000..7a8b0829 --- /dev/null +++ b/wgpu/src/widget/canvas/event.rs @@ -0,0 +1,6 @@ +use iced_native::input::mouse; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Event { + Mouse(mouse::Event), +} diff --git a/wgpu/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs index de4717f1..1c4a038a 100644 --- a/wgpu/src/widget/canvas/frame.rs +++ b/wgpu/src/widget/canvas/frame.rs @@ -1,7 +1,7 @@ use iced_native::{Point, Rectangle, Size, Vector}; use crate::{ - canvas::{Fill, Path, Stroke, Text}, + canvas::{Fill, Geometry, Path, Stroke, Text}, triangle, Primitive, }; @@ -10,8 +10,7 @@ use crate::{ /// [`Canvas`]: struct.Canvas.html #[derive(Debug)] pub struct Frame { - width: f32, - height: f32, + size: Size, buffers: lyon::tessellation::VertexBuffers<triangle::Vertex2D, u32>, primitives: Vec<Primitive>, transforms: Transforms, @@ -36,10 +35,9 @@ impl Frame { /// top-left corner of its bounds. /// /// [`Frame`]: struct.Frame.html - pub fn new(width: f32, height: f32) -> Frame { + pub fn new(size: Size) -> Frame { Frame { - width, - height, + size, buffers: lyon::tessellation::VertexBuffers::new(), primitives: Vec::new(), transforms: Transforms { @@ -57,7 +55,7 @@ impl Frame { /// [`Frame`]: struct.Frame.html #[inline] pub fn width(&self) -> f32 { - self.width + self.size.width } /// Returns the width of the [`Frame`]. @@ -65,7 +63,7 @@ impl Frame { /// [`Frame`]: struct.Frame.html #[inline] pub fn height(&self) -> f32 { - self.height + self.size.height } /// Returns the dimensions of the [`Frame`]. @@ -73,7 +71,7 @@ impl Frame { /// [`Frame`]: struct.Frame.html #[inline] pub fn size(&self) -> Size { - Size::new(self.width, self.height) + self.size } /// Returns the coordinate of the center of the [`Frame`]. @@ -81,7 +79,7 @@ impl Frame { /// [`Frame`]: struct.Frame.html #[inline] pub fn center(&self) -> Point { - Point::new(self.width / 2.0, self.height / 2.0) + Point::new(self.size.width / 2.0, self.size.height / 2.0) } /// Draws the given [`Path`] on the [`Frame`] by filling it with the @@ -262,13 +260,14 @@ impl Frame { self.transforms.current.is_identity = false; } - /// Produces the primitive representing everything drawn on the [`Frame`]. + /// Produces the [`Geometry`] representing everything drawn on the [`Frame`]. /// /// [`Frame`]: struct.Frame.html - pub fn into_primitive(mut self) -> Primitive { + /// [`Geometry`]: struct.Geometry.html + pub fn into_geometry(mut self) -> Geometry { if !self.buffers.indices.is_empty() { self.primitives.push(Primitive::Mesh2D { - origin: Point::ORIGIN, + size: self.size, buffers: triangle::Mesh2D { vertices: self.buffers.vertices, indices: self.buffers.indices, @@ -276,9 +275,9 @@ impl Frame { }); } - Primitive::Group { + Geometry::from_primitive(Primitive::Group { primitives: self.primitives, - } + }) } } diff --git a/wgpu/src/widget/canvas/geometry.rs b/wgpu/src/widget/canvas/geometry.rs new file mode 100644 index 00000000..12ef828f --- /dev/null +++ b/wgpu/src/widget/canvas/geometry.rs @@ -0,0 +1,20 @@ +use crate::Primitive; + +#[derive(Debug, Clone)] +pub struct Geometry(Primitive); + +impl Geometry { + pub(crate) fn from_primitive(primitive: Primitive) -> Self { + Self(primitive) + } + + pub fn into_primitive(self) -> Primitive { + self.0 + } +} + +impl From<Geometry> for Primitive { + fn from(geometry: Geometry) -> Primitive { + geometry.0 + } +} diff --git a/wgpu/src/widget/canvas/layer.rs b/wgpu/src/widget/canvas/layer.rs deleted file mode 100644 index a46b7fb1..00000000 --- a/wgpu/src/widget/canvas/layer.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! Produce, store, and reuse geometry. -mod cache; - -pub use cache::Cache; - -use crate::Primitive; -use iced_native::Size; - -use std::sync::Arc; - -/// A layer that can be presented at a [`Canvas`]. -/// -/// [`Canvas`]: ../struct.Canvas.html -pub trait Layer: std::fmt::Debug { - /// Draws the [`Layer`] in the given bounds and produces a [`Primitive`] as - /// a result. - /// - /// The [`Layer`] may choose to store the produced [`Primitive`] locally and - /// only recompute it when the bounds change, its contents change, or is - /// otherwise explicitly cleared by other means. - /// - /// [`Layer`]: trait.Layer.html - /// [`Primitive`]: ../../../enum.Primitive.html - fn draw(&self, bounds: Size) -> Arc<Primitive>; -} diff --git a/wgpu/src/widget/canvas/layer/cache.rs b/wgpu/src/widget/canvas/layer/cache.rs deleted file mode 100644 index 4f8c2bec..00000000 --- a/wgpu/src/widget/canvas/layer/cache.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::{ - canvas::{Drawable, Frame, Layer}, - Primitive, -}; - -use iced_native::Size; -use std::{cell::RefCell, marker::PhantomData, sync::Arc}; - -enum State { - Empty, - Filled { - bounds: Size, - primitive: Arc<Primitive>, - }, -} - -impl Default for State { - fn default() -> Self { - State::Empty - } -} -/// A simple cache that stores generated geometry to avoid recomputation. -/// -/// A [`Cache`] will not redraw its geometry unless the dimensions of its layer -/// change or it is explicitly cleared. -/// -/// [`Layer`]: ../trait.Layer.html -/// [`Cache`]: struct.Cache.html -#[derive(Debug)] -pub struct Cache<T: Drawable> { - input: PhantomData<T>, - state: RefCell<State>, -} - -impl<T> Default for Cache<T> -where - T: Drawable, -{ - fn default() -> Self { - Self { - input: PhantomData, - state: Default::default(), - } - } -} - -impl<T> Cache<T> -where - T: Drawable + std::fmt::Debug, -{ - /// Creates a new empty [`Cache`]. - /// - /// [`Cache`]: struct.Cache.html - pub fn new() -> Self { - Cache { - input: PhantomData, - state: Default::default(), - } - } - - /// Clears the cache, forcing a redraw the next time it is used. - /// - /// [`Cached`]: struct.Cached.html - pub fn clear(&mut self) { - *self.state.borrow_mut() = State::Empty; - } - - /// Binds the [`Cache`] with some data, producing a [`Layer`] that can be - /// added to a [`Canvas`]. - /// - /// [`Cache`]: struct.Cache.html - /// [`Layer`]: ../trait.Layer.html - /// [`Canvas`]: ../../struct.Canvas.html - pub fn with<'a>(&'a self, input: &'a T) -> impl Layer + 'a { - Bind { - cache: self, - input: input, - } - } -} - -#[derive(Debug)] -struct Bind<'a, T: Drawable> { - cache: &'a Cache<T>, - input: &'a T, -} - -impl<'a, T> Layer for Bind<'a, T> -where - T: Drawable + std::fmt::Debug, -{ - fn draw(&self, current_bounds: Size) -> Arc<Primitive> { - use std::ops::Deref; - - if let State::Filled { bounds, primitive } = - self.cache.state.borrow().deref() - { - if *bounds == current_bounds { - return primitive.clone(); - } - } - - let mut frame = Frame::new(current_bounds.width, current_bounds.height); - self.input.draw(&mut frame); - - let primitive = Arc::new(frame.into_primitive()); - - *self.cache.state.borrow_mut() = State::Filled { - bounds: current_bounds, - primitive: primitive.clone(), - }; - - primitive - } -} - -impl std::fmt::Debug for State { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - State::Empty => write!(f, "Empty"), - State::Filled { primitive, bounds } => f - .debug_struct("Filled") - .field("primitive", primitive) - .field("bounds", bounds) - .finish(), - } - } -} diff --git a/wgpu/src/widget/canvas/program.rs b/wgpu/src/widget/canvas/program.rs new file mode 100644 index 00000000..f8e54514 --- /dev/null +++ b/wgpu/src/widget/canvas/program.rs @@ -0,0 +1,41 @@ +use crate::canvas::{Cursor, Event, Geometry}; +use iced_native::{MouseCursor, Rectangle}; + +pub trait Program<Message> { + fn update( + &mut self, + _event: Event, + _bounds: Rectangle, + _cursor: Cursor, + ) -> Option<Message> { + None + } + + fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Vec<Geometry>; + + fn mouse_cursor(&self, _bounds: Rectangle, _cursor: Cursor) -> MouseCursor { + MouseCursor::default() + } +} + +impl<T, Message> Program<Message> for &mut T +where + T: Program<Message>, +{ + fn update( + &mut self, + event: Event, + bounds: Rectangle, + cursor: Cursor, + ) -> Option<Message> { + T::update(self, event, bounds, cursor) + } + + fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Vec<Geometry> { + T::draw(self, bounds, cursor) + } + + fn mouse_cursor(&self, bounds: Rectangle, cursor: Cursor) -> MouseCursor { + T::mouse_cursor(self, bounds, cursor) + } +} diff --git a/wgpu/src/widget/canvas/stroke.rs b/wgpu/src/widget/canvas/stroke.rs index 46d669c4..e20379cd 100644 --- a/wgpu/src/widget/canvas/stroke.rs +++ b/wgpu/src/widget/canvas/stroke.rs @@ -14,6 +14,24 @@ pub struct Stroke { pub line_join: LineJoin, } +impl Stroke { + pub fn with_color(self, color: Color) -> Stroke { + Stroke { color, ..self } + } + + pub fn with_width(self, width: f32) -> Stroke { + Stroke { width, ..self } + } + + pub fn with_line_cap(self, line_cap: LineCap) -> Stroke { + Stroke { line_cap, ..self } + } + + pub fn with_line_join(self, line_join: LineJoin) -> Stroke { + Stroke { line_join, ..self } + } +} + impl Default for Stroke { fn default() -> Stroke { Stroke { |