Files
supersonic/ui/widgets/searcher.go
T

119 lines
2.2 KiB
Go

package widgets
import (
"sync"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
// Searcher is a search entry widget that will issue a search command
// (aka call OnSearched) when a short span of time has elapsed since
// the user typed into the widget.
type Searcher struct {
Entry *SearchEntry
OnSearched func(string)
searchGoroutine bool
pendingSearch bool
pendingSearchLock sync.Mutex
}
func NewSearcher() *Searcher {
s := &Searcher{
Entry: NewSearchEntry(),
}
s.Entry.OnChanged = s.onSearchTextChanged
return s
}
func (s *Searcher) onSearchTextChanged(text string) {
if text == "" {
s.sendSearch("")
return
}
s.pendingSearchLock.Lock()
defer s.pendingSearchLock.Unlock()
s.pendingSearch = true
if !s.searchGoroutine {
go s.waitAndSearch()
s.searchGoroutine = true
}
}
func (s *Searcher) waitAndSearch() {
t := time.NewTicker(200 * time.Millisecond)
var getReadyToSearch bool
var done bool
for !done {
<-t.C
s.pendingSearchLock.Lock()
if s.pendingSearch {
getReadyToSearch = true
s.pendingSearch = false
} else if getReadyToSearch {
s.sendSearch(s.Entry.Text)
t.Stop()
s.searchGoroutine = false
done = true
}
s.pendingSearchLock.Unlock()
}
}
func (s *Searcher) sendSearch(text string) {
if s.OnSearched != nil {
s.OnSearched(text)
}
}
type SearchEntry struct {
widget.Entry
}
func NewSearchEntry() *SearchEntry {
sf := &SearchEntry{}
sf.ExtendBaseWidget(sf)
sf.PlaceHolder = "Search"
sf.ActionItem = NewClearTextButton(func() {
sf.SetText("")
})
return sf
}
func (s *SearchEntry) Refresh() {
if s.Text == "" {
s.ActionItem.(*clearTextButton).Resource = theme.SearchIcon()
} else {
s.ActionItem.(*clearTextButton).Resource = theme.ContentClearIcon()
}
s.Entry.Refresh()
}
func (s *SearchEntry) MinSize() fyne.Size {
return fyne.NewSize(200, s.Entry.MinSize().Height)
}
var _ fyne.Tappable = (*clearTextButton)(nil)
type clearTextButton struct {
widget.Icon
OnTapped func()
}
func NewClearTextButton(onTapped func()) *clearTextButton {
c := &clearTextButton{OnTapped: onTapped}
c.ExtendBaseWidget(c)
c.Resource = theme.SearchIcon()
return c
}
func (c *clearTextButton) Tapped(*fyne.PointEvent) {
if c.OnTapped != nil {
c.OnTapped()
}
}