paiagram/
settings.rs

1use crate::i18n::Language;
2use bevy::prelude::*;
3use bevy_inspector_egui::DefaultInspectorConfigPlugin;
4use egui_i18n::{set_language, tr};
5use strum::IntoEnumIterator;
6use strum_macros::EnumIter;
7
8pub struct SettingsPlugin;
9impl Plugin for SettingsPlugin {
10    fn build(&self, app: &mut App) {
11        app.insert_resource(ApplicationSettings::default())
12            .add_systems(
13                Update,
14                update_language_setting.run_if(resource_changed_or_removed::<ApplicationSettings>),
15            )
16            .add_plugins(DefaultInspectorConfigPlugin);
17    }
18}
19
20/// Update the application's language when the language setting changes
21/// This function should only be called when the ApplicationSettings resource changes
22/// using Bevy's [`resource_changed_or_removed`] condition.
23fn update_language_setting(settings: Res<ApplicationSettings>) {
24    set_language(settings.language.identifier());
25}
26
27/// Different terminology schemes for railway terms
28#[derive(Reflect, Clone, Copy, Debug, EnumIter, PartialEq, Eq)]
29pub enum TerminologyScheme {
30    Paiagram,
31    ChineseRailway,
32    JapaneseRailway,
33}
34
35impl TerminologyScheme {
36    pub fn name(self) -> &'static str {
37        match self {
38            Self::Paiagram => "Paiagram",
39            Self::ChineseRailway => "Chinese Railway",
40            Self::JapaneseRailway => "Japanese Railway",
41        }
42    }
43}
44
45#[derive(Reflect, Clone, Copy, Debug, EnumIter, PartialEq, Eq)]
46pub enum PinyinScheme {
47    Sogou,
48    Microsoft,
49}
50
51#[derive(Reflect, Clone, Debug)]
52pub enum Author {
53    Unknown,
54    OpenStreetMapContributors,
55    Lead(String),
56    Contributor(String),
57}
58
59#[derive(Reflect, Clone, Debug)]
60pub struct AuthorList(pub Vec<Author>);
61
62/// TODO: Extract user preferences from this list
63#[derive(Reflect, Resource)]
64#[reflect(Resource)]
65pub struct ApplicationSettings {
66    pub enable_romaji_search: bool,
67    pub show_performance_stats: bool,
68    pub pinyin_scheme: Vec<String>,
69    pub terminology_scheme: TerminologyScheme,
70    pub language: Language,
71    pub authors: AuthorList,
72    pub remarks: String,
73    pub autosave_enabled: bool,
74    pub autosave_interval_minutes: u32,
75    pub is_developer_mode: bool,
76}
77
78impl Default for ApplicationSettings {
79    fn default() -> Self {
80        Self {
81            enable_romaji_search: false,
82            show_performance_stats: cfg!(debug_assertions),
83            pinyin_scheme: vec!["quanpin".into(), "diletter_microsoft".into()],
84            terminology_scheme: TerminologyScheme::Paiagram,
85            language: Language::EnCA,
86            authors: AuthorList(if cfg!(target_arch = "wasm32") {
87                vec![Author::Unknown]
88            } else {
89                vec![
90                    std::env::var("USER")
91                        .or_else(|_| std::env::var("USERNAME"))
92                        .map(Author::Lead)
93                        .unwrap_or(Author::Unknown),
94                ]
95            }),
96            remarks: String::new(),
97            autosave_enabled: true,
98            autosave_interval_minutes: 5,
99            is_developer_mode: cfg!(debug_assertions),
100        }
101    }
102}
103
104impl egui::Widget for &mut ApplicationSettings {
105    fn ui(self, ui: &mut egui::Ui) -> egui::Response {
106        ui.checkbox(
107            &mut self.enable_romaji_search,
108            tr!("settings-enable-romaji-search"),
109        );
110        ui.checkbox(
111            &mut self.show_performance_stats,
112            tr!("settings-show-performance-stats"),
113        );
114        egui::ComboBox::from_id_salt("Language")
115            .selected_text(self.language.name())
116            .show_ui(ui, |ui| {
117                for lang in Language::iter() {
118                    ui.selectable_value(&mut self.language, lang, lang.name());
119                }
120            });
121        egui::ComboBox::from_id_salt("Terminology scheme")
122            .selected_text(self.terminology_scheme.name())
123            .show_ui(ui, |ui| {
124                for scheme in TerminologyScheme::iter() {
125                    ui.selectable_value(&mut self.terminology_scheme, scheme, scheme.name());
126                }
127            });
128        ui.text_edit_multiline(&mut self.remarks);
129        ui.checkbox(&mut self.autosave_enabled, tr!("settings-enable-autosave"));
130        ui.add(
131            egui::Slider::new(&mut self.autosave_interval_minutes, 1..=10)
132                .text(tr!("settings-autosave-interval")),
133        );
134        ui.checkbox(
135            &mut self.is_developer_mode,
136            tr!("settings-enable-developer-mode"),
137        )
138    }
139}