From 85f8eeb9d5ca4be14c99740ac31052a9d246c773 Mon Sep 17 00:00:00 2001 From: JP Date: Wed, 30 Nov 2022 19:40:11 +0000 Subject: [PATCH] Fix key pressed event (#2334) * Fix key press event * Add example with key presses * Changelog line for key_press fix * PR review improvements * Add PR link in changelog Co-authored-by: Emil Ernerfeldt --- CHANGELOG.md | 1 + Cargo.lock | 8 +++++ crates/egui/src/input_state.rs | 42 +++++++++++++----------- examples/keyboard_events/Cargo.toml | 13 ++++++++ examples/keyboard_events/README.md | 3 ++ examples/keyboard_events/src/main.rs | 48 ++++++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 18 deletions(-) create mode 100644 examples/keyboard_events/Cargo.toml create mode 100644 examples/keyboard_events/README.md create mode 100644 examples/keyboard_events/src/main.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d3ebfc9b63a..8f5f34020b46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG * MSRV (Minimum Supported Rust Version) is now `1.65.0` ([#2314](https://github.com/emilk/egui/pull/2314)). * ⚠️ BREAKING: egui now expects integrations to do all color blending in gamma space ([#2071](https://github.com/emilk/egui/pull/2071)). * ⚠️ BREAKING: if you have overlapping interactive widgets, only the top widget (last added) will be interactive ([#2244](https://github.com/emilk/egui/pull/2244)). +* Keyboard press events are only present at the frame when the key was pressed, consistent with how key releases work ([#2334](https://github.com/emilk/egui/pull/2334)). ### Added ⭐ * Added helper functions for animating panels that collapse/expand ([#2190](https://github.com/emilk/egui/pull/2190)). diff --git a/Cargo.lock b/Cargo.lock index a4c7b627bd25..a822a3e56c1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2070,6 +2070,14 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keyboard_events" +version = "0.1.0" +dependencies = [ + "eframe", + "tracing-subscriber", +] + [[package]] name = "khronos-egl" version = "4.1.0" diff --git a/crates/egui/src/input_state.rs b/crates/egui/src/input_state.rs index ab0d321df366..b5938612ba63 100644 --- a/crates/egui/src/input_state.rs +++ b/crates/egui/src/input_state.rs @@ -138,7 +138,11 @@ impl Default for InputState { impl InputState { #[must_use] - pub fn begin_frame(mut self, new: RawInput, requested_repaint_last_frame: bool) -> InputState { + pub fn begin_frame( + mut self, + mut new: RawInput, + requested_repaint_last_frame: bool, + ) -> InputState { let time = new.time.unwrap_or(self.time + new.predicted_dt as f64); let unstable_dt = (time - self.time) as f32; @@ -160,24 +164,26 @@ impl InputState { let mut keys_down = self.keys_down; let mut scroll_delta = Vec2::ZERO; let mut zoom_factor_delta = 1.0; - for event in &new.events { - match event { - Event::Key { key, pressed, .. } => { - if *pressed { - keys_down.insert(*key); - } else { - keys_down.remove(key); - } - } - Event::Scroll(delta) => { - scroll_delta += *delta; - } - Event::Zoom(factor) => { - zoom_factor_delta *= *factor; + new.events.retain(|event| match event { + Event::Key { key, pressed, .. } => { + if *pressed { + // We only retain presses that are novel (i.e. the first Press event, not those generated by key-repeat) + keys_down.insert(*key) + } else { + keys_down.remove(key); + true } - _ => {} } - } + Event::Scroll(delta) => { + scroll_delta += *delta; + true + } + Event::Zoom(factor) => { + zoom_factor_delta *= *factor; + true + } + _ => true, + }); InputState { pointer, touch_states: self.touch_states, @@ -285,7 +291,7 @@ impl InputState { self.num_presses(desired_key) > 0 } - /// How many times were the given key pressed this frame? + /// How many times was the given key pressed this frame? pub fn num_presses(&self, desired_key: Key) -> usize { self.events .iter() diff --git a/examples/keyboard_events/Cargo.toml b/examples/keyboard_events/Cargo.toml new file mode 100644 index 000000000000..f22309427a33 --- /dev/null +++ b/examples/keyboard_events/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "keyboard_events" +version = "0.1.0" +authors = ["Jose Palazon "] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.65" +publish = false + + +[dependencies] +eframe = { path = "../../crates/eframe" } +tracing-subscriber = "0.3" diff --git a/examples/keyboard_events/README.md b/examples/keyboard_events/README.md new file mode 100644 index 000000000000..d528b1bc60c9 --- /dev/null +++ b/examples/keyboard_events/README.md @@ -0,0 +1,3 @@ +```sh +cargo run -p hello_world +``` diff --git a/examples/keyboard_events/src/main.rs b/examples/keyboard_events/src/main.rs new file mode 100644 index 000000000000..ab1e8ec86cb0 --- /dev/null +++ b/examples/keyboard_events/src/main.rs @@ -0,0 +1,48 @@ +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release + +use eframe::egui; +use egui::*; +fn main() { + // Log to stdout (if you run with `RUST_LOG=debug`). + tracing_subscriber::fmt::init(); + + let options = eframe::NativeOptions::default(); + eframe::run_native( + "Keyboard events", + options, + Box::new(|_cc| Box::new(Content::default())), + ); +} + +#[derive(Default)] +struct Content { + text: String, +} + +impl eframe::App for Content { + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + egui::CentralPanel::default().show(ctx, |ui| { + ui.heading("Press/Hold/Release example. Press A to test."); + if ui.button("Clear").clicked() { + self.text.clear(); + } + ScrollArea::vertical() + .auto_shrink([false; 2]) + .stick_to_bottom(true) + .show(ui, |ui| { + ui.label(&self.text); + }); + + if ctx.input().key_pressed(Key::A) { + self.text.push_str("\nPressed"); + } + if ctx.input().key_down(Key::A) { + self.text.push_str("\nHeld"); + ui.ctx().request_repaint(); // make sure we note the holding. + } + if ctx.input().key_released(Key::A) { + self.text.push_str("\nReleased"); + } + }); + } +}