settings_dialog.rs - icy_draw - icy_draw is the successor to mystic draw. fork / mirror
(HTM) git clone https://git.drkhsh.at/icy_draw.git
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
settings_dialog.rs (18866B)
---
1 use std::sync::Arc;
2
3 use eframe::{
4 egui::{self, color_picker, Layout, Modifiers, RichText},
5 epaint::{mutex::Mutex, Color32, Vec2},
6 };
7 use egui::Context;
8 use i18n_embed_fl::fl;
9 use icy_engine::{AttributedChar, BitFont, Buffer, Color, Position, Size, TextAttribute};
10 use icy_engine_egui::{show_monitor_settings, show_terminal_area, BufferView, MarkerSettings, MonitorSettings};
11
12 use crate::{CharSetMapping, CharTableToolWindow, Commands, FontSelector, ModalDialog, SelectOutlineDialog, Settings, CHARACTER_SETS, KEYBINDINGS, SETTINGS};
13 pub struct SettingsDialog {
14 settings_category: usize,
15 select_outline_dialog: SelectOutlineDialog,
16 is_dark_mode: Option<bool>,
17 monitor_settings: MonitorSettings,
18 marker_settings: MarkerSettings,
19 key_filter: String,
20 key_bindings: Vec<(String, eframe::egui::Key, Modifiers)>,
21
22 font_cache: Vec<BitFont>,
23 font_selector: Option<FontSelector>,
24 cur_char_set: usize,
25 char_sets: Vec<CharSetMapping>,
26 views: Vec<Arc<Mutex<BufferView>>>,
27 selected_view: usize,
28 char_view: CharTableToolWindow,
29 }
30 const MONITOR_CAT: usize = 0;
31 const MARKER_CAT: usize = 1;
32 const OUTLINE_CAT: usize = 2;
33 const CHAR_SET_CAT: usize = 3;
34 const KEYBIND_CAT: usize = 4;
35
36 impl SettingsDialog {
37 pub fn new(ctx: &Context, gl: &Arc<glow::Context>) -> Self {
38 let mut views = Vec::new();
39
40 for _ in 0..15 {
41 let mut buffer = Buffer::new(Size::new(10, 1));
42 buffer.is_terminal_buffer = false;
43 let mut buffer_view = BufferView::from_buffer(gl, buffer);
44 buffer_view.interactive = true;
45 views.push(Arc::new(Mutex::new(buffer_view)));
46 }
47 let char_view = CharTableToolWindow::new(ctx, 32);
48
49 let mut font_cache = if let Ok(font_dir) = Settings::get_font_diretory() {
50 FontSelector::load_fonts(font_dir.as_path())
51 } else {
52 Vec::new()
53 };
54
55 for f in icy_engine::SAUCE_FONT_NAMES {
56 font_cache.push(BitFont::from_sauce_name(f).unwrap());
57 }
58 for slot in 0..icy_engine::ANSI_FONTS {
59 let ansi_font = BitFont::from_ansi_font_page(slot).unwrap();
60 font_cache.push(ansi_font);
61 }
62 font_cache.dedup_by(|x, y| x.get_checksum() == y.get_checksum());
63 let font = BitFont::default();
64 font_cache.retain(|x| x.get_checksum() != font.get_checksum());
65 font_cache.insert(0, font);
66
67 Self {
68 settings_category: MONITOR_CAT,
69 select_outline_dialog: SelectOutlineDialog::default(),
70 monitor_settings: Default::default(),
71 marker_settings: Default::default(),
72 key_filter: String::new(),
73 key_bindings: Commands::default_keybindings(),
74 char_sets: Default::default(),
75 font_cache,
76 cur_char_set: 0,
77 selected_view: 0,
78 views,
79 char_view,
80 font_selector: None,
81 is_dark_mode: unsafe { SETTINGS.is_dark_mode },
82 }
83 }
84
85 pub(crate) fn init(&mut self) {
86 self.monitor_settings = unsafe { SETTINGS.monitor_settings.clone() };
87 self.marker_settings = unsafe { SETTINGS.marker_settings.clone() };
88 self.key_bindings = unsafe { KEYBINDINGS.key_bindings.clone() };
89 self.char_sets = unsafe { CHARACTER_SETS.character_sets.clone() };
90 }
91
92 pub fn show(&mut self, ctx: &egui::Context) -> bool {
93 let mut open = true;
94 let mut dialog_open = true;
95 let title = RichText::new(fl!(crate::LANGUAGE_LOADER, "settings-heading"));
96 if ctx.input(|i| i.key_down(egui::Key::Escape)) {
97 open = false;
98 }
99
100 if let Some(selector) = &mut self.font_selector {
101 if selector.show(ctx) {
102 if selector.should_commit() {
103 let font = selector.selected_font().get_checksum();
104 let mut new_set = self.char_sets[0].clone();
105 new_set.font_checksum = font;
106 self.char_sets.push(new_set);
107 }
108 self.font_selector = None;
109 }
110 return open;
111 }
112
113 egui::Window::new(title)
114 .open(&mut open)
115 .collapsible(false)
116 .fixed_size(Vec2::new(400., 300.))
117 .resizable(false)
118 .frame(egui::Frame::window(&ctx.style()))
119 .show(ctx, |ui| {
120 ui.horizontal(|ui| {
121 egui::widgets::global_dark_light_mode_switch(ui);
122 self.is_dark_mode = Some(ui.visuals().dark_mode);
123
124 let settings_category = self.settings_category;
125
126 if ui
127 .selectable_label(settings_category == MONITOR_CAT, fl!(crate::LANGUAGE_LOADER, "settings-monitor-category"))
128 .clicked()
129 {
130 self.settings_category = MONITOR_CAT;
131 }
132
133 if ui
134 .selectable_label(settings_category == MARKER_CAT, fl!(crate::LANGUAGE_LOADER, "settings-markers-guides-category"))
135 .clicked()
136 {
137 self.settings_category = MARKER_CAT;
138 }
139
140 if ui
141 .selectable_label(settings_category == OUTLINE_CAT, fl!(crate::LANGUAGE_LOADER, "settings-font-outline-category"))
142 .clicked()
143 {
144 self.settings_category = OUTLINE_CAT;
145 }
146 if ui
147 .selectable_label(settings_category == CHAR_SET_CAT, fl!(crate::LANGUAGE_LOADER, "settings-char-set-category"))
148 .clicked()
149 {
150 self.settings_category = CHAR_SET_CAT;
151 }
152
153 if ui
154 .selectable_label(settings_category == KEYBIND_CAT, fl!(crate::LANGUAGE_LOADER, "settings-keybindings-category"))
155 .clicked()
156 {
157 self.settings_category = KEYBIND_CAT;
158 }
159 });
160 ui.separator();
161 match self.settings_category {
162 MONITOR_CAT => unsafe {
163 if let Some(new_settings) = show_monitor_settings(ui, &SETTINGS.monitor_settings) {
164 SETTINGS.monitor_settings = new_settings;
165 }
166 },
167 MARKER_CAT => {
168 ui.add_space(8.0);
169 unsafe {
170 if let Some(new_settings) = show_marker_settings(ui, &SETTINGS.marker_settings) {
171 SETTINGS.marker_settings = new_settings;
172 }
173 }
174 }
175
176 CHAR_SET_CAT => {
177 ui.add_space(8.0);
178 self.show_charset_editor(ui);
179 }
180
181 OUTLINE_CAT => {
182 ui.add_space(8.0);
183 self.select_outline_dialog.show_outline_ui(ui, 4, Vec2::new(8.0, 8.0));
184 }
185
186 KEYBIND_CAT => {
187 let mut map = std::collections::HashMap::new();
188 for (s, key, modifier) in &self.key_bindings {
189 map.insert(s.clone(), (*key, *modifier));
190 }
191 crate::Commands::show_keybinds_settings(ui, &mut self.key_filter, &mut map);
192 self.key_bindings.clear();
193 for (s, (key, modifier)) in map {
194 self.key_bindings.push((s, key, modifier));
195 }
196 }
197 _ => {}
198 }
199
200 ui.separator();
201 ui.add_space(4.0);
202 ui.with_layout(Layout::right_to_left(egui::Align::TOP), |ui| {
203 if ui.button(fl!(crate::LANGUAGE_LOADER, "new-file-ok")).clicked() {
204 unsafe {
205 if KEYBINDINGS.key_bindings != self.key_bindings {
206 KEYBINDINGS.key_bindings = self.key_bindings.clone();
207 if let Err(err) = KEYBINDINGS.save() {
208 log::error!("Error saving keybindings: {}", err);
209 }
210 }
211 if CHARACTER_SETS.character_sets != self.char_sets {
212 CHARACTER_SETS.character_sets = self.char_sets.clone();
213 if let Err(err) = CHARACTER_SETS.save() {
214 log::error!("Error saving character sets: {}", err);
215 }
216 }
217 SETTINGS.is_dark_mode = self.is_dark_mode;
218 if let Err(err) = Settings::save() {
219 log::error!("Error saving settings: {err}");
220 }
221 }
222 dialog_open = false;
223 }
224
225 if ui.button(fl!(crate::LANGUAGE_LOADER, "new-file-cancel")).clicked() {
226 unsafe {
227 SETTINGS.monitor_settings = self.monitor_settings.clone();
228 SETTINGS.marker_settings = self.marker_settings.clone();
229 if let Some(dark_mode) = SETTINGS.is_dark_mode {
230 ui.visuals_mut().dark_mode = dark_mode;
231 }
232 }
233 dialog_open = false;
234 }
235
236 if (self.settings_category == MONITOR_CAT
237 || self.settings_category == MARKER_CAT
238 || self.settings_category == CHAR_SET_CAT
239 || self.settings_category == KEYBIND_CAT)
240 && ui.button(fl!(crate::LANGUAGE_LOADER, "settings-reset_button")).clicked()
241 {
242 unsafe {
243 match self.settings_category {
244 MONITOR_CAT => SETTINGS.monitor_settings = Default::default(),
245 MARKER_CAT => SETTINGS.marker_settings = Default::default(),
246 CHAR_SET_CAT => self.char_sets = Default::default(),
247 KEYBIND_CAT => {
248 self.key_bindings = Commands::default_keybindings();
249 }
250 _ => {}
251 }
252 }
253 }
254 });
255 });
256
257 open && dialog_open
258 }
259
260 pub fn show_charset_editor(&mut self, ui: &mut egui::Ui) {
261 ui.set_height(580.);
262 let mut id = 0;
263 ui.add_space(48.0);
264
265 let mut cur_font = &self.font_cache[0];
266 for font in &self.font_cache {
267 if font.checksum == self.char_sets[self.cur_char_set].font_checksum {
268 cur_font = font;
269 break;
270 }
271 }
272
273 ui.horizontal(|ui| {
274 ui.vertical(|ui| {
275 ui.label(fl!(crate::LANGUAGE_LOADER, "settings-char_set_list_label"));
276 egui::ScrollArea::vertical().max_height(240.0).show(ui, |ui| {
277 ui.vertical(|ui| {
278 for (i, char_set) in self.char_sets.iter().enumerate() {
279 let label = if char_set.font_checksum == 0 {
280 "Default".to_string()
281 } else {
282 let mut result = "Unknown".to_string();
283 for font in &self.font_cache {
284 if font.checksum == char_set.font_checksum {
285 result = font.name.to_string();
286 break;
287 }
288 }
289 result
290 };
291 if ui.selectable_label(self.cur_char_set == i, label).clicked() {
292 self.cur_char_set = i;
293 }
294 }
295 });
296 });
297 });
298 ui.separator();
299 if ui.add(egui::Button::new(fl!(crate::LANGUAGE_LOADER, "add-font-dialog-select"))).clicked() {
300 self.font_selector = Some(FontSelector::font_library());
301 }
302
303 if ui
304 .add_enabled(
305 self.cur_char_set > 0,
306 egui::Button::new(fl!(crate::LANGUAGE_LOADER, "manage-font-remove_font_button")),
307 )
308 .clicked()
309 {
310 self.char_sets.remove(self.cur_char_set);
311 self.cur_char_set = 0;
312 }
313 if ui
314 .add_enabled(self.cur_char_set == 0, egui::Button::new(fl!(crate::LANGUAGE_LOADER, "settings-reset_button")))
315 .clicked()
316 {
317 self.char_sets[0] = CharSetMapping::default();
318 for view in self.views.iter() {
319 view.lock().get_edit_state_mut().set_is_buffer_dirty();
320 }
321 }
322 });
323 ui.separator();
324 egui::Grid::new("paste_mode_grid").num_columns(6).spacing([8.0, 8.0]).show(ui, |ui| {
325 for (i, view) in self.views.iter().enumerate() {
326 let font_dims = view.lock().get_buffer_mut().get_font_dimensions();
327
328 let opt = icy_engine_egui::TerminalOptions {
329 stick_to_bottom: false,
330 scale: Some(Vec2::new(2.0, 2.0)),
331 id: Some(egui::Id::new(200 + id)),
332 terminal_size: Some(Vec2::new(font_dims.width as f32 * 10. * 2.0, font_dims.height as f32 * 2.0)),
333 force_focus: self.selected_view == i,
334 ..Default::default()
335 };
336
337 for x in 0..10 {
338 let ch = self.char_sets[self.cur_char_set].table[id][x];
339 view.lock().get_buffer_mut().layers[0].set_char((x, 0), AttributedChar::new(ch, TextAttribute::default()));
340 }
341 if id % 3 == 0 {
342 ui.end_row();
343 }
344 id += 1;
345
346 ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
347 if i == self.selected_view {
348 ui.strong(fl!(crate::LANGUAGE_LOADER, "settings-set-label", set = id));
349 } else {
350 ui.label(fl!(crate::LANGUAGE_LOADER, "settings-set-label", set = id));
351 }
352 });
353 let (response, calc) = show_terminal_area(ui, view.clone(), opt);
354 if response.has_focus() {
355 self.selected_view = i;
356 }
357 if response.clicked() {
358 if let Some(click_pos) = response.hover_pos() {
359 let pos = calc.calc_click_pos(click_pos);
360 let pos = Position::new(pos.x as i32, pos.y as i32);
361 view.lock().get_caret_mut().set_position(pos);
362 }
363 }
364 }
365 });
366 if self.char_view.get_font().get_checksum() != cur_font.checksum {
367 self.char_view.set_font(ui.ctx(), cur_font.clone());
368 for view in &self.views {
369 view.lock().get_buffer_mut().set_font(0, cur_font.clone())
370 }
371 }
372 if let Some(ch) = self.char_view.show_plain_char_table(ui) {
373 let mut pos = self.views[self.selected_view].lock().get_caret().get_position();
374 self.char_sets[self.cur_char_set].table[self.selected_view][pos.x as usize] = ch;
375 pos.x = (pos.x + 1).min(9);
376 self.views[self.selected_view].lock().get_caret_mut().set_position(pos);
377
378 for x in 0..10 {
379 let ch = self.char_sets[self.cur_char_set].table[self.selected_view][x];
380 self.views[self.selected_view].lock().get_buffer_mut().layers[0].set_char((x, 0), AttributedChar::new(ch, TextAttribute::default()));
381 }
382
383 self.views[self.selected_view].lock().get_edit_state_mut().set_is_buffer_dirty();
384 }
385
386 if ui.input(|i| i.key_pressed(egui::Key::ArrowLeft)) {
387 let mut pos = self.views[self.selected_view].lock().get_caret().get_position();
388 pos.x = (pos.x - 1).max(0);
389 self.views[self.selected_view].lock().get_caret_mut().set_position(pos);
390 }
391
392 if ui.input(|i| i.key_pressed(egui::Key::ArrowRight)) {
393 let mut pos = self.views[self.selected_view].lock().get_caret().get_position();
394 pos.x = (pos.x + 1).min(9);
395 self.views[self.selected_view].lock().get_caret_mut().set_position(pos);
396 }
397 }
398 }
399
400 pub fn show_marker_settings(ui: &mut egui::Ui, old_settings: &MarkerSettings) -> Option<MarkerSettings> {
401 let mut result = None;
402
403 let mut marker_settings = old_settings.clone();
404
405 ui.add(egui::Slider::new(&mut marker_settings.reference_image_alpha, 0.1..=0.9).text(fl!(crate::LANGUAGE_LOADER, "settings-reference-alpha")));
406
407 ui.horizontal(|ui| {
408 ui.label(fl!(crate::LANGUAGE_LOADER, "settings-raster-label"));
409 let (r, g, b) = marker_settings.raster_color.get_rgb();
410 let mut color = Color32::from_rgb(r, g, b);
411
412 color_picker::color_edit_button_srgba(ui, &mut color, color_picker::Alpha::Opaque);
413 marker_settings.raster_color = Color::new(color.r(), color.g(), color.b());
414 ui.add(egui::Slider::new(&mut marker_settings.raster_alpha, 0.1..=0.9).text(fl!(crate::LANGUAGE_LOADER, "settings-alpha")));
415 });
416
417 ui.horizontal(|ui| {
418 ui.label(fl!(crate::LANGUAGE_LOADER, "settings-guide-label"));
419 let (r, g, b) = marker_settings.guide_color.get_rgb();
420 let mut color = Color32::from_rgb(r, g, b);
421
422 color_picker::color_edit_button_srgba(ui, &mut color, color_picker::Alpha::Opaque);
423 marker_settings.guide_color = Color::new(color.r(), color.g(), color.b());
424
425 ui.add(egui::Slider::new(&mut marker_settings.guide_alpha, 0.1..=0.9).text(fl!(crate::LANGUAGE_LOADER, "settings-alpha")));
426 });
427
428 if marker_settings != *old_settings {
429 result = Some(marker_settings);
430 }
431
432 result
433 }