mostly working now, except for track result action, and results ranking

This commit is contained in:
Drew Weymouth
2023-11-05 14:07:52 -08:00
parent 17df4d780e
commit 155ee877e3
3 changed files with 116 additions and 24 deletions
+3
View File
@@ -506,7 +506,10 @@ func (c *Controller) ShowQuickSearch() {
c.NavigateTo(GenreRoute(id)) c.NavigateTo(GenreRoute(id))
} }
} }
c.ClosePopUpOnEscape(pop)
c.haveModal = true
pop.Show() pop.Show()
c.MainWindow.Canvas().Focus(qs.SearchEntry)
} }
func (c *Controller) trySetPasswordAndConnectToServer(server *backend.ServerConfig, password string) error { func (c *Controller) trySetPasswordAndConnectToServer(server *backend.ServerConfig, password string) error {
+107 -23
View File
@@ -6,12 +6,13 @@ import (
"sync" "sync"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"github.com/dweymouth/supersonic/backend" "github.com/dweymouth/supersonic/backend"
"github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/dweymouth/supersonic/backend/mediaprovider"
"github.com/dweymouth/supersonic/ui/layouts"
myTheme "github.com/dweymouth/supersonic/ui/theme" myTheme "github.com/dweymouth/supersonic/ui/theme"
"github.com/dweymouth/supersonic/ui/widgets" "github.com/dweymouth/supersonic/ui/widgets"
) )
@@ -22,6 +23,8 @@ type QuickSearch struct {
OnDismiss func() OnDismiss func()
OnNavigateTo func(mediaprovider.ContentType, string) OnNavigateTo func(mediaprovider.ContentType, string)
SearchEntry fyne.Focusable // exported so it can be focused by the Controller
mp mediaprovider.MediaProvider mp mediaprovider.MediaProvider
im *backend.ImageManager im *backend.ImageManager
@@ -40,15 +43,36 @@ func NewQuickSearch(mp mediaprovider.MediaProvider, im *backend.ImageManager) *Q
} }
q.ExtendBaseWidget(q) q.ExtendBaseWidget(q)
searchEntry := widgets.NewSearchEntry() se := newQuickSearchEntry()
searchEntry.OnChanged = q.onSearched se.OnSearched = q.onSearched
se.OnSubmitted = func(_ string) {
q.onSelected(q.selectedIndex)
}
se.OnTypedDown = func() {
q.resultsMutex.RLock()
if q.selectedIndex < len(q.searchResults)-1 {
q.selectedIndex++
}
q.resultsMutex.RUnlock()
q.list.Select(q.selectedIndex)
}
se.OnTypedUp = func() {
q.resultsMutex.RLock()
if q.selectedIndex > 0 {
q.selectedIndex--
}
q.resultsMutex.RUnlock()
q.list.Select(q.selectedIndex)
}
se.OnTypedEscape = q.onDismiss
q.SearchEntry = se
q.list = widget.NewList( q.list = widget.NewList(
func() int { func() int {
q.resultsMutex.RLock() q.resultsMutex.RLock()
defer q.resultsMutex.RUnlock() defer q.resultsMutex.RUnlock()
return len(q.searchResults) return len(q.searchResults)
}, },
func() fyne.CanvasObject { return newQuickSearchResult(im) }, func() fyne.CanvasObject { return newQuickSearchResult(q) },
func(lii widget.ListItemID, co fyne.CanvasObject) { func(lii widget.ListItemID, co fyne.CanvasObject) {
var result *mediaprovider.SearchResult var result *mediaprovider.SearchResult
q.resultsMutex.RLock() q.resultsMutex.RLock()
@@ -56,16 +80,43 @@ func NewQuickSearch(mp mediaprovider.MediaProvider, im *backend.ImageManager) *Q
result = q.searchResults[lii] result = q.searchResults[lii]
} }
q.resultsMutex.RUnlock() q.resultsMutex.RUnlock()
co.(*quickSearchResult).Update(result) qs := co.(*quickSearchResult)
qs.index = lii
qs.Update(result)
}, },
) )
dismissBtn := widget.NewButton("Close", q.onDismiss)
title := widget.NewRichText(&widget.TextSegment{Text: "Quick Search", Style: boldStyle}) title := widget.NewRichText(&widget.TextSegment{Text: "Quick Search", Style: boldStyle})
title.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignCenter title.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignCenter
q.content = container.NewBorder( q.content = container.NewBorder(
container.NewVBox(title, searchEntry), nil, nil, nil, q.list) container.NewVBox(title, se),
container.NewVBox(widget.NewSeparator(), container.NewHBox(layout.NewSpacer(), dismissBtn)),
nil, nil, q.list)
return q return q
} }
func (q *QuickSearch) onDismiss() {
if q.OnDismiss != nil {
q.OnDismiss()
}
}
func (q *QuickSearch) onSelected(idx int) {
if q.OnNavigateTo == nil {
return
}
q.resultsMutex.RLock()
if len(q.searchResults) <= idx {
q.resultsMutex.RUnlock()
return
}
id := q.searchResults[idx].ID
typ := q.searchResults[idx].Type
q.resultsMutex.RUnlock()
q.OnNavigateTo(typ, id)
}
func (q *QuickSearch) onSearched(query string) { func (q *QuickSearch) onSearched(query string) {
var results []*mediaprovider.SearchResult var results []*mediaprovider.SearchResult
if query != "" { if query != "" {
@@ -79,6 +130,8 @@ func (q *QuickSearch) onSearched(query string) {
q.searchResults = results q.searchResults = results
q.resultsMutex.Unlock() q.resultsMutex.Unlock()
q.list.Refresh() q.list.Refresh()
q.list.ScrollToTop()
q.list.Select(0)
} }
func (q *QuickSearch) CreateRenderer() fyne.WidgetRenderer { func (q *QuickSearch) CreateRenderer() fyne.WidgetRenderer {
@@ -86,15 +139,16 @@ func (q *QuickSearch) CreateRenderer() fyne.WidgetRenderer {
} }
func (q *QuickSearch) MinSize() fyne.Size { func (q *QuickSearch) MinSize() fyne.Size {
return fyne.NewSize(500, 350) return fyne.NewSize(400, 350)
} }
type quickSearchResult struct { type quickSearchResult struct {
widget.BaseWidget widget.BaseWidget
// parent *QuickSearch parent *QuickSearch
id string id string
index int
contentType mediaprovider.ContentType contentType mediaprovider.ContentType
imageLoader backend.ThumbnailLoader imageLoader backend.ThumbnailLoader
@@ -102,19 +156,21 @@ type quickSearchResult struct {
image *widgets.ImagePlaceholder image *widgets.ImagePlaceholder
title *widget.Label title *widget.Label
secondary *widget.RichText secondary *widget.RichText
selection *canvas.Rectangle
content *fyne.Container content *fyne.Container
} }
func newQuickSearchResult(im *backend.ImageManager) *quickSearchResult { func newQuickSearchResult(parent *QuickSearch) *quickSearchResult {
qs := &quickSearchResult{ qs := &quickSearchResult{
parent: parent,
image: widgets.NewImagePlaceholder(myTheme.AlbumIcon, 50), image: widgets.NewImagePlaceholder(myTheme.AlbumIcon, 50),
title: widget.NewLabel(""), title: widget.NewLabel(""),
secondary: widget.NewRichText(), secondary: widget.NewRichText(),
} }
qs.title.Wrapping = fyne.TextTruncate
qs.secondary.Wrapping = fyne.TextTruncate
qs.ExtendBaseWidget(qs) qs.ExtendBaseWidget(qs)
qs.imageLoader = im.NewThumbnailLoader(func(im image.Image) { qs.imageLoader = parent.im.NewThumbnailLoader(func(im image.Image) {
qs.image.SetImage(im, false) qs.image.SetImage(im, false)
}) })
qs.imageLoader.OnBeforeLoad = func() { qs.imageLoader.OnBeforeLoad = func() {
@@ -136,22 +192,21 @@ func (q *quickSearchResult) Update(result *mediaprovider.SearchResult) {
q.image.CenterIcon = placeholderIconForContentType(result.Type) q.image.CenterIcon = placeholderIconForContentType(result.Type)
q.imageLoader.Load(result.CoverID) q.imageLoader.Load(result.CoverID)
q.title.SetText(result.Name) q.title.SetText(result.Name)
//TODO: q.secondary.Segments = q.secondary.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: result.Type.String()}}
q.secondary.Refresh()
}
func (q *quickSearchResult) Tapped(_ *fyne.PointEvent) {
q.parent.onSelected(q.index)
} }
func (q *quickSearchResult) CreateRenderer() fyne.WidgetRenderer { func (q *quickSearchResult) CreateRenderer() fyne.WidgetRenderer {
if q.selection == nil {
q.selection = canvas.NewRectangle(theme.SelectionColor())
}
if q.content == nil { if q.content == nil {
q.content = container.NewMax( q.content = container.NewBorder(nil, nil, container.NewCenter(q.image), nil,
q.selection, container.New(&layouts.VboxCustomPadding{ExtraPad: -15},
container.NewBorder(nil, nil, q.image, nil, q.title,
container.NewVBox( q.secondary,
q.title, ))
q.secondary,
)),
)
} }
return widget.NewSimpleRenderer(q.content) return widget.NewSimpleRenderer(q.content)
} }
@@ -160,6 +215,35 @@ func (q *quickSearchResult) Refresh() {
q.BaseWidget.Refresh() q.BaseWidget.Refresh()
} }
type quickSearchEntry struct {
widgets.SearchEntry
OnTypedUp func()
OnTypedDown func()
OnTypedEscape func()
}
func newQuickSearchEntry() *quickSearchEntry {
q := &quickSearchEntry{}
q.ExtendBaseWidget(q)
q.SearchEntry.Init()
return q
}
func (q *quickSearchEntry) TypedKey(e *fyne.KeyEvent) {
switch {
case e.Name == fyne.KeyUp && q.OnTypedUp != nil:
q.OnTypedUp()
case e.Name == fyne.KeyDown && q.OnTypedDown != nil:
q.OnTypedDown()
case e.Name == fyne.KeyEscape && q.OnTypedEscape != nil:
q.OnTypedEscape()
default:
q.SearchEntry.TypedKey(e)
}
}
func placeholderIconForContentType(c mediaprovider.ContentType) fyne.Resource { func placeholderIconForContentType(c mediaprovider.ContentType) fyne.Resource {
switch c { switch c {
case mediaprovider.ContentTypeAlbum: case mediaprovider.ContentTypeAlbum:
+6 -1
View File
@@ -21,6 +21,12 @@ type SearchEntry struct {
func NewSearchEntry() *SearchEntry { func NewSearchEntry() *SearchEntry {
sf := &SearchEntry{} sf := &SearchEntry{}
sf.ExtendBaseWidget(sf) sf.ExtendBaseWidget(sf)
sf.Init()
return sf
}
// For use only by extending widgets
func (sf *SearchEntry) Init() {
sf.PlaceHolder = "Search" sf.PlaceHolder = "Search"
sf.ActionItem = NewClearTextButton(func() { sf.ActionItem = NewClearTextButton(func() {
sf.SetText("") sf.SetText("")
@@ -34,7 +40,6 @@ func NewSearchEntry() *SearchEntry {
} }
debounceFunc() debounceFunc()
} }
return sf
} }
func (s *SearchEntry) TypedKey(e *fyne.KeyEvent) { func (s *SearchEntry) TypedKey(e *fyne.KeyEvent) {