URI: 
       Added keybinding settings page. - icy_draw - icy_draw is the successor to mystic draw. fork / mirror
  HTML git clone https://git.drkhsh.at/icy_draw.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit bd3276e114a6d57c4d46e45fbbeddaa1b924ec3b
   DIR parent 876ea5a37d7a75573f8f2535fb5058178e546cef
  HTML Author: Mike Krüger <mkrueger@posteo.de>
       Date:   Tue, 19 Sep 2023 14:19:28 +0200
       
       Added keybinding settings page.
       
       Diffstat:
         M i18n/en/icy_draw.ftl                |       1 +
         M src/main.rs                         |       1 +
         M src/ui/commands.rs                  |      76 +++++++++++++++++++++++++++++--
         M src/ui/dialogs/settings_dialog.rs   |      89 +++++++++++++++++++++++++------
         M src/ui/main_window.rs               |      15 ++++++++++++---
         M src/ui/messages.rs                  |       1 +
         M src/ui/settings.rs                  |       7 +++++--
       
       7 files changed, 167 insertions(+), 23 deletions(-)
       ---
   DIR diff --git a/i18n/en/icy_draw.ftl b/i18n/en/icy_draw.ftl
       @@ -382,6 +382,7 @@ settings-reset_button=Reset
        settings-monitor-category=Monitor
        settings-font-outline-category=Font Outline
        settings-markers-guides-category=Markers & Guides
       +settings-keybindings-category=Keys
        settings-reference-alpha=Reference image alpha
        settings-raster-label=Grid color:
        settings-alpha=alpha
   DIR diff --git a/src/main.rs b/src/main.rs
       @@ -105,6 +105,7 @@ fn main() {
                if settings_file.exists() {
                    if let Ok(settings) = Settings::load(&settings_file) {
                        unsafe {
       +                    SETTINGS.key_bindings = Commands::default_keybindings();
                            SETTINGS = settings;
                        }
                    }
   DIR diff --git a/src/ui/commands.rs b/src/ui/commands.rs
       @@ -217,12 +217,13 @@ macro_rules! key {
                None
            };
            ($key:ident, $modifier: ident) => {
       -        Some((KeyOrPointer::Key(egui::Key::$key), modifier_keys::$modifier))
       +        Some((egui::Key::$key, modifier_keys::$modifier))
            };
        }
        
        macro_rules! keys {
            ($( ($l:ident, $translation: expr, $message:ident, $cmd_state: ident$(, $key:ident, $modifier: ident)? ) ),* $(,)? ) => {
       +
                pub struct Commands {
                    state_map: HashMap<u32, Box<dyn CommandState>>,
                    $(
       @@ -247,6 +248,16 @@ macro_rules! keys {
                }
        
                impl Commands {
       +            pub fn default_keybindings() -> Vec<(String, egui::Key, Modifiers)> {
       +                let mut result = Vec::new();
       +                $(
       +                    let key = key!($($key, $modifier)?);
       +                    if let Some((key, modifier)) = key  {
       +                        result.push((stringify!($l).to_string(), key, modifier));
       +                    }
       +                )*
       +                result
       +            }
                    pub fn check(&self, ctx: &egui::Context, message: &mut Option<Message>) {
                        $(
                            if self.$l.is_pressed(ctx) {
       @@ -269,6 +280,64 @@ macro_rules! keys {
                        )*
                    }
        
       +            pub fn apply_key_bindings(&mut self, key_bindings: &Vec<(String, egui::Key, Modifiers)> ) {
       +                for (binding, key, modifier) in key_bindings {
       +                    match binding.as_str() {
       +                        $(
       +                            stringify!($l) => {
       +                                self.$l.key = Some((KeyOrPointer::Key(*key), *modifier));
       +                            }
       +                        )*
       +
       +                        _ => {}
       +                    }
       +                }
       +            }
       +
       +            pub(crate) fn show_keybinds_settings(ui: &mut egui::Ui, filter: &mut String, keys: &mut HashMap<String, (egui::Key, Modifiers)>) -> bool {
       +                let mut changed_bindings = false;
       +
       +                ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
       +                    let response = ui.button("🗙");
       +                    if response.clicked() {
       +                        filter.clear();
       +                    }
       +
       +                    ui.add(
       +                        egui::TextEdit::singleline(filter).hint_text(fl!(
       +                            crate::LANGUAGE_LOADER,
       +                            "select-font-dialog-filter-text"
       +                        )),
       +                    );
       +                }); 
       +                egui::ScrollArea::vertical()
       +                    .max_height(240.0)
       +                    .show(ui, |ui| {
       +           
       +                    $(
       +                        let label = fl!(crate::LANGUAGE_LOADER, $translation);
       +                        if filter.is_empty() || label.to_lowercase().contains(filter.to_lowercase().as_str()) {
       +                            ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
       +                                let mut bind = if let Some(x) = keys.get(stringify!($l)) { Some(x.clone ()) } else {None };
       +                                if ui.add(egui_bind::Bind::new(
       +                                    stringify!($l).to_string(),
       +                                    &mut bind,
       +                                )).changed() {
       +                                    if let Some(bind) = bind {
       +                                        keys.insert(stringify!($l).into(), bind);
       +                                    }  else {
       +                                        keys.remove(stringify!($l));
       +                                    } 
       +                                    changed_bindings = true;
       +                                }
       +                                ui.label(label);
       +                            });
       +                        }
       +                    )*
       +                });
       +                changed_bindings
       +            }
       +
                }
            };
        }
       @@ -284,11 +353,12 @@ fn hash(str: impl Into<String>) -> u32 {
        
        impl CommandWrapper {
            pub fn new(
       -        key: Option<(KeyOrPointer, Modifiers)>,
       +        key: Option<(egui::Key, Modifiers)>,
                message: Message,
                description: String,
                state_key: u32,
            ) -> Self {
       +        let key = key.map(|(k, m)| (KeyOrPointer::Key(k), m));
                Self {
                    key,
                    message,
       @@ -388,7 +458,7 @@ keys![
                close_window,
                "menu-close",
                CloseWindow,
       -        BufferOpenState,
       +        AlwaysEnabledState,
                Q,
                CTRL
            ),
   DIR diff --git a/src/ui/dialogs/settings_dialog.rs b/src/ui/dialogs/settings_dialog.rs
       @@ -1,19 +1,35 @@
        use eframe::{
       -    egui::{self, color_picker, Layout, RichText},
       +    egui::{self, color_picker, Layout, RichText, Modifiers},
            epaint::Vec2,
        };
        use i18n_embed_fl::fl;
       -use icy_engine_egui::{show_monitor_settings, MarkerSettings};
       +use icy_engine_egui::{show_monitor_settings, MarkerSettings, MonitorSettings};
        
       -use crate::{SelectOutlineDialog, SETTINGS};
       +use crate::{SelectOutlineDialog, SETTINGS, Commands};
        
        #[derive(Default)]
        pub struct SettingsDialog {
            settings_category: usize,
            select_outline_dialog: SelectOutlineDialog,
       +
       +    monitor_settings: MonitorSettings,
       +    marker_settings: MarkerSettings,
       +    key_filter: String,
       +    key_bindings: Vec<(String, eframe::egui::Key, Modifiers)>,
       +
        }
       +const MONITOR_CAT: usize = 0 ;
       +const MARKER_CAT: usize = 1 ;
       +const OUTLINE_CAT: usize = 2;
       +const KEYBIND_CAT: usize = 3 ;
        
        impl SettingsDialog {
       +
       +    pub(crate) fn init(&mut self) {
       +        self.monitor_settings = unsafe { SETTINGS.monitor_settings.clone() };
       +        self.marker_settings = unsafe { SETTINGS.marker_settings.clone() };
       +        self.key_bindings = unsafe { SETTINGS.key_bindings.clone() };
       +    }
            pub fn show(&mut self, ctx: &egui::Context) -> bool {
                let mut open = true;
                let mut dialog_open = true;
       @@ -35,44 +51,55 @@ impl SettingsDialog {
        
                            if ui
                                .selectable_label(
       -                            settings_category == 0,
       +                            settings_category == MONITOR_CAT,
                                    fl!(crate::LANGUAGE_LOADER, "settings-monitor-category"),
                                )
                                .clicked()
                            {
       -                        self.settings_category = 0;
       +                        self.settings_category = MONITOR_CAT;
                            }
        
                            if ui
                                .selectable_label(
       -                            settings_category == 1,
       +                            settings_category == MARKER_CAT,
                                    fl!(crate::LANGUAGE_LOADER, "settings-markers-guides-category"),
                                )
                                .clicked()
                            {
       -                        self.settings_category = 1;
       +                        self.settings_category = MARKER_CAT;
                            }
        
                            if ui
                                .selectable_label(
       -                            settings_category == 2,
       +                            settings_category == OUTLINE_CAT,
                                    fl!(crate::LANGUAGE_LOADER, "settings-font-outline-category"),
                                )
                                .clicked()
                            {
       -                        self.settings_category = 2;
       +                        self.settings_category = OUTLINE_CAT;
       +                    }
       +
       +
       +                    if ui
       +                        .selectable_label(
       +                            settings_category == KEYBIND_CAT,
       +                            fl!(crate::LANGUAGE_LOADER, "settings-keybindings-category"),
       +                        )
       +                        .clicked()
       +                    {
       +                        self.settings_category = KEYBIND_CAT;
                            }
                        });
                        ui.separator();
                        match self.settings_category {
       -                    0 => unsafe {
       +                    MONITOR_CAT => unsafe {
                                if let Some(new_settings) =
                                    show_monitor_settings(ui, &SETTINGS.monitor_settings)
                                {
                                    SETTINGS.monitor_settings = new_settings;
                                }
                            },
       -                    1 => {
       +                    MARKER_CAT => {
                                ui.add_space(8.0);
                                unsafe {
                                    if let Some(new_settings) =
       @@ -83,11 +110,23 @@ impl SettingsDialog {
                                }
                            }
        
       -                    2 => {
       +                    OUTLINE_CAT => {
                                ui.add_space(8.0);
                                self.select_outline_dialog
                                    .show_outline_ui(ui, 4, Vec2::new(8.0, 8.0));
                            }
       +
       +                    KEYBIND_CAT => {
       +                        let mut map = std::collections::HashMap::new();
       +                        for (s, key, modifier) in &self.key_bindings {
       +                            map.insert(s.clone(), (*key, *modifier));
       +                        }
       +                        crate::Commands::show_keybinds_settings(ui, &mut self.key_filter, &mut map);
       +                        self.key_bindings.clear();
       +                        for (s, (key, modifier)) in map {
       +                            self.key_bindings.push((s, key, modifier));
       +                        }
       +                    },
                            _ => {}
                        }
        
       @@ -98,18 +137,37 @@ impl SettingsDialog {
                                .button(fl!(crate::LANGUAGE_LOADER, "new-file-ok"))
                                .clicked()
                            {
       +                        unsafe {
       +                            SETTINGS.key_bindings = self.key_bindings.clone();
       +                        }
                                dialog_open = false;
                            }
        
       -                    if (self.settings_category == 0 || self.settings_category == 1)
       +                    if ui
       +                    .button(fl!(crate::LANGUAGE_LOADER, "new-file-cancel"))
       +                    .clicked()
       +                    {
       +                        unsafe {
       +                            SETTINGS.monitor_settings = self.monitor_settings.clone();
       +                            SETTINGS.marker_settings = self.marker_settings.clone();
       +                            SETTINGS.key_bindings = self.key_bindings.clone();
       +                        }
       +                        dialog_open = false;
       +                    }
       +
       +
       +                    if (self.settings_category == MONITOR_CAT || self.settings_category == 1|| self.settings_category == 3)
                                && ui
                                    .button(fl!(crate::LANGUAGE_LOADER, "settings-reset_button"))
                                    .clicked()
                            {
                                unsafe {
                                    match self.settings_category {
       -                                0 => SETTINGS.monitor_settings = Default::default(),
       -                                1 => SETTINGS.marker_settings = Default::default(),
       +                                MONITOR_CAT => SETTINGS.monitor_settings = Default::default(),
       +                                MARKER_CAT => SETTINGS.marker_settings = Default::default(),
       +                                KEYBIND_CAT => {
       +                                    self.key_bindings = Commands::default_keybindings();
       +                                },
                                        _ => {}
                                    }
                                }
       @@ -119,6 +177,7 @@ impl SettingsDialog {
        
                open && dialog_open
            }
       +
        }
        
        pub fn show_marker_settings(
   DIR diff --git a/src/ui/main_window.rs b/src/ui/main_window.rs
       @@ -11,7 +11,7 @@ use crate::{
            add_child, model::Tool, util::autosave, AnsiEditor, AskCloseFileDialog, BitFontEditor,
            ChannelToolWindow, CharFontEditor, CharTableToolWindow, Commands, Document, DocumentBehavior,
            DocumentTab, LayerToolWindow, Message, MinimapToolWindow, ModalDialog, Settings,
       -    SettingsDialog, ToolBehavior, ToolTab, TopBar,
       +    SettingsDialog, ToolBehavior, ToolTab, TopBar, SETTINGS,
        };
        use directories::UserDirs;
        use eframe::egui::{Button, PointerButton};
       @@ -43,7 +43,7 @@ pub struct MainWindow {
            pub bottom_panel: bool,
        
            pub show_settings: bool,
       -    settings_dialog: SettingsDialog,
       +    pub settings_dialog: SettingsDialog,
            pub commands: Vec<Box<Commands>>,
            pub is_fullscreen: bool,
        
       @@ -191,6 +191,10 @@ impl MainWindow {
        
                tool_tree.root = Some(vert_id);
                let open_file_window = view_library::MainWindow::new(&gl, None);
       +        let mut c = Box::<Commands>::default();
       +        unsafe {
       +            c.apply_key_bindings(&SETTINGS.key_bindings);
       +        }
                MainWindow {
                    document_behavior: DocumentBehavior::new(Arc::new(Mutex::new(tools))),
                    tool_behavior: ToolBehavior::default(),
       @@ -205,7 +209,7 @@ impl MainWindow {
                    right_panel: true,
                    bottom_panel: false,
                    top_bar: TopBar::new(&cc.egui_ctx),
       -            commands: vec![Box::<Commands>::default()],
       +            commands: vec![c],
                    is_closed: false,
                    is_fullscreen: false,
                    in_open_file_mode: false,
       @@ -831,6 +835,11 @@ impl eframe::App for MainWindow {
        
                if self.show_settings {
                    self.show_settings = self.settings_dialog.show(ctx);
       +            if !self.show_settings {
       +                unsafe {
       +                    self.commands[0].apply_key_bindings(&SETTINGS.key_bindings);
       +                }
       +            }
                }
        
                ctx.request_repaint_after(Duration::from_millis(150));
   DIR diff --git a/src/ui/messages.rs b/src/ui/messages.rs
       @@ -1133,6 +1133,7 @@ impl MainWindow {
                    }
                    Message::ShowSettings => {
                        self.show_settings = true;
       +                self.settings_dialog.init();
                    }
                }
            }
   DIR diff --git a/src/ui/settings.rs b/src/ui/settings.rs
       @@ -1,12 +1,12 @@
        use directories::ProjectDirs;
       -use eframe::epaint::Color32;
       +use eframe::{epaint::Color32, egui::Modifiers};
        use icy_engine_egui::{BackgroundEffect, MarkerSettings, MonitorSettings};
        use serde::{Deserialize, Serialize};
        use std::{
            error::Error,
            fs::{self, File},
            io::{self, BufReader, BufWriter},
       -    path::{Path, PathBuf},
       +    path::{Path, PathBuf}, 
        };
        
        use crate::{plugins::Plugin, TerminalResult};
       @@ -24,6 +24,8 @@ pub struct Settings {
            pub monitor_settings: MonitorSettings,
            pub marker_settings: MarkerSettings,
        
       +    pub key_bindings: Vec<(String, eframe::egui::Key, Modifiers)>,
       +
            recent_files: Vec<PathBuf>,
        }
        
       @@ -195,6 +197,7 @@ pub static mut SETTINGS: Settings = Settings {
            show_layer_borders: true,
            show_line_numbers: false,
            recent_files: Vec::new(),
       +    key_bindings: Vec::new(),
            monitor_settings: MonitorSettings {
                use_filter: false,
                monitor_type: 0,