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::Paint;
14use torin::prelude::Area;
15
16use crate::{
17    prelude::Color,
18    style::{
19        gradient::{
20            ConicGradient,
21            LinearGradient,
22            RadialGradient,
23        },
24        shader::ShaderFill,
25    },
26};
27
28/// A paint source for backgrounds and text: a solid [`Color`], a gradient or a custom shader.
29///
30/// A plain [`Color`] converts into a [`Fill`] automatically, so most APIs that take an
31/// `impl Into<Fill>` accept a color directly. For gradients, build a
32/// [`LinearGradient`], [`RadialGradient`] or [`ConicGradient`] and wrap it in the
33/// matching variant:
34///
35/// ```
36/// # use freya::prelude::*;
37/// let gradient = Fill::LinearGradient(Box::new(
38///     LinearGradient::new()
39///         .stop((Color::RED, 0.0))
40///         .stop((Color::BLUE, 100.0)),
41/// ));
42/// ```
43#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
44#[derive(Clone, Debug, PartialEq)]
45pub enum Fill {
46    /// A solid [`Color`].
47    Color(Color),
48    /// A custom SkSL shader. See [`ShaderFill`].
49    Shader(Box<ShaderFill>),
50    /// A [`LinearGradient`].
51    LinearGradient(Box<LinearGradient>),
52    /// A [`RadialGradient`].
53    RadialGradient(Box<RadialGradient>),
54    /// A [`ConicGradient`].
55    ConicGradient(Box<ConicGradient>),
56}
57
58impl Fill {
59    pub fn set_a(&mut self, a: u8) {
60        if let Fill::Color(color) = self {
61            // Only actually change the alpha if its non-transparent
62            if *color != Color::TRANSPARENT {
63                *color = color.with_a(a);
64            }
65        }
66    }
67
68    /// Returns the inner [Color] when this is a [Fill::Color].
69    pub fn as_color(&self) -> Option<Color> {
70        match self {
71            Fill::Color(color) => Some(*color),
72            _ => None,
73        }
74    }
75
76    pub fn apply_to_paint(&self, paint: &mut Paint, area: Area) {
77        match &self {
78            Fill::Color(color) => {
79                paint.set_color(*color);
80            }
81            Fill::LinearGradient(gradient) => {
82                paint.set_shader(gradient.prepare_shader(area));
83            }
84            Fill::RadialGradient(gradient) => {
85                paint.set_shader(gradient.prepare_shader(area));
86            }
87            Fill::ConicGradient(gradient) => {
88                paint.set_shader(gradient.prepare_shader(area));
89            }
90            Fill::Shader(shader) => {
91                paint.set_shader(shader.prepare_shader(area));
92            }
93        }
94    }
95}
96
97impl Default for Fill {
98    fn default() -> Self {
99        Self::Color(Color::default())
100    }
101}
102
103impl Hash for Fill {
104    fn hash<H: Hasher>(&self, state: &mut H) {
105        discriminant(self).hash(state);
106        match self {
107            Fill::Color(color) => color.hash(state),
108            Fill::Shader(shader) => shader.hash(state),
109            Fill::LinearGradient(gradient) => gradient.hash(state),
110            Fill::RadialGradient(gradient) => gradient.hash(state),
111            Fill::ConicGradient(gradient) => gradient.hash(state),
112        }
113    }
114}
115
116impl From<Color> for Fill {
117    fn from(color: Color) -> Self {
118        Fill::Color(color)
119    }
120}
121
122impl fmt::Display for Fill {
123    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124        match self {
125            Self::Color(color) => color.fmt(f),
126            Self::Shader(shader) => shader.as_ref().fmt(f),
127            Self::LinearGradient(gradient) => gradient.as_ref().fmt(f),
128            Self::RadialGradient(gradient) => gradient.as_ref().fmt(f),
129            Self::ConicGradient(gradient) => gradient.as_ref().fmt(f),
130        }
131    }
132}