1use lunar_math::{Color, Vec2};
18
19#[derive(Debug, Clone)]
24pub struct Textbox {
25 pub text: String,
27 pub position: Vec2,
29 pub size: Vec2,
31 pub font_id: u32,
33 pub font_size: f32,
35 pub color: Color,
37 pub background_color: Option<Color>,
39 pub padding: f32,
41 pub typewriter: Option<TypewriterState>,
43}
44
45#[derive(Debug, Clone)]
50pub struct TypewriterState {
51 pub visible_chars: usize,
53 pub char_count: usize,
55 pub interval: f32,
57 pub accumulator: f32,
59 pub complete: bool,
61}
62
63impl Textbox {
64 #[must_use]
66 pub fn new(text: &str, position: Vec2, size: Vec2) -> Self {
67 Self {
68 text: text.to_string(),
69 position,
70 size,
71 font_id: 0,
72 font_size: 16.0,
73 color: Color::WHITE,
74 background_color: None,
75 padding: 8.0,
76 typewriter: None,
77 }
78 }
79
80 pub fn set_font(&mut self, font_id: u32, font_size: f32) -> &mut Self {
82 self.font_id = font_id;
83 self.font_size = font_size;
84 self
85 }
86
87 pub fn set_color(&mut self, color: Color) -> &mut Self {
89 self.color = color;
90 self
91 }
92
93 pub fn set_background(&mut self, color: Color) -> &mut Self {
95 self.background_color = Some(color);
96 self
97 }
98
99 pub fn set_padding(&mut self, padding: f32) -> &mut Self {
101 self.padding = padding;
102 self
103 }
104
105 pub fn start_typewriter(&mut self, interval: f32) {
108 self.typewriter = Some(TypewriterState {
109 visible_chars: 0,
110 char_count: self.text.chars().count(),
111 interval,
112 accumulator: 0.0,
113 complete: false,
114 });
115 }
116
117 pub fn update_typewriter(&mut self, delta: f32) -> bool {
120 let Some(state) = &mut self.typewriter else {
121 return false;
122 };
123
124 if state.complete {
125 return false;
126 }
127
128 state.accumulator += delta;
129
130 while state.accumulator >= state.interval && state.visible_chars < state.char_count {
131 state.accumulator -= state.interval;
132 state.visible_chars += 1;
133 }
134
135 if state.visible_chars >= state.char_count {
136 state.complete = true;
137 false
138 } else {
139 true
140 }
141 }
142
143 pub fn skip_typewriter(&mut self) {
145 if let Some(state) = &mut self.typewriter {
146 state.visible_chars = state.char_count;
147 state.complete = true;
148 }
149 }
150
151 #[must_use]
153 pub fn visible_text(&self) -> &str {
154 if let Some(state) = &self.typewriter {
155 let char_count = state.visible_chars;
156 self.text
157 .char_indices()
158 .nth(char_count)
159 .map_or(&self.text, |(idx, _)| &self.text[..idx])
160 } else {
161 &self.text
162 }
163 }
164}