1use crate::{px, EntityId, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};
2use collections::{FxHashMap, FxHashSet};
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 its 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 Some(line_start_boundary) = self.wrap_boundaries.get(wrapped_line_ix - 1) else {
251 return None;
252 };
253 let run = &self.unwrapped_layout.runs[line_start_boundary.run_ix];
254 run.glyphs[line_start_boundary.glyph_ix].position.x
255 } else {
256 Pixels::ZERO
257 };
258
259 let wrapped_line_end_x = if wrapped_line_ix < self.wrap_boundaries.len() {
260 let next_wrap_boundary_ix = wrapped_line_ix;
261 let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix];
262 let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix];
263 run.glyphs[next_wrap_boundary.glyph_ix].position.x
264 } else {
265 self.unwrapped_layout.width
266 };
267
268 let mut position_in_unwrapped_line = position;
269 position_in_unwrapped_line.x += wrapped_line_start_x;
270 if position_in_unwrapped_line.x > wrapped_line_end_x {
271 None
272 } else {
273 self.unwrapped_layout
274 .index_for_x(position_in_unwrapped_line.x)
275 }
276 }
277}
278
279pub(crate) struct LineLayoutCache {
280 view_stack: Mutex<Vec<EntityId>>,
281 previous_frame: Mutex<FxHashMap<CacheKey, Arc<LineLayout>>>,
282 current_frame: RwLock<FxHashMap<CacheKey, Arc<LineLayout>>>,
283 previous_frame_wrapped: Mutex<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
284 current_frame_wrapped: RwLock<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
285 platform_text_system: Arc<dyn PlatformTextSystem>,
286}
287
288impl LineLayoutCache {
289 pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
290 Self {
291 view_stack: Mutex::default(),
292 previous_frame: Mutex::default(),
293 current_frame: RwLock::default(),
294 previous_frame_wrapped: Mutex::default(),
295 current_frame_wrapped: RwLock::default(),
296 platform_text_system,
297 }
298 }
299
300 pub fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) {
301 debug_assert_eq!(self.view_stack.lock().len(), 0);
302
303 let mut prev_frame = self.previous_frame.lock();
304 let mut curr_frame = self.current_frame.write();
305 for (key, layout) in prev_frame.drain() {
306 if key
307 .parent_view_id
308 .map_or(false, |view_id| reused_views.contains(&view_id))
309 {
310 curr_frame.insert(key, layout);
311 }
312 }
313 std::mem::swap(&mut *prev_frame, &mut *curr_frame);
314
315 let mut prev_frame_wrapped = self.previous_frame_wrapped.lock();
316 let mut curr_frame_wrapped = self.current_frame_wrapped.write();
317 for (key, layout) in prev_frame_wrapped.drain() {
318 if key
319 .parent_view_id
320 .map_or(false, |view_id| reused_views.contains(&view_id))
321 {
322 curr_frame_wrapped.insert(key, layout);
323 }
324 }
325 std::mem::swap(&mut *prev_frame_wrapped, &mut *curr_frame_wrapped);
326 }
327
328 pub fn with_view<R>(&self, view_id: EntityId, f: impl FnOnce() -> R) -> R {
329 self.view_stack.lock().push(view_id);
330 let result = f();
331 self.view_stack.lock().pop();
332 result
333 }
334
335 fn parent_view_id(&self) -> Option<EntityId> {
336 self.view_stack.lock().last().copied()
337 }
338
339 pub fn layout_wrapped_line(
340 &self,
341 text: &str,
342 font_size: Pixels,
343 runs: &[FontRun],
344 wrap_width: Option<Pixels>,
345 ) -> Arc<WrappedLineLayout> {
346 let key = &CacheKeyRef {
347 text,
348 font_size,
349 runs,
350 wrap_width,
351 parent_view_id: self.parent_view_id(),
352 } as &dyn AsCacheKeyRef;
353
354 let current_frame = self.current_frame_wrapped.upgradable_read();
355 if let Some(layout) = current_frame.get(key) {
356 return layout.clone();
357 }
358
359 let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
360 if let Some((key, layout)) = self.previous_frame_wrapped.lock().remove_entry(key) {
361 current_frame.insert(key, layout.clone());
362 layout
363 } else {
364 let unwrapped_layout = self.layout_line(text, font_size, runs);
365 let wrap_boundaries = if let Some(wrap_width) = wrap_width {
366 unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width)
367 } else {
368 SmallVec::new()
369 };
370 let layout = Arc::new(WrappedLineLayout {
371 unwrapped_layout,
372 wrap_boundaries,
373 wrap_width,
374 });
375 let key = CacheKey {
376 text: text.into(),
377 font_size,
378 runs: SmallVec::from(runs),
379 wrap_width,
380 parent_view_id: self.parent_view_id(),
381 };
382 current_frame.insert(key, layout.clone());
383 layout
384 }
385 }
386
387 pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> {
388 let key = &CacheKeyRef {
389 text,
390 font_size,
391 runs,
392 wrap_width: None,
393 parent_view_id: self.parent_view_id(),
394 } as &dyn AsCacheKeyRef;
395
396 let current_frame = self.current_frame.upgradable_read();
397 if let Some(layout) = current_frame.get(key) {
398 return layout.clone();
399 }
400
401 let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
402 if let Some((key, layout)) = self.previous_frame.lock().remove_entry(key) {
403 current_frame.insert(key, layout.clone());
404 layout
405 } else {
406 let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
407 let key = CacheKey {
408 text: text.into(),
409 font_size,
410 runs: SmallVec::from(runs),
411 wrap_width: None,
412 parent_view_id: self.parent_view_id(),
413 };
414 current_frame.insert(key, layout.clone());
415 layout
416 }
417 }
418}
419
420/// A run of text with a single font.
421#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
422pub struct FontRun {
423 pub(crate) len: usize,
424 pub(crate) font_id: FontId,
425}
426
427trait AsCacheKeyRef {
428 fn as_cache_key_ref(&self) -> CacheKeyRef;
429}
430
431#[derive(Debug, Eq)]
432struct CacheKey {
433 text: String,
434 font_size: Pixels,
435 runs: SmallVec<[FontRun; 1]>,
436 wrap_width: Option<Pixels>,
437 parent_view_id: Option<EntityId>,
438}
439
440#[derive(Copy, Clone, PartialEq, Eq, Hash)]
441struct CacheKeyRef<'a> {
442 text: &'a str,
443 font_size: Pixels,
444 runs: &'a [FontRun],
445 wrap_width: Option<Pixels>,
446 parent_view_id: Option<EntityId>,
447}
448
449impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
450 fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
451 self.as_cache_key_ref() == other.as_cache_key_ref()
452 }
453}
454
455impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
456
457impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
458 fn hash<H: Hasher>(&self, state: &mut H) {
459 self.as_cache_key_ref().hash(state)
460 }
461}
462
463impl AsCacheKeyRef for CacheKey {
464 fn as_cache_key_ref(&self) -> CacheKeyRef {
465 CacheKeyRef {
466 text: &self.text,
467 font_size: self.font_size,
468 runs: self.runs.as_slice(),
469 wrap_width: self.wrap_width,
470 parent_view_id: self.parent_view_id,
471 }
472 }
473}
474
475impl PartialEq for CacheKey {
476 fn eq(&self, other: &Self) -> bool {
477 self.as_cache_key_ref().eq(&other.as_cache_key_ref())
478 }
479}
480
481impl Hash for CacheKey {
482 fn hash<H: Hasher>(&self, state: &mut H) {
483 self.as_cache_key_ref().hash(state);
484 }
485}
486
487impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for CacheKey {
488 fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
489 self as &dyn AsCacheKeyRef
490 }
491}
492
493impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
494 fn as_cache_key_ref(&self) -> CacheKeyRef {
495 *self
496 }
497}