salvation_cosmic_text/
attrs.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3#[cfg(not(feature = "std"))]
4use alloc::{
5    string::{String, ToString},
6    vec::Vec,
7};
8use core::ops::Range;
9use rangemap::RangeMap;
10
11use crate::CacheKeyFlags;
12
13pub use fontdb::{Family, Stretch, Style, Weight};
14
15/// Text color
16#[derive(Clone, Copy, Debug, PartialOrd, Ord, Eq, Hash, PartialEq)]
17pub struct Color(pub u32);
18
19impl Color {
20    /// Create new color with red, green, and blue components
21    #[inline]
22    pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
23        Self::rgba(r, g, b, 0xFF)
24    }
25
26    /// Create new color with red, green, blue, and alpha components
27    #[inline]
28    pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
29        Self(((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32))
30    }
31
32    /// Get a tuple over all of the attributes, in `(r, g, b, a)` order.
33    #[inline]
34    pub fn as_rgba_tuple(self) -> (u8, u8, u8, u8) {
35        (self.r(), self.g(), self.b(), self.a())
36    }
37
38    /// Get an array over all of the components, in `[r, g, b, a]` order.
39    #[inline]
40    pub fn as_rgba(self) -> [u8; 4] {
41        [self.r(), self.g(), self.b(), self.a()]
42    }
43
44    /// Get the red component
45    #[inline]
46    pub fn r(&self) -> u8 {
47        ((self.0 & 0x00_FF_00_00) >> 16) as u8
48    }
49
50    /// Get the green component
51    #[inline]
52    pub fn g(&self) -> u8 {
53        ((self.0 & 0x00_00_FF_00) >> 8) as u8
54    }
55
56    /// Get the blue component
57    #[inline]
58    pub fn b(&self) -> u8 {
59        (self.0 & 0x00_00_00_FF) as u8
60    }
61
62    /// Get the alpha component
63    #[inline]
64    pub fn a(&self) -> u8 {
65        ((self.0 & 0xFF_00_00_00) >> 24) as u8
66    }
67}
68
69/// An owned version of [`Family`]
70#[derive(Clone, Debug, Eq, Hash, PartialEq)]
71pub enum FamilyOwned {
72    Name(String),
73    Serif,
74    SansSerif,
75    Cursive,
76    Fantasy,
77    Monospace,
78}
79
80impl FamilyOwned {
81    pub fn new(family: Family) -> Self {
82        match family {
83            Family::Name(name) => FamilyOwned::Name(name.to_string()),
84            Family::Serif => FamilyOwned::Serif,
85            Family::SansSerif => FamilyOwned::SansSerif,
86            Family::Cursive => FamilyOwned::Cursive,
87            Family::Fantasy => FamilyOwned::Fantasy,
88            Family::Monospace => FamilyOwned::Monospace,
89        }
90    }
91
92    pub fn as_family(&self) -> Family {
93        match self {
94            FamilyOwned::Name(name) => Family::Name(name),
95            FamilyOwned::Serif => Family::Serif,
96            FamilyOwned::SansSerif => Family::SansSerif,
97            FamilyOwned::Cursive => Family::Cursive,
98            FamilyOwned::Fantasy => Family::Fantasy,
99            FamilyOwned::Monospace => Family::Monospace,
100        }
101    }
102}
103
104/// Text attributes
105#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
106pub struct Attrs<'a> {
107    //TODO: should this be an option?
108    pub color_opt: Option<Color>,
109    pub family: Family<'a>,
110    pub stretch: Stretch,
111    pub style: Style,
112    pub weight: Weight,
113    pub metadata: usize,
114    pub cache_key_flags: CacheKeyFlags,
115    pub is_preedit: bool,
116}
117
118impl<'a> Attrs<'a> {
119    /// Create a new set of attributes with sane defaults
120    ///
121    /// This defaults to a regular Sans-Serif font.
122    pub fn new() -> Self {
123        Self {
124            color_opt: None,
125            family: Family::SansSerif,
126            stretch: Stretch::Normal,
127            style: Style::Normal,
128            weight: Weight::NORMAL,
129            metadata: 0,
130            cache_key_flags: CacheKeyFlags::empty(),
131            is_preedit: false,
132        }
133    }
134
135    /// Set [Color]
136    pub fn color(mut self, color: Color) -> Self {
137        self.color_opt = Some(color);
138        self
139    }
140
141    /// Set [Family]
142    pub fn family(mut self, family: Family<'a>) -> Self {
143        self.family = family;
144        self
145    }
146
147    /// Set [Stretch]
148    pub fn stretch(mut self, stretch: Stretch) -> Self {
149        self.stretch = stretch;
150        self
151    }
152
153    /// Set [Style]
154    pub fn style(mut self, style: Style) -> Self {
155        self.style = style;
156        self
157    }
158
159    /// Set [Weight]
160    pub fn weight(mut self, weight: Weight) -> Self {
161        self.weight = weight;
162        self
163    }
164
165    /// Set metadata
166    pub fn metadata(mut self, metadata: usize) -> Self {
167        self.metadata = metadata;
168        self
169    }
170
171    /// Set [`CacheKeyFlags`]
172    pub fn cache_key_flags(mut self, cache_key_flags: CacheKeyFlags) -> Self {
173        self.cache_key_flags = cache_key_flags;
174        self
175    }
176    
177    /// Set preedit
178    pub fn preedit(mut self, is_preedit: bool) -> Self {
179        self.is_preedit = is_preedit;
180        self
181    }
182
183    /// Check if font matches
184    pub fn matches(&self, face: &fontdb::FaceInfo) -> bool {
185        //TODO: smarter way of including emoji
186        face.post_script_name.contains("Emoji")
187            || (face.style == self.style && face.stretch == self.stretch)
188    }
189
190    /// Check if this set of attributes can be shaped with another
191    pub fn compatible(&self, other: &Self) -> bool {
192        self.family == other.family
193            && self.stretch == other.stretch
194            && self.style == other.style
195            && self.weight == other.weight
196    }
197}
198
199/// Font-specific part of [`Attrs`] to be used for matching
200#[derive(Clone, Debug, Eq, Hash, PartialEq)]
201pub struct FontMatchAttrs {
202    family: FamilyOwned,
203    stretch: Stretch,
204    style: Style,
205    weight: Weight,
206}
207
208impl<'a> From<Attrs<'a>> for FontMatchAttrs {
209    fn from(attrs: Attrs<'a>) -> Self {
210        Self {
211            family: FamilyOwned::new(attrs.family),
212            stretch: attrs.stretch,
213            style: attrs.style,
214            weight: attrs.weight,
215        }
216    }
217}
218
219/// An owned version of [`Attrs`]
220#[derive(Clone, Debug, Eq, Hash, PartialEq)]
221pub struct AttrsOwned {
222    //TODO: should this be an option?
223    pub color_opt: Option<Color>,
224    pub family_owned: FamilyOwned,
225    pub stretch: Stretch,
226    pub style: Style,
227    pub weight: Weight,
228    pub metadata: usize,
229    pub cache_key_flags: CacheKeyFlags,
230    pub is_preedit: bool,
231}
232
233impl AttrsOwned {
234    pub fn new(attrs: Attrs) -> Self {
235        Self {
236            color_opt: attrs.color_opt,
237            family_owned: FamilyOwned::new(attrs.family),
238            stretch: attrs.stretch,
239            style: attrs.style,
240            weight: attrs.weight,
241            metadata: attrs.metadata,
242            cache_key_flags: attrs.cache_key_flags,
243            is_preedit: attrs.is_preedit,
244        }
245    }
246
247    pub fn as_attrs(&self) -> Attrs {
248        Attrs {
249            color_opt: self.color_opt,
250            family: self.family_owned.as_family(),
251            stretch: self.stretch,
252            style: self.style,
253            weight: self.weight,
254            metadata: self.metadata,
255            cache_key_flags: self.cache_key_flags,
256            is_preedit: self.is_preedit,
257        }
258    }
259}
260
261/// List of text attributes to apply to a line
262//TODO: have this clean up the spans when changes are made
263#[derive(Debug, Clone, Eq, PartialEq)]
264pub struct AttrsList {
265    defaults: AttrsOwned,
266    pub(crate) spans: RangeMap<usize, AttrsOwned>,
267}
268
269impl AttrsList {
270    /// Create a new attributes list with a set of default [Attrs]
271    pub fn new(defaults: Attrs) -> Self {
272        Self {
273            defaults: AttrsOwned::new(defaults),
274            spans: RangeMap::new(),
275        }
276    }
277
278    /// Get the default [Attrs]
279    pub fn defaults(&self) -> Attrs {
280        self.defaults.as_attrs()
281    }
282
283    /// Get the current attribute spans
284    pub fn spans(&self) -> Vec<(&Range<usize>, &AttrsOwned)> {
285        self.spans.iter().collect()
286    }
287
288    /// Clear the current attribute spans
289    pub fn clear_spans(&mut self) {
290        self.spans.clear();
291    }
292
293    /// Add an attribute span, removes any previous matching parts of spans
294    pub fn add_span(&mut self, range: Range<usize>, attrs: Attrs) {
295        //do not support 1..1 or 2..1 even if by accident.
296        if range.is_empty() {
297            return;
298        }
299
300        self.spans.insert(range, AttrsOwned::new(attrs));
301    }
302
303    /// Get the attribute span for an index
304    ///
305    /// This returns a span that contains the index
306    pub fn get_span(&self, index: usize) -> Attrs {
307        self.spans
308            .get(&index)
309            .map(|v| v.as_attrs())
310            .unwrap_or(self.defaults.as_attrs())
311    }
312
313    /// Split attributes list at an offset
314    pub fn split_off(&mut self, index: usize) -> Self {
315        let mut new = Self::new(self.defaults.as_attrs());
316        let mut removes = Vec::new();
317
318        //get the keys we need to remove or fix.
319        for span in self.spans.iter() {
320            if span.0.end <= index {
321                continue;
322            } else if span.0.start >= index {
323                removes.push((span.0.clone(), false));
324            } else {
325                removes.push((span.0.clone(), true));
326            }
327        }
328
329        for (key, resize) in removes {
330            let (range, attrs) = self
331                .spans
332                .get_key_value(&key.start)
333                .map(|v| (v.0.clone(), v.1.clone()))
334                .expect("attrs span not found");
335            self.spans.remove(key);
336
337            if resize {
338                new.spans.insert(0..range.end - index, attrs.clone());
339                self.spans.insert(range.start..index, attrs);
340            } else {
341                new.spans
342                    .insert(range.start - index..range.end - index, attrs);
343            }
344        }
345        new
346    }
347}