1use super::*;
5
6impl Ui {
7 pub fn add_tab(&mut self, parent: NodeId, header: NodeId, content: NodeId) {
9 if let Kind::Control(Control::Tabs { tabs, .. }) = &mut self.nodes[parent].kind {
10 tabs.push((header, content));
11 }
12 }
13
14 pub(crate) fn make_text(&mut self, font: Option<&str>, text: String) -> TextData {
15 TextData {
16 font: font.and_then(|f| self.res.font(f)),
17 text,
18 max_width: 0,
19 justify: 0,
20 wrap: false,
21 shadow: false,
22 ellipsis_width: 0,
23 layout: TextLayout::default(),
24 }
25 }
26
27 pub(super) fn control_button(&mut self, b: &ui::Button) -> NodeId {
28 let states =
29 std::array::from_fn(|i| b.states[i].as_deref().and_then(|r| self.instantiate(r)));
30 let content = b.content.as_deref().and_then(|r| self.instantiate(r));
31 self.add(Node::new(Kind::Button {
32 states,
33 content,
34 colors: b.colors,
35 state: 0,
36 }))
37 }
38
39 pub(crate) fn instantiate_control(&mut self, kind: &ElementKind) -> Control {
40 let child = |ui: &mut Ui, r: &Option<String>| r.as_deref().and_then(|r| ui.instantiate(r));
41 let sprites = |ui: &mut Ui, sheet: &Option<String>, names: &[String]| {
42 names
43 .iter()
44 .map(|s| ui.res.sprite(sheet.as_deref(), s))
45 .collect::<Vec<_>>()
46 };
47 match kind {
48 ElementKind::Slider {
49 track,
50 sprite_sheet,
51 sprites: names,
52 unknown,
53 range,
54 unknown_b0,
55 } => Control::Slider(SliderData {
56 sprites: sprites(self, sprite_sheet, names),
57 range: *range,
58 value: range[0],
59 steps: *unknown_b0,
60 inset: unknown[1],
61 track_y: unknown[0],
62 number: child(self, track),
63 over_knob: false,
64 drag: None,
65 }),
66 ElementKind::Scroll {
67 sprite_sheet,
68 sprites: names,
69 vertical,
70 unknown,
71 unknown_u32,
72 } => Control::Scroll {
73 sprites: sprites(self, sprite_sheet, names),
74 vertical: *vertical,
75 value: 0.0,
76 page: 0.25,
77 inset: *unknown,
78 thumb_color: unknown_u32[0],
79 drag: None,
80 },
81 ElementKind::Progress {
82 color,
83 sprite_sheet,
84 sprites: names,
85 unknown_ref,
86 } => Control::Progress {
87 sprites: sprites(self, sprite_sheet, names),
88 background_color: *color,
89 value: 0.0,
90 content: child(self, unknown_ref),
91 },
92 ElementKind::Edit {
93 font,
94 text,
95 max_length,
96 unknown_bool,
97 unknown_string,
98 } => Control::Edit(EditData {
99 text: self.make_text(font.as_deref(), text.clone()),
100 cursor: text.chars().count(),
101 max_length: if *max_length > 0 {
102 *max_length as usize
103 } else {
104 usize::MAX
105 },
106 numeric: false,
107 range: None,
108 password: *unknown_bool,
109 allowed: unknown_string.clone(),
110 caret: true,
111 blink: 0.0,
112 }),
113 ElementKind::NumberEdit {
114 font,
115 unknown,
116 unknown_bool,
117 } => Control::Edit(EditData {
118 text: self.make_text(font.as_deref(), unknown[0].to_string()),
119 cursor: 1,
120 max_length: unknown[3].max(1) as usize,
121 numeric: true,
122 range: Some([unknown[1] as f32, unknown[2] as f32]),
123 password: false,
124 allowed: if *unknown_bool {
125 "0123456789-".into()
126 } else {
127 "0123456789".into()
128 },
129 caret: true,
130 blink: 0.0,
131 }),
132 ElementKind::Number {
133 font,
134 unknown_float,
135 ..
136 } => Control::Edit(EditData {
137 text: self.make_text(font.as_deref(), format_number(*unknown_float)),
138 cursor: 0,
139 max_length: 0,
140 numeric: true,
141 range: None,
142 password: false,
143 allowed: String::new(),
144 caret: true,
145 blink: 0.0,
146 }),
147 ElementKind::Graph {
148 unknown,
149 unknown_u32,
150 sprite_sheet,
151 sprite,
152 } => Control::Graph {
153 size: [unknown[0], unknown[1]],
156 sprite: self.res.sprite(sprite_sheet.as_deref(), sprite),
157 tick: {
158 let _ = unknown_u32;
159 unknown[2]
160 },
161 y_range: 1.0,
162 x_divisions: 12,
163 y_divisions: 4,
164 series: Vec::new(),
165 },
166 ElementKind::Table { unknown, cells } => {
167 let cols = unknown[0].clamp(0, 256) as usize;
168 let rows = unknown[1].clamp(0, 4096) as usize;
169 let mut out = Vec::new();
170 for y in 0..rows {
171 for x in 0..cols {
172 let template = cells
174 .iter()
175 .filter(|cell| {
176 (cell.column < 0 || cell.column as usize == x)
177 && (cell.row < 0 || cell.row as usize == y)
178 })
179 .max_by_key(|cell| (cell.column >= 0) as u8 + (cell.row >= 0) as u8);
180 if let Some(c) = template.and_then(|cell| child(self, &cell.element)) {
181 out.push((x, y, c));
182 }
183 }
184 }
185 Control::Table {
186 cells: out,
187 columns: vec![0; cols],
188 rows: vec![0; rows],
189 spacing: unknown[2],
190 }
191 }
192 ElementKind::Layout { items } => Control::Layout(
193 items
194 .iter()
195 .filter_map(|spec| {
196 child(self, &spec.element).map(|node| LayoutChild {
197 node,
198 spec: spec.clone(),
199 })
200 })
201 .collect(),
202 ),
203 ElementKind::Tab {
204 refs,
205 tabs,
206 unknown,
207 ..
208 } => Control::Tabs {
209 decoration: std::array::from_fn(|i| child(self, &refs[i])),
210 tabs: tabs
211 .iter()
212 .filter_map(|(a, b)| Some((child(self, a)?, child(self, b)?)))
213 .collect(),
214 selected: 0,
215 gap: *unknown,
216 header_height: 0,
217 },
218 ElementKind::ClipRegion { unknown, content } => Control::Clip {
219 content: child(self, content),
220 size: *unknown,
221 offset: [0, 0],
222 },
223 ElementKind::Container { unknown, refs, .. } => Control::Container {
224 content: child(self, &refs[2]),
225 bars: [child(self, &refs[0]), child(self, &refs[1])],
226 size: [unknown[0], unknown[1]],
227 spacing: unknown[2],
228 offset: [0, 0],
229 view: [0; 4],
230 wheel: 0,
231 fine: 0.0,
232 },
233 ElementKind::Check { button, check } => Control::Check {
234 button: self.control_button(button),
235 mark: child(self, check),
236 checked: false,
237 },
238 ElementKind::Combo { button, refs } => {
239 let b = self.control_button(button);
240 self.nodes[b].flags |= crate::flags::FILL_WIDTH | crate::flags::FILL_HEIGHT;
242 self.set_text(b, "");
243 let font = refs[1]
244 .as_deref()
245 .and_then(|r| self.res.element(r))
246 .and_then(|e| match &e.kind {
247 ElementKind::Text(t) => t.font.clone(),
248 _ => None,
249 })
250 .unwrap_or_else(|| r"Font\FontSmall.rsc:resource".into());
251 let list = self.list(&font);
252 if let Some(items) = refs[2].as_deref().and_then(|r| {
255 let data = self.res.archive.read_ref(r).ok()?;
256 banished_formats::ui::string_table(&data).ok()
257 }) {
258 self.set_list_items(list, items.into_iter().map(|(_, v)| v).collect());
259 }
260 Control::Combo {
261 button: b,
262 list,
263 open: false,
264 }
265 }
266 ElementKind::Spinner { refs, unknown } => Control::Spinner {
267 parts: std::array::from_fn(|i| child(self, &refs[i])),
268 value: 0,
269 step: *unknown,
270 },
271 ElementKind::Page { children, page } => Control::Page {
272 children: children.iter().filter_map(|r| child(self, r)).collect(),
273 selected: (*page).max(0) as usize,
274 },
275 ElementKind::Radio { children, selected } => Control::Radio {
276 children: children.iter().filter_map(|r| child(self, r)).collect(),
277 selected: (*selected).max(0) as usize,
278 },
279 ElementKind::DocumentText(d) => Control::Document(self.load_document(d)),
280 ElementKind::List {
281 refs,
282 sprite_sheet,
283 sprite,
284 ..
285 } => {
286 let f = refs[0]
287 .as_deref()
288 .and_then(|r| self.res.element(r))
289 .and_then(|e| match &e.kind {
290 ElementKind::Text(t) => t.font.clone(),
291 _ => None,
292 });
293 let font = f.as_deref().and_then(|f| self.res.font(f));
294 let highlight = self.res.sprite(sprite_sheet.as_deref(), sprite);
295 let id = self.add(Node::new(Kind::List(ListData {
296 items: Vec::new(),
297 icons: Vec::new(),
298 selected: None,
299 hovered: None,
300 scroll: 0,
301 font,
302 row_height: font.map_or(20, |f| self.res.fonts[f].font.size + 4),
303 highlight,
304 rows: Vec::new(),
305 reveal: false,
306 })));
307 Control::Clip {
308 content: Some(id),
309 size: [0, 0],
310 offset: [0, 0],
311 }
312 }
313 _ => unreachable!("ordinary element handled by Ui"),
314 }
315 }
316}