Skip to main content

cranpose_text/
text_layout.rs

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/// A paragraph laid out in lines: glyph runs placed for drawing, and the
25/// queries of Compose's `TextLayoutResult`. Distances are in pixels from the
26/// layout's top left corner; offsets are byte offsets in the text. The
27/// default is empty, ready for [`TextLayouter::layout_into`].
28///
29/// [`TextLayouter::layout_into`]: crate::TextLayouter::layout_into
30#[derive(Clone, Debug, Default)]
31pub struct TextLayout {
32    // The lines and glyphs, shared by the copies aligned at other widths
33    // while their lines keep their places.
34    body: Arc<LayoutBody>,
35    width: f32,
36    // The width the lines wrap at.
37    wrap: f32,
38    did_overflow_width: bool,
39    id: u32,
40    // How far the glyphs reach, as `glyph_reach` gives it. Kept beside the
41    // body, so a renderer reads it in the layout it holds.
42    glyph_reach: Rect,
43}
44
45impl PartialEq for TextLayout {
46    fn eq(&self, other: &Self) -> bool {
47        // Copies that share a body hold the same lines and glyphs. A text
48        // laid out again into the same lines draws the same, though it
49        // takes a new id.
50        (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
56// The id the next layout made or aligned takes.
57static NEXT_ID: AtomicU32 = AtomicU32::new(1);
58
59// A new id for a layout whose glyphs or positions changed.
60fn next_id() -> u32 {
61    NEXT_ID.fetch_add(1, Ordering::Relaxed).max(1)
62}
63
64// How far the visible text of the lines of `body` reaches across, from the
65// left edge of the leftmost to the right edge of the rightmost.
66fn 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// How far the glyphs of `body`, in the places its lines now take, reach, as
77// `TextLayout::glyph_reach` gives it; `reach` tells whether a glyph may put
78// ink past its advance or its typeface's lines. Inline: each layout takes
79// its lines.
80#[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// Widths from `min` up to but not including `max`.
96#[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    // Narrows the range to the widths from `min` up to `max`.
109    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
115// How far inside a hold's ends a width must be for the layout to stand for
116// it, so that the order a sum is taken in cannot decide.
117const HOLD_MARGIN: f32 = 1e-3;
118
119// A run of one typeface, size and bidi level inside a line, in visual order.
120#[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    // Extra width of each cluster, half on each side.
127    pub(crate) spacing: f32,
128    // In a justified line, how many of the run's glyphs are spaces before
129    // the line's trailing whitespace and the extra width each of them takes.
130    pub(crate) spaces: u16,
131    pub(crate) word_spacing: f32,
132    // How far the run's baseline sits above the line's.
133    pub(crate) baseline_shift: f32,
134    pub(crate) level: BidiLevel,
135    // The left edge from the line's left edge and the width, before
136    // justification, and how far justification moves the run right.
137    pub(crate) x: f32,
138    pub(crate) width: f32,
139    pub(crate) shift: f32,
140}
141
142// One cluster of a run, left to right: the offset its text starts at, its
143// glyphs within the run and its width with the run's spacing.
144struct Cluster {
145    offset: usize,
146    glyphs: Range<usize>,
147    width: f32,
148}
149
150impl LineRun {
151    // The left edge from the line's left edge.
152    pub(crate) fn left(&self) -> f32 {
153        self.x + self.shift
154    }
155
156    // The right edge from the line's left edge.
157    fn right(&self) -> f32 {
158        self.left() + self.width + self.word_spacing * f32::from(self.spaces)
159    }
160
161    // Pixels per design unit of `face`, the run's typeface.
162    fn scale(&self, face: &Typeface) -> f32 {
163        self.size / f32::from(face.units_per_em())
164    }
165
166    // The run's clusters left to right, of its `glyphs` in its typeface
167    // `face`.
168    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    // The run's clusters left to right, each with its left edge from the
199    // run's left edge and the offset where its text ends: where the next
200    // cluster in text order starts.
201    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    // The end of the text before the line's trailing whitespace.
229    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    // The left edge of the line's visible text, its indent, its width
235    // before justification and what justification adds to it.
236    pub(crate) left: f32,
237    pub(crate) indent: f32,
238    pub(crate) width: f32,
239    pub(crate) stretch: f32,
240    // The level of the paragraph the line is in.
241    pub(crate) level: BidiLevel,
242    pub(crate) ellipsized: bool,
243    pub(crate) ends_paragraph: bool,
244}
245
246impl LayoutLine {
247    // The right edge of the visible text.
248    pub(crate) fn right(&self) -> f32 {
249        self.left + self.width + self.stretch
250    }
251}
252
253/// Which of its two places a caret takes where the text before and after it
254/// runs in different directions.
255#[derive(Clone, Copy, Debug, PartialEq, Eq)]
256pub enum CaretDirection {
257    /// The place of the paragraph's direction: Compose's
258    /// `usePrimaryDirection = true`.
259    Primary,
260    /// The other place.
261    Secondary,
262}
263
264/// The glyphs of one run of a line, ready to draw.
265pub struct GlyphRun<'a> {
266    /// The typeface the glyphs come from.
267    pub typeface: &'a Typeface,
268    /// The font size in pixels.
269    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/// A glyph placed in a layout.
278#[derive(Clone, Copy, Debug, PartialEq)]
279pub struct PositionedGlyph {
280    /// The glyph.
281    pub id: GlyphId,
282    /// Where its origin sits, from the layout's left edge.
283    pub x: f32,
284    /// Where its origin sits, from the layout's top edge.
285    pub y: f32,
286    /// The offset in the text of the character it draws, the first of its
287    /// cluster.
288    pub offset: usize,
289}
290
291impl GlyphRun<'_> {
292    /// Each glyph with the position of its origin in the layout, left to
293    /// right.
294    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    /// The text the run draws.
323    pub fn text(&self) -> Range<usize> {
324        self.run.text.range()
325    }
326
327    /// Where the run's baseline sits, from the layout's top edge.
328    pub fn baseline(&self) -> f32 {
329        self.baseline - self.run.baseline_shift
330    }
331
332    /// The run's left and right edges, from the layout's left edge.
333    pub fn edges(&self) -> (f32, f32) {
334        (self.pen, self.pen + self.run.right() - self.run.left())
335    }
336}
337
338impl TextLayout {
339    // The layout of `body`, whose lines wrap at `wrap`, laid out between
340    // `widths`, the narrowest and the widest it may be, of glyphs whose ink
341    // reaches as `reach` says that ask `synthesized` of their faces.
342    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    // Lays the layout out again between `widths`: `fill` fills its body, in
362    // the body's own storage when no copy shares it, and gives the width its
363    // lines wrap at, how the ink of its glyphs reaches and what they ask of
364    // their faces.
365    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    // The body, which the layouter reads glyphs shaped before from.
382    pub(crate) fn body(&self) -> &LayoutBody {
383        &self.body
384    }
385
386    /// A number this layout's glyphs and positions go by: a layout laid out
387    /// or aligned again takes a new one, so a renderer keys what it drew of
388    /// a layout by it; a clone keeps it.
389    pub fn id(&self) -> u32 {
390        self.id
391    }
392
393    /// Whether the text laid out again between `min_width` and `max_width`,
394    /// with the rest of its input the same, breaks into the same lines:
395    /// [`TextLayout::align`] then gives that layout without laying the text
396    /// out again.
397    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    // Whether lines that wrap at `wrap` are this layout's.
402    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    /// The width the paragraph takes laid out between `min_width` and
411    /// `max_width`: its max intrinsic width rounded up to whole device
412    /// pixels within them, the max when they are equal, and its widest line
413    /// rounded up to whole device pixels within them when its lines wrap.
414    pub fn width_between(&self, min_width: f32, max_width: f32) -> f32 {
415        self.body.width_between(min_width, max_width)
416    }
417
418    /// The max widths, from `.0` up to and with `.1`, at which the paragraph
419    /// laid out from `min_width` keeps its width and its lines: every width
420    /// when its width follows no max, every width from its own when its text
421    /// fits, else the widths its lines wrap the same at below the width it
422    /// fits at, or only the one it wraps at when those are not known or its
423    /// widest line is wider than that one: its width is then the one it
424    /// wraps at, and moves with it.
425    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    /// Places the lines across the width the paragraph takes between
448    /// `min_width` and `max_width` as `align` says, for widths the layout
449    /// [holds](TextLayout::holds).
450    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    /// [Aligns](TextLayout::align) the lines between `min_width` and
459    /// `max_width` when the layout [holds](TextLayout::holds) there; `false`,
460    /// leaving the layout as it was, when it does not.
461    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    // Places the lines, which wrap at `wrap`, across the width the
471    // paragraph takes between `(min_width, max_width)` as `align` says.
472    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        // Lines that keep their places keep the body the copies share, and
477        // the id what a renderer drew of the layout goes by.
478        if self.body.lines_move(self.width, align) {
479            self.move_lines(align);
480        }
481    }
482
483    // Places the lines across the layout's width as `align` says. Out of
484    // line: as a width animates, most lines keep their places, and a call
485    // that moves none then saves no registers.
486    #[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            // Moved across, the glyphs and the lines' heights stay.
491            self.glyph_reach = glyph_reach(body, body.ink_reach);
492            self.id = next_id();
493        }
494    }
495
496    /// The paragraph's width: its max intrinsic width rounded up to whole
497    /// device pixels, within the widths it was laid out between.
498    pub fn width(&self) -> f32 {
499        self.width
500    }
501
502    /// The height of all lines.
503    pub fn height(&self) -> f32 {
504        self.body.height
505    }
506
507    /// The distance from the first line's baseline down to the next line's:
508    /// the first line's height before the paragraph's edges trim it.
509    pub fn line_advance(&self) -> f32 {
510        self.body.advance
511    }
512
513    /// The number of lines, at least one.
514    pub fn line_count(&self) -> usize {
515        self.body.lines.len()
516    }
517
518    /// How far the visible text of the lines reaches across: the left edge
519    /// of the leftmost line's and the right edge of the rightmost line's,
520    /// both zero for the empty default.
521    pub fn lines_reach(&self) -> (f32, f32) {
522        lines_reach(&self.body)
523    }
524
525    /// The typefaces the glyphs come from.
526    pub fn typefaces(&self) -> &[Typeface] {
527        &self.body.faces
528    }
529
530    /// The bold and the italic the glyphs ask of faces that lack them, which
531    /// a renderer synthesizes and which spread their ink.
532    pub fn synthesized_style(&self) -> SynthesizedStyle {
533        self.body.synthesized
534    }
535
536    /// How far the glyphs reach, in pixels from the layout's top left
537    /// corner: across, the visible text of the lines and the ink a glyph puts
538    /// past it, as the overhang of an `f` that ends a line does; down, from
539    /// the top, or the ink above it, to the lowest ink, at the top when the
540    /// glyphs ink no lower than the box. A renderer joins it with the box it
541    /// draws the text in.
542    pub fn glyph_reach(&self) -> Rect {
543        self.glyph_reach
544    }
545
546    /// Whether the text had more lines than the line limit: Compose's
547    /// `didOverflowHeight` of a layout the line limit cut.
548    pub fn did_overflow_height(&self) -> bool {
549        self.body.did_overflow_height
550    }
551
552    /// Whether the paragraph is wider than the widest it was laid out at, or
553    /// a line wider than the paragraph: Compose's `didOverflowWidth`.
554    pub fn did_overflow_width(&self) -> bool {
555        self.did_overflow_width
556    }
557
558    /// Whether `line` shows an ellipsis in place of text.
559    pub fn is_line_ellipsized(&self, line: usize) -> bool {
560        self.body.lines[line].ellipsized
561    }
562
563    /// The offset where `line` starts. Panics when `line` is out of range, as
564    /// Compose throws.
565    pub fn line_start(&self, line: usize) -> usize {
566        self.body.lines[line].text.start()
567    }
568
569    /// The offset where `line` ends, after its line break and trailing
570    /// whitespace.
571    pub fn line_end(&self, line: usize) -> usize {
572        self.body.lines[line].text.end()
573    }
574
575    /// The offset where the visible text of `line` ends, before its line
576    /// break and trailing whitespace: Compose's `getLineEnd(line, true)`.
577    pub fn line_visible_end(&self, line: usize) -> usize {
578        widen(self.body.lines[line].visible_end)
579    }
580
581    /// The top of `line`.
582    pub fn line_top(&self, line: usize) -> f32 {
583        self.body.lines[line].top
584    }
585
586    /// The bottom of `line`.
587    pub fn line_bottom(&self, line: usize) -> f32 {
588        self.body.lines[line].bottom
589    }
590
591    /// The baseline of `line`.
592    pub fn line_baseline(&self, line: usize) -> f32 {
593        self.body.lines[line].baseline
594    }
595
596    /// The left edge of the visible text of `line`.
597    pub fn line_left(&self, line: usize) -> f32 {
598        self.body.lines[line].left
599    }
600
601    /// The right edge of the visible text of `line`.
602    pub fn line_right(&self, line: usize) -> f32 {
603        self.body.lines[line].right()
604    }
605
606    /// The line that holds `offset`; the last line for the end of the text.
607    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    /// The line at height `y`, the first above the text and the last below it.
615    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    /// The x of a caret at `offset`. Where the text before and after `offset`
623    /// runs in different directions the caret has two places, and `direction`
624    /// picks one.
625    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    /// The offset of the caret nearest to the point `x`, `y`.
655    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    /// The glyph runs of `line`, left to right.
670    pub fn glyph_runs(&self, line: usize) -> impl Iterator<Item = GlyphRun<'_>> + '_ {
671        self.body.glyph_runs(line)
672    }
673
674    /// The box of the character at `offset`, across its line: Compose's
675    /// `getBoundingBox`. A zero-width box at the caret for an offset with no
676    /// character, such as the end of the text.
677    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    /// The caret at `offset` as a zero-width rectangle across its line:
699    /// Compose's `getCursorRect`.
700    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    /// Pushes onto `rects` the rectangles that cover the text from `start`
711    /// to `end` line by line, one for each stretch of it that runs in one
712    /// direction, within the paragraph's width or its visible text: Compose's
713    /// `getPathForRange` as rectangles. The spaces a wrapped line ends with
714    /// hang past the widest line, which they do not widen, and cover nothing
715    /// there.
716    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    // The caret x of `offset` inside `run`, from the line's left edge.
745    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    // The offset nearest to `x` pixels from the left edge of `run`: each
760    // cluster's left half gives the offset on its left.
761    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
779// Adds the rectangle of `line` from `left` to `right`, from the line's left
780// edge.
781// Pushes the rectangle of `line` from `left` to `right` within the
782// paragraph's `width`, or past it as far as the line's visible text reaches.
783fn 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
799// A distance in font design units as a float, exact for every value under
800// 2^24: shaped values lie far inside it for any font, whose em is at most
801// 16,384 units.
802pub(crate) fn font_units(value: i32) -> f32 {
803    value as f32
804}