Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API for raw mouse motion #4063

Merged
merged 3 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions crates/eframe/src/native/glow_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,33 @@ impl WinitApp for GlowWinitApp {
}
}

winit::event::Event::DeviceEvent {
device_id: _,
event: winit::event::DeviceEvent::MouseMotion { delta },
} => {
if let Some(running) = &mut self.running {
let mut glutin = running.glutin.borrow_mut();
if let Some(viewport) = glutin
.focused_viewport
.and_then(|viewport| glutin.viewports.get_mut(&viewport))
{
if let Some(egui_winit) = viewport.egui_winit.as_mut() {
egui_winit.on_mouse_motion(*delta);
}

if let Some(window) = viewport.window.as_ref() {
EventResult::RepaintNext(window.id())
} else {
EventResult::Wait
}
} else {
EventResult::Wait
}
} else {
EventResult::Wait
}
}

#[cfg(feature = "accesskit")]
winit::event::Event::UserEvent(UserEvent::AccessKitActionRequest(
accesskit_winit::ActionRequestEvent { request, window_id },
Expand Down
27 changes: 27 additions & 0 deletions crates/eframe/src/native/wgpu_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,33 @@ impl WinitApp for WgpuWinitApp {
}
}

winit::event::Event::DeviceEvent {
device_id: _,
event: winit::event::DeviceEvent::MouseMotion { delta },
} => {
if let Some(running) = &mut self.running {
let mut shared = running.shared.borrow_mut();
if let Some(viewport) = shared
.focused_viewport
.and_then(|viewport| shared.viewports.get_mut(&viewport))
{
if let Some(egui_winit) = viewport.egui_winit.as_mut() {
egui_winit.on_mouse_motion(*delta);
}

if let Some(window) = viewport.window.as_ref() {
EventResult::RepaintNext(window.id())
} else {
EventResult::Wait
}
} else {
EventResult::Wait
}
} else {
EventResult::Wait
}
}

#[cfg(feature = "accesskit")]
winit::event::Event::UserEvent(UserEvent::AccessKitActionRequest(
accesskit_winit::ActionRequestEvent { request, window_id },
Expand Down
7 changes: 7 additions & 0 deletions crates/egui-winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,13 @@ impl State {
}
}

pub fn on_mouse_motion(&mut self, delta: (f64, f64)) {
self.egui_input.events.push(egui::Event::MouseMoved(Vec2 {
x: delta.0 as f32,
y: delta.1 as f32,
}));
}

/// Call this when there is a new [`accesskit::ActionRequest`].
///
/// The result can be found in [`Self::egui_input`] and be extracted with [`Self::take_egui_input`].
Expand Down
6 changes: 6 additions & 0 deletions crates/egui/src/data/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,12 @@ pub enum Event {
/// The mouse or touch moved to a new place.
PointerMoved(Pos2),

/// The mouse moved, the units are unspecified.
/// Represents the actual movement of the mouse, without acceleration or clamped by screen edges.
/// `PointerMoved` and `MouseMoved` can be sent at the same time.
/// This event is optional. If the integration can not determine unfiltered motion it should not send this event.
MouseMoved(Vec2),

/// A mouse button was pressed or released (or a touch started or stopped).
PointerButton {
/// Where is the pointer?
Expand Down
20 changes: 20 additions & 0 deletions crates/egui/src/input_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,11 @@ pub struct PointerState {
/// How much the pointer moved compared to last frame, in points.
delta: Vec2,

/// How much the mouse moved since the last frame, in unspecified units.
/// Represents the actual movement of the mouse, without acceleration or clamped by screen edges.
/// May be unavailable on some integrations.
motion: Option<Vec2>,

/// Current velocity of pointer.
velocity: Vec2,

Expand Down Expand Up @@ -664,6 +669,7 @@ impl Default for PointerState {
latest_pos: None,
interact_pos: None,
delta: Vec2::ZERO,
motion: None,
velocity: Vec2::ZERO,
pos_history: History::new(0..1000, 0.1),
down: Default::default(),
Expand All @@ -690,6 +696,9 @@ impl PointerState {

let old_pos = self.latest_pos;
self.interact_pos = self.latest_pos;
if self.motion.is_some() {
self.motion = Some(Vec2::ZERO);
}

for event in &new.events {
match event {
Expand Down Expand Up @@ -775,6 +784,7 @@ impl PointerState {
self.latest_pos = None;
// 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 Expand Up @@ -819,6 +829,14 @@ impl PointerState {
self.delta
}

/// How much the mouse moved since the last frame, in unspecified units.
/// Represents the actual movement of the mouse, without acceleration or clamped by screen edges.
/// May be unavailable on some integrations.
#[inline(always)]
pub fn motion(&self) -> Option<Vec2> {
self.motion
}

/// Current velocity of pointer.
#[inline(always)]
pub fn velocity(&self) -> Vec2 {
Expand Down Expand Up @@ -1139,6 +1157,7 @@ impl PointerState {
latest_pos,
interact_pos,
delta,
motion,
velocity,
pos_history: _,
down,
Expand All @@ -1155,6 +1174,7 @@ impl PointerState {
ui.label(format!("latest_pos: {latest_pos:?}"));
ui.label(format!("interact_pos: {interact_pos:?}"));
ui.label(format!("delta: {delta:?}"));
ui.label(format!("motion: {motion:?}"));
ui.label(format!(
"velocity: [{:3.0} {:3.0}] points/sec",
velocity.x, velocity.y
Expand Down
14 changes: 14 additions & 0 deletions crates/egui/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,20 @@ impl Response {
}
}

/// If dragged, how far did the mouse move?
/// This will use raw mouse movement if provided by the integration, otherwise will fall back to [`Response::drag_delta`]
/// Raw mouse movement is unaccelerated and unclamped by screen boundaries, and does not relate to any position on the screen.
/// This may be useful in certain situations such as draggable values and 3D cameras, where screen position does not matter.
#[inline]
pub fn drag_motion(&self) -> Vec2 {
if self.dragged() {
self.ctx
.input(|i| i.pointer.motion().unwrap_or(i.pointer.delta()))
} else {
Vec2::ZERO
}
}

/// If the user started dragging this widget this frame, store the payload for drag-and-drop.
#[doc(alias = "drag and drop")]
pub fn dnd_set_drag_payload<Payload: Any + Send + Sync>(&self, payload: Payload) {
Expand Down
2 changes: 1 addition & 1 deletion crates/egui_demo_app/src/apps/custom3d_glow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl Custom3d {
let (rect, response) =
ui.allocate_exact_size(egui::Vec2::splat(300.0), egui::Sense::drag());

self.angle += response.drag_delta().x * 0.01;
self.angle += response.drag_motion().x * 0.01;

// Clone locals so we can move them into the paint callback:
let angle = self.angle;
Expand Down
2 changes: 1 addition & 1 deletion crates/egui_demo_app/src/apps/custom3d_wgpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ impl Custom3d {
let (rect, response) =
ui.allocate_exact_size(egui::Vec2::splat(300.0), egui::Sense::drag());

self.angle += response.drag_delta().x * 0.01;
self.angle += response.drag_motion().x * 0.01;
ui.painter().add(egui_wgpu::Callback::new_paint_callback(
rect,
CustomTriangleCallback { angle: self.angle },
Expand Down
2 changes: 1 addition & 1 deletion examples/custom_3d_glow/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl MyApp {
let (rect, response) =
ui.allocate_exact_size(egui::Vec2::splat(300.0), egui::Sense::drag());

self.angle += response.drag_delta().x * 0.01;
self.angle += response.drag_motion().x * 0.01;

// Clone locals so we can move them into the paint callback:
let angle = self.angle;
Expand Down
Loading