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}