1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
/// Defines a keybinding used for a specific action inside the file dialog.
#[derive(Debug, Clone)]
pub enum KeyBinding {
    /// If a single key should be used as a keybinding
    Key(egui::Key),
    /// If a keyboard shortcut should be used as a keybinding
    KeyboardShortcut(egui::KeyboardShortcut),
    /// If a pointer button should be used as the keybinding
    PointerButton(egui::PointerButton),
    /// If a text event should be used as the keybinding.
    Text(String),
}
impl KeyBinding {
    /// Creates a new keybinding where a single key is used.
    pub fn key(key: egui::Key) -> Self {
        Self::Key(key)
    }
    /// Creates a new keybinding where a keyboard shortcut is used.
    pub fn keyboard_shortcut(modifiers: egui::Modifiers, logical_key: egui::Key) -> Self {
        Self::KeyboardShortcut(egui::KeyboardShortcut {
            modifiers,
            logical_key,
        })
    }
    /// Creates a new keybinding where a pointer button is used.
    pub fn pointer_button(pointer_button: egui::PointerButton) -> Self {
        Self::PointerButton(pointer_button)
    }
    /// Creates a new keybinding where a text event is used.
    pub fn text(text: String) -> Self {
        Self::Text(text)
    }
    /// Checks if the keybinding was pressed by the user.
    ///
    /// # Arguments
    ///
    /// * `ignore_if_any_focused` - Determines whether keyboard shortcuts pressed while another
    ///    widget is currently in focus should be ignored.
    ///    In most cases, this should be enabled so that no shortcuts are executed if,
    ///    for example, the search  text field is currently in focus. With the selection
    ///    keybindings, however, it is desired that when they are pressed, the text fields
    ///    lose focus and the keybinding is executed.
    pub fn pressed(&self, ctx: &egui::Context, ignore_if_any_focused: bool) -> bool {
        let any_focused = ctx.memory(|r| r.focused()).is_some();
        // We want to suppress keyboard input when any other widget like
        // text fields have focus.
        if ignore_if_any_focused && any_focused {
            return false;
        }
        match self {
            KeyBinding::Key(k) => ctx.input(|i| i.key_pressed(*k)),
            KeyBinding::KeyboardShortcut(s) => ctx.input_mut(|i| i.consume_shortcut(s)),
            KeyBinding::PointerButton(b) => ctx.input(|i| i.pointer.button_clicked(*b)),
            KeyBinding::Text(s) => ctx.input_mut(|i| {
                // We force to suppress the text events when any other widget has focus
                if any_focused {
                    return false;
                }
                let mut found_item: Option<usize> = None;
                for (i, text) in i
                    .events
                    .iter()
                    .filter_map(|ev| match ev {
                        egui::Event::Text(t) => Some(t),
                        _ => None,
                    })
                    .enumerate()
                {
                    if text == s {
                        found_item = Some(i);
                        break;
                    }
                }
                if let Some(index) = found_item {
                    i.events.remove(index);
                    return true;
                }
                false
            }),
        }
    }
}
/// Stores the keybindings used for the file dialog.
#[derive(Debug, Clone)]
pub struct FileDialogKeyBindings {
    /// Shortcut to submit the current action or enter the currently selected directory
    pub submit: Vec<KeyBinding>,
    /// Shortcut to cancel the current action
    pub cancel: Vec<KeyBinding>,
    /// Shortcut to open the parent directory
    pub parent: Vec<KeyBinding>,
    /// Shortcut to go back
    pub back: Vec<KeyBinding>,
    /// Shortcut to go forward
    pub forward: Vec<KeyBinding>,
    /// Shortcut to reload the file dialog
    pub reload: Vec<KeyBinding>,
    /// Shortcut to open the dialog to create a new folder
    pub new_folder: Vec<KeyBinding>,
    /// Shortcut to text edit the current path
    pub edit_path: Vec<KeyBinding>,
    /// Shortcut to switch to the home directory and text edit the current path
    pub home_edit_path: Vec<KeyBinding>,
    /// Shortcut to move the selection one item up
    pub selection_up: Vec<KeyBinding>,
    /// Shortcut to move the selection one item down
    pub selection_down: Vec<KeyBinding>,
    /// Shortcut to select every item when the dialog is in `DialogMode::SelectMultiple` mode
    pub select_all: Vec<KeyBinding>,
}
impl FileDialogKeyBindings {
    /// Checks wether any of the given keybindings is pressed.
    pub fn any_pressed(
        ctx: &egui::Context,
        keybindings: &Vec<KeyBinding>,
        suppress_if_any_focused: bool,
    ) -> bool {
        for keybinding in keybindings {
            if keybinding.pressed(ctx, suppress_if_any_focused) {
                return true;
            }
        }
        false
    }
}
impl Default for FileDialogKeyBindings {
    fn default() -> Self {
        use egui::{Key, Modifiers, PointerButton};
        Self {
            submit: vec![KeyBinding::key(Key::Enter)],
            cancel: vec![KeyBinding::key(Key::Escape)],
            parent: vec![KeyBinding::keyboard_shortcut(Modifiers::ALT, Key::ArrowUp)],
            back: vec![
                KeyBinding::pointer_button(PointerButton::Extra1),
                KeyBinding::keyboard_shortcut(Modifiers::ALT, Key::ArrowLeft),
                KeyBinding::key(Key::Backspace),
            ],
            forward: vec![
                KeyBinding::pointer_button(PointerButton::Extra2),
                KeyBinding::keyboard_shortcut(Modifiers::ALT, Key::ArrowRight),
            ],
            reload: vec![KeyBinding::key(egui::Key::F5)],
            new_folder: vec![KeyBinding::keyboard_shortcut(Modifiers::CTRL, Key::N)],
            edit_path: vec![KeyBinding::key(Key::Slash)],
            home_edit_path: vec![
                KeyBinding::keyboard_shortcut(Modifiers::SHIFT, egui::Key::Backtick),
                KeyBinding::text("~".to_string()),
            ],
            selection_up: vec![KeyBinding::key(Key::ArrowUp)],
            selection_down: vec![KeyBinding::key(Key::ArrowDown)],
            select_all: vec![KeyBinding::keyboard_shortcut(Modifiers::CTRL, Key::A)],
        }
    }
}