comfy_core/
text.rs

1use std::sync::atomic::AtomicU64;
2
3use crate::*;
4
5pub struct DrawText {
6    pub text: TextData,
7    pub position: Vec2,
8    pub font: egui::FontId,
9    pub color: Color,
10    pub align: TextAlign,
11    pub z_index: i32,
12    // Temporarily to allow both egui and comfy rasterization
13    pub pro_params: Option<ProTextParams>,
14}
15
16#[derive(Clone, Debug)]
17pub struct TextParams {
18    pub font: egui::FontId,
19    pub rotation: f32,
20    pub color: Color,
21    pub z_index: i32,
22}
23
24impl Default for TextParams {
25    fn default() -> TextParams {
26        TextParams {
27            font: egui::FontId::new(20.0, egui::FontFamily::Monospace),
28            color: WHITE,
29            rotation: 0.0,
30            z_index: 0,
31        }
32    }
33}
34
35#[doc(hidden)]
36pub enum TextData {
37    Raw(String),
38    Rich(RichText),
39}
40
41pub fn draw_text_ex(
42    text: &str,
43    position: Vec2,
44    align: TextAlign,
45    params: TextParams,
46) {
47    let _span = span!("draw_text_ex");
48
49    draw_text_internal(
50        TextData::Raw(text.to_string()),
51        position,
52        align,
53        None,
54        params,
55    );
56}
57
58pub fn draw_text(text: &str, position: Vec2, color: Color, align: TextAlign) {
59    draw_text_internal(
60        TextData::Raw(text.to_string()),
61        position,
62        align,
63        None,
64        TextParams { color, ..Default::default() },
65    )
66}
67
68/// This is a first iteration of Comfy's rich text rendering.
69///
70/// The API works and is fully usable, but it's going to change in backwards
71/// incompatible ways in the future, which is the main reason for the `_experimental`
72/// suffix.
73///
74/// This is not intended as a high performance API for rendering webpages
75/// or fullscreen books as fast as possible. The goal is maximizing flexibility
76/// and ergonomics of highly stylized text for games.
77pub fn draw_text_pro_experimental(
78    text: RichText,
79    position: Vec2,
80    color: Color,
81    align: TextAlign,
82    font_size: f32,
83    font: FontHandle,
84    z_index: i32,
85) {
86    draw_text_internal(
87        TextData::Rich(text),
88        position,
89        align,
90        Some(ProTextParams { font_size, font }),
91        TextParams { color, z_index, ..Default::default() },
92    );
93}
94
95static FONT_HANDLE_COUNTER: AtomicU64 = AtomicU64::new(0);
96
97/// Internal use only font handle generation.
98#[doc(hidden)]
99pub fn gen_font_handle() -> FontHandle {
100    FontHandle(
101        FONT_HANDLE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst),
102    )
103}
104
105/// Opaque handle to a user font.
106///
107/// The ID is exposed only for debugging purposes.
108#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
109pub struct FontHandle(pub u64);
110
111#[doc(hidden)]
112/// Temporary while the API stabilizes.
113pub struct ProTextParams {
114    pub font: FontHandle,
115    pub font_size: f32,
116}
117
118#[derive(Copy, Clone, Debug)]
119pub enum TextAlign {
120    TopLeft,
121    TopRight,
122    BottomLeft,
123    BottomRight,
124    Center,
125}
126
127#[derive(Copy, Clone, Debug)]
128pub struct StyledGlyph {
129    #[allow(dead_code)]
130    /// The related character.
131    ///
132    /// We don't really need this, but it's easier for debugging the parser.
133    pub char: char,
134    /// If true the character will wiggle.
135    pub wiggle: bool,
136    /// Color override of the character
137    pub color: Option<Color>,
138}
139
140#[derive(Debug)]
141pub struct RichText {
142    pub clean_text: String,
143    pub styled_glyphs: Vec<StyledGlyph>,
144}
145
146/// Parses a simple subset of markdown-like syntax.
147///
148/// Users should feel encouraged to build their own syntax for rich text
149/// based on their needs. This should only serve as a baseline.
150pub fn simple_styled_text(text: &str) -> RichText {
151    let mut i = 0;
152
153    let mut clean_text = String::new();
154    let mut styled_glyphs = vec![];
155
156    let chars = text.chars().collect_vec();
157
158    while i < chars.len() {
159        let mut c = chars[i];
160
161        if c == '*' {
162            i += 1;
163            if i == chars.len() {
164                break;
165            }
166
167            c = chars[i];
168            styled_glyphs.push(StyledGlyph {
169                char: c,
170                wiggle: true,
171                color: Some(PINK.boost(4.0)),
172            });
173        } else {
174            styled_glyphs.push(StyledGlyph {
175                char: c,
176                wiggle: false,
177                color: None,
178            });
179        }
180
181        clean_text.push(c);
182
183        i += 1;
184    }
185
186    RichText { clean_text, styled_glyphs }
187}