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