-
Notifications
You must be signed in to change notification settings - Fork 55
feat: add scroll events to MouseEvent #142
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -142,6 +142,40 @@ pub enum MouseButton { | |
| Unidentified, | ||
| } | ||
|
|
||
| /// Scroll delta with the original delta mode from the browser. | ||
| #[derive(Debug, Clone, Copy, Eq, PartialEq)] | ||
| pub enum ScrollDelta { | ||
| /// Delta in pixels | ||
| Pixels(i32), | ||
| /// Delta in lines (typically represents wheel notches) | ||
| Lines(i32), | ||
| /// Delta in pages | ||
| Pages(i32), | ||
| } | ||
|
|
||
| impl ScrollDelta { | ||
| /// DOM_DELTA_PIXEL: The units of measurement for the delta are pixels. | ||
| const DOM_DELTA_PIXEL: u32 = 0; | ||
| /// DOM_DELTA_LINE: The units of measurement for the delta are individual lines of text. | ||
| const DOM_DELTA_LINE: u32 = 1; | ||
| /// DOM_DELTA_PAGE: The units of measurement for the delta are pages. | ||
| const DOM_DELTA_PAGE: u32 = 2; | ||
|
|
||
| /// Normalize the scroll delta to discrete wheel steps/clicks. | ||
| /// | ||
| /// This converts the delta to an approximate number of wheel notches: | ||
| /// - Lines: Already represent wheel notches, returned as-is | ||
| /// - Pixels: Divided by ~100 pixels per notch | ||
| /// - Pages: Multiplied by ~10 notches per page | ||
| pub fn to_steps(self) -> i32 { | ||
| match self { | ||
| ScrollDelta::Pixels(px) => px / 100, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
where did you get 100 from? i think this value could vary across devices. we probably have to measure it if we care about pixels to rows/cols
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. its claude suggested and I approximately tried to verify that and "pages" to approximate 10 wheel events to scroll one page view, etc. But yes, I agree, they are arbitrary, I suspected this would be a problem.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i tried with: document.addEventListener('wheel', (e) => {
console.log({
deltaX: e.deltaX,
deltaY: e.deltaY,
deltaMode: ['PIXEL', 'LINE', 'PAGE'][e.deltaMode],
ctrlKey: e.ctrlKey,
shiftKey: e.shiftKey
});
});depending on how quick i scroll the wheel, on at 27" 1440p screen, runing kde/wayland:
so 100 is definitely too high, esp for ff. |
||
| ScrollDelta::Lines(lines) => lines, | ||
| ScrollDelta::Pages(pages) => pages * 10, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// A mouse event. | ||
| #[derive(Debug, Clone, Eq, PartialEq)] | ||
| pub enum MouseEventKind { | ||
|
|
@@ -151,6 +185,10 @@ pub enum MouseEventKind { | |
| Pressed, | ||
| /// Mouse button released | ||
| Released, | ||
| /// Mouse scrolled vertically (positive = down, negative = up) | ||
| ScrolledVertical(ScrollDelta), | ||
| /// Mouse scrolled horizontally (positive = right, negative = left) | ||
| ScrolledHorizontal(ScrollDelta), | ||
| /// Unidentified mouse event | ||
| Unidentified, | ||
| } | ||
|
|
@@ -205,3 +243,47 @@ impl From<String> for MouseEventKind { | |
| } | ||
| } | ||
| } | ||
|
|
||
| /// Convert a [`web_sys::WheelEvent`] to a [`MouseEvent`]. | ||
| impl From<web_sys::WheelEvent> for MouseEvent { | ||
| fn from(event: web_sys::WheelEvent) -> Self { | ||
| let ctrl = event.ctrl_key(); | ||
| let alt = event.alt_key(); | ||
| let shift = event.shift_key(); | ||
| let delta_mode = event.delta_mode(); | ||
| let delta_x = event.delta_x(); | ||
| let delta_y = event.delta_y(); | ||
|
|
||
| // Create ScrollDelta based on the browser's delta mode | ||
| let to_scroll_delta = |delta: f64| -> ScrollDelta { | ||
| let delta_int = delta as i32; | ||
| match delta_mode { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just a thought, but the more i think about it: would it maybe be easier if we treated all scroll events as either -1 or +1 (or some other increment)? i don't really see pixel-perfect scrolling translating to TUIs. this would omit the need for resolving actual values behind
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's how I use it, but thought it better left to the user to decide if they want to use that information or discard it. |
||
| ScrollDelta::DOM_DELTA_PIXEL => ScrollDelta::Pixels(delta_int), | ||
| ScrollDelta::DOM_DELTA_LINE => ScrollDelta::Lines(delta_int), | ||
| ScrollDelta::DOM_DELTA_PAGE => ScrollDelta::Pages(delta_int), | ||
| _ => ScrollDelta::Pixels(delta_int), // fallback to pixels | ||
| } | ||
| }; | ||
|
|
||
| let scroll_x = to_scroll_delta(delta_x); | ||
| let scroll_y = to_scroll_delta(delta_y); | ||
|
|
||
| // Determine the event kind based on which delta is larger | ||
| // Compare normalized steps to determine primary scroll direction | ||
| let event_kind = if scroll_x.to_steps().abs() > scroll_y.to_steps().abs() { | ||
| MouseEventKind::ScrolledHorizontal(scroll_x) | ||
| } else { | ||
| MouseEventKind::ScrolledVertical(scroll_y) | ||
| }; | ||
|
|
||
| MouseEvent { | ||
| button: MouseButton::Unidentified, | ||
| event: event_kind, | ||
| x: event.client_x() as u32, | ||
| y: event.client_y() as u32, | ||
| ctrl, | ||
| alt, | ||
| shift, | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -65,6 +65,24 @@ pub trait WebRenderer { | |
| closure.forget(); | ||
| } | ||
|
|
||
| /// Handles wheel events. | ||
| /// | ||
| /// This method takes a closure that will be called on every `wheel` event. | ||
| fn on_wheel_event<F>(&self, mut callback: F) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would it make sense to merge |
||
| where | ||
| F: FnMut(MouseEvent) + 'static, | ||
| { | ||
| let closure = Closure::<dyn FnMut(_)>::new(move |event: web_sys::WheelEvent| { | ||
| callback(event.into()); | ||
| }); | ||
| let window = window().unwrap(); | ||
| let document = window.document().unwrap(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can use |
||
| document | ||
| .add_event_listener_with_callback("wheel", closure.as_ref().unchecked_ref()) | ||
| .unwrap(); | ||
| closure.forget(); | ||
| } | ||
|
|
||
| /// Requests an animation frame. | ||
| fn request_animation_frame(f: &Closure<dyn FnMut()>) { | ||
| window() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe track scrolled and h&v separately, otherwise it can give the impression that ratzilla does not distinguish between the two.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is h&v?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, horizontal and vertical