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