Skip to content
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

Slider calls OnChanged for each value between steps #1748

Closed
Jacalz opened this issue Jan 9, 2021 · 4 comments
Closed

Slider calls OnChanged for each value between steps #1748

Jacalz opened this issue Jan 9, 2021 · 4 comments
Assignees
Labels
blocker Items that would block a forthcoming release bug Something isn't working optimization Tickets that could help Fyne apps run faster

Comments

@Jacalz
Copy link
Member

Jacalz commented Jan 9, 2021

Describe the bug:

A slider with a set step size (say for example 1), will call OnChanged() even on all the values it is draged over to get between the two steps, thus running the function many more times than what it should do.

To Reproduce:

Steps to reproduce the behaviour:

  1. Add a call to fmt.Println("Changed") in the slider.OnChanged callback.
  2. Drag the slider between two steps and see that it is called many more times than necessary

Screenshots:

slider-onchanged

Example code:

slider := &widget.Slider{Step: 1, Min: 0, Max: 10, OnChanged: func(f float64) {
	fmt.Println("Changed")
}}

Device (please complete the following information):

  • OS: Linux
  • Version: 5.10.5
  • Go version: 1.15.6
  • Fyne version: develop @ 5ff00e1
@Jacalz Jacalz added bug Something isn't working optimization Tickets that could help Fyne apps run faster labels Jan 9, 2021
@stuartmscott
Copy link
Member

stuartmscott commented Jan 10, 2021

I appreciate that the callback is triggered whenever the Slider receives a drag event, and if the callback is doing a lot of work it results in visible jank, but I'm not sure how else the slider could know when it is necessary to trigger the callback, or if the user will continue dragging. For example, if there is a timeout and the slider batches changes together, then people might say it is laggy.

One approach that springs to mind is to use a variable, a channel, and a goroutine. Whenever the OnChange handler is called, save the value to the variable, and use a select to try to write struct{} to a channel of size 1. A goroutine is running in an infinite loop reading from the channel and then executing your specific workload. If the slider doesn't move, nothing is sent on the channel, so the goroutine stays blocked. When the slider moves, the value is saved and an empty struct is sent, which triggers the goroutine to do the workload. When the slider triggers the callback multiple times, the first invocation will save the value and send a message through the channel, and each subsequent invocation will overwrite the variable to the latest value, but will not be able to send through the channel. When the goroutine finally gets around to reading the channel, it will only loop once, with the latest value.

Psuedocode (written while sleepy) :

var myVariable float64

myChannel := make(chan struct{}, 1)
defer close(myChannel) // so worker can exit when app quits

go func() {
    for _ := range myChannel {
        v := myVariable
        fmt.Println("Worker working with:", v)
        doSomething(v)
    }
}()

mySlider.OnChanged = func(v float64) {
    fmt.Println("Slider changed:", v)
    myVariable = v
    select {
    case myChannel <- struct{}{}:
        fmt.Println("Worker notified")
    default:
        fmt.Println("Worker already notified by previous OnChanged")
    }
}

@Jacalz
Copy link
Member Author

Jacalz commented Jan 10, 2021

The issue is not about only calling the last value, but that it calls before it hits the actual step. That is the issue here that I think should be fixed. If I drag my slider from position 1 to 4 (with a step size of 1) I want OnChanged() to be called 3 times, not upwards of 1000 times or more. That is just a waste of CPU resources.

@stuartmscott
Copy link
Member

Ah I see! Yes I agree the callback should only be fired in increments of Slider.Step

@andydotxyz andydotxyz self-assigned this Mar 19, 2021
@andydotxyz andydotxyz added the blocker Items that would block a forthcoming release label Mar 22, 2021
andydotxyz added a commit that referenced this issue Mar 23, 2021
Call OnChanged only when the slider.Value has changed, fixes #1748
@andydotxyz
Copy link
Member

Fixed and prepping for 2.0.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocker Items that would block a forthcoming release bug Something isn't working optimization Tickets that could help Fyne apps run faster
Projects
None yet
Development

No branches or pull requests

3 participants