1use strum::VariantNames;
2use std::str::FromStr;
3use crate::core::button::{Button, parse_button_to_component};
4use crate::core::CoreHandle;
5use crate::modules::components::{map_ui_values, map_ui_values_ref, UIField, UIFieldType, UIFieldValue, UIValue};
6use crate::thread::rendering::{ButtonBackground, ButtonText, ButtonTextShadow, RendererComponent};
7use crate::thread::util::TextAlignment;
8use crate::images::SDImage;
9use crate::util::hash_str;
10
11pub async fn get_renderer_component_values(core: &CoreHandle, button: &Button) -> Vec<UIValue> {
13 if let Ok(component) = parse_button_to_component::<RendererComponent>(button) {
14 let mut fields = vec![];
15
16 fields.push(
17 UIValue {
18 name: "renderer".to_string(),
19 display_name: "Renderer to use".to_string(),
20 description: "Renderers can be added by plugins".to_string(),
21 ty: UIFieldType::Choice({
22 let mut names = vec!["default".to_string()];
23
24 names.extend(core.core.render_manager.read_renderers().await.values()
25 .map(|x| x.name()));
26
27 names
28 }),
29 value: UIFieldValue::Choice(if component.renderer.is_empty() { "default".to_string() } else { component.renderer.clone() })
30 }
31 );
32
33 if !component.renderer.is_empty() {
34 if let Some(renderer) = core.core.render_manager.read_renderers().await.get(&component.renderer).cloned() {
35 fields.extend(renderer.component_values(button, &component, core).await);
36 }
37 } else {
38 fields.push(
40 UIValue {
41 name: "background_params".to_string(),
42 display_name: "Background Parameters".to_string(),
43 description: "Parameters related to background of the button".to_string(),
44 ty: UIFieldType::Collapsable,
45 value: UIFieldValue::Collapsable({
46 let mut fields = vec![];
47
48 fields.push(
49 UIValue {
50 name: "background".to_string(),
51 display_name: "Background Type".to_string(),
52 description: "Type of the background to use".to_string(),
53 ty: UIFieldType::Choice(vec!["Solid Color".to_string(), "Horizontal Gradient".to_string(), "Vertical Gradient".to_string(), "Existing Image".to_string(), "New Image".to_string()]),
54 value: UIFieldValue::Choice(
55 match &component.background {
56 ButtonBackground::Solid(_) => "Solid Color",
57 ButtonBackground::HorizontalGradient(_, _) => "Horizontal Gradient",
58 ButtonBackground::VerticalGradient(_, _) => "Vertical Gradient",
59 ButtonBackground::ExistingImage(_) => "Existing Image",
60 ButtonBackground::NewImage(_) => "New Image",
61 }.to_string()
62 )
63 }
64 );
65
66 match &component.background {
68 ButtonBackground::Solid(color) => {
69 fields.push(
70 UIValue {
71 name: "color".to_string(),
72 display_name: "Background Color".to_string(),
73 description: "Color that will be the background of the button".to_string(),
74 ty: UIFieldType::Color,
75 value: color.into()
76 }
77 );
78 }
79
80 ButtonBackground::HorizontalGradient(start_color, end_color) => {
81 fields.push(
82 UIValue {
83 name: "start_color".to_string(),
84 display_name: "Gradient Start Color".to_string(),
85 description: "Color that will be on left side of the gradient".to_string(),
86 ty: UIFieldType::Color,
87 value: start_color.into()
88 }
89 );
90
91 fields.push(
92 UIValue {
93 name: "end_color".to_string(),
94 display_name: "Gradient End Color".to_string(),
95 description: "Color that will be on right side of the gradient".to_string(),
96 ty: UIFieldType::Color,
97 value: end_color.into()
98 }
99 );
100 }
101 ButtonBackground::VerticalGradient(start_color, end_color) => {
102 fields.push(
103 UIValue {
104 name: "start_color".to_string(),
105 display_name: "Gradient Start Color".to_string(),
106 description: "Color that will be on top side of the gradient".to_string(),
107 ty: UIFieldType::Color,
108 value: start_color.into()
109 }
110 );
111
112 fields.push(
113 UIValue {
114 name: "end_color".to_string(),
115 display_name: "Gradient End Color".to_string(),
116 description: "Color that will be on bottom side of the gradient".to_string(),
117 ty: UIFieldType::Color,
118 value: end_color.into()
119 }
120 );
121 }
122 ButtonBackground::ExistingImage(identifier) => {
123 fields.push(
124 UIValue {
125 name: "image".to_string(),
126 display_name: "Image".to_string(),
127 description: "Image to use as background of the button".to_string(),
128 ty: UIFieldType::ExistingImage,
129 value: UIFieldValue::ExistingImage(identifier.to_string())
130 }
131 );
132 }
133 ButtonBackground::NewImage(blob) => {
134 fields.push(
135 UIValue {
136 name: "image".to_string(),
137 display_name: "Image".to_string(),
138 description: "Image to use as background of the button".to_string(),
139 ty: UIFieldType::ImageData,
140 value: UIFieldValue::ImageData(blob.to_string())
141 }
142 );
143 }
144 }
145
146 fields
147 })
148 }
149 );
150
151 fields.push(
153 UIValue {
154 name: "text_params".to_string(),
155 display_name: "Text Parameters".to_string(),
156 description: "Parameters related to text on the button".to_string(),
157 ty: UIFieldType::Collapsable,
158 value: UIFieldValue::Collapsable({
159 let mut fields = vec![];
160
161 fields.push(
162 UIValue {
163 name: "text".to_string(),
164 display_name: "Text Objects".to_string(),
165 description: "Array of text objects".to_string(),
166 ty: UIFieldType::Array(
167 vec![
168 UIField {
169 name: "text".to_string(),
170 display_name: "Text".to_string(),
171 description: "Text that will be displayed".to_string(),
172 ty: UIFieldType::InputFieldString,
173 default_value: UIFieldValue::InputFieldString("".to_string())
174 },
175 UIField {
176 name: "font".to_string(),
177 display_name: "Font".to_string(),
178 description: "Font that will be used for text rendering".to_string(),
179 ty: UIFieldType::Font,
180 default_value: UIFieldValue::Font("default".to_string())
181 },
182 UIField {
183 name: "scale".to_string(),
184 display_name: "Text Scale".to_string(),
185 description: "Scale of the text".to_string(),
186 ty: UIFieldType::InputFieldFloat2,
187 default_value: UIFieldValue::InputFieldFloat2(15.0, 15.0)
188 },
189 UIField {
190 name: "alignment".to_string(),
191 display_name: "Alignment".to_string(),
192 description: "To which point of the button the text will be anchored to".to_string(),
193 ty: UIFieldType::Choice(
194 TextAlignment::VARIANTS.iter().map(|x| x.to_string()).collect()
195 ),
196 default_value: UIFieldValue::Choice("Center".to_string())
197 },
198 UIField {
199 name: "padding".to_string(),
200 display_name: "Padding".to_string(),
201 description: "Gap to have from alignment/anchor point".to_string(),
202 ty: UIFieldType::InputFieldUnsignedInteger,
203 default_value: UIFieldValue::InputFieldUnsignedInteger(0)
204 },
205 UIField {
206 name: "offset".to_string(),
207 display_name: "Text Offset".to_string(),
208 description: "2D offset of the text from its alignment/anchor point".to_string(),
209 ty: UIFieldType::InputFieldFloat2,
210 default_value: UIFieldValue::InputFieldFloat2(0.0, 0.0)
211 },
212 UIField {
213 name: "color".to_string(),
214 display_name: "Text Color".to_string(),
215 description: "Color that text will be displayed in".to_string(),
216 ty: UIFieldType::Color,
217 default_value: UIFieldValue::Color(0, 0, 0, 255)
218 },
219 UIField {
220 name: "shadow_enabled".to_string(),
221 display_name: "Text Shadow".to_string(),
222 description: "If text shadow should be rendered or not".to_string(),
223 ty: UIFieldType::Checkbox {
224 disabled: false
225 },
226 default_value: UIFieldValue::Checkbox(false)
227 }
228 ]
229 ),
230 value: UIFieldValue::Array({
231 let mut text_objects = vec![];
232
233 for text in &component.text {
234 let mut values = vec![];
235
236 values.push(UIValue {
237 name: "text".to_string(),
238 display_name: "Text".to_string(),
239 description: "Text that will be displayed".to_string(),
240 ty: UIFieldType::InputFieldString,
241 value: UIFieldValue::InputFieldString(text.text.clone())
242 });
243
244 values.push(UIValue {
245 name: "font".to_string(),
246 display_name: "Font".to_string(),
247 description: "Font that will be used for text rendering".to_string(),
248 ty: UIFieldType::Font,
249 value: UIFieldValue::Font(text.font.clone())
250 });
251
252 values.push(UIValue {
253 name: "scale".to_string(),
254 display_name: "Text Scale".to_string(),
255 description: "Scale of the text".to_string(),
256 ty: UIFieldType::InputFieldFloat2,
257 value: UIFieldValue::InputFieldFloat2(text.scale.0, text.scale.1)
258 });
259
260 values.push(UIValue {
261 name: "alignment".to_string(),
262 display_name: "Alignment".to_string(),
263 description: "To which point of the button the text will be anchored to".to_string(),
264 ty: UIFieldType::Choice(
265 TextAlignment::VARIANTS.iter().map(|x| x.to_string()).collect()
266 ),
267 value: UIFieldValue::Choice(text.alignment.to_string())
268 });
269
270 values.push(UIValue {
271 name: "padding".to_string(),
272 display_name: "Padding".to_string(),
273 description: "Gap to have from alignment/anchor point".to_string(),
274 ty: UIFieldType::InputFieldUnsignedInteger,
275 value: UIFieldValue::InputFieldUnsignedInteger(text.padding)
276 });
277
278 values.push(UIValue {
279 name: "offset".to_string(),
280 display_name: "Text Offset".to_string(),
281 description: "2D offset of the text from its alignment/anchor point".to_string(),
282 ty: UIFieldType::InputFieldFloat2,
283 value: UIFieldValue::InputFieldFloat2(text.offset.0, text.offset.1)
284 });
285
286 values.push(UIValue {
287 name: "color".to_string(),
288 display_name: "Text Color".to_string(),
289 description: "Color that text will be displayed in".to_string(),
290 ty: UIFieldType::Color,
291 value: text.color.into()
292 });
293
294 if let Some(shadow) = &text.shadow {
295 values.push(
296 UIValue {
297 name: "shadow_enabled".to_string(),
298 display_name: "Text Shadow".to_string(),
299 description: "If text shadow should be rendered or not".to_string(),
300 ty: UIFieldType::Checkbox {
301 disabled: false
302 },
303 value: UIFieldValue::Checkbox(true)
304 }
305 );
306
307 values.push(UIValue {
308 name: "shadow_color".to_string(),
309 display_name: "Text Shadow Color".to_string(),
310 description: "Color of the shadow".to_string(),
311 ty: UIFieldType::Color,
312 value: shadow.color.into()
313 });
314
315 values.push(UIValue {
316 name: "shadow_offset".to_string(),
317 display_name: "Text Shadow Offset".to_string(),
318 description: "Offset of the shadow from text".to_string(),
319 ty: UIFieldType::InputFieldInteger2,
320 value: UIFieldValue::InputFieldInteger2(shadow.offset.0, shadow.offset.1)
321 });
322 } else {
323 values.push(
324 UIValue {
325 name: "shadow_enabled".to_string(),
326 display_name: "Text Shadow".to_string(),
327 description: "If text shadow should be rendered or not".to_string(),
328 ty: UIFieldType::Checkbox {
329 disabled: false
330 },
331 value: UIFieldValue::Checkbox(false)
332 }
333 );
334 }
335
336 text_objects.push(values);
337 }
338
339 text_objects
340 })
341 }
342 );
343
344 fields
345 })
346 }
347 );
348
349 fields.push(
351 UIValue {
352 name: "plugin_blacklist".to_string(),
353 display_name: "Allowed plugins to render".to_string(),
354 description: "Disabled plugins will not appear on button".to_string(),
355 ty: UIFieldType::Collapsable,
356 value: UIFieldValue::Collapsable({
357 let names = core.module_manager().get_modules_for_rendering(&button.component_names()).await;
358
359 names.into_values()
360 .map(|x| {
361 let name = x.name();
362
363 UIValue {
364 name: name.clone(),
365 display_name: name.clone(),
366 description: "".to_string(),
367 ty: UIFieldType::Checkbox { disabled: false },
368 value: UIFieldValue::Checkbox(!component.plugin_blacklist.contains(&name))
369 }
370 }).collect()
371 })
372 }
373 );
374
375 fields.push(
376 UIValue {
377 name: "to_cache".to_string(),
378 display_name: "Caching".to_string(),
379 description: "If renderer should cache render result or not. Caching might use a lot of RAM, no caching will use a lot more CPU".to_string(),
380 ty: UIFieldType::Checkbox {
381 disabled: false
382 },
383 value: UIFieldValue::Checkbox(component.to_cache)
384 }
385 );
386 }
387
388 fields
389 } else {
390 vec![]
391 }
392}
393
394
395pub async fn set_renderer_component_values(core: &CoreHandle, button: &mut Button, value: Vec<UIValue>) {
397 if let Ok(mut component) = parse_button_to_component::<RendererComponent>(button) {
398 let change_map = map_ui_values(value);
399
400 if let Some(value) = change_map.get("renderer") {
401 if let Ok(value) = value.value.try_into_string() {
402 if value == "default" {
403 component.renderer = "".to_string();
404 } else {
405 if let Some(_) = core.core.render_manager.read_renderers().await.get(&value) {
406 component.renderer = value;
407 }
408 }
409 }
410 }
411
412 if !component.renderer.is_empty() {
413 if let Some(renderer) = core.core.render_manager.read_renderers().await.get(&component.renderer).cloned() {
414 renderer.set_component_value(button, &mut component, core, change_map.values().cloned().collect()).await;
415 }
416 } else {
417 if let Some(value) = change_map.get("background_params") {
418 if let UIFieldValue::Collapsable(value) = &value.value {
419 let change_map = map_ui_values(value.clone());
420
421 if let Some(value) = change_map.get("background") {
423 if let Ok(choice) = value.value.try_into_string() {
424 match choice.as_str() {
425 "Solid Color" => component.background = ButtonBackground::Solid((0, 0, 0, 255)),
426 "Horizontal Gradient" => component.background = ButtonBackground::HorizontalGradient((0, 0, 0, 255), (0, 0, 0, 255)),
427 "Vertical Gradient" => component.background = ButtonBackground::VerticalGradient((0, 0, 0, 255), (0, 0, 0, 255)),
428 "Existing Image" => component.background = ButtonBackground::ExistingImage("".to_string()),
429 "New Image" => component.background = ButtonBackground::NewImage("".to_string()),
430
431 _ => {}
432 }
433 }
434 }
435
436 if let Some(value) = change_map.get("color") {
438 if let ButtonBackground::Solid(_) = component.background {
439 if let Ok(color) = (&value.value).try_into() {
440 component.background = ButtonBackground::Solid(color);
441 }
442 }
443 }
444
445 if let Some(value) = change_map.get("start_color") {
446 if let ButtonBackground::HorizontalGradient(_, end) = component.background {
447 if let Ok(color) = (&value.value).try_into() {
448 component.background = ButtonBackground::HorizontalGradient(color, end);
449 }
450 }
451
452 if let ButtonBackground::VerticalGradient(_, end) = component.background {
453 if let Ok(color) = (&value.value).try_into() {
454 component.background = ButtonBackground::VerticalGradient(color, end);
455 }
456 }
457 }
458
459 if let Some(value) = change_map.get("end_color") {
460 if let ButtonBackground::HorizontalGradient(start, _) = component.background {
461 if let Ok(color) = (&value.value).try_into() {
462 component.background = ButtonBackground::HorizontalGradient(start, color);
463 }
464 }
465
466 if let ButtonBackground::VerticalGradient(start, _) = component.background {
467 if let Ok(color) = (&value.value).try_into() {
468 component.background = ButtonBackground::VerticalGradient(start, color);
469 }
470 }
471 }
472
473 if let Some(value) = change_map.get("image") {
474 match &component.background {
475 ButtonBackground::ExistingImage(_) => {
476 if let Ok(identifier) = (&value.value).try_into() {
477 component.background = ButtonBackground::ExistingImage(identifier);
478 }
479 }
480
481 ButtonBackground::NewImage(_) => {
482 if let Ok(blob) = (&value.value).try_into_string() {
483 let identifier = hash_str(&blob);
484
485 if let Ok(image) = SDImage::from_base64(&blob, core.core.image_size).await {
486 component.background = ButtonBackground::ExistingImage(identifier.clone());
487
488 let mut handle = core.core.image_collection.write().await;
489 handle.insert(identifier, image);
490 } else {
491 component.background = ButtonBackground::NewImage("".to_string());
492 }
493 }
494 }
495
496 _ => {}
497 }
498 }
499 }
500 }
501
502 if let Some(value) = change_map.get("text_params") {
503 if let UIFieldValue::Collapsable(value) = &value.value {
504 let change_map = map_ui_values(value.clone());
505
506 if let Some(value) = change_map.get("text") {
507 if let UIFieldValue::Array(items) = &value.value {
508 component.text = vec![];
509
510 fn get_text_object(item: &Vec<UIValue>) -> Option<ButtonText> {
511 let map = map_ui_values_ref(item);
512
513 Some(ButtonText {
514 text: (&map.get("text")?.value).try_into().ok()?,
515 font: (&map.get("font")?.value).try_into().ok()?,
516 scale: (&map.get("scale")?.value).try_into().ok()?,
517 alignment: TextAlignment::from_str(&map.get("alignment")?.value.try_into_string().ok()?).ok()?,
518 padding: (&map.get("padding")?.value).try_into().ok()?,
519 offset: (&map.get("offset")?.value).try_into_f32_f32().ok()?,
520 color: (&map.get("color")?.value).try_into().ok()?,
521 shadow: if let Some(bool) = map.get("shadow_enabled")?.value.try_into_bool().ok() {
522 let get_shadow = || {
523 Some(ButtonTextShadow {
524 offset: (&map.get("shadow_offset")?.value).try_into().ok()?,
525 color: (&map.get("shadow_color")?.value).try_into().ok()?
526 })
527 };
528
529 if bool {
530 get_shadow().or(Some(ButtonTextShadow {
531 offset: (0, 0),
532 color: (0, 0, 0, 0)
533 }))
534 } else {
535 None
536 }
537 } else {
538 None
539 }
540 })
541 }
542
543 for item in items {
544 if let Some(object) = get_text_object(item) {
545 component.text.push(object)
546 }
547 }
548 }
549 }
550 }
551 }
552
553 if let Some(value) = change_map.get("plugin_blacklist") {
554 if let UIFieldValue::Collapsable(value) = &value.value {
555 let change_map = map_ui_values(value.clone());
556
557 for (name, value) in change_map {
558 if let UIFieldValue::Checkbox(state) = value.value {
559 if state {
560 component.plugin_blacklist.retain(|x| *x != name);
561 } else {
562 component.plugin_blacklist.push(name);
563 }
564 }
565 }
566 }
567 }
568
569 if let Some(value) = change_map.get("to_cache") {
570 if let Ok(value) = value.value.try_into_bool() {
571 component.to_cache = value;
572 }
573 }
574 }
575
576 button.insert_component(component).ok();
578
579 core.core.mark_for_redraw().await;
580 }
581}