egui_file_dialog/config/
keybindings.rs

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