settings_dialog.rs - icy_draw - [fork] icy_draw is the successor to mystic draw.
HTML git clone https://git.drkhsh.at/icy_draw.git
DIR Log
DIR Files
DIR Refs
DIR README
---
settings_dialog.rs (16942B)
---
1 use eframe::egui::{self, RichText};
2 use egui::{Layout, TextEdit, Vec2};
3 use i18n_embed_fl::fl;
4 use icy_engine_gui::show_monitor_settings;
5 use icy_net::serial::{CharSize, Parity, StopBits};
6
7 use crate::{
8 ui::{MainWindowMode, MainWindowState},
9 KeyBindings, Modem,
10 };
11
12 #[derive(Default)]
13 pub struct DialogState {
14 pub settings_category: usize,
15 }
16
17 #[derive(Clone, Debug)]
18 pub(crate) enum Message {
19 SwitchSettingsCategory(usize),
20 CloseDialog,
21 OpenSettingsFolder,
22 ResetMonitorSettings,
23 ResetKeybindSettings,
24 UpdateIEMSI(crate::IEMSISettings),
25 UpdateModem(Modem),
26 UpdateMonitorSettings(icy_engine_gui::MonitorSettings),
27 // ChangeOpenglScaling(Scaling),
28 UpdateKeybinds(KeyBindings),
29 ChangeConsoleBeep(bool),
30 }
31
32 type ShowSettingsCallback = fn(&MainWindowState, ui: &mut egui::Ui) -> Option<Message>;
33 type ResetMessage = Option<Message>;
34
35 lazy_static::lazy_static! {
36 static ref SETTING_CATEGORIES: [(String, ShowSettingsCallback, ResetMessage); 5] = [
37 (
38 fl!(crate::LANGUAGE_LOADER, "settings-monitor-category"),
39 show_monitor_settings2,
40 Some(Message::ResetMonitorSettings)
41 ),
42 (
43 fl!(crate::LANGUAGE_LOADER, "settings-iemsi-category"),
44 show_iemsi_settings,
45 None
46 ),
47 (
48 fl!(crate::LANGUAGE_LOADER, "settings-terminal-category"),
49 show_terminal_settings,
50 None
51 ),
52 (
53 fl!(crate::LANGUAGE_LOADER, "settings-keybinds-category"),
54 crate::show_keybinds_settings,
55 Some(Message::ResetKeybindSettings)
56 ),
57 (
58 fl!(crate::LANGUAGE_LOADER, "settings-modem-category"),
59 show_modem_settings,
60 None
61 ),
62 ];
63 }
64
65 impl MainWindowState {
66 pub fn show_settings(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
67 let mut result = None;
68
69 let mut open = true;
70 let title = RichText::new(fl!(crate::LANGUAGE_LOADER, "settings-heading"));
71 if ctx.input(|i| i.key_down(egui::Key::Escape)) {
72 open = false;
73 }
74
75 egui::Window::new(title)
76 .open(&mut open)
77 .collapsible(false)
78 .fixed_size(Vec2::new(400., 300.))
79 .resizable(false)
80 .frame(egui::Frame::window(&ctx.style()))
81 .show(ctx, |ui| {
82 ui.horizontal(|ui| {
83 let old_dark = self.options.is_dark_mode;
84 egui::widgets::global_dark_light_mode_switch(ui);
85 self.options.is_dark_mode = Some(ui.visuals().dark_mode);
86 if self.options.is_dark_mode != old_dark {
87 self.store_options();
88 }
89 let settings_category = self.settings_dialog.settings_category;
90 for i in 0..SETTING_CATEGORIES.len() {
91 if ui.selectable_label(settings_category == i, &SETTING_CATEGORIES[i].0).clicked() {
92 result = Some(Message::SwitchSettingsCategory(i));
93 }
94 }
95 });
96 ui.separator();
97 let settings_category = self.settings_dialog.settings_category;
98 if let Some(cat) = SETTING_CATEGORIES.get(settings_category) {
99 if let Some(cmd) = cat.1(self, ui) {
100 result = Some(cmd);
101 }
102 } else {
103 ui.colored_label(ui.style().visuals.error_fg_color, "Invalid settings category");
104 }
105
106 ui.separator();
107 ui.add_space(4.0);
108 ui.with_layout(Layout::right_to_left(egui::Align::TOP), |ui| {
109 if ui.button(fl!(crate::LANGUAGE_LOADER, "dialing_directory-ok-button")).clicked() {
110 result = Some(Message::CloseDialog);
111 }
112
113 let settings_category = self.settings_dialog.settings_category;
114 if let Some(cat) = SETTING_CATEGORIES.get(settings_category) {
115 if let Some(reset_cmd) = &cat.2 {
116 if ui.button(fl!(crate::LANGUAGE_LOADER, "settings-reset-button")).clicked() {
117 result = Some(reset_cmd.clone());
118 }
119 }
120 }
121 });
122 });
123
124 if !open {
125 result = Some(Message::CloseDialog);
126 }
127
128 update_state(self, result);
129 }
130 }
131
132 fn show_iemsi_settings(state: &MainWindowState, ui: &mut egui::Ui) -> Option<Message> {
133 let mut iemsi = state.options.iemsi.clone();
134 ui.checkbox(&mut iemsi.autologin, fl!(crate::LANGUAGE_LOADER, "settings-iemsi-autologin-checkbox"));
135
136 egui::Grid::new("some_unique_id")
137 .num_columns(2)
138 .spacing([4.0, 8.0])
139 .min_row_height(24.)
140 .show(ui, |ui| {
141 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
142 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "settings-iemsi-alias")));
143 });
144 ui.add(TextEdit::singleline(&mut iemsi.alias));
145 ui.end_row();
146
147 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
148 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "settings-iemsi-location")));
149 });
150 ui.add(TextEdit::singleline(&mut iemsi.location));
151 ui.end_row();
152
153 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
154 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "settings-iemsi-data-phone")));
155 });
156 ui.add(TextEdit::singleline(&mut iemsi.data_phone));
157 ui.end_row();
158
159 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
160 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "settings-iemsi-voice-phone")));
161 });
162 ui.add(TextEdit::singleline(&mut iemsi.voice_phone));
163 ui.end_row();
164
165 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
166 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "settings-iemsi-birth-date")));
167 });
168 ui.add(TextEdit::singleline(&mut iemsi.birth_date));
169 ui.end_row();
170 });
171
172 if iemsi == state.options.iemsi {
173 None
174 } else {
175 Some(Message::UpdateIEMSI(iemsi))
176 }
177 }
178
179 fn show_terminal_settings(state: &MainWindowState, ui: &mut egui::Ui) -> Option<Message> {
180 let mut result = None;
181 let mut beep = state.options.console_beep;
182
183 if ui
184 .checkbox(&mut beep, fl!(crate::LANGUAGE_LOADER, "settings-terminal-console-beep-checkbox"))
185 .changed()
186 {
187 result = Some(Message::ChangeConsoleBeep(beep));
188 }
189
190 ui.add_space(16.0);
191 if ui.button(fl!(crate::LANGUAGE_LOADER, "settings-terminal-open-settings-dir-button")).clicked() {
192 result = Some(Message::OpenSettingsFolder);
193 }
194 ui.add_space(8.0);
195
196 result
197 }
198
199 fn show_monitor_settings2(state: &MainWindowState, ui: &mut egui::Ui) -> Option<Message> {
200 let mut result = None;
201
202 let monitor_settings = state.options.monitor_settings.clone();
203 if let Some(settings) = show_monitor_settings(ui, &monitor_settings) {
204 result = Some(Message::UpdateMonitorSettings(settings));
205 }
206
207 result
208 }
209
210 fn update_state(state: &mut MainWindowState, message_opt: Option<Message>) {
211 match message_opt {
212 Some(Message::CloseDialog) => {
213 state.mode = MainWindowMode::ShowTerminal;
214 }
215 Some(Message::SwitchSettingsCategory(category)) => {
216 state.settings_dialog.settings_category = category;
217 }
218 Some(Message::ResetMonitorSettings) => {
219 state.options.reset_monitor_settings();
220 state.store_options();
221 }
222 Some(Message::ResetKeybindSettings) => {
223 state.options.reset_keybindings();
224 state.store_options();
225 }
226 Some(Message::OpenSettingsFolder) => {
227 if let Some(proj_dirs) = directories::ProjectDirs::from("com", "GitHub", "icy_term") {
228 open::that(proj_dirs.config_dir()).unwrap();
229 }
230 }
231 Some(Message::UpdateIEMSI(iemsi)) => {
232 state.options.iemsi = iemsi;
233 state.store_options();
234 }
235 Some(Message::UpdateModem(modem)) => {
236 state.options.modem = modem;
237 state.store_options();
238 }
239 Some(Message::UpdateMonitorSettings(monitor_settings)) => {
240 state.options.monitor_settings = monitor_settings;
241 state.store_options();
242 } /*
243 Some(Message::ChangeOpenglScaling(scaling)) => {
244 state.options.scaling = scaling;
245 state.store_options();
246 }*/
247 Some(Message::UpdateKeybinds(keybinds)) => {
248 state.options.bind = keybinds;
249 state.store_options();
250 }
251 Some(Message::ChangeConsoleBeep(beep)) => {
252 state.options.console_beep = beep;
253 state.store_options();
254 }
255 _ => {}
256 }
257 }
258
259 fn show_modem_settings(state: &MainWindowState, ui: &mut egui::Ui) -> Option<Message> {
260 let mut modem = state.options.modem.clone();
261
262 egui::Grid::new("some_unique_id")
263 .num_columns(2)
264 .spacing([4.0, 8.0])
265 .min_row_height(24.)
266 .show(ui, |ui| {
267 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
268 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "settings-modem-device")));
269 });
270 ui.add(TextEdit::singleline(&mut modem.device));
271 ui.end_row();
272
273 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
274 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "settings-modem-baud_rate")));
275 });
276 let mut baud = modem.baud_rate.to_string();
277 ui.add(TextEdit::singleline(&mut baud));
278 if let Ok(baud) = baud.parse() {
279 modem.baud_rate = baud;
280 }
281 ui.end_row();
282
283 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui: &mut egui::Ui| {
284 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "settings-modem-data_bits")));
285 });
286
287 ui.horizontal(|ui| {
288 let txt = match modem.char_size {
289 CharSize::Bits5 => "5",
290 CharSize::Bits6 => "6",
291 CharSize::Bits7 => "7",
292 CharSize::Bits8 => "8",
293 };
294 egui::ComboBox::from_id_source("combobox1").selected_text(RichText::new(txt)).show_ui(ui, |ui| {
295 ui.selectable_value(&mut modem.char_size, CharSize::Bits5, "5");
296 ui.selectable_value(&mut modem.char_size, CharSize::Bits6, "6");
297 ui.selectable_value(&mut modem.char_size, CharSize::Bits7, "7");
298 ui.selectable_value(&mut modem.char_size, CharSize::Bits8, "8");
299 });
300
301 let txt = match modem.stop_bits {
302 StopBits::One => "1",
303 StopBits::Two => "2",
304 };
305 egui::ComboBox::from_id_source("combobox2").selected_text(RichText::new(txt)).show_ui(ui, |ui| {
306 ui.selectable_value(&mut modem.stop_bits, StopBits::One, "1");
307 ui.selectable_value(&mut modem.stop_bits, StopBits::Two, "2");
308 });
309 let txt = match modem.parity {
310 Parity::None => "None",
311 Parity::Odd => "Odd",
312 Parity::Even => "Even",
313 };
314 egui::ComboBox::from_id_source("combobox3").selected_text(RichText::new(txt)).show_ui(ui, |ui| {
315 ui.selectable_value(&mut modem.parity, Parity::None, "None");
316 ui.selectable_value(&mut modem.parity, Parity::Odd, "Odd");
317 ui.selectable_value(&mut modem.parity, Parity::Even, "Even");
318 });
319 });
320 ui.end_row();
321
322 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
323 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "settings-modem-init_string")));
324 });
325 ui.add(TextEdit::singleline(&mut modem.init_string));
326 ui.end_row();
327
328 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
329 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "settings-modem_dial_string")));
330 });
331 ui.add(TextEdit::singleline(&mut modem.dial_string));
332 ui.end_row();
333 });
334
335 if modem == state.options.modem {
336 None
337 } else {
338 Some(Message::UpdateModem(modem))
339 }
340 }
341
342 #[cfg(test)]
343 mod tests {
344 #![allow(clippy::field_reassign_with_default)]
345 use icy_engine_gui::MonitorSettings;
346
347 use crate::{
348 ui::{
349 dialogs::settings_dialog::{update_state, SETTING_CATEGORIES},
350 MainWindowState,
351 },
352 IEMSISettings, KeyBindings, Options, Scaling,
353 };
354
355 #[test]
356 fn test_close_dialog() {
357 let mut state: MainWindowState = MainWindowState::default();
358 state.mode = super::MainWindowMode::ShowSettings;
359 update_state(&mut state, Some(super::Message::CloseDialog));
360 assert!(matches!(state.mode, super::MainWindowMode::ShowTerminal));
361 assert!(!state.options_written);
362 }
363
364 #[test]
365 fn test_switch_category_dialog() {
366 let mut state: MainWindowState = MainWindowState::default();
367 for i in 0..SETTING_CATEGORIES.len() {
368 update_state(&mut state, Some(super::Message::SwitchSettingsCategory(i)));
369 assert_eq!(i, state.settings_dialog.settings_category);
370 }
371 assert!(!state.options_written);
372 }
373
374 #[test]
375 fn test_reset_monitor_settings() {
376 let mut state: MainWindowState = MainWindowState::default();
377 let mut opt: Options = Options::default();
378 opt.scaling = Scaling::Linear;
379 opt.monitor_settings.blur = 0.0;
380 opt.monitor_settings.brightness = 1.0;
381 state.options = opt;
382
383 assert_ne!(Options::default().scaling, state.options.scaling);
384 assert_ne!(Options::default().monitor_settings, state.options.monitor_settings);
385 update_state(&mut state, Some(super::Message::ResetMonitorSettings));
386 assert_eq!(Options::default().scaling, state.options.scaling);
387 assert_eq!(Options::default().monitor_settings, state.options.monitor_settings);
388 assert!(state.options_written);
389 }
390
391 #[test]
392 fn test_reset_keybindings() {
393 let mut state: MainWindowState = MainWindowState::default();
394 let mut opt = Options::default();
395 opt.bind.download = None;
396 opt.bind.full_screen = None;
397 state.options = opt;
398
399 assert_ne!(Options::default().bind, state.options.bind);
400 update_state(&mut state, Some(super::Message::ResetKeybindSettings));
401 assert_eq!(Options::default().bind, state.options.bind);
402 assert!(state.options_written);
403 }
404
405 #[test]
406 fn test_change_console_beep() {
407 let mut state: MainWindowState = MainWindowState::default();
408 update_state(&mut state, Some(super::Message::ChangeConsoleBeep(false)));
409 assert_ne!(Options::default().console_beep, state.options.console_beep);
410 assert!(state.options_written);
411 }
412
413 #[test]
414 fn test_set_keybindings() {
415 let mut state: MainWindowState = MainWindowState::default();
416 let mut bind = KeyBindings::default();
417 bind.download = None;
418 bind.full_screen = None;
419 update_state(&mut state, Some(super::Message::UpdateKeybinds(bind)));
420 assert_ne!(KeyBindings::default(), state.options.bind);
421 assert!(state.options_written);
422 }
423
424 #[test]
425 fn test_set_monitor_settings() {
426 let mut state: MainWindowState = MainWindowState::default();
427 let mut settings = MonitorSettings::default();
428 settings.blur = 0.0;
429 settings.brightness = 1.0;
430 update_state(&mut state, Some(super::Message::UpdateMonitorSettings(settings)));
431 assert_ne!(MonitorSettings::default(), state.options.monitor_settings);
432 assert!(state.options_written);
433 }
434
435 #[test]
436 fn test_set_iemsi_settings() {
437 let mut state: MainWindowState = MainWindowState::default();
438 let mut settings = IEMSISettings::default();
439 settings.alias = "foo".to_string();
440 settings.voice_phone = "42".to_string();
441 update_state(&mut state, Some(super::Message::UpdateIEMSI(settings)));
442 assert_ne!(IEMSISettings::default(), state.options.iemsi);
443 assert!(state.options_written);
444 }
445 }