1use 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
44const TAB_WIDTH: f32 = 2.0;
46
47#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Visit, Reflect)]
49pub struct Position {
50 pub line: usize,
52
53 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 pub begin: usize,
69 pub end: usize,
71 pub width: f32,
73 pub height: f32,
75 pub x_offset: f32,
77 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#[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 #[default]
125 NoWrap,
126
127 Letter,
129
130 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 match metrics.glyph(character, super_sampling_scale) {
198 Some(glyph) => {
199 let k = 1.0 / super_sampling_scale;
202 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 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 #[reflect(hidden)]
267 #[visit(skip)]
268 lines: Vec<TextLine>,
269 #[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 #[visit(optional)]
296 pub line_indent: InheritableVariable<f32>,
297 #[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 pub fn position_to_char_index_unclamped(&self, position: Position) -> Option<usize> {
440 self.position_to_char_index_internal(position, false)
441 }
442
443 pub fn position_to_char_index_clamped(&self, position: Position) -> Option<usize> {
450 self.position_to_char_index_internal(position, true)
451 }
452
453 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 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 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 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 pub fn set_shadow(&mut self, shadow: bool) -> &mut Self {
696 self.shadow.set_value_and_mark_modified(shadow);
697 self
698 }
699
700 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 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 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 pub fn runs(&self) -> &RunSet {
723 &self.runs
724 }
725
726 pub fn runs_mut(&mut self) -> &mut RunSet {
730 &mut self.runs
731 }
732
733 pub fn set_runs(&mut self, runs: RunSet) -> &mut Self {
737 self.runs = runs;
738 self
739 }
740
741 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 pub fn line_indent(&mut self) -> f32 {
755 *self.line_indent
756 }
757
758 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 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 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 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 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 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 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 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 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 line_indent: f32,
1117 line_space: f32,
1119}
1120
1121impl FormattedTextBuilder {
1122 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 pub fn with_shadow(mut self, shadow: bool) -> Self {
1193 self.shadow = shadow;
1194 self
1195 }
1196
1197 pub fn with_shadow_brush(mut self, brush: Brush) -> Self {
1199 self.shadow_brush = brush;
1200 self
1201 }
1202
1203 pub fn with_shadow_dilation(mut self, thickness: f32) -> Self {
1206 self.shadow_dilation = thickness;
1207 self
1208 }
1209
1210 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 pub fn with_super_sampling_scaling(mut self, scaling: f32) -> Self {
1223 self.super_sampling_scaling = scaling;
1224 self
1225 }
1226
1227 pub fn with_run(mut self, run: Run) -> Self {
1231 self.runs.push(run);
1232 self
1233 }
1234
1235 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 pub fn with_line_indent(mut self, indent: f32) -> Self {
1250 self.line_indent = indent;
1251 self
1252 }
1253
1254 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}