gpui/text_system/
line.rs

1use crate::{
2    App, Bounds, Half, Hsla, LineLayout, Pixels, Point, Result, SharedString, StrikethroughStyle,
3    TextAlign, TransformationMatrix, UnderlineStyle, Window, WrapBoundary, WrappedLineLayout,
4    black, fill, point, px, size,
5};
6use derive_more::{Deref, DerefMut};
7use smallvec::SmallVec;
8use std::sync::Arc;
9
10/// Set the text decoration for a run of text.
11#[derive(Debug, Clone)]
12pub struct DecorationRun {
13    /// The length of the run in utf-8 bytes.
14    pub len: u32,
15
16    /// The color for this run
17    pub color: Hsla,
18
19    /// The background color for this run
20    pub background_color: Option<Hsla>,
21
22    /// The underline style for this run
23    pub underline: Option<UnderlineStyle>,
24
25    /// The strikethrough style for this run
26    pub strikethrough: Option<StrikethroughStyle>,
27}
28
29/// A line of text that has been shaped and decorated.
30#[derive(Clone, Default, Debug, Deref, DerefMut)]
31pub struct ShapedLine {
32    #[deref]
33    #[deref_mut]
34    pub(crate) layout: Arc<LineLayout>,
35    /// The text that was shaped for this line.
36    pub text: SharedString,
37    pub(crate) decoration_runs: SmallVec<[DecorationRun; 32]>,
38}
39
40impl ShapedLine {
41    /// The length of the line in utf-8 bytes.
42    #[allow(clippy::len_without_is_empty)]
43    pub fn len(&self) -> usize {
44        self.layout.len
45    }
46
47    /// Override the len, useful if you're rendering text a
48    /// as text b (e.g. rendering invisibles).
49    pub fn with_len(mut self, len: usize) -> Self {
50        let layout = self.layout.as_ref();
51        self.layout = Arc::new(LineLayout {
52            font_size: layout.font_size,
53            width: layout.width,
54            ascent: layout.ascent,
55            descent: layout.descent,
56            runs: layout.runs.clone(),
57            len,
58        });
59        self
60    }
61
62    /// Paint the line of text to the window.
63    pub fn paint(
64        &self,
65        origin: Point<Pixels>,
66        line_height: Pixels,
67        align: TextAlign,
68        align_width: Option<Pixels>,
69        window: &mut Window,
70        cx: &mut App,
71    ) -> Result<()> {
72        self.paint_with_transform(
73            origin,
74            line_height,
75            align,
76            align_width,
77            window,
78            cx,
79            TransformationMatrix::unit(),
80        )?;
81
82        Ok(())
83    }
84
85    /// Paint the line of text to the window with an explicit visual transform.
86    pub fn paint_with_transform(
87        &self,
88        origin: Point<Pixels>,
89        line_height: Pixels,
90        align: TextAlign,
91        align_width: Option<Pixels>,
92        window: &mut Window,
93        cx: &mut App,
94        transform: TransformationMatrix,
95    ) -> Result<()> {
96        paint_line(
97            origin,
98            &self.layout,
99            line_height,
100            align,
101            align_width,
102            &self.decoration_runs,
103            &[],
104            window,
105            cx,
106            transform,
107        )
108    }
109
110    /// Paint the background of the line to the window.
111    pub fn paint_background(
112        &self,
113        origin: Point<Pixels>,
114        line_height: Pixels,
115        align: TextAlign,
116        align_width: Option<Pixels>,
117        window: &mut Window,
118        cx: &mut App,
119    ) -> Result<()> {
120        self.paint_background_with_transform(
121            origin,
122            line_height,
123            align,
124            align_width,
125            window,
126            cx,
127            TransformationMatrix::unit(),
128        )?;
129
130        Ok(())
131    }
132
133    /// Paint the background of the line to the window with an explicit visual transform.
134    pub fn paint_background_with_transform(
135        &self,
136        origin: Point<Pixels>,
137        line_height: Pixels,
138        align: TextAlign,
139        align_width: Option<Pixels>,
140        window: &mut Window,
141        cx: &mut App,
142        transform: TransformationMatrix,
143    ) -> Result<()> {
144        paint_line_background(
145            origin,
146            &self.layout,
147            line_height,
148            align,
149            align_width,
150            &self.decoration_runs,
151            &[],
152            window,
153            cx,
154            transform,
155        )
156    }
157}
158
159/// A line of text that has been shaped, decorated, and wrapped by the text layout system.
160#[derive(Clone, Default, Debug, Deref, DerefMut)]
161pub struct WrappedLine {
162    #[deref]
163    #[deref_mut]
164    pub(crate) layout: Arc<WrappedLineLayout>,
165    /// The text that was shaped for this line.
166    pub text: SharedString,
167    pub(crate) decoration_runs: SmallVec<[DecorationRun; 32]>,
168}
169
170impl WrappedLine {
171    /// The length of the underlying, unwrapped layout, in utf-8 bytes.
172    #[allow(clippy::len_without_is_empty)]
173    pub fn len(&self) -> usize {
174        self.layout.len()
175    }
176
177    /// Paint this line of text to the window.
178    pub fn paint(
179        &self,
180        origin: Point<Pixels>,
181        line_height: Pixels,
182        align: TextAlign,
183        bounds: Option<Bounds<Pixels>>,
184        window: &mut Window,
185        cx: &mut App,
186    ) -> Result<()> {
187        self.paint_with_transform(
188            origin,
189            line_height,
190            align,
191            bounds,
192            window,
193            cx,
194            TransformationMatrix::unit(),
195        )
196    }
197
198    /// Paint this line of text with an explicit visual transform.
199    pub fn paint_with_transform(
200        &self,
201        origin: Point<Pixels>,
202        line_height: Pixels,
203        align: TextAlign,
204        bounds: Option<Bounds<Pixels>>,
205        window: &mut Window,
206        cx: &mut App,
207        transform: TransformationMatrix,
208    ) -> Result<()> {
209        let align_width = match bounds {
210            Some(bounds) => Some(bounds.size.width),
211            None => self.layout.wrap_width,
212        };
213
214        paint_line(
215            origin,
216            &self.layout.unwrapped_layout,
217            line_height,
218            align,
219            align_width,
220            &self.decoration_runs,
221            &self.wrap_boundaries,
222            window,
223            cx,
224            transform,
225        )?;
226
227        Ok(())
228    }
229
230    /// Paint the background of line of text to the window.
231    pub fn paint_background(
232        &self,
233        origin: Point<Pixels>,
234        line_height: Pixels,
235        align: TextAlign,
236        bounds: Option<Bounds<Pixels>>,
237        window: &mut Window,
238        cx: &mut App,
239    ) -> Result<()> {
240        self.paint_background_with_transform(
241            origin,
242            line_height,
243            align,
244            bounds,
245            window,
246            cx,
247            TransformationMatrix::unit(),
248        )
249    }
250
251    /// Paint the background of line of text with an explicit visual transform.
252    pub fn paint_background_with_transform(
253        &self,
254        origin: Point<Pixels>,
255        line_height: Pixels,
256        align: TextAlign,
257        bounds: Option<Bounds<Pixels>>,
258        window: &mut Window,
259        cx: &mut App,
260        transform: TransformationMatrix,
261    ) -> Result<()> {
262        let align_width = match bounds {
263            Some(bounds) => Some(bounds.size.width),
264            None => self.layout.wrap_width,
265        };
266
267        paint_line_background(
268            origin,
269            &self.layout.unwrapped_layout,
270            line_height,
271            align,
272            align_width,
273            &self.decoration_runs,
274            &self.wrap_boundaries,
275            window,
276            cx,
277            transform,
278        )?;
279
280        Ok(())
281    }
282}
283
284fn paint_line(
285    origin: Point<Pixels>,
286    layout: &LineLayout,
287    line_height: Pixels,
288    align: TextAlign,
289    align_width: Option<Pixels>,
290    decoration_runs: &[DecorationRun],
291    wrap_boundaries: &[WrapBoundary],
292    window: &mut Window,
293    cx: &mut App,
294    transform: TransformationMatrix,
295) -> Result<()> {
296    let local_bounds = Bounds::new(
297        origin,
298        size(
299            layout.width,
300            line_height * (wrap_boundaries.len() as f32 + 1.),
301        ),
302    );
303    // When a visual transform is applied (e.g. from React Native view transforms),
304    // compute the transformed axis-aligned bounds so clipping/layering still occurs
305    // in window-space coordinates.
306    let layer_bounds = if transform.is_unit() {
307        local_bounds
308    } else {
309        // Transform the four corners and take their AABB.
310        let corners = [
311            local_bounds.origin,
312            point(
313                local_bounds.origin.x + local_bounds.size.width,
314                local_bounds.origin.y,
315            ),
316            point(
317                local_bounds.origin.x,
318                local_bounds.origin.y + local_bounds.size.height,
319            ),
320            point(
321                local_bounds.origin.x + local_bounds.size.width,
322                local_bounds.origin.y + local_bounds.size.height,
323            ),
324        ];
325
326        let mut min_x = f32::INFINITY;
327        let mut max_x = f32::NEG_INFINITY;
328        let mut min_y = f32::INFINITY;
329        let mut max_y = f32::NEG_INFINITY;
330
331        for corner in corners {
332            let p = transform.apply(corner);
333            let x = f32::from(p.x);
334            let y = f32::from(p.y);
335            if x < min_x {
336                min_x = x;
337            }
338            if x > max_x {
339                max_x = x;
340            }
341            if y < min_y {
342                min_y = y;
343            }
344            if y > max_y {
345                max_y = y;
346            }
347        }
348
349        Bounds {
350            origin: point(px(min_x), px(min_y)),
351            size: size(px((max_x - min_x).max(0.0)), px((max_y - min_y).max(0.0))),
352        }
353    };
354
355    window.paint_layer(layer_bounds, |window| {
356        let padding_top = (line_height - layout.ascent - layout.descent) / 2.;
357        let baseline_offset = point(px(0.), padding_top + layout.ascent);
358        let mut decoration_runs = decoration_runs.iter();
359        let mut wraps = wrap_boundaries.iter().peekable();
360        let mut run_end = 0;
361        let mut color = black();
362        let mut current_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
363        let mut current_strikethrough: Option<(Point<Pixels>, StrikethroughStyle)> = None;
364        let text_system = cx.text_system().clone();
365        let mut glyph_origin = point(
366            aligned_origin_x(
367                origin,
368                align_width.unwrap_or(layout.width),
369                px(0.0),
370                &align,
371                layout,
372                wraps.peek(),
373            ),
374            origin.y,
375        );
376        let mut prev_glyph_position = Point::default();
377        let mut max_glyph_size = size(px(0.), px(0.));
378        let mut first_glyph_x = origin.x;
379        for (run_ix, run) in layout.runs.iter().enumerate() {
380            max_glyph_size = text_system.bounding_box(run.font_id, layout.font_size).size;
381
382            for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
383                glyph_origin.x += glyph.position.x - prev_glyph_position.x;
384                if glyph_ix == 0 && run_ix == 0 {
385                    first_glyph_x = glyph_origin.x;
386                }
387
388                if wraps.peek() == Some(&&WrapBoundary { run_ix, glyph_ix }) {
389                    wraps.next();
390                    if let Some((underline_origin, underline_style)) = current_underline.as_mut() {
391                        if glyph_origin.x == underline_origin.x {
392                            underline_origin.x -= max_glyph_size.width.half();
393                        };
394                        window.paint_underline_with_transform(
395                            *underline_origin,
396                            glyph_origin.x - underline_origin.x,
397                            underline_style,
398                            transform,
399                        );
400                        underline_origin.x = origin.x;
401                        underline_origin.y += line_height;
402                    }
403                    if let Some((strikethrough_origin, strikethrough_style)) =
404                        current_strikethrough.as_mut()
405                    {
406                        if glyph_origin.x == strikethrough_origin.x {
407                            strikethrough_origin.x -= max_glyph_size.width.half();
408                        };
409                        window.paint_strikethrough_with_transform(
410                            *strikethrough_origin,
411                            glyph_origin.x - strikethrough_origin.x,
412                            strikethrough_style,
413                            transform,
414                        );
415                        strikethrough_origin.x = origin.x;
416                        strikethrough_origin.y += line_height;
417                    }
418
419                    glyph_origin.x = aligned_origin_x(
420                        origin,
421                        align_width.unwrap_or(layout.width),
422                        glyph.position.x,
423                        &align,
424                        layout,
425                        wraps.peek(),
426                    );
427                    glyph_origin.y += line_height;
428                }
429                prev_glyph_position = glyph.position;
430
431                let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
432                let mut finished_strikethrough: Option<(Point<Pixels>, StrikethroughStyle)> = None;
433                if glyph.index >= run_end {
434                    let mut style_run = decoration_runs.next();
435
436                    // ignore style runs that apply to a partial glyph
437                    while let Some(run) = style_run {
438                        if glyph.index < run_end + (run.len as usize) {
439                            break;
440                        }
441                        run_end += run.len as usize;
442                        style_run = decoration_runs.next();
443                    }
444
445                    if let Some(style_run) = style_run {
446                        if let Some((_, underline_style)) = &mut current_underline
447                            && style_run.underline.as_ref() != Some(underline_style)
448                        {
449                            finished_underline = current_underline.take();
450                        }
451                        if let Some(run_underline) = style_run.underline.as_ref() {
452                            current_underline.get_or_insert((
453                                point(
454                                    glyph_origin.x,
455                                    glyph_origin.y + baseline_offset.y + (layout.descent * 0.618),
456                                ),
457                                UnderlineStyle {
458                                    color: Some(run_underline.color.unwrap_or(style_run.color)),
459                                    thickness: run_underline.thickness,
460                                    wavy: run_underline.wavy,
461                                },
462                            ));
463                        }
464                        if let Some((_, strikethrough_style)) = &mut current_strikethrough
465                            && style_run.strikethrough.as_ref() != Some(strikethrough_style)
466                        {
467                            finished_strikethrough = current_strikethrough.take();
468                        }
469                        if let Some(run_strikethrough) = style_run.strikethrough.as_ref() {
470                            current_strikethrough.get_or_insert((
471                                point(
472                                    glyph_origin.x,
473                                    glyph_origin.y
474                                        + (((layout.ascent * 0.5) + baseline_offset.y) * 0.5),
475                                ),
476                                StrikethroughStyle {
477                                    color: Some(run_strikethrough.color.unwrap_or(style_run.color)),
478                                    thickness: run_strikethrough.thickness,
479                                },
480                            ));
481                        }
482
483                        run_end += style_run.len as usize;
484                        color = style_run.color;
485                    } else {
486                        run_end = layout.len;
487                        finished_underline = current_underline.take();
488                        finished_strikethrough = current_strikethrough.take();
489                    }
490                }
491
492                if let Some((mut underline_origin, underline_style)) = finished_underline {
493                    if underline_origin.x == glyph_origin.x {
494                        underline_origin.x -= max_glyph_size.width.half();
495                    };
496                    window.paint_underline_with_transform(
497                        underline_origin,
498                        glyph_origin.x - underline_origin.x,
499                        &underline_style,
500                        transform,
501                    );
502                }
503
504                if let Some((mut strikethrough_origin, strikethrough_style)) =
505                    finished_strikethrough
506                {
507                    if strikethrough_origin.x == glyph_origin.x {
508                        strikethrough_origin.x -= max_glyph_size.width.half();
509                    };
510                    window.paint_strikethrough_with_transform(
511                        strikethrough_origin,
512                        glyph_origin.x - strikethrough_origin.x,
513                        &strikethrough_style,
514                        transform,
515                    );
516                }
517
518                let max_glyph_bounds = Bounds {
519                    origin: glyph_origin,
520                    size: max_glyph_size,
521                };
522
523                let content_mask = window.content_mask();
524                let glyph_intersects_mask = if transform.is_unit() {
525                    max_glyph_bounds.intersects(&content_mask.bounds)
526                } else {
527                    // Transform glyph bounds into window space for correct masking when a
528                    // visual transform is applied.
529                    let corners = [
530                        max_glyph_bounds.origin,
531                        point(
532                            max_glyph_bounds.origin.x + max_glyph_bounds.size.width,
533                            max_glyph_bounds.origin.y,
534                        ),
535                        point(
536                            max_glyph_bounds.origin.x,
537                            max_glyph_bounds.origin.y + max_glyph_bounds.size.height,
538                        ),
539                        point(
540                            max_glyph_bounds.origin.x + max_glyph_bounds.size.width,
541                            max_glyph_bounds.origin.y + max_glyph_bounds.size.height,
542                        ),
543                    ];
544
545                    let mut min_x = f32::INFINITY;
546                    let mut max_x = f32::NEG_INFINITY;
547                    let mut min_y = f32::INFINITY;
548                    let mut max_y = f32::NEG_INFINITY;
549
550                    for corner in corners {
551                        let p = transform.apply(corner);
552                        let x = f32::from(p.x);
553                        let y = f32::from(p.y);
554                        if x < min_x {
555                            min_x = x;
556                        }
557                        if x > max_x {
558                            max_x = x;
559                        }
560                        if y < min_y {
561                            min_y = y;
562                        }
563                        if y > max_y {
564                            max_y = y;
565                        }
566                    }
567
568                    let world_bounds = Bounds {
569                        origin: point(px(min_x), px(min_y)),
570                        size: size(px((max_x - min_x).max(0.0)), px((max_y - min_y).max(0.0))),
571                    };
572
573                    world_bounds.intersects(&content_mask.bounds)
574                };
575
576                if glyph_intersects_mask {
577                    let vertical_offset = point(px(0.0), glyph.position.y);
578                    if glyph.is_emoji {
579                        window.paint_emoji_with_transform(
580                            glyph_origin + baseline_offset + vertical_offset,
581                            run.font_id,
582                            glyph.id,
583                            layout.font_size,
584                            transform,
585                        )?;
586                    } else {
587                        window.paint_glyph_with_transform(
588                            glyph_origin + baseline_offset + vertical_offset,
589                            run.font_id,
590                            glyph.id,
591                            layout.font_size,
592                            color,
593                            transform,
594                        )?;
595                    }
596                }
597            }
598        }
599
600        let mut last_line_end_x = first_glyph_x + layout.width;
601        if let Some(boundary) = wrap_boundaries.last() {
602            let run = &layout.runs[boundary.run_ix];
603            let glyph = &run.glyphs[boundary.glyph_ix];
604            last_line_end_x -= glyph.position.x;
605        }
606
607        if let Some((mut underline_start, underline_style)) = current_underline.take() {
608            if last_line_end_x == underline_start.x {
609                underline_start.x -= max_glyph_size.width.half()
610            };
611            window.paint_underline_with_transform(
612                underline_start,
613                last_line_end_x - underline_start.x,
614                &underline_style,
615                transform,
616            );
617        }
618
619        if let Some((mut strikethrough_start, strikethrough_style)) = current_strikethrough.take() {
620            if last_line_end_x == strikethrough_start.x {
621                strikethrough_start.x -= max_glyph_size.width.half()
622            };
623            window.paint_strikethrough_with_transform(
624                strikethrough_start,
625                last_line_end_x - strikethrough_start.x,
626                &strikethrough_style,
627                transform,
628            );
629        }
630
631        Ok(())
632    })
633}
634
635fn paint_line_background(
636    origin: Point<Pixels>,
637    layout: &LineLayout,
638    line_height: Pixels,
639    align: TextAlign,
640    align_width: Option<Pixels>,
641    decoration_runs: &[DecorationRun],
642    wrap_boundaries: &[WrapBoundary],
643    window: &mut Window,
644    cx: &mut App,
645    transform: TransformationMatrix,
646) -> Result<()> {
647    let local_bounds = Bounds::new(
648        origin,
649        size(
650            layout.width,
651            line_height * (wrap_boundaries.len() as f32 + 1.),
652        ),
653    );
654    let layer_bounds = if transform.is_unit() {
655        local_bounds
656    } else {
657        let corners = [
658            local_bounds.origin,
659            point(
660                local_bounds.origin.x + local_bounds.size.width,
661                local_bounds.origin.y,
662            ),
663            point(
664                local_bounds.origin.x,
665                local_bounds.origin.y + local_bounds.size.height,
666            ),
667            point(
668                local_bounds.origin.x + local_bounds.size.width,
669                local_bounds.origin.y + local_bounds.size.height,
670            ),
671        ];
672
673        let mut min_x = f32::INFINITY;
674        let mut max_x = f32::NEG_INFINITY;
675        let mut min_y = f32::INFINITY;
676        let mut max_y = f32::NEG_INFINITY;
677
678        for corner in corners {
679            let p = transform.apply(corner);
680            let x = f32::from(p.x);
681            let y = f32::from(p.y);
682            if x < min_x {
683                min_x = x;
684            }
685            if x > max_x {
686                max_x = x;
687            }
688            if y < min_y {
689                min_y = y;
690            }
691            if y > max_y {
692                max_y = y;
693            }
694        }
695
696        Bounds {
697            origin: point(px(min_x), px(min_y)),
698            size: size(px((max_x - min_x).max(0.0)), px((max_y - min_y).max(0.0))),
699        }
700    };
701
702    window.paint_layer(layer_bounds, |window| {
703        let mut decoration_runs = decoration_runs.iter();
704        let mut wraps = wrap_boundaries.iter().peekable();
705        let mut run_end = 0;
706        let mut current_background: Option<(Point<Pixels>, Hsla)> = None;
707        let text_system = cx.text_system().clone();
708        let mut glyph_origin = point(
709            aligned_origin_x(
710                origin,
711                align_width.unwrap_or(layout.width),
712                px(0.0),
713                &align,
714                layout,
715                wraps.peek(),
716            ),
717            origin.y,
718        );
719        let mut prev_glyph_position = Point::default();
720        let mut max_glyph_size = size(px(0.), px(0.));
721        for (run_ix, run) in layout.runs.iter().enumerate() {
722            max_glyph_size = text_system.bounding_box(run.font_id, layout.font_size).size;
723
724            for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
725                glyph_origin.x += glyph.position.x - prev_glyph_position.x;
726
727                if wraps.peek() == Some(&&WrapBoundary { run_ix, glyph_ix }) {
728                    wraps.next();
729                    if let Some((background_origin, background_color)) = current_background.as_mut()
730                    {
731                        if glyph_origin.x == background_origin.x {
732                            background_origin.x -= max_glyph_size.width.half()
733                        }
734                        window.paint_quad_with_transform(
735                            fill(
736                                Bounds {
737                                    origin: *background_origin,
738                                    size: size(glyph_origin.x - background_origin.x, line_height),
739                                },
740                                *background_color,
741                            ),
742                            transform,
743                        );
744                        background_origin.x = origin.x;
745                        background_origin.y += line_height;
746                    }
747
748                    glyph_origin.x = aligned_origin_x(
749                        origin,
750                        align_width.unwrap_or(layout.width),
751                        glyph.position.x,
752                        &align,
753                        layout,
754                        wraps.peek(),
755                    );
756                    glyph_origin.y += line_height;
757                }
758                prev_glyph_position = glyph.position;
759
760                let mut finished_background: Option<(Point<Pixels>, Hsla)> = None;
761                if glyph.index >= run_end {
762                    let mut style_run = decoration_runs.next();
763
764                    // ignore style runs that apply to a partial glyph
765                    while let Some(run) = style_run {
766                        if glyph.index < run_end + (run.len as usize) {
767                            break;
768                        }
769                        run_end += run.len as usize;
770                        style_run = decoration_runs.next();
771                    }
772
773                    if let Some(style_run) = style_run {
774                        if let Some((_, background_color)) = &mut current_background
775                            && style_run.background_color.as_ref() != Some(background_color)
776                        {
777                            finished_background = current_background.take();
778                        }
779                        if let Some(run_background) = style_run.background_color {
780                            current_background.get_or_insert((
781                                point(glyph_origin.x, glyph_origin.y),
782                                run_background,
783                            ));
784                        }
785                        run_end += style_run.len as usize;
786                    } else {
787                        run_end = layout.len;
788                        finished_background = current_background.take();
789                    }
790                }
791
792                if let Some((mut background_origin, background_color)) = finished_background {
793                    let mut width = glyph_origin.x - background_origin.x;
794                    if background_origin.x == glyph_origin.x {
795                        background_origin.x -= max_glyph_size.width.half();
796                    };
797                    window.paint_quad_with_transform(
798                        fill(
799                            Bounds {
800                                origin: background_origin,
801                                size: size(width, line_height),
802                            },
803                            background_color,
804                        ),
805                        transform,
806                    );
807                }
808            }
809        }
810
811        let mut last_line_end_x = origin.x + layout.width;
812        if let Some(boundary) = wrap_boundaries.last() {
813            let run = &layout.runs[boundary.run_ix];
814            let glyph = &run.glyphs[boundary.glyph_ix];
815            last_line_end_x -= glyph.position.x;
816        }
817
818        if let Some((mut background_origin, background_color)) = current_background.take() {
819            if last_line_end_x == background_origin.x {
820                background_origin.x -= max_glyph_size.width.half()
821            };
822            window.paint_quad_with_transform(
823                fill(
824                    Bounds {
825                        origin: background_origin,
826                        size: size(last_line_end_x - background_origin.x, line_height),
827                    },
828                    background_color,
829                ),
830                transform,
831            );
832        }
833
834        Ok(())
835    })
836}
837
838fn aligned_origin_x(
839    origin: Point<Pixels>,
840    align_width: Pixels,
841    last_glyph_x: Pixels,
842    align: &TextAlign,
843    layout: &LineLayout,
844    wrap_boundary: Option<&&WrapBoundary>,
845) -> Pixels {
846    let end_of_line = if let Some(WrapBoundary { run_ix, glyph_ix }) = wrap_boundary {
847        layout.runs[*run_ix].glyphs[*glyph_ix].position.x
848    } else {
849        layout.width
850    };
851
852    let line_width = end_of_line - last_glyph_x;
853
854    match align {
855        TextAlign::Left => origin.x,
856        TextAlign::Center => (origin.x * 2.0 + align_width - line_width) / 2.0,
857        TextAlign::Right => origin.x + align_width - line_width,
858    }
859}