1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 05:10:03 -04:00

Fix crash on request_animation_frame when destroying web runner (#4169)

Previously, any frames in flight (`requestAnimationFrame`) on web were
not being cancelled (`cancelAnimationFrame`) when `WebRunner::destroy`
was called. If a user called `destroy`, then immediately removed the
canvas from the DOM, eframe could panic with a "failed to find (canvas)
element by id" error message.

This PR changes two things:
- The canvas element is directly referenced everywhere it's needed
instead of being looked up by `canvas_id`[^1]
- The RAF handle is stored in `WebRunner` and `cancelAnimationFrame` is
called on it inside of `WebRunner::destroy`[^2]

[^1]: The WebGL/WGPU backends were already holding onto the canvas (and
associated GPU context), so the change is just converting all the
`get_element_by_id` lookups to retrieve the canvas from the web runner
handle.

[^2]: There is only ever one frame in flight, so we store it directly as
a scalar field.
This commit is contained in:
Jan Procházka
2024-03-14 10:26:34 +01:00
committed by GitHub
parent 00e8ce6d7e
commit c5eaba43cd
9 changed files with 73 additions and 61 deletions

View File

@@ -5,7 +5,7 @@ use std::{cell::Cell, rc::Rc};
use wasm_bindgen::prelude::*;
use super::{canvas_element, AppRunner, WebRunner};
use super::{AppRunner, WebRunner};
static AGENT_ID: &str = "egui_text_agent";
@@ -121,7 +121,7 @@ pub fn update_text_agent(runner: &mut AppRunner) -> Option<()> {
let window = web_sys::window()?;
let document = window.document()?;
let input: HtmlInputElement = document.get_element_by_id(AGENT_ID)?.dyn_into().unwrap();
let canvas_style = canvas_element(runner.canvas_id())?.style();
let canvas_style = runner.canvas().style();
if runner.mutable_text_under_cursor {
let is_already_editing = input.hidden();
@@ -205,14 +205,16 @@ fn is_mobile() -> Option<bool> {
// candidate window moves following text element (agent),
// so it appears that the IME candidate window moves with text cursor.
// On mobile devices, there is no need to do that.
pub fn move_text_cursor(ime: Option<egui::output::IMEOutput>, canvas_id: &str) -> Option<()> {
pub fn move_text_cursor(
ime: Option<egui::output::IMEOutput>,
canvas: &web_sys::HtmlCanvasElement,
) -> Option<()> {
let style = text_agent().style();
// Note: moving agent on mobile devices will lead to unpredictable scroll.
if is_mobile() == Some(false) {
ime.as_ref().and_then(|ime| {
let egui::Pos2 { x, y } = ime.cursor_rect.left_top();
let canvas = canvas_element(canvas_id)?;
let bounding_rect = text_agent().get_bounding_client_rect();
let y = (y + (canvas.scroll_top() + canvas.offset_top()) as f32)
.min(canvas.client_height() as f32 - bounding_rect.height() as f32);