Skip to content

Conversation

Brayan-724
Copy link

@Brayan-724 Brayan-724 commented Sep 17, 2025

Closes #4115

Also written as fluent scrolling, smooth scroll is one of the details that Slint needs to have a better user experience.

In this PR, I add the opt-out smooth-scroll property to Flickable and platform-specific ScrollView that enables the smooth scrolling.

There's just one thing to resolve, in ensure_updated_listview I have to remove one line because it discards the running animated value, but I want some feedback to handle it better.

If everything is right, then I will change the doc.

@CLAassistant
Copy link

CLAassistant commented Sep 17, 2025

CLA assistant check
All committers have signed the CLA.

@Brayan-724 Brayan-724 force-pushed the master branch 4 times, most recently from f92a728 to 74fc84d Compare September 17, 2025 07:56
@Brayan-724
Copy link
Author

Brayan-724 commented Sep 17, 2025

After around ~50 minutes waiting (and wasting hours of machine-time) for CI, it may be improved with some cache like Swatinem/rust-cache.

In another project, I have used a similar strategy but with nix-cache, and it helps a lot in CI times.

Copy link
Member

@ogoffart ogoffart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR.
I haven't tried it yet, but it looks good at first glance.

I'm just wondering if the "smooth_scroll" property is really necessary or if we just want to always have smooth scroll.

If anything, it we could have properties in Flickable to configure the smooth scrolling, but I think it would be simpler to always have it.

let new_viewport_y = -inner.anchor_y + new_offset_y;
viewport_y.set(new_viewport_y);
// XXX: This `set` prevents smooth scroll animated value
// viewport_y.set(new_viewport_y);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see why this is a problem, but maybe there are better options:

Either:

  • try to avoid calling it if nothing changed (which should be most of the time as this is only useful if the height changed or something, so we could try to heck if it is not different from the current one before setting)
  • Instead of passing viewport_* properties, pass a Pin<&Flickable> to that function, and add a function in Flickable to scroll to that position. (Although thinking about this, it might not want to be what you want if scrolling.

@ogoffart
Copy link
Member

After around ~50 minutes waiting (and wasting hours of machine-time) for CI, it may be improved with some cache like Swatinem/rust-cache.

We do have that:
eg:

- uses: Swatinem/rust-cache@v2

Let us know if you find something that is misconfigured.

Regarding the listview tests, they are probably failing because they expect things to happen, fast. You can try to add i_slint_core::tests::slint_mock_elapsed_time in them.
You may need to use the SLINT_TEST_FILTER env variable when testing locally to get faster compilation: https://github.com/slint-ui/slint/blob/master/docs/testing.md#rust-driver

@Brayan-724
Copy link
Author

I'm just wondering if the "smooth_scroll" property is really necessary or if we just want to always have smooth scroll.

I think if someone already depends on no-smooth scrolling, there should be a way to disable it. I don't know a case for it, so if you think it's better to have a simpler API, then I remove it

@Brayan-724
Copy link
Author

Brayan-724 commented Sep 17, 2025

@ogoffart I'm working on tests and I have a big problem, maybe you know what to do.

tests/cases/elements/flickable.slint

// 19..21
flicked => {
    root.flicked += viewport_x/1phx * 100000 + viewport_y/1phx;
}

Here the value flicked is updated every time the callback gets triggered, but my current implementation only calls it at the start of the scroll, that means that viewport_* are 0.

// 258..263
// test scrolling behaviour
instance.window().dispatch_event(WindowEvent::PointerScrolled { position: LogicalPosition::new(175.0, 175.0), delta_x: -30.0, delta_y: -50.0 });
dbg!(instance.get_flicked());
assert_eq!(instance.get_flicked(), -3000050); //flicked got called after scrolling
instance.set_flicked(0);

But the test expects to be the final state.

I see many solutions:

  1. Update the test and remove that section, due to this is the new behavior.
  2. Update flicked callback to receive the final expected state
  3. Call flicked at the end of the scroll event (creates same issue as 4)
  4. Call flicked every time animated value is evaluated (Also needs to update test, but have more sense). This creates the following issue:

internal/core/items/flickable.rs

// 454..473
let should_hook_x = old_pos.x_length() != new_pos.x_length();
let should_hook_y = old_pos.y_length() != new_pos.y_length();
let should_hook = should_hook_x || should_hook_y;

if should_hook {
    let hook = move || {
        (Flickable::FIELD_OFFSETS.flicked).apply_pin(flick).call(&());
    };

    if should_hook_x {
        viewport_x.set_animated_value_hooked(new_pos.x_length(), SMOOTH_SCROLL_ANIM, hook);
        viewport_y.set_animated_value(new_pos.y_length(), SMOOTH_SCROLL_ANIM);
    } else {
        viewport_x.set_animated_value(new_pos.x_length(), SMOOTH_SCROLL_ANIM);
        viewport_y.set_animated_value_hooked(new_pos.y_length(), SMOOTH_SCROLL_ANIM, hook);
    }
} else {
    viewport_x.set_animated_value(new_pos.x_length(), SMOOTH_SCROLL_ANIM);
    viewport_y.set_animated_value(new_pos.y_length(), SMOOTH_SCROLL_ANIM);
}

I was trying to implement my fourth solution, with a new struct that calls any callback along the animation get evaluated, but flick variable is a pinned reference (Pin<&Flickable>) so can't be moved.

Do you know any way to implement this? Should I just implement another solution? Let me know your thoughts.

@ogoffart
Copy link
Member

This is a good question when should we emit the flicked callback.
I would think either option 3 or 4 makes the most sense (emitting flicked at the end or all the time till the end) but I realize that this is not how it is currently implemented for the normal flicking.

I think if someone already depends on no-smooth scrolling, there should be a way to disable it. I

Then it should be off by default.
But my feeling is that i don't think it should be optional.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feature: fluent scrolling
3 participants