Skip to main content

ferroui_base/media/immutable/
immutable_pen.rs

1use crate::media::immutable::{ImmutableDashStyle, ImmutableSolidColorBrush};
2use crate::media::{IBrush, IDashStyle, IImmutableBrush, IPen, PenLineCap, PenLineJoin};
3use std::any::Any;
4use std::rc::Rc;
5
6/// Describes how a stroke is drawn.
7#[derive(Clone, Debug)]
8pub struct ImmutablePen {
9    brush: Option<Rc<dyn IBrush>>,
10    thickness: f64,
11    dash_style: Option<Rc<dyn IDashStyle>>,
12    line_cap: PenLineCap,
13    line_join: PenLineJoin,
14    miter_limit: f64,
15    /// The form sent to the render thread, created on first use.
16    shared: std::sync::OnceLock<Option<crate::media::SharedPen>>,
17}
18
19impl ImmutablePen {
20    /// Creates a pen.
21    pub fn new(
22        brush: Option<Rc<dyn IImmutableBrush>>,
23        thickness: f64,
24        dash_style: Option<Rc<ImmutableDashStyle>>,
25        line_cap: PenLineCap,
26        line_join: PenLineJoin,
27        miter_limit: f64,
28    ) -> Self {
29        Self {
30            brush: brush.map(|b| b as Rc<dyn IBrush>),
31            thickness,
32            line_cap,
33            line_join,
34            miter_limit,
35            dash_style: dash_style.map(|d| d as Rc<dyn IDashStyle>),
36            shared: std::sync::OnceLock::new(),
37        }
38    }
39
40    /// Creates a solid pen with the given brush and thickness: no dashes,
41    /// flat caps, miter joins and a miter limit of 10.
42    pub fn with_brush(brush: Option<Rc<dyn IImmutableBrush>>, thickness: f64) -> Self {
43        Self::new(brush, thickness, None, PenLineCap::Flat, PenLineJoin::Miter, 10.0)
44    }
45
46    /// Creates a solid pen with the color given as an `0xAARRGGBB` value.
47    pub fn from_uint32(color: u32, thickness: f64) -> Self {
48        Self::with_brush(Some(Rc::new(ImmutableSolidColorBrush::from_uint32(color))), thickness)
49    }
50
51    /// The brush used to draw the stroke.
52    #[inline]
53    pub fn brush(&self) -> Option<Rc<dyn IBrush>> {
54        self.brush.clone()
55    }
56
57    /// The stroke thickness.
58    #[inline]
59    pub fn thickness(&self) -> f64 {
60        self.thickness
61    }
62
63    /// The dash style for the stroke.
64    #[inline]
65    pub fn dash_style(&self) -> Option<Rc<dyn IDashStyle>> {
66        self.dash_style.clone()
67    }
68
69    /// The type of shape to use on both ends of a line.
70    #[inline]
71    pub fn line_cap(&self) -> PenLineCap {
72        self.line_cap
73    }
74
75    /// A value describing how to join consecutive line or curve segments.
76    #[inline]
77    pub fn line_join(&self) -> PenLineJoin {
78        self.line_join
79    }
80
81    /// The limit of the ratio of the miter length to half this pen's
82    /// thickness.
83    #[inline]
84    pub fn miter_limit(&self) -> f64 {
85        self.miter_limit
86    }
87}
88
89impl IPen for ImmutablePen {
90    fn brush(&self) -> Option<Rc<dyn IBrush>> {
91        self.brush.clone()
92    }
93
94    fn dash_style(&self) -> Option<Rc<dyn IDashStyle>> {
95        self.dash_style.clone()
96    }
97
98    #[inline]
99    fn line_cap(&self) -> PenLineCap {
100        self.line_cap
101    }
102
103    #[inline]
104    fn line_join(&self) -> PenLineJoin {
105        self.line_join
106    }
107
108    #[inline]
109    fn miter_limit(&self) -> f64 {
110        self.miter_limit
111    }
112
113    #[inline]
114    fn thickness(&self) -> f64 {
115        self.thickness
116    }
117
118    fn as_any(&self) -> &dyn Any {
119        self
120    }
121
122    fn into_immutable_pen(self: Rc<Self>) -> Rc<ImmutablePen> {
123        self
124    }
125
126    fn to_shared(&self) -> Option<crate::media::SharedPen> {
127        self.shared.get_or_init(|| crate::media::SharedPen::from_pen(self)).clone()
128    }
129
130    fn equals(&self, other: &dyn IPen) -> bool {
131        if self.reference_id() == other.reference_id() {
132            return true;
133        }
134
135        self.brush == other.brush()
136            && self.thickness == other.thickness()
137            && self.dash_style == other.dash_style()
138            && self.line_cap == other.line_cap()
139            && self.line_join == other.line_join()
140            && self.miter_limit == other.miter_limit()
141    }
142}
143
144impl PartialEq for ImmutablePen {
145    fn eq(&self, other: &Self) -> bool {
146        IPen::equals(self, other)
147    }
148}