From 37d76e2a05e89f0d6b83935bd9e2786a7802544e Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 21 May 2024 23:02:15 +0200 Subject: [PATCH 1/5] Web: keep track of mouse cursor even when it leaves the canvas --- crates/eframe/src/web/events.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index f7234d76fad9..b52533bfc220 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -284,6 +284,8 @@ pub(crate) fn install_color_scheme_change_event(runner_ref: &WebRunner) -> Resul pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValue> { let canvas = runner_ref.try_lock().unwrap().canvas().clone(); + let window = web_sys::window().unwrap(); + let document = window.document().unwrap(); { let prevent_default_events = [ @@ -333,8 +335,11 @@ pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValu }, )?; + // NOTE: we register "mousemove" on `document` instead of just the canvas + // in order to track a dragged mouse outside the canvas. + // See https://github.com/emilk/egui/issues/3157 runner_ref.add_event_listener( - &canvas, + &document, "mousemove", |event: web_sys::MouseEvent, runner| { let modifiers = modifiers_from_mouse_event(&event); From 8c72df970d79d327d43051c6e2a3d2ce350cbe1f Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 21 May 2024 23:03:27 +0200 Subject: [PATCH 2/5] Don't treat `PointerGone` as `Released` --- crates/egui/src/input_state.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/egui/src/input_state.rs b/crates/egui/src/input_state.rs index 22851a470b6e..e82fd62b75e1 100644 --- a/crates/egui/src/input_state.rs +++ b/crates/egui/src/input_state.rs @@ -800,10 +800,8 @@ impl PointerState { } Event::PointerGone => { self.latest_pos = None; - self.pointer_events.push(PointerEvent::Released { - click: None, - button: PointerButton::Primary, - }); + // When dragging a slider and the mouse leaves the viewport, we still want the drag to work, + // so we don't treat this as a `PointerEvent::Released`. // NOTE: we do NOT clear `self.interact_pos` here. It will be cleared next frame. } Event::MouseMoved(delta) => *self.motion.get_or_insert(Vec2::ZERO) += *delta, From 0767d91a4f4512aaba545b7c805a44a2f7d49ef8 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 21 May 2024 23:08:51 +0200 Subject: [PATCH 3/5] Revert "Treat `Event::PointerGone` as `PointerEvent::Released` (#4419)" This reverts commit 66d2b3ffe43f969c7b75d70423ec02dacc6ba129. --- crates/egui/src/context.rs | 2 +- crates/egui/src/interaction.rs | 2 +- crates/egui_demo_lib/src/demo/tests.rs | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 2d58be7e1265..04bdd16432aa 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -1875,8 +1875,8 @@ impl Context { drag_started: _, dragged, drag_stopped: _, - hovered, contains_pointer, + hovered, } = interact_widgets; if true { diff --git a/crates/egui/src/interaction.rs b/crates/egui/src/interaction.rs index e25e1c4aa134..8a7b2d0948c5 100644 --- a/crates/egui/src/interaction.rs +++ b/crates/egui/src/interaction.rs @@ -283,7 +283,7 @@ pub(crate) fn interact( drag_started, dragged, drag_stopped, - hovered, contains_pointer, + hovered, } } diff --git a/crates/egui_demo_lib/src/demo/tests.rs b/crates/egui_demo_lib/src/demo/tests.rs index 6a8348ac56ad..44e355d0e8e3 100644 --- a/crates/egui_demo_lib/src/demo/tests.rs +++ b/crates/egui_demo_lib/src/demo/tests.rs @@ -466,23 +466,23 @@ fn response_summary(response: &egui::Response, show_hovers: bool) -> String { // These are in inverse logical/chonological order, because we show them in the ui that way: if response.triple_clicked_by(button) { - writeln!(new_info, "Triple_clicked_by{button_suffix}").ok(); + writeln!(new_info, "Triple-clicked{button_suffix}").ok(); } if response.double_clicked_by(button) { - writeln!(new_info, "Double_clicked_by{button_suffix}").ok(); + writeln!(new_info, "Double-clicked{button_suffix}").ok(); } if response.clicked_by(button) { - writeln!(new_info, "Clicked_by{button_suffix}").ok(); + writeln!(new_info, "Clicked{button_suffix}").ok(); } if response.drag_stopped_by(button) { - writeln!(new_info, "Drag_stopped_by{button_suffix}").ok(); + writeln!(new_info, "Drag stopped{button_suffix}").ok(); } if response.dragged_by(button) { - writeln!(new_info, "Dragged_by{button_suffix}").ok(); + writeln!(new_info, "Dragged{button_suffix}").ok(); } if response.drag_started_by(button) { - writeln!(new_info, "Drag_started_by{button_suffix}").ok(); + writeln!(new_info, "Drag started{button_suffix}").ok(); } } From 3bc7978f260a04280c1ded9d5dd98b34c6500b82 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 21 May 2024 23:13:11 +0200 Subject: [PATCH 4/5] Track `mouseup` event outside of canvas --- crates/eframe/src/web/events.rs | 49 ++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index b52533bfc220..eefa030cf2e2 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -352,31 +352,36 @@ pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValu }, )?; - runner_ref.add_event_listener(&canvas, "mouseup", |event: web_sys::MouseEvent, runner| { - let modifiers = modifiers_from_mouse_event(&event); - runner.input.raw.modifiers = modifiers; - if let Some(button) = button_from_mouse_event(&event) { - let pos = pos_from_mouse_event(runner.canvas(), &event, runner.egui_ctx()); - let modifiers = runner.input.raw.modifiers; - runner.input.raw.events.push(egui::Event::PointerButton { - pos, - button, - pressed: false, - modifiers, - }); + // Same with mouseup: we want to notice if a drag stops outside the canvas + runner_ref.add_event_listener( + &document, + "mouseup", + |event: web_sys::MouseEvent, runner| { + let modifiers = modifiers_from_mouse_event(&event); + runner.input.raw.modifiers = modifiers; + if let Some(button) = button_from_mouse_event(&event) { + let pos = pos_from_mouse_event(runner.canvas(), &event, runner.egui_ctx()); + let modifiers = runner.input.raw.modifiers; + runner.input.raw.events.push(egui::Event::PointerButton { + pos, + button, + pressed: false, + modifiers, + }); - // In Safari we are only allowed to write to the clipboard during the - // event callback, which is why we run the app logic here and now: - runner.logic(); + // In Safari we are only allowed to write to the clipboard during the + // event callback, which is why we run the app logic here and now: + runner.logic(); - // Make sure we paint the output of the above logic call asap: - runner.needs_repaint.repaint_asap(); + // 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(); - })?; + text_agent::update_text_agent(runner); + } + event.stop_propagation(); + event.prevent_default(); + }, + )?; runner_ref.add_event_listener( &canvas, From 68351992d544351f7b1a0a666578bb1e461f266f Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 21 May 2024 23:15:49 +0200 Subject: [PATCH 5/5] Do the same for touches --- crates/eframe/src/web/events.rs | 53 +++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index eefa030cf2e2..66b90d3e0c22 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -352,7 +352,8 @@ pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValu }, )?; - // Same with mouseup: we want to notice if a drag stops outside the canvas + // Use `document` here to notice if the user releases a drag outside of the canvas. + // See https://github.com/emilk/egui/issues/3157 runner_ref.add_event_listener( &document, "mouseup", @@ -422,8 +423,10 @@ pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValu }, )?; + // Use `document` here to notice if the user drag outside of the canvas. + // See https://github.com/emilk/egui/issues/3157 runner_ref.add_event_listener( - &canvas, + &document, "touchmove", |event: web_sys::TouchEvent, runner| { let mut latest_touch_pos_id = runner.input.latest_touch_pos_id; @@ -444,28 +447,34 @@ pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValu }, )?; - runner_ref.add_event_listener(&canvas, "touchend", |event: web_sys::TouchEvent, runner| { - if let Some(pos) = runner.input.latest_touch_pos { - let modifiers = runner.input.raw.modifiers; - // First release mouse to click: - runner.input.raw.events.push(egui::Event::PointerButton { - pos, - button: egui::PointerButton::Primary, - pressed: false, - modifiers, - }); - // Then remove hover effect: - runner.input.raw.events.push(egui::Event::PointerGone); + // Use `document` here to notice if the user releases a drag outside of the canvas. + // See https://github.com/emilk/egui/issues/3157 + runner_ref.add_event_listener( + &document, + "touchend", + |event: web_sys::TouchEvent, runner| { + if let Some(pos) = runner.input.latest_touch_pos { + let modifiers = runner.input.raw.modifiers; + // First release mouse to click: + runner.input.raw.events.push(egui::Event::PointerButton { + pos, + button: egui::PointerButton::Primary, + pressed: false, + modifiers, + }); + // Then remove hover effect: + runner.input.raw.events.push(egui::Event::PointerGone); - push_touches(runner, egui::TouchPhase::End, &event); - runner.needs_repaint.repaint_asap(); - event.stop_propagation(); - event.prevent_default(); - } + push_touches(runner, egui::TouchPhase::End, &event); + 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); - })?; + // Finally, focus or blur text agent to toggle mobile keyboard: + text_agent::update_text_agent(runner); + }, + )?; runner_ref.add_event_listener( &canvas,