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),
59            );
60
61            let (bounds, has_rtl) = text::measure(&buffer);
62
63            if has_rtl {
64                buffer.set_size(
65                    font_system,
66                    Some(bounds.width),
67                    Some(bounds.height),
68                );
69            }
70
71            let _ = entry.insert(Entry {
72                buffer,
73                min_bounds: bounds,
74            });
75
76            for bounds in [
77                bounds,
78                Size {
79                    width: key.bounds.width,
80                    ..bounds
81                },
82            ] {
83                if key.bounds != bounds {
84                    let _ = self.aliases.insert(
85                        Key { bounds, ..key }.hash(FxHasher::default()),
86                        hash,
87                    );
88                }
89            }
90        }
91
92        let _ = self.recently_used.insert(hash);
93
94        (hash, self.entries.get_mut(&hash).unwrap())
95    }
96
97    /// Trims the [`Cache`].
98    ///
99    /// This will clear the sections of text that have not been used since the last `trim`.
100    pub fn trim(&mut self) {
101        self.entries
102            .retain(|key, _| self.recently_used.contains(key));
103
104        self.aliases
105            .retain(|_, value| self.recently_used.contains(value));
106
107        self.recently_used.clear();
108    }
109}
110
111/// A cache key representing a section of text.
112#[derive(Debug, Clone, Copy)]
113pub struct Key<'a> {
114    /// The content of the text.
115    pub content: &'a str,
116    /// The size of the text.
117    pub size: f32,
118    /// The line height of the text.
119    pub line_height: f32,
120    /// The [`Font`] of the text.
121    pub font: Font,
122    /// The bounds of the text.
123    pub bounds: Size,
124    /// The shaping strategy of the text.
125    pub shaping: text::Shaping,
126}
127
128impl Key<'_> {
129    fn hash<H: Hasher>(self, mut hasher: H) -> KeyHash {
130        self.content.hash(&mut hasher);
131        self.size.to_bits().hash(&mut hasher);
132        self.line_height.to_bits().hash(&mut hasher);
133        self.font.hash(&mut hasher);
134        self.bounds.width.to_bits().hash(&mut hasher);
135        self.bounds.height.to_bits().hash(&mut hasher);
136        self.shaping.hash(&mut hasher);
137
138        hasher.finish()
139    }
140}
141
142/// The hash of a [`Key`].
143pub type KeyHash = u64;
144
145/// A cache entry.
146#[derive(Debug)]
147pub struct Entry {
148    /// The buffer of text, ready for drawing.
149    pub buffer: cosmic_text::Buffer,
150    /// The minimum bounds of the text.
151    pub min_bounds: Size,
152}