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 synthesizes: bool,
43 id: u32,
44 glyph_reach: Rect,
47}
48
49impl PartialEq for TextLayout {
50 fn eq(&self, other: &Self) -> bool {
51 (Arc::ptr_eq(&self.body, &other.body) || self.body == other.body)
55 && self.width == other.width
56 && self.did_overflow_width == other.did_overflow_width
57 }
58}
59
60static NEXT_ID: AtomicU32 = AtomicU32::new(1);
62
63fn next_id() -> u32 {
65 NEXT_ID.fetch_add(1, Ordering::Relaxed).max(1)
66}
67
68fn lines_reach(body: &LayoutBody) -> (f32, f32) {
71 body.lines
72 .iter()
73 .map(|line| (line.left, line.right()))
74 .reduce(|(left, right), (line_left, line_right)| {
75 (left.min(line_left), right.max(line_right))
76 })
77 .unwrap_or_default()
78}
79
80#[inline]
85fn glyph_reach(body: &LayoutBody, reach: InkReach) -> Rect {
86 let (left, right) = lines_reach(body);
87 let lines = Rect {
88 x: left,
89 y: 0.0,
90 width: right - left,
91 height: 0.0,
92 };
93 if !may_ink_past_lines(body, reach) {
94 return lines;
95 }
96 find_ink_past_lines(body, (left, right), reach).map_or(lines, |past| lines.union(past))
97}
98
99#[derive(Clone, Copy, Debug, PartialEq)]
101pub(crate) struct WidthRange {
102 pub(crate) min: f32,
103 pub(crate) max: f32,
104}
105
106impl WidthRange {
107 pub(crate) const ALL: Self = Self {
108 min: f32::NEG_INFINITY,
109 max: f32::INFINITY,
110 };
111
112 pub(crate) fn narrow(&mut self, min: f32, max: f32) {
114 self.min = self.min.max(min);
115 self.max = self.max.min(max);
116 }
117}
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 synthesizes: synthesized != SynthesizedStyle::default(),
354 body: Arc::new(body),
355 width,
356 wrap,
357 id: next_id(),
358 glyph_reach,
359 }
360 }
361
362 pub(crate) fn refill(
367 &mut self,
368 widths: (f32, f32),
369 fill: impl FnOnce(&mut LayoutBody) -> (f32, (InkReach, SynthesizedStyle)),
370 ) {
371 if Arc::get_mut(&mut self.body).is_none() {
372 self.body = Arc::default();
373 }
374 let body = Arc::make_mut(&mut self.body);
375 (self.wrap, (body.ink_reach, body.synthesized)) = fill(body);
376 self.synthesizes = body.synthesized != SynthesizedStyle::default();
377 self.width = body.width_between(widths.0, widths.1);
378 self.did_overflow_width = body.widest > self.width || self.width > widths.1;
379 self.glyph_reach = glyph_reach(body, body.ink_reach);
380 self.id = next_id();
381 }
382
383 pub(crate) fn body(&self) -> &LayoutBody {
385 &self.body
386 }
387
388 pub fn id(&self) -> u32 {
392 self.id
393 }
394
395 pub fn holds(&self, min_width: f32, max_width: f32) -> bool {
400 self.holds_wrap(self.body.wrap_width(min_width, max_width))
401 }
402
403 fn holds_wrap(&self, wrap: f32) -> bool {
407 match self.body.hold {
408 Some(hold) => hold.min <= wrap && wrap < hold.max,
409 None => wrap.to_bits() == self.wrap.to_bits(),
410 }
411 }
412
413 pub fn width_between(&self, min_width: f32, max_width: f32) -> f32 {
418 self.body.width_between(min_width, max_width)
419 }
420
421 pub fn max_widths_kept(&self, min_width: f32) -> (f32, f32) {
429 let body = &self.body;
430 if !body.follows_max {
431 (f32::NEG_INFINITY, f32::INFINITY)
432 } else if self.width >= body.content_width.max(min_width) {
433 (self.width, f32::INFINITY)
434 } else if let Some(hold) = body
435 .hold
436 .filter(|_| body.line_width.max(min_width) <= self.wrap)
437 {
438 let low = hold.min.max(self.width);
439 let high = hold.max.min(body.content_width).next_down();
440 if low <= high {
441 (low, high)
442 } else {
443 (self.wrap, self.wrap)
444 }
445 } else {
446 (self.wrap, self.wrap)
447 }
448 }
449
450 pub fn align(&mut self, min_width: f32, max_width: f32, align: TextAlign) {
454 self.place(
455 self.body.wrap_width(min_width, max_width),
456 (min_width, max_width),
457 align,
458 );
459 }
460
461 pub fn try_align(&mut self, min_width: f32, max_width: f32, align: TextAlign) -> bool {
465 let wrap = self.body.wrap_width(min_width, max_width);
466 let held = self.holds_wrap(wrap);
467 if held {
468 self.place(wrap, (min_width, max_width), align);
469 }
470 held
471 }
472
473 fn place(&mut self, wrap: f32, (min_width, max_width): (f32, f32), align: TextAlign) {
476 self.wrap = wrap;
477 self.width = self.body.width_at(wrap, min_width);
478 self.did_overflow_width = self.body.widest > self.width || self.width > max_width;
479 if self.body.lines_move(self.width, align) {
482 self.move_lines(align);
483 }
484 }
485
486 #[inline(never)]
490 fn move_lines(&mut self, align: TextAlign) {
491 let body = Arc::make_mut(&mut self.body);
492 if body.align_lines(self.width, align) {
493 self.glyph_reach = glyph_reach(body, body.ink_reach);
495 self.id = next_id();
496 }
497 }
498
499 pub fn width(&self) -> f32 {
502 self.width
503 }
504
505 pub fn height(&self) -> f32 {
507 self.body.height
508 }
509
510 pub fn line_advance(&self) -> f32 {
513 self.body.advance
514 }
515
516 pub fn line_count(&self) -> usize {
518 self.body.lines.len()
519 }
520
521 pub fn lines_reach(&self) -> (f32, f32) {
525 lines_reach(&self.body)
526 }
527
528 pub fn typefaces(&self) -> &[Typeface] {
530 &self.body.faces
531 }
532
533 pub fn synthesized_style(&self) -> SynthesizedStyle {
536 if self.synthesizes {
537 self.body.synthesized
538 } else {
539 SynthesizedStyle::default()
540 }
541 }
542
543 pub fn glyph_reach(&self) -> Rect {
550 self.glyph_reach
551 }
552
553 pub fn did_overflow_height(&self) -> bool {
556 self.body.did_overflow_height
557 }
558
559 pub fn did_overflow_width(&self) -> bool {
562 self.did_overflow_width
563 }
564
565 pub fn is_line_ellipsized(&self, line: usize) -> bool {
567 self.body.lines[line].ellipsized
568 }
569
570 pub fn line_start(&self, line: usize) -> usize {
573 self.body.lines[line].text.start()
574 }
575
576 pub fn line_end(&self, line: usize) -> usize {
579 self.body.lines[line].text.end()
580 }
581
582 pub fn line_visible_end(&self, line: usize) -> usize {
585 widen(self.body.lines[line].visible_end)
586 }
587
588 pub fn line_top(&self, line: usize) -> f32 {
590 self.body.lines[line].top
591 }
592
593 pub fn line_bottom(&self, line: usize) -> f32 {
595 self.body.lines[line].bottom
596 }
597
598 pub fn line_baseline(&self, line: usize) -> f32 {
600 self.body.lines[line].baseline
601 }
602
603 pub fn line_left(&self, line: usize) -> f32 {
605 self.body.lines[line].left
606 }
607
608 pub fn line_right(&self, line: usize) -> f32 {
610 self.body.lines[line].right()
611 }
612
613 pub fn line_for_offset(&self, offset: usize) -> usize {
615 self.body
616 .lines
617 .partition_point(|line| line.text.end() <= offset)
618 .min(self.body.lines.len() - 1)
619 }
620
621 pub fn line_for_vertical_position(&self, y: f32) -> usize {
623 self.body
624 .lines
625 .partition_point(|line| line.bottom <= y)
626 .min(self.body.lines.len() - 1)
627 }
628
629 pub fn horizontal_position(&self, offset: usize, direction: CaretDirection) -> f32 {
633 let line = &self.body.lines[self.line_for_offset(offset)];
634 let runs = &self.body.runs[line.runs.range()];
635 let after = runs
636 .iter()
637 .find(|run| run.text.start() <= offset && offset < run.text.end());
638 let before = runs
639 .iter()
640 .find(|run| run.text.start() < offset && offset <= run.text.end());
641 let run = match (before, after) {
642 (Some(before), Some(after)) if before.level != after.level => {
643 if (before.level == line.level) == (direction == CaretDirection::Primary) {
644 before
645 } else {
646 after
647 }
648 }
649 (_, Some(run)) | (Some(run), None) => run,
650 (None, None) => {
651 return if line.level.is_rtl() {
652 line.right()
653 } else {
654 line.left
655 };
656 }
657 };
658 line.left + self.caret_in_run(run, offset)
659 }
660
661 pub fn offset_for_position(&self, x: f32, y: f32) -> usize {
663 let line = &self.body.lines[self.line_for_vertical_position(y)];
664 let runs = &self.body.runs[line.runs.range()];
665 let local = x - line.left;
666 let Some(run) = runs
667 .iter()
668 .find(|run| local < run.right())
669 .or_else(|| runs.last())
670 else {
671 return line.text.start();
672 };
673 self.offset_in_run(run, local - run.left())
674 }
675
676 pub fn glyph_runs(&self, line: usize) -> impl Iterator<Item = GlyphRun<'_>> + '_ {
678 self.body.glyph_runs(line)
679 }
680
681 pub fn bounding_box(&self, offset: usize) -> Rect {
685 let line = &self.body.lines[self.line_for_offset(offset)];
686 let found = self.body.runs[line.runs.range()].iter().find_map(|run| {
687 run.cluster_spans(self.body.run_glyphs(run))
688 .find(|(_, cluster, end)| (cluster.offset..*end).contains(&offset))
689 .map(|(left, cluster, _)| (run.left() + left, cluster.width))
690 });
691 let (x, width) = found.unwrap_or_else(|| {
692 (
693 self.horizontal_position(offset, CaretDirection::Primary) - line.left,
694 0.0,
695 )
696 });
697 Rect {
698 x: line.left + x,
699 y: line.top,
700 width,
701 height: line.bottom - line.top,
702 }
703 }
704
705 pub fn cursor_rect(&self, offset: usize) -> Rect {
708 let line = &self.body.lines[self.line_for_offset(offset)];
709 Rect {
710 x: self.horizontal_position(offset, CaretDirection::Primary),
711 y: line.top,
712 width: 0.0,
713 height: line.bottom - line.top,
714 }
715 }
716
717 pub fn selection_rects(&self, start: usize, end: usize, rects: &mut Vec<Rect>) {
724 let width = self.width;
725 for line in &self.body.lines {
726 if line.text.end() <= start || line.text.start() >= end {
727 continue;
728 }
729 let mut open: Option<(f32, f32)> = None;
730 for run in &self.body.runs[line.runs.range()] {
731 for (left, cluster, cluster_end) in run.cluster_spans(self.body.run_glyphs(run)) {
732 let x = run.left() + left;
733 let covered = cluster.offset < end && cluster_end > start;
734 match (covered, open) {
735 (true, Some((from, _))) => open = Some((from, x + cluster.width)),
736 (true, None) => open = Some((x, x + cluster.width)),
737 (false, Some(span)) => {
738 push_span(rects, (line, width), span);
739 open = None;
740 }
741 (false, None) => {}
742 }
743 }
744 }
745 if let Some(span) = open {
746 push_span(rects, (line, width), span);
747 }
748 }
749 }
750
751 fn caret_in_run(&self, run: &LineRun, offset: usize) -> f32 {
753 let (glyphs, face) = self.body.run_glyphs(run);
754 let before: f32 = run
755 .clusters(glyphs, face)
756 .filter(|cluster| cluster.offset < offset)
757 .map(|cluster| cluster.width)
758 .sum();
759 if run.level.is_rtl() {
760 run.right() - before
761 } else {
762 run.left() + before
763 }
764 }
765
766 fn offset_in_run(&self, run: &LineRun, x: f32) -> usize {
769 for (left, cluster, end) in run.cluster_spans(self.body.run_glyphs(run)) {
770 if x < left + cluster.width / 2.0 {
771 return if run.level.is_rtl() {
772 end
773 } else {
774 cluster.offset
775 };
776 }
777 }
778 if run.level.is_rtl() {
779 run.text.start()
780 } else {
781 run.text.end()
782 }
783 }
784}
785
786fn push_span(rects: &mut Vec<Rect>, (line, width): (&LayoutLine, f32), (left, right): (f32, f32)) {
791 let (low, high) = (line.left.min(0.0), width.max(line.left + line.width));
792 let (left, right) = (
793 (line.left + left).clamp(low, high),
794 (line.left + right).clamp(low, high),
795 );
796 if left < right {
797 rects.push(Rect {
798 x: left,
799 y: line.top,
800 width: right - left,
801 height: line.bottom - line.top,
802 });
803 }
804}
805
806pub(crate) fn font_units(value: i32) -> f32 {
810 value as f32
811}