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