1use crate::{px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};
2use collections::FxHashMap;
3use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
4use smallvec::SmallVec;
5use std::{
6 borrow::Borrow,
7 hash::{Hash, Hasher},
8 ops::Range,
9 sync::Arc,
10};
11
12/// A laid out and styled line of text
13#[derive(Default, Debug)]
14pub struct LineLayout {
15 /// The font size for this line
16 pub font_size: Pixels,
17 /// The width of the line
18 pub width: Pixels,
19 /// The ascent of the line
20 pub ascent: Pixels,
21 /// The descent of the line
22 pub descent: Pixels,
23 /// The shaped runs that make up this line
24 pub runs: Vec<ShapedRun>,
25 /// The length of the line in utf-8 bytes
26 pub len: usize,
27}
28
29/// A run of text that has been shaped .
30#[derive(Debug)]
31pub struct ShapedRun {
32 /// The font id for this run
33 pub font_id: FontId,
34 /// The glyphs that make up this run
35 pub glyphs: SmallVec<[ShapedGlyph; 8]>,
36}
37
38/// A single glyph, ready to paint.
39#[derive(Clone, Debug)]
40pub struct ShapedGlyph {
41 /// The ID for this glyph, as determined by the text system.
42 pub id: GlyphId,
43
44 /// The position of this glyph in its containing line.
45 pub position: Point<Pixels>,
46
47 /// The index of this glyph in the original text.
48 pub index: usize,
49
50 /// Whether this glyph is an emoji
51 pub is_emoji: bool,
52}
53
54impl LineLayout {
55 /// The index for the character at the given x coordinate
56 pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
57 if x >= self.width {
58 None
59 } else {
60 for run in self.runs.iter().rev() {
61 for glyph in run.glyphs.iter().rev() {
62 if glyph.position.x <= x {
63 return Some(glyph.index);
64 }
65 }
66 }
67 Some(0)
68 }
69 }
70
71 /// closest_index_for_x returns the character boundary closest to the given x coordinate
72 /// (e.g. to handle aligning up/down arrow keys)
73 pub fn closest_index_for_x(&self, x: Pixels) -> usize {
74 let mut prev_index = 0;
75 let mut prev_x = px(0.);
76
77 for run in self.runs.iter() {
78 for glyph in run.glyphs.iter() {
79 if glyph.position.x >= x {
80 if glyph.position.x - x < x - prev_x {
81 return glyph.index;
82 } else {
83 return prev_index;
84 }
85 }
86 prev_index = glyph.index;
87 prev_x = glyph.position.x;
88 }
89 }
90
91 self.len
92 }
93
94 /// The x position of the character at the given index
95 pub fn x_for_index(&self, index: usize) -> Pixels {
96 for run in &self.runs {
97 for glyph in &run.glyphs {
98 if glyph.index >= index {
99 return glyph.position.x;
100 }
101 }
102 }
103 self.width
104 }
105
106 fn compute_wrap_boundaries(
107 &self,
108 text: &str,
109 wrap_width: Pixels,
110 ) -> SmallVec<[WrapBoundary; 1]> {
111 let mut boundaries = SmallVec::new();
112
113 let mut first_non_whitespace_ix = None;
114 let mut last_candidate_ix = None;
115 let mut last_candidate_x = px(0.);
116 let mut last_boundary = WrapBoundary {
117 run_ix: 0,
118 glyph_ix: 0,
119 };
120 let mut last_boundary_x = px(0.);
121 let mut prev_ch = '\0';
122 let mut glyphs = self
123 .runs
124 .iter()
125 .enumerate()
126 .flat_map(move |(run_ix, run)| {
127 run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
128 let character = text[glyph.index..].chars().next().unwrap();
129 (
130 WrapBoundary { run_ix, glyph_ix },
131 character,
132 glyph.position.x,
133 )
134 })
135 })
136 .peekable();
137
138 while let Some((boundary, ch, x)) = glyphs.next() {
139 if ch == '\n' {
140 continue;
141 }
142
143 if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
144 last_candidate_ix = Some(boundary);
145 last_candidate_x = x;
146 }
147
148 if ch != ' ' && first_non_whitespace_ix.is_none() {
149 first_non_whitespace_ix = Some(boundary);
150 }
151
152 let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
153 let width = next_x - last_boundary_x;
154 if width > wrap_width && boundary > last_boundary {
155 if let Some(last_candidate_ix) = last_candidate_ix.take() {
156 last_boundary = last_candidate_ix;
157 last_boundary_x = last_candidate_x;
158 } else {
159 last_boundary = boundary;
160 last_boundary_x = x;
161 }
162
163 boundaries.push(last_boundary);
164 }
165 prev_ch = ch;
166 }
167
168 boundaries
169 }
170}
171
172/// A line of text that has been wrapped to fit a given width
173#[derive(Default, Debug)]
174pub struct WrappedLineLayout {
175 /// The line layout, pre-wrapping.
176 pub unwrapped_layout: Arc<LineLayout>,
177
178 /// The boundaries at which the line was wrapped
179 pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
180
181 /// The width of the line, if it was wrapped
182 pub wrap_width: Option<Pixels>,
183}
184
185/// A boundary at which a line was wrapped
186#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
187pub struct WrapBoundary {
188 /// The index in the run just before the line was wrapped
189 pub run_ix: usize,
190 /// The index of the glyph just before the line was wrapped
191 pub glyph_ix: usize,
192}
193
194impl WrappedLineLayout {
195 /// The length of the underlying text, in utf8 bytes.
196 #[allow(clippy::len_without_is_empty)]
197 pub fn len(&self) -> usize {
198 self.unwrapped_layout.len
199 }
200
201 /// The width of this line, in pixels, whether or not it was wrapped.
202 pub fn width(&self) -> Pixels {
203 self.wrap_width
204 .unwrap_or(Pixels::MAX)
205 .min(self.unwrapped_layout.width)
206 }
207
208 /// The size of the whole wrapped text, for the given line_height.
209 /// can span multiple lines if there are multiple wrap boundaries.
210 pub fn size(&self, line_height: Pixels) -> Size<Pixels> {
211 Size {
212 width: self.width(),
213 height: line_height * (self.wrap_boundaries.len() + 1),
214 }
215 }
216
217 /// The ascent of a line in this layout
218 pub fn ascent(&self) -> Pixels {
219 self.unwrapped_layout.ascent
220 }
221
222 /// The descent of a line in this layout
223 pub fn descent(&self) -> Pixels {
224 self.unwrapped_layout.descent
225 }
226
227 /// The wrap boundaries in this layout
228 pub fn wrap_boundaries(&self) -> &[WrapBoundary] {
229 &self.wrap_boundaries
230 }
231
232 /// The font size of this layout
233 pub fn font_size(&self) -> Pixels {
234 self.unwrapped_layout.font_size
235 }
236
237 /// The runs in this layout, sans wrapping
238 pub fn runs(&self) -> &[ShapedRun] {
239 &self.unwrapped_layout.runs
240 }
241
242 /// The index corresponding to a given position in this layout for the given line height.
243 pub fn index_for_position(
244 &self,
245 position: Point<Pixels>,
246 line_height: Pixels,
247 ) -> Option<usize> {
248 let wrapped_line_ix = (position.y / line_height) as usize;
249
250 let wrapped_line_start_x = if wrapped_line_ix > 0 {
251 let Some(line_start_boundary) = self.wrap_boundaries.get(wrapped_line_ix - 1) else {
252 return None;
253 };
254 let run = &self.unwrapped_layout.runs[line_start_boundary.run_ix];
255 run.glyphs[line_start_boundary.glyph_ix].position.x
256 } else {
257 Pixels::ZERO
258 };
259
260 let wrapped_line_end_x = if wrapped_line_ix < self.wrap_boundaries.len() {
261 let next_wrap_boundary_ix = wrapped_line_ix;
262 let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix];
263 let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix];
264 run.glyphs[next_wrap_boundary.glyph_ix].position.x
265 } else {
266 self.unwrapped_layout.width
267 };
268
269 let mut position_in_unwrapped_line = position;
270 position_in_unwrapped_line.x += wrapped_line_start_x;
271 if position_in_unwrapped_line.x > wrapped_line_end_x {
272 None
273 } else {
274 self.unwrapped_layout
275 .index_for_x(position_in_unwrapped_line.x)
276 }
277 }
278}
279
280pub(crate) struct LineLayoutCache {
281 previous_frame: Mutex<FrameCache>,
282 current_frame: RwLock<FrameCache>,
283 platform_text_system: Arc<dyn PlatformTextSystem>,
284}
285
286#[derive(Default)]
287struct FrameCache {
288 lines: FxHashMap<Arc<CacheKey>, Arc<LineLayout>>,
289 wrapped_lines: FxHashMap<Arc<CacheKey>, Arc<WrappedLineLayout>>,
290 used_lines: Vec<Arc<CacheKey>>,
291 used_wrapped_lines: Vec<Arc<CacheKey>>,
292}
293
294#[derive(Clone, Default)]
295pub(crate) struct LineLayoutIndex {
296 lines_index: usize,
297 wrapped_lines_index: usize,
298}
299
300impl LineLayoutCache {
301 pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
302 Self {
303 previous_frame: Mutex::default(),
304 current_frame: RwLock::default(),
305 platform_text_system,
306 }
307 }
308
309 pub fn layout_index(&self) -> LineLayoutIndex {
310 let frame = self.current_frame.read();
311 LineLayoutIndex {
312 lines_index: frame.used_lines.len(),
313 wrapped_lines_index: frame.used_wrapped_lines.len(),
314 }
315 }
316
317 pub fn reuse_layouts(&self, range: Range<LineLayoutIndex>) {
318 let mut previous_frame = &mut *self.previous_frame.lock();
319 let mut current_frame = &mut *self.current_frame.write();
320
321 for key in &previous_frame.used_lines[range.start.lines_index..range.end.lines_index] {
322 if let Some((key, line)) = previous_frame.lines.remove_entry(key) {
323 current_frame.lines.insert(key, line);
324 }
325 current_frame.used_lines.push(key.clone());
326 }
327
328 for key in &previous_frame.used_wrapped_lines
329 [range.start.wrapped_lines_index..range.end.wrapped_lines_index]
330 {
331 if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) {
332 current_frame.wrapped_lines.insert(key, line);
333 }
334 current_frame.used_wrapped_lines.push(key.clone());
335 }
336 }
337
338 pub fn finish_frame(&self) {
339 let mut prev_frame = self.previous_frame.lock();
340 let mut curr_frame = self.current_frame.write();
341 std::mem::swap(&mut *prev_frame, &mut *curr_frame);
342 curr_frame.lines.clear();
343 curr_frame.wrapped_lines.clear();
344 curr_frame.used_lines.clear();
345 curr_frame.used_wrapped_lines.clear();
346 }
347
348 pub fn layout_wrapped_line(
349 &self,
350 text: &str,
351 font_size: Pixels,
352 runs: &[FontRun],
353 wrap_width: Option<Pixels>,
354 ) -> Arc<WrappedLineLayout> {
355 let key = &CacheKeyRef {
356 text,
357 font_size,
358 runs,
359 wrap_width,
360 } as &dyn AsCacheKeyRef;
361
362 let current_frame = self.current_frame.upgradable_read();
363 if let Some(layout) = current_frame.wrapped_lines.get(key) {
364 return layout.clone();
365 }
366
367 let previous_frame_entry = self.previous_frame.lock().wrapped_lines.remove_entry(key);
368 if let Some((key, layout)) = previous_frame_entry {
369 let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
370 current_frame
371 .wrapped_lines
372 .insert(key.clone(), layout.clone());
373 current_frame.used_wrapped_lines.push(key);
374 layout
375 } else {
376 drop(current_frame);
377
378 let unwrapped_layout = self.layout_line(text, font_size, runs);
379 let wrap_boundaries = if let Some(wrap_width) = wrap_width {
380 unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width)
381 } else {
382 SmallVec::new()
383 };
384 let layout = Arc::new(WrappedLineLayout {
385 unwrapped_layout,
386 wrap_boundaries,
387 wrap_width,
388 });
389 let key = Arc::new(CacheKey {
390 text: text.into(),
391 font_size,
392 runs: SmallVec::from(runs),
393 wrap_width,
394 });
395
396 let mut current_frame = self.current_frame.write();
397 current_frame
398 .wrapped_lines
399 .insert(key.clone(), layout.clone());
400 current_frame.used_wrapped_lines.push(key);
401
402 layout
403 }
404 }
405
406 pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> {
407 let key = &CacheKeyRef {
408 text,
409 font_size,
410 runs,
411 wrap_width: None,
412 } as &dyn AsCacheKeyRef;
413
414 let current_frame = self.current_frame.upgradable_read();
415 if let Some(layout) = current_frame.lines.get(key) {
416 return layout.clone();
417 }
418
419 let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
420 if let Some((key, layout)) = self.previous_frame.lock().lines.remove_entry(key) {
421 current_frame.lines.insert(key.clone(), layout.clone());
422 current_frame.used_lines.push(key);
423 layout
424 } else {
425 let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
426 let key = Arc::new(CacheKey {
427 text: text.into(),
428 font_size,
429 runs: SmallVec::from(runs),
430 wrap_width: None,
431 });
432 current_frame.lines.insert(key.clone(), layout.clone());
433 current_frame.used_lines.push(key);
434 layout
435 }
436 }
437}
438
439/// A run of text with a single font.
440#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
441pub struct FontRun {
442 pub(crate) len: usize,
443 pub(crate) font_id: FontId,
444}
445
446trait AsCacheKeyRef {
447 fn as_cache_key_ref(&self) -> CacheKeyRef;
448}
449
450#[derive(Clone, Debug, Eq)]
451struct CacheKey {
452 text: String,
453 font_size: Pixels,
454 runs: SmallVec<[FontRun; 1]>,
455 wrap_width: Option<Pixels>,
456}
457
458#[derive(Copy, Clone, PartialEq, Eq, Hash)]
459struct CacheKeyRef<'a> {
460 text: &'a str,
461 font_size: Pixels,
462 runs: &'a [FontRun],
463 wrap_width: Option<Pixels>,
464}
465
466impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
467 fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
468 self.as_cache_key_ref() == other.as_cache_key_ref()
469 }
470}
471
472impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
473
474impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
475 fn hash<H: Hasher>(&self, state: &mut H) {
476 self.as_cache_key_ref().hash(state)
477 }
478}
479
480impl AsCacheKeyRef for CacheKey {
481 fn as_cache_key_ref(&self) -> CacheKeyRef {
482 CacheKeyRef {
483 text: &self.text,
484 font_size: self.font_size,
485 runs: self.runs.as_slice(),
486 wrap_width: self.wrap_width,
487 }
488 }
489}
490
491impl PartialEq for CacheKey {
492 fn eq(&self, other: &Self) -> bool {
493 self.as_cache_key_ref().eq(&other.as_cache_key_ref())
494 }
495}
496
497impl Hash for CacheKey {
498 fn hash<H: Hasher>(&self, state: &mut H) {
499 self.as_cache_key_ref().hash(state);
500 }
501}
502
503impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for Arc<CacheKey> {
504 fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
505 self.as_ref() as &dyn AsCacheKeyRef
506 }
507}
508
509impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
510 fn as_cache_key_ref(&self) -> CacheKeyRef {
511 *self
512 }
513}