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    /// 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}