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}
32
33pub(super) struct Painted {
40 block: usize,
41 part: Part,
42 range: Range<usize>,
43 layout: TextLayout,
44}
45
46pub(super) struct PaintedRow {
47 painted: usize,
48 block: usize,
49 part: Part,
50 range: Range<usize>,
51 bounds: Bounds<Pixels>,
52 line_start: usize,
53 wrapped_row: usize,
54}
55
56impl BlockLayouts {
57 pub fn hit(&self, point: Point<Pixels>) -> Option<Cursor> {
63 let frames = self.0.borrow();
64 if let Some(row) = frames.rows.iter().find(|row| row.bounds.contains(&point)) {
65 return cursor_in_row(&frames, row, point.x);
66 }
67 frames
68 .rows
69 .iter()
70 .min_by_key(|row| {
71 let bounds = row.bounds;
72 let above = (bounds.origin.y - point.y).abs();
73 let below = (bounds.origin.y + bounds.size.height - point.y).abs();
74 f32::from(above.min(below)) as i64
75 })
76 .and_then(|row| cursor_in_row(&frames, row, point.x))
77 }
78
79 pub fn position(&self, at: Cursor) -> Option<(Point<Pixels>, Pixels)> {
85 let frames = self.0.borrow();
86 let painted = frames.texts.iter().find(|painted| {
87 painted.block == at.block
88 && painted.part == at.part
89 && painted.range.start <= at.offset
90 && at.offset <= painted.range.end
91 })?;
92 let point = painted
93 .layout
94 .position_for_index(at.offset - painted.range.start)?;
95 Some((point, painted.layout.line_height()))
96 }
97
98 pub fn rects(&self, selection: Selection) -> Vec<Bounds<Pixels>> {
106 let (start, end) = selection.ordered();
107 self.0
108 .borrow()
109 .texts
110 .iter()
111 .filter_map(|painted| {
112 let here = Cursor::new(painted.block, painted.part, 0);
113 let (from, to) = (
114 Cursor::new(start.block, start.part, 0),
115 Cursor::new(end.block, end.part, 0),
116 );
117 if here < from || here > to {
118 return None;
119 }
120 let len = painted.range.len();
123 let first = if here == from { start.offset } else { 0 };
124 let last = if here == to { end.offset } else { usize::MAX };
125 let range = first.saturating_sub(painted.range.start).min(len)
126 ..last.saturating_sub(painted.range.start).min(len);
127 (range.start < range.end).then(|| range_rects(&painted.layout, &range, 0.0, 0.0))
128 })
129 .flatten()
130 .collect()
131 }
132
133 pub fn step_row(
144 &self,
145 at: Cursor,
146 from: Point<Pixels>,
147 down: bool,
148 ) -> Option<(Cursor, Pixels)> {
149 let frames = self.0.borrow();
150 let ix = frames
151 .rows
152 .iter()
153 .position(|row| {
154 row.block == at.block
155 && row.part == at.part
156 && row_contains(row, at.offset)
157 && row.bounds.origin.y <= from.y
158 && from.y < row.bounds.origin.y + row.bounds.size.height
159 })
160 .or_else(|| {
161 frames.rows.iter().position(|row| {
162 row.block == at.block && row.part == at.part && row_contains(row, at.offset)
163 })
164 })?;
165 let next = match down {
166 true => frames.rows.get(ix + 1)?,
167 false => frames.rows.get(ix.checked_sub(1)?)?,
168 };
169 Some((cursor_in_row(&frames, next, from.x)?, next.bounds.origin.y))
170 }
171
172 pub fn over_text(&self, point: Point<Pixels>) -> bool {
179 self.0
180 .borrow()
181 .texts
182 .iter()
183 .any(|painted| painted.layout.bounds().contains(&point))
184 }
185
186 pub fn block_at(&self, point: Point<Pixels>) -> Option<usize> {
188 let blocks = &self.0.borrow().blocks;
189 blocks
190 .iter()
191 .find(|(_, bounds)| bounds.contains(&point))
192 .or_else(|| {
193 blocks.iter().min_by_key(|(_, bounds)| {
194 let above = (bounds.origin.y - point.y).abs();
195 let below = (bounds.origin.y + bounds.size.height - point.y).abs();
196 f32::from(above.min(below)) as i64
197 })
198 })
199 .map(|(ix, _)| *ix)
200 }
201
202 pub fn first_row(&self, ix: usize) -> Option<(Pixels, Pixels)> {
211 let texts = &self.0.borrow().texts;
212 let painted = texts.iter().find(|painted| painted.block == ix)?;
213 Some((
214 painted.layout.bounds().origin.y,
215 painted.layout.line_height(),
216 ))
217 }
218
219 pub fn block_bounds(&self, ix: usize) -> Option<Bounds<Pixels>> {
221 self.0
222 .borrow()
223 .blocks
224 .iter()
225 .find(|(block, _)| *block == ix)
226 .map(|(_, bounds)| *bounds)
227 }
228
229 pub fn language_bounds(&self, ix: usize) -> Option<Bounds<Pixels>> {
234 self.0
235 .borrow()
236 .languages
237 .iter()
238 .find(|(block, _)| *block == ix)
239 .map(|(_, bounds)| *bounds)
240 }
241
242 pub fn picture_bounds(&self, ix: usize) -> Option<Bounds<Pixels>> {
247 self.0
248 .borrow()
249 .pictures
250 .iter()
251 .find(|(block, _)| *block == ix)
252 .map(|(_, bounds)| *bounds)
253 }
254
255 pub fn checkbox_bounds(&self, ix: usize) -> Option<Bounds<Pixels>> {
259 self.0
260 .borrow()
261 .checkboxes
262 .iter()
263 .find(|(block, _)| *block == ix)
264 .map(|(_, bounds)| *bounds)
265 }
266
267 pub(super) fn record(&self, block: usize, part: Part, range: Range<usize>, layout: TextLayout) {
268 let mut frames = self.0.borrow_mut();
269 let painted = frames.texts.len();
270 record_rows(&mut frames.rows, painted, block, part, &range, &layout);
271 frames.texts.push(Painted {
272 block,
273 part,
274 range,
275 layout,
276 });
277 }
278
279 pub(super) fn record_block(&self, ix: usize, bounds: Bounds<Pixels>) {
280 self.0.borrow_mut().blocks.push((ix, bounds));
281 }
282
283 pub(super) fn record_language(&self, ix: usize, bounds: Bounds<Pixels>) {
284 self.0.borrow_mut().languages.push((ix, bounds));
285 }
286
287 pub(super) fn record_picture(&self, ix: usize, bounds: Bounds<Pixels>) {
288 self.0.borrow_mut().pictures.push((ix, bounds));
289 }
290
291 pub(super) fn record_checkbox(&self, ix: usize, bounds: Bounds<Pixels>) {
292 self.0.borrow_mut().checkboxes.push((ix, bounds));
293 }
294
295 pub(super) fn clear(&self) {
296 let mut frames = self.0.borrow_mut();
297 frames.texts.clear();
298 frames.rows.clear();
299 frames.blocks.clear();
300 frames.languages.clear();
301 frames.pictures.clear();
302 frames.checkboxes.clear();
303 }
304}
305
306pub(super) fn row_contains(row: &PaintedRow, offset: usize) -> bool {
307 row.range.start <= offset && offset <= row.range.end
308}
309
310pub(super) fn cursor_in_row(frames: &Frames, row: &PaintedRow, x: Pixels) -> Option<Cursor> {
311 let painted = &frames.texts[row.painted];
312 let line = painted
313 .layout
314 .line_layout_for_index(row.line_start - painted.range.start)?;
315 let height = row.bounds.size.height;
316 let local = point(
317 x - row.bounds.origin.x,
318 height * (row.wrapped_row as f32 + 0.5),
319 );
320 let (Ok(offset) | Err(offset)) = line.closest_index_for_position(local, height);
321 Some(Cursor::new(
322 row.block,
323 row.part,
324 (row.line_start + offset).min(row.range.end),
325 ))
326}
327
328pub(super) fn record_rows(
329 rows: &mut Vec<PaintedRow>,
330 painted: usize,
331 block: usize,
332 part: Part,
333 range: &Range<usize>,
334 layout: &TextLayout,
335) {
336 let line_height = layout.line_height();
337 let bounds = layout.bounds();
338 let mut origin = bounds.origin;
339 let mut line_start = range.start;
340 for line in layout.line_layouts() {
341 let shaped = &line.unwrapped_layout;
342 let row_ends = line
343 .wrap_boundaries()
344 .iter()
345 .map(|wrap| shaped.runs[wrap.run_ix].glyphs[wrap.glyph_ix].index)
346 .chain([line.len()]);
347 let mut row_start = 0;
348 for (row, row_end) in row_ends.enumerate() {
349 rows.push(PaintedRow {
350 painted,
351 block,
352 part,
353 range: line_start + row_start..line_start + row_end,
354 bounds: Bounds::new(
355 origin + point(px(0.0), line_height * row as f32),
356 size(bounds.size.width, line_height),
357 ),
358 line_start,
359 wrapped_row: row,
360 });
361 row_start = row_end;
362 }
363 origin.y += line.size(line_height).height;
364 line_start += line.len() + 1;
365 }
366}