Skip to main content

gpui/text_system/
font_width.rs

1use derive_more::{Add, FromStr, Sub};
2use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};
3use serde::{Deserialize, Serialize};
4use std::{
5    borrow::Cow,
6    fmt::{self, Display, Formatter},
7    hash::{Hash, Hasher},
8};
9
10/// The requested width of a font face, as a percentage of its normal width.
11///
12/// `100.0` is [`FontWidth::NORMAL`]. Smaller values select condensed faces, and
13/// larger values select expanded faces. Use the named constants for standard steps, or any value
14/// in between. [`Font`](crate::Font) carries this value into text shaping. For a variable font, the backend
15/// uses it for the `wdth` axis and clamps it to that axis's range.
16///
17/// # Examples
18///
19/// ```
20/// # use gpui::{Font, FontWidth, font};
21/// let narrow = Font {
22///     width: FontWidth::CONDENSED,
23///     ..font("Noto Sans")
24/// };
25/// assert_eq!(narrow.width, FontWidth(75.0));
26/// ```
27#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Serialize, Deserialize, Add, Sub, FromStr)]
28#[serde(transparent)]
29pub struct FontWidth(pub f32);
30
31impl FontWidth {
32    /// Ultra-condensed width, 50% of normal.
33    pub const ULTRA_CONDENSED: Self = Self(50.0);
34    /// Extra-condensed width, 62.5% of normal.
35    pub const EXTRA_CONDENSED: Self = Self(62.5);
36    /// Condensed width, 75% of normal.
37    pub const CONDENSED: Self = Self(75.0);
38    /// Semi-condensed width, 87.5% of normal.
39    pub const SEMI_CONDENSED: Self = Self(87.5);
40    /// Normal width, 100%.
41    pub const NORMAL: Self = Self(100.0);
42    /// Semi-expanded width, 112.5% of normal.
43    pub const SEMI_EXPANDED: Self = Self(112.5);
44    /// Expanded width, 125% of normal.
45    pub const EXPANDED: Self = Self(125.0);
46    /// Extra-expanded width, 150% of normal.
47    pub const EXTRA_EXPANDED: Self = Self(150.0);
48    /// Ultra-expanded width, 200% of normal.
49    pub const ULTRA_EXPANDED: Self = Self(200.0);
50}
51
52impl From<f32> for FontWidth {
53    fn from(width: f32) -> Self {
54        Self(width)
55    }
56}
57
58impl Default for FontWidth {
59    fn default() -> Self {
60        Self::NORMAL
61    }
62}
63
64impl Eq for FontWidth {}
65
66impl Hash for FontWidth {
67    fn hash<H: Hasher>(&self, state: &mut H) {
68        self.0.to_bits().hash(state);
69    }
70}
71
72impl Display for FontWidth {
73    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
74        write!(formatter, "{}%", self.0)
75    }
76}
77
78impl JsonSchema for FontWidth {
79    fn schema_name() -> Cow<'static, str> {
80        "FontWidth".into()
81    }
82
83    fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
84        json_schema!({
85            "type": "number",
86            "exclusiveMinimum": 0,
87            "default": Self::default(),
88            "description": "Font width as a percentage of normal width (100)"
89        })
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn custom_widths_convert_parse_and_serialize() {
99        for percentage in [0.5, 75.0, 82.5, 100.0, 250.0] {
100            let width = FontWidth(percentage);
101            let json = serde_json::to_string(&width).unwrap();
102
103            assert_eq!(FontWidth::from(percentage), width);
104            assert_eq!(json.parse::<FontWidth>().unwrap(), width);
105            assert_eq!(serde_json::from_str::<FontWidth>(&json).unwrap(), width);
106        }
107
108        assert_eq!(FontWidth::default(), FontWidth::NORMAL);
109    }
110}