line_layout.rs

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