line_layout.rs

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