From c6c6d2dc5de622441b014f99975b3bd204d1b873 Mon Sep 17 00:00:00 2001 From: Matt Campbell Date: Fri, 29 Jul 2022 06:15:26 -0500 Subject: [PATCH] Track the global focus state of the UI (#1859) * Track the global focus state of the UI * Fix changelog entries * Document the new difference between `Response::has_focus` and `Memory::has_focus` --- CHANGELOG.md | 1 + egui-winit/CHANGELOG.md | 1 + egui-winit/src/lib.rs | 10 ++++++++-- egui/src/data/input.rs | 9 +++++++++ egui/src/memory.rs | 5 +++++ egui/src/response.rs | 8 +++++++- 6 files changed, 31 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 139f44a8f..54febf60f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui-w * Added support for using `PaintCallback` shapes with the WGPU backend ([#1684](https://github.com/emilk/egui/pull/1684)). * Added `Contex::request_repaint_after` ([#1694](https://github.com/emilk/egui/pull/1694)). * `ctrl-h` now acts like backspace in `TextEdit` ([#1812](https://github.com/emilk/egui/pull/1812)). +* Added `RawInput::has_focus` which backends can set to indicate whether the UI as a whole has the keyboard focus ([#1859](https://github.com/emilk/egui/pull/1859)). ### Changed * MSRV (Minimum Supported Rust Version) is now `1.61.0` ([#1846](https://github.com/emilk/egui/pull/1846)). diff --git a/egui-winit/CHANGELOG.md b/egui-winit/CHANGELOG.md index 046453d78..97a6b7029 100644 --- a/egui-winit/CHANGELOG.md +++ b/egui-winit/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to the `egui-winit` integration will be noted in this file. * Allow deferred render + surface state initialization for Android ([#1634](https://github.com/emilk/egui/pull/1634)). * Fixed window position persistence ([#1745](https://github.com/emilk/egui/pull/1745)). * Fixed mouse cursor change on Linux ([#1747](https://github.com/emilk/egui/pull/1747)). +* Use the new `RawInput::has_focus` field to indicate whether the window has the keyboard focus ([#1859](https://github.com/emilk/egui/pull/1859)). ## 0.18.0 - 2022-04-30 diff --git a/egui-winit/src/lib.rs b/egui-winit/src/lib.rs index dceb287b7..d50374e39 100644 --- a/egui-winit/src/lib.rs +++ b/egui-winit/src/lib.rs @@ -70,9 +70,14 @@ impl State { } pub fn new_with_wayland_display(wayland_display: Option<*mut c_void>) -> Self { + let egui_input = egui::RawInput { + has_focus: false, // winit will tell us when we have focus + ..Default::default() + }; + Self { start_time: instant::Instant::now(), - egui_input: Default::default(), + egui_input, pointer_pos_in_points: None, any_pointer_button_down: false, current_cursor_icon: egui::CursorIcon::Default, @@ -214,7 +219,8 @@ impl State { egui_ctx.wants_keyboard_input() || input.virtual_keycode == Some(winit::event::VirtualKeyCode::Tab) } - WindowEvent::Focused(_) => { + WindowEvent::Focused(has_focus) => { + self.egui_input.has_focus = *has_focus; // 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(); diff --git a/egui/src/data/input.rs b/egui/src/data/input.rs index 6bd28d4d5..01aa1c6c1 100644 --- a/egui/src/data/input.rs +++ b/egui/src/data/input.rs @@ -62,6 +62,9 @@ pub struct RawInput { /// Note: when using `eframe` on Windows you need to enable /// drag-and-drop support using `eframe::NativeOptions`. pub dropped_files: Vec, + + /// The window has the keyboard focus (i.e. is receiving key presses). + pub has_focus: bool, } impl Default for RawInput { @@ -76,6 +79,7 @@ impl Default for RawInput { events: vec![], hovered_files: Default::default(), dropped_files: Default::default(), + has_focus: true, // integrations opt into global focus tracking } } } @@ -96,6 +100,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, } } @@ -111,6 +116,7 @@ impl RawInput { mut events, mut hovered_files, mut dropped_files, + has_focus, } = newer; self.screen_rect = screen_rect.or(self.screen_rect); @@ -122,6 +128,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; } } @@ -541,6 +548,7 @@ impl RawInput { events, hovered_files, dropped_files, + has_focus, } = self; ui.label(format!("screen_rect: {:?} points", screen_rect)); @@ -558,6 +566,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.scope(|ui| { ui.set_min_height(150.0); ui.label(format!("events: {:#?}", events)) diff --git a/egui/src/memory.rs b/egui/src/memory.rs index 4286a1601..4241a932f 100644 --- a/egui/src/memory.rs +++ b/egui/src/memory.rs @@ -350,6 +350,11 @@ impl Memory { } /// Does this widget have keyboard focus? + /// + /// This function does not consider whether the UI as a whole (e.g. window) + /// has the keyboard focus. That makes this function suitable for deciding + /// widget state that should not be disrupted if the user moves away + /// from the window and back. #[inline(always)] pub fn has_focus(&self, id: Id) -> bool { self.interaction.focus.id == Some(id) diff --git a/egui/src/response.rs b/egui/src/response.rs index 8edd48ba8..591adbd08 100644 --- a/egui/src/response.rs +++ b/egui/src/response.rs @@ -212,8 +212,14 @@ impl Response { } /// This widget has the keyboard focus (i.e. is receiving key presses). + /// + /// This function only returns true if the UI as a whole (e.g. window) + /// 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.memory().has_focus(self.id) + // Access input and memory in separate statements to prevent deadlock. + let has_global_focus = self.ctx.input().raw.has_focus; + has_global_focus && self.ctx.memory().has_focus(self.id) } /// True if this widget has keyboard focus this frame, but didn't last frame.