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