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}