Skip to main content

freya_core/style/
border.rs

1use std::fmt;
2
3use freya_engine::prelude::{
4    SkPath,
5    SkRRect,
6};
7use torin::scaled::Scaled;
8
9use crate::prelude::Color;
10
11/// Width of each side of a [`Border`], in pixels.
12///
13/// Implements `From<f32>`, applied to all sides.
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[derive(Default, Clone, Copy, Debug, PartialEq)]
16pub struct BorderWidth {
17    pub top: f32,
18    pub right: f32,
19    pub bottom: f32,
20    pub left: f32,
21}
22
23impl Scaled for BorderWidth {
24    fn scale(&mut self, scale_factor: f32) {
25        self.top *= scale_factor;
26        self.left *= scale_factor;
27        self.bottom *= scale_factor;
28        self.right *= scale_factor;
29    }
30}
31
32impl From<f32> for BorderWidth {
33    fn from(value: f32) -> Self {
34        BorderWidth {
35            top: value,
36            right: value,
37            bottom: value,
38            left: value,
39        }
40    }
41}
42
43impl fmt::Display for BorderWidth {
44    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45        write!(
46            f,
47            "{} {} {} {}",
48            self.top, self.right, self.bottom, self.left,
49        )
50    }
51}
52
53/// Where a [`Border`] is drawn relative to the element's edge.
54#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
55#[derive(Default, Clone, Copy, Debug, PartialEq)]
56pub enum BorderAlignment {
57    /// Draw the border inside the element's bounds. This is the default.
58    #[default]
59    Inner,
60    /// Draw the border outside the element's bounds.
61    Outer,
62    /// Draw the border centered on the element's edge, half inside and half outside.
63    Center,
64}
65
66pub enum BorderShape {
67    DRRect(SkRRect, SkRRect),
68    Path(SkPath),
69}
70
71/// An outline drawn around an element, with a [`fill`](Border::fill) color,
72/// a [`width`](Border::width) per side and an [`alignment`](Border::alignment).
73///
74/// Start from [`Border::new`] and chain the methods you need:
75///
76/// ```
77/// # use freya::prelude::*;
78/// let border = Border::new()
79///     .fill(Color::RED)
80///     .width(2.0)
81///     .alignment(BorderAlignment::Inner);
82/// ```
83#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84#[derive(Default, Clone, Debug, PartialEq)]
85pub struct Border {
86    pub fill: Color,
87    pub width: BorderWidth,
88    pub alignment: BorderAlignment,
89}
90
91impl Border {
92    /// Create a new [`Border`] with default values.
93    pub fn new() -> Self {
94        Self::default()
95    }
96
97    /// Set the [`Color`] the border is painted with.
98    pub fn fill(mut self, color: impl Into<Color>) -> Self {
99        self.fill = color.into();
100        self
101    }
102
103    /// Set the [`BorderWidth`] of the border, in pixels.
104    pub fn width(mut self, width: impl Into<BorderWidth>) -> Self {
105        self.width = width.into();
106        self
107    }
108
109    /// Set how the border is aligned to the element's edge. See [`BorderAlignment`].
110    pub fn alignment(mut self, alignment: impl Into<BorderAlignment>) -> Self {
111        self.alignment = alignment.into();
112        self
113    }
114
115    #[inline]
116    pub(crate) fn is_visible(&self) -> bool {
117        !(self.width.top == 0.0
118            && self.width.left == 0.0
119            && self.width.bottom == 0.0
120            && self.width.right == 0.0)
121            && self.fill != Color::TRANSPARENT
122    }
123
124    pub fn pretty(&self) -> String {
125        format!("{} {:?}", self.width, self.alignment)
126    }
127}
128
129impl Scaled for Border {
130    fn scale(&mut self, scale_factor: f32) {
131        self.width.scale(scale_factor);
132    }
133}