1use crate::effects::{BlendMode, ColorFilter};
2use crate::geometry::CornerRadii;
3use crate::gradients::{Brush, Shader};
4use incular_core::finite_non_negative;
5use incular_core::{Color, Lerp, Offset};
6use smart_default::SmartDefault;
7
8#[derive(Clone, Copy, Debug, PartialEq)]
9pub struct Border {
10 pub width: f32,
11 pub color: Color,
12}
13impl Border {
14 #[must_use]
15 pub fn new(width: f32, color: Color) -> Self {
16 Self {
17 width: width.max(0.),
18 color,
19 }
20 }
21}
22#[derive(Clone, Debug, Default, PartialEq)]
23pub struct Decoration {
24 pub background: Option<Brush>,
25 pub border: Option<Border>,
26 pub border_radius: CornerRadii,
27}
28#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
29pub enum FillRule {
30 NonZero,
31 EvenOdd,
32}
33#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
34pub enum LineCap {
35 Butt,
36 Round,
37 Square,
38}
39#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
40pub enum LineJoin {
41 Miter,
42 Round,
43 Bevel,
44}
45#[derive(Clone, Copy, Debug, PartialEq, SmartDefault)]
46pub struct Stroke {
47 #[default(1.)]
48 pub width: f32,
49 #[default(LineCap::Butt)]
50 pub cap: LineCap,
51 #[default(LineJoin::Miter)]
52 pub join: LineJoin,
53 #[default(4.)]
54 pub miter_limit: f32,
55}
56
57#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
59pub enum PaintStyle {
60 #[default]
61 Fill,
62 Stroke,
63}
64
65#[derive(Clone, Debug, PartialEq, SmartDefault)]
72pub struct Paint {
73 #[default(Brush::Solid(Color::BLACK))]
74 brush: Brush,
75 style: PaintStyle,
76 stroke: Stroke,
77 blend_mode: BlendMode,
78 color_filter: Option<ColorFilter>,
79}
80
81impl Paint {
82 #[must_use]
83 pub fn new() -> Self {
84 Self::default()
85 }
86
87 #[must_use]
88 pub fn brush(mut self, brush: impl Into<Brush>) -> Self {
89 self.brush = brush.into();
90 self
91 }
92
93 #[must_use]
94 pub fn shader(self, shader: impl Into<Shader>) -> Self {
95 self.brush(shader)
96 }
97
98 #[must_use]
99 pub fn color(self, color: Color) -> Self {
100 self.brush(color)
101 }
102
103 #[must_use]
104 pub fn style(mut self, style: PaintStyle) -> Self {
105 self.style = style;
106 self
107 }
108
109 #[must_use]
110 pub fn fill(self) -> Self {
111 self.style(PaintStyle::Fill)
112 }
113
114 #[must_use]
115 pub fn stroke(self) -> Self {
116 self.style(PaintStyle::Stroke)
117 }
118
119 #[must_use]
120 pub fn stroke_settings(mut self, stroke: Stroke) -> Self {
121 self.stroke = Stroke {
122 width: finite_non_negative(stroke.width),
123 cap: stroke.cap,
124 join: stroke.join,
125 miter_limit: finite_non_negative(stroke.miter_limit),
126 };
127 self
128 }
129
130 #[must_use]
131 pub fn stroke_width(mut self, width: f32) -> Self {
132 self.stroke.width = finite_non_negative(width);
133 self
134 }
135
136 #[must_use]
137 pub fn blend_mode(mut self, blend_mode: BlendMode) -> Self {
138 self.blend_mode = blend_mode;
139 self
140 }
141
142 #[must_use]
143 pub fn color_filter(mut self, color_filter: Option<ColorFilter>) -> Self {
144 self.color_filter = color_filter;
145 self
146 }
147
148 #[must_use]
149 pub fn brush_value(&self) -> &Brush {
150 &self.brush
151 }
152
153 #[must_use]
154 pub const fn style_value(&self) -> PaintStyle {
155 self.style
156 }
157
158 #[must_use]
159 pub const fn stroke_value(&self) -> Stroke {
160 self.stroke
161 }
162
163 #[must_use]
164 pub const fn blend_mode_value(&self) -> BlendMode {
165 self.blend_mode
166 }
167
168 #[must_use]
169 pub fn color_filter_value(&self) -> Option<ColorFilter> {
170 self.color_filter
171 }
172}
173
174#[derive(Clone, Copy, Debug, PartialEq, SmartDefault)]
179pub struct Shadow {
180 #[default(Color::BLACK)]
181 pub color: Color,
182 pub offset: Offset,
183 pub blur_radius: f32,
184}
185
186impl Shadow {
187 #[must_use]
188 pub const fn new(color: Color, offset: Offset, blur_radius: f32) -> Self {
189 Self {
190 color,
191 offset,
192 blur_radius: if blur_radius.is_finite() && blur_radius > 0. {
193 blur_radius
194 } else {
195 0.
196 },
197 }
198 }
199}
200
201impl Lerp for Shadow {
202 fn lerp(&self, other: &Self, t: f32) -> Self {
203 Self::new(
204 self.color.lerp(&other.color, t),
205 self.offset.lerp(&other.offset, t),
206 self.blur_radius.lerp(&other.blur_radius, t),
207 )
208 }
209}