streamduck_core/modules/
components.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3use std::str::FromStr;
4use serde::{Deserialize, Serialize};
5use crate::thread::rendering::{Color, RendererComponent};
6
7/// Component definition
8#[derive(Serialize, Deserialize, Clone, Debug)]
9pub struct ComponentDefinition {
10    /// Display name for the component
11    pub display_name: String,
12
13    /// Description of the component
14    pub description: String,
15
16    /// Default looks for a button, in case user doesn't want to setup one on their own
17    pub default_looks: RendererComponent
18}
19
20/// UI Field, will be represented in a list similar to Unity's inspector
21#[derive(Serialize, Deserialize, Clone, Debug)]
22pub struct UIField {
23    /// Name of the field
24    pub name: String,
25
26    /// Display name of the field
27    pub display_name: String,
28
29    /// Description of the field
30    pub description: String,
31
32    /// Type of the field
33    pub ty: UIFieldType,
34
35    /// Default value that will be used when initializing this field
36    pub default_value: UIFieldValue<UIValue>
37}
38
39/// UI Value, represents what fields currently have
40#[derive(Serialize, Deserialize, Clone, Debug)]
41pub struct UIValue {
42    /// Name of the value
43    pub name: String,
44
45    /// Display name of the value
46    pub display_name: String,
47
48    /// Description of the value
49    pub description: String,
50
51    /// Type of the value
52    pub ty: UIFieldType,
53
54    /// Actual value
55    pub value: UIFieldValue<UIValue>,
56}
57
58/// UI Path Value, represents a value that has a path inside of the value hierarchy
59#[derive(Serialize, Deserialize, Clone, Debug)]
60pub struct UIPathValue {
61    /// Name of the value
62    pub name: String,
63
64    /// Path of the value
65    pub path: String,
66
67    /// Display name of the value
68    pub display_name: String,
69
70    /// Description of the value
71    pub description: String,
72
73    /// Type of the value
74    pub ty: UIFieldType,
75
76    /// Actual value
77    pub value: UIFieldValue<UIPathValue>,
78}
79
80/// UI Field Types, defines types that fields will have
81#[derive(Serialize, Deserialize, Clone, Debug)]
82pub enum UIFieldType {
83    /// Displays a header for separation reasons
84    Header,
85
86    /// Displays text
87    Label,
88
89    /// Text field that accepts float values
90    InputFieldFloat,
91    /// Text field that accepts integer values
92    InputFieldInteger,
93    /// Text field that accepts strings
94    InputFieldString,
95
96    /// Text field that accepts 2 float values
97    InputFieldFloat2,
98
99    /// Text field that accepts 2 integer values
100    InputFieldInteger2,
101
102    /// Text field that accepts only positive integer values
103    InputFieldUnsignedInteger,
104
105    // TODO: Add more types of inputs
106
107    /// Float slider of specified bounds
108    ValueSliderFloat(UIScalar<f32>),
109    /// Integer slider of specified bounds
110    ValueSliderInteger(UIScalar<i32>),
111
112    /// Collapsable submenu
113    Collapsable,
114
115    /// Array of menus, this definition acts as a template of how to construct the array, each field will be duplicated for each item in the array like structs
116    Array(Vec<UIField>),
117
118    /// Choice dropdown
119    Choice(Vec<String>),
120
121    /// Checkbox
122    Checkbox {
123        /// If checkbox should appear disabled in UI
124        ///
125        /// Note: Doesn't actually prevent setting the checkbox, so do internal checks if value should be changed
126        disabled: bool
127    },
128
129    /// Color picker
130    Color,
131
132    /// Image data encoded in base64
133    ImageData,
134
135    /// Image from image collection
136    ExistingImage,
137
138    /// Font name
139    Font,
140
141    /// Button for receiving impulses from clients
142    Button {
143        /// If button should appear disabled
144        disabled: bool
145    },
146
147    /// Previews an image on UI
148    ImagePreview,
149}
150
151/// UI Field value, current state of the settings
152#[derive(Serialize, Deserialize, Clone, Debug)]
153pub enum UIFieldValue<V> {
154    /// Displays a header for separation reasons
155    Header,
156
157    /// Displays text
158    Label(String),
159
160    /// Text field that accepts float values
161    InputFieldFloat(f32),
162    /// Text field that accepts integer values
163    InputFieldInteger(i32),
164    /// Text field that accepts strings
165    InputFieldString(String),
166
167    /// Text field that accepts 2 float values
168    InputFieldFloat2(f32, f32),
169
170    /// Text field that accepts 2 integer values
171    InputFieldInteger2(i32, i32),
172
173    /// Text field that accepts only positive integer values
174    InputFieldUnsignedInteger(u32),
175
176    /// Float slider of specified bounds
177    ValueSliderFloat(f32),
178    /// Integer slider of specified bounds
179    ValueSliderInteger(i32),
180
181    /// Collapsable submenu
182    Collapsable(Vec<V>),
183
184    /// Array of menus
185    Array(Vec<Vec<V>>),
186
187    /// Choice dropdown
188    Choice(String),
189
190    /// Checkbox
191    Checkbox(bool),
192
193    /// Color picker
194    Color(u8, u8, u8, u8),
195
196    /// Image data encoded in base64
197    ImageData(String),
198
199    /// Image from image collection
200    ExistingImage(String),
201
202    /// Font name
203    Font(String),
204
205    /// Button
206    Button,
207
208    /// Previews an image on UI, png image data encoded in base64
209    ImagePreview(String)
210}
211
212impl<V> UIFieldValue<V> {
213    /// Attempts to parse the value into a boolean
214    pub fn try_into_bool(&self) -> Result<bool, String> {
215        TryInto::<bool>::try_into(self)
216    }
217
218    /// Attempts to parse the value into a floating number
219    pub fn try_into_f32(&self) -> Result<f32, String> {
220        TryInto::<f32>::try_into(self)
221    }
222
223    /// Attempts to parse the value into an integer
224    pub fn try_into_i32(&self) -> Result<i32, String> {
225        TryInto::<i32>::try_into(self)
226    }
227
228    /// Attempts to parse the value into an unsigned integer
229    pub fn try_into_u32(&self) -> Result<u32, String> {
230        TryInto::<u32>::try_into(self)
231    }
232
233    /// Attempts to parse the value into a pair of floating numbers
234    pub fn try_into_f32_f32(&self) -> Result<(f32, f32), String> {
235        TryInto::<(f32, f32)>::try_into(self)
236    }
237
238    /// Attempts to parse the value into a pair of integers
239    pub fn try_into_i32_i32(&self) -> Result<(i32, i32), String> {
240        TryInto::<(i32, i32)>::try_into(self)
241    }
242
243    /// Attempts to parse the value into a color
244    pub fn try_into_color(&self) -> Result<Color, String> {
245        TryInto::<Color>::try_into(self)
246    }
247
248    /// Attempts to parse the value into a string
249    pub fn try_into_string(&self) -> Result<String, String> {
250        TryInto::<String>::try_into(self)
251    }
252}
253
254/// From conversions
255impl<V> Into<UIFieldValue<V>> for Color {
256    fn into(self) -> UIFieldValue<V> {
257        UIFieldValue::Color(self.0, self.1, self.2, self.3)
258    }
259}
260
261impl<V> Into<UIFieldValue<V>> for &Color {
262    fn into(self) -> UIFieldValue<V> {
263        UIFieldValue::Color(self.0, self.1, self.2, self.3)
264    }
265}
266
267/// To conversions
268impl<V> TryInto<bool> for UIFieldValue<V> {
269    type Error = String;
270
271    fn try_into(self) -> Result<bool, Self::Error> {
272        if let UIFieldValue::Checkbox(b) = self {
273            Ok(b)
274        } else {
275            Err("Incorrect value".to_string())
276        }
277    }
278}
279
280impl<V> TryInto<bool> for &UIFieldValue<V> {
281    type Error = String;
282
283    fn try_into(self) -> Result<bool, Self::Error> {
284        if let UIFieldValue::Checkbox(b) = self {
285            Ok(*b)
286        } else {
287            Err("Incorrect value".to_string())
288        }
289    }
290}
291
292impl<V> TryInto<f32> for UIFieldValue<V> {
293    type Error = String;
294
295    fn try_into(self) -> Result<f32, Self::Error> {
296        if let UIFieldValue::InputFieldFloat(f) | UIFieldValue::ValueSliderFloat(f) = self {
297            Ok(f)
298        } else {
299            Err("Incorrect value".to_string())
300        }
301    }
302}
303
304impl<V> TryInto<f32> for &UIFieldValue<V> {
305    type Error = String;
306
307    fn try_into(self) -> Result<f32, Self::Error> {
308        if let UIFieldValue::InputFieldFloat(f) | UIFieldValue::ValueSliderFloat(f) = self {
309            Ok(*f)
310        } else {
311            Err("Incorrect value".to_string())
312        }
313    }
314}
315
316impl<V> TryInto<i32> for UIFieldValue<V> {
317    type Error = String;
318
319    fn try_into(self) -> Result<i32, Self::Error> {
320        if let UIFieldValue::InputFieldInteger(i) | UIFieldValue::ValueSliderInteger(i) = self {
321            Ok(i)
322        } else {
323            Err("Incorrect value".to_string())
324        }
325    }
326}
327
328impl<V> TryInto<i32> for &UIFieldValue<V> {
329    type Error = String;
330
331    fn try_into(self) -> Result<i32, Self::Error> {
332        if let UIFieldValue::InputFieldInteger(i) | UIFieldValue::ValueSliderInteger(i) = self {
333            Ok(*i)
334        } else {
335            Err("Incorrect value".to_string())
336        }
337    }
338}
339
340impl<V> TryInto<u32> for UIFieldValue<V> {
341    type Error = String;
342
343    fn try_into(self) -> Result<u32, Self::Error> {
344        if let UIFieldValue::InputFieldUnsignedInteger(u) = self {
345            Ok(u)
346        } else {
347            Err("Incorrect value".to_string())
348        }
349    }
350}
351
352impl<V> TryInto<u32> for &UIFieldValue<V> {
353    type Error = String;
354
355    fn try_into(self) -> Result<u32, Self::Error> {
356        if let UIFieldValue::InputFieldUnsignedInteger(u) = self {
357            Ok(*u)
358        } else {
359            Err("Incorrect value".to_string())
360        }
361    }
362}
363
364impl<V> TryInto<(f32, f32)> for UIFieldValue<V> {
365    type Error = String;
366
367    fn try_into(self) -> Result<(f32, f32), Self::Error> {
368        if let UIFieldValue::InputFieldFloat2(f1, f2) = self {
369            Ok((f1, f2))
370        } else {
371            Err("Incorrect value".to_string())
372        }
373    }
374}
375
376impl<V> TryInto<(f32, f32)> for &UIFieldValue<V> {
377    type Error = String;
378
379    fn try_into(self) -> Result<(f32, f32), Self::Error> {
380        if let UIFieldValue::InputFieldFloat2(f1, f2) = self {
381            Ok((*f1, *f2))
382        } else {
383            Err("Incorrect value".to_string())
384        }
385    }
386}
387
388impl<V> TryInto<(i32, i32)> for UIFieldValue<V> {
389    type Error = String;
390
391    fn try_into(self) -> Result<(i32, i32), Self::Error> {
392        if let UIFieldValue::InputFieldInteger2(i1, i2) = self {
393            Ok((i1, i2))
394        } else {
395            Err("Incorrect value".to_string())
396        }
397    }
398}
399
400impl<V> TryInto<(i32, i32)> for &UIFieldValue<V> {
401    type Error = String;
402
403    fn try_into(self) -> Result<(i32, i32), Self::Error> {
404        if let UIFieldValue::InputFieldInteger2(i1, i2) = self {
405            Ok((*i1, *i2))
406        } else {
407            Err("Incorrect value".to_string())
408        }
409    }
410}
411
412impl<V> TryInto<Color> for UIFieldValue<V> {
413    type Error = String;
414
415    fn try_into(self) -> Result<Color, Self::Error> {
416        if let UIFieldValue::Color(c1, c2, c3, c4) = self {
417            Ok((c1, c2, c3, c4))
418        } else {
419            Err("Incorrect value".to_string())
420        }
421    }
422}
423
424impl<V> TryInto<Color> for &UIFieldValue<V> {
425    type Error = String;
426
427    fn try_into(self) -> Result<Color, Self::Error> {
428        if let UIFieldValue::Color(c1, c2, c3, c4) = self {
429            Ok((*c1, *c2, *c3, *c4))
430        } else {
431            Err("Incorrect value".to_string())
432        }
433    }
434}
435
436impl<V> TryInto<String> for UIFieldValue<V> {
437    type Error = String;
438
439    fn try_into(self) -> Result<String, Self::Error> {
440        if let UIFieldValue::InputFieldString(str) |
441                UIFieldValue::Choice(str) |
442                UIFieldValue::ImageData(str) |
443                UIFieldValue::ExistingImage(str) |
444                UIFieldValue::Font(str) |
445                UIFieldValue::Label(str) |
446                UIFieldValue::ImagePreview(str) = self {
447            Ok(str)
448        } else {
449            Err("Incorrect value".to_string())
450        }
451    }
452}
453
454impl<V> TryInto<String> for &UIFieldValue<V> {
455    type Error = String;
456
457    fn try_into(self) -> Result<String, Self::Error> {
458        if let UIFieldValue::InputFieldString(str) |
459                UIFieldValue::Choice(str) |
460                UIFieldValue::ImageData(str) |
461                UIFieldValue::ExistingImage(str) |
462                UIFieldValue::Font(str) |
463                UIFieldValue::Label(str) |
464                UIFieldValue::ImagePreview(str) = self {
465            Ok(str.clone())
466        } else {
467            Err("Incorrect value".to_string())
468        }
469    }
470}
471
472impl<V> TryInto<PathBuf> for UIFieldValue<V> {
473    type Error = String;
474
475    fn try_into(self) -> Result<PathBuf, Self::Error> {
476        if let UIFieldValue::InputFieldString(str) | UIFieldValue::Choice(str) = self {
477            if let Ok(path) = PathBuf::from_str(&str) {
478                Ok(path)
479            } else {
480                Err("Failed to parse path".to_string())
481            }
482        } else {
483            Err("Incorrect enum value".to_string())
484        }
485    }
486}
487
488impl<V> TryInto<PathBuf> for &UIFieldValue<V> {
489    type Error = String;
490
491    fn try_into(self) -> Result<PathBuf, Self::Error> {
492        if let UIFieldValue::InputFieldString(str) | UIFieldValue::Choice(str) = self {
493            if let Ok(path) = PathBuf::from_str(str) {
494                Ok(path)
495            } else {
496                Err("Failed to parse path".to_string())
497            }
498        } else {
499            Err("Incorrect value".to_string())
500        }
501    }
502}
503
504impl From<UIFieldValue<UIValue>> for UIFieldValue<UIPathValue> {
505    fn from(val: UIFieldValue<UIValue>) -> Self {
506        match val {
507            UIFieldValue::Header => UIFieldValue::Header,
508            UIFieldValue::Label(s) => UIFieldValue::Label(s),
509            UIFieldValue::InputFieldFloat(f) => UIFieldValue::InputFieldFloat(f),
510            UIFieldValue::InputFieldInteger(i) => UIFieldValue::InputFieldInteger(i),
511            UIFieldValue::InputFieldString(s) => UIFieldValue::InputFieldString(s),
512            UIFieldValue::InputFieldFloat2(f1, f2) => UIFieldValue::InputFieldFloat2(f1, f2),
513            UIFieldValue::InputFieldInteger2(i1, i2) => UIFieldValue::InputFieldInteger2(i1, i2),
514            UIFieldValue::InputFieldUnsignedInteger(u) => UIFieldValue::InputFieldUnsignedInteger(u),
515            UIFieldValue::ValueSliderFloat(f) => UIFieldValue::ValueSliderFloat(f),
516            UIFieldValue::ValueSliderInteger(i) => UIFieldValue::ValueSliderInteger(i),
517
518            UIFieldValue::Collapsable(_) => {
519                panic!("Please use convert_value_to_path")
520            }
521
522            UIFieldValue::Array(_) => {
523                panic!("Please use convert_value_to_path")
524            }
525
526            UIFieldValue::Choice(c) => UIFieldValue::Choice(c),
527            UIFieldValue::Checkbox(b) => UIFieldValue::Checkbox(b),
528            UIFieldValue::Color(c1, c2, c3, c4) => UIFieldValue::Color(c1, c2, c3, c4),
529            UIFieldValue::ImageData(d) => UIFieldValue::ImageData(d),
530            UIFieldValue::ExistingImage(i) => UIFieldValue::ExistingImage(i),
531            UIFieldValue::Font(f) => UIFieldValue::Font(f),
532            UIFieldValue::Button => UIFieldValue::Button,
533            UIFieldValue::ImagePreview(d) => UIFieldValue::ImagePreview(d)
534        }
535    }
536}
537
538impl From<UIFieldValue<UIPathValue>> for UIFieldValue<UIValue> {
539    fn from(val: UIFieldValue<UIPathValue>) -> Self {
540        match val {
541            UIFieldValue::Header => UIFieldValue::Header,
542            UIFieldValue::Label(s) => UIFieldValue::Label(s),
543            UIFieldValue::InputFieldFloat(f) => UIFieldValue::InputFieldFloat(f),
544            UIFieldValue::InputFieldInteger(i) => UIFieldValue::InputFieldInteger(i),
545            UIFieldValue::InputFieldString(s) => UIFieldValue::InputFieldString(s),
546            UIFieldValue::InputFieldFloat2(f1, f2) => UIFieldValue::InputFieldFloat2(f1, f2),
547            UIFieldValue::InputFieldInteger2(i1, i2) => UIFieldValue::InputFieldInteger2(i1, i2),
548            UIFieldValue::InputFieldUnsignedInteger(u) => UIFieldValue::InputFieldUnsignedInteger(u),
549            UIFieldValue::ValueSliderFloat(f) => UIFieldValue::ValueSliderFloat(f),
550            UIFieldValue::ValueSliderInteger(i) => UIFieldValue::ValueSliderInteger(i),
551
552            UIFieldValue::Collapsable(m) => {
553                UIFieldValue::Collapsable(m.into_iter()
554                    .map(|x| UIValue {
555                        name: x.name,
556                        display_name: x.display_name,
557                        description: x.description,
558                        ty: x.ty,
559                        value: x.value.into()
560                    })
561                    .collect())
562            }
563
564            UIFieldValue::Array(a) => {
565                UIFieldValue::Array(a.into_iter()
566                    .map(|x| x.into_iter()
567                        .map(|x| UIValue {
568                            name: x.name,
569                            display_name: x.display_name,
570                            description: x.description,
571                            ty: x.ty,
572                            value: x.value.into()
573                        })
574                        .collect())
575                    .collect())
576            }
577
578            UIFieldValue::Choice(c) => UIFieldValue::Choice(c),
579            UIFieldValue::Checkbox(b) => UIFieldValue::Checkbox(b),
580            UIFieldValue::Color(c1, c2, c3, c4) => UIFieldValue::Color(c1, c2, c3, c4),
581            UIFieldValue::ImageData(d) => UIFieldValue::ImageData(d),
582            UIFieldValue::ExistingImage(i) => UIFieldValue::ExistingImage(i),
583            UIFieldValue::Font(f) => UIFieldValue::Font(f),
584            UIFieldValue::Button => UIFieldValue::Button,
585            UIFieldValue::ImagePreview(d) => UIFieldValue::ImagePreview(d)
586        }
587    }
588}
589
590impl From<UIPathValue> for UIValue {
591    fn from(value: UIPathValue) -> Self {
592        UIValue {
593            name: value.name,
594            display_name: value.display_name,
595            description: value.description,
596            ty: value.ty,
597            value: value.value.into()
598        }
599    }
600}
601
602/// Information for running sliders in UI
603#[derive(Serialize, Deserialize, Clone, Debug)]
604pub struct UIScalar<T: PartialEq> {
605    /// Maximum value for the slider
606    pub max_value: T,
607    /// Minimum value for the slider
608    pub min_value: T,
609    /// How precise the slider will be in UI
610    pub step: T,
611    /// To allow manually inputting values outside of the range
612    pub allow_out_of_bounds: bool
613}
614
615/// Converts array of values to map
616pub fn map_ui_values(values: Vec<UIValue>) -> HashMap<String, UIValue> {
617    values.into_iter()
618        .map(|x| (x.name.clone(), x))
619        .collect()
620}
621
622/// Converts reference to array of values to map
623pub fn map_ui_values_ref(values: &Vec<UIValue>) -> HashMap<String, UIValue> {
624    values.into_iter()
625        .map(|x| (x.name.clone(), x.clone()))
626        .collect()
627}
628
629/// Converts reference to array of path values to map
630pub fn map_ui_path_values(values: &Vec<UIPathValue>) -> HashMap<String, UIPathValue> {
631    let mut map = HashMap::new();
632
633    fn add_values_to_map(values: &Vec<UIPathValue>, map: &mut HashMap<String, UIPathValue>) {
634        values.into_iter()
635            .for_each(|x| {
636                match &x.value {
637                    UIFieldValue::Collapsable(m) => {
638                        add_values_to_map(m, map);
639                    }
640
641                    UIFieldValue::Array(a) => {
642                        a.into_iter()
643                            .for_each(|x| add_values_to_map(x, map))
644                    }
645
646                    _ => {
647                        map.insert(x.path.to_string(), x.clone());
648                    }
649                }
650            })
651    }
652
653    add_values_to_map(values, &mut map);
654
655    return map;
656}