Skip to content

Commit

Permalink
Check if the mouse / touch event is above the canvas via element_from…
Browse files Browse the repository at this point in the history
…_point

Fixes #4752
  • Loading branch information
lucasmerlin committed Jul 3, 2024
1 parent 8bf498d commit ebc5f0f
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 13 deletions.
44 changes: 33 additions & 11 deletions crates/eframe/src/web/events.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use web_sys::EventTarget;

use super::*;
use egui::{pos2, vec2};
use web_sys::{window, EventTarget};

// TODO(emilk): there are more calls to `prevent_default` and `stop_propagaton`
// than what is probably needed.
Expand Down Expand Up @@ -422,8 +422,18 @@ fn install_mousedown(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),
}

/// Returns true if the cursor is above the canvas, or if we're dragging something.
fn is_interested_in_pointer_event(egui_ctx: &egui::Context, pos: egui::Pos2) -> bool {
egui_ctx.input(|i| i.screen_rect().contains(pos) || i.pointer.any_down() || i.any_touches())
/// Pass in the position in browser viewport coordinates (usually event.clientX/Y).
fn is_interested_in_pointer_event(runner: &AppRunner, pos: egui::Pos2) -> bool {
window()
.unwrap()
.document()
.unwrap()
.element_from_point(pos.x, pos.y)
.map(|element| element.eq(runner.canvas()))
.unwrap_or(false)
|| runner
.egui_ctx()
.input(|i| i.pointer.any_down() || i.any_touches())
}

fn install_mousemove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {
Expand All @@ -433,7 +443,10 @@ fn install_mousemove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),

let pos = pos_from_mouse_event(runner.canvas(), &event, runner.egui_ctx());

if is_interested_in_pointer_event(runner.egui_ctx(), pos) {
if is_interested_in_pointer_event(
runner,
pos2(event.client_x() as f32, event.client_y() as f32),
) {
runner.input.raw.events.push(egui::Event::PointerMoved(pos));
runner.needs_repaint.repaint_asap();
event.stop_propagation();
Expand All @@ -449,7 +462,10 @@ fn install_mouseup(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), J

let pos = pos_from_mouse_event(runner.canvas(), &event, runner.egui_ctx());

if is_interested_in_pointer_event(runner.egui_ctx(), pos) {
if is_interested_in_pointer_event(
runner,
pos2(event.client_x() as f32, event.client_y() as f32),
) {
if let Some(button) = button_from_mouse_event(&event) {
let modifiers = runner.input.raw.modifiers;
runner.input.raw.events.push(egui::Event::PointerButton {
Expand Down Expand Up @@ -493,7 +509,7 @@ fn install_touchstart(runner_ref: &WebRunner, target: &EventTarget) -> Result<()
target,
"touchstart",
|event: web_sys::TouchEvent, runner| {
if let Some(pos) = primary_touch_pos(runner, &event) {
if let Some((pos, _)) = primary_touch_pos(runner, &event) {
runner.input.raw.events.push(egui::Event::PointerButton {
pos,
button: egui::PointerButton::Primary,
Expand All @@ -512,8 +528,11 @@ fn install_touchstart(runner_ref: &WebRunner, target: &EventTarget) -> Result<()

fn install_touchmove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {
runner_ref.add_event_listener(target, "touchmove", |event: web_sys::TouchEvent, runner| {
if let Some(pos) = primary_touch_pos(runner, &event) {
if is_interested_in_pointer_event(runner.egui_ctx(), pos) {
if let Some((pos, touch)) = primary_touch_pos(runner, &event) {
if is_interested_in_pointer_event(
runner,
pos2(touch.client_x() as f32, touch.client_y() as f32),
) {
runner.input.raw.events.push(egui::Event::PointerMoved(pos));

push_touches(runner, egui::TouchPhase::Move, &event);
Expand All @@ -527,8 +546,11 @@ fn install_touchmove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),

fn install_touchend(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {
runner_ref.add_event_listener(target, "touchend", |event: web_sys::TouchEvent, runner| {
if let Some(pos) = primary_touch_pos(runner, &event) {
if is_interested_in_pointer_event(runner.egui_ctx(), pos) {
if let Some((pos, touch)) = primary_touch_pos(runner, &event) {
if is_interested_in_pointer_event(
runner,
pos2(touch.client_x() as f32, touch.client_y() as f32),
) {
// First release mouse to click:
runner.input.raw.events.push(egui::Event::PointerButton {
pos,
Expand Down
7 changes: 5 additions & 2 deletions crates/eframe/src/web/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub fn button_from_mouse_event(event: &web_sys::MouseEvent) -> Option<egui::Poin
pub fn primary_touch_pos(
runner: &mut AppRunner,
event: &web_sys::TouchEvent,
) -> Option<egui::Pos2> {
) -> Option<(egui::Pos2, web_sys::Touch)> {
let all_touches: Vec<_> = (0..event.touches().length())
.filter_map(|i| event.touches().get(i))
// On touchend we don't get anything in `touches`, but we still get `changed_touches`, so include those:
Expand Down Expand Up @@ -59,7 +59,10 @@ pub fn primary_touch_pos(
for touch in all_touches {
if primary_touch == egui::TouchId::from(touch.identifier()) {
let canvas_rect = canvas_content_rect(runner.canvas());
return Some(pos_from_touch(canvas_rect, &touch, runner.egui_ctx()));
return Some((
pos_from_touch(canvas_rect, &touch, runner.egui_ctx()),
touch,
));
}
}
}
Expand Down

0 comments on commit ebc5f0f

Please sign in to comment.