Browse Source

Clear all keys and modifies on focus change (#2933)

It is very easy for keys to become stuck when we alt-tab,
or a save-dialog opens by Ctrl+S, etc.
Therefore we new clear all the modifiers and down keys to avoid that.
pull/2934/head
Emil Ernerfeldt 2 years ago
committed by GitHub
parent
commit
d1af798a9b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      crates/eframe/src/epi.rs
  2. 5
      crates/eframe/src/web/backend.rs
  3. 9
      crates/egui-winit/src/lib.rs
  4. 2
      crates/egui/src/context.rs
  5. 21
      crates/egui/src/data/input.rs
  6. 25
      crates/egui/src/input_state.rs
  7. 2
      crates/egui/src/response.rs

2
crates/eframe/src/epi.rs

@ -927,6 +927,8 @@ pub struct WindowInfo {
pub maximized: bool,
/// Is the window focused and able to receive input?
///
/// This should be the same as [`egui::InputState::focused`].
pub focused: bool,
/// Window inner size in egui points (logical pixels).

5
crates/eframe/src/web/backend.rs

@ -31,9 +31,10 @@ impl WebInput {
}
}
pub fn on_web_page_focus_change(&mut self, has_focus: bool) {
pub fn on_web_page_focus_change(&mut self, focused: bool) {
self.raw.modifiers = egui::Modifiers::default();
self.raw.has_focus = has_focus;
self.raw.focused = focused;
self.raw.events.push(egui::Event::WindowFocused(focused));
self.latest_touch_pos = None;
self.latest_touch_pos_id = None;
}

9
crates/egui-winit/src/lib.rs

@ -90,7 +90,7 @@ impl State {
/// The returned `State` must not outlive the input `display_target`.
pub fn new(display_target: &dyn HasRawDisplayHandle) -> Self {
let egui_input = egui::RawInput {
has_focus: false, // winit will tell us when we have focus
focused: false, // winit will tell us when we have focus
..Default::default()
};
@ -314,11 +314,14 @@ impl State {
consumed,
}
}
WindowEvent::Focused(has_focus) => {
self.egui_input.has_focus = *has_focus;
WindowEvent::Focused(focused) => {
self.egui_input.focused = *focused;
// We will not be given a KeyboardInput event when the modifiers are released while
// the window does not have focus. Unset all modifier state to be safe.
self.egui_input.modifiers = egui::Modifiers::default();
self.egui_input
.events
.push(egui::Event::WindowFocused(*focused));
EventResponse {
repaint: true,
consumed: false,

2
crates/egui/src/context.rs

@ -1139,7 +1139,7 @@ impl Context {
{
let state = self.frame_state_mut(|fs| fs.accesskit_state.take());
if let Some(state) = state {
let has_focus = self.input(|i| i.raw.has_focus);
let has_focus = self.input(|i| i.raw.focused);
let root_id = crate::accesskit_root_id().accesskit_id();
let nodes = self.write(|ctx| {
state

21
crates/egui/src/data/input.rs

@ -63,8 +63,10 @@ pub struct RawInput {
/// drag-and-drop support using `eframe::NativeOptions`.
pub dropped_files: Vec<DroppedFile>,
/// The window has the keyboard focus (i.e. is receiving key presses).
pub has_focus: bool,
/// The native window has the keyboard focus (i.e. is receiving key presses).
///
/// False when the user alt-tab away from the application, for instance.
pub focused: bool,
}
impl Default for RawInput {
@ -79,7 +81,7 @@ impl Default for RawInput {
events: vec![],
hovered_files: Default::default(),
dropped_files: Default::default(),
has_focus: true, // integrations opt into global focus tracking
focused: true, // integrations opt into global focus tracking
}
}
}
@ -100,7 +102,7 @@ impl RawInput {
events: std::mem::take(&mut self.events),
hovered_files: self.hovered_files.clone(),
dropped_files: std::mem::take(&mut self.dropped_files),
has_focus: self.has_focus,
focused: self.focused,
}
}
@ -116,7 +118,7 @@ impl RawInput {
mut events,
mut hovered_files,
mut dropped_files,
has_focus,
focused,
} = newer;
self.screen_rect = screen_rect.or(self.screen_rect);
@ -128,7 +130,7 @@ impl RawInput {
self.events.append(&mut events);
self.hovered_files.append(&mut hovered_files);
self.dropped_files.append(&mut dropped_files);
self.has_focus = has_focus;
self.focused = focused;
}
}
@ -294,6 +296,9 @@ pub enum Event {
modifiers: Modifiers,
},
/// The native window gained or lost focused (e.g. the user clicked alt-tab).
WindowFocused(bool),
/// An assistive technology (e.g. screen reader) requested an action.
#[cfg(feature = "accesskit")]
AccessKitActionRequest(accesskit::ActionRequest),
@ -847,7 +852,7 @@ impl RawInput {
events,
hovered_files,
dropped_files,
has_focus,
focused,
} = self;
ui.label(format!("screen_rect: {:?} points", screen_rect));
@ -865,7 +870,7 @@ impl RawInput {
ui.label(format!("modifiers: {:#?}", modifiers));
ui.label(format!("hovered_files: {}", hovered_files.len()));
ui.label(format!("dropped_files: {}", dropped_files.len()));
ui.label(format!("has_focus: {}", has_focus));
ui.label(format!("focused: {}", focused));
ui.scope(|ui| {
ui.set_min_height(150.0);
ui.label(format!("events: {:#?}", events))

25
crates/egui/src/input_state.rs

@ -104,6 +104,11 @@ pub struct InputState {
/// and will effectively slow down the animation when FPS drops below 10.
pub stable_dt: f32,
/// The native window has the keyboard focus (i.e. is receiving key presses).
///
/// False when the user alt-tab away from the application, for instance.
pub focused: bool,
/// Which modifier keys are down at the start of the frame?
pub modifiers: Modifiers,
@ -129,6 +134,7 @@ impl Default for InputState {
unstable_dt: 1.0 / 60.0,
predicted_dt: 1.0 / 60.0,
stable_dt: 1.0 / 60.0,
focused: false,
modifiers: Default::default(),
keys_down: Default::default(),
events: Default::default(),
@ -189,6 +195,20 @@ impl InputState {
}
}
let mut modifiers = new.modifiers;
let focused_changed = self.focused != new.focused
|| new
.events
.iter()
.any(|e| matches!(e, Event::WindowFocused(_)));
if focused_changed {
// It is very common for keys to become stuck when we alt-tab, or a save-dialog opens by Ctrl+S.
// Therefore we clear all the modifiers and down keys here to avoid that.
modifiers = Default::default();
keys_down = Default::default();
}
InputState {
pointer,
touch_states: self.touch_states,
@ -201,7 +221,8 @@ impl InputState {
unstable_dt,
predicted_dt: new.predicted_dt,
stable_dt,
modifiers: new.modifiers,
focused: new.focused,
modifiers,
keys_down,
events: new.events.clone(), // TODO(emilk): remove clone() and use raw.events
raw: new,
@ -926,6 +947,7 @@ impl InputState {
unstable_dt,
predicted_dt,
stable_dt,
focused,
modifiers,
keys_down,
events,
@ -969,6 +991,7 @@ impl InputState {
));
ui.label(format!("predicted_dt: {:.1} ms", 1e3 * predicted_dt));
ui.label(format!("stable_dt: {:.1} ms", 1e3 * stable_dt));
ui.label(format!("focused: {}", focused));
ui.label(format!("modifiers: {:#?}", modifiers));
ui.label(format!("keys_down: {:?}", keys_down));
ui.scope(|ui| {

2
crates/egui/src/response.rs

@ -233,7 +233,7 @@ impl Response {
/// also has the keyboard focus. That makes this function suitable
/// for style choices, e.g. a thicker border around focused widgets.
pub fn has_focus(&self) -> bool {
self.ctx.input(|i| i.raw.has_focus) && self.ctx.memory(|mem| mem.has_focus(self.id))
self.ctx.input(|i| i.focused) && self.ctx.memory(|mem| mem.has_focus(self.id))
}
/// True if this widget has keyboard focus this frame, but didn't last frame.

Loading…
Cancel
Save