From 7e12229f59e239e070db734f28ae24cfc88e96f5 Mon Sep 17 00:00:00 2001 From: Ygor Souza Date: Sat, 4 Mar 2023 17:22:35 +0100 Subject: [PATCH 1/6] Add raw mouse wheel event The event is sent as it comes from the backend, so it will follow different conventions depending on the target, and it is up to the user code to deal with that. The goal is to allow advanced users to implement alternative UI controls, e.g., using Ctrl to scroll the plot horizontally instead of zooming, or use Shift to scroll faster instead of changing direction. --- crates/eframe/src/web/events.rs | 17 +++++++++++++++++ crates/egui-winit/src/lib.rs | 18 ++++++++++++++++++ crates/egui/src/data/input.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index d85adaa3a5e..df4537ff132 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -402,6 +402,23 @@ pub fn install_canvas_events(runner_container: &mut AppRunnerContainer) -> Resul &canvas, "wheel", |event: web_sys::WheelEvent, mut runner_lock| { + let mut push_raw_event = || { + let unit = match event.delta_mode() { + web_sys::WheelEvent::DOM_DELTA_PIXEL => egui::MouseWheelUnit::Pixel, + web_sys::WheelEvent::DOM_DELTA_LINE => egui::MouseWheelUnit::Line, + web_sys::WheelEvent::DOM_DELTA_PAGE => egui::MouseWheelUnit::Page, + 3u32..=u32::MAX => return, + }; + let delta = egui::vec2(event.delta_x() as f32, event.delta_y() as f32); + let modifiers = runner_lock.input.raw.modifiers; + + runner_lock.input.raw.events.push(egui::Event::MouseWheel { + unit, + delta, + modifiers, + }); + }; + push_raw_event(); let scroll_multiplier = match event.delta_mode() { web_sys::WheelEvent::DOM_DELTA_PAGE => { canvas_size_in_points(runner_lock.canvas_id()).y diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 5ae22a4335e..6f2595956fc 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -549,6 +549,24 @@ impl State { } fn on_mouse_wheel(&mut self, delta: winit::event::MouseScrollDelta) { + let mut push_raw_event = || { + let (unit, delta) = match delta { + winit::event::MouseScrollDelta::LineDelta(x, y) => { + (egui::MouseWheelUnit::Line, egui::vec2(x, y)) + } + winit::event::MouseScrollDelta::PixelDelta(winit::dpi::PhysicalPosition { + x, + y, + }) => (egui::MouseWheelUnit::Pixel, egui::vec2(x as f32, y as f32)), + }; + let modifiers = self.egui_input.modifiers; + self.egui_input.events.push(egui::Event::MouseWheel { + unit, + delta, + modifiers, + }); + }; + push_raw_event(); let delta = match delta { winit::event::MouseScrollDelta::LineDelta(x, y) => { let points_per_scroll_line = 50.0; // Scroll speed decided by consensus: https://github.com/emilk/egui/issues/461 diff --git a/crates/egui/src/data/input.rs b/crates/egui/src/data/input.rs index 30dab6760d2..5fca9adb50d 100644 --- a/crates/egui/src/data/input.rs +++ b/crates/egui/src/data/input.rs @@ -278,6 +278,22 @@ pub enum Event { force: f32, }, + /// A raw mouse wheel event as sent by the backend (minus the z coordinate), + /// for implementing alternative custom controls. + /// If this event is handled, Zoom and Scroll should be ignored, as they might be duplicates + /// coming from the same input device. + MouseWheel { + /// The unit of scrolling: pixels, lines etc + unit: MouseWheelUnit, + + /// The amount scrolled horizontally and vertically. The amount and direction corresponding + /// to one step of the wheel depends on the platform. + delta: Vec2, + + /// The state of the modifier keys at the time of the event. + modifiers: Modifiers, + }, + /// An assistive technology (e.g. screen reader) requested an action. #[cfg(feature = "accesskit")] AccessKitActionRequest(accesskit::ActionRequest), @@ -891,6 +907,20 @@ pub enum TouchPhase { Cancel, } +/// The unit associated with the numeric value of a mouse wheel event +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub enum MouseWheelUnit { + /// Number of pixels + Pixel, + + /// Number of lines + Line, + + /// Number of pages + Page, +} + impl From for TouchId { fn from(id: u64) -> Self { Self(id) From 4d95a7539ce7dcb53b07709a20f76bac5f544198 Mon Sep 17 00:00:00 2001 From: YgorSouza <43298013+YgorSouza@users.noreply.github.com> Date: Wed, 29 Mar 2023 21:37:18 +0200 Subject: [PATCH 2/6] Change Pixel to Point for consistency Apply suggestions from code review by emilk Co-authored-by: Emil Ernerfeldt --- crates/eframe/src/web/events.rs | 2 +- crates/egui-winit/src/lib.rs | 5 ++++- crates/egui/src/data/input.rs | 10 +++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index df4537ff132..b64473514e4 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -404,7 +404,7 @@ pub fn install_canvas_events(runner_container: &mut AppRunnerContainer) -> Resul |event: web_sys::WheelEvent, mut runner_lock| { let mut push_raw_event = || { let unit = match event.delta_mode() { - web_sys::WheelEvent::DOM_DELTA_PIXEL => egui::MouseWheelUnit::Pixel, + web_sys::WheelEvent::DOM_DELTA_PIXEL => egui::MouseWheelUnit::Point, web_sys::WheelEvent::DOM_DELTA_LINE => egui::MouseWheelUnit::Line, web_sys::WheelEvent::DOM_DELTA_PAGE => egui::MouseWheelUnit::Page, 3u32..=u32::MAX => return, diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 6f2595956fc..fd16ba8a03c 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -557,7 +557,10 @@ impl State { winit::event::MouseScrollDelta::PixelDelta(winit::dpi::PhysicalPosition { x, y, - }) => (egui::MouseWheelUnit::Pixel, egui::vec2(x as f32, y as f32)), + }) => ( + egui::MouseWheelUnit::Point, + egui::vec2(x as f32, y as f32) / self.pixels_per_point(), + ), }; let modifiers = self.egui_input.modifiers; self.egui_input.events.push(egui::Event::MouseWheel { diff --git a/crates/egui/src/data/input.rs b/crates/egui/src/data/input.rs index 5fca9adb50d..67c1853a489 100644 --- a/crates/egui/src/data/input.rs +++ b/crates/egui/src/data/input.rs @@ -280,10 +280,10 @@ pub enum Event { /// A raw mouse wheel event as sent by the backend (minus the z coordinate), /// for implementing alternative custom controls. - /// If this event is handled, Zoom and Scroll should be ignored, as they might be duplicates - /// coming from the same input device. + /// Note that the same event can also trigger [`Self::Zoom`] and [`Self::Scroll`], + /// so you probably want to handle only one of them. MouseWheel { - /// The unit of scrolling: pixels, lines etc + /// The unit of scrolling: points, lines, or pages. unit: MouseWheelUnit, /// The amount scrolled horizontally and vertically. The amount and direction corresponding @@ -911,8 +911,8 @@ pub enum TouchPhase { #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum MouseWheelUnit { - /// Number of pixels - Pixel, + /// Number of ui points (logical pixels) + Point, /// Number of lines Line, From 3d1655d7751ff24969aa80b37cdee26e6f7ecdc2 Mon Sep 17 00:00:00 2001 From: Ygor Souza Date: Wed, 29 Mar 2023 21:46:52 +0200 Subject: [PATCH 3/6] Inline mouse wheel raw event closure It was meant only to be able to use the same variable names without shadowing the rest of the code, but a simple block accomplishes the same thing. --- crates/eframe/src/web/events.rs | 5 ++--- crates/egui-winit/src/lib.rs | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index b64473514e4..6ff573997d0 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -402,7 +402,7 @@ pub fn install_canvas_events(runner_container: &mut AppRunnerContainer) -> Resul &canvas, "wheel", |event: web_sys::WheelEvent, mut runner_lock| { - let mut push_raw_event = || { + { let unit = match event.delta_mode() { web_sys::WheelEvent::DOM_DELTA_PIXEL => egui::MouseWheelUnit::Point, web_sys::WheelEvent::DOM_DELTA_LINE => egui::MouseWheelUnit::Line, @@ -417,8 +417,7 @@ pub fn install_canvas_events(runner_container: &mut AppRunnerContainer) -> Resul delta, modifiers, }); - }; - push_raw_event(); + } let scroll_multiplier = match event.delta_mode() { web_sys::WheelEvent::DOM_DELTA_PAGE => { canvas_size_in_points(runner_lock.canvas_id()).y diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index fd16ba8a03c..0c9a9c71635 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -549,7 +549,7 @@ impl State { } fn on_mouse_wheel(&mut self, delta: winit::event::MouseScrollDelta) { - let mut push_raw_event = || { + { let (unit, delta) = match delta { winit::event::MouseScrollDelta::LineDelta(x, y) => { (egui::MouseWheelUnit::Line, egui::vec2(x, y)) @@ -568,8 +568,7 @@ impl State { delta, modifiers, }); - }; - push_raw_event(); + } let delta = match delta { winit::event::MouseScrollDelta::LineDelta(x, y) => { let points_per_scroll_line = 50.0; // Scroll speed decided by consensus: https://github.com/emilk/egui/issues/461 From c7f8ece5e766833f85110347ea4e1d9149ac2e19 Mon Sep 17 00:00:00 2001 From: YgorSouza <43298013+YgorSouza@users.noreply.github.com> Date: Thu, 30 Mar 2023 19:53:59 +0200 Subject: [PATCH 4/6] Use wildcard on wheel event match Co-authored-by: Emil Ernerfeldt --- crates/eframe/src/web/events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index 6ff573997d0..329212fd657 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -407,7 +407,7 @@ pub fn install_canvas_events(runner_container: &mut AppRunnerContainer) -> Resul web_sys::WheelEvent::DOM_DELTA_PIXEL => egui::MouseWheelUnit::Point, web_sys::WheelEvent::DOM_DELTA_LINE => egui::MouseWheelUnit::Line, web_sys::WheelEvent::DOM_DELTA_PAGE => egui::MouseWheelUnit::Page, - 3u32..=u32::MAX => return, + _ => return, }; let delta = egui::vec2(event.delta_x() as f32, event.delta_y() as f32); let modifiers = runner_lock.input.raw.modifiers; From 61ad737f8cf3c32be60f27e39dc8c463a796c2f9 Mon Sep 17 00:00:00 2001 From: Ygor Souza Date: Fri, 31 Mar 2023 19:10:56 +0200 Subject: [PATCH 5/6] Flip mouse wheel delta sign on web to match native --- crates/eframe/src/web/events.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index 329212fd657..324f9c31ac5 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -409,7 +409,8 @@ pub fn install_canvas_events(runner_container: &mut AppRunnerContainer) -> Resul web_sys::WheelEvent::DOM_DELTA_PAGE => egui::MouseWheelUnit::Page, _ => return, }; - let delta = egui::vec2(event.delta_x() as f32, event.delta_y() as f32); + // delta sign is flipped to match native convention + let delta = -egui::vec2(event.delta_x() as f32, event.delta_y() as f32); let modifiers = runner_lock.input.raw.modifiers; runner_lock.input.raw.events.push(egui::Event::MouseWheel { From 729937cc79b6bd0c1dfc3229ffc6d06ebe81cf75 Mon Sep 17 00:00:00 2001 From: Ygor Souza Date: Fri, 31 Mar 2023 21:01:47 +0200 Subject: [PATCH 6/6] Use wheel event data to generate scroll event To avoid doing the same match and sign conversion twice. --- crates/eframe/src/web/events.rs | 44 +++++++++++++++------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index 324f9c31ac5..99d2ef14369 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -402,37 +402,33 @@ pub fn install_canvas_events(runner_container: &mut AppRunnerContainer) -> Resul &canvas, "wheel", |event: web_sys::WheelEvent, mut runner_lock| { - { - let unit = match event.delta_mode() { - web_sys::WheelEvent::DOM_DELTA_PIXEL => egui::MouseWheelUnit::Point, - web_sys::WheelEvent::DOM_DELTA_LINE => egui::MouseWheelUnit::Line, - web_sys::WheelEvent::DOM_DELTA_PAGE => egui::MouseWheelUnit::Page, - _ => return, - }; - // delta sign is flipped to match native convention - let delta = -egui::vec2(event.delta_x() as f32, event.delta_y() as f32); - let modifiers = runner_lock.input.raw.modifiers; + let unit = match event.delta_mode() { + web_sys::WheelEvent::DOM_DELTA_PIXEL => egui::MouseWheelUnit::Point, + web_sys::WheelEvent::DOM_DELTA_LINE => egui::MouseWheelUnit::Line, + web_sys::WheelEvent::DOM_DELTA_PAGE => egui::MouseWheelUnit::Page, + _ => return, + }; + // delta sign is flipped to match native (winit) convention. + let delta = -egui::vec2(event.delta_x() as f32, event.delta_y() as f32); + let modifiers = runner_lock.input.raw.modifiers; - runner_lock.input.raw.events.push(egui::Event::MouseWheel { - unit, - delta, - modifiers, - }); - } - let scroll_multiplier = match event.delta_mode() { - web_sys::WheelEvent::DOM_DELTA_PAGE => { - canvas_size_in_points(runner_lock.canvas_id()).y - } - web_sys::WheelEvent::DOM_DELTA_LINE => { + runner_lock.input.raw.events.push(egui::Event::MouseWheel { + unit, + delta, + modifiers, + }); + + let scroll_multiplier = match unit { + egui::MouseWheelUnit::Page => canvas_size_in_points(runner_lock.canvas_id()).y, + egui::MouseWheelUnit::Line => { #[allow(clippy::let_and_return)] let points_per_scroll_line = 8.0; // Note that this is intentionally different from what we use in winit. points_per_scroll_line } - _ => 1.0, // DOM_DELTA_PIXEL + egui::MouseWheelUnit::Point => 1.0, }; - let mut delta = - -scroll_multiplier * egui::vec2(event.delta_x() as f32, event.delta_y() as f32); + let mut delta = scroll_multiplier * delta; // Report a zoom event in case CTRL (on Windows or Linux) or CMD (on Mac) is pressed. // This if-statement is equivalent to how `Modifiers.command` is determined in