diff --git a/crates/eframe/src/web/app_runner.rs b/crates/eframe/src/web/app_runner.rs index 75fee203..f8b7f14a 100644 --- a/crates/eframe/src/web/app_runner.rs +++ b/crates/eframe/src/web/app_runner.rs @@ -2,7 +2,7 @@ use egui::TexturesDelta; use crate::{epi, App}; -use super::{now_sec, web_painter::WebPainter, NeedRepaint}; +use super::{now_sec, text_agent::TextAgent, web_painter::WebPainter, NeedRepaint}; pub struct AppRunner { #[allow(dead_code)] @@ -14,7 +14,7 @@ pub struct AppRunner { app: Box, pub(crate) needs_repaint: std::sync::Arc, last_save_time: f64, - pub(crate) ime: Option, + pub(crate) text_agent: TextAgent, pub(crate) mutable_text_under_cursor: bool, // Output for the last run: @@ -35,6 +35,7 @@ impl AppRunner { canvas_id: &str, web_options: crate::WebOptions, app_creator: epi::AppCreator, + text_agent: TextAgent, ) -> Result { let painter = super::ActiveWebPainter::new(canvas_id, &web_options).await?; @@ -119,7 +120,7 @@ impl AppRunner { app, needs_repaint, last_save_time: now_sec(), - ime: None, + text_agent, mutable_text_under_cursor: false, textures_delta: Default::default(), clipped_primitives: None, @@ -270,9 +271,11 @@ impl AppRunner { self.mutable_text_under_cursor = mutable_text_under_cursor; - if self.ime != ime { - super::text_agent::move_text_cursor(ime, self.canvas()); - self.ime = ime; + if let Err(err) = self.text_agent.move_to(ime, self.canvas()) { + log::error!( + "failed to update text agent position: {}", + super::string_from_js_value(&err) + ); } } } diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index 33d36c35..b2f371bd 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -94,8 +94,8 @@ pub(crate) fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsVa if !modifiers.ctrl && !modifiers.command && !should_ignore_key(&key) - // When text agent is shown, it sends text event instead. - && text_agent::text_agent().hidden() + // When text agent is focused, it is responsible for handling input events + && !runner.text_agent.has_focus() { runner.input.raw.events.push(egui::Event::Text(key)); } @@ -375,10 +375,12 @@ pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValu // event callback, which is why we run the app logic here and now: runner.logic(); + runner + .text_agent + .set_focus(runner.mutable_text_under_cursor); + // Make sure we paint the output of the above logic call asap: runner.needs_repaint.repaint_asap(); - - text_agent::update_text_agent(runner); } event.stop_propagation(); event.prevent_default(); @@ -467,13 +469,15 @@ pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValu runner.input.raw.events.push(egui::Event::PointerGone); push_touches(runner, egui::TouchPhase::End, &event); + + runner + .text_agent + .set_focus(runner.mutable_text_under_cursor); + runner.needs_repaint.repaint_asap(); event.stop_propagation(); event.prevent_default(); } - - // Finally, focus or blur text agent to toggle mobile keyboard: - text_agent::update_text_agent(runner); }, )?; diff --git a/crates/eframe/src/web/mod.rs b/crates/eframe/src/web/mod.rs index f98bbc1e..50e7aeb3 100644 --- a/crates/eframe/src/web/mod.rs +++ b/crates/eframe/src/web/mod.rs @@ -53,6 +53,29 @@ pub(crate) fn string_from_js_value(value: &JsValue) -> String { value.as_string().unwrap_or_else(|| format!("{value:#?}")) } +/// Returns the `Element` with active focus. +/// +/// Elements can only be focused if they are: +/// - ``/`` with an `href` attribute +/// - ``/`