diff --git a/CHANGELOG.md b/CHANGELOG.md index 713a2011fda..e5d18ad37c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w ## Unreleased ### Added ⭐ +* `Ui::input_mut` to modify how subsequent widgets see the `InputState` and a convenience method `InputState::consume_key` for shortcuts or hotkeys ([#1212](https://github.com/emilk/egui/pull/1212)). * Much improved font selection ([#1154](https://github.com/emilk/egui/pull/1154)): * You can now select any font size and family using `RichText::size` amd `RichText::family` and the new `FontId`. * Easily change text styles with `Style::text_styles`. diff --git a/egui/src/data/input.rs b/egui/src/data/input.rs index 21234b650ef..29a3458c6bd 100644 --- a/egui/src/data/input.rs +++ b/egui/src/data/input.rs @@ -264,6 +264,42 @@ pub struct Modifiers { } impl Modifiers { + pub fn new() -> Self { + Default::default() + } + + pub fn alt(self, value: bool) -> Self { + Self { alt: value, ..self } + } + + pub fn ctrl(self, value: bool) -> Self { + Self { + ctrl: value, + ..self + } + } + + pub fn shift(self, value: bool) -> Self { + Self { + shift: value, + ..self + } + } + + pub fn mac_cmd(self, value: bool) -> Self { + Self { + mac_cmd: value, + ..self + } + } + + pub fn command(self, value: bool) -> Self { + Self { + command: value, + ..self + } + } + #[inline(always)] pub fn is_none(&self) -> bool { self == &Self::default() diff --git a/egui/src/input_state.rs b/egui/src/input_state.rs index 6395b54fce7..ef516bb7388 100644 --- a/egui/src/input_state.rs +++ b/egui/src/input_state.rs @@ -192,6 +192,24 @@ impl InputState { self.pointer.wants_repaint() || self.scroll_delta != Vec2::ZERO || !self.events.is_empty() } + /// Ignore a key if it was pressed or released this frame. Useful for hotkeys. + /// Matches on both key press and key release, consuming them and removing them from `self.events`. + /// Returns true if the key was pressed this frame (even if the key release was consumed). + pub fn consume_key(&mut self, modifiers: Modifiers, key: Key) -> bool { + self.events.retain(|event| { + !matches!( + event, + Event::Key { + key: ev_key, + modifiers: ev_mods, + .. + } if *ev_key == key && *ev_mods == modifiers + ) + }); + + self.keys_down.remove(&key) + } + /// Was the given key pressed this frame? pub fn key_pressed(&self, desired_key: Key) -> bool { self.num_presses(desired_key) > 0 diff --git a/egui/src/ui.rs b/egui/src/ui.rs index b5a04c8fc24..930751a183e 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -338,6 +338,21 @@ impl Ui { self.ctx().input() } + /// The [`InputState`] of the [`Context`] associated with this [`Ui`]. + /// Equivalent to `.ctx().input_mut()`. + /// + /// Note that this locks the [`Context`], so be careful with if-let bindings + /// like for [`Self::input()`]. + /// ``` + /// # egui::__run_test_ui(|ui| { + /// ui.input_mut().consume_key(egui::Modifiers::default(), egui::Key::Enter); + /// # }); + /// ``` + #[inline] + pub fn input_mut(&self) -> RwLockWriteGuard<'_, InputState> { + self.ctx().input_mut() + } + /// The [`Memory`] of the [`Context`] associated with this ui. /// Equivalent to `.ctx().memory()`. #[inline] diff --git a/egui_demo_lib/src/easy_mark/easy_mark_editor.rs b/egui_demo_lib/src/easy_mark/easy_mark_editor.rs index aa1468d9f37..5a82f3ce105 100644 --- a/egui_demo_lib/src/easy_mark/easy_mark_editor.rs +++ b/egui_demo_lib/src/easy_mark/easy_mark_editor.rs @@ -117,59 +117,27 @@ impl EasyMarkEditor { fn shortcuts(ui: &Ui, code: &mut dyn TextBuffer, ccursor_range: &mut CCursorRange) -> bool { let mut any_change = false; - for event in &ui.input().events { - if let Event::Key { - key, - pressed: true, - modifiers, - } = event + for (key, surrounding) in [ + (Key::B, "*"), // *bold* + (Key::C, "`"), // `code` + (Key::I, "/"), // /italics/ + (Key::L, "$"), // $subscript$ + (Key::R, "^"), // ^superscript^ + (Key::S, "~"), // ~strikethrough~ + (Key::U, "_"), // _underline_ + ] { + if ui + .input_mut() + .consume_key(egui::Modifiers::new().command(true), key) { - if modifiers.command_only() { - match &key { - // toggle *bold* - Key::B => { - toggle_surrounding(code, ccursor_range, "*"); - any_change = true; - } - // toggle `code` - Key::C => { - toggle_surrounding(code, ccursor_range, "`"); - any_change = true; - } - // toggle /italics/ - Key::I => { - toggle_surrounding(code, ccursor_range, "/"); - any_change = true; - } - // toggle $lowered$ - Key::L => { - toggle_surrounding(code, ccursor_range, "$"); - any_change = true; - } - // toggle ^raised^ - Key::R => { - toggle_surrounding(code, ccursor_range, "^"); - any_change = true; - } - // toggle ~strikethrough~ - Key::S => { - toggle_surrounding(code, ccursor_range, "~"); - any_change = true; - } - // toggle _underline_ - Key::U => { - toggle_surrounding(code, ccursor_range, "_"); - any_change = true; - } - _ => {} - } - } - } + toggle_surrounding(code, ccursor_range, surrounding); + any_change = true; + }; } any_change } -/// E.g. toggle *strong* with `toggle(&mut text, &mut cursor, "*")` +/// E.g. toggle *strong* with `toggle_surrounding(&mut text, &mut cursor, "*")` fn toggle_surrounding( code: &mut dyn TextBuffer, ccursor_range: &mut CCursorRange,