first post =) Just finished going through the Rust Book, and am trying one of my first Rust app with a GUI using nannou.
In my nannou GUI app, the mouse wheel is used for zooming in and out. However, in my laptop, the touchpad scroll triggers dozens of events with very small delta each. The zooming takes some millis to process, so I can't affort to process all these events.
How can I tell nannou to stop processing events until the previous one is completed?
In my experience with PyQt, the processing could be put in another thread, then a flag is set to ignore further events until the thread is finished. Is that a way to go in Rust/nannou?
Also, putting this event in a thread seems overkill for this simple app. All other events are ok, and even touchpad scroll in another laptop is not triggered that often. So I'm also tempted to create a not very elegant solution to this, something like "processes every n-th event" and tune n to something that works. Suggestions welcome.
The classic non-threaded approach would be to use a timer instead of immediately applying the scroll events as they arrive:
When a scroll event arrives, if no timer is running, start a timer and record the amount of scroll. Optionally, update the UI so that the user gets immediate feedback that scrolling did something, if it's possible to do so quickly.
When a scroll event arrives, and a timer is running, restart the timer and record the amount of additional scroll.
When the timer expires, apply all of the accumulated scroll at once.
However, I can't immediately see a way to do that in Nannou. It does look like it should be possible, but you might want to have a look at the various loop mode options.
I'm not familiar with nannou, but you probably don't need to solve this problem by adding explicit debouncing logic.
Right now, you're calling update(app, model) whenever some parameter changes, which immediately recalculates the image. Instead, set a flag that specifies that an update is needed, and if it is, call update() just before you draw, not from inside the handler of an individual event. This improves performance for all possible incoming events and avoids wasting time calculating updates that won't be seen, because window systems are generally smart about only asking the application to draw as frequently as is actually needed for the monitor and compositor.
Ah, but for redrawing, nannou provides only an &Model, no mutable access. But that's solvable; change the Model's field from
texture: wgpu::Texture,
to
texture: std::sync::OnceLock<wgpu::Texture>,
Then, in view(), use model.texture.get_or_init(|| ...) to compute the texture if it is absent, and in the event handlers, use model.texture = OnceLock::new(); to clear it when it is invalid (do that in all the places you currently use update()).
Cool! This was super easy to implement with minimal other changes... have actually removed more code than added =)
For reference, there is a good example of this application. I've used a LoopMode::Wait which matches my app (no changes/updates required unless a user input event occurs).
The app is working ok now with this change, but will only be able to do a good test later today, as it is my personal laptop that seems to be flooding the app with touchpad scroll events.
--- edit ---
The method is working very well in my laptop that emits many touchpad scroll events.