iced_graphics/text/
cache.rs

1//! Cache text.
2use crate::core::{Font, Size};
3use crate::text;
4
5use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
6use std::collections::hash_map;
7use std::hash::{Hash, Hasher};
8
9/// A store of recently used sections of text.
10#[derive(Debug, Default)]
11pub struct Cache {
12    entries: FxHashMap<KeyHash, Entry>,
13    aliases: FxHashMap<KeyHash, KeyHash>,
14    recently_used: FxHashSet<KeyHash>,
15}
16
17impl Cache {
18    /// Creates a new empty [`Cache`].
19    pub fn new() -> Self {
20        Self::default()
21    }
22
23    /// Gets the text [`Entry`] with the given [`KeyHash`].
24    pub fn get(&self, key: &KeyHash) -> Option<&Entry> {
25        self.entries.get(key)
26    }
27
28    /// Allocates a text [`Entry`] if it is not already present in the [`Cache`].
29    pub fn allocate(
30        &mut self,
31        font_system: &mut cosmic_text::FontSystem,
32        key: Key<'_>,
33    ) -> (KeyHash, &mut Entry) {
34        let hash = key.hash(FxHasher::default());
35
36        if let Some(hash) = self.aliases.get(&hash) {
37            let _ = self.recently_used.insert(*hash);
38
39            return (*hash, self.entries.get_mut(hash).unwrap());
40        }
41
42        if let hash_map::Entry::Vacant(entry) = self.entries.entry(hash) {
43            let metrics = cosmic_text::Metrics::new(
44                key.size,
45                key.line_height.max(f32::MIN_POSITIVE),
46            );
47            let mut buffer = cosmic_text::Buffer::new(font_system, metrics);
48
49            buffer.set_size(
50                font_system,
51                Some(key.bounds.width),
52                Some(key.bounds.height.max(key.line_height)),
53            );
54            buffer.set_text(
55                font_system,
56                key.content,
57                &text::to_attributes(key.font),
58                text::to_shaping(key.shaping, key.content),
59                None,
60            );
61
62            let bounds = text::align(&mut buffer, font_system, key.align_x);
63
64            let _ = entry.insert(Entry {
65                buffer,
66                min_bounds: bounds,
67            });
68
69            for bounds in [
70                bounds,
71                Size {
72                    width: key.bounds.width,
73                    ..bounds
74                },
75            ] {
76                if key.bounds != bounds {
77                    let _ = self.aliases.insert(
78                        Key { bounds, ..key }.hash(FxHasher::default()),
79                        hash,
80                    );
81                }
82            }
83        }
84
85        let _ = self.recently_used.insert(hash);
86
87        (hash, self.entries.get_mut(&hash).unwrap())
88    }
89
90    /// Trims the [`Cache`].
91    ///
92    /// This will clear the sections of text that have not been used since the last `trim`.
93    pub fn trim(&mut self) {
94        self.entries
95            .retain(|key, _| self.recently_used.contains(key));
96
97        self.aliases
98            .retain(|_, value| self.recently_used.contains(value));
99
100        self.recently_used.clear();
101    }
102}
103
104/// A cache key representing a section of text.
105#[derive(Debug, Clone, Copy)]
106pub struct Key<'a> {
107    /// The content of the text.
108    pub content: &'a str,
109    /// The size of the text.
110    pub size: f32,
111    /// The line height of the text.
112    pub line_height: f32,
113    /// The [`Font`] of the text.
114    pub font: Font,
115    /// The bounds of the text.
116    pub bounds: Size,
117    /// The shaping strategy of the text.
118    pub shaping: text::Shaping,
119    /// The alignment of the text.
120    pub align_x: text::Alignment,
121}
122
123impl Key<'_> {
124    fn hash<H: Hasher>(self, mut hasher: H) -> KeyHash {
125        self.content.hash(&mut hasher);
126        self.size.to_bits().hash(&mut hasher);
127        self.line_height.to_bits().hash(&mut hasher);
128        self.font.hash(&mut hasher);
129        self.bounds.width.to_bits().hash(&mut hasher);
130        self.bounds.height.to_bits().hash(&mut hasher);
131        self.shaping.hash(&mut hasher);
132        self.align_x.hash(&mut hasher);
133
134        hasher.finish()
135    }
136}
137
138/// The hash of a [`Key`].
139pub type KeyHash = u64;
140
141/// A cache entry.
142#[derive(Debug)]
143pub struct Entry {
144    /// The buffer of text, ready for drawing.
145    pub buffer: cosmic_text::Buffer,
146    /// The minimum bounds of the text.
147    pub min_bounds: Size,
148}