Skip to content

Commit

Permalink
Fix focus problems on web (emilk#4745)
Browse files Browse the repository at this point in the history
* Closes emilk#4743
* Related to emilk#4569
  • Loading branch information
emilk authored and hacknus committed Oct 30, 2024
1 parent 56ef7f1 commit a199475
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 18 deletions.
40 changes: 35 additions & 5 deletions crates/eframe/src/web/app_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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!(
Expand Down
13 changes: 2 additions & 11 deletions crates/eframe/src/web/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down
8 changes: 6 additions & 2 deletions crates/eframe/src/web/text_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
};
Expand Down

0 comments on commit a199475

Please sign in to comment.