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        prev_index
 72    }
 73
 74    pub fn x_for_index(&self, index: usize) -> Pixels {
 75        for run in &self.runs {
 76            for glyph in &run.glyphs {
 77                if glyph.index >= index {
 78                    return glyph.position.x;
 79                }
 80            }
 81        }
 82        self.width
 83    }
 84
 85    pub fn font_for_index(&self, index: usize) -> Option<FontId> {
 86        for run in &self.runs {
 87            for glyph in &run.glyphs {
 88                if glyph.index >= index {
 89                    return Some(run.font_id);
 90                }
 91            }
 92        }
 93
 94        None
 95    }
 96
 97    fn compute_wrap_boundaries(
 98        &self,
 99        text: &str,
100        wrap_width: Pixels,
101    ) -> SmallVec<[WrapBoundary; 1]> {
102        let mut boundaries = SmallVec::new();
103
104        let mut first_non_whitespace_ix = None;
105        let mut last_candidate_ix = None;
106        let mut last_candidate_x = px(0.);
107        let mut last_boundary = WrapBoundary {
108            run_ix: 0,
109            glyph_ix: 0,
110        };
111        let mut last_boundary_x = px(0.);
112        let mut prev_ch = '\0';
113        let mut glyphs = self
114            .runs
115            .iter()
116            .enumerate()
117            .flat_map(move |(run_ix, run)| {
118                run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
119                    let character = text[glyph.index..].chars().next().unwrap();
120                    (
121                        WrapBoundary { run_ix, glyph_ix },
122                        character,
123                        glyph.position.x,
124                    )
125                })
126            })
127            .peekable();
128
129        while let Some((boundary, ch, x)) = glyphs.next() {
130            if ch == '\n' {
131                continue;
132            }
133
134            if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
135                last_candidate_ix = Some(boundary);
136                last_candidate_x = x;
137            }
138
139            if ch != ' ' && first_non_whitespace_ix.is_none() {
140                first_non_whitespace_ix = Some(boundary);
141            }
142
143            let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
144            let width = next_x - last_boundary_x;
145            if width > wrap_width && boundary > last_boundary {
146                if let Some(last_candidate_ix) = last_candidate_ix.take() {
147                    last_boundary = last_candidate_ix;
148                    last_boundary_x = last_candidate_x;
149                } else {
150                    last_boundary = boundary;
151                    last_boundary_x = x;
152                }
153
154                boundaries.push(last_boundary);
155            }
156            prev_ch = ch;
157        }
158
159        boundaries
160    }
161}
162
163#[derive(Deref, DerefMut, Default, Debug)]
164pub struct WrappedLineLayout {
165    #[deref]
166    #[deref_mut]
167    pub layout: LineLayout,
168    pub text: SharedString,
169    pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
170}
171
172#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
173pub struct WrapBoundary {
174    pub run_ix: usize,
175    pub glyph_ix: usize,
176}
177
178pub(crate) struct LineLayoutCache {
179    prev_frame: Mutex<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
180    curr_frame: RwLock<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
181    platform_text_system: Arc<dyn PlatformTextSystem>,
182}
183
184impl LineLayoutCache {
185    pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
186        Self {
187            prev_frame: Mutex::new(HashMap::new()),
188            curr_frame: RwLock::new(HashMap::new()),
189            platform_text_system,
190        }
191    }
192
193    pub fn start_frame(&self) {
194        let mut prev_frame = self.prev_frame.lock();
195        let mut curr_frame = self.curr_frame.write();
196        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
197        curr_frame.clear();
198    }
199
200    pub fn layout_line(
201        &self,
202        text: &SharedString,
203        font_size: Pixels,
204        runs: &[FontRun],
205        wrap_width: Option<Pixels>,
206    ) -> Arc<WrappedLineLayout> {
207        let key = &CacheKeyRef {
208            text,
209            font_size,
210            runs,
211            wrap_width,
212        } as &dyn AsCacheKeyRef;
213        let curr_frame = self.curr_frame.upgradable_read();
214        if let Some(layout) = curr_frame.get(key) {
215            return layout.clone();
216        }
217
218        let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
219        if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
220            curr_frame.insert(key, layout.clone());
221            layout
222        } else {
223            let layout = self.platform_text_system.layout_line(text, font_size, runs);
224            let wrap_boundaries = wrap_width
225                .map(|wrap_width| layout.compute_wrap_boundaries(text.as_ref(), wrap_width))
226                .unwrap_or_default();
227            let wrapped_line = Arc::new(WrappedLineLayout {
228                layout,
229                text: text.clone(),
230                wrap_boundaries,
231            });
232
233            let key = CacheKey {
234                text: text.clone(),
235                font_size,
236                runs: SmallVec::from(runs),
237                wrap_width,
238            };
239            curr_frame.insert(key, wrapped_line.clone());
240            wrapped_line
241        }
242    }
243}
244
245#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
246pub struct FontRun {
247    pub(crate) len: usize,
248    pub(crate) font_id: FontId,
249}
250
251trait AsCacheKeyRef {
252    fn as_cache_key_ref(&self) -> CacheKeyRef;
253}
254
255#[derive(Eq)]
256struct CacheKey {
257    text: SharedString,
258    font_size: Pixels,
259    runs: SmallVec<[FontRun; 1]>,
260    wrap_width: Option<Pixels>,
261}
262
263#[derive(Copy, Clone, PartialEq, Eq, Hash)]
264struct CacheKeyRef<'a> {
265    text: &'a str,
266    font_size: Pixels,
267    runs: &'a [FontRun],
268    wrap_width: Option<Pixels>,
269}
270
271impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
272    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
273        self.as_cache_key_ref() == other.as_cache_key_ref()
274    }
275}
276
277impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
278
279impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
280    fn hash<H: Hasher>(&self, state: &mut H) {
281        self.as_cache_key_ref().hash(state)
282    }
283}
284
285impl AsCacheKeyRef for CacheKey {
286    fn as_cache_key_ref(&self) -> CacheKeyRef {
287        CacheKeyRef {
288            text: &self.text,
289            font_size: self.font_size,
290            runs: self.runs.as_slice(),
291            wrap_width: self.wrap_width,
292        }
293    }
294}
295
296impl PartialEq for CacheKey {
297    fn eq(&self, other: &Self) -> bool {
298        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
299    }
300}
301
302impl Hash for CacheKey {
303    fn hash<H: Hasher>(&self, state: &mut H) {
304        self.as_cache_key_ref().hash(state);
305    }
306}
307
308impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for CacheKey {
309    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
310        self as &dyn AsCacheKeyRef
311    }
312}
313
314impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
315    fn as_cache_key_ref(&self) -> CacheKeyRef {
316        *self
317    }
318}