1use std::{
2 ops::Range,
3 sync::{
4 Arc,
5 atomic::{AtomicU32, Ordering},
6 },
7};
8
9use cranpose_ui_graphics::Rect;
10
11use crate::{
12 bidi_level::BidiLevel,
13 glyph_id::GlyphId,
14 glyph_ink::{InkReach, find_ink_past_lines, may_ink_past_lines},
15 index_range::{IndexRange, widen},
16 laid_glyphs::RunGlyphs,
17 layout_body::LayoutBody,
18 paragraph::TextAlign,
19 shaped_glyphs::GlyphOffset,
20 synthesized_style::SynthesizedStyle,
21 typeface::Typeface,
22};
23
24#[derive(Clone, Debug, Default)]
31pub struct TextLayout {
32 body: Arc<LayoutBody>,
35 width: f32,
36 wrap: f32,
38 did_overflow_width: bool,
39 id: u32,
40 glyph_reach: Rect,
43}
44
45impl PartialEq for TextLayout {
46 fn eq(&self, other: &Self) -> bool {
47 (Arc::ptr_eq(&self.body, &other.body) || self.body == other.body)
51 && self.width == other.width
52 && self.did_overflow_width == other.did_overflow_width
53 }
54}
55
56static NEXT_ID: AtomicU32 = AtomicU32::new(1);
58
59fn next_id() -> u32 {
61 NEXT_ID.fetch_add(1, Ordering::Relaxed).max(1)
62}
63
64fn lines_reach(body: &LayoutBody) -> (f32, f32) {
67 body.lines
68 .iter()
69 .map(|line| (line.left, line.right()))
70 .reduce(|(left, right), (line_left, line_right)| {
71 (left.min(line_left), right.max(line_right))
72 })
73 .unwrap_or_default()
74}
75
76#[inline]
81fn glyph_reach(body: &LayoutBody, reach: InkReach) -> Rect {
82 let (left, right) = lines_reach(body);
83 let lines = Rect {
84 x: left,
85 y: 0.0,
86 width: right - left,
87 height: 0.0,
88 };
89 if !may_ink_past_lines(body, reach) {
90 return lines;
91 }
92 find_ink_past_lines(body, (left, right)).map_or(lines, |past| lines.union(past))
93}
94
95#[derive(Clone, Copy, Debug, PartialEq)]
97pub(crate) struct WidthRange {
98 pub(crate) min: f32,
99 pub(crate) max: f32,
100}
101
102impl WidthRange {
103 pub(crate) const ALL: Self = Self {
104 min: f32::NEG_INFINITY,
105 max: f32::INFINITY,
106 };
107
108 pub(crate) fn narrow(&mut self, min: f32, max: f32) {
110 self.min = self.min.max(min);
111 self.max = self.max.min(max);
112 }
113}
114
115const HOLD_MARGIN: f32 = 1e-3;
118
119#[derive(Clone, Debug, PartialEq)]
121pub(crate) struct LineRun {
122 pub(crate) text: IndexRange,
123 pub(crate) glyphs: IndexRange,
124 pub(crate) face: u32,
125 pub(crate) size: f32,
126 pub(crate) spacing: f32,
128 pub(crate) spaces: u16,
131 pub(crate) word_spacing: f32,
132 pub(crate) baseline_shift: f32,
134 pub(crate) level: BidiLevel,
135 pub(crate) x: f32,
138 pub(crate) width: f32,
139 pub(crate) shift: f32,
140}
141
142struct Cluster {
145 offset: usize,
146 glyphs: Range<usize>,
147 width: f32,
148}
149
150impl LineRun {
151 pub(crate) fn left(&self) -> f32 {
153 self.x + self.shift
154 }
155
156 fn right(&self) -> f32 {
158 self.left() + self.width + self.word_spacing * f32::from(self.spaces)
159 }
160
161 fn scale(&self, face: &Typeface) -> f32 {
163 self.size / f32::from(face.units_per_em())
164 }
165
166 fn clusters<'a>(
169 &'a self,
170 glyphs: RunGlyphs<'a>,
171 face: &Typeface,
172 ) -> impl Iterator<Item = Cluster> + 'a {
173 let scale = self.scale(face);
174 let space = face.space();
175 let mut at = 0;
176 std::iter::from_fn(move || {
177 let first = glyphs.get(at)?;
178 let start = at;
179 let mut width = self.spacing;
180 while let Some(glyph) = glyphs
181 .get(at)
182 .filter(|glyph| glyph.cluster == first.cluster)
183 {
184 width += font_units(glyph.advance) * scale;
185 if space == Some(glyph.id()) {
186 width += self.word_spacing;
187 }
188 at += 1;
189 }
190 Some(Cluster {
191 offset: first.text_offset(),
192 glyphs: start..at,
193 width,
194 })
195 })
196 }
197
198 fn cluster_spans<'a>(
202 &'a self,
203 (glyphs, face): (RunGlyphs<'a>, &Typeface),
204 ) -> impl Iterator<Item = (f32, Cluster, usize)> + 'a {
205 let mut clusters = self.clusters(glyphs, face).peekable();
206 let mut left = 0.0;
207 let mut previous = None;
208 std::iter::from_fn(move || {
209 let cluster = clusters.next()?;
210 let end = if self.level.is_rtl() {
211 previous.unwrap_or_else(|| self.text.end())
212 } else {
213 clusters
214 .peek()
215 .map_or_else(|| self.text.end(), |next| next.offset)
216 };
217 previous = Some(cluster.offset);
218 let at = left;
219 left += cluster.width;
220 Some((at, cluster, end))
221 })
222 }
223}
224
225#[derive(Clone, Debug, PartialEq)]
226pub(crate) struct LayoutLine {
227 pub(crate) text: IndexRange,
228 pub(crate) visible_end: u32,
230 pub(crate) runs: IndexRange,
231 pub(crate) top: f32,
232 pub(crate) baseline: f32,
233 pub(crate) bottom: f32,
234 pub(crate) left: f32,
237 pub(crate) indent: f32,
238 pub(crate) width: f32,
239 pub(crate) stretch: f32,
240 pub(crate) level: BidiLevel,
242 pub(crate) ellipsized: bool,
243 pub(crate) ends_paragraph: bool,
244}
245
246impl LayoutLine {
247 pub(crate) fn right(&self) -> f32 {
249 self.left + self.width + self.stretch
250 }
251}
252
253#[derive(Clone, Copy, Debug, PartialEq, Eq)]
256pub enum CaretDirection {
257 Primary,
260 Secondary,
262}
263
264pub struct GlyphRun<'a> {
266 pub typeface: &'a Typeface,
268 pub size: f32,
270 pub(crate) glyphs: RunGlyphs<'a>,
271 pub(crate) offsets: Option<&'a [GlyphOffset]>,
272 pub(crate) run: &'a LineRun,
273 pub(crate) pen: f32,
274 pub(crate) baseline: f32,
275}
276
277#[derive(Clone, Copy, Debug, PartialEq)]
279pub struct PositionedGlyph {
280 pub id: GlyphId,
282 pub x: f32,
284 pub y: f32,
286 pub offset: usize,
289}
290
291impl GlyphRun<'_> {
292 pub fn positions(&self) -> impl Iterator<Item = PositionedGlyph> + '_ {
295 let run = self.run;
296 let baseline = self.baseline - run.baseline_shift;
297 let scale = run.scale(self.typeface);
298 let mut pen = self.pen;
299 run.clusters(self.glyphs, self.typeface)
300 .flat_map(move |cluster| {
301 let mut x = pen + run.spacing / 2.0;
302 pen += cluster.width;
303 cluster.glyphs.filter_map(move |index| {
304 let glyph = self.glyphs.get(index)?;
305 let drawn = self
306 .offsets
307 .and_then(|offsets| offsets.get(index))
308 .copied()
309 .unwrap_or_default();
310 let at = x + font_units(drawn.x) * scale;
311 x += font_units(glyph.advance) * scale;
312 Some(PositionedGlyph {
313 id: glyph.id(),
314 x: at,
315 y: baseline - font_units(drawn.y) * scale,
316 offset: cluster.offset,
317 })
318 })
319 })
320 }
321
322 pub fn text(&self) -> Range<usize> {
324 self.run.text.range()
325 }
326
327 pub fn baseline(&self) -> f32 {
329 self.baseline - self.run.baseline_shift
330 }
331
332 pub fn edges(&self) -> (f32, f32) {
334 (self.pen, self.pen + self.run.right() - self.run.left())
335 }
336}
337
338impl TextLayout {
339 pub(crate) fn new(
343 mut body: LayoutBody,
344 wrap: f32,
345 widths: (f32, f32),
346 (reach, synthesized): (InkReach, SynthesizedStyle),
347 ) -> Self {
348 let width = body.width_between(widths.0, widths.1);
349 (body.ink_reach, body.synthesized) = (reach, synthesized);
350 let glyph_reach = glyph_reach(&body, reach);
351 Self {
352 did_overflow_width: body.widest > width || width > widths.1,
353 body: Arc::new(body),
354 width,
355 wrap,
356 id: next_id(),
357 glyph_reach,
358 }
359 }
360
361 pub(crate) fn refill(
366 &mut self,
367 widths: (f32, f32),
368 fill: impl FnOnce(&mut LayoutBody) -> (f32, (InkReach, SynthesizedStyle)),
369 ) {
370 if Arc::get_mut(&mut self.body).is_none() {
371 self.body = Arc::default();
372 }
373 let body = Arc::make_mut(&mut self.body);
374 (self.wrap, (body.ink_reach, body.synthesized)) = fill(body);
375 self.width = body.width_between(widths.0, widths.1);
376 self.did_overflow_width = body.widest > self.width || self.width > widths.1;
377 self.glyph_reach = glyph_reach(body, body.ink_reach);
378 self.id = next_id();
379 }
380
381 pub(crate) fn body(&self) -> &LayoutBody {
383 &self.body
384 }
385
386 pub fn id(&self) -> u32 {
390 self.id
391 }
392
393 pub fn holds(&self, min_width: f32, max_width: f32) -> bool {
398 self.holds_wrap(self.body.wrap_width(min_width, max_width))
399 }
400
401 fn holds_wrap(&self, wrap: f32) -> bool {
403 (wrap - self.wrap).abs() < HOLD_MARGIN
404 || self
405 .body
406 .hold
407 .is_some_and(|hold| hold.min + HOLD_MARGIN <= wrap && wrap + HOLD_MARGIN < hold.max)
408 }
409
410 pub fn width_between(&self, min_width: f32, max_width: f32) -> f32 {
415 self.body.width_between(min_width, max_width)
416 }
417
418 pub fn max_widths_kept(&self, min_width: f32) -> (f32, f32) {
426 let body = &self.body;
427 if !body.follows_max {
428 (f32::NEG_INFINITY, f32::INFINITY)
429 } else if self.width >= body.content_width.max(min_width) {
430 (self.width, f32::INFINITY)
431 } else if let Some(hold) = body
432 .hold
433 .filter(|_| body.line_width.max(min_width) <= self.wrap)
434 {
435 let low = (hold.min + HOLD_MARGIN).max(self.width);
436 let high = (hold.max - HOLD_MARGIN).min(body.content_width).next_down();
437 if low <= high {
438 (low, high)
439 } else {
440 (self.wrap, self.wrap)
441 }
442 } else {
443 (self.wrap, self.wrap)
444 }
445 }
446
447 pub fn align(&mut self, min_width: f32, max_width: f32, align: TextAlign) {
451 self.place(
452 self.body.wrap_width(min_width, max_width),
453 (min_width, max_width),
454 align,
455 );
456 }
457
458 pub fn try_align(&mut self, min_width: f32, max_width: f32, align: TextAlign) -> bool {
462 let wrap = self.body.wrap_width(min_width, max_width);
463 let held = self.holds_wrap(wrap);
464 if held {
465 self.place(wrap, (min_width, max_width), align);
466 }
467 held
468 }
469
470 fn place(&mut self, wrap: f32, (min_width, max_width): (f32, f32), align: TextAlign) {
473 self.wrap = wrap;
474 self.width = self.body.width_at(wrap, min_width);
475 self.did_overflow_width = self.body.widest > self.width || self.width > max_width;
476 if self.body.lines_move(self.width, align) {
479 self.move_lines(align);
480 }
481 }
482
483 #[inline(never)]
487 fn move_lines(&mut self, align: TextAlign) {
488 let body = Arc::make_mut(&mut self.body);
489 if body.align_lines(self.width, align) {
490 self.glyph_reach = glyph_reach(body, body.ink_reach);
492 self.id = next_id();
493 }
494 }
495
496 pub fn width(&self) -> f32 {
499 self.width
500 }
501
502 pub fn height(&self) -> f32 {
504 self.body.height
505 }
506
507 pub fn line_advance(&self) -> f32 {
510 self.body.advance
511 }
512
513 pub fn line_count(&self) -> usize {
515 self.body.lines.len()
516 }
517
518 pub fn lines_reach(&self) -> (f32, f32) {
522 lines_reach(&self.body)
523 }
524
525 pub fn typefaces(&self) -> &[Typeface] {
527 &self.body.faces
528 }
529
530 pub fn synthesized_style(&self) -> SynthesizedStyle {
533 self.body.synthesized
534 }
535
536 pub fn glyph_reach(&self) -> Rect {
543 self.glyph_reach
544 }
545
546 pub fn did_overflow_height(&self) -> bool {
549 self.body.did_overflow_height
550 }
551
552 pub fn did_overflow_width(&self) -> bool {
555 self.did_overflow_width
556 }
557
558 pub fn is_line_ellipsized(&self, line: usize) -> bool {
560 self.body.lines[line].ellipsized
561 }
562
563 pub fn line_start(&self, line: usize) -> usize {
566 self.body.lines[line].text.start()
567 }
568
569 pub fn line_end(&self, line: usize) -> usize {
572 self.body.lines[line].text.end()
573 }
574
575 pub fn line_visible_end(&self, line: usize) -> usize {
578 widen(self.body.lines[line].visible_end)
579 }
580
581 pub fn line_top(&self, line: usize) -> f32 {
583 self.body.lines[line].top
584 }
585
586 pub fn line_bottom(&self, line: usize) -> f32 {
588 self.body.lines[line].bottom
589 }
590
591 pub fn line_baseline(&self, line: usize) -> f32 {
593 self.body.lines[line].baseline
594 }
595
596 pub fn line_left(&self, line: usize) -> f32 {
598 self.body.lines[line].left
599 }
600
601 pub fn line_right(&self, line: usize) -> f32 {
603 self.body.lines[line].right()
604 }
605
606 pub fn line_for_offset(&self, offset: usize) -> usize {
608 self.body
609 .lines
610 .partition_point(|line| line.text.end() <= offset)
611 .min(self.body.lines.len() - 1)
612 }
613
614 pub fn line_for_vertical_position(&self, y: f32) -> usize {
616 self.body
617 .lines
618 .partition_point(|line| line.bottom <= y)
619 .min(self.body.lines.len() - 1)
620 }
621
622 pub fn horizontal_position(&self, offset: usize, direction: CaretDirection) -> f32 {
626 let line = &self.body.lines[self.line_for_offset(offset)];
627 let runs = &self.body.runs[line.runs.range()];
628 let after = runs
629 .iter()
630 .find(|run| run.text.start() <= offset && offset < run.text.end());
631 let before = runs
632 .iter()
633 .find(|run| run.text.start() < offset && offset <= run.text.end());
634 let run = match (before, after) {
635 (Some(before), Some(after)) if before.level != after.level => {
636 if (before.level == line.level) == (direction == CaretDirection::Primary) {
637 before
638 } else {
639 after
640 }
641 }
642 (_, Some(run)) | (Some(run), None) => run,
643 (None, None) => {
644 return if line.level.is_rtl() {
645 line.right()
646 } else {
647 line.left
648 };
649 }
650 };
651 line.left + self.caret_in_run(run, offset)
652 }
653
654 pub fn offset_for_position(&self, x: f32, y: f32) -> usize {
656 let line = &self.body.lines[self.line_for_vertical_position(y)];
657 let runs = &self.body.runs[line.runs.range()];
658 let local = x - line.left;
659 let Some(run) = runs
660 .iter()
661 .find(|run| local < run.right())
662 .or_else(|| runs.last())
663 else {
664 return line.text.start();
665 };
666 self.offset_in_run(run, local - run.left())
667 }
668
669 pub fn glyph_runs(&self, line: usize) -> impl Iterator<Item = GlyphRun<'_>> + '_ {
671 self.body.glyph_runs(line)
672 }
673
674 pub fn bounding_box(&self, offset: usize) -> Rect {
678 let line = &self.body.lines[self.line_for_offset(offset)];
679 let found = self.body.runs[line.runs.range()].iter().find_map(|run| {
680 run.cluster_spans(self.body.run_glyphs(run))
681 .find(|(_, cluster, end)| (cluster.offset..*end).contains(&offset))
682 .map(|(left, cluster, _)| (run.left() + left, cluster.width))
683 });
684 let (x, width) = found.unwrap_or_else(|| {
685 (
686 self.horizontal_position(offset, CaretDirection::Primary) - line.left,
687 0.0,
688 )
689 });
690 Rect {
691 x: line.left + x,
692 y: line.top,
693 width,
694 height: line.bottom - line.top,
695 }
696 }
697
698 pub fn cursor_rect(&self, offset: usize) -> Rect {
701 let line = &self.body.lines[self.line_for_offset(offset)];
702 Rect {
703 x: self.horizontal_position(offset, CaretDirection::Primary),
704 y: line.top,
705 width: 0.0,
706 height: line.bottom - line.top,
707 }
708 }
709
710 pub fn selection_rects(&self, start: usize, end: usize, rects: &mut Vec<Rect>) {
717 let width = self.width;
718 for line in &self.body.lines {
719 if line.text.end() <= start || line.text.start() >= end {
720 continue;
721 }
722 let mut open: Option<(f32, f32)> = None;
723 for run in &self.body.runs[line.runs.range()] {
724 for (left, cluster, cluster_end) in run.cluster_spans(self.body.run_glyphs(run)) {
725 let x = run.left() + left;
726 let covered = cluster.offset < end && cluster_end > start;
727 match (covered, open) {
728 (true, Some((from, _))) => open = Some((from, x + cluster.width)),
729 (true, None) => open = Some((x, x + cluster.width)),
730 (false, Some(span)) => {
731 push_span(rects, (line, width), span);
732 open = None;
733 }
734 (false, None) => {}
735 }
736 }
737 }
738 if let Some(span) = open {
739 push_span(rects, (line, width), span);
740 }
741 }
742 }
743
744 fn caret_in_run(&self, run: &LineRun, offset: usize) -> f32 {
746 let (glyphs, face) = self.body.run_glyphs(run);
747 let before: f32 = run
748 .clusters(glyphs, face)
749 .filter(|cluster| cluster.offset < offset)
750 .map(|cluster| cluster.width)
751 .sum();
752 if run.level.is_rtl() {
753 run.right() - before
754 } else {
755 run.left() + before
756 }
757 }
758
759 fn offset_in_run(&self, run: &LineRun, x: f32) -> usize {
762 for (left, cluster, end) in run.cluster_spans(self.body.run_glyphs(run)) {
763 if x < left + cluster.width / 2.0 {
764 return if run.level.is_rtl() {
765 end
766 } else {
767 cluster.offset
768 };
769 }
770 }
771 if run.level.is_rtl() {
772 run.text.start()
773 } else {
774 run.text.end()
775 }
776 }
777}
778
779fn push_span(rects: &mut Vec<Rect>, (line, width): (&LayoutLine, f32), (left, right): (f32, f32)) {
784 let (low, high) = (line.left.min(0.0), width.max(line.left + line.width));
785 let (left, right) = (
786 (line.left + left).clamp(low, high),
787 (line.left + right).clamp(low, high),
788 );
789 if left < right {
790 rects.push(Rect {
791 x: left,
792 y: line.top,
793 width: right - left,
794 height: line.bottom - line.top,
795 });
796 }
797}
798
799pub(crate) fn font_units(value: i32) -> f32 {
803 value as f32
804}