fyne-x
fyne-x copied to clipboard
CompletionEntry cannot type word when SetText is called in widget.List
As the following code shows, the text in CompletionEntry cannot be edited.
Screenshots
Steps to reproduce the behaviour:
- Run the code below
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/widget"
x "fyne.io/x/fyne/widget"
)
var data = []string{"a", "string", "list"}
var option = []string{"option1", "option2", "option3"}
func main() {
myApp := app.New()
myWindow := myApp.NewWindow("List Widget")
list := widget.NewList(
func() int {
return len(data)
},
func() fyne.CanvasObject {
return x.NewCompletionEntry(option)
},
func(i widget.ListItemID, o fyne.CanvasObject) {
e := o.(*x.CompletionEntry)
e.SetText(data[i])
e.OnChanged = func(s string) {
e.ShowCompletion()
}
})
myWindow.SetContent(list)
myWindow.ShowAndRun()
}
After debugging, I found that whenever I input in the input box, the widget.List.UpdateItem is also called, so the completionentry.text is set back. It is not clear for me why UpdateItem is called now.
It is not clear for me why UpdateItem is called now.
It will be called at least whenever a cell may need to be refreshed (usually due to re-use, for example if list scrolls).
Additionally it will be called if something calls List.Refresh()
.
Essentially the problem is that you are mutating data that is being re-loaded from static data. If however you updated your data source on edit (by setting the variable, or by using data binding) the issue would be resolved.
Referring to your suggestion, I re implemented the code. But when I specified the method OnChange
, the program crashed.
I debuged the code, and found:
func (c *CompletionEntry) maxSize() fyne.Size {
cnv := fyne.CurrentApp().Driver().CanvasForObject(c) // cnv is nil
...
}
The demo code is as beblow:
import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/widget"
x "fyne.io/x/fyne/widget"
)
var option = []string{"option1", "option2", "option3"}
func main() {
myApp := app.New()
myWindow := myApp.NewWindow("List Data")
data := binding.BindStringList(
&[]string{"Item 1", "Item 2", "Item 3"},
)
list := widget.NewListWithData(
data,
func() fyne.CanvasObject {
return x.NewCompletionEntry(option)
},
func(i binding.DataItem, o fyne.CanvasObject) {
e := o.(*x.CompletionEntry)
e.Bind(i.(binding.String))
e.OnChanged = func(s string) {
e.ShowCompletion()
}
})
add := widget.NewButton("Append", func() {
val := fmt.Sprintf("Item %d", data.Length()+1)
data.Append(val)
})
myWindow.SetContent(container.NewBorder(nil, add, nil, nil, list))
myWindow.CenterOnScreen()
myWindow.Resize(fyne.NewSize(600, 400))
myWindow.ShowAndRun()
}
Essentially the problem is that you are mutating data that is being re-loaded from static data. If however you updated your data source on edit (by setting the variable, or by using data binding) the issue would be resolved.
I think the problem should still be on the CompletionEntry component. Because I replace with widget.SelectEntry, the issue is resolved.
That it works this way with a SelectEntry
is more luck than anything. You are applying a static data source to a dynamic item of data, this mismatch may or may not cause problems but either way it is a race condition in your code.
You mentioned that the updated code crashes - can you provide the crash log and the replication steps that led to it please?
You mentioned that the updated code crashes - can you provide the crash log and the replication steps that led to it please?
To Reproduce:
Steps to reproduce the behaviour:
- Run the code below:
Crash Log
GOROOT=D:\ProgramFiles\go1.17.5 #gosetup
GOPATH=C:\Users\sid\go #gosetup
D:\ProgramFiles\go1.17.5\bin\go.exe build -o D:\projects\go-fyne\output\go_build_go_fyne.exe . #gosetup
D:\projects\go-fyne\output\go_build_go_fyne.exe
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0xa8 pc=0xc03f25]
goroutine 9 [running]:
fyne.io/x/fyne/widget.(*CompletionEntry).maxSize(0xc000332400)
C:/Users/sid/go/pkg/mod/fyne.io/x/[email protected]/widget/completionentry.go:91 +0xe5
fyne.io/x/fyne/widget.(*CompletionEntry).ShowCompletion(0xc000332400)
C:/Users/sid/go/pkg/mod/fyne.io/x/[email protected]/widget/completionentry.go:76 +0x1be
main.main.func2.1({0xc000332400, 0xc00008fe60})
D:/projects/go-fyne/main.go:32 +0x1d
fyne.io/fyne/v2/widget.(*Entry).updateText(0xc000332400, {0xfb0a80, 0x6})
C:/Users/sid/go/pkg/mod/fyne.io/fyne/[email protected]/widget/entry.go:1179 +0x9f
fyne.io/fyne/v2/widget.(*Entry).SetText(0xc000332400, {0xfb0a80, 0xc0000520a0})
C:/Users/sid/go/pkg/mod/fyne.io/fyne/[email protected]/widget/entry.go:457 +0x25
fyne.io/fyne/v2/widget.(*Entry).updateFromData(0xc000332400, {0x1068178, 0xc0000520a0})
C:/Users/sid/go/pkg/mod/fyne.io/fyne/[email protected]/widget/entry.go:1124 +0xc5
fyne.io/fyne/v2/widget.(*basicBinder).Bind.func1()
C:/Users/sid/go/pkg/mod/fyne.io/fyne/[email protected]/widget/bind_helper.go:25 +0xa2
fyne.io/fyne/v2/data/binding.(*listener).DataChanged(0x1)
C:/Users/sid/go/pkg/mod/fyne.io/fyne/[email protected]/data/binding/binding.go:55 +0x1a
fyne.io/fyne/v2/data/binding.queueItem.func1.1()
C:/Users/sid/go/pkg/mod/fyne.io/fyne/[email protected]/data/binding/queue.go:19 +0x42
created by fyne.io/fyne/v2/data/binding.queueItem.func1
C:/Users/sid/go/pkg/mod/fyne.io/fyne/[email protected]/data/binding/queue.go:17 +0x45
Process finished with the exit code 2
Example Code
package main
import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/widget"
x "fyne.io/x/fyne/widget"
)
var option = []string{"option1", "option2", "option3"}
func main() {
myApp := app.New()
myWindow := myApp.NewWindow("List Data")
data := binding.BindStringList(
&[]string{"Item 1", "Item 2", "Item 3"},
)
list := widget.NewListWithData(
data,
func() fyne.CanvasObject {
return x.NewCompletionEntry(option)
},
func(i binding.DataItem, o fyne.CanvasObject) {
e := o.(*x.CompletionEntry)
e.Bind(i.(binding.String))
e.OnChanged = func(s string) {
e.ShowCompletion()
}
})
add := widget.NewButton("Append", func() {
val := fmt.Sprintf("Item %d", data.Length()+1)
data.Append(val)
})
myWindow.SetContent(container.NewBorder(nil, add, nil, nil, list))
myWindow.CenterOnScreen()
myWindow.Resize(fyne.NewSize(600, 400))
myWindow.ShowAndRun()
}