editor: Smooth scrolling - #44827
Conversation
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
|
Thanks for this PR, feels smooth. Are there any plans to integrate this into vim-mode as well? (e.g., or is this not part of this PR? |
|
@niekdomi yep, I still need to make the changes. I'm currently perfecting the scroll animation |
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
the set_scroll_position of the editor saves to the db the position and creates event which is wasteful for an animation Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
ttytm
left a comment
There was a problem hiding this comment.
Amazing effort!
Hope giving input already doesn't bother you.
Taking it through manual testing, I found that it doesn’t work with the optional preceding [count].
For example, my personal default for C+e / Ctrl+y is to scroll 5 lines. Currently it is reverted to 1 line when smooth scrolling is enabled.
No matter which scrolling motion is pressed [count] is ignored.
I wouldn’t want this to be a blocker, though. So a fix could also be part of a separate PR.
|
@ttytm, thank you for the feedback. I'm primarily focusing on improving the animation, especially for trackpads. I'm waiting for some input from the team before I make significant changes and dive deep into the vim crate |
62fbef4 to
c929736
Compare
|
@marcocondrache when you mentioned detecting whether a device is "pre-smoothed" (macOS trackpad) or not (maybe standard mouse), is it possible to infer smooth scrolling from the scroll-delta and scroll-event frequency? for example, it seems macOS trackpads emit a bunch of events compared to a mouse scroll-wheel, and the scroll-deltas are usually much bigger for a click-wheel mouse and much smaller for a macOS trackpad (since it emits many more events). so could it be possible to sample the scroll device and guess if it's acting as a smoothed device? |
|
@seanstrom As I mentioned in my previous message, this is not deterministic. Some mice, such as Logitech devices, can emit scroll events with pixel-precise deltas, just like trackpads. With the information currently available, there is no reliable way to distinguish between them at the software level. This is also reflected in the macOS documentation for
So relying solely on event timing, delta precision, or event frequency is not reliable. High-DPI mice can emit as many events as, or even more events than, trackpads, especially when accounting for software-level enhancements. Firefox takes a similar approach: smooth scrolling is enabled by default for both mouse wheel and pixel-precise scroll inputs, without relying on a mouse-vs-trackpad distinction. |
|
@marcocondrache thanks for clarifying and adding more context I suppose I was wondering more whether if it's deterministic to detect pre-smoothing from a device, not necessarily whether a mouse or trackpad is being used. From what you mentioned, it seems like macOS provides a way to check if any device is providing precise deltas. The reason I ask is because of the jittering issues that happen with trackpad scrolling on macOS and I was wondering whether there's a simpler path to avoid post-processing the already smooth events. Since that could side-step the jittering issues by allowing the OS to handle the smoothing. for example, we wouldn't use zed smooth scrolling for certain devices because they support Though you mentioned that jittering is expected behaviour in debug builds, and I'm not sure if you mean that it's a known bug to fix, or it's an implementation detail that we need to live with for debug builds. Could you explain more? |
|
@seanstrom Jittering or lagging is expected in debug builds when smooth scrolling is enabled, mainly for performance reasons. This is similar to how opening the same file in release mode can feel fine, while in debug mode it may lag or become difficult to use. The two environments are not fully comparable from a performance perspective because release builds benefit from many compiler optimizations. That said, the jittering you are currently experiencing could also be a regression caused by the fixes I recently made, so I do not want to dismiss it as only a debug-build performance issue. Smooth scrolling is not lightweight, especially in Zed. We need to persist the scroll position, and the behavior also depends on the number of scroll events we receive. We also cannot rely on the OS to handle this for us, since Zed does not use native system components. We draw and compose everything ourselves using the OS graphics APIs. A device supporting precise deltas does not mean that smoothing is being applied. It only means the device can report more precise movement values. If precise deltas were enough on their own, large applications like Chrome and Firefox would not need to implement smooth scrolling for those devices. We also need to distinguish between actual regressions, jittering caused by debug-build performance, and other possible causes. Right now, there is no reliable way to ensure that smooth scrolling works correctly other than testing it directly. So if I make fixes in the vim-related code just to get some tests passing, there is a risk that I might introduce a visual bug in the animation or state handling. That is why I am working on a more comprehensive test suite: to make sure we can catch regressions without accidentally breaking the visual behavior of smooth scrolling. |
|
@marcocondrache I see but I suppose when you mention:
I thought Also I'm not sure what you mean by needing to handle smoothing scrolling internally, since on macOS with latest stable Zed I get similar smooth-scrolling to what I see on VSCode. Am I missing something here? |
|
@seanstrom Again, having precise delta events still doesn’t imply any kind of smoothing. Logitech mice can report precise deltas (so pixel based deltas) for every event while still scrolling line-by-line, so the resulting behavior is not smooth scrolling. What the docs are saying is that we should interpret the event delta as raw pixel-based values rather than line-based values. See: https://github.com/marcocondrache/zed/blob/4640666e094d518bb622a6c172a35679bb92d1c8/crates/gpui_macos/src/events.rs#L272 |
|
@seanstrom Don’t get me wrong: when we have precise deltas most of the time, such as with macOS trackpads, we effectively already get continuous scrolling. That said, this does not necessarily apply to all devices or cases. I think it depends on how we want to handle this. When I initially started this PR, I disabled smooth scrolling for precise-delta devices, but I later removed that change after realizing that my mouse was no longer getting smooth scrolling. One option would be to provide a setting to disable it, similar to Firefox. Another would be to implement a best-effort classifier, like VS Code does, which tries to infer the device based on the raw values: In any case, I would prefer to keep the classifier for a separate PR and, for now, agree on a sensible default while acknowledging that we may not cover smooth scrolling perfectly in all cases. Happy to discuss this further. I’d also love to hear your opinion, @MrSubidubi |
|
I do not have a strong opinion in either direction, but
definitely sounds like a good idea overall. Happy to go with whatever you decice upon for this here. |
|
@marcocondrache yeah I can see now that the pixels values from the precise-deltas could be too large (not tiny enough for continuous smooth scrolling), so we need to smooth them out. That makes sense, thanks again for walking through that. The classification approach is pretty interesting, I imagine that approach could allow for devices with native smooth scrolling to work without extra processing, though I can see your point about it complicating this PR. One thing to double check, if you don't mind, is the behaviour for scrolling when the feature is disabled. I think either something changed for the default scrolling behaviour (or the settings toggle for smooth scrolling is inverted), because I'm only seeing jittering (for a release build) when I have the feature disabled. I think I was testing with the most recent changes, here's a screen capture: smooth-scrolling-zed.mov |
| let behavior = behavior | ||
| .or(self | ||
| .smooth_scroll | ||
| .then_some(ScrollBehavior::RequestAnimation)) | ||
| .unwrap_or_default(); | ||
|
|
||
| if behavior == ScrollBehavior::Instant { | ||
| self.scroll_animation = Some(ScrollAnimation::Completed { | ||
| position: target_position, | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| if self | ||
| .scroll_animation | ||
| .is_some_and(|a| a.target_position() == target_position) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| if let Some(animation) = &mut self.scroll_animation { | ||
| animation.restart(target_position); |
There was a problem hiding this comment.
Think the error might lie here @marcocondrache , from a quick glance before I'll sign off, we could have errors when !self.smooth_scroll and behavior == Some(ScrollBehavior::RequestAnimation) since then we'll not hit that unwrap_or_default, not the == Instant right after and might end up with that flickering. Might be wrong though.
Anyway, good find and think worth a test if that or something similar is the case
There was a problem hiding this comment.
Nice catch indeed. I’ve fixed it, and I just need to add tests.
Unfortunately, it didn’t resolve the jittering issue when smooth scrolling is disabled, so I’ll need to dig deeper into that.
|
@seanstrom @xab3r The jittering should now be fixed. |
|
@marcocondrache thanks for the fixes, seems to be working well when tested on macOS with my trackpad (with both smooth-scrolling enabled and disabled). Screen.Recording.2026-06-22.at.13.28.33.movThough I was testing with my Logitech MX vertical mouse (which I think also emits precise pixel-deltas when scrolling), and it seems like there's a little glitch when scrolling without smooth-scrolling enabled. For example, occasionally when I do a full mouse-scroll by scrolling from edge-to-edge on the scroll-wheel, I notice that the end of my scroll has a weird "jump". It basically appears like I've finished scrolling and then suddenly jump to the real scroll position. Here's a screen capture for reference (it occurs a few times in the video, but not every time): Screen.Recording.2026-06-22.at.13.42.10.mov |
|
@seanstrom Thanks for these reports. The jump should be fixed as well. |
|
@marcocondrache I've been testing the recent changes, and it doesn't seem like the issue is resolved, we may want to revert the latest changes unless you're sure removing the Based on my manual testing, I think I found a simple way to reproduce the issue, though it did require me to do a couple of tricks to recreate the glitch.
My best guess here is that some of the smooth-scroll state is being persisted when toggled off, and perhaps it should be cleared (?). The good news is that it didn't seem to happen unless I toggled the smooth-scroll feature on and off and used both devices (trackpad and mouse) for scrolling. |
|
@seanstrom Once smooth scrolling is disabled, it no longer interferes with normal scrolling, so toggling it on or off shouldn’t be the cause of the regressions we observed. I’m no longer able to reproduce the jumping behavior when scrolling only with the mouse, which I was seeing before yesterday evening’s fix. I can reproduce it when scrolling with the trackpad and then interrupting those events with the mouse, but that appears to be an existing bug on Zed (tested with Zed Preview 1.8.2), not something introduced by this PR. FYI @MrSubidubi |
|
@marcocondrache, there are still a few issues: When the feature is The same goes for the mouse: as long as my finger is on the wheel, it feels more natural for the scroll to follow the wheel directly, without any added acceleration or smoothing. When I disable the feature, though, I hit a regression. The flickering is gone now, but when I press a key, the scroll often lags or skips.
"vim_mode": true,
"helix_mode": true
"ctrl-k": [
"action::Sequence",
["vim::LineDown", "vim::LineDown", "vim::LineDown", "vim::LineDown"]
]Zed version: |
Make this only pub(crate) in preparation for zed-industries#44827 Release Notes: - N/A
|
😢 |
|
damn |
|
A PR that has been under discussion for 10 months really makes it hard to have the courage to keep submitting PRs; many PRs for other issues are like this too |
This comment was marked as off-topic.
This comment was marked as off-topic.
I think all manpower went for delta dev, all zed is orphaned child. Many long awaited features, PRs became abandoned, lack of answears |
Closes #4355
Before:
zed-no-smooth.mp4
After:
zed.-.smooth.scroll.mp4
Ref: https://pavelfatin.com/scrolling-with-pleasure
Release Notes: