Skip to main content

freya_core/style/
fill.rs

1use std::{
2    fmt::{
3        self,
4        Pointer,
5    },
6    hash::{
7        Hash,
8        Hasher,
9    },
10    mem::discriminant,
11};
12
13use freya_engine::prelude::{
14    Paint,
15    SkColor,
16    TextStyle,
17};
18use torin::prelude::Area;
19
20use crate::{
21    data::TextStyleState,
22    prelude::Color,
23    style::{
24        gradient::{
25            ConicGradient,
26            LinearGradient,
27            RadialGradient,
28        },
29        render_callback::{
30            RenderCallback,
31            RenderContext,
32            RenderTarget,
33        },
34        shader::ShaderFill,
35    },
36};
37
38/// A paint source for backgrounds and text: a solid [`Color`], a gradient or a custom shader.
39///
40/// A [`Color`], [`LinearGradient`], [`RadialGradient`], [`ConicGradient`] or [`ShaderFill`]
41/// all convert into a [`Fill`] automatically, so APIs that take an `impl Into<Fill>` accept
42/// any of them directly:
43///
44/// ```
45/// # use freya::prelude::*;
46/// let gradient: Fill = LinearGradient::new()
47///     .stop((Color::RED, 0.0))
48///     .stop((Color::BLUE, 100.0))
49///     .into();
50/// ```
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52#[derive(Clone, Debug, PartialEq)]
53pub enum Fill {
54    /// A solid [`Color`].
55    Color(Color),
56    /// A custom SkSL shader. See [`ShaderFill`].
57    Shader(Box<ShaderFill>),
58    /// A [`LinearGradient`].
59    LinearGradient(Box<LinearGradient>),
60    /// A [`RadialGradient`].
61    RadialGradient(Box<RadialGradient>),
62    /// A [`ConicGradient`].
63    ConicGradient(Box<ConicGradient>),
64    /// Custom background or text paint.
65    Callback(#[cfg_attr(feature = "serde", serde(skip))] RenderCallback),
66}
67
68impl Fill {
69    pub fn set_a(&mut self, a: u8) {
70        if let Fill::Color(color) = self {
71            *color = color.with_a(a);
72        }
73    }
74
75    /// Returns the inner [Color] when this is a [Fill::Color].
76    pub fn as_color(&self) -> Option<Color> {
77        match self {
78            Fill::Color(color) => Some(*color),
79            _ => None,
80        }
81    }
82
83    /// Applies this fill as a text color.
84    pub fn apply_to_text_style(
85        &self,
86        text_style: &mut TextStyle,
87        area: Area,
88        text_style_state: &TextStyleState,
89        scale_factor: f64,
90    ) {
91        match self {
92            Self::Color(color) => {
93                text_style.set_color(*color);
94            }
95            Self::Callback(callback) => {
96                text_style.set_color(Color::TRANSPARENT);
97                callback.call(&mut RenderContext {
98                    target: RenderTarget::TextStyle(text_style),
99                    scale_factor,
100                    origin: area.origin,
101                    size: area.size,
102                    text_style_state,
103                });
104            }
105            _ => {
106                let mut paint = Paint::default();
107                paint.set_anti_alias(true);
108                self.apply_to_paint(&mut paint, area);
109                text_style.set_foreground_paint(&paint);
110            }
111        }
112    }
113
114    pub fn render_callback(&self) -> Option<&RenderCallback> {
115        match self {
116            Self::Callback(callback) => Some(callback),
117            _ => None,
118        }
119    }
120
121    pub fn apply_to_paint(&self, paint: &mut Paint, area: Area) {
122        match self {
123            Fill::Color(color) => {
124                paint.set_color(*color);
125            }
126            Fill::LinearGradient(gradient) => {
127                paint.set_dither(gradient.dither);
128                paint.set_shader(gradient.prepare_shader(area));
129            }
130            Fill::RadialGradient(gradient) => {
131                paint.set_dither(gradient.dither);
132                paint.set_shader(gradient.prepare_shader(area));
133            }
134            Fill::ConicGradient(gradient) => {
135                paint.set_dither(gradient.dither);
136                paint.set_shader(gradient.prepare_shader(area));
137            }
138            Fill::Shader(shader) => {
139                paint.set_shader(shader.prepare_shader(area));
140            }
141            Fill::Callback(_) => {
142                paint.set_color(Color::TRANSPARENT);
143            }
144        }
145    }
146}
147
148impl Default for Fill {
149    fn default() -> Self {
150        Self::Color(Color::default())
151    }
152}
153
154impl Hash for Fill {
155    fn hash<H: Hasher>(&self, state: &mut H) {
156        discriminant(self).hash(state);
157        match self {
158            Fill::Color(color) => color.hash(state),
159            Fill::Shader(shader) => shader.hash(state),
160            Fill::LinearGradient(gradient) => gradient.hash(state),
161            Fill::RadialGradient(gradient) => gradient.hash(state),
162            Fill::ConicGradient(gradient) => gradient.hash(state),
163            Fill::Callback(callback) => callback.hash(state),
164        }
165    }
166}
167
168impl From<RenderCallback> for Fill {
169    fn from(callback: RenderCallback) -> Self {
170        Self::Callback(callback)
171    }
172}
173
174impl From<Color> for Fill {
175    fn from(color: Color) -> Self {
176        Fill::Color(color)
177    }
178}
179
180impl From<(u8, u8, u8)> for Fill {
181    fn from(color: (u8, u8, u8)) -> Self {
182        Fill::Color(color.into())
183    }
184}
185
186impl From<(u8, u8, u8, f32)> for Fill {
187    fn from(color: (u8, u8, u8, f32)) -> Self {
188        Fill::Color(color.into())
189    }
190}
191
192impl From<(u8, u8, u8, u8)> for Fill {
193    fn from(color: (u8, u8, u8, u8)) -> Self {
194        Fill::Color(color.into())
195    }
196}
197
198impl From<u32> for Fill {
199    fn from(color: u32) -> Self {
200        Fill::Color(color.into())
201    }
202}
203
204impl From<SkColor> for Fill {
205    fn from(color: SkColor) -> Self {
206        Fill::Color(color.into())
207    }
208}
209
210impl From<LinearGradient> for Fill {
211    fn from(gradient: LinearGradient) -> Self {
212        Fill::LinearGradient(Box::new(gradient))
213    }
214}
215
216impl From<RadialGradient> for Fill {
217    fn from(gradient: RadialGradient) -> Self {
218        Fill::RadialGradient(Box::new(gradient))
219    }
220}
221
222impl From<ConicGradient> for Fill {
223    fn from(gradient: ConicGradient) -> Self {
224        Fill::ConicGradient(Box::new(gradient))
225    }
226}
227
228impl From<ShaderFill> for Fill {
229    fn from(shader: ShaderFill) -> Self {
230        Fill::Shader(Box::new(shader))
231    }
232}
233
234impl fmt::Display for Fill {
235    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236        match self {
237            Self::Color(color) => color.fmt(f),
238            Self::Shader(shader) => shader.as_ref().fmt(f),
239            Self::LinearGradient(gradient) => gradient.as_ref().fmt(f),
240            Self::RadialGradient(gradient) => gradient.as_ref().fmt(f),
241            Self::ConicGradient(gradient) => gradient.as_ref().fmt(f),
242            Self::Callback(callback) => callback.fmt(f),
243        }
244    }
245}