line_layout.rs

  1use crate::{px, EntityId, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};
  2use collections::{HashMap, HashSet};
  3use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
  4use smallvec::SmallVec;
  5use std::{
  6    borrow::Borrow,
  7    hash::{Hash, Hasher},
  8    sync::Arc,
  9};
 10
 11/// A laid out and styled line of text
 12#[derive(Default, Debug)]
 13pub struct LineLayout {
 14    /// The font size for this line
 15    pub font_size: Pixels,
 16    /// The width of the line
 17    pub width: Pixels,
 18    /// The ascent of the line
 19    pub ascent: Pixels,
 20    /// The descent of the line
 21    pub descent: Pixels,
 22    /// The shaped runs that make up this line
 23    pub runs: Vec<ShapedRun>,
 24    /// The length of the line in utf-8 bytes
 25    pub len: usize,
 26}
 27
 28/// A run of text that has been shaped .
 29#[derive(Debug)]
 30pub struct ShapedRun {
 31    /// The font id for this run
 32    pub font_id: FontId,
 33    /// The glyphs that make up this run
 34    pub glyphs: SmallVec<[ShapedGlyph; 8]>,
 35}
 36
 37/// A single glyph, ready to paint.
 38#[derive(Clone, Debug)]
 39pub struct ShapedGlyph {
 40    /// The ID for this glyph, as determined by the text system.
 41    pub id: GlyphId,
 42
 43    /// The position of this glyph in it's containing line.
 44    pub position: Point<Pixels>,
 45
 46    /// The index of this glyph in the original text.
 47    pub index: usize,
 48
 49    /// Whether this glyph is an emoji
 50    pub is_emoji: bool,
 51}
 52
 53impl LineLayout {
 54    /// The index for the character at the given x coordinate
 55    pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
 56        if x >= self.width {
 57            None
 58        } else {
 59            for run in self.runs.iter().rev() {
 60                for glyph in run.glyphs.iter().rev() {
 61                    if glyph.position.x <= x {
 62                        return Some(glyph.index);
 63                    }
 64                }
 65            }
 66            Some(0)
 67        }
 68    }
 69
 70    /// closest_index_for_x returns the character boundary closest to the given x coordinate
 71    /// (e.g. to handle aligning up/down arrow keys)
 72    pub fn closest_index_for_x(&self, x: Pixels) -> usize {
 73        let mut prev_index = 0;
 74        let mut prev_x = px(0.);
 75
 76        for run in self.runs.iter() {
 77            for glyph in run.glyphs.iter() {
 78                if glyph.position.x >= x {
 79                    if glyph.position.x - x < x - prev_x {
 80                        return glyph.index;
 81                    } else {
 82                        return prev_index;
 83                    }
 84                }
 85                prev_index = glyph.index;
 86                prev_x = glyph.position.x;
 87            }
 88        }
 89
 90        self.len
 91    }
 92
 93    /// The x position of the character at the given index
 94    pub fn x_for_index(&self, index: usize) -> Pixels {
 95        for run in &self.runs {
 96            for glyph in &run.glyphs {
 97                if glyph.index >= index {
 98                    return glyph.position.x;
 99                }
100            }
101        }
102        self.width
103    }
104
105    fn compute_wrap_boundaries(
106        &self,
107        text: &str,
108        wrap_width: Pixels,
109    ) -> SmallVec<[WrapBoundary; 1]> {
110        let mut boundaries = SmallVec::new();
111
112        let mut first_non_whitespace_ix = None;
113        let mut last_candidate_ix = None;
114        let mut last_candidate_x = px(0.);
115        let mut last_boundary = WrapBoundary {
116            run_ix: 0,
117            glyph_ix: 0,
118        };
119        let mut last_boundary_x = px(0.);
120        let mut prev_ch = '\0';
121        let mut glyphs = self
122            .runs
123            .iter()
124            .enumerate()
125            .flat_map(move |(run_ix, run)| {
126                run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
127                    let character = text[glyph.index..].chars().next().unwrap();
128                    (
129                        WrapBoundary { run_ix, glyph_ix },
130                        character,
131                        glyph.position.x,
132                    )
133                })
134            })
135            .peekable();
136
137        while let Some((boundary, ch, x)) = glyphs.next() {
138            if ch == '\n' {
139                continue;
140            }
141
142            if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
143                last_candidate_ix = Some(boundary);
144                last_candidate_x = x;
145            }
146
147            if ch != ' ' && first_non_whitespace_ix.is_none() {
148                first_non_whitespace_ix = Some(boundary);
149            }
150
151            let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
152            let width = next_x - last_boundary_x;
153            if width > wrap_width && boundary > last_boundary {
154                if let Some(last_candidate_ix) = last_candidate_ix.take() {
155                    last_boundary = last_candidate_ix;
156                    last_boundary_x = last_candidate_x;
157                } else {
158                    last_boundary = boundary;
159                    last_boundary_x = x;
160                }
161
162                boundaries.push(last_boundary);
163            }
164            prev_ch = ch;
165        }
166
167        boundaries
168    }
169}
170
171/// A line of text that has been wrapped to fit a given width
172#[derive(Default, Debug)]
173pub struct WrappedLineLayout {
174    /// The line layout, pre-wrapping.
175    pub unwrapped_layout: Arc<LineLayout>,
176
177    /// The boundaries at which the line was wrapped
178    pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
179
180    /// The width of the line, if it was wrapped
181    pub wrap_width: Option<Pixels>,
182}
183
184/// A boundary at which a line was wrapped
185#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
186pub struct WrapBoundary {
187    /// The index in the run just before the line was wrapped
188    pub run_ix: usize,
189    /// The index of the glyph just before the line was wrapped
190    pub glyph_ix: usize,
191}
192
193impl WrappedLineLayout {
194    /// The length of the underlying text, in utf8 bytes.
195    #[allow(clippy::len_without_is_empty)]
196    pub fn len(&self) -> usize {
197        self.unwrapped_layout.len
198    }
199
200    /// The width of this line, in pixels, whether or not it was wrapped.
201    pub fn width(&self) -> Pixels {
202        self.wrap_width
203            .unwrap_or(Pixels::MAX)
204            .min(self.unwrapped_layout.width)
205    }
206
207    /// The size of the whole wrapped text, for the given line_height.
208    /// can span multiple lines if there are multiple wrap boundaries.
209    pub fn size(&self, line_height: Pixels) -> Size<Pixels> {
210        Size {
211            width: self.width(),
212            height: line_height * (self.wrap_boundaries.len() + 1),
213        }
214    }
215
216    /// The ascent of a line in this layout
217    pub fn ascent(&self) -> Pixels {
218        self.unwrapped_layout.ascent
219    }
220
221    /// The descent of a line in this layout
222    pub fn descent(&self) -> Pixels {
223        self.unwrapped_layout.descent
224    }
225
226    /// The wrap boundaries in this layout
227    pub fn wrap_boundaries(&self) -> &[WrapBoundary] {
228        &self.wrap_boundaries
229    }
230
231    /// The font size of this layout
232    pub fn font_size(&self) -> Pixels {
233        self.unwrapped_layout.font_size
234    }
235
236    /// The runs in this layout, sans wrapping
237    pub fn runs(&self) -> &[ShapedRun] {
238        &self.unwrapped_layout.runs
239    }
240
241    /// The index corresponding to a given position in this layout for the given line height.
242    pub fn index_for_position(
243        &self,
244        position: Point<Pixels>,
245        line_height: Pixels,
246    ) -> Option<usize> {
247        let wrapped_line_ix = (position.y / line_height) as usize;
248
249        let wrapped_line_start_x = if wrapped_line_ix > 0 {
250            let wrap_boundary_ix = wrapped_line_ix - 1;
251            let wrap_boundary = self.wrap_boundaries[wrap_boundary_ix];
252            let run = &self.unwrapped_layout.runs[wrap_boundary.run_ix];
253            run.glyphs[wrap_boundary.glyph_ix].position.x
254        } else {
255            Pixels::ZERO
256        };
257
258        let wrapped_line_end_x = if wrapped_line_ix < self.wrap_boundaries.len() {
259            let next_wrap_boundary_ix = wrapped_line_ix;
260            let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix];
261            let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix];
262            run.glyphs[next_wrap_boundary.glyph_ix].position.x
263        } else {
264            self.unwrapped_layout.width
265        };
266
267        let mut position_in_unwrapped_line = position;
268        position_in_unwrapped_line.x += wrapped_line_start_x;
269        if position_in_unwrapped_line.x > wrapped_line_end_x {
270            None
271        } else {
272            self.unwrapped_layout
273                .index_for_x(position_in_unwrapped_line.x)
274        }
275    }
276}
277
278pub(crate) struct LineLayoutCache {
279    view_stack: Mutex<Vec<EntityId>>,
280    previous_frame: Mutex<HashMap<CacheKey, Arc<LineLayout>>>,
281    current_frame: RwLock<HashMap<CacheKey, Arc<LineLayout>>>,
282    previous_frame_wrapped: Mutex<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
283    current_frame_wrapped: RwLock<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
284    platform_text_system: Arc<dyn PlatformTextSystem>,
285}
286
287impl LineLayoutCache {
288    pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
289        Self {
290            view_stack: Mutex::default(),
291            previous_frame: Mutex::default(),
292            current_frame: RwLock::default(),
293            previous_frame_wrapped: Mutex::default(),
294            current_frame_wrapped: RwLock::default(),
295            platform_text_system,
296        }
297    }
298
299    pub fn finish_frame(&self, reused_views: &HashSet<EntityId>) {
300        debug_assert_eq!(self.view_stack.lock().len(), 0);
301
302        let mut prev_frame = self.previous_frame.lock();
303        let mut curr_frame = self.current_frame.write();
304        for (key, layout) in prev_frame.drain() {
305            if key
306                .parent_view_id
307                .map_or(false, |view_id| reused_views.contains(&view_id))
308            {
309                curr_frame.insert(key, layout);
310            }
311        }
312        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
313
314        let mut prev_frame_wrapped = self.previous_frame_wrapped.lock();
315        let mut curr_frame_wrapped = self.current_frame_wrapped.write();
316        for (key, layout) in prev_frame_wrapped.drain() {
317            if key
318                .parent_view_id
319                .map_or(false, |view_id| reused_views.contains(&view_id))
320            {
321                curr_frame_wrapped.insert(key, layout);
322            }
323        }
324        std::mem::swap(&mut *prev_frame_wrapped, &mut *curr_frame_wrapped);
325    }
326
327    pub fn with_view<R>(&self, view_id: EntityId, f: impl FnOnce() -> R) -> R {
328        self.view_stack.lock().push(view_id);
329        let result = f();
330        self.view_stack.lock().pop();
331        result
332    }
333
334    fn parent_view_id(&self) -> Option<EntityId> {
335        self.view_stack.lock().last().copied()
336    }
337
338    pub fn layout_wrapped_line(
339        &self,
340        text: &str,
341        font_size: Pixels,
342        runs: &[FontRun],
343        wrap_width: Option<Pixels>,
344    ) -> Arc<WrappedLineLayout> {
345        let key = &CacheKeyRef {
346            text,
347            font_size,
348            runs,
349            wrap_width,
350            parent_view_id: self.parent_view_id(),
351        } as &dyn AsCacheKeyRef;
352
353        let current_frame = self.current_frame_wrapped.upgradable_read();
354        if let Some(layout) = current_frame.get(key) {
355            return layout.clone();
356        }
357
358        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
359        if let Some((key, layout)) = self.previous_frame_wrapped.lock().remove_entry(key) {
360            current_frame.insert(key, layout.clone());
361            layout
362        } else {
363            let unwrapped_layout = self.layout_line(text, font_size, runs);
364            let wrap_boundaries = if let Some(wrap_width) = wrap_width {
365                unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width)
366            } else {
367                SmallVec::new()
368            };
369            let layout = Arc::new(WrappedLineLayout {
370                unwrapped_layout,
371                wrap_boundaries,
372                wrap_width,
373            });
374            let key = CacheKey {
375                text: text.into(),
376                font_size,
377                runs: SmallVec::from(runs),
378                wrap_width,
379                parent_view_id: self.parent_view_id(),
380            };
381            current_frame.insert(key, layout.clone());
382            layout
383        }
384    }
385
386    pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> {
387        let key = &CacheKeyRef {
388            text,
389            font_size,
390            runs,
391            wrap_width: None,
392            parent_view_id: self.parent_view_id(),
393        } as &dyn AsCacheKeyRef;
394
395        let current_frame = self.current_frame.upgradable_read();
396        if let Some(layout) = current_frame.get(key) {
397            return layout.clone();
398        }
399
400        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
401        if let Some((key, layout)) = self.previous_frame.lock().remove_entry(key) {
402            current_frame.insert(key, layout.clone());
403            layout
404        } else {
405            let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
406            let key = CacheKey {
407                text: text.into(),
408                font_size,
409                runs: SmallVec::from(runs),
410                wrap_width: None,
411                parent_view_id: self.parent_view_id(),
412            };
413            current_frame.insert(key, layout.clone());
414            layout
415        }
416    }
417}
418
419/// A run of text with a single font.
420#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
421pub struct FontRun {
422    pub(crate) len: usize,
423    pub(crate) font_id: FontId,
424}
425
426trait AsCacheKeyRef {
427    fn as_cache_key_ref(&self) -> CacheKeyRef;
428}
429
430#[derive(Debug, Eq)]
431struct CacheKey {
432    text: String,
433    font_size: Pixels,
434    runs: SmallVec<[FontRun; 1]>,
435    wrap_width: Option<Pixels>,
436    parent_view_id: Option<EntityId>,
437}
438
439#[derive(Copy, Clone, PartialEq, Eq, Hash)]
440struct CacheKeyRef<'a> {
441    text: &'a str,
442    font_size: Pixels,
443    runs: &'a [FontRun],
444    wrap_width: Option<Pixels>,
445    parent_view_id: Option<EntityId>,
446}
447
448impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
449    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
450        self.as_cache_key_ref() == other.as_cache_key_ref()
451    }
452}
453
454impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
455
456impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
457    fn hash<H: Hasher>(&self, state: &mut H) {
458        self.as_cache_key_ref().hash(state)
459    }
460}
461
462impl AsCacheKeyRef for CacheKey {
463    fn as_cache_key_ref(&self) -> CacheKeyRef {
464        CacheKeyRef {
465            text: &self.text,
466            font_size: self.font_size,
467            runs: self.runs.as_slice(),
468            wrap_width: self.wrap_width,
469            parent_view_id: self.parent_view_id,
470        }
471    }
472}
473
474impl PartialEq for CacheKey {
475    fn eq(&self, other: &Self) -> bool {
476        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
477    }
478}
479
480impl Hash for CacheKey {
481    fn hash<H: Hasher>(&self, state: &mut H) {
482        self.as_cache_key_ref().hash(state);
483    }
484}
485
486impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for CacheKey {
487    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
488        self as &dyn AsCacheKeyRef
489    }
490}
491
492impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
493    fn as_cache_key_ref(&self) -> CacheKeyRef {
494        *self
495    }
496}