From 6d2c9845c2568b591b1e48f83d512a1e6f3a622b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 1 Jul 2024 09:33:00 +0200 Subject: [PATCH] Fix focus problems on web --- crates/eframe/src/web/app_runner.rs | 40 +++++++++++++++++++++++++---- crates/eframe/src/web/events.rs | 12 +-------- crates/eframe/src/web/text_agent.rs | 4 +-- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/crates/eframe/src/web/app_runner.rs b/crates/eframe/src/web/app_runner.rs index 4b4aad45fe97..86bc0228644d 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 a3ea11d35146..361fc10ab3ac 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -103,17 +103,7 @@ 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(); + 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 876a50e0a959..8df53c4d0910 100644 --- a/crates/eframe/src/web/text_agent.rs +++ b/crates/eframe/src/web/text_agent.rs @@ -142,7 +142,7 @@ impl TextAgent { super::has_focus(&self.input) } - fn focus(&self) { + pub fn focus(&self) { if self.has_focus() { return; } @@ -152,7 +152,7 @@ impl TextAgent { }; } - fn blur(&self) { + pub fn blur(&self) { if !self.has_focus() { return; }