diff options
Diffstat (limited to 'graphics/src/widget')
-rw-r--r-- | graphics/src/widget/canvas.rs | 20 | ||||
-rw-r--r-- | graphics/src/widget/canvas/frame.rs | 32 | ||||
-rw-r--r-- | graphics/src/widget/canvas/path.rs | 44 | ||||
-rw-r--r-- | graphics/src/widget/canvas/stroke.rs | 29 | ||||
-rw-r--r-- | graphics/src/widget/qr_code.rs | 9 |
5 files changed, 105 insertions, 29 deletions
diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index f9722f33..65d7e37e 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -9,10 +9,9 @@ use crate::{Backend, Primitive}; use iced_native::layout; use iced_native::mouse; use iced_native::{ - Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Shell, Size, - Vector, Widget, + Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Size, Vector, + Widget, }; -use std::hash::Hash; use std::marker::PhantomData; pub mod event; @@ -35,7 +34,7 @@ pub use frame::Frame; pub use geometry::Geometry; pub use path::Path; pub use program::Program; -pub use stroke::{LineCap, LineJoin, Stroke}; +pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; pub use text::Text; /// A widget capable of drawing 2D graphics. @@ -212,6 +211,11 @@ where 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); @@ -226,14 +230,6 @@ where }); }); } - - fn hash_layout(&self, state: &mut Hasher) { - struct Marker; - std::any::TypeId::of::<Marker>().hash(state); - - self.width.hash(state); - self.height.hash(state); - } } impl<'a, Message, P, B> From<Canvas<Message, P>> diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 4873e7fb..a3449605 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -1,6 +1,9 @@ +use std::borrow::Cow; + use iced_native::{Point, Rectangle, Size, Vector}; use crate::{ + canvas::path, canvas::{Fill, Geometry, Path, Stroke, Text}, triangle, Primitive, }; @@ -150,7 +153,7 @@ impl Frame { /// Draws the stroke of the given [`Path`] on the [`Frame`] with the /// provided style. - pub fn stroke(&mut self, path: &Path, stroke: impl Into<Stroke>) { + pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) { let stroke = stroke.into(); let mut buffers = tessellation::BuffersBuilder::new( @@ -164,6 +167,12 @@ impl Frame { options.end_cap = stroke.line_cap.into(); options.line_join = stroke.line_join.into(); + let path = if stroke.line_dash.segments.is_empty() { + Cow::Borrowed(path) + } else { + Cow::Owned(path::dashed(path, stroke.line_dash)) + }; + let result = if self.transforms.current.is_identity { self.stroke_tessellator.tessellate_path( path.raw(), @@ -244,6 +253,27 @@ impl Frame { self.transforms.current = self.transforms.previous.pop().unwrap(); } + /// Executes the given drawing operations within a [`Rectangle`] region, + /// clipping any geometry that overflows its bounds. Any transformations + /// performed are local to the provided closure. + /// + /// This method is useful to perform drawing operations that need to be + /// clipped. + #[inline] + pub fn with_clip(&mut self, region: Rectangle, f: impl FnOnce(&mut Frame)) { + let mut frame = Frame::new(region.size()); + + f(&mut frame); + + self.primitives.push(Primitive::Clip { + bounds: region, + content: Box::new(Primitive::Translate { + translation: Vector::new(region.x, region.y), + content: Box::new(frame.into_geometry().into_primitive()), + }), + }); + } + /// Applies a translation to the current transform of the [`Frame`]. #[inline] pub fn translate(&mut self, translation: Vector) { diff --git a/graphics/src/widget/canvas/path.rs b/graphics/src/widget/canvas/path.rs index 4e4fd734..1728f060 100644 --- a/graphics/src/widget/canvas/path.rs +++ b/graphics/src/widget/canvas/path.rs @@ -7,7 +7,11 @@ mod builder; pub use arc::Arc; pub use builder::Builder; +use crate::canvas::LineDash; + use iced_native::{Point, Size}; +use lyon::algorithms::walk::{walk_along_path, RepeatedPattern}; +use lyon::path::iterator::PathIterator; /// An immutable set of points that may or may not be connected. /// @@ -66,3 +70,43 @@ impl Path { } } } + +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, + &mut RepeatedPattern { + callback: |position: lyon::algorithms::math::Point, + _tangent, + _distance| { + let point = Point { + x: position.x, + y: 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_ref() + .map(Vec::as_slice) + .unwrap_or(line_dash.segments), + }, + ); + }) +} diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index 9f0449d0..6accc2fb 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -2,7 +2,7 @@ use iced_native::Color; /// The style of a stroke. #[derive(Debug, Clone, Copy)] -pub struct Stroke { +pub struct Stroke<'a> { /// The color of the stroke. pub color: Color, /// The distance between the two edges of the stroke. @@ -12,37 +12,40 @@ pub struct Stroke { /// The shape to be used at the corners of paths or basic shapes when they /// are stroked. pub line_join: LineJoin, + /// The dash pattern used when stroking the line. + pub line_dash: LineDash<'a>, } -impl Stroke { +impl<'a> Stroke<'a> { /// Sets the color of the [`Stroke`]. - pub fn with_color(self, color: Color) -> Stroke { + pub fn with_color(self, color: Color) -> Self { Stroke { color, ..self } } /// Sets the width of the [`Stroke`]. - pub fn with_width(self, width: f32) -> Stroke { + pub fn with_width(self, width: f32) -> Self { Stroke { width, ..self } } /// Sets the [`LineCap`] of the [`Stroke`]. - pub fn with_line_cap(self, line_cap: LineCap) -> Stroke { + pub fn with_line_cap(self, line_cap: LineCap) -> Self { Stroke { line_cap, ..self } } /// Sets the [`LineJoin`] of the [`Stroke`]. - pub fn with_line_join(self, line_join: LineJoin) -> Stroke { + pub fn with_line_join(self, line_join: LineJoin) -> Self { Stroke { line_join, ..self } } } -impl Default for Stroke { - fn default() -> Stroke { +impl<'a> Default for Stroke<'a> { + fn default() -> Self { Stroke { color: Color::BLACK, width: 1.0, line_cap: LineCap::default(), line_join: LineJoin::default(), + line_dash: LineDash::default(), } } } @@ -103,3 +106,13 @@ impl From<LineJoin> for lyon::tessellation::LineJoin { } } } + +/// The dash pattern used when stroking the line. +#[derive(Debug, Clone, Copy, Default)] +pub struct LineDash<'a> { + /// The alternating lengths of lines and gaps which describe the pattern. + pub segments: &'a [f32], + + /// The offset of [`LineDash::segments`] to start the pattern. + pub offset: usize, +} diff --git a/graphics/src/widget/qr_code.rs b/graphics/src/widget/qr_code.rs index 285b8622..907794b7 100644 --- a/graphics/src/widget/qr_code.rs +++ b/graphics/src/widget/qr_code.rs @@ -5,8 +5,7 @@ use crate::Backend; use iced_native::layout; use iced_native::{ - Color, Element, Hasher, Layout, Length, Point, Rectangle, Size, Vector, - Widget, + Color, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget, }; use thiserror::Error; @@ -74,12 +73,6 @@ where )) } - fn hash_layout(&self, state: &mut Hasher) { - use std::hash::Hash; - - self.state.contents.hash(state); - } - fn draw( &self, renderer: &mut Renderer<B>, |