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    prelude::Color,
22    style::{
23        gradient::{
24            ConicGradient,
25            LinearGradient,
26            RadialGradient,
27        },
28        shader::ShaderFill,
29    },
30};
31
32/// A paint source for backgrounds and text: a solid [`Color`], a gradient or a custom shader.
33///
34/// A [`Color`], [`LinearGradient`], [`RadialGradient`], [`ConicGradient`] or [`ShaderFill`]
35/// all convert into a [`Fill`] automatically, so APIs that take an `impl Into<Fill>` accept
36/// any of them directly:
37///
38/// ```
39/// # use freya::prelude::*;
40/// let gradient: Fill = LinearGradient::new()
41///     .stop((Color::RED, 0.0))
42///     .stop((Color::BLUE, 100.0))
43///     .into();
44/// ```
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46#[derive(Clone, Debug, PartialEq)]
47pub enum Fill {
48    /// A solid [`Color`].
49    Color(Color),
50    /// A custom SkSL shader. See [`ShaderFill`].
51    Shader(Box<ShaderFill>),
52    /// A [`LinearGradient`].
53    LinearGradient(Box<LinearGradient>),
54    /// A [`RadialGradient`].
55    RadialGradient(Box<RadialGradient>),
56    /// A [`ConicGradient`].
57    ConicGradient(Box<ConicGradient>),
58}
59
60impl Fill {
61    pub fn set_a(&mut self, a: u8) {
62        if let Fill::Color(color) = self {
63            // Only actually change the alpha if its non-transparent
64            if *color != Color::TRANSPARENT {
65                *color = color.with_a(a);
66            }
67        }
68    }
69
70    /// Returns the inner [Color] when this is a [Fill::Color].
71    pub fn as_color(&self) -> Option<Color> {
72        match self {
73            Fill::Color(color) => Some(*color),
74            _ => None,
75        }
76    }
77
78    /// Applies this fill as a text color.
79    pub fn apply_to_text_style(&self, text_style: &mut TextStyle, area: Area) {
80        if let Some(color) = self.as_color() {
81            text_style.set_color(color);
82            return;
83        }
84
85        let mut paint = Paint::default();
86        paint.set_anti_alias(true);
87        self.apply_to_paint(&mut paint, area);
88        text_style.set_foreground_paint(&paint);
89    }
90
91    pub fn apply_to_paint(&self, paint: &mut Paint, area: Area) {
92        match &self {
93            Fill::Color(color) => {
94                paint.set_color(*color);
95            }
96            Fill::LinearGradient(gradient) => {
97                paint.set_shader(gradient.prepare_shader(area));
98            }
99            Fill::RadialGradient(gradient) => {
100                paint.set_shader(gradient.prepare_shader(area));
101            }
102            Fill::ConicGradient(gradient) => {
103                paint.set_shader(gradient.prepare_shader(area));
104            }
105            Fill::Shader(shader) => {
106                paint.set_shader(shader.prepare_shader(area));
107            }
108        }
109    }
110}
111
112impl Default for Fill {
113    fn default() -> Self {
114        Self::Color(Color::default())
115    }
116}
117
118impl Hash for Fill {
119    fn hash<H: Hasher>(&self, state: &mut H) {
120        discriminant(self).hash(state);
121        match self {
122            Fill::Color(color) => color.hash(state),
123            Fill::Shader(shader) => shader.hash(state),
124            Fill::LinearGradient(gradient) => gradient.hash(state),
125            Fill::RadialGradient(gradient) => gradient.hash(state),
126            Fill::ConicGradient(gradient) => gradient.hash(state),
127        }
128    }
129}
130
131impl From<Color> for Fill {
132    fn from(color: Color) -> Self {
133        Fill::Color(color)
134    }
135}
136
137impl From<(u8, u8, u8)> for Fill {
138    fn from(color: (u8, u8, u8)) -> Self {
139        Fill::Color(color.into())
140    }
141}
142
143impl From<(u8, u8, u8, f32)> for Fill {
144    fn from(color: (u8, u8, u8, f32)) -> Self {
145        Fill::Color(color.into())
146    }
147}
148
149impl From<(u8, u8, u8, u8)> for Fill {
150    fn from(color: (u8, u8, u8, u8)) -> Self {
151        Fill::Color(color.into())
152    }
153}
154
155impl From<u32> for Fill {
156    fn from(color: u32) -> Self {
157        Fill::Color(color.into())
158    }
159}
160
161impl From<SkColor> for Fill {
162    fn from(color: SkColor) -> Self {
163        Fill::Color(color.into())
164    }
165}
166
167impl From<LinearGradient> for Fill {
168    fn from(gradient: LinearGradient) -> Self {
169        Fill::LinearGradient(Box::new(gradient))
170    }
171}
172
173impl From<RadialGradient> for Fill {
174    fn from(gradient: RadialGradient) -> Self {
175        Fill::RadialGradient(Box::new(gradient))
176    }
177}
178
179impl From<ConicGradient> for Fill {
180    fn from(gradient: ConicGradient) -> Self {
181        Fill::ConicGradient(Box::new(gradient))
182    }
183}
184
185impl From<ShaderFill> for Fill {
186    fn from(shader: ShaderFill) -> Self {
187        Fill::Shader(Box::new(shader))
188    }
189}
190
191impl fmt::Display for Fill {
192    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
193        match self {
194            Self::Color(color) => color.fmt(f),
195            Self::Shader(shader) => shader.as_ref().fmt(f),
196            Self::LinearGradient(gradient) => gradient.as_ref().fmt(f),
197            Self::RadialGradient(gradient) => gradient.as_ref().fmt(f),
198            Self::ConicGradient(gradient) => gradient.as_ref().fmt(f),
199        }
200    }
201}