freya_core/values/
border.rs

1use std::fmt;
2
3use freya_engine::prelude::Color;
4use torin::scaled::Scaled;
5
6use super::Fill;
7use crate::parsing::{
8    ExtSplit,
9    Parse,
10    ParseError,
11};
12
13#[derive(Default, Clone, Debug, PartialEq)]
14pub struct Border {
15    pub fill: Fill,
16    pub width: BorderWidth,
17    pub alignment: BorderAlignment,
18}
19
20impl Border {
21    #[inline]
22    pub fn is_visible(&self) -> bool {
23        !(self.width.top == 0.0
24            && self.width.left == 0.0
25            && self.width.bottom == 0.0
26            && self.width.right == 0.0)
27            && self.fill != Fill::Color(Color::TRANSPARENT)
28    }
29}
30
31#[derive(Default, Clone, Copy, Debug, PartialEq)]
32pub struct BorderWidth {
33    pub top: f32,
34    pub right: f32,
35    pub bottom: f32,
36    pub left: f32,
37}
38
39impl Scaled for BorderWidth {
40    fn scale(&mut self, scale_factor: f32) {
41        self.top *= scale_factor;
42        self.left *= scale_factor;
43        self.bottom *= scale_factor;
44        self.right *= scale_factor;
45    }
46}
47
48impl fmt::Display for BorderWidth {
49    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50        write!(
51            f,
52            "{} {} {} {}",
53            self.top, self.right, self.bottom, self.left,
54        )
55    }
56}
57
58#[derive(Default, Clone, Copy, Debug, PartialEq)]
59pub enum BorderAlignment {
60    #[default]
61    Inner,
62    Outer,
63    Center,
64}
65
66impl Parse for BorderAlignment {
67    fn parse(value: &str) -> Result<Self, ParseError> {
68        Ok(match value {
69            "inner" => BorderAlignment::Inner,
70            "outer" => BorderAlignment::Outer,
71            "center" => BorderAlignment::Center,
72            _ => BorderAlignment::default(),
73        })
74    }
75}
76
77impl fmt::Display for BorderAlignment {
78    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79        f.write_str(match self {
80            BorderAlignment::Inner => "inner",
81            BorderAlignment::Outer => "outer",
82            BorderAlignment::Center => "center",
83        })
84    }
85}
86
87impl Parse for Border {
88    fn parse(value: &str) -> Result<Self, ParseError> {
89        if value == "none" {
90            return Ok(Self::default());
91        }
92
93        let mut border_values = value.split_ascii_whitespace_excluding_group('(', ')');
94
95        Ok(match border_values.clone().count() {
96            // <width> <style> <fill>
97            3 => {
98                let width = border_values
99                    .next()
100                    .ok_or(ParseError)?
101                    .parse::<f32>()
102                    .map_err(|_| ParseError)?;
103
104                Border {
105                    width: BorderWidth {
106                        top: width,
107                        left: width,
108                        bottom: width,
109                        right: width,
110                    },
111                    alignment: BorderAlignment::parse(border_values.next().ok_or(ParseError)?)?,
112                    fill: Fill::parse(&border_values.collect::<Vec<&str>>().join(" "))
113                        .map_err(|_| ParseError)?,
114                }
115            }
116
117            // <vertical> <horizontal> <solid> <fill>
118            4 => {
119                let vertical_width = border_values
120                    .next()
121                    .ok_or(ParseError)?
122                    .parse::<f32>()
123                    .map_err(|_| ParseError)?;
124                let horizontal_width = border_values
125                    .next()
126                    .ok_or(ParseError)?
127                    .parse::<f32>()
128                    .map_err(|_| ParseError)?;
129
130                Border {
131                    width: BorderWidth {
132                        top: vertical_width,
133                        left: horizontal_width,
134                        bottom: vertical_width,
135                        right: horizontal_width,
136                    },
137                    alignment: BorderAlignment::parse(border_values.next().ok_or(ParseError)?)?,
138                    fill: Fill::parse(&border_values.collect::<Vec<&str>>().join(" "))
139                        .map_err(|_| ParseError)?,
140                }
141            }
142            // <top> <horizontal> <bottom> <style> <fill>
143            5 => {
144                let top_width = border_values
145                    .next()
146                    .ok_or(ParseError)?
147                    .parse::<f32>()
148                    .map_err(|_| ParseError)?;
149                let horizontal_width = border_values
150                    .next()
151                    .ok_or(ParseError)?
152                    .parse::<f32>()
153                    .map_err(|_| ParseError)?;
154                let bottom_width = border_values
155                    .next()
156                    .ok_or(ParseError)?
157                    .parse::<f32>()
158                    .map_err(|_| ParseError)?;
159
160                Border {
161                    width: BorderWidth {
162                        top: top_width,
163                        left: horizontal_width,
164                        bottom: bottom_width,
165                        right: horizontal_width,
166                    },
167                    alignment: BorderAlignment::parse(border_values.next().ok_or(ParseError)?)?,
168                    fill: Fill::parse(&border_values.collect::<Vec<&str>>().join(" "))
169                        .map_err(|_| ParseError)?,
170                }
171            }
172            // <top> <right> <bottom> <left> <style> <fill>
173            6 => Border {
174                width: BorderWidth {
175                    top: border_values
176                        .next()
177                        .ok_or(ParseError)?
178                        .parse::<f32>()
179                        .map_err(|_| ParseError)?,
180                    right: border_values
181                        .next()
182                        .ok_or(ParseError)?
183                        .parse::<f32>()
184                        .map_err(|_| ParseError)?,
185                    bottom: border_values
186                        .next()
187                        .ok_or(ParseError)?
188                        .parse::<f32>()
189                        .map_err(|_| ParseError)?,
190                    left: border_values
191                        .next()
192                        .ok_or(ParseError)?
193                        .parse::<f32>()
194                        .map_err(|_| ParseError)?,
195                },
196                alignment: BorderAlignment::parse(border_values.next().ok_or(ParseError)?)?,
197                fill: Fill::parse(&border_values.collect::<Vec<&str>>().join(" "))
198                    .map_err(|_| ParseError)?,
199            },
200            _ => return Err(ParseError),
201        })
202    }
203}
204
205impl fmt::Display for Border {
206    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207        write!(f, "{} {} {}", self.width, self.alignment, self.fill,)
208    }
209}
210
211impl Scaled for Border {
212    fn scale(&mut self, scale_factor: f32) {
213        self.width.scale(scale_factor);
214    }
215}