diff options
Diffstat (limited to '')
| -rw-r--r-- | core/src/gradient.rs (renamed from graphics/src/gradient.rs) | 0 | ||||
| -rw-r--r-- | core/src/gradient/linear.rs (renamed from graphics/src/gradient/linear.rs) | 0 | ||||
| -rw-r--r-- | graphics/src/backend.rs | 2 | ||||
| -rw-r--r-- | graphics/src/lib.rs | 13 | ||||
| -rw-r--r-- | graphics/src/primitive.rs | 128 | ||||
| -rw-r--r-- | graphics/src/renderer.rs | 43 | ||||
| -rw-r--r-- | graphics/src/triangle.rs | 32 | ||||
| -rw-r--r-- | native/src/widget/canvas.rs (renamed from graphics/src/widget/canvas.rs) | 101 | ||||
| -rw-r--r-- | native/src/widget/canvas/cursor.rs (renamed from graphics/src/widget/canvas/cursor.rs) | 2 | ||||
| -rw-r--r-- | native/src/widget/canvas/event.rs (renamed from graphics/src/widget/canvas/event.rs) | 8 | ||||
| -rw-r--r-- | native/src/widget/canvas/fill.rs (renamed from graphics/src/widget/canvas/fill.rs) | 18 | ||||
| -rw-r--r-- | native/src/widget/canvas/path.rs (renamed from graphics/src/widget/canvas/path.rs) | 52 | ||||
| -rw-r--r-- | native/src/widget/canvas/path/arc.rs (renamed from graphics/src/widget/canvas/path/arc.rs) | 2 | ||||
| -rw-r--r-- | native/src/widget/canvas/path/builder.rs (renamed from graphics/src/widget/canvas/path/builder.rs) | 24 | ||||
| -rw-r--r-- | native/src/widget/canvas/program.rs (renamed from graphics/src/widget/canvas/program.rs) | 24 | ||||
| -rw-r--r-- | native/src/widget/canvas/stroke.rs (renamed from graphics/src/widget/canvas/stroke.rs) | 22 | ||||
| -rw-r--r-- | native/src/widget/canvas/style.rs (renamed from graphics/src/widget/canvas/style.rs) | 3 | ||||
| -rw-r--r-- | native/src/widget/canvas/text.rs (renamed from graphics/src/widget/canvas/text.rs) | 0 | ||||
| -rw-r--r-- | renderer/src/widget/qr_code.rs (renamed from graphics/src/widget/qr_code.rs) | 90 | ||||
| -rw-r--r-- | wgpu/src/layer.rs (renamed from graphics/src/layer.rs) | 80 | ||||
| -rw-r--r-- | wgpu/src/layer/image.rs (renamed from graphics/src/layer/image.rs) | 0 | ||||
| -rw-r--r-- | wgpu/src/layer/mesh.rs (renamed from graphics/src/layer/mesh.rs) | 6 | ||||
| -rw-r--r-- | wgpu/src/layer/quad.rs (renamed from graphics/src/layer/quad.rs) | 0 | ||||
| -rw-r--r-- | wgpu/src/layer/text.rs (renamed from graphics/src/layer/text.rs) | 0 | ||||
| -rw-r--r-- | wgpu/src/widget.rs (renamed from graphics/src/widget.rs) | 9 | ||||
| -rw-r--r-- | wgpu/src/widget/canvas/cache.rs (renamed from graphics/src/widget/canvas/cache.rs) | 19 | ||||
| -rw-r--r-- | wgpu/src/widget/canvas/frame.rs (renamed from graphics/src/widget/canvas/frame.rs) | 157 | ||||
| -rw-r--r-- | wgpu/src/widget/canvas/geometry.rs (renamed from graphics/src/widget/canvas/geometry.rs) | 0 | 
28 files changed, 427 insertions, 408 deletions
| diff --git a/graphics/src/gradient.rs b/core/src/gradient.rs index 61e919d6..61e919d6 100644 --- a/graphics/src/gradient.rs +++ b/core/src/gradient.rs diff --git a/graphics/src/gradient/linear.rs b/core/src/gradient/linear.rs index c886db47..c886db47 100644 --- a/graphics/src/gradient/linear.rs +++ b/core/src/gradient/linear.rs diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index 8658cffe..c44372e8 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -10,6 +10,8 @@ use std::borrow::Cow;  ///  /// [`Renderer`]: crate::Renderer  pub trait Backend { +    type Geometry: Into<crate::Primitive>; +      /// Trims the measurements cache.      ///      /// This method is currently necessary to properly trim the text cache in diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index bbbdfa0e..576b2d78 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -9,7 +9,7 @@  )]  #![deny(      missing_debug_implementations, -    missing_docs, +    //missing_docs,      unsafe_code,      unused_results,      clippy::extra_unused_lifetimes, @@ -23,25 +23,19 @@  #![cfg_attr(docsrs, feature(doc_cfg))]  mod antialiasing;  mod error; -mod primitive;  mod transformation;  mod viewport;  pub mod backend; -pub mod gradient;  pub mod image; -pub mod layer;  pub mod overlay; +pub mod primitive;  pub mod renderer; -pub mod triangle; -pub mod widget;  pub mod window;  pub use antialiasing::Antialiasing;  pub use backend::Backend;  pub use error::Error; -pub use gradient::Gradient; -pub use layer::Layer;  pub use primitive::Primitive;  pub use renderer::Renderer;  pub use transformation::Transformation; @@ -50,5 +44,6 @@ pub use viewport::Viewport;  pub use iced_native::alignment;  pub use iced_native::text;  pub use iced_native::{ -    Alignment, Background, Color, Font, Point, Rectangle, Size, Vector, +    Alignment, Background, Color, Font, Gradient, Point, Rectangle, Size, +    Vector,  }; diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 5a163a2f..e4826591 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -1,23 +1,15 @@ +use crate::alignment; +  use iced_native::image;  use iced_native::svg; -use iced_native::{Background, Color, Font, Rectangle, Size, Vector}; - -use crate::alignment; -use crate::gradient::Gradient; -use crate::triangle; +use iced_native::{Background, Color, Font, Gradient, Rectangle, Size, Vector}; +use bytemuck::{Pod, Zeroable};  use std::sync::Arc;  /// A rendering primitive.  #[derive(Debug, Clone)]  pub enum Primitive { -    /// An empty primitive -    None, -    /// A group of primitives -    Group { -        /// The primitives of the group -        primitives: Vec<Primitive>, -    },      /// A text primitive      Text {          /// The contents of the text @@ -66,27 +58,12 @@ pub enum Primitive {          /// The bounds of the viewport          bounds: Rectangle,      }, -    /// A clip primitive -    Clip { -        /// The bounds of the clip -        bounds: Rectangle, -        /// The content of the clip -        content: Box<Primitive>, -    }, -    /// A primitive that applies a translation -    Translate { -        /// The translation vector -        translation: Vector, - -        /// The primitive to translate -        content: Box<Primitive>, -    },      /// A low-level primitive to render a mesh of triangles with a solid color.      ///      /// It can be used to render many kinds of geometry freely.      SolidMesh {          /// The vertices and indices of the mesh. -        buffers: triangle::Mesh2D<triangle::ColoredVertex2D>, +        buffers: Mesh2D<ColoredVertex2D>,          /// The size of the drawable region of the mesh.          /// @@ -98,7 +75,7 @@ pub enum Primitive {      /// It can be used to render many kinds of geometry freely.      GradientMesh {          /// The vertices and indices of the mesh. -        buffers: triangle::Mesh2D<triangle::Vertex2D>, +        buffers: Mesh2D<Vertex2D>,          /// The size of the drawable region of the mesh.          /// @@ -108,18 +85,101 @@ pub enum Primitive {          /// The [`Gradient`] to apply to the mesh.          gradient: Gradient,      }, +    Fill { +        path: tiny_skia::Path, +        paint: tiny_skia::Paint<'static>, +        rule: tiny_skia::FillRule, +        transform: tiny_skia::Transform, +    }, +    Stroke { +        path: tiny_skia::Path, +        paint: tiny_skia::Paint<'static>, +        stroke: tiny_skia::Stroke, +        transform: tiny_skia::Transform, +    }, +    /// A group of primitives +    Group { +        /// The primitives of the group +        primitives: Vec<Primitive>, +    }, +    /// A clip primitive +    Clip { +        /// The bounds of the clip +        bounds: Rectangle, +        /// The content of the clip +        content: Box<Primitive>, +    }, +    /// A primitive that applies a translation +    Translate { +        /// The translation vector +        translation: Vector, + +        /// The primitive to translate +        content: Box<Primitive>, +    },      /// A cached primitive.      ///      /// This can be useful if you are implementing a widget where primitive      /// generation is expensive. -    Cached { +    Cache {          /// The cached primitive -        cache: Arc<Primitive>, +        content: Arc<Primitive>,      },  } -impl Default for Primitive { -    fn default() -> Primitive { -        Primitive::None +impl Primitive { +    pub fn group(primitives: Vec<Self>) -> Self { +        Self::Group { primitives } +    } + +    pub fn clip(self, bounds: Rectangle) -> Self { +        Self::Clip { +            bounds, +            content: Box::new(self), +        } +    } + +    pub fn translate(self, translation: Vector) -> Self { +        Self::Translate { +            translation, +            content: Box::new(self), +        } +    } +} + +/// A set of [`Vertex2D`] and indices representing a list of triangles. +#[derive(Clone, Debug)] +pub struct Mesh2D<T> { +    /// The vertices of the mesh +    pub vertices: Vec<T>, + +    /// The list of vertex indices that defines the triangles of the mesh. +    /// +    /// Therefore, this list should always have a length that is a multiple of 3. +    pub indices: Vec<u32>, +} + +/// A two-dimensional vertex. +#[derive(Copy, Clone, Debug, Zeroable, Pod)] +#[repr(C)] +pub struct Vertex2D { +    /// The vertex position in 2D space. +    pub position: [f32; 2], +} + +/// A two-dimensional vertex with a color. +#[derive(Copy, Clone, Debug, Zeroable, Pod)] +#[repr(C)] +pub struct ColoredVertex2D { +    /// The vertex position in 2D space. +    pub position: [f32; 2], + +    /// The color of the vertex in __linear__ RGBA. +    pub color: [f32; 4], +} + +impl From<()> for Primitive { +    fn from(_: ()) -> Self { +        Self::Group { primitives: vec![] }      }  } diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 859ebc04..793ee7d7 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -1,6 +1,7 @@  //! Create a renderer from a [`Backend`].  use crate::backend::{self, Backend};  use crate::{Primitive, Vector}; +  use iced_native::image;  use iced_native::layout;  use iced_native::renderer; @@ -70,19 +71,13 @@ where      }      fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { -        let current_primitives = std::mem::take(&mut self.primitives); +        let current = std::mem::take(&mut self.primitives);          f(self); -        let layer_primitives = -            std::mem::replace(&mut self.primitives, current_primitives); +        let layer = std::mem::replace(&mut self.primitives, current); -        self.primitives.push(Primitive::Clip { -            bounds, -            content: Box::new(Primitive::Group { -                primitives: layer_primitives, -            }), -        }); +        self.primitives.push(Primitive::group(layer).clip(bounds));      }      fn with_translation( @@ -90,19 +85,14 @@ where          translation: Vector,          f: impl FnOnce(&mut Self),      ) { -        let current_primitives = std::mem::take(&mut self.primitives); +        let current = std::mem::take(&mut self.primitives);          f(self); -        let layer_primitives = -            std::mem::replace(&mut self.primitives, current_primitives); +        let layer = std::mem::replace(&mut self.primitives, current); -        self.primitives.push(Primitive::Translate { -            translation, -            content: Box::new(Primitive::Group { -                primitives: layer_primitives, -            }), -        }); +        self.primitives +            .push(Primitive::group(layer).translate(translation));      }      fn fill_quad( @@ -199,7 +189,7 @@ where      }      fn draw(&mut self, handle: image::Handle, bounds: Rectangle) { -        self.draw_primitive(Primitive::Image { handle, bounds }) +        self.primitives.push(Primitive::Image { handle, bounds })      }  } @@ -217,10 +207,23 @@ where          color: Option<Color>,          bounds: Rectangle,      ) { -        self.draw_primitive(Primitive::Svg { +        self.primitives.push(Primitive::Svg {              handle,              color,              bounds,          })      }  } + +#[cfg(feature = "canvas")] +impl<B, T> iced_native::widget::canvas::Renderer for Renderer<B, T> +where +    B: Backend, +{ +    type Geometry = B::Geometry; + +    fn draw(&mut self, layers: Vec<Self::Geometry>) { +        self.primitives +            .extend(layers.into_iter().map(B::Geometry::into)); +    } +} diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs index f52b2339..09b61767 100644 --- a/graphics/src/triangle.rs +++ b/graphics/src/triangle.rs @@ -1,33 +1 @@  //! Draw geometry using meshes of triangles. -use bytemuck::{Pod, Zeroable}; - -/// A set of [`Vertex2D`] and indices representing a list of triangles. -#[derive(Clone, Debug)] -pub struct Mesh2D<T> { -    /// The vertices of the mesh -    pub vertices: Vec<T>, - -    /// The list of vertex indices that defines the triangles of the mesh. -    /// -    /// Therefore, this list should always have a length that is a multiple of 3. -    pub indices: Vec<u32>, -} - -/// A two-dimensional vertex. -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -#[repr(C)] -pub struct Vertex2D { -    /// The vertex position in 2D space. -    pub position: [f32; 2], -} - -/// A two-dimensional vertex with a color. -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -#[repr(C)] -pub struct ColoredVertex2D { -    /// The vertex position in 2D space. -    pub position: [f32; 2], - -    /// The color of the vertex in __linear__ RGBA. -    pub color: [f32; 4], -} diff --git a/graphics/src/widget/canvas.rs b/native/src/widget/canvas.rs index a8d050f5..8a9addd2 100644 --- a/graphics/src/widget/canvas.rs +++ b/native/src/widget/canvas.rs @@ -8,34 +8,26 @@ pub mod fill;  pub mod path;  pub mod stroke; -mod cache;  mod cursor; -mod frame; -mod geometry;  mod program;  mod style;  mod text;  pub use crate::gradient::{self, Gradient}; -pub use cache::Cache;  pub use cursor::Cursor;  pub use event::Event; -pub use fill::{Fill, FillRule}; -pub use frame::Frame; -pub use geometry::Geometry; +pub use fill::Fill;  pub use path::Path;  pub use program::Program;  pub use stroke::{LineCap, LineDash, LineJoin, Stroke};  pub use style::Style;  pub use text::Text; -use crate::{Backend, Primitive, Renderer}; - -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::widget::tree::{self, Tree}; -use iced_native::{ +use crate::layout::{self, Layout}; +use crate::mouse; +use crate::renderer; +use crate::widget::tree::{self, Tree}; +use crate::{      Clipboard, Element, Length, Point, Rectangle, Shell, Size, Vector, Widget,  }; @@ -85,20 +77,22 @@ use std::marker::PhantomData;  /// let canvas = Canvas::new(Circle { radius: 50.0 });  /// ```  #[derive(Debug)] -pub struct Canvas<Message, Theme, P> +pub struct Canvas<Message, Renderer, P>  where -    P: Program<Message, Theme>, +    Renderer: self::Renderer, +    P: Program<Message, Renderer>,  {      width: Length,      height: Length,      program: P,      message_: PhantomData<Message>, -    theme_: PhantomData<Theme>, +    theme_: PhantomData<Renderer>,  } -impl<Message, Theme, P> Canvas<Message, Theme, P> +impl<Message, Renderer, P> Canvas<Message, Renderer, P>  where -    P: Program<Message, Theme>, +    Renderer: self::Renderer, +    P: Program<Message, Renderer>,  {      const DEFAULT_SIZE: f32 = 100.0; @@ -126,10 +120,11 @@ where      }  } -impl<Message, P, B, T> Widget<Message, Renderer<B, T>> for Canvas<Message, T, P> +impl<Message, Renderer, P> Widget<Message, Renderer> +    for Canvas<Message, Renderer, P>  where -    P: Program<Message, T>, -    B: Backend, +    Renderer: self::Renderer, +    P: Program<Message, Renderer>,  {      fn tag(&self) -> tree::Tag {          struct Tag<T>(T); @@ -150,7 +145,7 @@ where      fn layout(          &self, -        _renderer: &Renderer<B, T>, +        _renderer: &Renderer,          limits: &layout::Limits,      ) -> layout::Node {          let limits = limits.width(self.width).height(self.height); @@ -162,23 +157,19 @@ where      fn on_event(          &mut self,          tree: &mut Tree, -        event: iced_native::Event, +        event: crate::Event,          layout: Layout<'_>,          cursor_position: Point, -        _renderer: &Renderer<B, T>, +        _renderer: &Renderer,          _clipboard: &mut dyn Clipboard,          shell: &mut Shell<'_, Message>,      ) -> event::Status {          let bounds = layout.bounds();          let canvas_event = match event { -            iced_native::Event::Mouse(mouse_event) => { -                Some(Event::Mouse(mouse_event)) -            } -            iced_native::Event::Touch(touch_event) => { -                Some(Event::Touch(touch_event)) -            } -            iced_native::Event::Keyboard(keyboard_event) => { +            crate::Event::Mouse(mouse_event) => Some(Event::Mouse(mouse_event)), +            crate::Event::Touch(touch_event) => Some(Event::Touch(touch_event)), +            crate::Event::Keyboard(keyboard_event) => {                  Some(Event::Keyboard(keyboard_event))              }              _ => None, @@ -208,7 +199,7 @@ where          layout: Layout<'_>,          cursor_position: Point,          _viewport: &Rectangle, -        _renderer: &Renderer<B, T>, +        _renderer: &Renderer,      ) -> mouse::Interaction {          let bounds = layout.bounds();          let cursor = Cursor::from_window_position(cursor_position); @@ -220,49 +211,49 @@ where      fn draw(          &self,          tree: &Tree, -        renderer: &mut Renderer<B, T>, -        theme: &T, +        renderer: &mut Renderer, +        theme: &Renderer::Theme,          _style: &renderer::Style,          layout: Layout<'_>,          cursor_position: Point,          _viewport: &Rectangle,      ) { -        use iced_native::Renderer as _; -          let bounds = layout.bounds();          if bounds.width < 1.0 || bounds.height < 1.0 {              return;          } -        let translation = Vector::new(bounds.x, bounds.y);          let cursor = Cursor::from_window_position(cursor_position);          let state = tree.state.downcast_ref::<P::State>(); -        renderer.with_translation(translation, |renderer| { -            renderer.draw_primitive(Primitive::Group { -                primitives: self -                    .program -                    .draw(state, theme, bounds, cursor) -                    .into_iter() -                    .map(Geometry::into_primitive) -                    .collect(), -            }); -        }); +        renderer.with_translation( +            Vector::new(bounds.x, bounds.y), +            |renderer| { +                renderer.draw( +                    self.program.draw(state, renderer, theme, bounds, cursor), +                ); +            }, +        );      }  } -impl<'a, Message, P, B, T> From<Canvas<Message, T, P>> -    for Element<'a, Message, Renderer<B, T>> +impl<'a, Message, Renderer, P> From<Canvas<Message, Renderer, P>> +    for Element<'a, Message, Renderer>  where      Message: 'a, -    P: Program<Message, T> + 'a, -    B: Backend, -    T: 'a, +    Renderer: 'a + self::Renderer, +    P: Program<Message, Renderer> + 'a,  {      fn from( -        canvas: Canvas<Message, T, P>, -    ) -> Element<'a, Message, Renderer<B, T>> { +        canvas: Canvas<Message, Renderer, P>, +    ) -> Element<'a, Message, Renderer> {          Element::new(canvas)      }  } + +pub trait Renderer: crate::Renderer { +    type Geometry; + +    fn draw(&mut self, geometry: Vec<Self::Geometry>); +} diff --git a/graphics/src/widget/canvas/cursor.rs b/native/src/widget/canvas/cursor.rs index 9588d129..ef6a7771 100644 --- a/graphics/src/widget/canvas/cursor.rs +++ b/native/src/widget/canvas/cursor.rs @@ -1,4 +1,4 @@ -use iced_native::{Point, Rectangle}; +use crate::{Point, Rectangle};  /// The mouse cursor state.  #[derive(Debug, Clone, Copy, PartialEq)] diff --git a/graphics/src/widget/canvas/event.rs b/native/src/widget/canvas/event.rs index 7c733a4d..1d726577 100644 --- a/graphics/src/widget/canvas/event.rs +++ b/native/src/widget/canvas/event.rs @@ -1,9 +1,9 @@  //! Handle events of a canvas. -use iced_native::keyboard; -use iced_native::mouse; -use iced_native::touch; +use crate::keyboard; +use crate::mouse; +use crate::touch; -pub use iced_native::event::Status; +pub use crate::event::Status;  /// A [`Canvas`] event.  /// diff --git a/graphics/src/widget/canvas/fill.rs b/native/src/widget/canvas/fill.rs index e954ebb5..92b1e47e 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/native/src/widget/canvas/fill.rs @@ -1,5 +1,6 @@  //! Fill [crate::widget::canvas::Geometry] with a certain style. -use crate::{Color, Gradient}; +use crate::widget::canvas::Gradient; +use crate::Color;  pub use crate::widget::canvas::Style; @@ -19,14 +20,14 @@ pub struct Fill {      /// By default, it is set to `NonZero`.      ///      /// [1]: https://www.w3.org/TR/SVG/painting.html#FillRuleProperty -    pub rule: FillRule, +    pub rule: Rule,  }  impl Default for Fill {      fn default() -> Self {          Self {              style: Style::Solid(Color::BLACK), -            rule: FillRule::NonZero, +            rule: Rule::NonZero,          }      }  } @@ -57,16 +58,7 @@ impl From<Gradient> for Fill {  /// [1]: https://www.w3.org/TR/SVG/painting.html#FillRuleProperty  #[derive(Debug, Clone, Copy, PartialEq, Eq)]  #[allow(missing_docs)] -pub enum FillRule { +pub enum Rule {      NonZero,      EvenOdd,  } - -impl From<FillRule> for lyon::tessellation::FillRule { -    fn from(rule: FillRule) -> lyon::tessellation::FillRule { -        match rule { -            FillRule::NonZero => lyon::tessellation::FillRule::NonZero, -            FillRule::EvenOdd => lyon::tessellation::FillRule::EvenOdd, -        } -    } -} diff --git a/graphics/src/widget/canvas/path.rs b/native/src/widget/canvas/path.rs index aeb2589e..30c387c5 100644 --- a/graphics/src/widget/canvas/path.rs +++ b/native/src/widget/canvas/path.rs @@ -7,18 +7,16 @@ mod builder;  pub use arc::Arc;  pub use builder::Builder; -use crate::widget::canvas::LineDash; +pub use lyon_path; -use iced_native::{Point, Size}; -use lyon::algorithms::walk::{walk_along_path, RepeatedPattern, WalkerEvent}; -use lyon::path::iterator::PathIterator; +use crate::{Point, Size};  /// An immutable set of points that may or may not be connected.  ///  /// A single [`Path`] can represent different kinds of 2D shapes!  #[derive(Debug, Clone)]  pub struct Path { -    raw: lyon::path::Path, +    raw: lyon_path::Path,  }  impl Path { @@ -56,54 +54,14 @@ impl Path {      }      #[inline] -    pub(crate) fn raw(&self) -> &lyon::path::Path { +    pub fn raw(&self) -> &lyon_path::Path {          &self.raw      }      #[inline] -    pub(crate) fn transformed( -        &self, -        transform: &lyon::math::Transform, -    ) -> Path { +    pub fn transform(&self, transform: &lyon_path::math::Transform) -> Path {          Path {              raw: self.raw.clone().transformed(transform),          }      }  } - -pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path { -    Path::new(|builder| { -        let segments_odd = (line_dash.segments.len() % 2 == 1) -            .then(|| [line_dash.segments, line_dash.segments].concat()); - -        let mut draw_line = false; - -        walk_along_path( -            path.raw().iter().flattened(0.01), -            0.0, -            lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE, -            &mut RepeatedPattern { -                callback: |event: WalkerEvent<'_>| { -                    let point = Point { -                        x: event.position.x, -                        y: event.position.y, -                    }; - -                    if draw_line { -                        builder.line_to(point); -                    } else { -                        builder.move_to(point); -                    } - -                    draw_line = !draw_line; - -                    true -                }, -                index: line_dash.offset, -                intervals: segments_odd -                    .as_deref() -                    .unwrap_or(line_dash.segments), -            }, -        ); -    }) -} diff --git a/graphics/src/widget/canvas/path/arc.rs b/native/src/widget/canvas/path/arc.rs index b8e72daf..e0747d3e 100644 --- a/graphics/src/widget/canvas/path/arc.rs +++ b/native/src/widget/canvas/path/arc.rs @@ -1,5 +1,5 @@  //! Build and draw curves. -use iced_native::{Point, Vector}; +use crate::{Point, Vector};  /// A segment of a differentiable curve.  #[derive(Debug, Clone, Copy)] diff --git a/graphics/src/widget/canvas/path/builder.rs b/native/src/widget/canvas/path/builder.rs index 5121aa68..84fda052 100644 --- a/graphics/src/widget/canvas/path/builder.rs +++ b/native/src/widget/canvas/path/builder.rs @@ -1,35 +1,37 @@  use crate::widget::canvas::path::{arc, Arc, Path}; +use crate::{Point, Size}; -use iced_native::{Point, Size}; -use lyon::path::builder::SvgPathBuilder; +use lyon_path::builder::{self, SvgPathBuilder}; +use lyon_path::geom; +use lyon_path::math;  /// A [`Path`] builder.  ///  /// Once a [`Path`] is built, it can no longer be mutated.  #[allow(missing_debug_implementations)]  pub struct Builder { -    raw: lyon::path::builder::WithSvg<lyon::path::path::BuilderImpl>, +    raw: builder::WithSvg<lyon_path::path::BuilderImpl>,  }  impl Builder {      /// Creates a new [`Builder`].      pub fn new() -> Builder {          Builder { -            raw: lyon::path::Path::builder().with_svg(), +            raw: lyon_path::Path::builder().with_svg(),          }      }      /// Moves the starting point of a new sub-path to the given `Point`.      #[inline]      pub fn move_to(&mut self, point: Point) { -        let _ = self.raw.move_to(lyon::math::Point::new(point.x, point.y)); +        let _ = self.raw.move_to(math::Point::new(point.x, point.y));      }      /// Connects the last point in the [`Path`] to the given `Point` with a      /// straight line.      #[inline]      pub fn line_to(&mut self, point: Point) { -        let _ = self.raw.line_to(lyon::math::Point::new(point.x, point.y)); +        let _ = self.raw.line_to(math::Point::new(point.x, point.y));      }      /// Adds an [`Arc`] to the [`Path`] from `start_angle` to `end_angle` in @@ -53,8 +55,6 @@ impl Builder {      /// See [the HTML5 specification of `arcTo`](https://html.spec.whatwg.org/multipage/canvas.html#building-paths:dom-context-2d-arcto)      /// for more details and examples.      pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) { -        use lyon::{math, path}; -          let start = self.raw.current_position();          let mid = math::Point::new(a.x, a.y);          let end = math::Point::new(b.x, b.y); @@ -92,7 +92,7 @@ impl Builder {          self.raw.arc_to(              math::Vector::new(radius, radius),              math::Angle::radians(0.0), -            path::ArcFlags { +            lyon_path::ArcFlags {                  large_arc: false,                  sweep,              }, @@ -102,8 +102,6 @@ impl Builder {      /// Adds an ellipse to the [`Path`] using a clockwise direction.      pub fn ellipse(&mut self, arc: arc::Elliptical) { -        use lyon::{geom, math}; -          let arc = geom::Arc {              center: math::Point::new(arc.center.x, arc.center.y),              radii: math::Vector::new(arc.radii.x, arc.radii.y), @@ -128,8 +126,6 @@ impl Builder {          control_b: Point,          to: Point,      ) { -        use lyon::math; -          let _ = self.raw.cubic_bezier_to(              math::Point::new(control_a.x, control_a.y),              math::Point::new(control_b.x, control_b.y), @@ -141,8 +137,6 @@ impl Builder {      /// and its end point.      #[inline]      pub fn quadratic_curve_to(&mut self, control: Point, to: Point) { -        use lyon::math; -          let _ = self.raw.quadratic_bezier_to(              math::Point::new(control.x, control.y),              math::Point::new(to.x, to.y), diff --git a/graphics/src/widget/canvas/program.rs b/native/src/widget/canvas/program.rs index 656dbfa6..17a5a137 100644 --- a/graphics/src/widget/canvas/program.rs +++ b/native/src/widget/canvas/program.rs @@ -1,6 +1,6 @@  use crate::widget::canvas::event::{self, Event};  use crate::widget::canvas::mouse; -use crate::widget::canvas::{Cursor, Geometry}; +use crate::widget::canvas::{Cursor, Renderer};  use crate::Rectangle;  /// The state and logic of a [`Canvas`]. @@ -9,7 +9,10 @@ use crate::Rectangle;  /// application.  ///  /// [`Canvas`]: crate::widget::Canvas -pub trait Program<Message, Theme = iced_native::Theme> { +pub trait Program<Message, Renderer> +where +    Renderer: self::Renderer, +{      /// The internal state mutated by the [`Program`].      type State: Default + 'static; @@ -44,10 +47,11 @@ pub trait Program<Message, Theme = iced_native::Theme> {      fn draw(          &self,          state: &Self::State, -        theme: &Theme, +        renderer: &Renderer, +        theme: &Renderer::Theme,          bounds: Rectangle,          cursor: Cursor, -    ) -> Vec<Geometry>; +    ) -> Vec<Renderer::Geometry>;      /// Returns the current mouse interaction of the [`Program`].      /// @@ -65,9 +69,10 @@ pub trait Program<Message, Theme = iced_native::Theme> {      }  } -impl<Message, Theme, T> Program<Message, Theme> for &T +impl<Message, Renderer, T> Program<Message, Renderer> for &T  where -    T: Program<Message, Theme>, +    Renderer: self::Renderer, +    T: Program<Message, Renderer>,  {      type State = T::State; @@ -84,11 +89,12 @@ where      fn draw(          &self,          state: &Self::State, -        theme: &Theme, +        renderer: &Renderer, +        theme: &Renderer::Theme,          bounds: Rectangle,          cursor: Cursor, -    ) -> Vec<Geometry> { -        T::draw(self, state, theme, bounds, cursor) +    ) -> Vec<Renderer::Geometry> { +        T::draw(self, state, renderer, theme, bounds, cursor)      }      fn mouse_interaction( diff --git a/graphics/src/widget/canvas/stroke.rs b/native/src/widget/canvas/stroke.rs index 4c19251d..ab4727b2 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/native/src/widget/canvas/stroke.rs @@ -1,7 +1,7 @@  //! Create lines from a [crate::widget::canvas::Path] and assigns them various attributes/styles.  pub use crate::widget::canvas::Style; -use iced_native::Color; +use crate::Color;  /// The style of a stroke.  #[derive(Debug, Clone)] @@ -77,16 +77,6 @@ impl Default for LineCap {      }  } -impl From<LineCap> for lyon::tessellation::LineCap { -    fn from(line_cap: LineCap) -> lyon::tessellation::LineCap { -        match line_cap { -            LineCap::Butt => lyon::tessellation::LineCap::Butt, -            LineCap::Square => lyon::tessellation::LineCap::Square, -            LineCap::Round => lyon::tessellation::LineCap::Round, -        } -    } -} -  /// The shape used at the corners of paths or basic shapes when they are  /// stroked.  #[derive(Debug, Clone, Copy)] @@ -105,16 +95,6 @@ impl Default for LineJoin {      }  } -impl From<LineJoin> for lyon::tessellation::LineJoin { -    fn from(line_join: LineJoin) -> lyon::tessellation::LineJoin { -        match line_join { -            LineJoin::Miter => lyon::tessellation::LineJoin::Miter, -            LineJoin::Round => lyon::tessellation::LineJoin::Round, -            LineJoin::Bevel => lyon::tessellation::LineJoin::Bevel, -        } -    } -} -  /// The dash pattern used when stroking the line.  #[derive(Debug, Clone, Copy, Default)]  pub struct LineDash<'a> { diff --git a/graphics/src/widget/canvas/style.rs b/native/src/widget/canvas/style.rs index 6794f2e7..2642fdb8 100644 --- a/graphics/src/widget/canvas/style.rs +++ b/native/src/widget/canvas/style.rs @@ -1,4 +1,5 @@ -use crate::{Color, Gradient}; +use crate::widget::canvas::Gradient; +use crate::Color;  /// The coloring style of some drawing.  #[derive(Debug, Clone, PartialEq)] diff --git a/graphics/src/widget/canvas/text.rs b/native/src/widget/canvas/text.rs index 8c0b2dfb..8c0b2dfb 100644 --- a/graphics/src/widget/canvas/text.rs +++ b/native/src/widget/canvas/text.rs diff --git a/graphics/src/widget/qr_code.rs b/renderer/src/widget/qr_code.rs index 12ce5b1f..aae4ec88 100644 --- a/graphics/src/widget/qr_code.rs +++ b/renderer/src/widget/qr_code.rs @@ -1,7 +1,8 @@  //! Encode and display information in a QR code. -use crate::renderer::{self, Renderer};  use crate::widget::canvas; -use crate::Backend; +use crate::Renderer; + +use iced_graphics::renderer;  use iced_native::layout;  use iced_native::widget::Tree; @@ -48,10 +49,7 @@ impl<'a> QRCode<'a> {      }  } -impl<'a, Message, B, T> Widget<Message, Renderer<B, T>> for QRCode<'a> -where -    B: Backend, -{ +impl<'a, Message, Theme> Widget<Message, Renderer<Theme>> for QRCode<'a> {      fn width(&self) -> Length {          Length::Shrink      } @@ -62,7 +60,7 @@ where      fn layout(          &self, -        _renderer: &Renderer<B, T>, +        _renderer: &Renderer<Theme>,          _limits: &layout::Limits,      ) -> layout::Node {          let side_length = (self.state.width + 2 * QUIET_ZONE) as f32 @@ -74,8 +72,8 @@ where      fn draw(          &self,          _state: &Tree, -        renderer: &mut Renderer<B, T>, -        _theme: &T, +        renderer: &mut Renderer<Theme>, +        _theme: &Theme,          _style: &renderer::Style,          layout: Layout<'_>,          _cursor_position: Point, @@ -87,50 +85,52 @@ where          let side_length = self.state.width + 2 * QUIET_ZONE;          // Reuse cache if possible -        let geometry = self.state.cache.draw(bounds.size(), |frame| { -            // Scale units to cell size -            frame.scale(f32::from(self.cell_size)); - -            // Draw background -            frame.fill_rectangle( -                Point::ORIGIN, -                Size::new(side_length as f32, side_length as f32), -                self.light, -            ); - -            // Avoid drawing on the quiet zone -            frame.translate(Vector::new(QUIET_ZONE as f32, QUIET_ZONE as f32)); - -            // Draw contents -            self.state -                .contents -                .iter() -                .enumerate() -                .filter(|(_, value)| **value == qrcode::Color::Dark) -                .for_each(|(index, _)| { -                    let row = index / self.state.width; -                    let column = index % self.state.width; - -                    frame.fill_rectangle( -                        Point::new(column as f32, row as f32), -                        Size::UNIT, -                        self.dark, -                    ); -                }); -        }); +        let geometry = +            self.state.cache.draw(renderer, bounds.size(), |frame| { +                // Scale units to cell size +                frame.scale(f32::from(self.cell_size)); + +                // Draw background +                frame.fill_rectangle( +                    Point::ORIGIN, +                    Size::new(side_length as f32, side_length as f32), +                    self.light, +                ); + +                // Avoid drawing on the quiet zone +                frame.translate(Vector::new( +                    QUIET_ZONE as f32, +                    QUIET_ZONE as f32, +                )); + +                // Draw contents +                self.state +                    .contents +                    .iter() +                    .enumerate() +                    .filter(|(_, value)| **value == qrcode::Color::Dark) +                    .for_each(|(index, _)| { +                        let row = index / self.state.width; +                        let column = index % self.state.width; + +                        frame.fill_rectangle( +                            Point::new(column as f32, row as f32), +                            Size::UNIT, +                            self.dark, +                        ); +                    }); +            });          let translation = Vector::new(bounds.x, bounds.y);          renderer.with_translation(translation, |renderer| { -            renderer.draw_primitive(geometry.into_primitive()); +            renderer.draw_primitive(geometry.0);          });      }  } -impl<'a, Message, B, T> From<QRCode<'a>> -    for Element<'a, Message, Renderer<B, T>> -where -    B: Backend, +impl<'a, Message, Theme> From<QRCode<'a>> +    for Element<'a, Message, Renderer<Theme>>  {      fn from(qr_code: QRCode<'a>) -> Self {          Self::new(qr_code) diff --git a/graphics/src/layer.rs b/wgpu/src/layer.rs index f6eb2fdd..0840555a 100644 --- a/graphics/src/layer.rs +++ b/wgpu/src/layer.rs @@ -10,10 +10,11 @@ pub use mesh::Mesh;  pub use quad::Quad;  pub use text::Text; -use crate::alignment; -use crate::{ -    Background, Color, Font, Point, Primitive, Rectangle, Size, Vector, -    Viewport, +use crate::Primitive; + +use iced_graphics::alignment; +use iced_graphics::{ +    Background, Color, Font, Point, Rectangle, Size, Vector, Viewport,  };  /// A group of primitives that should be clipped together. @@ -110,18 +111,6 @@ impl<'a> Layer<'a> {          current_layer: usize,      ) {          match primitive { -            Primitive::None => {} -            Primitive::Group { primitives } => { -                // TODO: Inspect a bit and regroup (?) -                for primitive in primitives { -                    Self::process_primitive( -                        layers, -                        translation, -                        primitive, -                        current_layer, -                    ) -                } -            }              Primitive::Text {                  content,                  bounds, @@ -167,6 +156,27 @@ impl<'a> Layer<'a> {                      border_color: border_color.into_linear(),                  });              } +            Primitive::Image { handle, bounds } => { +                let layer = &mut layers[current_layer]; + +                layer.images.push(Image::Raster { +                    handle: handle.clone(), +                    bounds: *bounds + translation, +                }); +            } +            Primitive::Svg { +                handle, +                color, +                bounds, +            } => { +                let layer = &mut layers[current_layer]; + +                layer.images.push(Image::Vector { +                    handle: handle.clone(), +                    color: *color, +                    bounds: *bounds + translation, +                }); +            }              Primitive::SolidMesh { buffers, size } => {                  let layer = &mut layers[current_layer]; @@ -206,6 +216,17 @@ impl<'a> Layer<'a> {                      });                  }              } +            Primitive::Group { primitives } => { +                // TODO: Inspect a bit and regroup (?) +                for primitive in primitives { +                    Self::process_primitive( +                        layers, +                        translation, +                        primitive, +                        current_layer, +                    ) +                } +            }              Primitive::Clip { bounds, content } => {                  let layer = &mut layers[current_layer];                  let translated_bounds = *bounds + translation; @@ -236,34 +257,17 @@ impl<'a> Layer<'a> {                      current_layer,                  );              } -            Primitive::Cached { cache } => { +            Primitive::Cache { content } => {                  Self::process_primitive(                      layers,                      translation, -                    cache, +                    content,                      current_layer,                  );              } -            Primitive::Image { handle, bounds } => { -                let layer = &mut layers[current_layer]; - -                layer.images.push(Image::Raster { -                    handle: handle.clone(), -                    bounds: *bounds + translation, -                }); -            } -            Primitive::Svg { -                handle, -                color, -                bounds, -            } => { -                let layer = &mut layers[current_layer]; - -                layer.images.push(Image::Vector { -                    handle: handle.clone(), -                    color: *color, -                    bounds: *bounds + translation, -                }); +            Primitive::Fill { .. } | Primitive::Stroke { .. } => { +                // Unsupported! +                // TODO: Draw a placeholder (?)              }          }      } diff --git a/graphics/src/layer/image.rs b/wgpu/src/layer/image.rs index 3eff2397..3eff2397 100644 --- a/graphics/src/layer/image.rs +++ b/wgpu/src/layer/image.rs diff --git a/graphics/src/layer/mesh.rs b/wgpu/src/layer/mesh.rs index 7661c5c9..5c1e41ad 100644 --- a/graphics/src/layer/mesh.rs +++ b/wgpu/src/layer/mesh.rs @@ -1,5 +1,5 @@  //! A collection of triangle primitives. -use crate::triangle; +use crate::primitive;  use crate::{Gradient, Point, Rectangle};  /// A mesh of triangles. @@ -11,7 +11,7 @@ pub enum Mesh<'a> {          origin: Point,          /// The vertex and index buffers of the [`Mesh`]. -        buffers: &'a triangle::Mesh2D<triangle::ColoredVertex2D>, +        buffers: &'a primitive::Mesh2D<primitive::ColoredVertex2D>,          /// The clipping bounds of the [`Mesh`].          clip_bounds: Rectangle<f32>, @@ -22,7 +22,7 @@ pub enum Mesh<'a> {          origin: Point,          /// The vertex and index buffers of the [`Mesh`]. -        buffers: &'a triangle::Mesh2D<triangle::Vertex2D>, +        buffers: &'a primitive::Mesh2D<primitive::Vertex2D>,          /// The clipping bounds of the [`Mesh`].          clip_bounds: Rectangle<f32>, diff --git a/graphics/src/layer/quad.rs b/wgpu/src/layer/quad.rs index 0d8bde9d..0d8bde9d 100644 --- a/graphics/src/layer/quad.rs +++ b/wgpu/src/layer/quad.rs diff --git a/graphics/src/layer/text.rs b/wgpu/src/layer/text.rs index 38d62616..38d62616 100644 --- a/graphics/src/layer/text.rs +++ b/wgpu/src/layer/text.rs diff --git a/graphics/src/widget.rs b/wgpu/src/widget.rs index e7fab97c..8d05041e 100644 --- a/graphics/src/widget.rs +++ b/wgpu/src/widget.rs @@ -1,4 +1,5 @@  //! Use the graphical widgets supported out-of-the-box. +  #[cfg(feature = "canvas")]  #[cfg_attr(docsrs, doc(cfg(feature = "canvas")))]  pub mod canvas; @@ -6,11 +7,3 @@ pub mod canvas;  #[cfg(feature = "canvas")]  #[doc(no_inline)]  pub use canvas::Canvas; - -#[cfg(feature = "qr_code")] -#[cfg_attr(docsrs, doc(cfg(feature = "qr_code")))] -pub mod qr_code; - -#[cfg(feature = "qr_code")] -#[doc(no_inline)] -pub use qr_code::QRCode; diff --git a/graphics/src/widget/canvas/cache.rs b/wgpu/src/widget/canvas/cache.rs index 52217bbb..09b26b90 100644 --- a/graphics/src/widget/canvas/cache.rs +++ b/wgpu/src/widget/canvas/cache.rs @@ -4,7 +4,9 @@ use crate::Primitive;  use iced_native::Size;  use std::{cell::RefCell, sync::Arc}; +#[derive(Default)]  enum State { +    #[default]      Empty,      Filled {          bounds: Size, @@ -12,11 +14,6 @@ enum State {      },  } -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 @@ -62,8 +59,8 @@ impl Cache {          } = self.state.borrow().deref()          {              if *cached_bounds == bounds { -                return Geometry::from_primitive(Primitive::Cached { -                    cache: primitive.clone(), +                return Geometry::from_primitive(Primitive::Cache { +                    content: primitive.clone(),                  });              }          } @@ -71,18 +68,14 @@ impl Cache {          let mut frame = Frame::new(bounds);          draw_fn(&mut frame); -        let primitive = { -            let geometry = frame.into_geometry(); - -            Arc::new(geometry.into_primitive()) -        }; +        let primitive = Arc::new(frame.into_primitive());          *self.state.borrow_mut() = State::Filled {              bounds,              primitive: primitive.clone(),          }; -        Geometry::from_primitive(Primitive::Cached { cache: primitive }) +        Geometry::from_primitive(Primitive::Cache { content: primitive })      }  } diff --git a/graphics/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs index d68548ae..987570ec 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/wgpu/src/widget/canvas/frame.rs @@ -1,9 +1,10 @@ -use crate::gradient::Gradient; -use crate::triangle; -use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Style, Text}; -use crate::Primitive; +use crate::primitive::{self, Primitive}; +use crate::widget::canvas::fill::{self, Fill}; +use crate::widget::canvas::{ +    LineCap, LineDash, LineJoin, Path, Stroke, Style, Text, +}; -use iced_native::{Point, Rectangle, Size, Vector}; +use iced_native::{Gradient, Point, Rectangle, Size, Vector};  use lyon::geom::euclid;  use lyon::tessellation; @@ -23,9 +24,9 @@ pub struct Frame {  }  enum Buffer { -    Solid(tessellation::VertexBuffers<triangle::ColoredVertex2D, u32>), +    Solid(tessellation::VertexBuffers<primitive::ColoredVertex2D, u32>),      Gradient( -        tessellation::VertexBuffers<triangle::Vertex2D, u32>, +        tessellation::VertexBuffers<primitive::Vertex2D, u32>,          Gradient,      ),  } @@ -196,8 +197,8 @@ impl Frame {              .buffers              .get_fill(&self.transforms.current.transform_style(style)); -        let options = -            tessellation::FillOptions::default().with_fill_rule(rule.into()); +        let options = tessellation::FillOptions::default() +            .with_fill_rule(into_fill_rule(rule));          if self.transforms.current.is_identity {              self.fill_tessellator.tessellate_path( @@ -206,7 +207,7 @@ impl Frame {                  buffer.as_mut(),              )          } else { -            let path = path.transformed(&self.transforms.current.raw); +            let path = path.transform(&self.transforms.current.raw);              self.fill_tessellator.tessellate_path(                  path.raw(), @@ -241,8 +242,8 @@ impl Frame {                  lyon::math::Vector::new(size.width, size.height),              ); -        let options = -            tessellation::FillOptions::default().with_fill_rule(rule.into()); +        let options = tessellation::FillOptions::default() +            .with_fill_rule(into_fill_rule(rule));          self.fill_tessellator              .tessellate_rectangle( @@ -264,14 +265,14 @@ impl Frame {          let mut options = tessellation::StrokeOptions::default();          options.line_width = stroke.width; -        options.start_cap = stroke.line_cap.into(); -        options.end_cap = stroke.line_cap.into(); -        options.line_join = stroke.line_join.into(); +        options.start_cap = into_line_cap(stroke.line_cap); +        options.end_cap = into_line_cap(stroke.line_cap); +        options.line_join = into_line_join(stroke.line_join);          let path = if stroke.line_dash.segments.is_empty() {              Cow::Borrowed(path)          } else { -            Cow::Owned(path::dashed(path, stroke.line_dash)) +            Cow::Owned(dashed(path, stroke.line_dash))          };          if self.transforms.current.is_identity { @@ -281,7 +282,7 @@ impl Frame {                  buffer.as_mut(),              )          } else { -            let path = path.transformed(&self.transforms.current.raw); +            let path = path.transform(&self.transforms.current.raw);              self.stroke_tessellator.tessellate_path(                  path.raw(), @@ -344,10 +345,18 @@ impl Frame {      /// operations in different coordinate systems.      #[inline]      pub fn with_save(&mut self, f: impl FnOnce(&mut Frame)) { -        self.transforms.previous.push(self.transforms.current); +        self.push_transform();          f(self); +        self.pop_transform(); +    } + +    pub fn push_transform(&mut self) { +        self.transforms.previous.push(self.transforms.current); +    } + +    pub fn pop_transform(&mut self) {          self.transforms.current = self.transforms.previous.pop().unwrap();      } @@ -363,14 +372,19 @@ impl Frame {          f(&mut frame); +        let translation = Vector::new(region.x, region.y); + +        self.clip(frame, translation); +    } + +    pub fn clip(&mut self, frame: Frame, translation: Vector) { +        let size = frame.size();          let primitives = frame.into_primitives();          let (text, meshes) = primitives              .into_iter()              .partition(|primitive| matches!(primitive, Primitive::Text { .. })); -        let translation = Vector::new(region.x, region.y); -          self.primitives.push(Primitive::Group {              primitives: vec![                  Primitive::Translate { @@ -380,7 +394,7 @@ impl Frame {                  Primitive::Translate {                      translation,                      content: Box::new(Primitive::Clip { -                        bounds: Rectangle::with_size(region.size()), +                        bounds: Rectangle::with_size(size),                          content: Box::new(Primitive::Group {                              primitives: text,                          }), @@ -423,11 +437,11 @@ impl Frame {          self.transforms.current.is_identity = false;      } -    /// Produces the [`Geometry`] representing everything drawn on the [`Frame`]. -    pub fn into_geometry(self) -> Geometry { -        Geometry::from_primitive(Primitive::Group { +    /// Produces the [`Primitive`] representing everything drawn on the [`Frame`]. +    pub fn into_primitive(self) -> Primitive { +        Primitive::Group {              primitives: self.into_primitives(), -        }) +        }      }      fn into_primitives(mut self) -> Vec<Primitive> { @@ -436,7 +450,7 @@ impl Frame {                  Buffer::Solid(buffer) => {                      if !buffer.indices.is_empty() {                          self.primitives.push(Primitive::SolidMesh { -                            buffers: triangle::Mesh2D { +                            buffers: primitive::Mesh2D {                                  vertices: buffer.vertices,                                  indices: buffer.indices,                              }, @@ -447,7 +461,7 @@ impl Frame {                  Buffer::Gradient(buffer, gradient) => {                      if !buffer.indices.is_empty() {                          self.primitives.push(Primitive::GradientMesh { -                            buffers: triangle::Mesh2D { +                            buffers: primitive::Mesh2D {                                  vertices: buffer.vertices,                                  indices: buffer.indices,                              }, @@ -465,31 +479,31 @@ impl Frame {  struct Vertex2DBuilder; -impl tessellation::FillVertexConstructor<triangle::Vertex2D> +impl tessellation::FillVertexConstructor<primitive::Vertex2D>      for Vertex2DBuilder  {      fn new_vertex(          &mut self,          vertex: tessellation::FillVertex<'_>, -    ) -> triangle::Vertex2D { +    ) -> primitive::Vertex2D {          let position = vertex.position(); -        triangle::Vertex2D { +        primitive::Vertex2D {              position: [position.x, position.y],          }      }  } -impl tessellation::StrokeVertexConstructor<triangle::Vertex2D> +impl tessellation::StrokeVertexConstructor<primitive::Vertex2D>      for Vertex2DBuilder  {      fn new_vertex(          &mut self,          vertex: tessellation::StrokeVertex<'_, '_>, -    ) -> triangle::Vertex2D { +    ) -> primitive::Vertex2D {          let position = vertex.position(); -        triangle::Vertex2D { +        primitive::Vertex2D {              position: [position.x, position.y],          }      } @@ -497,34 +511,99 @@ impl tessellation::StrokeVertexConstructor<triangle::Vertex2D>  struct TriangleVertex2DBuilder([f32; 4]); -impl tessellation::FillVertexConstructor<triangle::ColoredVertex2D> +impl tessellation::FillVertexConstructor<primitive::ColoredVertex2D>      for TriangleVertex2DBuilder  {      fn new_vertex(          &mut self,          vertex: tessellation::FillVertex<'_>, -    ) -> triangle::ColoredVertex2D { +    ) -> primitive::ColoredVertex2D {          let position = vertex.position(); -        triangle::ColoredVertex2D { +        primitive::ColoredVertex2D {              position: [position.x, position.y],              color: self.0,          }      }  } -impl tessellation::StrokeVertexConstructor<triangle::ColoredVertex2D> +impl tessellation::StrokeVertexConstructor<primitive::ColoredVertex2D>      for TriangleVertex2DBuilder  {      fn new_vertex(          &mut self,          vertex: tessellation::StrokeVertex<'_, '_>, -    ) -> triangle::ColoredVertex2D { +    ) -> primitive::ColoredVertex2D {          let position = vertex.position(); -        triangle::ColoredVertex2D { +        primitive::ColoredVertex2D {              position: [position.x, position.y],              color: self.0,          }      }  } + +fn into_line_join(line_join: LineJoin) -> lyon::tessellation::LineJoin { +    match line_join { +        LineJoin::Miter => lyon::tessellation::LineJoin::Miter, +        LineJoin::Round => lyon::tessellation::LineJoin::Round, +        LineJoin::Bevel => lyon::tessellation::LineJoin::Bevel, +    } +} + +fn into_line_cap(line_cap: LineCap) -> lyon::tessellation::LineCap { +    match line_cap { +        LineCap::Butt => lyon::tessellation::LineCap::Butt, +        LineCap::Square => lyon::tessellation::LineCap::Square, +        LineCap::Round => lyon::tessellation::LineCap::Round, +    } +} + +fn into_fill_rule(rule: fill::Rule) -> lyon::tessellation::FillRule { +    match rule { +        fill::Rule::NonZero => lyon::tessellation::FillRule::NonZero, +        fill::Rule::EvenOdd => lyon::tessellation::FillRule::EvenOdd, +    } +} + +pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path { +    use lyon::algorithms::walk::{ +        walk_along_path, RepeatedPattern, WalkerEvent, +    }; +    use lyon::path::iterator::PathIterator; + +    Path::new(|builder| { +        let segments_odd = (line_dash.segments.len() % 2 == 1) +            .then(|| [line_dash.segments, line_dash.segments].concat()); + +        let mut draw_line = false; + +        walk_along_path( +            path.raw().iter().flattened(0.01), +            0.0, +            lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE, +            &mut RepeatedPattern { +                callback: |event: WalkerEvent<'_>| { +                    let point = Point { +                        x: event.position.x, +                        y: event.position.y, +                    }; + +                    if draw_line { +                        builder.line_to(point); +                    } else { +                        builder.move_to(point); +                    } + +                    draw_line = !draw_line; + +                    true +                }, +                index: line_dash.offset, +                intervals: segments_odd +                    .as_deref() +                    .unwrap_or(line_dash.segments), +            }, +        ); +    }) +} diff --git a/graphics/src/widget/canvas/geometry.rs b/wgpu/src/widget/canvas/geometry.rs index e8ac621d..e8ac621d 100644 --- a/graphics/src/widget/canvas/geometry.rs +++ b/wgpu/src/widget/canvas/geometry.rs | 
