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

Multiple mouse button support (#135)

Add support for primary, secondary and middle mouse buttons. Also improve ability to click things in low FPS situations.

This introduces a lot of breaking changes:

    Backends/integrations now pass mouse events via the even stream.
    Response has an interface of mostly methods instead of public members.
    input.mouse is now input.pointer and has new interface.


* Rename 'mouse' to 'pointer' everywhere (pointer = mouse or touch)

* Make Response::clicked and Response::double_clicked into methods

* Remove Response::active and add dragged() and interact_pointer_pos()

* Support multiple mouse buttons

* Make PointerState interface all methods

* Make most members of Response private
This commit is contained in:
Emil Ernerfeldt
2021-01-25 18:50:19 +01:00
committed by GitHub
parent 18b9214575
commit 247026149c
47 changed files with 1052 additions and 580 deletions

View File

@@ -81,6 +81,9 @@ pub struct WebInput {
/// Is this a touch screen? If so, we ignore mouse events.
pub is_touch: bool,
/// Required because we don't get a position on touched
pub latest_touch_pos: Option<egui::Pos2>,
pub raw: egui::RawInput,
}

View File

@@ -97,6 +97,15 @@ pub fn pos_from_mouse_event(canvas_id: &str, event: &web_sys::MouseEvent) -> egu
}
}
pub fn button_from_mouse_event(event: &web_sys::MouseEvent) -> Option<egui::PointerButton> {
match event.button() {
0 => Some(egui::PointerButton::Primary),
1 => Some(egui::PointerButton::Middle),
2 => Some(egui::PointerButton::Secondary),
_ => None,
}
}
pub fn pos_from_touch_event(event: &web_sys::TouchEvent) -> egui::Pos2 {
let t = event.touches().get(0).unwrap();
egui::Pos2 {
@@ -599,19 +608,40 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
use wasm_bindgen::JsCast;
let canvas = canvas_element(runner_ref.0.lock().canvas_id()).unwrap();
{
// By default, right-clicks open a context menu.
// We don't want to do that (right clicks is handled by egui):
let event_name = "contextmenu";
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
event.prevent_default();
}) as Box<dyn FnMut(_)>);
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
closure.forget();
}
{
let event_name = "mousedown";
let runner_ref = runner_ref.clone();
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
let mut runner_lock = runner_ref.0.lock();
if !runner_lock.input.is_touch {
runner_lock.input.raw.mouse_pos =
Some(pos_from_mouse_event(runner_lock.canvas_id(), &event));
runner_lock.input.raw.mouse_down = true;
runner_lock.logic().unwrap(); // in case we get "mouseup" the same frame. TODO: handle via events instead
runner_lock.needs_repaint.set_true();
event.stop_propagation();
event.prevent_default();
if let Some(button) = button_from_mouse_event(&event) {
let pos = pos_from_mouse_event(runner_lock.canvas_id(), &event);
let modifiers = runner_lock.input.raw.modifiers;
runner_lock
.input
.raw
.events
.push(egui::Event::PointerButton {
pos,
button,
pressed: true,
modifiers,
});
runner_lock.needs_repaint.set_true();
event.stop_propagation();
event.prevent_default();
}
}
}) as Box<dyn FnMut(_)>);
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
@@ -624,8 +654,12 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
let mut runner_lock = runner_ref.0.lock();
if !runner_lock.input.is_touch {
runner_lock.input.raw.mouse_pos =
Some(pos_from_mouse_event(runner_lock.canvas_id(), &event));
let pos = pos_from_mouse_event(runner_lock.canvas_id(), &event);
runner_lock
.input
.raw
.events
.push(egui::Event::PointerMoved(pos));
runner_lock.needs_repaint.set_true();
event.stop_propagation();
event.prevent_default();
@@ -641,12 +675,23 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
let mut runner_lock = runner_ref.0.lock();
if !runner_lock.input.is_touch {
runner_lock.input.raw.mouse_pos =
Some(pos_from_mouse_event(runner_lock.canvas_id(), &event));
runner_lock.input.raw.mouse_down = false;
runner_lock.needs_repaint.set_true();
event.stop_propagation();
event.prevent_default();
if let Some(button) = button_from_mouse_event(&event) {
let pos = pos_from_mouse_event(runner_lock.canvas_id(), &event);
let modifiers = runner_lock.input.raw.modifiers;
runner_lock
.input
.raw
.events
.push(egui::Event::PointerButton {
pos,
button,
pressed: false,
modifiers,
});
runner_lock.needs_repaint.set_true();
event.stop_propagation();
event.prevent_default();
}
}
}) as Box<dyn FnMut(_)>);
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
@@ -659,7 +704,7 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
let mut runner_lock = runner_ref.0.lock();
if !runner_lock.input.is_touch {
runner_lock.input.raw.mouse_pos = None;
runner_lock.input.raw.events.push(egui::Event::PointerGone);
runner_lock.needs_repaint.set_true();
event.stop_propagation();
event.prevent_default();
@@ -673,10 +718,21 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
let event_name = "touchstart";
let runner_ref = runner_ref.clone();
let closure = Closure::wrap(Box::new(move |event: web_sys::TouchEvent| {
let pos = pos_from_touch_event(&event);
let mut runner_lock = runner_ref.0.lock();
runner_lock.input.latest_touch_pos = Some(pos);
runner_lock.input.is_touch = true;
runner_lock.input.raw.mouse_pos = Some(pos_from_touch_event(&event));
runner_lock.input.raw.mouse_down = true;
let modifiers = runner_lock.input.raw.modifiers;
runner_lock
.input
.raw
.events
.push(egui::Event::PointerButton {
pos,
button: egui::PointerButton::Primary,
pressed: true,
modifiers,
});
runner_lock.needs_repaint.set_true();
event.stop_propagation();
event.prevent_default();
@@ -689,9 +745,15 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
let event_name = "touchmove";
let runner_ref = runner_ref.clone();
let closure = Closure::wrap(Box::new(move |event: web_sys::TouchEvent| {
let pos = pos_from_touch_event(&event);
let mut runner_lock = runner_ref.0.lock();
runner_lock.input.latest_touch_pos = Some(pos);
runner_lock.input.is_touch = true;
runner_lock.input.raw.mouse_pos = Some(pos_from_touch_event(&event));
runner_lock
.input
.raw
.events
.push(egui::Event::PointerMoved(pos));
runner_lock.needs_repaint.set_true();
event.stop_propagation();
event.prevent_default();
@@ -706,12 +768,25 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
let closure = Closure::wrap(Box::new(move |event: web_sys::TouchEvent| {
let mut runner_lock = runner_ref.0.lock();
runner_lock.input.is_touch = true;
runner_lock.input.raw.mouse_down = false; // First release mouse to click...
runner_lock.logic().unwrap(); // ...do the clicking... (TODO: handle via events instead)
runner_lock.input.raw.mouse_pos = None; // ...remove hover effect
runner_lock.needs_repaint.set_true();
event.stop_propagation();
event.prevent_default();
if let Some(pos) = runner_lock.input.latest_touch_pos {
let modifiers = runner_lock.input.raw.modifiers;
// First release mouse to click:
runner_lock
.input
.raw
.events
.push(egui::Event::PointerButton {
pos,
button: egui::PointerButton::Primary,
pressed: false,
modifiers,
});
// Then remove hover effect:
runner_lock.input.raw.events.push(egui::Event::PointerGone);
runner_lock.needs_repaint.set_true();
event.stop_propagation();
event.prevent_default();
}
}) as Box<dyn FnMut(_)>);
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
closure.forget();