1use crate::{TextCaretPosition, TextClusterSpan, TextLayout, TextLine, TextMetrics};
5use incular_core::Offset;
6use incular_rendering::{GlyphPosition, GlyphRun};
7use std::sync::Arc;
8
9pub trait TextGeometry {
16 fn metrics(&self) -> TextMetrics;
17
18 fn line_count(&self) -> usize;
19
20 fn get_line(&self, index: usize) -> Option<TextLineRef<'_>>;
21
22 fn lines(&self) -> TextLines<'_>;
23
24 fn lines_between(&self, top: f32, bottom: f32) -> TextLines<'_>;
27
28 fn line_for_offset(&self, byte: usize) -> usize;
31
32 fn line_caret_positions(&self, line: usize) -> &[TextCaretPosition];
34
35 fn line(&self, index: usize) -> TextLineRef<'_> {
41 self.get_line(index).expect("text line index out of range")
42 }
43}
44
45impl<T: TextGeometry + ?Sized> TextGeometry for Arc<T> {
47 fn metrics(&self) -> TextMetrics {
48 (**self).metrics()
49 }
50
51 fn line_count(&self) -> usize {
52 (**self).line_count()
53 }
54
55 fn get_line(&self, index: usize) -> Option<TextLineRef<'_>> {
56 (**self).get_line(index)
57 }
58
59 fn lines(&self) -> TextLines<'_> {
60 (**self).lines()
61 }
62
63 fn lines_between(&self, top: f32, bottom: f32) -> TextLines<'_> {
64 (**self).lines_between(top, bottom)
65 }
66
67 fn line_for_offset(&self, byte: usize) -> usize {
68 (**self).line_for_offset(byte)
69 }
70
71 fn line_caret_positions(&self, line: usize) -> &[TextCaretPosition] {
72 (**self).line_caret_positions(line)
73 }
74}
75
76impl TextGeometry for TextLayout {
77 fn metrics(&self) -> TextMetrics {
78 self.metrics
79 }
80
81 fn line_count(&self) -> usize {
82 self.lines.len()
83 }
84
85 fn get_line(&self, index: usize) -> Option<TextLineRef<'_>> {
86 let line = self.lines.get(index)?;
87 Some(TextLineRef::new(line, index, 0, 0.))
88 }
89
90 fn lines(&self) -> TextLines<'_> {
91 TextLines::flat(&self.lines, 0, self.lines.len())
92 }
93
94 fn lines_between(&self, top: f32, bottom: f32) -> TextLines<'_> {
95 let first = self
96 .lines
97 .partition_point(|line| line.baseline < top)
98 .saturating_sub(1);
99 let end = (self.lines.partition_point(|line| line.baseline <= bottom) + 1)
100 .min(self.lines.len())
101 .max(first);
102 TextLines::flat(&self.lines, first, end)
103 }
104
105 fn line_for_offset(&self, byte: usize) -> usize {
106 let last = self.lines.len().saturating_sub(1);
107 let line = self.lines.partition_point(|line| line.start <= byte);
108 match line.checked_sub(1) {
109 Some(line) if byte < self.lines[line].end => line,
110 _ => last,
111 }
112 }
113
114 fn line_caret_positions(&self, line: usize) -> &[TextCaretPosition] {
115 TextLayout::line_caret_positions(self, line)
116 }
117}
118
119#[derive(Clone, Copy, Debug)]
122pub struct TextLineRef<'a> {
123 line: &'a TextLine,
124 index: usize,
125 byte: usize,
126 y: f32,
127}
128
129impl<'a> TextLineRef<'a> {
130 pub(crate) const fn new(line: &'a TextLine, index: usize, byte: usize, y: f32) -> Self {
131 Self {
132 line,
133 index,
134 byte,
135 y,
136 }
137 }
138
139 #[must_use]
141 pub const fn index(&self) -> usize {
142 self.index
143 }
144
145 #[must_use]
146 pub const fn start(&self) -> usize {
147 self.line.start + self.byte
148 }
149
150 #[must_use]
153 pub const fn caret_end(&self) -> usize {
154 self.line.caret_end + self.byte
155 }
156
157 #[must_use]
158 pub const fn end(&self) -> usize {
159 self.line.end + self.byte
160 }
161
162 #[must_use]
163 pub fn baseline(&self) -> f32 {
164 self.line.baseline + self.y
165 }
166
167 #[must_use]
169 pub const fn width(&self) -> f32 {
170 self.line.width
171 }
172
173 #[must_use]
177 pub const fn offset(&self) -> f32 {
178 self.line.offset
179 }
180
181 #[must_use]
183 pub fn runs(&self) -> &'a [Arc<GlyphRun>] {
184 &self.line.runs
185 }
186
187 #[must_use]
190 pub const fn paint_origin(&self) -> Offset {
191 Offset::new(0., self.y)
192 }
193
194 pub fn glyphs(&self) -> impl ExactSizeIterator<Item = GlyphPosition> + 'a {
197 let (byte, y) = (self.byte as u32, self.y);
198 self.line.glyphs.iter().map(move |glyph| GlyphPosition {
199 offset: Offset::new(glyph.offset.x, glyph.offset.y - y),
200 cluster: glyph.cluster + byte,
201 ..*glyph
202 })
203 }
204
205 pub fn clusters(&self) -> impl ExactSizeIterator<Item = TextClusterSpan> + 'a {
207 let byte = self.byte as u32;
208 self.line.clusters.iter().map(move |span| TextClusterSpan {
209 start: span.start + byte,
210 end: span.end + byte,
211 ..*span
212 })
213 }
214
215 #[must_use]
218 pub fn selection_spans(&self, start: usize, end: usize) -> Vec<(f32, f32)> {
219 if end < self.byte {
220 return Vec::new();
221 }
222 self.line.selection_spans(
223 start.saturating_sub(self.byte),
224 end.saturating_sub(self.byte),
225 )
226 }
227
228 #[must_use]
230 pub const fn shaped(&self) -> &'a TextLine {
231 self.line
232 }
233}
234
235#[derive(Clone, Debug)]
237pub(crate) struct ParagraphSlot {
238 pub(crate) byte: usize,
240 pub(crate) y: f32,
242 pub(crate) first_line: usize,
244 pub(crate) layout: Arc<TextLayout>,
246}
247
248#[derive(Clone, Copy, Debug)]
250struct LineSegment<'a> {
251 lines: &'a [TextLine],
252 first_line: usize,
253 byte: usize,
254 y: f32,
255}
256
257impl<'a> LineSegment<'a> {
258 fn of(slot: &'a ParagraphSlot) -> Self {
259 Self {
260 lines: &slot.layout.lines,
261 first_line: slot.first_line,
262 byte: slot.byte,
263 y: slot.y,
264 }
265 }
266}
267
268#[derive(Clone, Debug)]
270pub struct TextLines<'a> {
271 current: LineSegment<'a>,
272 next: usize,
273 rest: &'a [ParagraphSlot],
274 remaining: usize,
275}
276
277impl<'a> TextLines<'a> {
278 fn flat(lines: &'a [TextLine], first: usize, end: usize) -> Self {
280 Self {
281 current: LineSegment {
282 lines,
283 first_line: 0,
284 byte: 0,
285 y: 0.,
286 },
287 next: first,
288 rest: &[],
289 remaining: end.saturating_sub(first),
290 }
291 }
292
293 pub(crate) fn paragraphs(slots: &'a [ParagraphSlot]) -> Self {
295 let remaining = slots.iter().map(|slot| slot.layout.lines.len()).sum();
296 match slots.split_first() {
297 Some((first, rest)) => Self {
298 current: LineSegment::of(first),
299 next: 0,
300 rest,
301 remaining,
302 },
303 None => Self::flat(&[], 0, 0),
304 }
305 }
306}
307
308impl<'a> Iterator for TextLines<'a> {
309 type Item = TextLineRef<'a>;
310
311 fn next(&mut self) -> Option<Self::Item> {
312 if self.remaining == 0 {
313 return None;
314 }
315 while self.next >= self.current.lines.len() {
316 let (slot, rest) = self.rest.split_first()?;
317 self.current = LineSegment::of(slot);
318 self.rest = rest;
319 self.next = 0;
320 }
321 let segment = self.current;
322 let index = self.next;
323 self.next += 1;
324 self.remaining -= 1;
325 Some(TextLineRef::new(
326 &segment.lines[index],
327 segment.first_line + index,
328 segment.byte,
329 segment.y,
330 ))
331 }
332
333 fn size_hint(&self) -> (usize, Option<usize>) {
334 (self.remaining, Some(self.remaining))
335 }
336}
337
338impl ExactSizeIterator for TextLines<'_> {}