Merge pull request #388 from dweymouth/improve-playlist-dialog

Improve Add to Playlist dialog
This commit is contained in:
Drew Weymouth
2024-05-24 08:02:18 -07:00
committed by GitHub
5 changed files with 286 additions and 276 deletions
+41 -37
View File
@@ -322,49 +322,52 @@ func (m *Controller) PromptForFirstServer() {
pop.Show() pop.Show()
} }
// Show dialog to prompt for playlist. // Show dialog to select playlist.
// Depending on the results of that dialog, potentially create a new playlist // Depending on the results of that dialog, potentially create a new playlist
// Add tracks to the user-specified playlist // Add tracks to the user-specified playlist
func (m *Controller) DoAddTracksToPlaylistWorkflow(trackIDs []string) { func (m *Controller) DoAddTracksToPlaylistWorkflow(trackIDs []string) {
go func() { sp := dialogs.NewSelectPlaylistDialog(m.App.ServerManager.Server, m.App.ImageManager, m.App.ServerManager.LoggedInUser)
pls, err := m.App.ServerManager.Server.GetPlaylists() pop := widget.NewModalPopUp(sp.SearchDialog, m.MainWindow.Canvas())
pls = sharedutil.FilterSlice(pls, func(pl *mediaprovider.Playlist) bool { sp.SetOnDismiss(func() {
return pl.Owner == m.App.ServerManager.LoggedInUser pop.Hide()
}) m.doModalClosed()
if err != nil { })
// TODO: surface this error to user sp.SetOnNavigateTo(func(contentType mediaprovider.ContentType, id string) {
log.Printf("error getting user-owned playlists: %s", err.Error()) pop.Hide()
return if id == "" /* creating new playlist */ {
} go m.App.ServerManager.Server.CreatePlaylist(sp.SearchDialog.SearchQuery(), trackIDs)
} else {
selectedIdx := -1 m.App.Config.Application.DefaultPlaylistID = id
plNames := make([]string, 0, len(pls)) if sp.SkipDuplicates {
for i, pl := range pls { go func() {
plNames = append(plNames, pl.Name) currentTrackIDs := make(map[string]struct{})
if defId := m.App.Config.Application.DefaultPlaylistID; defId != "" && pl.ID == defId { if selectedPlaylist, err := m.App.ServerManager.Server.GetPlaylist(id); err != nil {
selectedIdx = i log.Printf("error getting playlist: %s", err.Error())
} } else {
} for _, track := range selectedPlaylist.Tracks {
currentTrackIDs[track.ID] = struct{}{}
dlg := dialogs.NewAddToPlaylistDialog("Add to Playlist", plNames, selectedIdx) }
pop := widget.NewModalPopUp(dlg, m.MainWindow.Canvas()) filterTrackIDs := sharedutil.FilterSlice(trackIDs, func(trackID string) bool {
m.ClosePopUpOnEscape(pop) _, ok := currentTrackIDs[trackID]
dlg.OnCanceled = pop.Hide return !ok
dlg.OnSubmit = func(playlistChoice int, newPlaylistName string) { })
pop.Hide() m.App.ServerManager.Server.AddPlaylistTracks(id, filterTrackIDs)
m.doModalClosed() }
if playlistChoice < 0 { }()
go m.App.ServerManager.Server.CreatePlaylist(newPlaylistName, trackIDs)
} else { } else {
playlist := pls[playlistChoice] go m.App.ServerManager.Server.AddPlaylistTracks(id, trackIDs)
m.App.Config.Application.DefaultPlaylistID = playlist.ID
go m.App.ServerManager.Server.AddPlaylistTracks(
playlist.ID, trackIDs)
} }
} }
m.haveModal = true
pop.Show() })
}() m.ClosePopUpOnEscape(pop)
m.haveModal = true
min := sp.MinSize()
height := fyne.Max(min.Height, fyne.Min(min.Height*1.5, m.MainWindow.Canvas().Size().Height*0.7))
sp.SearchDialog.Show()
pop.Resize(fyne.NewSize(min.Width, height))
pop.Show()
m.MainWindow.Canvas().Focus(sp.GetSearchEntry())
} }
func (m *Controller) DoEditPlaylistWorkflow(playlist *mediaprovider.Playlist) { func (m *Controller) DoEditPlaylistWorkflow(playlist *mediaprovider.Playlist) {
@@ -642,6 +645,7 @@ func (c *Controller) ShowQuickSearch() {
c.haveModal = true c.haveModal = true
min := qs.MinSize() min := qs.MinSize()
height := fyne.Max(min.Height, fyne.Min(min.Height*1.5, c.MainWindow.Canvas().Size().Height*0.7)) height := fyne.Max(min.Height, fyne.Min(min.Height*1.5, c.MainWindow.Canvas().Size().Height*0.7))
qs.SearchDialog.Show()
pop.Resize(fyne.NewSize(min.Width, height)) pop.Resize(fyne.NewSize(min.Width, height))
pop.Show() pop.Show()
c.MainWindow.Canvas().Focus(qs.GetSearchEntry()) c.MainWindow.Canvas().Focus(qs.GetSearchEntry())
-120
View File
@@ -1,120 +0,0 @@
package dialogs
import (
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/widget"
)
type AddToPlaylistDialog struct {
widget.BaseWidget
OnCanceled func()
OnSubmit func(playlistChoice int, newPlaylistName string)
playlistSelect *widget.Select
newPlaylistLabel *widget.Label
newPlaylistName *widget.Entry
okBtn *widget.Button
container *fyne.Container
}
var _ fyne.Widget = (*AddToPlaylistDialog)(nil)
func NewAddToPlaylistDialog(title string, existingPlaylistNames []string, selectedIdx int) *AddToPlaylistDialog {
a := &AddToPlaylistDialog{}
a.ExtendBaseWidget(a)
titleLabel := widget.NewLabel(title)
titleLabel.TextStyle.Bold = true
options := []string{"New playlist..."}
options = append(options, existingPlaylistNames...)
a.playlistSelect = widget.NewSelect(options, func(_ string) {
a.onSelectionChanged()
})
a.playlistSelect.PlaceHolder = "(Choose playlist)"
if selectedIdx >= 0 {
// calling SetSelectedIndex before showing the Select crashes...
go func() {
time.Sleep(10 * time.Millisecond)
// add 1 to selectedIdx to account for "(Choose playlist)" entry
a.playlistSelect.SetSelectedIndex(selectedIdx + 1)
}()
}
a.newPlaylistName = widget.NewEntry()
a.newPlaylistName.Hidden = true
a.newPlaylistName.OnChanged = func(text string) {
if len(text) > 0 {
a.okBtn.Enable()
} else {
a.okBtn.Disable()
}
}
a.newPlaylistLabel = widget.NewLabel("Name")
a.newPlaylistLabel.Hidden = true
a.okBtn = widget.NewButton("OK", a.onOK)
a.okBtn.Importance = widget.HighImportance
a.okBtn.Disable()
cancelBtn := widget.NewButton("Cancel", a.onCancel)
a.container = container.NewVBox(
container.NewHBox(layout.NewSpacer(), titleLabel, layout.NewSpacer()),
container.New(layout.NewFormLayout(),
widget.NewLabel("Playlist"),
a.playlistSelect,
a.newPlaylistLabel,
a.newPlaylistName),
widget.NewSeparator(),
container.NewHBox(layout.NewSpacer(), a.okBtn, cancelBtn))
return a
}
func (a *AddToPlaylistDialog) onOK() {
var newPlaylistName string
playlistChoice := -1
if sel := a.playlistSelect.SelectedIndex(); sel == 0 {
newPlaylistName = a.newPlaylistName.Text
} else {
playlistChoice = sel - 1
}
if a.OnSubmit != nil {
a.OnSubmit(playlistChoice, newPlaylistName)
}
}
func (a *AddToPlaylistDialog) onSelectionChanged() {
if a.playlistSelect.SelectedIndex() == 0 {
a.newPlaylistName.Show()
a.newPlaylistLabel.Show()
if len(a.newPlaylistName.Text) == 0 {
a.okBtn.Disable()
} else {
a.okBtn.Enable()
}
} else {
a.newPlaylistName.Hide()
a.newPlaylistLabel.Hide()
a.okBtn.Enable()
}
}
func (a *AddToPlaylistDialog) onCancel() {
if a.OnCanceled != nil {
a.OnCanceled()
}
}
func (a *AddToPlaylistDialog) MinSize() fyne.Size {
a.ExtendBaseWidget(a)
return fyne.NewSize(300, a.container.MinSize().Height)
}
func (a *AddToPlaylistDialog) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(a.container)
}
+3 -63
View File
@@ -1,12 +1,9 @@
package dialogs package dialogs
import ( import (
"fmt"
"log" "log"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/dweymouth/supersonic/backend/mediaprovider"
"github.com/dweymouth/supersonic/ui/util" "github.com/dweymouth/supersonic/ui/util"
) )
@@ -17,18 +14,8 @@ type QuickSearch struct {
} }
func NewQuickSearch(mp mediaprovider.MediaProvider, im util.ImageFetcher) *QuickSearch { func NewQuickSearch(mp mediaprovider.MediaProvider, im util.ImageFetcher) *QuickSearch {
q := &QuickSearch{mp: mp}
q := &QuickSearch{ q.SearchDialog = NewSearchDialog(im, "Quick Search", "Close", q.onSearched)
mp: mp,
}
sd := NewSearchDialog(
im,
"Quick Search",
q.onSearched,
q.onUpdateSearchResult,
)
q.SearchDialog = sd
return q return q
} }
@@ -44,53 +31,6 @@ func (q *QuickSearch) onSearched(query string) []*mediaprovider.SearchResult {
return results return results
} }
func (q *QuickSearch) onUpdateSearchResult(sr *searchResult, result *mediaprovider.SearchResult) {
maybePluralize := func(s string, size int) string {
if size != 1 {
return s + "s"
}
return s
}
var secondaryText string
switch result.Type {
case mediaprovider.ContentTypeAlbum:
secondaryText = result.ArtistName
case mediaprovider.ContentTypeArtist:
secondaryText = fmt.Sprintf("%d %s", result.Size, maybePluralize("album", result.Size))
case mediaprovider.ContentTypeTrack:
secondaryText = result.ArtistName
case mediaprovider.ContentTypePlaylist:
secondaryText = fmt.Sprintf("%d %s", result.Size, maybePluralize("track", result.Size))
case mediaprovider.ContentTypeGenre:
if result.Size > 0 {
secondaryText = fmt.Sprintf("%d %s", result.Size, maybePluralize("album", result.Size))
} else {
secondaryText = ""
}
}
sr.secondary.Segments = []widget.RichTextSegment{
&widget.TextSegment{
Text: result.Type.String(),
Style: widget.RichTextStyle{SizeName: theme.SizeNameCaptionText, TextStyle: fyne.TextStyle{Bold: true}, Inline: true},
},
}
if secondaryText != "" {
sr.secondary.Segments = append(sr.secondary.Segments,
&widget.TextSegment{
Text: " · ",
Style: widget.RichTextStyle{SizeName: theme.SizeNameCaptionText, Inline: true},
},
&widget.TextSegment{
Text: secondaryText,
Style: widget.RichTextStyle{SizeName: theme.SizeNameCaptionText, Inline: true},
},
)
}
sr.secondary.Refresh()
}
func (q *QuickSearch) SetOnDismiss(onDismiss func()) { func (q *QuickSearch) SetOnDismiss(onDismiss func()) {
q.SearchDialog.OnDismiss = onDismiss q.SearchDialog.OnDismiss = onDismiss
} }
@@ -104,5 +44,5 @@ func (q *QuickSearch) MinSize() fyne.Size {
} }
func (q *QuickSearch) GetSearchEntry() fyne.Focusable { func (q *QuickSearch) GetSearchEntry() fyne.Focusable {
return q.SearchDialog.SearchEntry return q.SearchDialog.GetSearchEntry()
} }
+135 -56
View File
@@ -1,6 +1,7 @@
package dialogs package dialogs
import ( import (
"fmt"
"image" "image"
"log" "log"
"sync" "sync"
@@ -23,30 +24,36 @@ import (
type SearchDialog struct { type SearchDialog struct {
widget.BaseWidget widget.BaseWidget
SearchEntry fyne.Focusable // exported so it can be focused by the Controller PlaceholderText string
imgSource util.ImageFetcher // Additional item that can be placed to the left
// of the dismiss buttons
ActionItem fyne.CanvasObject
OnDismiss func()
OnNavigateTo func(mediaprovider.ContentType, string)
OnSearched func(string) []*mediaprovider.SearchResult
imgSource util.ImageFetcher
resultsMutex sync.RWMutex resultsMutex sync.RWMutex
searchResults []*mediaprovider.SearchResult searchResults []*mediaprovider.SearchResult
loadingDots *widgets.LoadingDots
list *widget.List
selectedIndex int selectedIndex int
content *fyne.Container searchEntry *searchEntry
loadingDots *widgets.LoadingDots
OnDismiss func() list *widget.List
OnNavigateTo func(mediaprovider.ContentType, string) dialogTitle string
OnSearched func(string) []*mediaprovider.SearchResult dismissText string
OnUpdateSearchResults func(*searchResult, *mediaprovider.SearchResult) content *fyne.Container
} }
func NewSearchDialog(im util.ImageFetcher, placeholderTitle string, onSearched func(string) []*mediaprovider.SearchResult, onUpdateSearchResult func(*searchResult, *mediaprovider.SearchResult)) *SearchDialog { func NewSearchDialog(im util.ImageFetcher, title, dismissBtn string, onSearched func(string) []*mediaprovider.SearchResult) *SearchDialog {
sd := &SearchDialog{ sd := &SearchDialog{
imgSource: im, imgSource: im,
loadingDots: widgets.NewLoadingDots(), loadingDots: widgets.NewLoadingDots(),
OnSearched: onSearched, OnSearched: onSearched,
OnUpdateSearchResults: onUpdateSearchResult, dialogTitle: title,
dismissText: dismissBtn,
} }
sd.ExtendBaseWidget(sd) sd.ExtendBaseWidget(sd)
@@ -58,7 +65,7 @@ func NewSearchDialog(im util.ImageFetcher, placeholderTitle string, onSearched f
se.OnTypedDown = sd.moveSelectionDown se.OnTypedDown = sd.moveSelectionDown
se.OnTypedUp = sd.moveSelectionUp se.OnTypedUp = sd.moveSelectionUp
se.OnTypedEscape = sd.onDismiss se.OnTypedEscape = sd.onDismiss
sd.SearchEntry = se sd.searchEntry = se
sd.list = widget.NewList( sd.list = widget.NewList(
func() int { func() int {
sd.resultsMutex.RLock() sd.resultsMutex.RLock()
@@ -75,23 +82,34 @@ func NewSearchDialog(im util.ImageFetcher, placeholderTitle string, onSearched f
sd.resultsMutex.RUnlock() sd.resultsMutex.RUnlock()
sr := co.(*searchResult) sr := co.(*searchResult)
sr.index = lii sr.index = lii
sd.update(sr, result) sr.Update(result)
}, },
) )
dismissBtn := widget.NewButton("Close", sd.onDismiss)
title := widget.NewRichText(&widget.TextSegment{Text: placeholderTitle, Style: util.BoldRichTextStyle})
title.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignCenter
sd.content = container.NewStack(
container.NewBorder(
container.NewVBox(title, se),
container.NewVBox(widget.NewSeparator(), container.NewHBox(layout.NewSpacer(), dismissBtn)),
nil, nil, sd.list),
container.NewCenter(sd.loadingDots),
)
return sd return sd
} }
// GetSearchEntry returns the search Entry widget for focusing
func (sd *SearchDialog) GetSearchEntry() fyne.Focusable {
return sd.searchEntry
}
// SearchQuery returns the current search query entered by the user
func (sd *SearchDialog) SearchQuery() string {
return sd.searchEntry.Text
}
func (sd *SearchDialog) Show() {
sd.BaseWidget.Show()
go sd.onSearched("")
}
func (sd *SearchDialog) Refresh() {
if sd.PlaceholderText != "" {
sd.searchEntry.SetPlaceHolder(sd.PlaceholderText)
}
sd.BaseWidget.Refresh()
}
func (sd *SearchDialog) onDismiss() { func (sd *SearchDialog) onDismiss() {
if sd.OnDismiss != nil { if sd.OnDismiss != nil {
sd.OnDismiss() sd.OnDismiss()
@@ -131,18 +149,7 @@ func (sd *SearchDialog) moveSelectionUp() {
sd.list.Select(sd.selectedIndex) sd.list.Select(sd.selectedIndex)
} }
func (sd *SearchDialog) onSearched(query string) { func (sd *SearchDialog) setResults(results []*mediaprovider.SearchResult) {
sd.loadingDots.Start()
var results []*mediaprovider.SearchResult
if query != "" {
res := sd.OnSearched(query)
if len(res) == 0 {
log.Println("No results matched the query.")
} else {
results = res
}
}
sd.loadingDots.Stop()
sd.resultsMutex.Lock() sd.resultsMutex.Lock()
sd.searchResults = results sd.searchResults = results
sd.resultsMutex.Unlock() sd.resultsMutex.Unlock()
@@ -152,7 +159,37 @@ func (sd *SearchDialog) onSearched(query string) {
sd.list.Select(0) sd.list.Select(0)
} }
func (sd *SearchDialog) onSearched(query string) {
sd.loadingDots.Start()
var results []*mediaprovider.SearchResult
res := sd.OnSearched(query)
if len(res) == 0 {
log.Println("No results matched the query.")
} else {
results = res
}
sd.loadingDots.Stop()
sd.setResults(results)
}
func (sd *SearchDialog) CreateRenderer() fyne.WidgetRenderer { func (sd *SearchDialog) CreateRenderer() fyne.WidgetRenderer {
dismissBtn := widget.NewButton(sd.dismissText, sd.onDismiss)
title := widget.NewRichText(&widget.TextSegment{Text: sd.dialogTitle, Style: util.BoldRichTextStyle})
title.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignCenter
bottomRow := container.NewHBox()
if sd.ActionItem != nil {
bottomRow.Objects = []fyne.CanvasObject{sd.ActionItem, layout.NewSpacer(), dismissBtn}
} else {
bottomRow.Objects = []fyne.CanvasObject{layout.NewSpacer(), dismissBtn}
}
sd.content = container.NewStack(
container.NewBorder(
container.NewVBox(title, sd.searchEntry),
container.NewVBox(widget.NewSeparator(), bottomRow),
nil, nil, sd.list),
container.NewCenter(sd.loadingDots),
)
return widget.NewSimpleRenderer(sd.content) return widget.NewSimpleRenderer(sd.content)
} }
@@ -160,22 +197,6 @@ func (sd *SearchDialog) MinSize() fyne.Size {
return fyne.NewSize(400, 350) return fyne.NewSize(400, 350)
} }
func (sd *SearchDialog) update(sr *searchResult, result *mediaprovider.SearchResult) {
if result == nil {
return
}
if sr.contentType == result.Type && sr.id == result.ID {
return // nothing to do
}
sr.id = result.ID
sr.contentType = result.Type
sr.image.CenterIcon = placeholderIconForContentType(result.Type)
sr.imageLoader.Load(result.CoverID)
sr.title.SetText(result.Name)
sd.OnUpdateSearchResults(sr, result)
}
func placeholderIconForContentType(c mediaprovider.ContentType) fyne.Resource { func placeholderIconForContentType(c mediaprovider.ContentType) fyne.Resource {
switch c { switch c {
case mediaprovider.ContentTypeAlbum: case mediaprovider.ContentTypeAlbum:
@@ -231,6 +252,64 @@ func newSearchResult(parent *SearchDialog) *searchResult {
return qs return qs
} }
func (s *searchResult) Update(result *mediaprovider.SearchResult) {
if result == nil {
return
}
if s.contentType == result.Type && s.id == result.ID && s.title.Text == result.Name {
return // nothing to do
}
s.id = result.ID
s.contentType = result.Type
s.image.CenterIcon = placeholderIconForContentType(result.Type)
s.imageLoader.Load(result.CoverID)
s.title.SetText(result.Name)
maybePluralize := func(s string, size int) string {
if size != 1 {
return s + "s"
}
return s
}
var secondaryText string
switch result.Type {
case mediaprovider.ContentTypeAlbum:
secondaryText = result.ArtistName
case mediaprovider.ContentTypeArtist:
secondaryText = fmt.Sprintf("%d %s", result.Size, maybePluralize("album", result.Size))
case mediaprovider.ContentTypeTrack:
secondaryText = result.ArtistName
case mediaprovider.ContentTypePlaylist:
secondaryText = fmt.Sprintf("%d %s", result.Size, maybePluralize("track", result.Size))
case mediaprovider.ContentTypeGenre:
if result.Size > 0 {
secondaryText = fmt.Sprintf("%d %s", result.Size, maybePluralize("album", result.Size))
} else {
secondaryText = ""
}
}
s.secondary.Segments = []widget.RichTextSegment{
&widget.TextSegment{
Text: result.Type.String(),
Style: widget.RichTextStyle{SizeName: theme.SizeNameCaptionText, TextStyle: fyne.TextStyle{Bold: true}, Inline: true},
},
}
if secondaryText != "" {
s.secondary.Segments = append(s.secondary.Segments,
&widget.TextSegment{
Text: " · ",
Style: widget.RichTextStyle{SizeName: theme.SizeNameCaptionText, Inline: true},
},
&widget.TextSegment{
Text: secondaryText,
Style: widget.RichTextStyle{SizeName: theme.SizeNameCaptionText, Inline: true},
},
)
}
s.secondary.Refresh()
}
func (q *searchResult) Tapped(_ *fyne.PointEvent) { func (q *searchResult) Tapped(_ *fyne.PointEvent) {
q.parent.onSelected(q.index) q.parent.onSelected(q.index)
} }
+107
View File
@@ -0,0 +1,107 @@
package dialogs
import (
// "fmt"
"fmt"
"log"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/widget"
"github.com/deluan/sanitize"
"github.com/dweymouth/supersonic/backend/mediaprovider"
"github.com/dweymouth/supersonic/sharedutil"
"github.com/dweymouth/supersonic/ui/util"
)
type SelectPlaylist struct {
SearchDialog *SearchDialog
mp mediaprovider.MediaProvider
loggedInUser string
allPlaylistResuts []*mediaprovider.SearchResult
SkipDuplicates bool
}
func NewSelectPlaylistDialog(mp mediaprovider.MediaProvider, im util.ImageFetcher, loggedInUser string) *SelectPlaylist {
sp := &SelectPlaylist{
mp: mp,
loggedInUser: loggedInUser,
SkipDuplicates: false,
}
sd := NewSearchDialog(
im,
"Add to playlist",
"Cancel",
sp.onSearched,
)
sd.ActionItem = widget.NewCheckWithData("Skip duplicate tracks", binding.BindBool(&sp.SkipDuplicates))
sd.PlaceholderText = "Search playlists or new playlist name"
sp.SearchDialog = sd
return sp
}
func (sp *SelectPlaylist) fetchUserOwnedPlaylists() {
playlists, err := sp.mp.GetPlaylists()
if err != nil {
// TODO: surface this error to user
log.Printf("error getting playlists: %s", err.Error())
}
userPlaylists := sharedutil.FilterSlice(playlists, func(playlist *mediaprovider.Playlist) bool {
return playlist.Owner == sp.loggedInUser
})
sp.allPlaylistResuts = sharedutil.MapSlice(userPlaylists, sp.playlistToSearchResult)
}
func (sp *SelectPlaylist) playlistToSearchResult(playlist *mediaprovider.Playlist) *mediaprovider.SearchResult {
if playlist == nil {
return nil
}
return &mediaprovider.SearchResult{
Name: playlist.Name,
ID: playlist.ID,
CoverID: playlist.CoverArtID,
Type: mediaprovider.ContentTypePlaylist,
Size: playlist.TrackCount,
ArtistName: playlist.Name,
}
}
func (sp *SelectPlaylist) onSearched(query string) []*mediaprovider.SearchResult {
if sp.allPlaylistResuts == nil {
sp.fetchUserOwnedPlaylists()
}
var results []*mediaprovider.SearchResult
if query == "" {
results = sp.allPlaylistResuts
} else {
results = sharedutil.FilterSlice(sp.allPlaylistResuts, func(playlist *mediaprovider.SearchResult) bool {
return strings.Contains(
sanitize.Accents(strings.ToLower(playlist.Name)),
sanitize.Accents(strings.ToLower(query)),
)
})
results = append(results, &mediaprovider.SearchResult{
Name: fmt.Sprintf("Create new playlist: %s", query),
Type: mediaprovider.ContentTypePlaylist,
})
}
return results
}
func (sp *SelectPlaylist) SetOnDismiss(onDismiss func()) {
sp.SearchDialog.OnDismiss = onDismiss
}
func (sp *SelectPlaylist) SetOnNavigateTo(onNavigateTo func(mediaprovider.ContentType, string)) {
sp.SearchDialog.OnNavigateTo = onNavigateTo
}
func (sp *SelectPlaylist) MinSize() fyne.Size {
return sp.SearchDialog.MinSize()
}
func (sp *SelectPlaylist) GetSearchEntry() fyne.Focusable {
return sp.SearchDialog.GetSearchEntry()
}