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