1use crate::{px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, SharedString};
2use derive_more::{Deref, DerefMut};
3use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
4use smallvec::SmallVec;
5use std::{
6 borrow::Borrow,
7 collections::HashMap,
8 hash::{Hash, Hasher},
9 sync::Arc,
10};
11
12#[derive(Default, Debug)]
13pub struct LineLayout {
14 pub font_size: Pixels,
15 pub width: Pixels,
16 pub ascent: Pixels,
17 pub descent: Pixels,
18 pub runs: Vec<ShapedRun>,
19 pub len: usize,
20}
21
22#[derive(Debug)]
23pub struct ShapedRun {
24 pub font_id: FontId,
25 pub glyphs: SmallVec<[ShapedGlyph; 8]>,
26}
27
28#[derive(Clone, Debug)]
29pub struct ShapedGlyph {
30 pub id: GlyphId,
31 pub position: Point<Pixels>,
32 pub index: usize,
33 pub is_emoji: bool,
34}
35
36impl LineLayout {
37 pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
38 if x >= self.width {
39 None
40 } else {
41 for run in self.runs.iter().rev() {
42 for glyph in run.glyphs.iter().rev() {
43 if glyph.position.x <= x {
44 return Some(glyph.index);
45 }
46 }
47 }
48 Some(0)
49 }
50 }
51
52 /// closest_index_for_x returns the character boundary closest to the given x coordinate
53 /// (e.g. to handle aligning up/down arrow keys)
54 pub fn closest_index_for_x(&self, x: Pixels) -> usize {
55 let mut prev_index = 0;
56 let mut prev_x = px(0.);
57
58 for run in self.runs.iter() {
59 for glyph in run.glyphs.iter() {
60 if glyph.position.x >= x {
61 if glyph.position.x - x < x - prev_x {
62 return glyph.index;
63 } else {
64 return prev_index;
65 }
66 }
67 prev_index = glyph.index;
68 prev_x = glyph.position.x;
69 }
70 }
71 prev_index
72 }
73
74 pub fn x_for_index(&self, index: usize) -> Pixels {
75 for run in &self.runs {
76 for glyph in &run.glyphs {
77 if glyph.index >= index {
78 return glyph.position.x;
79 }
80 }
81 }
82 self.width
83 }
84
85 fn compute_wrap_boundaries(
86 &self,
87 text: &str,
88 wrap_width: Pixels,
89 ) -> SmallVec<[WrapBoundary; 1]> {
90 let mut boundaries = SmallVec::new();
91
92 let mut first_non_whitespace_ix = None;
93 let mut last_candidate_ix = None;
94 let mut last_candidate_x = px(0.);
95 let mut last_boundary = WrapBoundary {
96 run_ix: 0,
97 glyph_ix: 0,
98 };
99 let mut last_boundary_x = px(0.);
100 let mut prev_ch = '\0';
101 let mut glyphs = self
102 .runs
103 .iter()
104 .enumerate()
105 .flat_map(move |(run_ix, run)| {
106 run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
107 let character = text[glyph.index..].chars().next().unwrap();
108 (
109 WrapBoundary { run_ix, glyph_ix },
110 character,
111 glyph.position.x,
112 )
113 })
114 })
115 .peekable();
116
117 while let Some((boundary, ch, x)) = glyphs.next() {
118 if ch == '\n' {
119 continue;
120 }
121
122 if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
123 last_candidate_ix = Some(boundary);
124 last_candidate_x = x;
125 }
126
127 if ch != ' ' && first_non_whitespace_ix.is_none() {
128 first_non_whitespace_ix = Some(boundary);
129 }
130
131 let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
132 let width = next_x - last_boundary_x;
133 if width > wrap_width && boundary > last_boundary {
134 if let Some(last_candidate_ix) = last_candidate_ix.take() {
135 last_boundary = last_candidate_ix;
136 last_boundary_x = last_candidate_x;
137 } else {
138 last_boundary = boundary;
139 last_boundary_x = x;
140 }
141
142 boundaries.push(last_boundary);
143 }
144 prev_ch = ch;
145 }
146
147 boundaries
148 }
149}
150
151#[derive(Deref, DerefMut, Default, Debug)]
152pub struct WrappedLineLayout {
153 #[deref]
154 #[deref_mut]
155 pub layout: LineLayout,
156 pub text: SharedString,
157 pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
158}
159
160#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
161pub struct WrapBoundary {
162 pub run_ix: usize,
163 pub glyph_ix: usize,
164}
165
166pub(crate) struct LineLayoutCache {
167 prev_frame: Mutex<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
168 curr_frame: RwLock<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
169 platform_text_system: Arc<dyn PlatformTextSystem>,
170}
171
172impl LineLayoutCache {
173 pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
174 Self {
175 prev_frame: Mutex::new(HashMap::new()),
176 curr_frame: RwLock::new(HashMap::new()),
177 platform_text_system,
178 }
179 }
180
181 pub fn start_frame(&self) {
182 let mut prev_frame = self.prev_frame.lock();
183 let mut curr_frame = self.curr_frame.write();
184 std::mem::swap(&mut *prev_frame, &mut *curr_frame);
185 curr_frame.clear();
186 }
187
188 pub fn layout_line(
189 &self,
190 text: &SharedString,
191 font_size: Pixels,
192 runs: &[FontRun],
193 wrap_width: Option<Pixels>,
194 ) -> Arc<WrappedLineLayout> {
195 let key = &CacheKeyRef {
196 text,
197 font_size,
198 runs,
199 wrap_width,
200 } as &dyn AsCacheKeyRef;
201 let curr_frame = self.curr_frame.upgradable_read();
202 if let Some(layout) = curr_frame.get(key) {
203 return layout.clone();
204 }
205
206 let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
207 if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
208 curr_frame.insert(key, layout.clone());
209 layout
210 } else {
211 let layout = self.platform_text_system.layout_line(text, font_size, runs);
212 let wrap_boundaries = wrap_width
213 .map(|wrap_width| layout.compute_wrap_boundaries(text.as_ref(), wrap_width))
214 .unwrap_or_default();
215 let wrapped_line = Arc::new(WrappedLineLayout {
216 layout,
217 text: text.clone(),
218 wrap_boundaries,
219 });
220
221 let key = CacheKey {
222 text: text.clone(),
223 font_size,
224 runs: SmallVec::from(runs),
225 wrap_width,
226 };
227 curr_frame.insert(key, wrapped_line.clone());
228 wrapped_line
229 }
230 }
231}
232
233#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
234pub struct FontRun {
235 pub(crate) len: usize,
236 pub(crate) font_id: FontId,
237}
238
239trait AsCacheKeyRef {
240 fn as_cache_key_ref(&self) -> CacheKeyRef;
241}
242
243#[derive(Eq)]
244struct CacheKey {
245 text: SharedString,
246 font_size: Pixels,
247 runs: SmallVec<[FontRun; 1]>,
248 wrap_width: Option<Pixels>,
249}
250
251#[derive(Copy, Clone, PartialEq, Eq, Hash)]
252struct CacheKeyRef<'a> {
253 text: &'a str,
254 font_size: Pixels,
255 runs: &'a [FontRun],
256 wrap_width: Option<Pixels>,
257}
258
259impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
260 fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
261 self.as_cache_key_ref() == other.as_cache_key_ref()
262 }
263}
264
265impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
266
267impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
268 fn hash<H: Hasher>(&self, state: &mut H) {
269 self.as_cache_key_ref().hash(state)
270 }
271}
272
273impl AsCacheKeyRef for CacheKey {
274 fn as_cache_key_ref(&self) -> CacheKeyRef {
275 CacheKeyRef {
276 text: &self.text,
277 font_size: self.font_size,
278 runs: self.runs.as_slice(),
279 wrap_width: self.wrap_width,
280 }
281 }
282}
283
284impl PartialEq for CacheKey {
285 fn eq(&self, other: &Self) -> bool {
286 self.as_cache_key_ref().eq(&other.as_cache_key_ref())
287 }
288}
289
290impl Hash for CacheKey {
291 fn hash<H: Hasher>(&self, state: &mut H) {
292 self.as_cache_key_ref().hash(state);
293 }
294}
295
296impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for CacheKey {
297 fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
298 self as &dyn AsCacheKeyRef
299 }
300}
301
302impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
303 fn as_cache_key_ref(&self) -> CacheKeyRef {
304 *self
305 }
306}