summaryrefslogtreecommitdiffstats
path: root/graphics/src/widget
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/src/widget')
-rw-r--r--graphics/src/widget/canvas.rs20
-rw-r--r--graphics/src/widget/canvas/frame.rs32
-rw-r--r--graphics/src/widget/canvas/path.rs44
-rw-r--r--graphics/src/widget/canvas/stroke.rs29
-rw-r--r--graphics/src/widget/qr_code.rs9
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>,