line_layout.rs

  1use crate::{px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, SharedString};
  2use derive_more::{Deref, DerefMut};
  3use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
  4use smallvec::SmallVec;
  5use std::{
  6    borrow::Borrow,
  7    collections::HashMap,
  8    hash::{Hash, Hasher},
  9    sync::Arc,
 10};
 11
 12#[derive(Default, Debug)]
 13pub struct LineLayout {
 14    pub font_size: Pixels,
 15    pub width: Pixels,
 16    pub ascent: Pixels,
 17    pub descent: Pixels,
 18    pub runs: Vec<ShapedRun>,
 19    pub len: usize,
 20}
 21
 22#[derive(Debug)]
 23pub struct ShapedRun {
 24    pub font_id: FontId,
 25    pub glyphs: SmallVec<[ShapedGlyph; 8]>,
 26}
 27
 28#[derive(Clone, Debug)]
 29pub struct ShapedGlyph {
 30    pub id: GlyphId,
 31    pub position: Point<Pixels>,
 32    pub index: usize,
 33    pub is_emoji: bool,
 34}
 35
 36impl LineLayout {
 37    pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
 38        if x >= self.width {
 39            None
 40        } else {
 41            for run in self.runs.iter().rev() {
 42                for glyph in run.glyphs.iter().rev() {
 43                    if glyph.position.x <= x {
 44                        return Some(glyph.index);
 45                    }
 46                }
 47            }
 48            Some(0)
 49        }
 50    }
 51
 52    /// closest_index_for_x returns the character boundary closest to the given x coordinate
 53    /// (e.g. to handle aligning up/down arrow keys)
 54    pub fn closest_index_for_x(&self, x: Pixels) -> usize {
 55        let mut prev_index = 0;
 56        let mut prev_x = px(0.);
 57
 58        for run in self.runs.iter() {
 59            for glyph in run.glyphs.iter() {
 60                if glyph.position.x >= x {
 61                    if glyph.position.x - x < x - prev_x {
 62                        return glyph.index;
 63                    } else {
 64                        return prev_index;
 65                    }
 66                }
 67                prev_index = glyph.index;
 68                prev_x = glyph.position.x;
 69            }
 70        }
 71
 72        self.len
 73    }
 74
 75    pub fn x_for_index(&self, index: usize) -> Pixels {
 76        for run in &self.runs {
 77            for glyph in &run.glyphs {
 78                if glyph.index >= index {
 79                    return glyph.position.x;
 80                }
 81            }
 82        }
 83        self.width
 84    }
 85
 86    fn compute_wrap_boundaries(
 87        &self,
 88        text: &str,
 89        wrap_width: Pixels,
 90    ) -> SmallVec<[WrapBoundary; 1]> {
 91        let mut boundaries = SmallVec::new();
 92
 93        let mut first_non_whitespace_ix = None;
 94        let mut last_candidate_ix = None;
 95        let mut last_candidate_x = px(0.);
 96        let mut last_boundary = WrapBoundary {
 97            run_ix: 0,
 98            glyph_ix: 0,
 99        };
100        let mut last_boundary_x = px(0.);
101        let mut prev_ch = '\0';
102        let mut glyphs = self
103            .runs
104            .iter()
105            .enumerate()
106            .flat_map(move |(run_ix, run)| {
107                run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
108                    let character = text[glyph.index..].chars().next().unwrap();
109                    (
110                        WrapBoundary { run_ix, glyph_ix },
111                        character,
112                        glyph.position.x,
113                    )
114                })
115            })
116            .peekable();
117
118        while let Some((boundary, ch, x)) = glyphs.next() {
119            if ch == '\n' {
120                continue;
121            }
122
123            if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
124                last_candidate_ix = Some(boundary);
125                last_candidate_x = x;
126            }
127
128            if ch != ' ' && first_non_whitespace_ix.is_none() {
129                first_non_whitespace_ix = Some(boundary);
130            }
131
132            let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
133            let width = next_x - last_boundary_x;
134            if width > wrap_width && boundary > last_boundary {
135                if let Some(last_candidate_ix) = last_candidate_ix.take() {
136                    last_boundary = last_candidate_ix;
137                    last_boundary_x = last_candidate_x;
138                } else {
139                    last_boundary = boundary;
140                    last_boundary_x = x;
141                }
142
143                boundaries.push(last_boundary);
144            }
145            prev_ch = ch;
146        }
147
148        boundaries
149    }
150}
151
152#[derive(Deref, DerefMut, Default, Debug)]
153pub struct WrappedLineLayout {
154    #[deref]
155    #[deref_mut]
156    pub layout: LineLayout,
157    pub text: SharedString,
158    pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
159}
160
161#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
162pub struct WrapBoundary {
163    pub run_ix: usize,
164    pub glyph_ix: usize,
165}
166
167pub(crate) struct LineLayoutCache {
168    prev_frame: Mutex<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
169    curr_frame: RwLock<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
170    platform_text_system: Arc<dyn PlatformTextSystem>,
171}
172
173impl LineLayoutCache {
174    pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
175        Self {
176            prev_frame: Mutex::new(HashMap::new()),
177            curr_frame: RwLock::new(HashMap::new()),
178            platform_text_system,
179        }
180    }
181
182    pub fn start_frame(&self) {
183        let mut prev_frame = self.prev_frame.lock();
184        let mut curr_frame = self.curr_frame.write();
185        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
186        curr_frame.clear();
187    }
188
189    pub fn layout_line(
190        &self,
191        text: &SharedString,
192        font_size: Pixels,
193        runs: &[FontRun],
194        wrap_width: Option<Pixels>,
195    ) -> Arc<WrappedLineLayout> {
196        let key = &CacheKeyRef {
197            text,
198            font_size,
199            runs,
200            wrap_width,
201        } as &dyn AsCacheKeyRef;
202        let curr_frame = self.curr_frame.upgradable_read();
203        if let Some(layout) = curr_frame.get(key) {
204            return layout.clone();
205        }
206
207        let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
208        if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
209            curr_frame.insert(key, layout.clone());
210            layout
211        } else {
212            let layout = self.platform_text_system.layout_line(text, font_size, runs);
213            let wrap_boundaries = wrap_width
214                .map(|wrap_width| layout.compute_wrap_boundaries(text.as_ref(), wrap_width))
215                .unwrap_or_default();
216            let wrapped_line = Arc::new(WrappedLineLayout {
217                layout,
218                text: text.clone(),
219                wrap_boundaries,
220            });
221
222            let key = CacheKey {
223                text: text.clone(),
224                font_size,
225                runs: SmallVec::from(runs),
226                wrap_width,
227            };
228            curr_frame.insert(key, wrapped_line.clone());
229            wrapped_line
230        }
231    }
232}
233
234#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
235pub struct FontRun {
236    pub(crate) len: usize,
237    pub(crate) font_id: FontId,
238}
239
240trait AsCacheKeyRef {
241    fn as_cache_key_ref(&self) -> CacheKeyRef;
242}
243
244#[derive(Eq)]
245struct CacheKey {
246    text: SharedString,
247    font_size: Pixels,
248    runs: SmallVec<[FontRun; 1]>,
249    wrap_width: Option<Pixels>,
250}
251
252#[derive(Copy, Clone, PartialEq, Eq, Hash)]
253struct CacheKeyRef<'a> {
254    text: &'a str,
255    font_size: Pixels,
256    runs: &'a [FontRun],
257    wrap_width: Option<Pixels>,
258}
259
260impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
261    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
262        self.as_cache_key_ref() == other.as_cache_key_ref()
263    }
264}
265
266impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
267
268impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
269    fn hash<H: Hasher>(&self, state: &mut H) {
270        self.as_cache_key_ref().hash(state)
271    }
272}
273
274impl AsCacheKeyRef for CacheKey {
275    fn as_cache_key_ref(&self) -> CacheKeyRef {
276        CacheKeyRef {
277            text: &self.text,
278            font_size: self.font_size,
279            runs: self.runs.as_slice(),
280            wrap_width: self.wrap_width,
281        }
282    }
283}
284
285impl PartialEq for CacheKey {
286    fn eq(&self, other: &Self) -> bool {
287        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
288    }
289}
290
291impl Hash for CacheKey {
292    fn hash<H: Hasher>(&self, state: &mut H) {
293        self.as_cache_key_ref().hash(state);
294    }
295}
296
297impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for CacheKey {
298    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
299        self as &dyn AsCacheKeyRef
300    }
301}
302
303impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
304    fn as_cache_key_ref(&self) -> CacheKeyRef {
305        *self
306    }
307}