Skip to content

Commit

Permalink
Fix: still track mouse when dragging outside web canvas (#4522)
Browse files Browse the repository at this point in the history
* Closes #3157

If the mouse leaves the canvas when dragging a slider, the slider will
still move.

---

To support this, I had to revert #4419
Despite that, I fail to reproduce the two issues it claimed to solve:

* #4406 may have been solved in
another way by this PR
* #4418 I cannot reproduce on Mac.
If it is still a problem, I think it should be solved by triggering a
`PointerEvent::Released` when focus is lost (i.e. on alt-tab), and not
on `PointerGone`
  • Loading branch information
emilk authored May 22, 2024
1 parent 7035aa4 commit c8578c9
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 56 deletions.
107 changes: 63 additions & 44 deletions crates/eframe/src/web/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -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);
Expand All @@ -347,31 +352,37 @@ 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,
});
// 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",
|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,
Expand Down Expand Up @@ -412,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;
Expand All @@ -434,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,
Expand Down
2 changes: 1 addition & 1 deletion crates/egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1875,8 +1875,8 @@ impl Context {
drag_started: _,
dragged,
drag_stopped: _,
hovered,
contains_pointer,
hovered,
} = interact_widgets;

if true {
Expand Down
6 changes: 2 additions & 4 deletions crates/egui/src/input_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion crates/egui/src/interaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ pub(crate) fn interact(
drag_started,
dragged,
drag_stopped,
hovered,
contains_pointer,
hovered,
}
}
12 changes: 6 additions & 6 deletions crates/egui_demo_lib/src/demo/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}

Expand Down

0 comments on commit c8578c9

Please sign in to comment.