more work on album filters
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
"github.com/dweymouth/supersonic/backend"
|
||||
"github.com/dweymouth/supersonic/ui/theme"
|
||||
"github.com/dweymouth/supersonic/ui/util"
|
||||
)
|
||||
|
||||
type AlbumFilterButton struct {
|
||||
widget.Button
|
||||
|
||||
OnChanged func()
|
||||
|
||||
filter *backend.AlbumFilter
|
||||
dialog *widget.PopUp
|
||||
}
|
||||
|
||||
func NewAlbumFilterButton(filter *backend.AlbumFilter) *AlbumFilterButton {
|
||||
a := &AlbumFilterButton{
|
||||
filter: filter,
|
||||
Button: widget.Button{
|
||||
Icon: theme.AlbumIcon,
|
||||
},
|
||||
}
|
||||
a.OnTapped = a.showFilterDialog
|
||||
a.ExtendBaseWidget(a)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *AlbumFilterButton) Refresh() {
|
||||
if a.filter.IsEmpty() {
|
||||
a.Importance = widget.MediumImportance
|
||||
} else {
|
||||
a.Importance = widget.HighImportance
|
||||
}
|
||||
a.Button.Refresh()
|
||||
}
|
||||
|
||||
func (a *AlbumFilterButton) onFilterChanged() {
|
||||
a.Refresh()
|
||||
if a.OnChanged != nil {
|
||||
a.OnChanged()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlbumFilterButton) showFilterDialog() {
|
||||
if a.dialog == nil {
|
||||
filterDlg := NewAlbumFilterPopup(a.filter)
|
||||
filterDlg.OnChanged = a.onFilterChanged
|
||||
a.dialog = widget.NewPopUp(filterDlg, fyne.CurrentApp().Driver().CanvasForObject(a))
|
||||
}
|
||||
pos := fyne.CurrentApp().Driver().AbsolutePositionForObject(a)
|
||||
a.dialog.ShowAtPosition(fyne.NewPos(pos.X+a.Size().Width/2-a.dialog.MinSize().Width/2, pos.Y+a.Size().Height))
|
||||
}
|
||||
|
||||
type AlbumFilterPopup struct {
|
||||
widget.BaseWidget
|
||||
|
||||
OnChanged func()
|
||||
|
||||
filter *backend.AlbumFilter
|
||||
container *fyne.Container
|
||||
}
|
||||
|
||||
func NewAlbumFilterPopup(filter *backend.AlbumFilter) *AlbumFilterPopup {
|
||||
a := &AlbumFilterPopup{filter: filter}
|
||||
a.ExtendBaseWidget(a)
|
||||
|
||||
debounceOnChanged := util.NewDebouncer(350*time.Millisecond, a.emitOnChanged)
|
||||
|
||||
// setup min and max year filters
|
||||
yearValidator := func(curText, selText string, r rune) bool {
|
||||
return unicode.IsDigit(r) && (len(selText) > 0 || len(curText) <= 3)
|
||||
}
|
||||
minYear := NewTextRestrictedEntry(yearValidator)
|
||||
minYear.SetMinCharWidth(4)
|
||||
minYear.OnChanged = func(yearStr string) {
|
||||
if yearStr == "" {
|
||||
a.filter.MinYear = 0
|
||||
} else if i, err := strconv.Atoi(yearStr); err == nil {
|
||||
a.filter.MinYear = i
|
||||
}
|
||||
debounceOnChanged()
|
||||
}
|
||||
if filter.MinYear > 0 {
|
||||
minYear.Text = strconv.Itoa(filter.MinYear)
|
||||
}
|
||||
maxYear := NewTextRestrictedEntry(yearValidator)
|
||||
maxYear.SetMinCharWidth(4)
|
||||
maxYear.OnChanged = func(yearStr string) {
|
||||
if yearStr == "" {
|
||||
a.filter.MaxYear = 0
|
||||
} else if i, err := strconv.Atoi(yearStr); err == nil {
|
||||
a.filter.MaxYear = i
|
||||
}
|
||||
debounceOnChanged()
|
||||
}
|
||||
if filter.MaxYear > 0 {
|
||||
maxYear.Text = strconv.Itoa(filter.MaxYear)
|
||||
}
|
||||
|
||||
// setup is favorite/not favorite filters
|
||||
var isNotFavorite *widget.Check
|
||||
isFavorite := widget.NewCheck("Is favorite", func(fav bool) {
|
||||
if fav {
|
||||
isNotFavorite.SetChecked(false)
|
||||
}
|
||||
a.filter.ExcludeUnfavorited = fav
|
||||
debounceOnChanged()
|
||||
})
|
||||
isNotFavorite = widget.NewCheck("Is not favorite", func(fav bool) {
|
||||
if fav {
|
||||
isFavorite.SetChecked(false)
|
||||
}
|
||||
a.filter.ExcludeFavorited = fav
|
||||
debounceOnChanged()
|
||||
})
|
||||
|
||||
// setup container
|
||||
title := widget.NewLabel("Album filters")
|
||||
title.TextStyle.Bold = true
|
||||
a.container = container.NewVBox(
|
||||
container.NewHBox(layout.NewSpacer(), title, layout.NewSpacer()),
|
||||
container.NewHBox(widget.NewLabel("Year from"), minYear, widget.NewLabel("to"), maxYear),
|
||||
container.NewHBox(isFavorite, isNotFavorite),
|
||||
)
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *AlbumFilterPopup) emitOnChanged() {
|
||||
if a.OnChanged != nil {
|
||||
a.OnChanged()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlbumFilterPopup) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(a.container)
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"github.com/dweymouth/supersonic/backend"
|
||||
"github.com/dweymouth/supersonic/res"
|
||||
"github.com/dweymouth/supersonic/ui/theme"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
type FavoriteButton struct {
|
||||
widget.Button
|
||||
|
||||
IsFavorited bool
|
||||
}
|
||||
|
||||
func NewFavoriteButton(onTapped func()) *FavoriteButton {
|
||||
f := &FavoriteButton{
|
||||
Button: widget.Button{
|
||||
OnTapped: onTapped,
|
||||
Icon: res.ResHeartOutlineInvertPng,
|
||||
},
|
||||
}
|
||||
f.ExtendBaseWidget(f)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *FavoriteButton) Tapped(e *fyne.PointEvent) {
|
||||
f.IsFavorited = !f.IsFavorited
|
||||
f.Button.Tapped(e)
|
||||
f.Refresh()
|
||||
}
|
||||
|
||||
func (f *FavoriteButton) Refresh() {
|
||||
if f.IsFavorited {
|
||||
f.Icon = theme.FavoriteIcon
|
||||
} else {
|
||||
f.Icon = theme.NotFavoriteIcon
|
||||
}
|
||||
f.Button.Refresh()
|
||||
}
|
||||
|
||||
type AlbumFilterButton struct {
|
||||
widget.Button
|
||||
|
||||
filter *backend.AlbumFilter
|
||||
|
||||
dialogConstructor func(*backend.AlbumFilter) fyne.CanvasObject
|
||||
dialog *widget.PopUp
|
||||
}
|
||||
|
||||
func NewAlbumFilterButton(filter *backend.AlbumFilter, dialogConstructor func(*backend.AlbumFilter) fyne.CanvasObject) *AlbumFilterButton {
|
||||
a := &AlbumFilterButton{
|
||||
filter: filter,
|
||||
dialogConstructor: dialogConstructor,
|
||||
Button: widget.Button{
|
||||
Icon: theme.AlbumIcon,
|
||||
},
|
||||
}
|
||||
a.OnTapped = a.showFilterDialog
|
||||
a.ExtendBaseWidget(a)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *AlbumFilterButton) showFilterDialog() {
|
||||
if a.dialog == nil {
|
||||
a.dialog = widget.NewModalPopUp(a.dialogConstructor(a.filter), fyne.CurrentApp().Driver().CanvasForObject(a))
|
||||
}
|
||||
a.dialog.Show()
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"github.com/dweymouth/supersonic/res"
|
||||
"github.com/dweymouth/supersonic/ui/theme"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
type FavoriteButton struct {
|
||||
widget.Button
|
||||
|
||||
IsFavorited bool
|
||||
}
|
||||
|
||||
func NewFavoriteButton(onTapped func()) *FavoriteButton {
|
||||
f := &FavoriteButton{
|
||||
Button: widget.Button{
|
||||
OnTapped: onTapped,
|
||||
Icon: res.ResHeartOutlineInvertPng,
|
||||
},
|
||||
}
|
||||
f.ExtendBaseWidget(f)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *FavoriteButton) Tapped(e *fyne.PointEvent) {
|
||||
f.IsFavorited = !f.IsFavorited
|
||||
f.Button.Tapped(e)
|
||||
f.Refresh()
|
||||
}
|
||||
|
||||
func (f *FavoriteButton) Refresh() {
|
||||
if f.IsFavorited {
|
||||
f.Icon = theme.FavoriteIcon
|
||||
} else {
|
||||
f.Icon = theme.NotFavoriteIcon
|
||||
}
|
||||
f.Button.Refresh()
|
||||
}
|
||||
@@ -13,19 +13,21 @@ import (
|
||||
type TextRestrictedEntry struct {
|
||||
widget.Entry
|
||||
|
||||
charAllowed func(string, rune) bool
|
||||
charAllowed CharAllowedFunc
|
||||
|
||||
minWidth float32
|
||||
}
|
||||
|
||||
func NewTextRestrictedEntry(charAllowed func(curText string, r rune) bool) *TextRestrictedEntry {
|
||||
type CharAllowedFunc func(curText string, selectedText string, r rune) bool
|
||||
|
||||
func NewTextRestrictedEntry(charAllowed CharAllowedFunc) *TextRestrictedEntry {
|
||||
e := &TextRestrictedEntry{charAllowed: charAllowed}
|
||||
e.ExtendBaseWidget(e)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *TextRestrictedEntry) TypedRune(r rune) {
|
||||
if e.charAllowed == nil || e.charAllowed(e.Text, r) {
|
||||
if e.charAllowed == nil || e.charAllowed(e.Text, e.SelectedText(), r) {
|
||||
e.Entry.TypedRune(r)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user