1use crate::{
22 brush::Brush,
23 core::{
24 algebra::Vector2, color::Color, log::Log, 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_resource::state::{LoadError, ResourceState};
32pub use run::*;
33use std::{
34 ops::{Range, RangeBounds},
35 path::PathBuf,
36};
37use strum_macros::{AsRefStr, EnumString, VariantNames};
38use textwrapper::*;
39
40mod run;
41mod textwrapper;
42
43const TAB_WIDTH: f32 = 2.0;
45const ELLIPSIS: char = '…';
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 #[visit(skip)]
302 #[reflect(hidden)]
303 total_height: f32,
304 #[visit(optional)]
307 pub trim_text: InheritableVariable<bool>,
308}
309
310impl FormattedText {
311 pub fn font_at(&self, index: usize) -> FontResource {
312 self.runs.font_at(index).unwrap_or_else(|| self.get_font())
313 }
314 pub fn font_size_at(&self, index: usize) -> f32 {
315 self.runs
316 .font_size_at(index)
317 .unwrap_or_else(|| self.font_size().property)
318 }
319 pub fn brush_at(&self, index: usize) -> Brush {
320 self.runs.brush_at(index).unwrap_or_else(|| self.brush())
321 }
322 pub fn shadow_at(&self, index: usize) -> bool {
323 self.runs.shadow_at(index).unwrap_or(*self.shadow)
324 }
325 pub fn shadow_brush_at(&self, index: usize) -> Brush {
326 self.runs
327 .shadow_brush_at(index)
328 .unwrap_or_else(|| self.shadow_brush.clone_inner())
329 }
330 pub fn shadow_dilation_at(&self, index: usize) -> f32 {
331 self.runs
332 .shadow_dilation_at(index)
333 .unwrap_or(*self.shadow_dilation)
334 }
335 pub fn shadow_offset_at(&self, index: usize) -> Vector2<f32> {
336 self.runs
337 .shadow_offset_at(index)
338 .unwrap_or(*self.shadow_offset)
339 }
340 pub fn nearest_valid_position(&self, start: Position) -> Position {
341 if self.lines.is_empty() {
342 return Position::default();
343 }
344 let mut pos = start;
345 pos.line = usize::min(pos.line, self.lines.len() - 1);
346 pos.offset = usize::min(pos.offset, self.lines[pos.line].len());
347 pos
348 }
349 pub fn get_relative_position_x(&self, start: Position, offset: isize) -> Position {
350 if self.lines.is_empty() {
351 return Position::default();
352 }
353 let mut pos = self.nearest_valid_position(start);
354 let distance = offset.abs();
355 for _ in 0..distance {
356 if offset < 0 {
357 if pos.offset > 0 {
358 pos.offset -= 1
359 } else if pos.line > 0 {
360 pos.line -= 1;
361 pos.offset = self.lines[pos.line].len().saturating_sub(1);
362 } else {
363 pos.offset = 0;
364 break;
365 }
366 } else {
367 let line = &self.lines[pos.line];
368 if pos.offset + 1 < line.len() {
369 pos.offset += 1;
370 } else if pos.line + 1 < self.lines.len() {
371 pos.line += 1;
372 pos.offset = 0;
373 } else {
374 pos.offset = line.len();
375 break;
376 }
377 }
378 }
379 pos
380 }
381
382 pub fn get_relative_position_y(&self, start: Position, offset: isize) -> Position {
383 let mut pos = self.nearest_valid_position(start);
384 pos.line = pos.line.saturating_add_signed(offset);
385 self.nearest_valid_position(pos)
386 }
387
388 pub fn get_line_range(&self, line: usize) -> Range<Position> {
389 let length = self.lines.get(line).map(TextLine::len).unwrap_or(0);
390 Range {
391 start: Position { line, offset: 0 },
392 end: Position {
393 line,
394 offset: length,
395 },
396 }
397 }
398
399 pub fn iter_line_ranges_within(
400 &self,
401 range: Range<Position>,
402 ) -> impl Iterator<Item = Range<Position>> + '_ {
403 (range.start.line..=range.end.line).map(move |i| {
404 let r = self.get_line_range(i);
405 Range {
406 start: Position::max(range.start, r.start),
407 end: Position::min(range.end, r.end),
408 }
409 })
410 }
411
412 pub fn end_position(&self) -> Position {
413 match self.lines.iter().enumerate().next_back() {
414 Some((i, line)) => Position {
415 line: i,
416 offset: line.len(),
417 },
418 None => Position::default(),
419 }
420 }
421
422 fn position_to_char_index_internal(&self, position: Position, clamp: bool) -> Option<usize> {
423 self.lines.get(position.line).map(|line| {
424 line.begin
425 + position.offset.min(if clamp {
426 line.len().saturating_sub(1)
427 } else {
428 line.len()
429 })
430 })
431 }
432
433 pub fn position_range_to_char_index_range(&self, range: Range<Position>) -> Range<usize> {
434 let start = self
435 .position_to_char_index_unclamped(range.start)
436 .unwrap_or(0);
437 let end = self
438 .position_to_char_index_unclamped(range.end)
439 .unwrap_or(self.text.len());
440 start..end
441 }
442 pub fn position_to_char_index_unclamped(&self, position: Position) -> Option<usize> {
447 self.position_to_char_index_internal(position, false)
448 }
449
450 pub fn position_to_char_index_clamped(&self, position: Position) -> Option<usize> {
457 self.position_to_char_index_internal(position, true)
458 }
459
460 pub fn char_index_to_position(&self, i: usize) -> Option<Position> {
462 self.lines
463 .iter()
464 .enumerate()
465 .find_map(|(line_index, line)| {
466 if (line.begin..line.end).contains(&i) {
467 Some(Position {
468 line: line_index,
469 offset: i - line.begin,
470 })
471 } else {
472 None
473 }
474 })
475 .or(Some(self.end_position()))
476 }
477
478 pub fn position_to_local(&self, position: Position) -> Vector2<f32> {
479 if self.get_font().state().data().is_none() {
480 return Default::default();
481 }
482 let position = self.nearest_valid_position(position);
483 let line = &self.lines[position.line];
484 let caret_pos = Vector2::new(line.x_offset, line.y_offset);
485 let range = line.begin..line.begin + position.offset;
486 caret_pos + Vector2::new(self.get_range_width(range), 0.0)
487 }
488
489 pub fn local_to_position(&self, point: Vector2<f32>) -> Position {
490 if self.get_font().state().data().is_none() {
491 return Position::default();
492 }
493 let y = point.y;
494
495 let Some(line_index) = self
496 .lines
497 .iter()
498 .enumerate()
499 .map(|(i, a)| (i, a.y_distance(y)))
500 .min_by(|a, b| f32::total_cmp(&a.1, &b.1))
501 .map(|(i, _)| i)
502 else {
503 return Position::default();
504 };
505 let line = self.lines[line_index];
506 let x = point.x - line.x_offset;
507 let mut glyph_x: f32 = 0.0;
508 let mut min_dist: f32 = x.abs();
509 let mut min_index: usize = 0;
510 for (offset, char_index) in (line.begin..line.end).enumerate() {
511 glyph_x += self.get_char_width(char_index).unwrap_or_default();
512 let dist = (x - glyph_x).abs();
513 if dist < min_dist {
514 min_dist = dist;
515 min_index = offset + 1;
516 }
517 }
518 Position {
519 line: line_index,
520 offset: min_index,
521 }
522 }
523
524 pub fn get_glyphs(&self) -> &[TextGlyph] {
525 &self.glyphs
526 }
527
528 pub fn get_glyph_draw_values(
529 &self,
530 layer: DrawValueLayer,
531 glyph: &TextGlyph,
532 ) -> GlyphDrawValues {
533 let atlas_page_index = glyph.atlas_page_index;
534 let i = glyph.source_char_index;
535 let font = self.font_at(i);
536 let height = FontHeight::from(self.font_size_at(i) * self.super_sampling_scale);
537 match layer {
538 DrawValueLayer::Main => GlyphDrawValues {
539 atlas_page_index,
540 font,
541 brush: self.brush_at(i),
542 height,
543 },
544 DrawValueLayer::Shadow => GlyphDrawValues {
545 atlas_page_index,
546 font,
547 brush: self.shadow_brush_at(i),
548 height,
549 },
550 }
551 }
552
553 pub fn get_font(&self) -> FontResource {
554 (*self.font).clone().unwrap_or(BUILT_IN_FONT.resource())
555 }
556
557 pub fn set_font(&mut self, font: FontResource) -> &mut Self {
558 self.font.set_value_and_mark_modified(Some(font));
559 self
560 }
561
562 pub fn font_size(&self) -> &StyledProperty<f32> {
563 &self.font_size
564 }
565
566 pub fn super_sampled_font_size(&self) -> f32 {
567 **self.font_size * self.super_sampling_scale
568 }
569
570 pub fn set_font_size(&mut self, font_size: StyledProperty<f32>) -> &mut Self {
571 self.font_size.set_value_and_mark_modified(font_size);
572 self
573 }
574
575 pub fn get_lines(&self) -> &[TextLine] {
576 &self.lines
577 }
578
579 pub fn set_vertical_alignment(&mut self, vertical_alignment: VerticalAlignment) -> &mut Self {
580 self.vertical_alignment
581 .set_value_and_mark_modified(vertical_alignment);
582 self
583 }
584
585 pub fn vertical_alignment(&self) -> VerticalAlignment {
586 *self.vertical_alignment
587 }
588
589 pub fn set_horizontal_alignment(
590 &mut self,
591 horizontal_alignment: HorizontalAlignment,
592 ) -> &mut Self {
593 self.horizontal_alignment
594 .set_value_and_mark_modified(horizontal_alignment);
595 self
596 }
597
598 pub fn horizontal_alignment(&self) -> HorizontalAlignment {
599 *self.horizontal_alignment
600 }
601
602 pub fn set_brush(&mut self, brush: Brush) -> &mut Self {
603 self.brush.set_value_and_mark_modified(brush);
604 self
605 }
606
607 pub fn brush(&self) -> Brush {
608 (*self.brush).clone()
609 }
610
611 pub fn set_super_sampling_scale(&mut self, scale: f32) -> &mut Self {
612 self.super_sampling_scale = scale;
613 self
614 }
615
616 pub fn super_sampling_scale(&self) -> f32 {
617 self.super_sampling_scale
618 }
619
620 pub fn set_constraint(&mut self, constraint: Vector2<f32>) -> &mut Self {
621 self.constraint = constraint;
622 self
623 }
624
625 pub fn get_raw_text(&self) -> &[char] {
626 &self.text
627 }
628
629 pub fn text(&self) -> String {
630 self.text.iter().collect()
631 }
632
633 pub fn text_range(&self, range: Range<usize>) -> String {
634 self.text[range].iter().collect()
635 }
636
637 pub fn get_char_width(&self, index: usize) -> Option<f32> {
639 let glyph = self.text.get(index)?;
640 Some(
641 GlyphMetrics {
642 font: &mut self.font_at(index).data_ref(),
643 size: self.font_size_at(index),
644 }
645 .advance(*glyph),
646 )
647 }
648
649 pub fn get_range_width<T: IntoIterator<Item = usize>>(&self, range: T) -> f32 {
652 let mut width = 0.0;
653 for index in range {
654 width += self.get_char_width(index).unwrap_or_default();
655 }
656 width
657 }
658
659 pub fn text_rect<R: RangeBounds<usize>>(&self, line: usize, range: R) -> Option<Rect<f32>> {
667 let line = self.lines.get(line)?;
668 let x = line.x_offset;
669 let y = line.y_offset;
670 let h = line.height;
671 use std::ops::Bound;
672 let start = match range.start_bound() {
673 Bound::Included(&n) => n,
674 Bound::Excluded(&n) => n + 1,
675 Bound::Unbounded => 0,
676 };
677 let end = match range.end_bound() {
678 Bound::Included(&n) => n + 1,
679 Bound::Excluded(&n) => n,
680 Bound::Unbounded => line.len(),
681 };
682 let start = line.begin + start;
683 let end = line.begin + end;
684 let offset = self.get_range_width(line.begin..start);
685 let w = self.get_range_width(start..end);
686 Some(Rect::new(offset + x, y, w, h))
687 }
688
689 pub fn set_text<P: AsRef<str>>(&mut self, text: P) -> &mut Self {
690 self.text
691 .set_value_and_mark_modified(text.as_ref().chars().collect());
692 self
693 }
694
695 pub fn set_chars(&mut self, text: Vec<char>) -> &mut Self {
696 self.text.set_value_and_mark_modified(text);
697 self
698 }
699
700 pub fn set_wrap(&mut self, wrap: WrapMode) -> &mut Self {
701 self.wrap.set_value_and_mark_modified(wrap);
702 self
703 }
704
705 pub fn set_shadow(&mut self, shadow: bool) -> &mut Self {
707 self.shadow.set_value_and_mark_modified(shadow);
708 self
709 }
710
711 pub fn set_shadow_brush(&mut self, brush: Brush) -> &mut Self {
713 self.shadow_brush.set_value_and_mark_modified(brush);
714 self
715 }
716
717 pub fn set_shadow_dilation(&mut self, thickness: f32) -> &mut Self {
720 self.shadow_dilation.set_value_and_mark_modified(thickness);
721 self
722 }
723
724 pub fn set_shadow_offset(&mut self, offset: Vector2<f32>) -> &mut Self {
726 self.shadow_offset.set_value_and_mark_modified(offset);
727 self
728 }
729
730 pub fn runs(&self) -> &RunSet {
734 &self.runs
735 }
736
737 pub fn runs_mut(&mut self) -> &mut RunSet {
741 &mut self.runs
742 }
743
744 pub fn set_runs(&mut self, runs: RunSet) -> &mut Self {
748 self.runs = runs;
749 self
750 }
751
752 pub fn set_line_indent(&mut self, indent: f32) -> &mut Self {
757 self.line_indent.set_value_and_mark_modified(indent);
758 self
759 }
760
761 pub fn line_indent(&mut self) -> f32 {
766 *self.line_indent
767 }
768
769 pub fn set_line_space(&mut self, space: f32) -> &mut Self {
772 self.line_space.set_value_and_mark_modified(space);
773 self
774 }
775
776 pub fn line_space(&self) -> f32 {
779 *self.line_space
780 }
781
782 pub fn wrap_mode(&self) -> WrapMode {
783 *self.wrap
784 }
785
786 pub fn insert_char(&mut self, code: char, index: usize) -> &mut Self {
787 self.text.insert(index, code);
788 self
789 }
790
791 pub fn insert_str(&mut self, str: &str, position: usize) -> &mut Self {
792 for (i, code) in str.chars().enumerate() {
793 self.text.insert(position + i, code);
794 }
795
796 self
797 }
798
799 pub fn remove_range(&mut self, range: Range<usize>) -> &mut Self {
800 self.text.drain(range);
801 self
802 }
803
804 pub fn remove_at(&mut self, index: usize) -> &mut Self {
805 self.text.remove(index);
806 self
807 }
808
809 pub async fn wait_for_fonts(&mut self) -> Result<(), LoadError> {
811 if let Some(font) = self.font.clone_inner() {
812 font.await?;
813 }
814 for run in self.runs.iter() {
815 if let Some(font) = run.font() {
816 font.clone().await?;
817 }
818 }
819 Ok(())
820 }
821
822 pub fn are_fonts_loaded(&self) -> bool {
825 if !self.get_font().is_ok() {
826 return false;
827 }
828 for run in self.runs.iter() {
829 if let Some(font) = run.font() {
830 if !font.is_ok() {
831 return false;
832 }
833 }
834 }
835 true
836 }
837
838 pub fn are_fonts_loading(&self) -> bool {
839 if !self.get_font().is_loading() {
840 return true;
841 }
842 for run in self.runs.iter() {
843 if let Some(font) = run.font() {
844 if !font.is_loading() {
845 return true;
846 }
847 }
848 }
849 false
850 }
851
852 pub fn font_load_error_list(&self) -> Vec<(PathBuf, LoadError)> {
853 let mut list = vec![];
854 if let ResourceState::LoadError { path, error } = &self.get_font().header().state {
855 list.push((path.clone(), error.clone()));
856 }
857 for run in self.runs.iter() {
858 if let Some(font) = run.font() {
859 if let ResourceState::LoadError { path, error } = &font.header().state {
860 list.push((path.clone(), error.clone()));
861 }
862 }
863 }
864 list
865 }
866
867 pub fn font_loading_summary(&self) -> String {
868 use std::fmt::Write;
869 let mut result = String::default();
870 write!(result, "Primary font: {}", self.get_font().header().state).unwrap();
871 for run in self.runs.iter() {
872 if let Some(font) = run.font() {
873 write!(result, "\nRun {:?}: {}", run.range, font.header().state).unwrap();
874 }
875 }
876 result
877 }
878
879 pub fn measure_and_arrange(&mut self) -> Vector2<f32> {
880 let size = self.measure();
881 self.arrange(self.constraint);
882 size
883 }
884
885 pub fn measure(&mut self) -> Vector2<f32> {
886 let mut lines = std::mem::take(&mut self.lines);
887 lines.clear();
888 if !self.are_fonts_loaded() {
890 Log::err(format!(
891 "Unable to measure text due to unloaded fonts. {:?}.\n{}",
892 self.text(),
893 self.font_loading_summary(),
894 ));
895 return Vector2::default();
896 }
897 let constraint = Vector2::new(
898 (self.constraint.x - (self.padding.left + self.padding.right)).max(0.0),
899 (self.constraint.y - (self.padding.top + self.padding.bottom)).max(0.0),
900 );
901 let first_indent = self.line_indent.max(0.0);
902 let normal_indent = -self.line_indent.min(0.0);
903 let sink = WrapSink {
904 lines: &mut lines,
905 normal_width: constraint.x - normal_indent,
906 first_width: constraint.x - first_indent,
907 };
908 if let Some(mask) = *self.mask_char {
909 let advance = GlyphMetrics {
910 font: &mut self.get_font().data_ref(),
911 size: **self.font_size,
912 }
913 .advance(mask);
914 match *self.wrap {
915 WrapMode::NoWrap => wrap_mask(NoWrap::new(sink), self.text.len(), mask, advance),
916 WrapMode::Letter => wrap_mask(
917 LetterWrap::new(sink),
918 self.text.len(),
919 mask,
920 **self.font_size,
921 ),
922 WrapMode::Word => wrap_mask(WordWrap::new(sink), self.text.len(), mask, advance),
923 }
924 } else {
925 let source = self.text.iter().enumerate().map(|(i, c)| {
926 let a = GlyphMetrics {
927 font: &mut self.font_at(i).data_ref(),
928 size: self.font_size_at(i),
929 }
930 .advance(*c);
931 (*c, a)
932 });
933 match *self.wrap {
934 WrapMode::NoWrap => wrap(NoWrap::new(sink), source),
935 WrapMode::Letter => wrap(LetterWrap::new(sink), source),
936 WrapMode::Word => wrap(WordWrap::new(sink), source),
937 }
938 }
939
940 self.total_height = 0.0;
941
942 for line in lines.iter_mut() {
944 if self.mask_char.is_some() || self.runs.is_empty() {
945 line.height = GlyphMetrics {
946 font: &mut self.get_font().data_ref(),
947 size: **self.font_size,
948 }
949 .ascender();
950 } else {
951 for i in line.begin..line.end {
952 let h = GlyphMetrics {
953 font: &mut self.font_at(i).data_ref(),
954 size: self.font_size_at(i),
955 }
956 .ascender();
957 line.height = line.height.max(h);
958 }
959 }
960 self.total_height += line.height + self.line_space();
961 }
962 self.total_height -= self.line_space();
963
964 let size_x = if constraint.x.is_finite() {
965 constraint.x
966 } else {
967 lines
968 .iter()
969 .map(|line| line.width)
970 .max_by(f32::total_cmp)
971 .unwrap_or_default()
972 };
973 let size_y = if constraint.y.is_finite() {
974 constraint.y
975 } else {
976 let descender = if self.mask_char.is_some() || self.runs.is_empty() {
977 GlyphMetrics {
978 font: &mut self.get_font().data_ref(),
979 size: **self.font_size,
980 }
981 .descender()
982 } else if let Some(line) = lines.last() {
983 (line.begin..line.end)
984 .map(|i| {
985 GlyphMetrics {
986 font: &mut self.font_at(i).data_ref(),
987 size: self.font_size_at(i),
988 }
989 .descender()
990 })
991 .min_by(f32::total_cmp)
992 .unwrap_or_default()
993 } else {
994 0.0
995 };
996 self.total_height - descender
998 };
999 self.lines = lines;
1000 Vector2::new(
1001 size_x + self.padding.left + self.padding.right,
1002 size_y + self.padding.top + self.padding.bottom,
1003 )
1004 }
1005
1006 pub fn arrange(&mut self, constraint: Vector2<f32>) {
1007 self.constraint = constraint;
1008 let constraint = Vector2::new(
1009 (self.constraint.x - (self.padding.left + self.padding.right)).max(0.0),
1010 (self.constraint.y - (self.padding.top + self.padding.bottom)).max(0.0),
1011 );
1012 let mut lines = std::mem::take(&mut self.lines);
1013 let first_indent = self.line_indent.max(0.0);
1014 let normal_indent = -self.line_indent.min(0.0);
1015 for (i, line) in lines.iter_mut().enumerate() {
1017 let indent = if i == 0 { first_indent } else { normal_indent };
1018 match *self.horizontal_alignment {
1019 HorizontalAlignment::Left => line.x_offset = indent,
1020 HorizontalAlignment::Center => {
1021 if constraint.x.is_infinite() {
1022 line.x_offset = indent;
1023 } else {
1024 line.x_offset = 0.5 * (constraint.x - line.width).max(0.0);
1025 }
1026 }
1027 HorizontalAlignment::Right => {
1028 if constraint.x.is_infinite() {
1029 line.x_offset = indent;
1030 } else {
1031 line.x_offset = (constraint.x - line.width - indent).max(0.0)
1032 }
1033 }
1034 HorizontalAlignment::Stretch => line.x_offset = indent,
1035 }
1036 line.x_offset += self.padding.left;
1037 }
1038
1039 self.glyphs.clear();
1041
1042 let cursor_y_start = self.padding.top
1043 + match *self.vertical_alignment {
1044 VerticalAlignment::Top => 0.0,
1045 VerticalAlignment::Center => {
1046 if constraint.y.is_infinite() {
1047 0.0
1048 } else {
1049 (constraint.y - self.total_height).max(0.0) * 0.5
1050 }
1051 }
1052 VerticalAlignment::Bottom => {
1053 if constraint.y.is_infinite() {
1054 0.0
1055 } else {
1056 (constraint.y - self.total_height).max(0.0)
1057 }
1058 }
1059 VerticalAlignment::Stretch => 0.0,
1060 };
1061 let mut y: f32 = cursor_y_start.floor();
1062 for line in lines.iter_mut() {
1063 let mut x = line.x_offset.floor();
1064 if let Some(mask) = *self.mask_char {
1065 let mut prev = None;
1066 let font = self.get_font();
1067 let mut metrics = GlyphMetrics {
1068 font: &mut font.data_ref(),
1069 size: **self.font_size,
1070 };
1071 for c in std::iter::repeat_n(mask, line.len()) {
1072 let (glyph, advance) =
1073 build_glyph(&mut metrics, x, y, 0, c, prev, self.super_sampling_scale);
1074 self.glyphs.push(glyph);
1075 x += advance;
1076 prev = Some(c);
1077 }
1078 } else {
1079 let mut prev = None;
1080 for (i, &c) in self.text.iter().enumerate().take(line.end).skip(line.begin) {
1081 let font = self.font_at(i);
1082 let font = &mut font.data_ref();
1083 let size = self.font_size_at(i);
1084 let mut metrics = GlyphMetrics { font, size };
1085 match c {
1086 '\n' => {
1087 x += metrics.newline_advance();
1088 }
1089 _ => {
1090 let y1 = y + line.height - metrics.ascender();
1091 let scale = self.super_sampling_scale;
1092 let (glyph, advance) =
1093 build_glyph(&mut metrics, x, y1, i, c, prev, scale);
1094
1095 if *self.trim_text
1096 && *self.wrap == WrapMode::NoWrap
1097 && line.width > constraint.x
1098 {
1099 let ellipsis_advance = metrics.advance(ELLIPSIS);
1100 if x + ellipsis_advance * 1.5 > constraint.x {
1101 let (glyph, _) =
1102 build_glyph(&mut metrics, x, y1, i, ELLIPSIS, prev, scale);
1103 self.glyphs.push(glyph);
1104 break;
1105 }
1106 }
1107
1108 self.glyphs.push(glyph);
1109 x += advance;
1110 }
1111 }
1112 prev = Some(c);
1113 }
1114 }
1115 line.y_offset = y;
1116 y += line.height + self.line_space();
1117 }
1118 self.lines = lines;
1119 }
1120}
1121
1122fn wrap<W, I>(mut wrapper: W, source: I)
1123where
1124 W: TextWrapper,
1125 I: Iterator<Item = (char, f32)>,
1126{
1127 for (character, advance) in source {
1128 wrapper.push(character, advance);
1129 }
1130 wrapper.finish();
1131}
1132
1133fn wrap_mask<W: TextWrapper>(mut wrapper: W, length: usize, mask_char: char, advance: f32) {
1134 for _ in 0..length {
1135 wrapper.push(mask_char, advance);
1136 }
1137 wrapper.finish();
1138}
1139
1140pub struct FormattedTextBuilder {
1141 font: FontResource,
1142 brush: Brush,
1143 constraint: Vector2<f32>,
1144 text: Vec<char>,
1145 vertical_alignment: VerticalAlignment,
1146 horizontal_alignment: HorizontalAlignment,
1147 wrap: WrapMode,
1148 mask_char: Option<char>,
1149 shadow: bool,
1150 shadow_brush: Brush,
1151 shadow_dilation: f32,
1152 shadow_offset: Vector2<f32>,
1153 font_size: StyledProperty<f32>,
1154 super_sampling_scaling: f32,
1155 padding: Thickness,
1156 runs: RunSet,
1157 line_indent: f32,
1159 line_space: f32,
1161 trim_text: bool,
1162}
1163
1164impl FormattedTextBuilder {
1165 pub fn new(font: FontResource) -> FormattedTextBuilder {
1167 FormattedTextBuilder {
1168 font,
1169 text: Vec::default(),
1170 horizontal_alignment: HorizontalAlignment::Left,
1171 vertical_alignment: VerticalAlignment::Top,
1172 brush: Brush::Solid(Color::WHITE),
1173 constraint: Vector2::new(128.0, 128.0),
1174 wrap: WrapMode::NoWrap,
1175 mask_char: None,
1176 shadow: false,
1177 shadow_brush: Brush::Solid(Color::BLACK),
1178 shadow_dilation: 1.0,
1179 shadow_offset: Vector2::new(1.0, 1.0),
1180 font_size: 14.0f32.into(),
1181 super_sampling_scaling: 1.0,
1182 padding: Thickness::uniform(0.0),
1183 runs: RunSet::default(),
1184 line_indent: 0.0,
1185 line_space: 0.0,
1186 trim_text: false,
1187 }
1188 }
1189
1190 pub fn with_vertical_alignment(mut self, vertical_alignment: VerticalAlignment) -> Self {
1191 self.vertical_alignment = vertical_alignment;
1192 self
1193 }
1194
1195 pub fn with_wrap(mut self, wrap: WrapMode) -> Self {
1196 self.wrap = wrap;
1197 self
1198 }
1199
1200 pub fn with_horizontal_alignment(mut self, horizontal_alignment: HorizontalAlignment) -> Self {
1201 self.horizontal_alignment = horizontal_alignment;
1202 self
1203 }
1204
1205 pub fn with_text(mut self, text: String) -> Self {
1206 self.text = text.chars().collect();
1207 self
1208 }
1209
1210 pub fn with_chars(mut self, text: Vec<char>) -> Self {
1211 self.text = text;
1212 self
1213 }
1214
1215 pub fn with_font_size(mut self, font_size: StyledProperty<f32>) -> Self {
1216 self.font_size = font_size;
1217 self
1218 }
1219
1220 pub fn with_constraint(mut self, constraint: Vector2<f32>) -> Self {
1221 self.constraint = constraint;
1222 self
1223 }
1224
1225 pub fn with_brush(mut self, brush: Brush) -> Self {
1226 self.brush = brush;
1227 self
1228 }
1229
1230 pub fn with_mask_char(mut self, mask_char: Option<char>) -> Self {
1231 self.mask_char = mask_char;
1232 self
1233 }
1234
1235 pub fn with_shadow(mut self, shadow: bool) -> Self {
1237 self.shadow = shadow;
1238 self
1239 }
1240
1241 pub fn with_shadow_brush(mut self, brush: Brush) -> Self {
1243 self.shadow_brush = brush;
1244 self
1245 }
1246
1247 pub fn with_shadow_dilation(mut self, thickness: f32) -> Self {
1250 self.shadow_dilation = thickness;
1251 self
1252 }
1253
1254 pub fn with_shadow_offset(mut self, offset: Vector2<f32>) -> Self {
1256 self.shadow_offset = offset;
1257 self
1258 }
1259
1260 pub fn with_padding(mut self, padding: Thickness) -> Self {
1261 self.padding = padding;
1262 self
1263 }
1264
1265 pub fn with_super_sampling_scaling(mut self, scaling: f32) -> Self {
1267 self.super_sampling_scaling = scaling;
1268 self
1269 }
1270
1271 pub fn with_run(mut self, run: Run) -> Self {
1275 self.runs.push(run);
1276 self
1277 }
1278
1279 pub fn with_runs<I: IntoIterator<Item = Run>>(mut self, runs: I) -> Self {
1283 for run in runs {
1284 self.runs.push(run);
1285 }
1286 self
1287 }
1288
1289 pub fn with_line_indent(mut self, indent: f32) -> Self {
1294 self.line_indent = indent;
1295 self
1296 }
1297
1298 pub fn with_line_space(mut self, space: f32) -> Self {
1301 self.line_space = space;
1302 self
1303 }
1304
1305 pub fn with_trim_text(mut self, trim: bool) -> Self {
1308 self.trim_text = trim;
1309 self
1310 }
1311
1312 pub fn build(self) -> FormattedText {
1313 FormattedText {
1314 text: self.text.into(),
1315 lines: Vec::new(),
1316 glyphs: Vec::new(),
1317 vertical_alignment: self.vertical_alignment.into(),
1318 horizontal_alignment: self.horizontal_alignment.into(),
1319 brush: self.brush.into(),
1320 constraint: self.constraint,
1321 wrap: self.wrap.into(),
1322 mask_char: self.mask_char.into(),
1323 super_sampling_scale: self.super_sampling_scaling,
1324 font_size: self.font_size.into(),
1325 shadow: self.shadow.into(),
1326 shadow_brush: self.shadow_brush.into(),
1327 font: Some(self.font).into(),
1328 shadow_dilation: self.shadow_dilation.into(),
1329 shadow_offset: self.shadow_offset.into(),
1330 runs: self.runs,
1331 line_indent: self.line_indent.into(),
1332 line_space: self.line_space.into(),
1333 padding: self.padding.into(),
1334 total_height: 0.0,
1335 trim_text: self.trim_text.into(),
1336 }
1337 }
1338}