Desktop: Implement pointer lock for NumberInput (#3638)

* Desktop: Implement pointer lock for NumberInput

* add shift and ctrl modifiers

* fixup
This commit is contained in:
Timon
2026-01-15 12:27:31 +01:00
committed by GitHub
parent 6616d1bfb1
commit 73682b482b
12 changed files with 150 additions and 26 deletions

View File

@@ -5,7 +5,7 @@ use std::sync::mpsc::{Receiver, Sender, SyncSender};
use std::thread;
use std::time::{Duration, Instant};
use winit::application::ApplicationHandler;
use winit::dpi::PhysicalSize;
use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::event::{ButtonSource, ElementState, MouseButton, WindowEvent};
use winit::event_loop::{ActiveEventLoop, ControlFlow};
use winit::window::WindowId;
@@ -27,6 +27,8 @@ pub(crate) struct App {
window_size: PhysicalSize<u32>,
window_maximized: bool,
window_fullscreen: bool,
pointer_position: PhysicalPosition<f64>,
pointer_lock_position: Option<PhysicalPosition<f64>>,
ui_scale: f64,
app_event_receiver: Receiver<AppEvent>,
app_event_scheduler: AppEventScheduler,
@@ -84,6 +86,8 @@ impl App {
window_size: PhysicalSize { width: 0, height: 0 },
window_maximized: false,
window_fullscreen: false,
pointer_position: Default::default(),
pointer_lock_position: Default::default(),
ui_scale: 1.,
app_event_receiver,
app_event_scheduler,
@@ -329,6 +333,12 @@ impl App {
window.clipboard_write(content);
}
}
DesktopFrontendMessage::PointerLock => {
self.pointer_lock_position = Some(self.pointer_position);
if let Some(window) = &self.window {
window.start_pointer_lock();
}
}
DesktopFrontendMessage::WindowClose => {
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
}
@@ -480,6 +490,26 @@ impl ApplicationHandler for App {
}
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
// Handle pointer lock release
if let Some(pointer_lock_position) = self.pointer_lock_position
&& let WindowEvent::PointerButton {
state: ElementState::Released,
button: ButtonSource::Mouse(MouseButton::Left),
..
} = event
{
self.pointer_lock_position = None;
if let Some(window) = &self.window {
window.end_pointer_lock();
}
self.cef_context.handle_window_event(&WindowEvent::PointerMoved {
device_id: None,
position: pointer_lock_position,
primary: true,
source: winit::event::PointerSource::Mouse,
});
}
self.cef_context.handle_window_event(&event);
match event {
@@ -556,6 +586,13 @@ impl ApplicationHandler for App {
self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message));
}
}
WindowEvent::PointerMoved { position, .. } | WindowEvent::PointerLeft { position: Some(position), .. } | WindowEvent::PointerEntered { position, .. }
if self.pointer_lock_position.is_none() =>
{
self.pointer_position = position;
}
_ => {}
}
@@ -563,6 +600,15 @@ impl ApplicationHandler for App {
self.cef_context.work();
}
fn device_event(&mut self, _event_loop: &dyn ActiveEventLoop, _device_id: Option<winit::event::DeviceId>, event: winit::event::DeviceEvent) {
if self.pointer_lock_position.is_some()
&& let winit::event::DeviceEvent::PointerMotion { delta: (x, y) } = event
{
let message = DesktopWrapperMessage::PointerLockMove { x, y };
self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message));
}
}
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
// Set a timeout in case we miss any cef schedule requests
let timeout = Instant::now() + Duration::from_millis(10);

View File

@@ -159,6 +159,16 @@ impl Window {
self.winit_window.set_cursor(cursor);
}
pub(crate) fn start_pointer_lock(&self) {
let _ = self.winit_window.set_cursor_grab(winit::window::CursorGrabMode::Locked);
self.winit_window.set_cursor_visible(false);
}
pub(crate) fn end_pointer_lock(&self) {
let _ = self.winit_window.set_cursor_grab(winit::window::CursorGrabMode::None);
self.winit_window.set_cursor_visible(true);
}
pub(crate) fn update_menu(&self, entries: Vec<MenuItem>) {
self.native_handle.update_menu(entries);
}