1use super::*;
4
5#[derive(Clone, Default)]
12pub struct BlockLayouts(Rc<RefCell<Frames>>);
13
14#[derive(Default)]
15pub(super) struct Frames {
16 texts: Vec<Painted>,
17 rows: Vec<PaintedRow>,
18 blocks: Vec<(usize, Bounds<Pixels>)>,
21 languages: Vec<(usize, Bounds<Pixels>)>,
24 pictures: Vec<(usize, Bounds<Pixels>)>,
28 checkboxes: Vec<(usize, Bounds<Pixels>)>,
31 heights: Heights,
33}
34
35#[derive(Default)]
38pub(super) struct Heights {
39 by_key: HashMap<u64, Pixels>,
42}
43
44pub(super) struct Painted {
51 block: usize,
52 part: Part,
53 range: Range<usize>,
54 layout: TextLayout,
55}
56
57pub(super) struct PaintedRow {
58 painted: usize,
59 block: usize,
60 part: Part,
61 range: Range<usize>,
62 bounds: Bounds<Pixels>,
63 line_start: usize,
64 wrapped_row: usize,
65}
66
67impl BlockLayouts {
68 pub fn hit(&self, point: Point<Pixels>) -> Option<Cursor> {
74 let frames = self.0.borrow();
75 if let Some(row) = frames.rows.iter().find(|row| row.bounds.contains(&point)) {
76 return cursor_in_row(&frames, row, point.x);
77 }
78 frames
79 .rows
80 .iter()
81 .min_by_key(|row| {
82 let bounds = row.bounds;
83 let above = (bounds.origin.y - point.y).abs();
84 let below = (bounds.origin.y + bounds.size.height - point.y).abs();
85 f32::from(above.min(below)) as i64
86 })
87 .and_then(|row| cursor_in_row(&frames, row, point.x))
88 }
89
90 pub fn position(&self, at: Cursor) -> Option<(Point<Pixels>, Pixels)> {
96 let frames = self.0.borrow();
97 let painted = frames.texts.iter().find(|painted| {
98 painted.block == at.block
99 && painted.part == at.part
100 && painted.range.start <= at.offset
101 && at.offset <= painted.range.end
102 })?;
103 let point = painted
104 .layout
105 .position_for_index(at.offset - painted.range.start)?;
106 Some((point, painted.layout.line_height()))
107 }
108
109 pub fn rects(&self, selection: Selection) -> Vec<Bounds<Pixels>> {
117 let (start, end) = selection.ordered();
118 self.0
119 .borrow()
120 .texts
121 .iter()
122 .filter_map(|painted| {
123 let here = Cursor::new(painted.block, painted.part, 0);
124 let (from, to) = (
125 Cursor::new(start.block, start.part, 0),
126 Cursor::new(end.block, end.part, 0),
127 );
128 if here < from || here > to {
129 return None;
130 }
131 let len = painted.range.len();
134 let first = if here == from { start.offset } else { 0 };
135 let last = if here == to { end.offset } else { usize::MAX };
136 let range = first.saturating_sub(painted.range.start).min(len)
137 ..last.saturating_sub(painted.range.start).min(len);
138 (range.start < range.end).then(|| range_rects(&painted.layout, &range, 0.0, 0.0))
139 })
140 .flatten()
141 .collect()
142 }
143
144 pub fn step_row(
155 &self,
156 at: Cursor,
157 from: Point<Pixels>,
158 down: bool,
159 ) -> Option<(Cursor, Pixels)> {
160 let frames = self.0.borrow();
161 let ix = frames
162 .rows
163 .iter()
164 .position(|row| {
165 row.block == at.block
166 && row.part == at.part
167 && row_contains(row, at.offset)
168 && row.bounds.origin.y <= from.y
169 && from.y < row.bounds.origin.y + row.bounds.size.height
170 })
171 .or_else(|| {
172 frames.rows.iter().position(|row| {
173 row.block == at.block && row.part == at.part && row_contains(row, at.offset)
174 })
175 })?;
176 let next = match down {
177 true => frames.rows.get(ix + 1)?,
178 false => frames.rows.get(ix.checked_sub(1)?)?,
179 };
180 Some((cursor_in_row(&frames, next, from.x)?, next.bounds.origin.y))
181 }
182
183 pub fn over_text(&self, point: Point<Pixels>) -> bool {
190 self.0
191 .borrow()
192 .texts
193 .iter()
194 .any(|painted| painted.layout.bounds().contains(&point))
195 }
196
197 pub fn block_at(&self, point: Point<Pixels>) -> Option<usize> {
199 let blocks = &self.0.borrow().blocks;
200 blocks
201 .iter()
202 .find(|(_, bounds)| bounds.contains(&point))
203 .or_else(|| {
204 blocks.iter().min_by_key(|(_, bounds)| {
205 let above = (bounds.origin.y - point.y).abs();
206 let below = (bounds.origin.y + bounds.size.height - point.y).abs();
207 f32::from(above.min(below)) as i64
208 })
209 })
210 .map(|(ix, _)| *ix)
211 }
212
213 pub fn first_row(&self, ix: usize) -> Option<(Pixels, Pixels)> {
222 let texts = &self.0.borrow().texts;
223 let painted = texts.iter().find(|painted| painted.block == ix)?;
224 Some((
225 painted.layout.bounds().origin.y,
226 painted.layout.line_height(),
227 ))
228 }
229
230 pub fn block_bounds(&self, ix: usize) -> Option<Bounds<Pixels>> {
232 self.0
233 .borrow()
234 .blocks
235 .iter()
236 .find(|(block, _)| *block == ix)
237 .map(|(_, bounds)| *bounds)
238 }
239
240 pub fn language_bounds(&self, ix: usize) -> Option<Bounds<Pixels>> {
245 self.0
246 .borrow()
247 .languages
248 .iter()
249 .find(|(block, _)| *block == ix)
250 .map(|(_, bounds)| *bounds)
251 }
252
253 pub fn picture_bounds(&self, ix: usize) -> Option<Bounds<Pixels>> {
258 self.0
259 .borrow()
260 .pictures
261 .iter()
262 .find(|(block, _)| *block == ix)
263 .map(|(_, bounds)| *bounds)
264 }
265
266 pub fn checkbox_bounds(&self, ix: usize) -> Option<Bounds<Pixels>> {
270 self.0
271 .borrow()
272 .checkboxes
273 .iter()
274 .find(|(block, _)| *block == ix)
275 .map(|(_, bounds)| *bounds)
276 }
277
278 pub(super) fn record(&self, block: usize, part: Part, range: Range<usize>, layout: TextLayout) {
279 let mut frames = self.0.borrow_mut();
280 let painted = frames.texts.len();
281 record_rows(&mut frames.rows, painted, block, part, &range, &layout);
282 frames.texts.push(Painted {
283 block,
284 part,
285 range,
286 layout,
287 });
288 }
289
290 pub(super) fn record_block(&self, ix: usize, bounds: Bounds<Pixels>) {
291 self.0.borrow_mut().blocks.push((ix, bounds));
292 }
293
294 pub(super) fn record_language(&self, ix: usize, bounds: Bounds<Pixels>) {
295 self.0.borrow_mut().languages.push((ix, bounds));
296 }
297
298 pub(super) fn record_picture(&self, ix: usize, bounds: Bounds<Pixels>) {
299 self.0.borrow_mut().pictures.push((ix, bounds));
300 }
301
302 pub(super) fn record_checkbox(&self, ix: usize, bounds: Bounds<Pixels>) {
303 self.0.borrow_mut().checkboxes.push((ix, bounds));
304 }
305
306 pub(super) fn height(&self, key: u64) -> Option<Pixels> {
307 self.0.borrow().heights.by_key.get(&key).copied()
308 }
309
310 pub(super) fn record_height(&self, key: u64, height: Pixels) {
311 self.0.borrow_mut().heights.by_key.insert(key, height);
312 }
313
314 pub(super) fn prune(&self, keys: &[u64]) {
317 let by_key = &mut self.0.borrow_mut().heights.by_key;
318 if by_key.len() > 2 * keys.len() {
319 let keep: std::collections::HashSet<u64> = keys.iter().copied().collect();
320 by_key.retain(|key, _| keep.contains(key));
321 }
322 }
323
324 pub(super) fn clear(&self) {
325 let mut frames = self.0.borrow_mut();
326 frames.texts.clear();
327 frames.rows.clear();
328 frames.blocks.clear();
329 frames.languages.clear();
330 frames.pictures.clear();
331 frames.checkboxes.clear();
332 }
333}
334
335pub(super) fn row_contains(row: &PaintedRow, offset: usize) -> bool {
336 row.range.start <= offset && offset <= row.range.end
337}
338
339pub(super) fn cursor_in_row(frames: &Frames, row: &PaintedRow, x: Pixels) -> Option<Cursor> {
340 let painted = &frames.texts[row.painted];
341 let line = painted
342 .layout
343 .line_layout_for_index(row.line_start - painted.range.start)?;
344 let height = row.bounds.size.height;
345 let local = point(
346 x - row.bounds.origin.x,
347 height * (row.wrapped_row as f32 + 0.5),
348 );
349 let (Ok(offset) | Err(offset)) = line.closest_index_for_position(local, height);
350 Some(Cursor::new(
351 row.block,
352 row.part,
353 (row.line_start + offset).min(row.range.end),
354 ))
355}
356
357pub(super) fn record_rows(
358 rows: &mut Vec<PaintedRow>,
359 painted: usize,
360 block: usize,
361 part: Part,
362 range: &Range<usize>,
363 layout: &TextLayout,
364) {
365 let line_height = layout.line_height();
366 let bounds = layout.bounds();
367 let mut origin = bounds.origin;
368 let mut line_start = range.start;
369 for line in layout.line_layouts() {
370 let shaped = &line.unwrapped_layout;
371 let row_ends = line
372 .wrap_boundaries()
373 .iter()
374 .map(|wrap| shaped.runs[wrap.run_ix].glyphs[wrap.glyph_ix].index)
375 .chain([line.len()]);
376 let mut row_start = 0;
377 for (row, row_end) in row_ends.enumerate() {
378 rows.push(PaintedRow {
379 painted,
380 block,
381 part,
382 range: line_start + row_start..line_start + row_end,
383 bounds: Bounds::new(
384 origin + point(px(0.0), line_height * row as f32),
385 size(bounds.size.width, line_height),
386 ),
387 line_start,
388 wrapped_row: row,
389 });
390 row_start = row_end;
391 }
392 origin.y += line.size(line_height).height;
393 line_start += line.len() + 1;
394 }
395}