1use crate::{point, 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 /// The corresponding Font at the given index
107 pub fn font_id_for_index(&self, index: usize) -> Option<FontId> {
108 for run in &self.runs {
109 for glyph in &run.glyphs {
110 if glyph.index >= index {
111 return Some(run.font_id);
112 }
113 }
114 }
115 None
116 }
117
118 fn compute_wrap_boundaries(
119 &self,
120 text: &str,
121 wrap_width: Pixels,
122 ) -> SmallVec<[WrapBoundary; 1]> {
123 let mut boundaries = SmallVec::new();
124
125 let mut first_non_whitespace_ix = None;
126 let mut last_candidate_ix = None;
127 let mut last_candidate_x = px(0.);
128 let mut last_boundary = WrapBoundary {
129 run_ix: 0,
130 glyph_ix: 0,
131 };
132 let mut last_boundary_x = px(0.);
133 let mut prev_ch = '\0';
134 let mut glyphs = self
135 .runs
136 .iter()
137 .enumerate()
138 .flat_map(move |(run_ix, run)| {
139 run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
140 let character = text[glyph.index..].chars().next().unwrap();
141 (
142 WrapBoundary { run_ix, glyph_ix },
143 character,
144 glyph.position.x,
145 )
146 })
147 })
148 .peekable();
149
150 while let Some((boundary, ch, x)) = glyphs.next() {
151 if ch == '\n' {
152 continue;
153 }
154
155 if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
156 last_candidate_ix = Some(boundary);
157 last_candidate_x = x;
158 }
159
160 if ch != ' ' && first_non_whitespace_ix.is_none() {
161 first_non_whitespace_ix = Some(boundary);
162 }
163
164 let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
165 let width = next_x - last_boundary_x;
166 if width > wrap_width && boundary > last_boundary {
167 if let Some(last_candidate_ix) = last_candidate_ix.take() {
168 last_boundary = last_candidate_ix;
169 last_boundary_x = last_candidate_x;
170 } else {
171 last_boundary = boundary;
172 last_boundary_x = x;
173 }
174
175 boundaries.push(last_boundary);
176 }
177 prev_ch = ch;
178 }
179
180 boundaries
181 }
182}
183
184/// A line of text that has been wrapped to fit a given width
185#[derive(Default, Debug)]
186pub struct WrappedLineLayout {
187 /// The line layout, pre-wrapping.
188 pub unwrapped_layout: Arc<LineLayout>,
189
190 /// The boundaries at which the line was wrapped
191 pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
192
193 /// The width of the line, if it was wrapped
194 pub wrap_width: Option<Pixels>,
195}
196
197/// A boundary at which a line was wrapped
198#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
199pub struct WrapBoundary {
200 /// The index in the run just before the line was wrapped
201 pub run_ix: usize,
202 /// The index of the glyph just before the line was wrapped
203 pub glyph_ix: usize,
204}
205
206impl WrappedLineLayout {
207 /// The length of the underlying text, in utf8 bytes.
208 #[allow(clippy::len_without_is_empty)]
209 pub fn len(&self) -> usize {
210 self.unwrapped_layout.len
211 }
212
213 /// The width of this line, in pixels, whether or not it was wrapped.
214 pub fn width(&self) -> Pixels {
215 self.wrap_width
216 .unwrap_or(Pixels::MAX)
217 .min(self.unwrapped_layout.width)
218 }
219
220 /// The size of the whole wrapped text, for the given line_height.
221 /// can span multiple lines if there are multiple wrap boundaries.
222 pub fn size(&self, line_height: Pixels) -> Size<Pixels> {
223 Size {
224 width: self.width(),
225 height: line_height * (self.wrap_boundaries.len() + 1),
226 }
227 }
228
229 /// The ascent of a line in this layout
230 pub fn ascent(&self) -> Pixels {
231 self.unwrapped_layout.ascent
232 }
233
234 /// The descent of a line in this layout
235 pub fn descent(&self) -> Pixels {
236 self.unwrapped_layout.descent
237 }
238
239 /// The wrap boundaries in this layout
240 pub fn wrap_boundaries(&self) -> &[WrapBoundary] {
241 &self.wrap_boundaries
242 }
243
244 /// The font size of this layout
245 pub fn font_size(&self) -> Pixels {
246 self.unwrapped_layout.font_size
247 }
248
249 /// The runs in this layout, sans wrapping
250 pub fn runs(&self) -> &[ShapedRun] {
251 &self.unwrapped_layout.runs
252 }
253
254 /// The index corresponding to a given position in this layout for the given line height.
255 pub fn index_for_position(
256 &self,
257 mut position: Point<Pixels>,
258 line_height: Pixels,
259 ) -> Result<usize, usize> {
260 let wrapped_line_ix = (position.y / line_height) as usize;
261
262 let wrapped_line_start_index;
263 let wrapped_line_start_x;
264 if wrapped_line_ix > 0 {
265 let Some(line_start_boundary) = self.wrap_boundaries.get(wrapped_line_ix - 1) else {
266 return Err(0);
267 };
268 let run = &self.unwrapped_layout.runs[line_start_boundary.run_ix];
269 let glyph = &run.glyphs[line_start_boundary.glyph_ix];
270 wrapped_line_start_index = glyph.index;
271 wrapped_line_start_x = glyph.position.x;
272 } else {
273 wrapped_line_start_index = 0;
274 wrapped_line_start_x = Pixels::ZERO;
275 };
276
277 let wrapped_line_end_index;
278 let wrapped_line_end_x;
279 if wrapped_line_ix < self.wrap_boundaries.len() {
280 let next_wrap_boundary_ix = wrapped_line_ix;
281 let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix];
282 let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix];
283 let glyph = &run.glyphs[next_wrap_boundary.glyph_ix];
284 wrapped_line_end_index = glyph.index;
285 wrapped_line_end_x = glyph.position.x;
286 } else {
287 wrapped_line_end_index = self.unwrapped_layout.len;
288 wrapped_line_end_x = self.unwrapped_layout.width;
289 };
290
291 let mut position_in_unwrapped_line = position;
292 position_in_unwrapped_line.x += wrapped_line_start_x;
293 if position_in_unwrapped_line.x < wrapped_line_start_x {
294 Err(wrapped_line_start_index)
295 } else if position_in_unwrapped_line.x >= wrapped_line_end_x {
296 Err(wrapped_line_end_index)
297 } else {
298 Ok(self
299 .unwrapped_layout
300 .index_for_x(position_in_unwrapped_line.x)
301 .unwrap())
302 }
303 }
304
305 /// Returns the pixel position for the given byte index.
306 pub fn position_for_index(&self, index: usize, line_height: Pixels) -> Option<Point<Pixels>> {
307 let mut line_start_ix = 0;
308 let mut line_end_indices = self
309 .wrap_boundaries
310 .iter()
311 .map(|wrap_boundary| {
312 let run = &self.unwrapped_layout.runs[wrap_boundary.run_ix];
313 let glyph = &run.glyphs[wrap_boundary.glyph_ix];
314 glyph.index
315 })
316 .chain([self.len()])
317 .enumerate();
318 for (ix, line_end_ix) in line_end_indices {
319 let line_y = ix as f32 * line_height;
320 if index < line_start_ix {
321 break;
322 } else if index > line_end_ix {
323 line_start_ix = line_end_ix;
324 continue;
325 } else {
326 let line_start_x = self.unwrapped_layout.x_for_index(line_start_ix);
327 let x = self.unwrapped_layout.x_for_index(index) - line_start_x;
328 return Some(point(x, line_y));
329 }
330 }
331
332 None
333 }
334}
335
336pub(crate) struct LineLayoutCache {
337 previous_frame: Mutex<FrameCache>,
338 current_frame: RwLock<FrameCache>,
339 platform_text_system: Arc<dyn PlatformTextSystem>,
340}
341
342#[derive(Default)]
343struct FrameCache {
344 lines: FxHashMap<Arc<CacheKey>, Arc<LineLayout>>,
345 wrapped_lines: FxHashMap<Arc<CacheKey>, Arc<WrappedLineLayout>>,
346 used_lines: Vec<Arc<CacheKey>>,
347 used_wrapped_lines: Vec<Arc<CacheKey>>,
348}
349
350#[derive(Clone, Default)]
351pub(crate) struct LineLayoutIndex {
352 lines_index: usize,
353 wrapped_lines_index: usize,
354}
355
356impl LineLayoutCache {
357 pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
358 Self {
359 previous_frame: Mutex::default(),
360 current_frame: RwLock::default(),
361 platform_text_system,
362 }
363 }
364
365 pub fn layout_index(&self) -> LineLayoutIndex {
366 let frame = self.current_frame.read();
367 LineLayoutIndex {
368 lines_index: frame.used_lines.len(),
369 wrapped_lines_index: frame.used_wrapped_lines.len(),
370 }
371 }
372
373 pub fn reuse_layouts(&self, range: Range<LineLayoutIndex>) {
374 let mut previous_frame = &mut *self.previous_frame.lock();
375 let mut current_frame = &mut *self.current_frame.write();
376
377 for key in &previous_frame.used_lines[range.start.lines_index..range.end.lines_index] {
378 if let Some((key, line)) = previous_frame.lines.remove_entry(key) {
379 current_frame.lines.insert(key, line);
380 }
381 current_frame.used_lines.push(key.clone());
382 }
383
384 for key in &previous_frame.used_wrapped_lines
385 [range.start.wrapped_lines_index..range.end.wrapped_lines_index]
386 {
387 if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) {
388 current_frame.wrapped_lines.insert(key, line);
389 }
390 current_frame.used_wrapped_lines.push(key.clone());
391 }
392 }
393
394 pub fn truncate_layouts(&self, index: LineLayoutIndex) {
395 let mut current_frame = &mut *self.current_frame.write();
396 current_frame.used_lines.truncate(index.lines_index);
397 current_frame
398 .used_wrapped_lines
399 .truncate(index.wrapped_lines_index);
400 }
401
402 pub fn finish_frame(&self) {
403 let mut prev_frame = self.previous_frame.lock();
404 let mut curr_frame = self.current_frame.write();
405 std::mem::swap(&mut *prev_frame, &mut *curr_frame);
406 curr_frame.lines.clear();
407 curr_frame.wrapped_lines.clear();
408 curr_frame.used_lines.clear();
409 curr_frame.used_wrapped_lines.clear();
410 }
411
412 pub fn layout_wrapped_line(
413 &self,
414 text: &str,
415 font_size: Pixels,
416 runs: &[FontRun],
417 wrap_width: Option<Pixels>,
418 ) -> Arc<WrappedLineLayout> {
419 let key = &CacheKeyRef {
420 text,
421 font_size,
422 runs,
423 wrap_width,
424 } as &dyn AsCacheKeyRef;
425
426 let current_frame = self.current_frame.upgradable_read();
427 if let Some(layout) = current_frame.wrapped_lines.get(key) {
428 return layout.clone();
429 }
430
431 let previous_frame_entry = self.previous_frame.lock().wrapped_lines.remove_entry(key);
432 if let Some((key, layout)) = previous_frame_entry {
433 let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
434 current_frame
435 .wrapped_lines
436 .insert(key.clone(), layout.clone());
437 current_frame.used_wrapped_lines.push(key);
438 layout
439 } else {
440 drop(current_frame);
441
442 let unwrapped_layout = self.layout_line(text, font_size, runs);
443 let wrap_boundaries = if let Some(wrap_width) = wrap_width {
444 unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width)
445 } else {
446 SmallVec::new()
447 };
448 let layout = Arc::new(WrappedLineLayout {
449 unwrapped_layout,
450 wrap_boundaries,
451 wrap_width,
452 });
453 let key = Arc::new(CacheKey {
454 text: text.into(),
455 font_size,
456 runs: SmallVec::from(runs),
457 wrap_width,
458 });
459
460 let mut current_frame = self.current_frame.write();
461 current_frame
462 .wrapped_lines
463 .insert(key.clone(), layout.clone());
464 current_frame.used_wrapped_lines.push(key);
465
466 layout
467 }
468 }
469
470 pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> {
471 let key = &CacheKeyRef {
472 text,
473 font_size,
474 runs,
475 wrap_width: None,
476 } as &dyn AsCacheKeyRef;
477
478 let current_frame = self.current_frame.upgradable_read();
479 if let Some(layout) = current_frame.lines.get(key) {
480 return layout.clone();
481 }
482
483 let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
484 if let Some((key, layout)) = self.previous_frame.lock().lines.remove_entry(key) {
485 current_frame.lines.insert(key.clone(), layout.clone());
486 current_frame.used_lines.push(key);
487 layout
488 } else {
489 let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
490 let key = Arc::new(CacheKey {
491 text: text.into(),
492 font_size,
493 runs: SmallVec::from(runs),
494 wrap_width: None,
495 });
496 current_frame.lines.insert(key.clone(), layout.clone());
497 current_frame.used_lines.push(key);
498 layout
499 }
500 }
501}
502
503/// A run of text with a single font.
504#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
505pub struct FontRun {
506 pub(crate) len: usize,
507 pub(crate) font_id: FontId,
508}
509
510trait AsCacheKeyRef {
511 fn as_cache_key_ref(&self) -> CacheKeyRef;
512}
513
514#[derive(Clone, Debug, Eq)]
515struct CacheKey {
516 text: String,
517 font_size: Pixels,
518 runs: SmallVec<[FontRun; 1]>,
519 wrap_width: Option<Pixels>,
520}
521
522#[derive(Copy, Clone, PartialEq, Eq, Hash)]
523struct CacheKeyRef<'a> {
524 text: &'a str,
525 font_size: Pixels,
526 runs: &'a [FontRun],
527 wrap_width: Option<Pixels>,
528}
529
530impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
531 fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
532 self.as_cache_key_ref() == other.as_cache_key_ref()
533 }
534}
535
536impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
537
538impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
539 fn hash<H: Hasher>(&self, state: &mut H) {
540 self.as_cache_key_ref().hash(state)
541 }
542}
543
544impl AsCacheKeyRef for CacheKey {
545 fn as_cache_key_ref(&self) -> CacheKeyRef {
546 CacheKeyRef {
547 text: &self.text,
548 font_size: self.font_size,
549 runs: self.runs.as_slice(),
550 wrap_width: self.wrap_width,
551 }
552 }
553}
554
555impl PartialEq for CacheKey {
556 fn eq(&self, other: &Self) -> bool {
557 self.as_cache_key_ref().eq(&other.as_cache_key_ref())
558 }
559}
560
561impl Hash for CacheKey {
562 fn hash<H: Hasher>(&self, state: &mut H) {
563 self.as_cache_key_ref().hash(state);
564 }
565}
566
567impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for Arc<CacheKey> {
568 fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
569 self.as_ref() as &dyn AsCacheKeyRef
570 }
571}
572
573impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
574 fn as_cache_key_ref(&self) -> CacheKeyRef {
575 *self
576 }
577}