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