-
Couldn't load subscription status.
- Fork 42
feat(mouse): enhance mouse events with grid coordinates #110
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?
Conversation
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.
Nice, pretty clean and works!
Had some comments
src/backend/utils.rs
Outdated
| let width = if width > 0 { width } else { 10 }; | ||
| let height = if height > 0 { height } else { 19 }; |
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.
We defined these constants somewhere, probably those can be used.
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.
added MouseEventHandler - a simple wrapper around the boxed fn.
src/backend/utils.rs
Outdated
| /// | ||
| /// This function creates a span with the same styling as terminal cells, | ||
| /// measures its dimensions, and then removes it from the DOM. | ||
| pub(super) fn measure_dom_cell_size( |
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.
I'm curious about a couple of things:
- Is this being affected by the font being used by the app? (e.g. defined in
index.html) - We should probably avoid calling this frequently, because manipulating DOM takes time. Is that right?
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.
i'll have to confirm - but iirc it's sparsely called (at init/resize maybe).
We should probably avoid calling this frequently, because manipulating DOM takes time. Is that right?
this is at least 1 or 2 PR:s ;) the DomBackend frequently does a layout pass on everything, at least that what i gathered from a brief look at the profiler. there's also a lack of memoization around all the CSS style strings, but in the grand scheme of things - it's not the current bottleneck (it marginally improved perf with the current impl).
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.
only called during init; i wonder if it handles browser zoom levels correctly. (i'll check tomorrow)
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.
i wonder if it handles browser zoom levels correctly.
it does
src/backend/dom.rs
Outdated
| if let Ok(element) = target.dyn_into::<web_sys::Element>() { | ||
| let rect = element.get_bounding_client_rect(); | ||
| let grid_rect = (rect.left(), rect.top(), rect.width(), rect.height()); | ||
| handler(MouseEvent::new_relative(event, cell_size_px, grid_rect)); | ||
| } else { | ||
| // Fallback to viewport-relative coordinates if we can't get the element | ||
| handler(MouseEvent::new(event, cell_size_px)); | ||
| } |
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.
I'm not sure what's going on here. What's new vs new_relative?
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.
hmm, i don't recall exactly - if it's hypothetical or encountered, but the new_relative is the mouse coordinates relative to the canvas (and not the 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.
so, during DOM rebuild, i think mouse events can still fire. i'm sure i can't replicate the behavior though - should we skip the fallback and only process the mouse event when we're sure that the cursor is hovering a cell. yeah... makes sense when i write it.
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.
replaced it with debug_assert() - better no event than a wrong event
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.
::new_relative has been removed in the latest series of commits
| /// Handles mouse events. | ||
| /// | ||
| /// This method takes a closure that will be called on every `mousemove`, 'mousedown', and `mouseup` | ||
| /// event. | ||
| fn on_mouse_event<F>(&self, mut callback: F) | ||
| where | ||
| F: FnMut(MouseEvent) + 'static, | ||
| { | ||
| let closure = Closure::<dyn FnMut(_)>::new(move |event: web_sys::MouseEvent| { | ||
| callback(event.into()); | ||
| }); | ||
| let window = window().unwrap(); | ||
| let document = window.document().unwrap(); | ||
| document | ||
| .add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref()) | ||
| .unwrap(); | ||
| document | ||
| .add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref()) | ||
| .unwrap(); | ||
| document | ||
| .add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref()) | ||
| .unwrap(); | ||
| closure.forget(); | ||
| } | ||
|
|
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.
It makes sense to do this in the Backend options, but I think it causes an inconsistency. We should follow the same style for all the events and callbacks imo.
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.
are you suggesting we move it into Terminal (or the other way round)?
For it to work as expected, we'd need some sort of lifecycle management on the closure (and not closure.forget()), so that closures can be added and removed. adding a second event callback is UB territory without deregistering the old callback. I assume this applies to the current key event callback registration too, but it didn't occur to me until now.
So, either we move it back to Terminal and fix the issues with double callback registration or we move the key event registration to backend init. For the former to work, I guess we need to figure out the flow too (e.g., should a new callback replace the old one or do we want to support multiple callbacks per type).
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.
(let me confirm this is actually the issue and not a misinterpretation of previous results)
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.
updated description, but it's been moved back into WebRenderer/WebEventHandler.
0541f05 to
28c09f8
Compare
|
current issue: dom backend pos are offset, canvas coordinates are likely inverted in relation to browser zoom level |
|
updated PR description to be more comprehensive |
|
i'll take another look through the code tomorrow, but i think this addresses all mentioned issues + fixes stuff i didn't think about previously (support mouse wheel events, exposing all mouse events leaving the choice of low-level or high-level mouse events to the user, offset bug fix etc), the minimal example has been updated to show the current cursor position and event data. maybe we should copy it to |
0a9ee70 to
8c2f781
Compare
|
i guess that's it; callback lifecycle management it a bit more managed. i've also restored the old "minimal" example and copied the other minimal to a new example: "mouse". |
|
Thx dog, this is exactly what I needed, right when I needed it. Going to give it a whirl :b |
|
@0x01d have you tried this yet? Man... I need to re-review this sometime |
|
@orhun Yes it worked like a charm. I use it on my website rbn.dev. edit: All is fine error on my end.. |
|
Damn, your website looks pretty cool! I should also rebuild my blog using ratzilla sometime. Do you mind adding it to the docs here? 😇 |
|
@orhun Would love that, thx.The whole codebase is open source on my profile. Would love some feedback, not immediately though as it still needs a massive refactor -mvp state right now. |
|
Added in #126 :) |
Mouse events now report grid coordinates (col, row) instead of pixel coordinates, and event registration has been unified through the terminal API with proper lifecycle management.
Key Changes
MouseEventnow hascol/rowfields for terminal cell positionsterminal.on_mouse_event()/terminal.on_key_event()Usage
Mouse Event Types
Available
MouseEventKindvariants:Moved- mouse move eventButtonDown(MouseButton)- button pressedButtonUp(MouseButton)- button releasedEntered- mouse entered terminal areaExited- mouse left terminal areaSingleClick(MouseButton)- single click (down + up)DoubleClick(MouseButton),- single click x2 in quick successionWheel { delta_x, delta_y, delta_z }- scroll in delta cols, rows (should remove z)Breaking Changes
mutfor event registrationBackend Support