makepad_draw/text/
layouter.rs

1use {
2    super::{
3        color::Color,
4        font::{Font, FontId, GlyphId},
5        font_family::{FontFamily, FontFamilyId},
6        geom::{Point, Rect, Size},
7        loader::{self, FontDefinition, FontFamilyDefinition, Loader},
8        rasterizer::{self, RasterizedGlyph, Rasterizer},
9        sdfer,
10        selection::{Cursor, CursorPosition, Selection},
11        shaper::{self, ShapedText},
12        substr::Substr,
13    },
14    std::{
15        borrow::Borrow,
16        cell::RefCell,
17        collections::{HashMap, VecDeque},
18        hash::{Hash, Hasher},
19        rc::Rc,
20    },
21    unicode_segmentation::UnicodeSegmentation,
22};
23
24const LPXS_PER_INCH: f32 = 96.0;
25const PTS_PER_INCH: f32 = 72.0;
26
27#[derive(Debug)]
28pub struct Layouter {
29    loader: Loader,
30    cache_size: usize,
31    cached_params: VecDeque<OwnedLayoutParams>,
32    cached_results: HashMap<OwnedLayoutParams, Rc<LaidoutText>>,
33}
34
35impl Layouter {
36    pub fn new(settings: Settings) -> Self {
37        Self {
38            loader: Loader::new(settings.loader),
39            cache_size: settings.cache_size,
40            cached_params: VecDeque::with_capacity(settings.cache_size),
41            cached_results: HashMap::with_capacity(settings.cache_size),
42        }
43    }
44
45    pub fn rasterizer(&self) -> &Rc<RefCell<Rasterizer>> {
46        self.loader.rasterizer()
47    }
48
49    pub fn is_font_family_known(&self, id: FontFamilyId) -> bool {
50        self.loader.is_font_family_known(id)
51    }
52
53    pub fn is_font_known(&self, id: FontId) -> bool {
54        self.loader.is_font_known(id)
55    }
56
57    pub fn define_font_family(&mut self, id: FontFamilyId, definition: FontFamilyDefinition) {
58        self.loader.define_font_family(id, definition);
59    }
60
61    pub fn define_font(&mut self, id: FontId, definition: FontDefinition) {
62        self.loader.define_font(id, definition);
63    }
64
65    pub fn get_or_layout(&mut self, params: impl LayoutParams) -> Rc<LaidoutText> {
66        if let Some(result) = self.cached_results.get(&params as &dyn LayoutParams) {
67            return result.clone();
68        }
69        if self.cached_params.len() == self.cache_size {
70            let params = self.cached_params.pop_front().unwrap();
71            self.cached_results.remove(&params);
72        }
73        let params = params.to_owned();
74        let result = Rc::new(self.layout(params.clone()));
75        self.cached_params.push_back(params.clone());
76        self.cached_results.insert(params, result.clone());
77        result
78    }
79
80    fn layout(&mut self, params: OwnedLayoutParams) -> LaidoutText {
81        LayoutContext::new(&mut self.loader, params.text, params.options).layout(&params.spans)
82    }
83}
84
85#[derive(Clone, Copy, Debug)]
86pub struct Settings {
87    pub loader: loader::Settings,
88    pub cache_size: usize,
89}
90
91impl Default for Settings {
92    fn default() -> Self {
93        let r = Self {
94            loader: loader::Settings {
95                shaper: shaper::Settings { cache_size: 4096 },
96                rasterizer: rasterizer::Settings {
97                    sdfer: sdfer::Settings {
98                        padding: 4,
99                        radius: 8.0,
100                        cutoff: 0.25,
101                    },
102                    grayscale_atlas_size: Size::new(4096, 4096),
103                    color_atlas_size: Size::new(2048, 2048),
104                },
105            },
106            cache_size: 4096,
107        };
108        r
109    }
110}
111
112#[derive(Debug)]
113struct LayoutContext<'a> {
114    loader: &'a mut Loader,
115    text: Substr,
116    options: LayoutOptions,
117    current_point_in_lpxs: Point<f32>,
118    current_row_start: usize,
119    current_row_end: usize,
120    rows: Vec<LaidoutRow>,
121    glyphs: Vec<LaidoutGlyph>,
122}
123
124impl<'a> LayoutContext<'a> {
125    fn new(loader: &'a mut Loader, text: Substr, options: LayoutOptions) -> Self {
126        Self {
127            loader,
128            text,
129            options,
130            current_point_in_lpxs: Point::new(options.first_row_indent_in_lpxs, 0.0),
131            current_row_start: 0,
132            current_row_end: 0,
133            rows: Vec::new(),
134            glyphs: Vec::new(),
135        }
136    }
137
138    fn current_row_is_first(&self) -> bool {
139        self.rows.is_empty()
140    }
141
142    fn current_row_is_continuation(&self) -> bool {
143        self.current_row_is_first() && self.options.first_row_indent_in_lpxs > 0.0
144    }
145
146    fn current_row_is_empty(&self) -> bool {
147        self.current_row_start == self.current_row_end
148    }
149
150    fn current_row_len(&self) -> usize {
151        self.current_row_end - self.current_row_start
152    }
153
154    fn span_text(&self, len: usize) -> Substr {
155        self.text
156            .substr(self.current_row_end..self.current_row_end + len)
157    }
158
159    fn remaining_width_in_lpxs(&self) -> Option<f32> {
160        if self.options.wrap {
161            self.options
162                .max_width_in_lpxs
163                .map(|max_width_in_lpxs| max_width_in_lpxs - self.current_point_in_lpxs.x)
164        } else {
165            None
166        }
167    }
168
169    fn layout(mut self, spans: &[Span]) -> LaidoutText {
170        for (span_index, span) in spans.iter().enumerate() {
171            self.layout_span_multiline(span, span_index == spans.len() - 1);
172        }
173        self.finish()
174    }
175
176    fn layout_span_multiline(&mut self, span: &Span, is_last: bool) {
177        let font_family = self
178            .loader
179            .get_or_load_font_family(span.style.font_family_id)
180            .clone();
181        for (line_index, len) in self
182            .span_text(span.len)
183            .split('\n')
184            .map(|line| line.len())
185            .enumerate()
186        {
187            if line_index != 0 {
188                self.finish_current_row(&font_family, span.style, true);
189            }
190            self.layout_span(&font_family, span.style, len);
191        }
192        if is_last {
193            self.finish_current_row(&font_family, span.style, false);
194        }
195    }
196
197    fn layout_span(&mut self, font_family: &Rc<FontFamily>, style: Style, len: usize) {
198        if self.remaining_width_in_lpxs().is_none() {
199            self.layout_span_directly(font_family, style, len);
200        } else {
201            self.layout_span_by_word(font_family, style, len);
202        }
203    }
204
205    fn layout_span_by_word(&mut self, font_family: &Rc<FontFamily>, style: Style, len: usize) {
206        let mut fitter = Fitter::new(
207            self.span_text(len),
208            font_family.clone(),
209            style.font_size_in_lpxs(),
210            SegmentKind::Word,
211        );
212        while !fitter.is_empty() {
213            match fitter.fit(self.remaining_width_in_lpxs().unwrap()) {
214                Some(text) => self.append_text(style, &text),
215                None => {
216                    if self.current_row_is_empty() && !self.current_row_is_continuation() {
217                        self.layout_span_by_grapheme(font_family, style, fitter.pop());
218                    } else {
219                        self.finish_current_row(font_family, style, false);
220                    }
221                }
222            }
223        }
224    }
225
226    fn layout_span_by_grapheme(&mut self, font_family: &Rc<FontFamily>, style: Style, len: usize) {
227        let mut fitter = Fitter::new(
228            self.span_text(len),
229            font_family.clone(),
230            style.font_size_in_lpxs(),
231            SegmentKind::Grapheme,
232        );
233        while !fitter.is_empty() {
234            match fitter.fit(self.remaining_width_in_lpxs().unwrap()) {
235                Some(text) => self.append_text(style, &text),
236                None => {
237                    if self.current_row_is_empty() {
238                        self.layout_span_directly(font_family, style, fitter.pop());
239                    } else {
240                        self.finish_current_row(font_family, style, false);
241                    }
242                }
243            }
244        }
245    }
246
247    fn layout_span_directly(&mut self, font_family: &FontFamily, style: Style, len: usize) {
248        self.append_text(
249            style,
250            &font_family.get_or_shape(
251                self.text
252                    .substr(self.current_row_end..self.current_row_end + len),
253            ),
254        );
255    }
256
257    fn append_text(&mut self, style: Style, text: &ShapedText) {
258        use super::num::Zero;
259
260        for glyph in &text.glyphs {
261            let mut glyph = LaidoutGlyph {
262                origin_in_lpxs: Point::ZERO,
263                font: glyph.font.clone(),
264                font_size_in_lpxs: style.font_size_in_lpxs(),
265                color: style.color,
266                id: glyph.id,
267                cluster: self.current_row_len() + glyph.cluster,
268                advance_in_ems: glyph.advance_in_ems,
269                offset_in_ems: glyph.offset_in_ems,
270            };
271            glyph.origin_in_lpxs.x = self.current_point_in_lpxs.x;
272            self.current_point_in_lpxs.x += glyph.advance_in_lpxs();
273            self.glyphs.push(glyph);
274        }
275        self.current_row_end += text.text.len();
276    }
277
278    fn finish_current_row(
279        &mut self,
280        fallback_font_family: &FontFamily,
281        fallback_style: Style,
282        newline: bool,
283    ) {
284        use {super::num::Zero, std::mem};
285
286        let glyphs = mem::take(&mut self.glyphs);
287        let fallback_font = fallback_font_family.fonts().get(0);
288        let fallback_font_size_in_lpxs = fallback_style.font_size_in_lpxs();
289        let fallback_ascender_in_lpxs =
290            fallback_font.map_or(0.0, |font| font.ascender_in_ems()) * fallback_font_size_in_lpxs;
291        let fallback_descender_in_lpxs =
292            fallback_font.map_or(0.0, |font| font.descender_in_ems()) * fallback_font_size_in_lpxs;
293        let fallback_line_gap_in_lpxs =
294            fallback_font.map_or(0.0, |font| font.line_gap_in_ems()) * fallback_font_size_in_lpxs;
295
296        let text = self
297            .text
298            .substr(self.current_row_start..self.current_row_end);
299        let width_in_lpxs = self.current_point_in_lpxs.x;
300        let ascender_in_lpxs = glyphs
301            .iter()
302            .map(|glyph| glyph.ascender_in_lpxs())
303            .reduce(f32::max)
304            .unwrap_or(fallback_ascender_in_lpxs);
305        let descender_in_lpxs = glyphs
306            .iter()
307            .map(|glyph| glyph.descender_in_lpxs())
308            .reduce(f32::min)
309            .unwrap_or(fallback_descender_in_lpxs);
310        let line_gap_in_lpxs = glyphs
311            .iter()
312            .map(|glyph| glyph.line_gap_in_lpxs())
313            .reduce(f32::max)
314            .unwrap_or(fallback_line_gap_in_lpxs);
315        let line_spacing_scale = self.options.line_spacing_scale;
316        let line_spacing_above_in_lpxs = ascender_in_lpxs * line_spacing_scale;
317        let line_spacing_below_in_lpxs =
318            (-descender_in_lpxs + line_gap_in_lpxs) * line_spacing_scale;
319        let line_spacing_below_in_lpxs =
320            line_spacing_below_in_lpxs.max(if self.current_row_is_first() {
321                self.options.first_row_min_line_spacing_below_in_lpxs - line_spacing_above_in_lpxs
322            } else {
323                0.0
324            });
325        let mut row = LaidoutRow {
326            origin_in_lpxs: Point::ZERO,
327            text,
328            newline,
329            width_in_lpxs,
330            ascender_in_lpxs,
331            descender_in_lpxs,
332            line_gap_in_lpxs,
333            line_spacing_above_in_lpxs,
334            line_spacing_below_in_lpxs,
335            glyphs,
336        };
337
338        self.current_point_in_lpxs.x = 0.0;
339        self.current_point_in_lpxs.y += self.rows.last().map_or(row.ascender_in_lpxs, |prev_row| {
340            prev_row.line_spacing_in_lpxs(&row)
341        });
342        let max_width_in_lpxs = self.options.max_width_in_lpxs.unwrap_or(row.width_in_lpxs);
343        let remaining_width_in_lpxs = max_width_in_lpxs - row.width_in_lpxs;
344        row.origin_in_lpxs.x = self.options.align * remaining_width_in_lpxs;
345        row.origin_in_lpxs.y = self.current_point_in_lpxs.y;
346        self.current_row_start = self.current_row_end;
347        if newline {
348            self.current_row_start += 1;
349            self.current_row_end += 1;
350        }
351        self.rows.push(row);
352    }
353
354    fn finish(self) -> LaidoutText {
355        LaidoutText {
356            text: self.text,
357            size_in_lpxs: Size::new(
358                self.rows
359                    .iter()
360                    .map(|row| row.width_in_lpxs)
361                    .reduce(f32::max)
362                    .unwrap_or(0.0),
363                self.current_point_in_lpxs.y - self.rows.last().unwrap().descender_in_lpxs,
364            ),
365            rows: self.rows,
366        }
367    }
368}
369
370#[derive(Debug)]
371struct Fitter {
372    text: Substr,
373    font_family: Rc<FontFamily>,
374    font_size_in_lpxs: f32,
375    lens: Vec<usize>,
376    widths_in_lpxs: Vec<f32>,
377}
378
379impl Fitter {
380    fn new(
381        text: Substr,
382        font_family: Rc<FontFamily>,
383        font_size_in_lpxs: f32,
384        segment_kind: SegmentKind,
385    ) -> Self {
386        let lens: Vec<_> = match segment_kind {
387            SegmentKind::Word => text
388                .split_word_bounds()
389                .map(|segment| segment.len())
390                .collect(),
391            SegmentKind::Grapheme => text.graphemes(true).map(|segment| segment.len()).collect(),
392        };
393        let widths_in_lpxs: Vec<_> = lens
394            .iter()
395            .copied()
396            .scan(0, |state, len| {
397                let start = *state;
398                let end = start + len;
399                let segment = font_family.get_or_shape(text.substr(start..end));
400                let width_in_lpxs = segment.width_in_ems * font_size_in_lpxs;
401                *state = end;
402                Some(width_in_lpxs)
403            })
404            .collect();
405        Self {
406            text,
407            font_family,
408            font_size_in_lpxs,
409            lens,
410            widths_in_lpxs,
411        }
412    }
413
414    fn is_empty(&self) -> bool {
415        self.text.is_empty()
416    }
417
418    fn fit(&mut self, wrap_width_in_lpxs: f32) -> Option<Rc<ShapedText>> {
419        let mut min_count = 1;
420        let mut max_count = self.lens.len() + 1;
421        let mut best_count = None;
422        while min_count < max_count {
423            let mid_count = (min_count + max_count) / 2;
424            if self.can_fit(mid_count, wrap_width_in_lpxs) {
425                best_count = Some(mid_count);
426                min_count = mid_count + 1;
427            } else {
428                max_count = mid_count;
429            }
430        }
431        if let Some(best_count) = best_count {
432            let best_len = self.lens[..best_count].iter().sum();
433            let best_text = self.font_family.get_or_shape(self.text.substr(0..best_len));
434            self.lens.drain(..best_count);
435            self.widths_in_lpxs.drain(..best_count);
436            self.text = self.text.substr(best_len..);
437            Some(best_text)
438        } else {
439            None
440        }
441    }
442
443    fn can_fit(&self, count: usize, wrap_width_in_lpxs: f32) -> bool {
444        let len = self.lens[..count].iter().sum();
445        let estimated_width_in_lpxs: f32 = self.widths_in_lpxs[..count].iter().sum();
446        if 0.5 * estimated_width_in_lpxs > wrap_width_in_lpxs {
447            return false;
448        }
449        let text = self.font_family.get_or_shape(self.text.substr(0..len));
450        let actual_width_in_lpxs = text.width_in_ems * self.font_size_in_lpxs;
451        if actual_width_in_lpxs > wrap_width_in_lpxs {
452            return false;
453        }
454        true
455    }
456
457    fn pop(&mut self) -> usize {
458        let len = self.lens.remove(0);
459        self.widths_in_lpxs.remove(0);
460        self.text = self.text.substr(len..);
461        len
462    }
463}
464
465#[derive(Clone, Copy, Debug)]
466enum SegmentKind {
467    Word,
468    Grapheme,
469}
470
471pub trait LayoutParams {
472    fn to_owned(self) -> OwnedLayoutParams;
473    fn text(&self) -> &str;
474    fn spans(&self) -> &[Span];
475    fn options(&self) -> LayoutOptions;
476}
477
478impl Eq for dyn LayoutParams + '_ {}
479
480impl Hash for dyn LayoutParams + '_ {
481    fn hash<H>(&self, hasher: &mut H)
482    where
483        H: Hasher,
484    {
485        self.text().hash(hasher);
486        self.spans().hash(hasher);
487        self.options().hash(hasher);
488    }
489}
490
491impl PartialEq for dyn LayoutParams + '_ {
492    fn eq(&self, other: &Self) -> bool {
493        if self.text() != other.text() {
494            return false;
495        }
496        if self.spans() != other.spans() {
497            return false;
498        }
499        if self.options() != other.options() {
500            return false;
501        }
502        true
503    }
504}
505
506#[derive(Clone, Debug, Eq, Hash, PartialEq)]
507pub struct OwnedLayoutParams {
508    pub text: Substr,
509    pub spans: Rc<[Span]>,
510    pub options: LayoutOptions,
511}
512
513impl<'a> Borrow<dyn LayoutParams + 'a> for OwnedLayoutParams {
514    fn borrow(&self) -> &(dyn LayoutParams + 'a) {
515        self
516    }
517}
518
519impl LayoutParams for OwnedLayoutParams {
520    fn to_owned(self) -> Self {
521        self
522    }
523
524    fn text(&self) -> &str {
525        &self.text
526    }
527
528    fn spans(&self) -> &[Span] {
529        &self.spans
530    }
531
532    fn options(&self) -> LayoutOptions {
533        self.options
534    }
535}
536
537#[derive(Clone, Copy, Debug)]
538pub struct BorrowedLayoutParams<'a> {
539    pub text: &'a str,
540    pub spans: &'a [Span],
541    pub options: LayoutOptions,
542}
543
544impl<'a> Borrow<dyn LayoutParams + 'a> for BorrowedLayoutParams<'a> {
545    fn borrow(&self) -> &(dyn LayoutParams + 'a) {
546        self
547    }
548}
549
550impl<'a> LayoutParams for BorrowedLayoutParams<'a> {
551    fn to_owned(self) -> OwnedLayoutParams {
552        OwnedLayoutParams {
553            text: self.text.into(),
554            spans: self.spans.into(),
555            options: self.options,
556        }
557    }
558
559    fn text(&self) -> &str {
560        self.text
561    }
562
563    fn spans(&self) -> &[Span] {
564        self.spans
565    }
566
567    fn options(&self) -> LayoutOptions {
568        self.options
569    }
570}
571
572#[derive(Clone, Debug, Eq, Hash, PartialEq)]
573pub struct Span {
574    pub style: Style,
575    pub len: usize,
576}
577
578#[derive(Clone, Copy, Debug)]
579pub struct Style {
580    pub font_family_id: FontFamilyId,
581    pub font_size_in_pts: f32,
582    pub color: Option<Color>,
583}
584
585impl Style {
586    fn font_size_in_lpxs(&self) -> f32 {
587        self.font_size_in_pts * LPXS_PER_INCH / PTS_PER_INCH
588    }
589}
590
591impl Eq for Style {}
592
593impl Hash for Style {
594    fn hash<H>(&self, hasher: &mut H)
595    where
596        H: Hasher,
597    {
598        self.font_family_id.hash(hasher);
599        self.font_size_in_pts.to_bits().hash(hasher);
600        self.color.hash(hasher);
601    }
602}
603
604impl PartialEq for Style {
605    fn eq(&self, other: &Self) -> bool {
606        if self.font_family_id != other.font_family_id {
607            return false;
608        }
609        if self.font_size_in_lpxs().to_bits() != other.font_size_in_lpxs().to_bits() {
610            return false;
611        }
612        if self.color != other.color {
613            return false;
614        }
615        true
616    }
617}
618
619#[derive(Clone, Copy, Debug)]
620pub struct LayoutOptions {
621    pub first_row_indent_in_lpxs: f32,
622    pub first_row_min_line_spacing_below_in_lpxs: f32,
623    pub max_width_in_lpxs: Option<f32>,
624    pub wrap: bool,
625    pub align: f32,
626    pub line_spacing_scale: f32,
627}
628
629impl Default for LayoutOptions {
630    fn default() -> Self {
631        Self {
632            first_row_indent_in_lpxs: 0.0,
633            first_row_min_line_spacing_below_in_lpxs: 0.0,
634            max_width_in_lpxs: None,
635            wrap: false,
636            align: 0.0,
637            line_spacing_scale: 1.0,
638        }
639    }
640}
641
642impl Eq for LayoutOptions {}
643
644impl Hash for LayoutOptions {
645    fn hash<H>(&self, hasher: &mut H)
646    where
647        H: Hasher,
648    {
649        self.first_row_indent_in_lpxs.to_bits().hash(hasher);
650        self.first_row_min_line_spacing_below_in_lpxs
651            .to_bits()
652            .hash(hasher);
653        self.max_width_in_lpxs.map(f32::to_bits).hash(hasher);
654        self.align.to_bits().hash(hasher);
655        self.line_spacing_scale.to_bits().hash(hasher);
656    }
657}
658
659impl PartialEq for LayoutOptions {
660    fn eq(&self, other: &Self) -> bool {
661        if self.first_row_indent_in_lpxs.to_bits() != other.first_row_indent_in_lpxs.to_bits() {
662            return false;
663        }
664        if self.first_row_min_line_spacing_below_in_lpxs.to_bits()
665            != other.first_row_min_line_spacing_below_in_lpxs.to_bits()
666        {
667            return false;
668        }
669        if self.max_width_in_lpxs.map(f32::to_bits) != other.max_width_in_lpxs.map(f32::to_bits) {
670            return false;
671        }
672        if self.align != other.align {
673            return false;
674        }
675        true
676    }
677}
678
679#[derive(Clone, Debug)]
680pub struct LaidoutText {
681    pub text: Substr,
682    pub size_in_lpxs: Size<f32>,
683    pub rows: Vec<LaidoutRow>,
684}
685
686impl LaidoutText {
687    pub fn cursor_to_position(&self, cursor: Cursor) -> CursorPosition {
688        let row_index = self.cursor_to_row_index(cursor);
689        let row = &self.rows[row_index];
690        let x_in_lpxs = row.index_to_x_in_lpxs(cursor.index - row.text.start_in_parent());
691        CursorPosition {
692            row_index,
693            x_in_lpxs,
694        }
695    }
696
697    fn cursor_to_row_index(&self, cursor: Cursor) -> usize {
698        for (row_index, row) in self.rows.iter().enumerate() {
699            if cursor.index < row.text.end_in_parent() {
700                return row_index;
701            }
702            if cursor.index == row.text.end_in_parent() {
703                if row.newline || !cursor.prefer_next_row {
704                    return row_index;
705                }
706            }
707        }
708        self.rows.len() - 1
709    }
710
711    pub fn point_in_lpxs_to_cursor(&self, point_in_lpxs: Point<f32>) -> Cursor {
712        let row_index = self.y_in_lpxs_to_row_index(point_in_lpxs.y);
713        self.position_to_cursor(CursorPosition {
714            row_index,
715            x_in_lpxs: point_in_lpxs.x,
716        })
717    }
718
719    fn y_in_lpxs_to_row_index(&self, y_in_lpxs: f32) -> usize {
720        if y_in_lpxs < 0.0 {
721            return 0;
722        }
723        for (row_index, row) in self.rows.iter().enumerate() {
724            let line_spacing_in_lpxs = self
725                .rows
726                .get(row_index + 1)
727                .map_or(0.0, |next_row| row.line_spacing_in_lpxs(next_row));
728            if y_in_lpxs < row.origin_in_lpxs.y + 0.5 * line_spacing_in_lpxs {
729                return row_index;
730            }
731        }
732        self.rows.len() - 1
733    }
734
735    pub fn position_to_cursor(&self, position: CursorPosition) -> Cursor {
736        let row = &self.rows[position.row_index];
737        let index = row.x_in_lpxs_to_index(position.x_in_lpxs);
738        Cursor {
739            index: row.text.start_in_parent() + index,
740            prefer_next_row: if index == 0 { true } else { false },
741        }
742    }
743
744    pub fn selection_rects_in_lpxs(&self, selection: Selection) -> Vec<Rect<f32>> {
745        let CursorPosition {
746            row_index: start_row_index,
747            x_in_lpxs: start_x_in_lpxs,
748        } = self.cursor_to_position(selection.start());
749        let CursorPosition {
750            row_index: end_row_index,
751            x_in_lpxs: end_x_in_lpxs,
752        } = self.cursor_to_position(selection.end());
753        let mut rects_in_lpxs = Vec::new();
754        if start_row_index == end_row_index {
755            let row = &self.rows[start_row_index];
756            rects_in_lpxs.push(Rect::new(
757                Point::new(start_x_in_lpxs, row.origin_in_lpxs.y - row.ascender_in_lpxs),
758                Size::new(
759                    end_x_in_lpxs - start_x_in_lpxs,
760                    row.ascender_in_lpxs - row.descender_in_lpxs,
761                ),
762            ));
763        } else {
764            let start_row = &self.rows[start_row_index];
765            let end_row = &self.rows[end_row_index];
766            rects_in_lpxs.push(Rect::new(
767                Point::new(
768                    start_x_in_lpxs,
769                    start_row.origin_in_lpxs.y - start_row.ascender_in_lpxs,
770                ),
771                Size::new(
772                    start_row.width_in_lpxs - start_x_in_lpxs,
773                    start_row.ascender_in_lpxs - start_row.descender_in_lpxs,
774                ),
775            ));
776            for row_index in start_row_index + 1..end_row_index {
777                let row = &self.rows[row_index];
778                rects_in_lpxs.push(Rect::new(
779                    Point::new(
780                        row.origin_in_lpxs.x,
781                        row.origin_in_lpxs.y - row.ascender_in_lpxs,
782                    ),
783                    Size::new(
784                        row.width_in_lpxs,
785                        row.ascender_in_lpxs - row.descender_in_lpxs,
786                    ),
787                ));
788            }
789            rects_in_lpxs.push(Rect::new(
790                Point::new(0.0, end_row.origin_in_lpxs.y - end_row.ascender_in_lpxs),
791                Size::new(
792                    end_x_in_lpxs,
793                    end_row.ascender_in_lpxs - end_row.descender_in_lpxs,
794                ),
795            ));
796        }
797        rects_in_lpxs
798    }
799}
800
801#[derive(Clone, Debug)]
802pub struct LaidoutRow {
803    pub origin_in_lpxs: Point<f32>,
804    pub text: Substr,
805    pub newline: bool,
806    pub width_in_lpxs: f32,
807    pub ascender_in_lpxs: f32,
808    pub descender_in_lpxs: f32,
809    pub line_gap_in_lpxs: f32,
810    pub line_spacing_above_in_lpxs: f32,
811    pub line_spacing_below_in_lpxs: f32,
812    pub glyphs: Vec<LaidoutGlyph>,
813}
814
815impl LaidoutRow {
816    pub fn line_spacing_in_lpxs(&self, next_row: &LaidoutRow) -> f32 {
817        self.line_spacing_below_in_lpxs + next_row.line_spacing_above_in_lpxs
818    }
819
820    pub fn x_in_lpxs_to_index(&self, x_in_lpxs: f32) -> usize {
821        use {super::slice::SliceExt, unicode_segmentation::UnicodeSegmentation};
822
823        let mut glyph_groups = self
824            .glyphs
825            .group_by(|glyph_0, glyph_1| glyph_0.cluster == glyph_1.cluster)
826            .peekable();
827        while let Some(glyph_group) = glyph_groups.next() {
828            let start = glyph_group[0].cluster;
829            let start_x_in_lpxs = glyph_group[0].origin_in_lpxs.x;
830            let next_glyph_group = glyph_groups.peek();
831            let end = next_glyph_group.map_or(self.text.len(), |next_glyph_group| {
832                next_glyph_group[0].cluster
833            });
834            let end_x_in_lpxs = next_glyph_group.map_or(self.width_in_lpxs, |next_glyph_group| {
835                next_glyph_group[0].origin_in_lpxs.x
836            });
837            let width_in_lpxs = end_x_in_lpxs - start_x_in_lpxs;
838            let grapheme_count = self.text[start..end].graphemes(true).count();
839            let grapheme_width_in_lpxs = width_in_lpxs / grapheme_count as f32;
840            let mut current_x_in_lpxs = start_x_in_lpxs;
841            for (grapheme_start, _) in self.text[start..end].grapheme_indices(true) {
842                if x_in_lpxs < current_x_in_lpxs + 0.5 * grapheme_width_in_lpxs {
843                    return start + grapheme_start;
844                }
845                current_x_in_lpxs += grapheme_width_in_lpxs;
846            }
847        }
848        self.text.len()
849    }
850
851    pub fn index_to_x_in_lpxs(&self, index: usize) -> f32 {
852        use {super::slice::SliceExt, unicode_segmentation::UnicodeSegmentation};
853
854        let mut glyph_groups = self
855            .glyphs
856            .group_by(|glyph_0, glyph_1| glyph_0.cluster == glyph_1.cluster)
857            .peekable();
858        while let Some(glyph_group) = glyph_groups.next() {
859            let start = glyph_group[0].cluster;
860            let start_x_in_lpxs = glyph_group[0].origin_in_lpxs.x;
861            let end = glyph_groups
862                .peek()
863                .map_or(self.text.len(), |next_glyph_group| {
864                    next_glyph_group[0].cluster
865                });
866            let end_x_in_lpxs = glyph_groups
867                .peek()
868                .map_or(self.width_in_lpxs, |next_glyph_group| {
869                    next_glyph_group[0].origin_in_lpxs.x
870                });
871            let width_in_lpxs = end_x_in_lpxs - start_x_in_lpxs;
872            let grapheme_count = self.text[start..end].graphemes(true).count();
873            let grapheme_width_in_lpxs = width_in_lpxs / grapheme_count as f32;
874            let mut current_x_in_lpxs = start_x_in_lpxs;
875            for (grapheme_start, _) in self.text[start..end].grapheme_indices(true) {
876                let grapheme_start = start + grapheme_start;
877                if index == grapheme_start {
878                    return current_x_in_lpxs;
879                }
880                current_x_in_lpxs += grapheme_width_in_lpxs;
881            }
882        }
883        self.width_in_lpxs
884    }
885}
886
887#[derive(Clone, Debug)]
888pub struct LaidoutGlyph {
889    pub origin_in_lpxs: Point<f32>,
890    pub font: Rc<Font>,
891    pub font_size_in_lpxs: f32,
892    pub color: Option<Color>,
893    pub id: GlyphId,
894    pub cluster: usize,
895    pub advance_in_ems: f32,
896    pub offset_in_ems: f32,
897}
898
899impl LaidoutGlyph {
900    pub fn advance_in_lpxs(&self) -> f32 {
901        self.advance_in_ems * self.font_size_in_lpxs
902    }
903
904    pub fn offset_in_lpxs(&self) -> f32 {
905        self.offset_in_ems * self.font_size_in_lpxs
906    }
907
908    pub fn ascender_in_lpxs(&self) -> f32 {
909        self.font.ascender_in_ems() * self.font_size_in_lpxs
910    }
911
912    pub fn descender_in_lpxs(&self) -> f32 {
913        self.font.descender_in_ems() * self.font_size_in_lpxs
914    }
915
916    pub fn line_gap_in_lpxs(&self) -> f32 {
917        self.font.line_gap_in_ems() * self.font_size_in_lpxs
918    }
919
920    pub fn rasterize(&self, dpx_per_em: f32) -> Option<RasterizedGlyph> {
921        self.font.rasterize_glyph(self.id, dpx_per_em)
922    }
923}