Skip to content

Commit

Permalink
Add Ui.input_mut & InputState.ignore_key (#1212)
Browse files Browse the repository at this point in the history
  • Loading branch information
cat-state committed Feb 15, 2022
1 parent c8c871f commit c1569ed
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 48 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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`.
Expand Down
36 changes: 36 additions & 0 deletions egui/src/data/input.rs
Expand Up @@ -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()
Expand Down
18 changes: 18 additions & 0 deletions egui/src/input_state.rs
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions egui/src/ui.rs
Expand Up @@ -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]
Expand Down
64 changes: 16 additions & 48 deletions egui_demo_lib/src/easy_mark/easy_mark_editor.rs
Expand Up @@ -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,
Expand Down

0 comments on commit c1569ed

Please sign in to comment.