1use crate::text_data::{FontKind, TextAlignment, VerticalAlignment};
6
7#[cfg(feature = "shaping")]
11#[derive(Default)]
12pub struct TextState {
13 pub cache: TextCache,
14 pub font_engine: crate::font_engine::FontEngine,
15}
16
17#[derive(Default)]
18pub struct TextCache {
19 pub text_strings: Vec<Option<String>>,
20 pub next_free_slot: usize,
21 pub slot_generations: Vec<u64>,
22 pub generation: u64,
23}
24
25impl TextCache {
26 pub fn new() -> Self {
27 Self {
28 text_strings: Vec::new(),
29 next_free_slot: 0,
30 slot_generations: Vec::new(),
31 generation: 0,
32 }
33 }
34
35 pub fn add_text(&mut self, content: impl Into<String>) -> usize {
36 let content = content.into();
37 self.generation += 1;
38
39 if self.next_free_slot < self.text_strings.len() {
40 let slot = self.next_free_slot;
41 self.text_strings[slot] = Some(content);
42 self.slot_generations[slot] += 1;
43 self.next_free_slot = self.find_next_free_slot();
44 slot
45 } else {
46 let slot = self.text_strings.len();
47 self.text_strings.push(Some(content));
48 self.slot_generations.push(1);
49 self.next_free_slot = slot + 1;
50 slot
51 }
52 }
53
54 pub fn get_text(&self, index: usize) -> Option<&str> {
55 self.text_strings.get(index).and_then(|opt| opt.as_deref())
56 }
57
58 pub fn set_text(&mut self, index: usize, content: impl Into<String>) -> bool {
67 let Some(text_opt) = self.text_strings.get_mut(index) else {
68 return false;
69 };
70 let content = content.into();
71 if text_opt.as_deref() == Some(content.as_str()) {
72 return true;
73 }
74 *text_opt = Some(content);
75 self.slot_generations[index] += 1;
76 self.generation += 1;
77 true
78 }
79
80 pub fn remove_text(&mut self, index: usize) -> bool {
81 if let Some(text_opt) = self.text_strings.get_mut(index) {
82 if text_opt.is_some() {
83 *text_opt = None;
84 self.slot_generations[index] += 1;
85 if index < self.next_free_slot {
86 self.next_free_slot = index;
87 }
88 true
89 } else {
90 false
91 }
92 } else {
93 false
94 }
95 }
96
97 pub fn is_slot_valid(&self, index: usize) -> bool {
98 self.text_strings
99 .get(index)
100 .is_some_and(|opt| opt.is_some())
101 }
102
103 pub fn get_all_text_with_indices(&self) -> Vec<(usize, &str)> {
104 self.text_strings
105 .iter()
106 .enumerate()
107 .filter_map(|(i, opt)| opt.as_deref().map(|s| (i, s)))
108 .collect()
109 }
110
111 pub fn get_valid_slots(&self) -> Vec<usize> {
112 self.text_strings
113 .iter()
114 .enumerate()
115 .filter_map(|(i, opt)| if opt.is_some() { Some(i) } else { None })
116 .collect()
117 }
118
119 pub fn compact(&mut self) -> Vec<(usize, usize)> {
120 let mut new_strings = Vec::new();
121 let mut new_generations = Vec::new();
122 let mut index_mapping = Vec::new();
123
124 for (old_index, opt) in self.text_strings.iter().enumerate() {
125 if let Some(text) = opt {
126 index_mapping.push((old_index, new_strings.len()));
127 new_strings.push(Some(text.clone()));
128 new_generations.push(self.slot_generations.get(old_index).copied().unwrap_or(1));
129 }
130 }
131
132 self.text_strings = new_strings;
133 self.slot_generations = new_generations;
134 self.next_free_slot = self.text_strings.len();
135
136 index_mapping
137 }
138
139 fn find_next_free_slot(&self) -> usize {
140 for (i, opt) in self.text_strings.iter().enumerate() {
141 if opt.is_none() {
142 return i;
143 }
144 }
145 self.text_strings.len()
146 }
147}
148
149#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, enum2schema::Schema)]
152pub struct TextProperties {
153 pub font_size: f32,
154 pub color: nalgebra_glm::Vec4,
155 pub alignment: TextAlignment,
156 pub vertical_alignment: VerticalAlignment,
157 pub line_height: f32,
158 pub letter_spacing: f32,
159 pub outline_width: f32,
160 pub outline_color: nalgebra_glm::Vec4,
161 pub smoothing: f32,
162 pub monospace_width: Option<f32>,
163 pub anchor_character: Option<usize>,
164 pub font_kind: FontKind,
165}
166
167impl Default for TextProperties {
168 fn default() -> Self {
169 Self {
170 font_size: 16.0,
171 color: nalgebra_glm::Vec4::new(1.0, 1.0, 1.0, 1.0),
172 alignment: TextAlignment::Left,
173 vertical_alignment: VerticalAlignment::Baseline,
174 line_height: 1.2,
175 letter_spacing: 0.0,
176 outline_width: 0.0,
177 outline_color: nalgebra_glm::Vec4::new(0.0, 0.0, 0.0, 1.0),
178 smoothing: 0.003,
179 monospace_width: None,
180 anchor_character: None,
181 font_kind: FontKind::Default,
182 }
183 }
184}