vn_settings/
lib.rs

1mod color;
2mod image;
3mod layout;
4mod object;
5mod timing;
6
7pub use color::ColorSettings;
8pub use image::ImageSettings;
9use indexmap::IndexMap;
10pub use layout::{BoxLayoutSettings, LayoutSettings, ViewLayoutSettings};
11pub use object::ObjectSettings;
12pub use timing::TimingSettings;
13
14use color::{BoxColorParameter, ColorParameter, SelectableBoxColorParameter};
15use image::ImageParameter;
16use layout::{BoxLayoutParameter, LayoutParameter, ViewLayoutParameter};
17use object::{ObjectImageParameter, ObjectLayerLayoutParameter, ObjectLayoutParameter};
18use timing::TimingParameter;
19
20use dialogi::{DialogChange, DialogParameter};
21use simple_color::Color;
22
23use std::collections::HashMap;
24
25trait Settings<Parameter> {
26    type Value;
27
28    fn get_mut(&mut self, parameter: &Parameter) -> &mut Self::Value;
29}
30
31trait Resettable {
32    fn reset(&mut self) {}
33}
34
35#[derive(Default)]
36pub struct Override<T> {
37    default: T,
38    characters_default: HashMap<Box<str>, T>,
39    characters_scene: HashMap<Box<str>, T>,
40    scene: Option<T>,
41}
42
43impl<T> Override<T> {
44    fn set_default(&mut self, name: Option<Box<str>>, value: T) {
45        if let Some(name) = name {
46            self.characters_default.insert(name, value);
47        } else {
48            self.default = value;
49        }
50    }
51
52    fn set_scene(&mut self, name: Option<&str>, value: Option<T>) {
53        let Some(name) = name else {
54            self.scene = value;
55            return;
56        };
57
58        if let Some(value) = value {
59            self.characters_scene.insert(name.into(), value);
60        } else {
61            self.characters_scene.remove(name);
62        }
63    }
64}
65
66impl<T> Resettable for Override<T> {
67    fn reset(&mut self) {
68        self.scene = None;
69    }
70}
71
72impl<T> From<T> for Override<T> {
73    fn from(default: T) -> Self {
74        Self {
75            default,
76            characters_default: HashMap::new(),
77            characters_scene: HashMap::new(),
78            scene: None,
79        }
80    }
81}
82
83impl<T> Override<T> {
84    pub fn get(&self, name: &str) -> T
85    where
86        T: Copy,
87    {
88        *self.get_ref(name)
89    }
90
91    pub fn get_ref(&self, name: &str) -> &T {
92        if let Some(scene) = &self.scene {
93            scene
94        } else if name.is_empty() {
95            &self.default
96        } else if let Some(character_scene) = self.characters_scene.get(name) {
97            character_scene
98        } else if let Some(character_default) = self.characters_default.get(name) {
99            character_default
100        } else {
101            &self.default
102        }
103    }
104}
105
106#[derive(Default)]
107pub struct Names {
108    pub default: HashMap<Box<str>, Box<str>>,
109    pub scene: HashMap<Box<str>, Box<str>>,
110}
111
112impl Names {
113    pub fn new() -> Self {
114        Self::default()
115    }
116
117    pub fn reset(&mut self) {
118        self.scene.clear();
119    }
120
121    pub fn get<'a>(&'a self, name: &'a str) -> &'a str {
122        self.scene
123            .get(name)
124            .or_else(|| self.default.get(name))
125            .map_or(name, |v| v)
126    }
127}
128
129pub struct PlayerSettings {
130    pub colors: ColorSettings<Override<Color>>,
131    pub timing: TimingSettings<Override<f32>>,
132    pub images: ImageSettings<Override<Option<Box<str>>>>,
133    pub objects: ObjectSettings<Override<Option<usize>>, Override<f32>>,
134    pub layout: LayoutSettings<Override<f32>>,
135    pub names: Names,
136}
137
138impl PlayerSettings {
139    pub fn common() -> Self {
140        Self {
141            colors: ColorSettings::common(),
142            timing: TimingSettings::common(),
143            images: ImageSettings::common(),
144            objects: ObjectSettings::common(),
145            layout: LayoutSettings::common(),
146            names: Names::new(),
147        }
148    }
149
150    pub fn apply_setting(&mut self, context: &mut SettingsContext, key: &str, value: &str) {
151        if let Some(parameter) = Parameter::create(key, context) {
152            let setter = parameter.value_setter(value, context);
153            self.set_character_default(setter);
154        }
155    }
156
157    pub fn extract_settings(
158        &mut self,
159        context: &mut SettingsContext,
160        config_map: &mut IndexMap<Box<str>, Box<str>>,
161    ) {
162        for (key, value) in config_map.drain(..) {
163            self.apply_setting(context, &key, &value);
164        }
165    }
166
167    pub fn reset(&mut self) {
168        self.colors.reset();
169        self.timing.reset();
170        self.objects.reset();
171        self.layout.reset();
172        self.names.reset();
173    }
174}
175
176#[derive(Clone, Debug, PartialEq, Eq, Hash)]
177pub struct NamedParameter<T> {
178    name: Option<Box<str>>,
179    parameter: T,
180}
181
182trait ParameterNamer: Sized {
183    fn named(self, name: Option<Box<str>>) -> NamedParameter<Self> {
184        NamedParameter {
185            name,
186            parameter: self,
187        }
188    }
189}
190
191impl<T> ParameterNamer for T {}
192
193type NamedColorParameter = NamedParameter<ColorParameter>;
194type NamedTimingParameter = NamedParameter<TimingParameter>;
195type NamedImageParameter = NamedParameter<ImageParameter>;
196type NamedObjectImageParameter = NamedParameter<ObjectImageParameter>;
197type NamedObjectLayoutParameter = NamedParameter<ObjectLayoutParameter>;
198type NamedLayoutParameter = NamedParameter<LayoutParameter>;
199
200#[derive(Clone, Debug, PartialEq, Eq, Hash)]
201pub enum Parameter {
202    Color(NamedColorParameter),
203    Timing(NamedTimingParameter),
204    Image(NamedImageParameter),
205    ObjectImage(NamedObjectImageParameter),
206    ObjectLayout(NamedObjectLayoutParameter),
207    Layout(NamedLayoutParameter),
208    Name(Box<str>),
209}
210
211pub struct ImageObject {
212    index: usize,
213    order: HashMap<Box<str>, usize>,
214}
215
216impl ImageObject {
217    fn new(index: usize) -> Self {
218        Self {
219            index,
220            order: HashMap::new(),
221        }
222    }
223}
224
225pub struct LayerInfo {
226    pub name: Box<str>,
227    pub path: Box<str>,
228}
229
230pub fn extract_layers(layers: &mut Vec<LayerInfo>, config_map: &mut IndexMap<Box<str>, Box<str>>) {
231    let keys: Vec<_> = config_map.keys().cloned().collect();
232    for key in keys {
233        let Some(("Layer", name)) = key.split_once(':') else {
234            continue;
235        };
236
237        let path = config_map.shift_remove(&key).expect("Invalid layer");
238        layers.push(LayerInfo {
239            name: name.into(),
240            path,
241        });
242    }
243
244    if layers.is_empty() {
245        layers.push(LayerInfo {
246            name: "Background".into(),
247            path: Box::default(),
248        });
249        layers.push(LayerInfo {
250            name: "Character".into(),
251            path: Box::default(),
252        });
253    }
254}
255
256#[derive(Default)]
257pub struct SettingsContext {
258    pub object_cache: HashMap<Box<str>, ImageObject>,
259    pub layers: Vec<LayerInfo>,
260}
261
262impl SettingsContext {
263    pub fn new() -> Self {
264        Self::default()
265    }
266}
267
268impl Parameter {
269    fn create_character(name: &str, context: &mut SettingsContext) -> Option<Self> {
270        let (main, sub) = name.split_once(':')?;
271
272        if main != "Character" {
273            return None;
274        }
275
276        let (character_name, option) = sub.split_once(':')?;
277
278        let character_name = character_name.into();
279        if option == "name" {
280            Some(Self::Name(character_name))
281        } else {
282            create_parameter(option, Some(character_name), context)
283        }
284    }
285
286    pub fn value_setter(self, value: &str, context: &SettingsContext) -> Setter {
287        fn color(repr: &str) -> Color {
288            match repr {
289                "black" => Color::BLACK,
290                "white" => Color::WHITE,
291
292                "red" => Color::RED,
293                "yellow" => Color::YELLOW,
294                "green" => Color::GREEN,
295                "cyan" => Color::CYAN,
296                "blue" => Color::BLUE,
297                "magenta" => Color::MAGENTA,
298
299                _ => repr
300                    .parse::<Color>()
301                    .unwrap_or_else(|_| panic!("Color '{repr}' does not exist")),
302            }
303        }
304
305        match self {
306            Self::Color(parameter) => Setter::Color(parameter, color(value)),
307            Self::Timing(parameter) => Setter::Timing(
308                parameter,
309                value.parse().expect("Parsing timing parameter failed"),
310            ),
311            Self::Image(parameter) => Setter::Image(parameter, Some(value.into())),
312            Self::ObjectImage(parameter) => {
313                let index = if value.is_empty() {
314                    None
315                } else {
316                    let path = &context.layers[parameter.parameter].path;
317                    let path_holder;
318                    let full_path = if path.is_empty() {
319                        value
320                    } else {
321                        path_holder = format!("{path}:{value}");
322                        &path_holder
323                    };
324                    if let Some(ImageObject { index, .. }) = context.object_cache.get(full_path) {
325                        Some(*index)
326                    } else {
327                        eprintln!("Undefined image object {value:?}");
328                        None
329                    }
330                };
331                Setter::ObjectImage(parameter, index)
332            }
333            Self::ObjectLayout(parameter) => Setter::ObjectLayout(
334                parameter,
335                value
336                    .parse()
337                    .expect("Parsing object layout parameter failed"),
338            ),
339            Self::Layout(parameter) => Setter::Layout(
340                parameter,
341                value.parse().expect("Parsing layout parameter failed"),
342            ),
343            Self::Name(parameter) => Setter::Name(parameter, value.into()),
344        }
345    }
346}
347
348fn create_parameter(
349    parameter_name: &str,
350    character_name: Option<Box<str>>,
351    context: &mut SettingsContext,
352) -> Option<Parameter> {
353    let (path, parameter_name) = parameter_name.split_once(':')?;
354    Some(match path {
355        "Color" => Parameter::Color(
356            match parameter_name {
357                "background" => ColorParameter::Background,
358                "foreground" => ColorParameter::Foreground,
359                "dialog-text-fill" => ColorParameter::Dialog(BoxColorParameter::TextFill),
360                "dialog-text-line" => ColorParameter::Dialog(BoxColorParameter::TextLine),
361                "dialog-name-fill" => ColorParameter::Dialog(BoxColorParameter::NameFill),
362                "dialog-name-line" => ColorParameter::Dialog(BoxColorParameter::NameLine),
363
364                "choice-default-text-fill" => ColorParameter::Choice(SelectableBoxColorParameter {
365                    selected: false,
366                    parameter: BoxColorParameter::TextFill,
367                }),
368                "choice-default-text-line" => ColorParameter::Choice(SelectableBoxColorParameter {
369                    selected: false,
370                    parameter: BoxColorParameter::TextLine,
371                }),
372                "choice-default-name-fill" => ColorParameter::Choice(SelectableBoxColorParameter {
373                    selected: false,
374                    parameter: BoxColorParameter::NameFill,
375                }),
376                "choice-default-name-line" => ColorParameter::Choice(SelectableBoxColorParameter {
377                    selected: false,
378                    parameter: BoxColorParameter::NameLine,
379                }),
380                "choice-select-text-fill" => ColorParameter::Choice(SelectableBoxColorParameter {
381                    selected: true,
382                    parameter: BoxColorParameter::TextFill,
383                }),
384                "choice-select-text-line" => ColorParameter::Choice(SelectableBoxColorParameter {
385                    selected: true,
386                    parameter: BoxColorParameter::TextLine,
387                }),
388                "choice-select-name-fill" => ColorParameter::Choice(SelectableBoxColorParameter {
389                    selected: true,
390                    parameter: BoxColorParameter::NameFill,
391                }),
392                "choice-select-name-line" => ColorParameter::Choice(SelectableBoxColorParameter {
393                    selected: true,
394                    parameter: BoxColorParameter::NameLine,
395                }),
396
397                "revert-default-text-fill" => ColorParameter::Revert(SelectableBoxColorParameter {
398                    selected: false,
399                    parameter: BoxColorParameter::TextFill,
400                }),
401                "revert-default-text-line" => ColorParameter::Revert(SelectableBoxColorParameter {
402                    selected: false,
403                    parameter: BoxColorParameter::TextLine,
404                }),
405                "revert-default-name-fill" => ColorParameter::Revert(SelectableBoxColorParameter {
406                    selected: false,
407                    parameter: BoxColorParameter::NameFill,
408                }),
409                "revert-default-name-line" => ColorParameter::Revert(SelectableBoxColorParameter {
410                    selected: false,
411                    parameter: BoxColorParameter::NameLine,
412                }),
413                "revert-select-text-fill" => ColorParameter::Revert(SelectableBoxColorParameter {
414                    selected: true,
415                    parameter: BoxColorParameter::TextFill,
416                }),
417                "revert-select-text-line" => ColorParameter::Revert(SelectableBoxColorParameter {
418                    selected: true,
419                    parameter: BoxColorParameter::TextLine,
420                }),
421                "revert-select-name-fill" => ColorParameter::Revert(SelectableBoxColorParameter {
422                    selected: true,
423                    parameter: BoxColorParameter::NameFill,
424                }),
425                "revert-select-name-line" => ColorParameter::Revert(SelectableBoxColorParameter {
426                    selected: true,
427                    parameter: BoxColorParameter::NameLine,
428                }),
429
430                _ => return None,
431            }
432            .named(character_name),
433        ),
434
435        "Timing" => Parameter::Timing(
436            match parameter_name {
437                "auto-next" => TimingParameter::AutoNext,
438                "letter" => TimingParameter::Letter,
439                "line" => TimingParameter::Line,
440                "select" => TimingParameter::Select,
441                "view" => TimingParameter::View,
442
443                _ => return None,
444            }
445            .named(character_name),
446        ),
447
448        "Image" => {
449            let (layer_name, parameter_name) = parameter_name.rsplit_once(':')?;
450
451            if !context.object_cache.contains_key(layer_name) {
452                context.object_cache.insert(
453                    layer_name.into(),
454                    ImageObject::new(context.object_cache.len()),
455                );
456            }
457            let ImageObject { index, order } = context
458                .object_cache
459                .get_mut(layer_name)
460                .expect("Invalid layer");
461
462            if !order.contains_key(parameter_name) {
463                order.insert(parameter_name.into(), order.len());
464            }
465            Parameter::Image((*index, order[parameter_name]).named(character_name))
466        }
467
468        "Object" => {
469            let (layer_name, parameter_name) = parameter_name.split_once(':')?;
470            let layer = context
471                .layers
472                .iter()
473                .position(|layer| &*layer.name == layer_name)?;
474
475            match parameter_name {
476                "image" => Parameter::ObjectImage(layer.named(character_name)),
477                "hor" => Parameter::ObjectLayout(
478                    ObjectLayoutParameter {
479                        layer,
480                        layout: ObjectLayerLayoutParameter::Hor,
481                    }
482                    .named(character_name),
483                ),
484                "ver" => Parameter::ObjectLayout(
485                    ObjectLayoutParameter {
486                        layer,
487                        layout: ObjectLayerLayoutParameter::Ver,
488                    }
489                    .named(character_name),
490                ),
491                "scale" => Parameter::ObjectLayout(
492                    ObjectLayoutParameter {
493                        layer,
494                        layout: ObjectLayerLayoutParameter::Scale,
495                    }
496                    .named(character_name),
497                ),
498
499                _ => return None,
500            }
501        }
502
503        "Layout" => Parameter::Layout(
504            {
505                let (subpath, parameter_name) = parameter_name.split_once(':')?;
506                let box_layout_parameter = || {
507                    Some(match parameter_name {
508                        "hor" => BoxLayoutParameter::Hor,
509                        "ver" => BoxLayoutParameter::Ver,
510                        "width" => BoxLayoutParameter::Width,
511                        "height" => BoxLayoutParameter::Height,
512                        "corner" => BoxLayoutParameter::Corner,
513                        "line" => BoxLayoutParameter::Line,
514                        "text-size" => BoxLayoutParameter::TextSize,
515                        "name-size" => BoxLayoutParameter::NameSize,
516
517                        _ => return None,
518                    })
519                };
520
521                let view_layout_parameter = || {
522                    Some(match parameter_name {
523                        "hor" => ViewLayoutParameter::Hor,
524                        "ver" => ViewLayoutParameter::Ver,
525                        "zoom" => ViewLayoutParameter::Zoom,
526
527                        _ => return None,
528                    })
529                };
530
531                match subpath {
532                    "Main" => box_layout_parameter()?.main(),
533                    "List" => box_layout_parameter()?.list(),
534                    "View" => LayoutParameter::View(view_layout_parameter()?),
535
536                    _ => return None,
537                }
538            }
539            .named(character_name),
540        ),
541
542        _ => return None,
543    })
544}
545
546impl DialogParameter for Parameter {
547    type Context = SettingsContext;
548
549    fn create(name: &str, context: &mut SettingsContext) -> Option<Self> {
550        if name == "-" {
551            None
552        } else if let Some(parameter) = create_parameter(name, None, context) {
553            Some(parameter)
554        } else {
555            let result = Self::create_character(name, context);
556            if result.is_none() {
557                eprintln!("Setting not availabe: {name}");
558            }
559            result
560        }
561    }
562}
563
564pub enum Setter {
565    Color(NamedColorParameter, Color),
566    Timing(NamedTimingParameter, f32),
567    Image(NamedImageParameter, Option<Box<str>>),
568    ObjectImage(NamedObjectImageParameter, Option<usize>),
569    ObjectLayout(NamedObjectLayoutParameter, f32),
570    Layout(NamedLayoutParameter, f32),
571    Name(Box<str>, Box<str>),
572}
573
574impl Setter {
575    pub fn change(self) -> Change {
576        match self {
577            Self::Color(parameter, value) => Change::Color(parameter, Some(value)),
578            Self::Timing(parameter, value) => Change::Timing(parameter, Some(value)),
579            Self::Image(parameter, value) => Change::Image(parameter, Some(value)),
580            Self::ObjectImage(parameter, value) => Change::ObjectImage(parameter, Some(value)),
581            Self::ObjectLayout(parameter, value) => Change::ObjectLayout(parameter, Some(value)),
582            Self::Layout(parameter, value) => Change::Layout(parameter, Some(value)),
583            Self::Name(parameter, value) => Change::Name(parameter, Some(value)),
584        }
585    }
586}
587
588pub enum Change {
589    Color(NamedColorParameter, Option<Color>),
590    Timing(NamedTimingParameter, Option<f32>),
591    Image(NamedImageParameter, Option<Option<Box<str>>>),
592    ObjectImage(NamedObjectImageParameter, Option<Option<usize>>),
593    ObjectLayout(NamedObjectLayoutParameter, Option<f32>),
594    Layout(NamedLayoutParameter, Option<f32>),
595    Name(Box<str>, Option<Box<str>>),
596}
597
598impl Change {
599    pub fn parameter(&self) -> Parameter {
600        match self {
601            Self::Color(parameter, _) => Parameter::Color(parameter.clone()),
602            Self::Timing(parameter, _) => Parameter::Timing(parameter.clone()),
603            Self::Image(parameter, _) => Parameter::Image(parameter.clone()),
604            Self::ObjectImage(parameter, _) => Parameter::ObjectImage(parameter.clone()),
605            Self::ObjectLayout(parameter, _) => Parameter::ObjectLayout(parameter.clone()),
606            Self::Layout(parameter, _) => Parameter::Layout(parameter.clone()),
607            Self::Name(parameter, _) => Parameter::Name(parameter.clone()),
608        }
609    }
610}
611
612impl DialogChange for Change {
613    type Parameter = Parameter;
614
615    fn default_change(parameter: Parameter) -> Self {
616        match parameter {
617            Parameter::Color(parameter) => Self::Color(parameter, None),
618            Parameter::Timing(parameter) => Self::Timing(parameter, None),
619            Parameter::Image(parameter) => Self::Image(parameter, None),
620            Parameter::ObjectImage(parameter) => Self::ObjectImage(parameter, None),
621            Parameter::ObjectLayout(parameter) => Self::ObjectLayout(parameter, None),
622            Parameter::Layout(parameter) => Self::Layout(parameter, None),
623            Parameter::Name(parameter) => Self::Name(parameter, None),
624        }
625    }
626
627    fn value_change(parameter: Parameter, value: &str, context: &mut SettingsContext) -> Self {
628        parameter.value_setter(value, context).change()
629    }
630}
631
632impl PlayerSettings {
633    pub fn revert(&mut self, parameter: &Parameter) {
634        use Parameter::*;
635        match parameter {
636            Color(parameter) => self
637                .colors
638                .get_mut(&parameter.parameter)
639                .set_scene(parameter.name.as_deref(), None),
640            Timing(parameter) => self
641                .timing
642                .get_mut(&parameter.parameter)
643                .set_scene(parameter.name.as_deref(), None),
644            Image(parameter) => self
645                .images
646                .get_mut(&parameter.parameter)
647                .set_scene(parameter.name.as_deref(), None),
648            ObjectImage(parameter) => self
649                .objects
650                .get_mut(&parameter.parameter)
651                .set_scene(parameter.name.as_deref(), None),
652            ObjectLayout(parameter) => self
653                .objects
654                .get_mut(&parameter.parameter)
655                .set_scene(parameter.name.as_deref(), None),
656            Layout(parameter) => self
657                .layout
658                .get_mut(&parameter.parameter)
659                .set_scene(parameter.name.as_deref(), None),
660            Name(parameter) => {
661                self.names.scene.remove(parameter);
662            }
663        }
664    }
665
666    pub fn change(&mut self, change: &Change) {
667        use Change::*;
668        match change {
669            Color(parameter, value) => self
670                .colors
671                .get_mut(&parameter.parameter)
672                .set_scene(parameter.name.as_deref(), *value),
673            Timing(parameter, value) => self
674                .timing
675                .get_mut(&parameter.parameter)
676                .set_scene(parameter.name.as_deref(), *value),
677            Image(parameter, value) => self
678                .images
679                .get_mut(&parameter.parameter)
680                .set_scene(parameter.name.as_deref(), value.clone()),
681            ObjectImage(parameter, value) => self
682                .objects
683                .get_mut(&parameter.parameter)
684                .set_scene(parameter.name.as_deref(), *value),
685            ObjectLayout(parameter, value) => self
686                .objects
687                .get_mut(&parameter.parameter)
688                .set_scene(parameter.name.as_deref(), *value),
689            Layout(parameter, value) => self
690                .layout
691                .get_mut(&parameter.parameter)
692                .set_scene(parameter.name.as_deref(), *value),
693            Name(parameter, value) => {
694                if let Some(value) = value {
695                    self.names.scene.insert(parameter.clone(), value.clone());
696                } else {
697                    self.names.scene.remove(parameter);
698                }
699            }
700        }
701    }
702
703    pub fn set_character_default(&mut self, change: Setter) {
704        use Setter::*;
705        match change {
706            Color(parameter, value) => self
707                .colors
708                .get_mut(&parameter.parameter)
709                .set_default(parameter.name, value),
710            Timing(parameter, value) => self
711                .timing
712                .get_mut(&parameter.parameter)
713                .set_default(parameter.name, value),
714            Image(parameter, value) => self
715                .images
716                .get_mut(&parameter.parameter)
717                .set_default(parameter.name, value),
718            ObjectImage(parameter, value) => self
719                .objects
720                .get_mut(&parameter.parameter)
721                .set_default(parameter.name, value),
722            ObjectLayout(parameter, value) => self
723                .objects
724                .get_mut(&parameter.parameter)
725                .set_default(parameter.name, value),
726            Layout(parameter, value) => self
727                .layout
728                .get_mut(&parameter.parameter)
729                .set_default(parameter.name, value),
730            Name(parameter, value) => {
731                self.names.default.insert(parameter, value);
732            }
733        }
734    }
735}