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 (#4775)

* Closes #4752 

To test, start the web demo and follow the repro steps from #4752
  • Loading branch information
lucasmerlin authored Jul 5, 2024
1 parent b31d02d commit 9a4c462
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 13 deletions.
42 changes: 31 additions & 11 deletions crates/eframe/src/web/events.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use web_sys::EventTarget;

use super::*;
use web_sys::EventTarget;

// TODO(emilk): there are more calls to `prevent_default` and `stop_propagaton`
// than what is probably needed.
Expand Down Expand Up @@ -422,8 +421,17 @@ 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 {
let document = web_sys::window().unwrap().document().unwrap();
let is_hovering_canvas = document
.element_from_point(pos.x, pos.y)
.is_some_and(|element| element.eq(runner.canvas()));
let is_pointer_down = runner
.egui_ctx()
.input(|i| i.pointer.any_down() || i.any_touches());

is_hovering_canvas || is_pointer_down
}

fn install_mousemove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {
Expand All @@ -433,7 +441,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,
egui::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 +460,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,
egui::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 +507,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 +526,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,
egui::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 +544,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,
egui::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 9a4c462

Please sign in to comment.