From d2717180656d640e606a46932718dbef59fd5ef7 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 1 Jul 2024 10:38:54 +0200 Subject: [PATCH] Fix focus problems on web (#4745) * Closes https://github.com/emilk/egui/issues/4743 * Related to https://github.com/emilk/egui/issues/4569 --- crates/eframe/src/web/app_runner.rs | 40 +++++++++++++++++++++++++---- crates/eframe/src/web/events.rs | 13 ++-------- crates/eframe/src/web/text_agent.rs | 8 ++++-- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/crates/eframe/src/web/app_runner.rs b/crates/eframe/src/web/app_runner.rs index 4b4aad45fe9..e8af27d76be 100644 --- a/crates/eframe/src/web/app_runner.rs +++ b/crates/eframe/src/web/app_runner.rs @@ -181,13 +181,33 @@ impl AppRunner { self.clipped_primitives.is_some() } + /// Does the eframe app have focus? + /// + /// Technically: does either the canvas or the [`TextAgent`] have focus? + pub fn has_focus(&self) -> bool { + super::has_focus(self.canvas()) || self.text_agent.has_focus() + } + + pub fn update_focus(&mut self) { + let has_focus = self.has_focus(); + if self.input.raw.focused != has_focus { + // log::debug!("{} Focus changed to {has_focus}", self.canvas().id()); + self.input.set_focus(has_focus); + + if !has_focus { + // We lost focus - good idea to save + self.save(); + } + self.egui_ctx().request_repaint(); + } + } + /// Runs the logic, but doesn't paint the result. /// /// The result can be painted later with a call to [`Self::run_and_paint`] or [`Self::paint`]. pub fn logic(&mut self) { // We sometimes miss blur/focus events due to the text agent, so let's just poll each frame: - self.input - .set_focus(super::has_focus(self.canvas()) || self.text_agent.has_focus()); + self.update_focus(); let canvas_size = super::canvas_size_in_points(self.canvas(), self.egui_ctx()); let mut raw_input = self.input.new_frame(canvas_size); @@ -253,8 +273,8 @@ impl AppRunner { cursor_icon, open_url, copied_text, - events: _, // already handled - mutable_text_under_cursor, + events: _, // already handled + mutable_text_under_cursor: _, // TODO(#4569): https://github.com/emilk/egui/issues/4569 ime, #[cfg(feature = "accesskit")] accesskit_update: _, // not currently implemented @@ -273,7 +293,17 @@ impl AppRunner { #[cfg(not(web_sys_unstable_apis))] let _ = copied_text; - self.text_agent.set_focus(mutable_text_under_cursor); + if self.has_focus() { + // The eframe app has focus. + if ime.is_some() { + // We are editing text: give the focus to the text agent. + self.text_agent.focus(); + } else { + // We are not editing text - give the focus to the canvas. + self.text_agent.blur(); + self.canvas().focus().ok(); + } + } if let Err(err) = self.text_agent.move_to(ime, self.canvas()) { log::error!( diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index a3ea11d3514..befe6e3d850 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -103,17 +103,8 @@ fn install_blur_focus(runner_ref: &WebRunner, target: &EventTarget) -> Result<() // so we also poll the focus state each frame in `AppRunner::logic`. for event_name in ["blur", "focus"] { let closure = move |_event: web_sys::MouseEvent, runner: &mut AppRunner| { - // log::debug!("{event_name:?}"); - - let has_focus = event_name == "focus"; - - if !has_focus { - // We lost focus - good idea to save - runner.save(); - } - - runner.input.set_focus(has_focus); - runner.egui_ctx().request_repaint(); + // log::debug!("{} {event_name:?}", runner.canvas().id()); + runner.update_focus(); }; runner_ref.add_event_listener(target, event_name, closure)?; diff --git a/crates/eframe/src/web/text_agent.rs b/crates/eframe/src/web/text_agent.rs index 876a50e0a95..5ced0647b02 100644 --- a/crates/eframe/src/web/text_agent.rs +++ b/crates/eframe/src/web/text_agent.rs @@ -142,21 +142,25 @@ impl TextAgent { super::has_focus(&self.input) } - fn focus(&self) { + pub fn focus(&self) { if self.has_focus() { return; } + // log::debug!("Focusing text agent"); + if let Err(err) = self.input.focus() { log::error!("failed to set focus: {}", super::string_from_js_value(&err)); }; } - fn blur(&self) { + pub fn blur(&self) { if !self.has_focus() { return; } + // log::debug!("Blurring text agent"); + if let Err(err) = self.input.blur() { log::error!("failed to set focus: {}", super::string_from_js_value(&err)); };