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