Skip to main content

fyrox_ui/
formatted_text.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use crate::{
22    brush::Brush,
23    core::{
24        algebra::Vector2, color::Color, math::Rect, reflect::prelude::*, uuid_provider,
25        variable::InheritableVariable, visitor::prelude::*,
26    },
27    font::{Font, FontGlyph, FontHeight, FontResource, BUILT_IN_FONT},
28    style::StyledProperty,
29    HorizontalAlignment, Thickness, VerticalAlignment,
30};
31use fyrox_core::log::Log;
32use fyrox_resource::state::{LoadError, ResourceState};
33pub use run::*;
34use std::{
35    ops::{Range, RangeBounds},
36    path::PathBuf,
37};
38use strum_macros::{AsRefStr, EnumString, VariantNames};
39use textwrapper::*;
40
41mod run;
42mod textwrapper;
43
44/// Width of a tab when multiplied by font size.
45const TAB_WIDTH: f32 = 2.0;
46
47/// Defines a position in the text. It is just a coordinates of a character in text.
48#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Visit, Reflect)]
49pub struct Position {
50    /// Line index.
51    pub line: usize,
52
53    /// Offset from the beginning of the line.
54    pub offset: usize,
55}
56
57#[derive(Debug, Clone, Default)]
58pub struct TextGlyph {
59    pub bounds: Rect<f32>,
60    pub tex_coords: [Vector2<f32>; 4],
61    pub atlas_page_index: usize,
62    pub source_char_index: usize,
63}
64
65#[derive(Copy, Clone, Debug, Default)]
66pub struct TextLine {
67    /// Index of starting symbol in text array.
68    pub begin: usize,
69    /// Index of ending symbol in text array.
70    pub end: usize,
71    /// Total width of line.
72    pub width: f32,
73    /// Total height of line. Usually just ascender of a font.
74    pub height: f32,
75    /// Local horizontal position of line.
76    pub x_offset: f32,
77    /// Local vertical position of line.
78    pub y_offset: f32,
79}
80
81impl TextLine {
82    fn new() -> TextLine {
83        TextLine {
84            begin: 0,
85            end: 0,
86            width: 0.0,
87            height: 0.0,
88            x_offset: 0.0,
89            y_offset: 0.0,
90        }
91    }
92
93    pub fn len(&self) -> usize {
94        self.end - self.begin
95    }
96
97    pub fn is_empty(&self) -> bool {
98        self.end == self.begin
99    }
100
101    pub fn y_distance(&self, y: f32) -> f32 {
102        (self.y_offset + self.height / 2.0 - y).abs()
103    }
104}
105
106/// Wrapping mode for formatted text.
107#[derive(
108    Default,
109    Copy,
110    Clone,
111    PartialOrd,
112    PartialEq,
113    Hash,
114    Debug,
115    Eq,
116    Visit,
117    Reflect,
118    AsRefStr,
119    EnumString,
120    VariantNames,
121)]
122pub enum WrapMode {
123    /// No wrapping needed.
124    #[default]
125    NoWrap,
126
127    /// Letter-based wrapping.
128    Letter,
129
130    /// Word-based wrapping.
131    Word,
132}
133
134uuid_provider!(WrapMode = "f1290ceb-3fee-461f-a1e9-f9450bd06805");
135
136struct GlyphMetrics<'a> {
137    font: &'a mut Font,
138    size: f32,
139}
140
141impl GlyphMetrics<'_> {
142    fn ascender(&self) -> f32 {
143        self.font.ascender(self.size)
144    }
145
146    fn descender(&self) -> f32 {
147        self.font.descender(self.size)
148    }
149
150    fn newline_advance(&self) -> f32 {
151        self.size / 2.0
152    }
153
154    fn horizontal_kerning(&self, left: char, right: char) -> Option<f32> {
155        self.font.horizontal_kerning(self.size, left, right)
156    }
157
158    fn advance(&mut self, c: char) -> f32 {
159        match c {
160            '\n' => self.newline_advance(),
161            _ => self.font.glyph_advance(c, self.size),
162        }
163    }
164
165    fn glyph(&mut self, c: char, super_sampling_scale: f32) -> Option<&FontGlyph> {
166        self.font.glyph(c, self.size * super_sampling_scale)
167    }
168}
169
170fn build_glyph(
171    metrics: &mut GlyphMetrics,
172    mut x: f32,
173    mut y: f32,
174    source_char_index: usize,
175    character: char,
176    prev_character: Option<char>,
177    super_sampling_scale: f32,
178) -> (TextGlyph, f32) {
179    let ascender = metrics.ascender();
180    let font_size = metrics.size;
181
182    x = x.floor();
183    y = y.floor();
184
185    if character == '\t' {
186        let rect = Rect::new(x, y + ascender, font_size * TAB_WIDTH, font_size);
187        let text_glyph = TextGlyph {
188            bounds: rect,
189            tex_coords: [Vector2::default(); 4],
190            atlas_page_index: 0,
191            source_char_index,
192        };
193        return (text_glyph, rect.w());
194    }
195
196    // Request larger glyph with super sampling scaling.
197    match metrics.glyph(character, super_sampling_scale) {
198        Some(glyph) => {
199            // Discard super sampling scaling in the produced glyphs, because we're interested only
200            // in larger texture size, not the "physical" size.
201            let k = 1.0 / super_sampling_scale;
202            // Insert glyph
203            let rect = Rect::new(
204                x + glyph.bitmap_left * k,
205                y + ascender.floor() - glyph.bitmap_top * k - (glyph.bitmap_height * k),
206                glyph.bitmap_width * k,
207                glyph.bitmap_height * k,
208            );
209            let text_glyph = TextGlyph {
210                bounds: rect,
211                tex_coords: glyph.tex_coords,
212                atlas_page_index: glyph.page_index,
213                source_char_index,
214            };
215            let advance = glyph.advance
216                + prev_character
217                    .and_then(|prev| metrics.horizontal_kerning(prev, character))
218                    .unwrap_or_default();
219            (text_glyph, advance * k)
220        }
221        None => {
222            // Insert invalid symbol
223            let rect = Rect::new(x, y + ascender, font_size, font_size);
224            let text_glyph = TextGlyph {
225                bounds: rect,
226                tex_coords: [Vector2::default(); 4],
227                atlas_page_index: 0,
228                source_char_index,
229            };
230            (text_glyph, rect.w())
231        }
232    }
233}
234
235struct WrapSink<'a> {
236    lines: &'a mut Vec<TextLine>,
237    normal_width: f32,
238    first_width: f32,
239}
240
241impl LineSink for WrapSink<'_> {
242    fn push_line(&mut self, range: Range<usize>, width: f32) {
243        let mut line = TextLine::new();
244        line.begin = range.start;
245        line.end = range.end;
246        line.width = width;
247        self.lines.push(line);
248    }
249
250    fn max_width(&self) -> f32 {
251        if self.lines.is_empty() {
252            self.first_width
253        } else {
254            self.normal_width
255        }
256    }
257}
258
259#[derive(Default, Clone, Debug, Visit, Reflect)]
260pub struct FormattedText {
261    font: InheritableVariable<Option<FontResource>>,
262    pub text: InheritableVariable<Vec<char>>,
263    // Temporary buffer used to split text on lines. We need it to reduce memory allocations
264    // when we're changing text too frequently, here we sacrifice some memory in order to get
265    // more performance.
266    #[reflect(hidden)]
267    #[visit(skip)]
268    lines: Vec<TextLine>,
269    // Final glyphs for draw buffer.
270    #[visit(skip)]
271    #[reflect(hidden)]
272    glyphs: Vec<TextGlyph>,
273    vertical_alignment: InheritableVariable<VerticalAlignment>,
274    horizontal_alignment: InheritableVariable<HorizontalAlignment>,
275    #[reflect(hidden)]
276    brush: InheritableVariable<Brush>,
277    #[visit(skip)]
278    #[reflect(hidden)]
279    constraint: Vector2<f32>,
280    wrap: InheritableVariable<WrapMode>,
281    mask_char: InheritableVariable<Option<char>>,
282    #[visit(skip)]
283    #[reflect(hidden)]
284    pub(crate) super_sampling_scale: f32,
285    #[visit(rename = "Height")]
286    font_size: InheritableVariable<StyledProperty<f32>>,
287    pub shadow: InheritableVariable<bool>,
288    pub shadow_brush: InheritableVariable<Brush>,
289    pub shadow_dilation: InheritableVariable<f32>,
290    pub shadow_offset: InheritableVariable<Vector2<f32>>,
291    #[visit(optional)]
292    pub runs: RunSet,
293    /// The indent amount of the first line of the text.
294    /// A negative indent will cause every line except the first to indent.
295    #[visit(optional)]
296    pub line_indent: InheritableVariable<f32>,
297    /// The space between lines.
298    #[visit(optional)]
299    pub line_space: InheritableVariable<f32>,
300    pub padding: InheritableVariable<Thickness>,
301}
302
303impl FormattedText {
304    pub fn font_at(&self, index: usize) -> FontResource {
305        self.runs.font_at(index).unwrap_or_else(|| self.get_font())
306    }
307    pub fn font_size_at(&self, index: usize) -> f32 {
308        self.runs
309            .font_size_at(index)
310            .unwrap_or_else(|| self.font_size().property)
311    }
312    pub fn brush_at(&self, index: usize) -> Brush {
313        self.runs.brush_at(index).unwrap_or_else(|| self.brush())
314    }
315    pub fn shadow_at(&self, index: usize) -> bool {
316        self.runs.shadow_at(index).unwrap_or(*self.shadow)
317    }
318    pub fn shadow_brush_at(&self, index: usize) -> Brush {
319        self.runs
320            .shadow_brush_at(index)
321            .unwrap_or_else(|| self.shadow_brush.clone_inner())
322    }
323    pub fn shadow_dilation_at(&self, index: usize) -> f32 {
324        self.runs
325            .shadow_dilation_at(index)
326            .unwrap_or(*self.shadow_dilation)
327    }
328    pub fn shadow_offset_at(&self, index: usize) -> Vector2<f32> {
329        self.runs
330            .shadow_offset_at(index)
331            .unwrap_or(*self.shadow_offset)
332    }
333    pub fn nearest_valid_position(&self, start: Position) -> Position {
334        if self.lines.is_empty() {
335            return Position::default();
336        }
337        let mut pos = start;
338        pos.line = usize::min(pos.line, self.lines.len() - 1);
339        pos.offset = usize::min(pos.offset, self.lines[pos.line].len());
340        pos
341    }
342    pub fn get_relative_position_x(&self, start: Position, offset: isize) -> Position {
343        if self.lines.is_empty() {
344            return Position::default();
345        }
346        let mut pos = self.nearest_valid_position(start);
347        let distance = offset.abs();
348        for _ in 0..distance {
349            if offset < 0 {
350                if pos.offset > 0 {
351                    pos.offset -= 1
352                } else if pos.line > 0 {
353                    pos.line -= 1;
354                    pos.offset = self.lines[pos.line].len().saturating_sub(1);
355                } else {
356                    pos.offset = 0;
357                    break;
358                }
359            } else {
360                let line = &self.lines[pos.line];
361                if pos.offset + 1 < line.len() {
362                    pos.offset += 1;
363                } else if pos.line + 1 < self.lines.len() {
364                    pos.line += 1;
365                    pos.offset = 0;
366                } else {
367                    pos.offset = line.len();
368                    break;
369                }
370            }
371        }
372        pos
373    }
374
375    pub fn get_relative_position_y(&self, start: Position, offset: isize) -> Position {
376        let mut pos = self.nearest_valid_position(start);
377        pos.line = pos.line.saturating_add_signed(offset);
378        self.nearest_valid_position(pos)
379    }
380
381    pub fn get_line_range(&self, line: usize) -> Range<Position> {
382        let length = self.lines.get(line).map(TextLine::len).unwrap_or(0);
383        Range {
384            start: Position { line, offset: 0 },
385            end: Position {
386                line,
387                offset: length,
388            },
389        }
390    }
391
392    pub fn iter_line_ranges_within(
393        &self,
394        range: Range<Position>,
395    ) -> impl Iterator<Item = Range<Position>> + '_ {
396        (range.start.line..=range.end.line).map(move |i| {
397            let r = self.get_line_range(i);
398            Range {
399                start: Position::max(range.start, r.start),
400                end: Position::min(range.end, r.end),
401            }
402        })
403    }
404
405    pub fn end_position(&self) -> Position {
406        match self.lines.iter().enumerate().next_back() {
407            Some((i, line)) => Position {
408                line: i,
409                offset: line.len(),
410            },
411            None => Position::default(),
412        }
413    }
414
415    fn position_to_char_index_internal(&self, position: Position, clamp: bool) -> Option<usize> {
416        self.lines.get(position.line).map(|line| {
417            line.begin
418                + position.offset.min(if clamp {
419                    line.len().saturating_sub(1)
420                } else {
421                    line.len()
422                })
423        })
424    }
425
426    pub fn position_range_to_char_index_range(&self, range: Range<Position>) -> Range<usize> {
427        let start = self
428            .position_to_char_index_unclamped(range.start)
429            .unwrap_or(0);
430        let end = self
431            .position_to_char_index_unclamped(range.end)
432            .unwrap_or(self.text.len());
433        start..end
434    }
435    /// Maps input [`Position`] to a linear position in character array.
436    /// The index returned is the index of the character after the position, which may be
437    /// out-of-bounds if the position is at the end of the text.
438    /// You should check the index before trying to use it to fetch data from inner array of characters.
439    pub fn position_to_char_index_unclamped(&self, position: Position) -> Option<usize> {
440        self.position_to_char_index_internal(position, false)
441    }
442
443    /// Maps input [`Position`] to a linear position in character array.
444    /// The index returned is usually the index of the character after the position,
445    /// but if the position is at the end of a line, then return the index of the character _before_ the position.
446    /// In other words, the last two positions of each line are mapped to the same character index.
447    /// Output index will always be valid for fetching if the method returned `Some(index)`.
448    /// The index, however, cannot be used for text insertion because it cannot point to a "place after last char".
449    pub fn position_to_char_index_clamped(&self, position: Position) -> Option<usize> {
450        self.position_to_char_index_internal(position, true)
451    }
452
453    /// Maps linear character index (as in string) to its actual location in the text.
454    pub fn char_index_to_position(&self, i: usize) -> Option<Position> {
455        self.lines
456            .iter()
457            .enumerate()
458            .find_map(|(line_index, line)| {
459                if (line.begin..line.end).contains(&i) {
460                    Some(Position {
461                        line: line_index,
462                        offset: i - line.begin,
463                    })
464                } else {
465                    None
466                }
467            })
468            .or(Some(self.end_position()))
469    }
470
471    pub fn position_to_local(&self, position: Position) -> Vector2<f32> {
472        if self.get_font().state().data().is_none() {
473            return Default::default();
474        }
475        let position = self.nearest_valid_position(position);
476        let line = &self.lines[position.line];
477        let caret_pos = Vector2::new(line.x_offset, line.y_offset);
478        let range = line.begin..line.begin + position.offset;
479        caret_pos + Vector2::new(self.get_range_width(range), 0.0)
480    }
481
482    pub fn local_to_position(&self, point: Vector2<f32>) -> Position {
483        if self.get_font().state().data().is_none() {
484            return Position::default();
485        }
486        let y = point.y;
487
488        let Some(line_index) = self
489            .lines
490            .iter()
491            .enumerate()
492            .map(|(i, a)| (i, a.y_distance(y)))
493            .min_by(|a, b| f32::total_cmp(&a.1, &b.1))
494            .map(|(i, _)| i)
495        else {
496            return Position::default();
497        };
498        let line = self.lines[line_index];
499        let x = point.x - line.x_offset;
500        let mut glyph_x: f32 = 0.0;
501        let mut min_dist: f32 = x.abs();
502        let mut min_index: usize = 0;
503        for (offset, char_index) in (line.begin..line.end).enumerate() {
504            glyph_x += self.get_char_width(char_index).unwrap_or_default();
505            let dist = (x - glyph_x).abs();
506            if dist < min_dist {
507                min_dist = dist;
508                min_index = offset + 1;
509            }
510        }
511        Position {
512            line: line_index,
513            offset: min_index,
514        }
515    }
516
517    pub fn get_glyphs(&self) -> &[TextGlyph] {
518        &self.glyphs
519    }
520
521    pub fn get_glyph_draw_values(
522        &self,
523        layer: DrawValueLayer,
524        glyph: &TextGlyph,
525    ) -> GlyphDrawValues {
526        let atlas_page_index = glyph.atlas_page_index;
527        let i = glyph.source_char_index;
528        let font = self.font_at(i);
529        let height = FontHeight::from(self.font_size_at(i) * self.super_sampling_scale);
530        match layer {
531            DrawValueLayer::Main => GlyphDrawValues {
532                atlas_page_index,
533                font,
534                brush: self.brush_at(i),
535                height,
536            },
537            DrawValueLayer::Shadow => GlyphDrawValues {
538                atlas_page_index,
539                font,
540                brush: self.shadow_brush_at(i),
541                height,
542            },
543        }
544    }
545
546    pub fn get_font(&self) -> FontResource {
547        (*self.font).clone().unwrap_or(BUILT_IN_FONT.resource())
548    }
549
550    pub fn set_font(&mut self, font: FontResource) -> &mut Self {
551        self.font.set_value_and_mark_modified(Some(font));
552        self
553    }
554
555    pub fn font_size(&self) -> &StyledProperty<f32> {
556        &self.font_size
557    }
558
559    pub fn super_sampled_font_size(&self) -> f32 {
560        **self.font_size * self.super_sampling_scale
561    }
562
563    pub fn set_font_size(&mut self, font_size: StyledProperty<f32>) -> &mut Self {
564        self.font_size.set_value_and_mark_modified(font_size);
565        self
566    }
567
568    pub fn get_lines(&self) -> &[TextLine] {
569        &self.lines
570    }
571
572    pub fn set_vertical_alignment(&mut self, vertical_alignment: VerticalAlignment) -> &mut Self {
573        self.vertical_alignment
574            .set_value_and_mark_modified(vertical_alignment);
575        self
576    }
577
578    pub fn vertical_alignment(&self) -> VerticalAlignment {
579        *self.vertical_alignment
580    }
581
582    pub fn set_horizontal_alignment(
583        &mut self,
584        horizontal_alignment: HorizontalAlignment,
585    ) -> &mut Self {
586        self.horizontal_alignment
587            .set_value_and_mark_modified(horizontal_alignment);
588        self
589    }
590
591    pub fn horizontal_alignment(&self) -> HorizontalAlignment {
592        *self.horizontal_alignment
593    }
594
595    pub fn set_brush(&mut self, brush: Brush) -> &mut Self {
596        self.brush.set_value_and_mark_modified(brush);
597        self
598    }
599
600    pub fn brush(&self) -> Brush {
601        (*self.brush).clone()
602    }
603
604    pub fn set_super_sampling_scale(&mut self, scale: f32) -> &mut Self {
605        self.super_sampling_scale = scale;
606        self
607    }
608
609    pub fn set_constraint(&mut self, constraint: Vector2<f32>) -> &mut Self {
610        self.constraint = constraint;
611        self
612    }
613
614    pub fn get_raw_text(&self) -> &[char] {
615        &self.text
616    }
617
618    pub fn text(&self) -> String {
619        self.text.iter().collect()
620    }
621
622    pub fn text_range(&self, range: Range<usize>) -> String {
623        self.text[range].iter().collect()
624    }
625
626    /// The width of the character at the given index.
627    pub fn get_char_width(&self, index: usize) -> Option<f32> {
628        let glyph = self.text.get(index)?;
629        Some(
630            GlyphMetrics {
631                font: &mut self.font_at(index).data_ref(),
632                size: self.font_size_at(index),
633            }
634            .advance(*glyph),
635        )
636    }
637
638    /// The width of the characters at the indices in the given iterator.
639    /// This is equivalent to calling [`get_char_width`](Self::get_char_width) repeatedly and summing the results.
640    pub fn get_range_width<T: IntoIterator<Item = usize>>(&self, range: T) -> f32 {
641        let mut width = 0.0;
642        for index in range {
643            width += self.get_char_width(index).unwrap_or_default();
644        }
645        width
646    }
647
648    /// A rectangle relative to the top-left corner of the text that contains the given
649    /// range of characters on the given line. None is returned if the `line` is out of
650    /// bounds. The `range` is relative to the start of the line, so 0 is the first character
651    /// of the line, not the first character of the text.
652    ///
653    /// This rect is appropriate for drawing a selection or highlight for the text,
654    /// and the lower edge of the rectangle can be used to draw an underline.
655    pub fn text_rect<R: RangeBounds<usize>>(&self, line: usize, range: R) -> Option<Rect<f32>> {
656        let line = self.lines.get(line)?;
657        let x = line.x_offset;
658        let y = line.y_offset;
659        let h = line.height;
660        use std::ops::Bound;
661        let start = match range.start_bound() {
662            Bound::Included(&n) => n,
663            Bound::Excluded(&n) => n + 1,
664            Bound::Unbounded => 0,
665        };
666        let end = match range.end_bound() {
667            Bound::Included(&n) => n + 1,
668            Bound::Excluded(&n) => n,
669            Bound::Unbounded => line.len(),
670        };
671        let start = line.begin + start;
672        let end = line.begin + end;
673        let offset = self.get_range_width(line.begin..start);
674        let w = self.get_range_width(start..end);
675        Some(Rect::new(offset + x, y, w, h))
676    }
677
678    pub fn set_text<P: AsRef<str>>(&mut self, text: P) -> &mut Self {
679        self.text
680            .set_value_and_mark_modified(text.as_ref().chars().collect());
681        self
682    }
683
684    pub fn set_chars(&mut self, text: Vec<char>) -> &mut Self {
685        self.text.set_value_and_mark_modified(text);
686        self
687    }
688
689    pub fn set_wrap(&mut self, wrap: WrapMode) -> &mut Self {
690        self.wrap.set_value_and_mark_modified(wrap);
691        self
692    }
693
694    /// Sets whether the shadow enabled or not.
695    pub fn set_shadow(&mut self, shadow: bool) -> &mut Self {
696        self.shadow.set_value_and_mark_modified(shadow);
697        self
698    }
699
700    /// Sets desired shadow brush. It will be used to render the shadow.
701    pub fn set_shadow_brush(&mut self, brush: Brush) -> &mut Self {
702        self.shadow_brush.set_value_and_mark_modified(brush);
703        self
704    }
705
706    /// Sets desired shadow dilation in units. Keep in mind that the dilation is absolute,
707    /// not percentage-based.
708    pub fn set_shadow_dilation(&mut self, thickness: f32) -> &mut Self {
709        self.shadow_dilation.set_value_and_mark_modified(thickness);
710        self
711    }
712
713    /// Sets desired shadow offset in units.
714    pub fn set_shadow_offset(&mut self, offset: Vector2<f32>) -> &mut Self {
715        self.shadow_offset.set_value_and_mark_modified(offset);
716        self
717    }
718
719    /// Runs can optionally modify various style settings for portions of the text.
720    /// Later runs override earlier runs if their ranges overlap and the later run
721    /// sets a property that conflicts with an earlier run.
722    pub fn runs(&self) -> &RunSet {
723        &self.runs
724    }
725
726    /// Modify runs of the text to set the style for portions of the text.
727    /// Later runs potentially override earlier runs if the ranges of the runs overlap and the later run
728    /// sets a property that conflicts with an earlier run.
729    pub fn runs_mut(&mut self) -> &mut RunSet {
730        &mut self.runs
731    }
732
733    /// Replace runs of the text to set the style for portions of the text.
734    /// Later runs potentially override earlier runs if the ranges of the runs overlap and the later run
735    /// sets a property that conflicts with an earlier run.
736    pub fn set_runs(&mut self, runs: RunSet) -> &mut Self {
737        self.runs = runs;
738        self
739    }
740
741    /// The amount of indent of the first line, horizontally separating it
742    /// from the start of the remaining lines.
743    /// If the indent is negative, then the first line will not be indented
744    /// while all the other lines will be indented. By default, this is 0.0.
745    pub fn set_line_indent(&mut self, indent: f32) -> &mut Self {
746        self.line_indent.set_value_and_mark_modified(indent);
747        self
748    }
749
750    /// The amount of indent of the first line, horizontally separating it
751    /// from the start of the remaining lines.
752    /// If the indent is negative, then the first line will not be indented
753    /// while all the other lines will be indented. By default, this is 0.0.
754    pub fn line_indent(&mut self) -> f32 {
755        *self.line_indent
756    }
757
758    /// The space separating each line from the line above and below.
759    /// By default, this is 0.0.
760    pub fn set_line_space(&mut self, space: f32) -> &mut Self {
761        self.line_space.set_value_and_mark_modified(space);
762        self
763    }
764
765    /// The space separating each line from the line above and below.
766    /// By default, this is 0.0.
767    pub fn line_space(&self) -> f32 {
768        *self.line_space
769    }
770
771    pub fn wrap_mode(&self) -> WrapMode {
772        *self.wrap
773    }
774
775    pub fn insert_char(&mut self, code: char, index: usize) -> &mut Self {
776        self.text.insert(index, code);
777        self
778    }
779
780    pub fn insert_str(&mut self, str: &str, position: usize) -> &mut Self {
781        for (i, code) in str.chars().enumerate() {
782            self.text.insert(position + i, code);
783        }
784
785        self
786    }
787
788    pub fn remove_range(&mut self, range: Range<usize>) -> &mut Self {
789        self.text.drain(range);
790        self
791    }
792
793    pub fn remove_at(&mut self, index: usize) -> &mut Self {
794        self.text.remove(index);
795        self
796    }
797
798    /// Returns once all fonts used by this FormattedText are finished loading.
799    pub async fn wait_for_fonts(&mut self) -> Result<(), LoadError> {
800        if let Some(font) = self.font.clone_inner() {
801            font.await?;
802        }
803        for run in self.runs.iter() {
804            if let Some(font) = run.font() {
805                font.clone().await?;
806            }
807        }
808        Ok(())
809    }
810
811    /// Returns true if all fonts used by this resource are Ok.
812    /// This `FormattedText` will not build successfully unless this returns true.
813    pub fn are_fonts_loaded(&self) -> bool {
814        if !self.get_font().is_ok() {
815            return false;
816        }
817        for run in self.runs.iter() {
818            if let Some(font) = run.font() {
819                if !font.is_ok() {
820                    return false;
821                }
822            }
823        }
824        true
825    }
826
827    pub fn are_fonts_loading(&self) -> bool {
828        if !self.get_font().is_loading() {
829            return true;
830        }
831        for run in self.runs.iter() {
832            if let Some(font) = run.font() {
833                if !font.is_loading() {
834                    return true;
835                }
836            }
837        }
838        false
839    }
840
841    pub fn font_load_error_list(&self) -> Vec<(PathBuf, LoadError)> {
842        let mut list = vec![];
843        if let ResourceState::LoadError { path, error } = &self.get_font().header().state {
844            list.push((path.clone(), error.clone()));
845        }
846        for run in self.runs.iter() {
847            if let Some(font) = run.font() {
848                if let ResourceState::LoadError { path, error } = &font.header().state {
849                    list.push((path.clone(), error.clone()));
850                }
851            }
852        }
853        list
854    }
855
856    pub fn font_loading_summary(&self) -> String {
857        use std::fmt::Write;
858        let mut result = String::default();
859        write!(result, "Primary font: {}", self.get_font().header().state).unwrap();
860        for run in self.runs.iter() {
861            if let Some(font) = run.font() {
862                write!(result, "\nRun {:?}: {}", run.range, font.header().state).unwrap();
863            }
864        }
865        result
866    }
867
868    pub fn build(&mut self) -> Vector2<f32> {
869        let mut lines = std::mem::take(&mut self.lines);
870        lines.clear();
871        // Fail early if any font is not available.
872        if !self.are_fonts_loaded() {
873            Log::err(format!(
874                "Text failed to build due to unloaded fonts. {:?}.\n{}",
875                self.text(),
876                self.font_loading_summary(),
877            ));
878            return Vector2::default();
879        }
880        let constraint = Vector2::new(
881            (self.constraint.x - (self.padding.left + self.padding.right)).max(0.0),
882            (self.constraint.y - (self.padding.top + self.padding.bottom)).max(0.0),
883        );
884        let first_indent = self.line_indent.max(0.0);
885        let normal_indent = -self.line_indent.min(0.0);
886        let sink = WrapSink {
887            lines: &mut lines,
888            normal_width: constraint.x - normal_indent,
889            first_width: constraint.x - first_indent,
890        };
891        if let Some(mask) = *self.mask_char {
892            let advance = GlyphMetrics {
893                font: &mut self.get_font().data_ref(),
894                size: **self.font_size,
895            }
896            .advance(mask);
897            match *self.wrap {
898                WrapMode::NoWrap => wrap_mask(NoWrap::new(sink), self.text.len(), mask, advance),
899                WrapMode::Letter => wrap_mask(
900                    LetterWrap::new(sink),
901                    self.text.len(),
902                    mask,
903                    **self.font_size,
904                ),
905                WrapMode::Word => wrap_mask(WordWrap::new(sink), self.text.len(), mask, advance),
906            }
907        } else {
908            let source = self.text.iter().enumerate().map(|(i, c)| {
909                let a = GlyphMetrics {
910                    font: &mut self.font_at(i).data_ref(),
911                    size: self.font_size_at(i),
912                }
913                .advance(*c);
914                (*c, a)
915            });
916            match *self.wrap {
917                WrapMode::NoWrap => wrap(NoWrap::new(sink), source),
918                WrapMode::Letter => wrap(LetterWrap::new(sink), source),
919                WrapMode::Word => wrap(WordWrap::new(sink), source),
920            }
921        }
922
923        let mut total_height = 0.0;
924        // Align lines according to desired alignment.
925        for (i, line) in lines.iter_mut().enumerate() {
926            let indent = if i == 0 { first_indent } else { normal_indent };
927            match *self.horizontal_alignment {
928                HorizontalAlignment::Left => line.x_offset = indent,
929                HorizontalAlignment::Center => {
930                    if constraint.x.is_infinite() {
931                        line.x_offset = indent;
932                    } else {
933                        line.x_offset = 0.5 * (constraint.x - line.width).max(0.0);
934                    }
935                }
936                HorizontalAlignment::Right => {
937                    if constraint.x.is_infinite() {
938                        line.x_offset = indent;
939                    } else {
940                        line.x_offset = (constraint.x - line.width - indent).max(0.0)
941                    }
942                }
943                HorizontalAlignment::Stretch => line.x_offset = indent,
944            }
945            line.x_offset += self.padding.left;
946        }
947        // Calculate line height
948        for line in lines.iter_mut() {
949            if self.mask_char.is_some() || self.runs.is_empty() {
950                line.height = GlyphMetrics {
951                    font: &mut self.get_font().data_ref(),
952                    size: **self.font_size,
953                }
954                .ascender();
955            } else {
956                for i in line.begin..line.end {
957                    let h = GlyphMetrics {
958                        font: &mut self.font_at(i).data_ref(),
959                        size: self.font_size_at(i),
960                    }
961                    .ascender();
962                    line.height = line.height.max(h);
963                }
964            }
965            total_height += line.height + self.line_space();
966        }
967        total_height -= self.line_space();
968
969        // Generate glyphs for each text line.
970        self.glyphs.clear();
971
972        let cursor_y_start = self.padding.top
973            + match *self.vertical_alignment {
974                VerticalAlignment::Top => 0.0,
975                VerticalAlignment::Center => {
976                    if constraint.y.is_infinite() {
977                        0.0
978                    } else {
979                        (constraint.y - total_height).max(0.0) * 0.5
980                    }
981                }
982                VerticalAlignment::Bottom => {
983                    if constraint.y.is_infinite() {
984                        0.0
985                    } else {
986                        (constraint.y - total_height).max(0.0)
987                    }
988                }
989                VerticalAlignment::Stretch => 0.0,
990            };
991        let mut y: f32 = cursor_y_start.floor();
992        for line in lines.iter_mut() {
993            let mut x = line.x_offset.floor();
994            if let Some(mask) = *self.mask_char {
995                let mut prev = None;
996                let font = self.get_font();
997                let mut metrics = GlyphMetrics {
998                    font: &mut font.data_ref(),
999                    size: **self.font_size,
1000                };
1001                for c in std::iter::repeat_n(mask, line.len()) {
1002                    let (glyph, advance) =
1003                        build_glyph(&mut metrics, x, y, 0, c, prev, self.super_sampling_scale);
1004                    self.glyphs.push(glyph);
1005                    x += advance;
1006                    prev = Some(c);
1007                }
1008            } else {
1009                let mut prev = None;
1010                for (i, &c) in self.text.iter().enumerate().take(line.end).skip(line.begin) {
1011                    let font = self.font_at(i);
1012                    let font = &mut font.data_ref();
1013                    let mut metrics = GlyphMetrics {
1014                        font,
1015                        size: self.font_size_at(i),
1016                    };
1017                    match c {
1018                        '\n' => {
1019                            x += metrics.newline_advance();
1020                        }
1021                        _ => {
1022                            let y1 = y + line.height - metrics.ascender();
1023                            let scale = self.super_sampling_scale;
1024                            let (glyph, advance) =
1025                                build_glyph(&mut metrics, x, y1, i, c, prev, scale);
1026                            self.glyphs.push(glyph);
1027                            x += advance;
1028                        }
1029                    }
1030                    prev = Some(c);
1031                }
1032            }
1033            line.y_offset = y;
1034            y += line.height + self.line_space();
1035        }
1036
1037        let size_x = if constraint.x.is_finite() {
1038            constraint.x
1039        } else {
1040            lines
1041                .iter()
1042                .map(|line| line.width)
1043                .max_by(f32::total_cmp)
1044                .unwrap_or_default()
1045        };
1046        let size_y = if constraint.y.is_finite() {
1047            constraint.y
1048        } else {
1049            let descender = if self.mask_char.is_some() || self.runs.is_empty() {
1050                GlyphMetrics {
1051                    font: &mut self.get_font().data_ref(),
1052                    size: **self.font_size,
1053                }
1054                .descender()
1055            } else if let Some(line) = self.lines.last() {
1056                (line.begin..line.end)
1057                    .map(|i| {
1058                        GlyphMetrics {
1059                            font: &mut self.font_at(i).data_ref(),
1060                            size: self.font_size_at(i),
1061                        }
1062                        .descender()
1063                    })
1064                    .min_by(f32::total_cmp)
1065                    .unwrap_or_default()
1066            } else {
1067                0.0
1068            };
1069            // Minus here is because descender has negative value.
1070            total_height - descender
1071        };
1072        self.lines = lines;
1073        Vector2::new(
1074            size_x + self.padding.left + self.padding.right,
1075            size_y + self.padding.top + self.padding.bottom,
1076        )
1077    }
1078}
1079
1080fn wrap<W, I>(mut wrapper: W, source: I)
1081where
1082    W: TextWrapper,
1083    I: Iterator<Item = (char, f32)>,
1084{
1085    for (character, advance) in source {
1086        wrapper.push(character, advance);
1087    }
1088    wrapper.finish();
1089}
1090
1091fn wrap_mask<W: TextWrapper>(mut wrapper: W, length: usize, mask_char: char, advance: f32) {
1092    for _ in 0..length {
1093        wrapper.push(mask_char, advance);
1094    }
1095    wrapper.finish();
1096}
1097
1098pub struct FormattedTextBuilder {
1099    font: FontResource,
1100    brush: Brush,
1101    constraint: Vector2<f32>,
1102    text: Vec<char>,
1103    vertical_alignment: VerticalAlignment,
1104    horizontal_alignment: HorizontalAlignment,
1105    wrap: WrapMode,
1106    mask_char: Option<char>,
1107    shadow: bool,
1108    shadow_brush: Brush,
1109    shadow_dilation: f32,
1110    shadow_offset: Vector2<f32>,
1111    font_size: StyledProperty<f32>,
1112    super_sampling_scaling: f32,
1113    padding: Thickness,
1114    runs: RunSet,
1115    /// The amount of indentation on the first line of the text.
1116    line_indent: f32,
1117    /// The space between lines.
1118    line_space: f32,
1119}
1120
1121impl FormattedTextBuilder {
1122    /// Creates new formatted text builder with default parameters.
1123    pub fn new(font: FontResource) -> FormattedTextBuilder {
1124        FormattedTextBuilder {
1125            font,
1126            text: Vec::default(),
1127            horizontal_alignment: HorizontalAlignment::Left,
1128            vertical_alignment: VerticalAlignment::Top,
1129            brush: Brush::Solid(Color::WHITE),
1130            constraint: Vector2::new(128.0, 128.0),
1131            wrap: WrapMode::NoWrap,
1132            mask_char: None,
1133            shadow: false,
1134            shadow_brush: Brush::Solid(Color::BLACK),
1135            shadow_dilation: 1.0,
1136            shadow_offset: Vector2::new(1.0, 1.0),
1137            font_size: 14.0f32.into(),
1138            super_sampling_scaling: 1.0,
1139            padding: Thickness::uniform(0.0),
1140            runs: RunSet::default(),
1141            line_indent: 0.0,
1142            line_space: 0.0,
1143        }
1144    }
1145
1146    pub fn with_vertical_alignment(mut self, vertical_alignment: VerticalAlignment) -> Self {
1147        self.vertical_alignment = vertical_alignment;
1148        self
1149    }
1150
1151    pub fn with_wrap(mut self, wrap: WrapMode) -> Self {
1152        self.wrap = wrap;
1153        self
1154    }
1155
1156    pub fn with_horizontal_alignment(mut self, horizontal_alignment: HorizontalAlignment) -> Self {
1157        self.horizontal_alignment = horizontal_alignment;
1158        self
1159    }
1160
1161    pub fn with_text(mut self, text: String) -> Self {
1162        self.text = text.chars().collect();
1163        self
1164    }
1165
1166    pub fn with_chars(mut self, text: Vec<char>) -> Self {
1167        self.text = text;
1168        self
1169    }
1170
1171    pub fn with_font_size(mut self, font_size: StyledProperty<f32>) -> Self {
1172        self.font_size = font_size;
1173        self
1174    }
1175
1176    pub fn with_constraint(mut self, constraint: Vector2<f32>) -> Self {
1177        self.constraint = constraint;
1178        self
1179    }
1180
1181    pub fn with_brush(mut self, brush: Brush) -> Self {
1182        self.brush = brush;
1183        self
1184    }
1185
1186    pub fn with_mask_char(mut self, mask_char: Option<char>) -> Self {
1187        self.mask_char = mask_char;
1188        self
1189    }
1190
1191    /// Whether the shadow enabled or not.
1192    pub fn with_shadow(mut self, shadow: bool) -> Self {
1193        self.shadow = shadow;
1194        self
1195    }
1196
1197    /// Sets desired shadow brush. It will be used to render the shadow.
1198    pub fn with_shadow_brush(mut self, brush: Brush) -> Self {
1199        self.shadow_brush = brush;
1200        self
1201    }
1202
1203    /// Sets desired shadow dilation in units. Keep in mind that the dilation is absolute,
1204    /// not percentage-based.
1205    pub fn with_shadow_dilation(mut self, thickness: f32) -> Self {
1206        self.shadow_dilation = thickness;
1207        self
1208    }
1209
1210    /// Sets desired shadow offset in units.
1211    pub fn with_shadow_offset(mut self, offset: Vector2<f32>) -> Self {
1212        self.shadow_offset = offset;
1213        self
1214    }
1215
1216    pub fn with_padding(mut self, padding: Thickness) -> Self {
1217        self.padding = padding;
1218        self
1219    }
1220
1221    /// Sets desired super sampling scaling.
1222    pub fn with_super_sampling_scaling(mut self, scaling: f32) -> Self {
1223        self.super_sampling_scaling = scaling;
1224        self
1225    }
1226
1227    /// Adds the given run to the text to set the style for a portion of the text.
1228    /// Later runs potentially overriding earlier runs if the ranges of the runs overlap and the later run
1229    /// sets a property that conflicts with an earlier run.
1230    pub fn with_run(mut self, run: Run) -> Self {
1231        self.runs.push(run);
1232        self
1233    }
1234
1235    /// Adds multiple runs to the text to set the style of portions of the text.
1236    /// Later runs potentially overriding earlier runs if the ranges of the runs overlap and the later run
1237    /// sets a property that conflicts with an earlier run.
1238    pub fn with_runs<I: IntoIterator<Item = Run>>(mut self, runs: I) -> Self {
1239        for run in runs {
1240            self.runs.push(run);
1241        }
1242        self
1243    }
1244
1245    /// The amount of indent of the first line, horizontally separating it
1246    /// from the start of the remaining lines.
1247    /// If the indent is negative, then the first line will not be indented
1248    /// while all the other lines will be indented. By default, this is 0.0.
1249    pub fn with_line_indent(mut self, indent: f32) -> Self {
1250        self.line_indent = indent;
1251        self
1252    }
1253
1254    /// The space separating each line from the line above and below.
1255    /// By default, this is 0.0.
1256    pub fn with_line_space(mut self, space: f32) -> Self {
1257        self.line_space = space;
1258        self
1259    }
1260
1261    pub fn build(self) -> FormattedText {
1262        FormattedText {
1263            text: self.text.into(),
1264            lines: Vec::new(),
1265            glyphs: Vec::new(),
1266            vertical_alignment: self.vertical_alignment.into(),
1267            horizontal_alignment: self.horizontal_alignment.into(),
1268            brush: self.brush.into(),
1269            constraint: self.constraint,
1270            wrap: self.wrap.into(),
1271            mask_char: self.mask_char.into(),
1272            super_sampling_scale: self.super_sampling_scaling,
1273            font_size: self.font_size.into(),
1274            shadow: self.shadow.into(),
1275            shadow_brush: self.shadow_brush.into(),
1276            font: Some(self.font).into(),
1277            shadow_dilation: self.shadow_dilation.into(),
1278            shadow_offset: self.shadow_offset.into(),
1279            runs: self.runs,
1280            line_indent: self.line_indent.into(),
1281            line_space: self.line_space.into(),
1282            padding: self.padding.into(),
1283        }
1284    }
1285}