From 827fdefd8343f0e92f203d7dfafa34cb5cf91cc7 Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Tue, 12 Mar 2024 18:06:08 +0800 Subject: [PATCH] eframe: Added `App::raw_input_hook` allows for the manipulation or filtering of raw input events (#4008) # What's New * eframe: Added `App::raw_input_hook` allows for the manipulation or filtering of raw input events A filter applied to raw input before [`Self::update`] This allows for the manipulation or filtering of input events before they are processed by egui. This can be used to exclude specific keyboard shortcuts, mouse events, etc. Additionally, it can be used to add custom keyboard or mouse events generated by a virtual keyboard. * examples: Added an example to demonstrates how to implement a custom virtual keyboard. [eframe-custom-keypad.webm](https://github.com/emilk/egui/assets/1274171/a9dc8e34-2c35-4172-b7ef-41010b794fb8) --- Cargo.lock | 9 + crates/eframe/src/epi.rs | 18 ++ crates/eframe/src/native/epi_integration.rs | 2 + examples/custom_keypad/Cargo.toml | 23 ++ examples/custom_keypad/README.md | 7 + examples/custom_keypad/screenshot.png | Bin 0 -> 39832 bytes examples/custom_keypad/src/keypad.rs | 255 ++++++++++++++++++++ examples/custom_keypad/src/main.rs | 68 ++++++ 8 files changed, 382 insertions(+) create mode 100644 examples/custom_keypad/Cargo.toml create mode 100644 examples/custom_keypad/README.md create mode 100644 examples/custom_keypad/screenshot.png create mode 100644 examples/custom_keypad/src/keypad.rs create mode 100644 examples/custom_keypad/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 92948c45..ba8d5a17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1072,6 +1072,15 @@ dependencies = [ "env_logger", ] +[[package]] +name = "custom_keypad" +version = "0.1.0" +dependencies = [ + "eframe", + "egui_extras", + "env_logger", +] + [[package]] name = "custom_plot_manipulation" version = "0.1.0" diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index e26bece3..16a8a689 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -196,6 +196,24 @@ pub trait App { fn persist_egui_memory(&self) -> bool { true } + + /// A hook for manipulating or filtering raw input before it is processed by [`Self::update`]. + /// + /// This function provides a way to modify or filter input events before they are processed by egui. + /// + /// It can be used to prevent specific keyboard shortcuts or mouse events from being processed by egui. + /// + /// Additionally, it can be used to inject custom keyboard or mouse events into the input stream, which can be useful for implementing features like a virtual keyboard. + /// + /// # Arguments + /// + /// * `_ctx` - The context of the egui, which provides access to the current state of the egui. + /// * `_raw_input` - The raw input events that are about to be processed. This can be modified to change the input that egui processes. + /// + /// # Note + /// + /// This function does not return a value. Any changes to the input should be made directly to `_raw_input`. + fn raw_input_hook(&mut self, _ctx: &egui::Context, _raw_input: &mut egui::RawInput) {} } /// Selects the level of hardware graphics acceleration. diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index ee54b521..f27d0112 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -274,6 +274,8 @@ impl EpiIntegration { let close_requested = raw_input.viewport().close_requested(); + app.raw_input_hook(&self.egui_ctx, &mut raw_input); + let full_output = self.egui_ctx.run(raw_input, |egui_ctx| { if let Some(viewport_ui_cb) = viewport_ui_cb { // Child viewport diff --git a/examples/custom_keypad/Cargo.toml b/examples/custom_keypad/Cargo.toml new file mode 100644 index 00000000..1557b35c --- /dev/null +++ b/examples/custom_keypad/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "custom_keypad" +version = "0.1.0" +authors = ["Varphone Wong "] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.72" +publish = false + + +[dependencies] +eframe = { workspace = true, features = [ + "default", + "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO +] } + +# For image support: +egui_extras = { workspace = true, features = ["default", "image"] } + +env_logger = { version = "0.10", default-features = false, features = [ + "auto-color", + "humantime", +] } diff --git a/examples/custom_keypad/README.md b/examples/custom_keypad/README.md new file mode 100644 index 00000000..9e5cdf7e --- /dev/null +++ b/examples/custom_keypad/README.md @@ -0,0 +1,7 @@ +Example showing how to implements a custom keypad. + +```sh +cargo run -p custom_keypad +``` + +![](screenshot.png) diff --git a/examples/custom_keypad/screenshot.png b/examples/custom_keypad/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..632459e51aa92fc9e642e0e99e3c3bd4319b4a37 GIT binary patch literal 39832 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

kyz%XJU00yocP@f90*mC$X(2}KC< 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 literal 0 HcmV?d00001 diff --git a/examples/custom_keypad/src/keypad.rs b/examples/custom_keypad/src/keypad.rs new file mode 100644 index 00000000..81800f47 --- /dev/null +++ b/examples/custom_keypad/src/keypad.rs @@ -0,0 +1,255 @@ +use eframe::egui::{self, pos2, vec2, Button, Ui, Vec2}; + +#[derive(Clone, Copy, Debug, Default, PartialEq)] +enum Transition { + #[default] + None, + CloseOnNextFrame, + CloseImmediately, +} + +#[derive(Clone, Debug)] +struct State { + open: bool, + closable: bool, + close_on_next_frame: bool, + start_pos: egui::Pos2, + focus: Option, + events: Option>, +} + +impl State { + fn new() -> Self { + Self { + open: false, + closable: false, + close_on_next_frame: false, + start_pos: pos2(100.0, 100.0), + focus: None, + events: None, + } + } + + fn queue_char(&mut self, c: char) { + let events = self.events.get_or_insert(vec![]); + if let Some(key) = egui::Key::from_name(&c.to_string()) { + events.push(egui::Event::Key { + key, + physical_key: Some(key), + pressed: true, + repeat: false, + modifiers: Default::default(), + }); + } + events.push(egui::Event::Text(c.to_string())); + } + + fn queue_key(&mut self, key: egui::Key) { + let events = self.events.get_or_insert(vec![]); + events.push(egui::Event::Key { + key, + physical_key: Some(key), + pressed: true, + repeat: false, + modifiers: Default::default(), + }); + } +} + +impl Default for State { + fn default() -> Self { + Self::new() + } +} + +/// A simple keypad widget. +pub struct Keypad { + id: egui::Id, +} + +impl Keypad { + pub fn new() -> Self { + Self { + id: egui::Id::new("keypad"), + } + } + + pub fn bump_events(&self, ctx: &egui::Context, raw_input: &mut egui::RawInput) { + let events = ctx.memory_mut(|m| { + m.data + .get_temp_mut_or_default::(self.id) + .events + .take() + }); + if let Some(mut events) = events { + events.append(&mut raw_input.events); + raw_input.events = events; + } + } + + fn buttons(ui: &mut Ui, state: &mut State) -> Transition { + let mut trans = Transition::None; + ui.vertical(|ui| { + let window_margin = ui.spacing().window_margin; + let size_1x1 = vec2(32.0, 26.0); + let _size_1x2 = vec2(32.0, 52.0 + window_margin.top); + let _size_2x1 = vec2(64.0 + window_margin.left, 26.0); + + ui.spacing_mut().item_spacing = Vec2::splat(window_margin.left); + + ui.horizontal(|ui| { + if ui.add_sized(size_1x1, Button::new("1")).clicked() { + state.queue_char('1'); + } + if ui.add_sized(size_1x1, Button::new("2")).clicked() { + state.queue_char('2'); + } + if ui.add_sized(size_1x1, Button::new("3")).clicked() { + state.queue_char('3'); + } + if ui.add_sized(size_1x1, Button::new("⏮")).clicked() { + state.queue_key(egui::Key::Home); + } + if ui.add_sized(size_1x1, Button::new("🔙")).clicked() { + state.queue_key(egui::Key::Backspace); + } + }); + ui.horizontal(|ui| { + if ui.add_sized(size_1x1, Button::new("4")).clicked() { + state.queue_char('4'); + } + if ui.add_sized(size_1x1, Button::new("5")).clicked() { + state.queue_char('5'); + } + if ui.add_sized(size_1x1, Button::new("6")).clicked() { + state.queue_char('6'); + } + if ui.add_sized(size_1x1, Button::new("⏭")).clicked() { + state.queue_key(egui::Key::End); + } + if ui.add_sized(size_1x1, Button::new("⎆")).clicked() { + state.queue_key(egui::Key::Enter); + trans = Transition::CloseOnNextFrame; + } + }); + ui.horizontal(|ui| { + if ui.add_sized(size_1x1, Button::new("7")).clicked() { + state.queue_char('7'); + } + if ui.add_sized(size_1x1, Button::new("8")).clicked() { + state.queue_char('8'); + } + if ui.add_sized(size_1x1, Button::new("9")).clicked() { + state.queue_char('9'); + } + if ui.add_sized(size_1x1, Button::new("⏶")).clicked() { + state.queue_key(egui::Key::ArrowUp); + } + if ui.add_sized(size_1x1, Button::new("⌨")).clicked() { + trans = Transition::CloseImmediately; + } + }); + ui.horizontal(|ui| { + if ui.add_sized(size_1x1, Button::new("0")).clicked() { + state.queue_char('0'); + } + if ui.add_sized(size_1x1, Button::new(".")).clicked() { + state.queue_char('.'); + } + if ui.add_sized(size_1x1, Button::new("⏴")).clicked() { + state.queue_key(egui::Key::ArrowLeft); + } + if ui.add_sized(size_1x1, Button::new("⏷")).clicked() { + state.queue_key(egui::Key::ArrowDown); + } + if ui.add_sized(size_1x1, Button::new("⏵")).clicked() { + state.queue_key(egui::Key::ArrowRight); + } + }); + }); + + trans + } + + pub fn show(&self, ctx: &egui::Context) { + let (focus, mut state) = ctx.memory(|m| { + ( + m.focus(), + m.data.get_temp::(self.id).unwrap_or_default(), + ) + }); + + let mut is_first_show = false; + if ctx.wants_keyboard_input() && state.focus != focus { + let y = ctx.style().spacing.interact_size.y * 1.25; + state.open = true; + state.start_pos = ctx.input(|i| { + i.pointer + .hover_pos() + .map_or(pos2(100.0, 100.0), |p| p + vec2(0.0, y)) + }); + state.focus = focus; + is_first_show = true; + } + + if state.close_on_next_frame { + state.open = false; + state.close_on_next_frame = false; + state.focus = None; + } + + let mut open = state.open; + + let win = egui::Window::new("⌨ Keypad"); + let win = if is_first_show { + win.current_pos(state.start_pos) + } else { + win.default_pos(state.start_pos) + }; + let resp = win + .movable(true) + .resizable(false) + .open(&mut open) + .show(ctx, |ui| Self::buttons(ui, &mut state)); + + state.open = open; + + if let Some(resp) = resp { + match resp.inner { + Some(Transition::CloseOnNextFrame) => { + state.close_on_next_frame = true; + } + Some(Transition::CloseImmediately) => { + state.open = false; + state.focus = None; + } + _ => {} + } + if !state.closable && resp.response.hovered() { + state.closable = true; + } + if state.closable && resp.response.clicked_elsewhere() { + state.open = false; + state.closable = false; + state.focus = None; + } + if is_first_show { + ctx.move_to_top(resp.response.layer_id); + } + } + + if let (true, Some(focus)) = (state.open, state.focus) { + ctx.memory_mut(|m| { + m.request_focus(focus); + }); + } + + ctx.memory_mut(|m| m.data.insert_temp(self.id, state)); + } +} + +impl Default for Keypad { + fn default() -> Self { + Self::new() + } +} diff --git a/examples/custom_keypad/src/main.rs b/examples/custom_keypad/src/main.rs new file mode 100644 index 00000000..5cb26240 --- /dev/null +++ b/examples/custom_keypad/src/main.rs @@ -0,0 +1,68 @@ +// #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release +use eframe::egui; + +mod keypad; +use keypad::Keypad; + +fn main() -> Result<(), eframe::Error> { + env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). + let options = eframe::NativeOptions { + viewport: egui::ViewportBuilder::default().with_inner_size([640.0, 480.0]), + ..Default::default() + }; + eframe::run_native( + "Custom Keypad App", + options, + Box::new(|cc| { + // Use the dark theme + cc.egui_ctx.set_visuals(egui::Visuals::dark()); + // This gives us image support: + egui_extras::install_image_loaders(&cc.egui_ctx); + + Box::::default() + }), + ) +} + +struct MyApp { + name: String, + age: u32, + keypad: Keypad, +} + +impl MyApp {} + +impl Default for MyApp { + fn default() -> Self { + Self { + name: "Arthur".to_owned(), + age: 42, + keypad: Keypad::new(), + } + } +} + +impl eframe::App for MyApp { + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + egui::Window::new("Custom Keypad") + .default_pos([100.0, 100.0]) + .title_bar(true) + .show(ctx, |ui| { + ui.horizontal(|ui| { + ui.label("Your name: "); + ui.text_edit_singleline(&mut self.name); + }); + ui.add(egui::Slider::new(&mut self.age, 0..=120).text("age")); + if ui.button("Increment").clicked() { + self.age += 1; + } + ui.label(format!("Hello '{}', age {}", self.name, self.age)); + }); + + self.keypad.show(ctx); + } + + fn raw_input_hook(&mut self, ctx: &egui::Context, raw_input: &mut egui::RawInput) { + self.keypad.bump_events(ctx, raw_input); + } +}