simplify SearchEntry and extract a debouncer util func
This commit is contained in:
@@ -26,7 +26,7 @@ type AlbumsPage struct {
|
|||||||
lm *backend.LibraryManager
|
lm *backend.LibraryManager
|
||||||
grid *widgets.GridView
|
grid *widgets.GridView
|
||||||
searchGrid *widgets.GridView
|
searchGrid *widgets.GridView
|
||||||
searcher *widgets.Searcher
|
searcher *widgets.SearchEntry
|
||||||
searchText string
|
searchText string
|
||||||
titleDisp *widget.RichText
|
titleDisp *widget.RichText
|
||||||
sortOrder *selectWidget
|
sortOrder *selectWidget
|
||||||
@@ -74,7 +74,7 @@ func NewAlbumsPage(cfg *backend.AlbumsPageConfig, contr *controller.Controller,
|
|||||||
iter := lm.AlbumsIter(backend.AlbumSortOrder(a.sortOrder.Selected))
|
iter := lm.AlbumsIter(backend.AlbumSortOrder(a.sortOrder.Selected))
|
||||||
a.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), im)
|
a.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), im)
|
||||||
contr.ConnectAlbumGridActions(a.grid)
|
contr.ConnectAlbumGridActions(a.grid)
|
||||||
a.searcher = widgets.NewSearcher()
|
a.searcher = widgets.NewSearchEntry()
|
||||||
a.searcher.OnSearched = a.OnSearched
|
a.searcher.OnSearched = a.OnSearched
|
||||||
a.createContainer(false)
|
a.createContainer(false)
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ func NewAlbumsPage(cfg *backend.AlbumsPageConfig, contr *controller.Controller,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *AlbumsPage) createContainer(searchgrid bool) {
|
func (a *AlbumsPage) createContainer(searchgrid bool) {
|
||||||
searchVbox := container.NewVBox(layout.NewSpacer(), a.searcher.Entry, layout.NewSpacer())
|
searchVbox := container.NewVBox(layout.NewSpacer(), a.searcher, layout.NewSpacer())
|
||||||
sortVbox := container.NewVBox(layout.NewSpacer(), a.sortOrder, layout.NewSpacer())
|
sortVbox := container.NewVBox(layout.NewSpacer(), a.sortOrder, layout.NewSpacer())
|
||||||
g := a.grid
|
g := a.grid
|
||||||
if searchgrid {
|
if searchgrid {
|
||||||
@@ -115,7 +115,7 @@ func restoreAlbumsPage(saved *savedAlbumsPage) *AlbumsPage {
|
|||||||
a.sortOrder.Selected = saved.sortOrder
|
a.sortOrder.Selected = saved.sortOrder
|
||||||
a.sortOrder.OnChanged = a.onSortOrderChanged
|
a.sortOrder.OnChanged = a.onSortOrderChanged
|
||||||
a.grid = widgets.NewGridViewFromState(saved.gridState)
|
a.grid = widgets.NewGridViewFromState(saved.gridState)
|
||||||
a.searcher = widgets.NewSearcher()
|
a.searcher = widgets.NewSearchEntry()
|
||||||
a.searcher.OnSearched = a.OnSearched
|
a.searcher.OnSearched = a.OnSearched
|
||||||
a.searcher.Entry.Text = saved.searchText
|
a.searcher.Entry.Text = saved.searchText
|
||||||
a.searchText = saved.searchText
|
a.searchText = saved.searchText
|
||||||
@@ -147,7 +147,7 @@ func (a *AlbumsPage) Route() controller.Route {
|
|||||||
var _ Searchable = (*AlbumsPage)(nil)
|
var _ Searchable = (*AlbumsPage)(nil)
|
||||||
|
|
||||||
func (a *AlbumsPage) SearchWidget() fyne.Focusable {
|
func (a *AlbumsPage) SearchWidget() fyne.Focusable {
|
||||||
return a.searcher.Entry
|
return a.searcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AlbumsPage) Reload() {
|
func (a *AlbumsPage) Reload() {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ type ArtistsGenresPage struct {
|
|||||||
|
|
||||||
titleDisp *widget.RichText
|
titleDisp *widget.RichText
|
||||||
container *fyne.Container
|
container *fyne.Container
|
||||||
searcher *widgets.Searcher
|
searcher *widgets.SearchEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewArtistsGenresPage(isGenresPage bool, contr *controller.Controller, sm *backend.ServerManager) *ArtistsGenresPage {
|
func NewArtistsGenresPage(isGenresPage bool, contr *controller.Controller, sm *backend.ServerManager) *ArtistsGenresPage {
|
||||||
@@ -63,7 +63,7 @@ func newArtistsGenresPage(isGenresPage bool, contr *controller.Controller, sm *b
|
|||||||
a.contr.NavigateTo(controller.ArtistRoute(id))
|
a.contr.NavigateTo(controller.ArtistRoute(id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
a.searcher = widgets.NewSearcher()
|
a.searcher = widgets.NewSearchEntry()
|
||||||
a.searcher.OnSearched = a.onSearched
|
a.searcher.OnSearched = a.onSearched
|
||||||
a.searcher.Entry.Text = searchText
|
a.searcher.Entry.Text = searchText
|
||||||
a.buildContainer()
|
a.buildContainer()
|
||||||
@@ -111,7 +111,7 @@ func (a *ArtistsGenresPage) onSearched(query string) {
|
|||||||
var _ Searchable = (*ArtistsGenresPage)(nil)
|
var _ Searchable = (*ArtistsGenresPage)(nil)
|
||||||
|
|
||||||
func (a *ArtistsGenresPage) SearchWidget() fyne.Focusable {
|
func (a *ArtistsGenresPage) SearchWidget() fyne.Focusable {
|
||||||
return a.searcher.Entry
|
return a.searcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ArtistsGenresPage) Route() controller.Route {
|
func (a *ArtistsGenresPage) Route() controller.Route {
|
||||||
@@ -175,7 +175,7 @@ func (a *ArtistsGenresPage) buildGenresListModel(genres []*subsonic.Genre) []wid
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *ArtistsGenresPage) buildContainer() {
|
func (a *ArtistsGenresPage) buildContainer() {
|
||||||
searchVbox := container.NewVBox(layout.NewSpacer(), a.searcher.Entry, layout.NewSpacer())
|
searchVbox := container.NewVBox(layout.NewSpacer(), a.searcher, layout.NewSpacer())
|
||||||
a.container = container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 5, PadBottom: 15},
|
a.container = container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 5, PadBottom: 15},
|
||||||
container.NewBorder(
|
container.NewBorder(
|
||||||
container.New(&layouts.MaxPadLayout{PadLeft: -5},
|
container.New(&layouts.MaxPadLayout{PadLeft: -5},
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ type FavoritesPage struct {
|
|||||||
searchGrid *widgets.GridView
|
searchGrid *widgets.GridView
|
||||||
artistListCtr *fyne.Container
|
artistListCtr *fyne.Container
|
||||||
tracklistCtr *fyne.Container
|
tracklistCtr *fyne.Container
|
||||||
searcher *widgets.Searcher
|
searcher *widgets.SearchEntry
|
||||||
titleDisp *widget.RichText
|
titleDisp *widget.RichText
|
||||||
toggleBtns *widgets.ToggleButtonGroup
|
toggleBtns *widgets.ToggleButtonGroup
|
||||||
container *fyne.Container
|
container *fyne.Container
|
||||||
@@ -78,13 +78,13 @@ func (a *FavoritesPage) createHeader(activeBtnIdx int, searchText string) {
|
|||||||
widget.NewButtonWithIcon("", myTheme.AlbumIcon, a.onShowFavoriteAlbums),
|
widget.NewButtonWithIcon("", myTheme.AlbumIcon, a.onShowFavoriteAlbums),
|
||||||
widget.NewButtonWithIcon("", myTheme.ArtistIcon, a.onShowFavoriteArtists),
|
widget.NewButtonWithIcon("", myTheme.ArtistIcon, a.onShowFavoriteArtists),
|
||||||
widget.NewButtonWithIcon("", myTheme.TracksIcon, a.onShowFavoriteSongs))
|
widget.NewButtonWithIcon("", myTheme.TracksIcon, a.onShowFavoriteSongs))
|
||||||
a.searcher = widgets.NewSearcher()
|
a.searcher = widgets.NewSearchEntry()
|
||||||
a.searcher.OnSearched = a.OnSearched
|
a.searcher.OnSearched = a.OnSearched
|
||||||
a.searcher.Entry.Text = searchText
|
a.searcher.Entry.Text = searchText
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *FavoritesPage) createContainer(initialView fyne.CanvasObject) {
|
func (a *FavoritesPage) createContainer(initialView fyne.CanvasObject) {
|
||||||
searchVbox := container.NewVBox(layout.NewSpacer(), a.searcher.Entry, layout.NewSpacer())
|
searchVbox := container.NewVBox(layout.NewSpacer(), a.searcher, layout.NewSpacer())
|
||||||
a.container = container.NewBorder(
|
a.container = container.NewBorder(
|
||||||
container.NewHBox(util.NewHSpace(9), a.titleDisp, container.NewCenter(a.toggleBtns), layout.NewSpacer(), searchVbox, util.NewHSpace(15)),
|
container.NewHBox(util.NewHSpace(9), a.titleDisp, container.NewCenter(a.toggleBtns), layout.NewSpacer(), searchVbox, util.NewHSpace(15)),
|
||||||
nil, nil, nil, initialView)
|
nil, nil, nil, initialView)
|
||||||
@@ -182,7 +182,7 @@ func (a *FavoritesPage) Save() SavedPage {
|
|||||||
var _ Searchable = (*FavoritesPage)(nil)
|
var _ Searchable = (*FavoritesPage)(nil)
|
||||||
|
|
||||||
func (a *FavoritesPage) SearchWidget() fyne.Focusable {
|
func (a *FavoritesPage) SearchWidget() fyne.Focusable {
|
||||||
return a.searcher.Entry
|
return a.searcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *FavoritesPage) OnSearched(query string) {
|
func (a *FavoritesPage) OnSearched(query string) {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type GenrePage struct {
|
|||||||
lm *backend.LibraryManager
|
lm *backend.LibraryManager
|
||||||
grid *widgets.GridView
|
grid *widgets.GridView
|
||||||
searchGrid *widgets.GridView
|
searchGrid *widgets.GridView
|
||||||
searcher *widgets.Searcher
|
searcher *widgets.SearchEntry
|
||||||
searchText string
|
searchText string
|
||||||
titleDisp *widget.RichText
|
titleDisp *widget.RichText
|
||||||
playRandom *widget.Button
|
playRandom *widget.Button
|
||||||
@@ -55,7 +55,7 @@ func NewGenrePage(genre string, contr *controller.Controller, pm *backend.Playba
|
|||||||
iter := g.lm.GenreIter(g.genre)
|
iter := g.lm.GenreIter(g.genre)
|
||||||
g.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), g.im)
|
g.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), g.im)
|
||||||
g.contr.ConnectAlbumGridActions(g.grid)
|
g.contr.ConnectAlbumGridActions(g.grid)
|
||||||
g.searcher = widgets.NewSearcher()
|
g.searcher = widgets.NewSearchEntry()
|
||||||
g.searcher.OnSearched = g.OnSearched
|
g.searcher.OnSearched = g.OnSearched
|
||||||
g.createContainer(false)
|
g.createContainer(false)
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ func NewGenrePage(genre string, contr *controller.Controller, pm *backend.Playba
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GenrePage) createContainer(searchGrid bool) {
|
func (g *GenrePage) createContainer(searchGrid bool) {
|
||||||
searchVbox := container.NewVBox(layout.NewSpacer(), g.searcher.Entry, layout.NewSpacer())
|
searchVbox := container.NewVBox(layout.NewSpacer(), g.searcher, layout.NewSpacer())
|
||||||
gr := g.grid
|
gr := g.grid
|
||||||
if searchGrid {
|
if searchGrid {
|
||||||
gr = g.searchGrid
|
gr = g.searchGrid
|
||||||
@@ -94,7 +94,7 @@ func restoreGenrePage(saved *savedGenrePage) *GenrePage {
|
|||||||
}
|
}
|
||||||
g.playRandom = widget.NewButtonWithIcon(" Play random", myTheme.ShuffleIcon, g.playRandomSongs)
|
g.playRandom = widget.NewButtonWithIcon(" Play random", myTheme.ShuffleIcon, g.playRandomSongs)
|
||||||
g.grid = widgets.NewGridViewFromState(saved.gridState)
|
g.grid = widgets.NewGridViewFromState(saved.gridState)
|
||||||
g.searcher = widgets.NewSearcher()
|
g.searcher = widgets.NewSearchEntry()
|
||||||
g.searcher.OnSearched = g.OnSearched
|
g.searcher.OnSearched = g.OnSearched
|
||||||
g.searcher.Entry.Text = saved.searchText
|
g.searcher.Entry.Text = saved.searchText
|
||||||
g.searchText = saved.searchText
|
g.searchText = saved.searchText
|
||||||
@@ -142,7 +142,7 @@ func (g *GenrePage) Save() SavedPage {
|
|||||||
var _ Searchable = (*AlbumsPage)(nil)
|
var _ Searchable = (*AlbumsPage)(nil)
|
||||||
|
|
||||||
func (g *GenrePage) SearchWidget() fyne.Focusable {
|
func (g *GenrePage) SearchWidget() fyne.Focusable {
|
||||||
return g.searcher.Entry
|
return g.searcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GenrePage) OnSearched(query string) {
|
func (g *GenrePage) OnSearched(query string) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ type PlaylistsPage struct {
|
|||||||
searchedPlaylists []*subsonic.Playlist
|
searchedPlaylists []*subsonic.Playlist
|
||||||
|
|
||||||
viewToggle *widgets.ToggleButtonGroup
|
viewToggle *widgets.ToggleButtonGroup
|
||||||
searcher *widgets.Searcher
|
searcher *widgets.SearchEntry
|
||||||
titleDisp *widget.RichText
|
titleDisp *widget.RichText
|
||||||
container *fyne.Container
|
container *fyne.Container
|
||||||
listView *PlaylistList
|
listView *PlaylistList
|
||||||
@@ -56,7 +56,7 @@ func newPlaylistsPage(contr *controller.Controller, cfg *backend.PlaylistsPageCo
|
|||||||
}
|
}
|
||||||
a.ExtendBaseWidget(a)
|
a.ExtendBaseWidget(a)
|
||||||
a.titleDisp.Segments[0].(*widget.TextSegment).Style.SizeName = theme.SizeNameHeadingText
|
a.titleDisp.Segments[0].(*widget.TextSegment).Style.SizeName = theme.SizeNameHeadingText
|
||||||
a.searcher = widgets.NewSearcher()
|
a.searcher = widgets.NewSearchEntry()
|
||||||
a.searcher.OnSearched = a.onSearched
|
a.searcher.OnSearched = a.onSearched
|
||||||
a.searcher.Entry.Text = searchText
|
a.searcher.Entry.Text = searchText
|
||||||
a.viewToggle = widgets.NewToggleButtonGroup(0,
|
a.viewToggle = widgets.NewToggleButtonGroup(0,
|
||||||
@@ -199,7 +199,7 @@ func (a *PlaylistsPage) refreshView(playlists []*subsonic.Playlist) {
|
|||||||
var _ Searchable = (*PlaylistsPage)(nil)
|
var _ Searchable = (*PlaylistsPage)(nil)
|
||||||
|
|
||||||
func (a *PlaylistsPage) SearchWidget() fyne.Focusable {
|
func (a *PlaylistsPage) SearchWidget() fyne.Focusable {
|
||||||
return a.searcher.Entry
|
return a.searcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *PlaylistsPage) Route() controller.Route {
|
func (a *PlaylistsPage) Route() controller.Route {
|
||||||
@@ -233,7 +233,7 @@ func (s *savedPlaylistsPage) Restore() Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *PlaylistsPage) buildContainer(initialView fyne.CanvasObject) {
|
func (a *PlaylistsPage) buildContainer(initialView fyne.CanvasObject) {
|
||||||
searchVbox := container.NewVBox(layout.NewSpacer(), a.searcher.Entry, layout.NewSpacer())
|
searchVbox := container.NewVBox(layout.NewSpacer(), a.searcher, layout.NewSpacer())
|
||||||
a.container = container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 5, PadBottom: 15},
|
a.container = container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 5, PadBottom: 15},
|
||||||
container.NewBorder(
|
container.NewBorder(
|
||||||
container.NewHBox(a.titleDisp, container.NewCenter(a.viewToggle), layout.NewSpacer(), searchVbox),
|
container.NewHBox(a.titleDisp, container.NewCenter(a.viewToggle), layout.NewSpacer(), searchVbox),
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ type TracksPage struct {
|
|||||||
nowPlayingID string
|
nowPlayingID string
|
||||||
|
|
||||||
title *widget.RichText
|
title *widget.RichText
|
||||||
searcher *widgets.Searcher
|
searcher *widgets.SearchEntry
|
||||||
tracklist *widgets.Tracklist
|
tracklist *widgets.Tracklist
|
||||||
loader widgets.TracklistLoader
|
loader widgets.TracklistLoader
|
||||||
searchTracklist *widgets.Tracklist
|
searchTracklist *widgets.Tracklist
|
||||||
@@ -58,7 +58,7 @@ func NewTracksPage(contr *controller.Controller, conf *backend.TracksPageConfig,
|
|||||||
t.title = widget.NewRichTextWithText("All Tracks")
|
t.title = widget.NewRichTextWithText("All Tracks")
|
||||||
t.title.Segments[0].(*widget.TextSegment).Style.SizeName = widget.RichTextStyleHeading.SizeName
|
t.title.Segments[0].(*widget.TextSegment).Style.SizeName = widget.RichTextStyleHeading.SizeName
|
||||||
t.playRandom = widget.NewButtonWithIcon("Play random", theme.ShuffleIcon, t.playRandomSongs)
|
t.playRandom = widget.NewButtonWithIcon("Play random", theme.ShuffleIcon, t.playRandomSongs)
|
||||||
t.searcher = widgets.NewSearcher()
|
t.searcher = widgets.NewSearchEntry()
|
||||||
t.searcher.OnSearched = t.OnSearched
|
t.searcher.OnSearched = t.OnSearched
|
||||||
t.createContainer()
|
t.createContainer()
|
||||||
t.Reload()
|
t.Reload()
|
||||||
@@ -67,7 +67,7 @@ func NewTracksPage(contr *controller.Controller, conf *backend.TracksPageConfig,
|
|||||||
|
|
||||||
func (t *TracksPage) createContainer() {
|
func (t *TracksPage) createContainer() {
|
||||||
playRandomVbox := container.NewVBox(layout.NewSpacer(), t.playRandom, layout.NewSpacer())
|
playRandomVbox := container.NewVBox(layout.NewSpacer(), t.playRandom, layout.NewSpacer())
|
||||||
searchVbox := container.NewVBox(layout.NewSpacer(), t.searcher.Entry, layout.NewSpacer())
|
searchVbox := container.NewVBox(layout.NewSpacer(), t.searcher, layout.NewSpacer())
|
||||||
topRow := container.NewHBox(t.title, playRandomVbox, layout.NewSpacer(), searchVbox)
|
topRow := container.NewHBox(t.title, playRandomVbox, layout.NewSpacer(), searchVbox)
|
||||||
t.container = container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 5, PadBottom: 15},
|
t.container = container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 5, PadBottom: 15},
|
||||||
container.NewBorder(topRow, nil, nil, nil, t.tracklist))
|
container.NewBorder(topRow, nil, nil, nil, t.tracklist))
|
||||||
@@ -100,7 +100,7 @@ func (t *TracksPage) OnSongChange(track *subsonic.Child, lastScrobbledIfAny *sub
|
|||||||
var _ Searchable = (*TracksPage)(nil)
|
var _ Searchable = (*TracksPage)(nil)
|
||||||
|
|
||||||
func (t *TracksPage) SearchWidget() fyne.Focusable {
|
func (t *TracksPage) SearchWidget() fyne.Focusable {
|
||||||
return t.searcher.Entry
|
return t.searcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TracksPage) OnSearched(query string) {
|
func (t *TracksPage) OnSearched(query string) {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/layout"
|
"fyne.io/fyne/v2/layout"
|
||||||
@@ -60,6 +62,22 @@ func ImageAspect(im image.Image) float32 {
|
|||||||
return float32(b.Max.X-b.Min.X) / float32(b.Max.Y-b.Min.Y)
|
return float32(b.Max.X-b.Min.X) / float32(b.Max.Y-b.Min.Y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debouncer returns a function that will call callOnDone when
|
||||||
|
// it has not been invoked since the last dur interval.
|
||||||
|
func NewDebouncer(dur time.Duration, callOnDone func()) func() {
|
||||||
|
var mu sync.Mutex
|
||||||
|
var timer *time.Timer
|
||||||
|
return func() {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
if timer != nil {
|
||||||
|
timer.Stop()
|
||||||
|
}
|
||||||
|
timer = time.AfterFunc(dur, callOnDone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func RichTextSegsFromHTMLString(s string) []widget.RichTextSegment {
|
func RichTextSegsFromHTMLString(s string) []widget.RichTextSegment {
|
||||||
tokr := html.NewTokenizer(strings.NewReader(s))
|
tokr := html.NewTokenizer(strings.NewReader(s))
|
||||||
var segs []widget.RichTextSegment
|
var segs []widget.RichTextSegment
|
||||||
|
|||||||
@@ -1,76 +1,21 @@
|
|||||||
package widgets
|
package widgets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/theme"
|
"fyne.io/fyne/v2/theme"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
|
"github.com/dweymouth/supersonic/ui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Searcher is a search entry widget that will issue a search command
|
// SearchEntry is a search entry widget that will issue a search command
|
||||||
// (aka call OnSearched) when a short span of time has elapsed since
|
// (aka call OnSearched) when a short span of time has elapsed since
|
||||||
// the user typed into the widget.
|
// 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 {
|
type SearchEntry struct {
|
||||||
widget.Entry
|
widget.Entry
|
||||||
|
|
||||||
|
OnSearched func(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSearchEntry() *SearchEntry {
|
func NewSearchEntry() *SearchEntry {
|
||||||
@@ -80,6 +25,10 @@ func NewSearchEntry() *SearchEntry {
|
|||||||
sf.ActionItem = NewClearTextButton(func() {
|
sf.ActionItem = NewClearTextButton(func() {
|
||||||
sf.SetText("")
|
sf.SetText("")
|
||||||
})
|
})
|
||||||
|
debounceFunc := util.NewDebouncer(200*time.Millisecond, func() {
|
||||||
|
sf.sendSearch(sf.Entry.Text)
|
||||||
|
})
|
||||||
|
sf.Entry.OnChanged = func(_ string) { debounceFunc() }
|
||||||
return sf
|
return sf
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +53,12 @@ type clearTextButton struct {
|
|||||||
OnTapped func()
|
OnTapped func()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SearchEntry) sendSearch(text string) {
|
||||||
|
if s.OnSearched != nil {
|
||||||
|
s.OnSearched(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewClearTextButton(onTapped func()) *clearTextButton {
|
func NewClearTextButton(onTapped func()) *clearTextButton {
|
||||||
c := &clearTextButton{OnTapped: onTapped}
|
c := &clearTextButton{OnTapped: onTapped}
|
||||||
c.ExtendBaseWidget(c)
|
c.ExtendBaseWidget(c)
|
||||||
Reference in New Issue
Block a user