Merge branch 'main' into feature/new-playlist-btn
This commit is contained in:
@@ -62,10 +62,7 @@ func (a *albumsPageAdapter) Route() controller.Route { return controller.AlbumsR
|
||||
|
||||
func (a *albumsPageAdapter) SortOrders() ([]string, int) {
|
||||
orders := a.mp.AlbumSortOrders()
|
||||
sortOrder := slices.Index(orders, a.cfg.SortOrder)
|
||||
if sortOrder < 0 {
|
||||
sortOrder = 0
|
||||
}
|
||||
sortOrder := max(slices.Index(orders, a.cfg.SortOrder), 0)
|
||||
|
||||
return util.LocalizeSlice(orders), sortOrder
|
||||
}
|
||||
|
||||
@@ -486,7 +486,7 @@ type ArtistPageHeader struct {
|
||||
menuBtn *widget.Button
|
||||
container *fyne.Container
|
||||
fullSizeCoverFetching bool
|
||||
//shareMenuItem *fyne.MenuItem
|
||||
// shareMenuItem *fyne.MenuItem
|
||||
}
|
||||
|
||||
func NewArtistPageHeader(page *ArtistPage) *ArtistPageHeader {
|
||||
|
||||
@@ -47,10 +47,7 @@ func (a *artistsPageAdapter) Route() controller.Route { return controller.Artist
|
||||
|
||||
func (a *artistsPageAdapter) SortOrders() ([]string, int) {
|
||||
orders := a.mp.ArtistSortOrders()
|
||||
sortOrder := slices.Index(orders, a.cfg.SortOrder)
|
||||
if sortOrder < 0 {
|
||||
sortOrder = 0
|
||||
}
|
||||
sortOrder := max(slices.Index(orders, a.cfg.SortOrder), 0)
|
||||
|
||||
return util.LocalizeSlice(orders), sortOrder
|
||||
}
|
||||
|
||||
@@ -204,7 +204,8 @@ func NewGenreList(sorting widgets.ListHeaderSort) *GenreList {
|
||||
a.hdr = widgets.NewListHeader([]widgets.ListColumn{
|
||||
{Text: lang.L("Name"), Alignment: fyne.TextAlignLeading, CanToggleVisible: false},
|
||||
{Text: albumCount, Alignment: fyne.TextAlignTrailing, CanToggleVisible: false},
|
||||
{Text: trackCount, Alignment: fyne.TextAlignTrailing, CanToggleVisible: false}},
|
||||
{Text: trackCount, Alignment: fyne.TextAlignTrailing, CanToggleVisible: false},
|
||||
},
|
||||
a.columnsLayout)
|
||||
a.hdr.SetSorting(sorting)
|
||||
a.hdr.OnColumnSortChanged = a.onSorted
|
||||
@@ -261,7 +262,7 @@ func (g *GenreList) doSortGenres() {
|
||||
return
|
||||
}
|
||||
switch g.sorting.ColNumber {
|
||||
case 0: //Name
|
||||
case 0: // Name
|
||||
g.stringSort(func(g *mediaprovider.Genre) string { return g.Name })
|
||||
case 1: // Album Count
|
||||
g.intSort(func(g *mediaprovider.Genre) int { return g.AlbumCount })
|
||||
|
||||
@@ -360,8 +360,10 @@ func (a *NowPlayingPage) updateLyrics() {
|
||||
// set the widget to an empty (not nil) lyric during fetch
|
||||
// to keep it from showing "Lyrics not available"
|
||||
a.lyricsViewer.DisableTapToSeek()
|
||||
a.lyricsViewer.SetLyrics(&mediaprovider.Lyrics{Synced: true,
|
||||
Lines: []mediaprovider.LyricLine{{Text: ""}}})
|
||||
a.lyricsViewer.SetLyrics(&mediaprovider.Lyrics{
|
||||
Synced: true,
|
||||
Lines: []mediaprovider.LyricLine{{Text: ""}},
|
||||
})
|
||||
tr, _ := a.nowPlaying.(*mediaprovider.Track)
|
||||
go a.fetchLyrics(ctx, tr)
|
||||
}
|
||||
@@ -482,7 +484,6 @@ func (a *NowPlayingPage) OnPlayTimeUpdate(curTime, _ float64, seeked bool) {
|
||||
|
||||
func (a *NowPlayingPage) currentTracklistOrNil() *widgets.PlayQueueList {
|
||||
if a.tabs != nil {
|
||||
|
||||
switch a.tabs.SelectedIndex() {
|
||||
case 0: /*queue*/
|
||||
return a.queueList
|
||||
|
||||
@@ -400,7 +400,8 @@ func (p *PlaylistList) buildHeaderAndLayout() {
|
||||
{Text: lang.L("Name"), Alignment: fyne.TextAlignLeading, CanToggleVisible: false},
|
||||
{Text: lang.L("_Description"), Alignment: fyne.TextAlignLeading, CanToggleVisible: false},
|
||||
{Text: lang.L("Owner"), Alignment: fyne.TextAlignLeading, CanToggleVisible: false},
|
||||
{Text: trackCount, Alignment: fyne.TextAlignTrailing, CanToggleVisible: false}}, p.columnsLayout)
|
||||
{Text: trackCount, Alignment: fyne.TextAlignTrailing, CanToggleVisible: false},
|
||||
}, p.columnsLayout)
|
||||
p.header.SetSorting(p.sorting)
|
||||
p.header.OnColumnSortChanged = p.onSorted
|
||||
}
|
||||
@@ -433,7 +434,7 @@ func (p *PlaylistList) doSortPlaylists() {
|
||||
return
|
||||
}
|
||||
switch p.sorting.ColNumber {
|
||||
case 0: //Name
|
||||
case 0: // Name
|
||||
p.stringSort(func(p *mediaprovider.Playlist) string { return p.Name })
|
||||
case 1: // Description
|
||||
p.stringSort(func(p *mediaprovider.Playlist) string { return p.Description })
|
||||
|
||||
@@ -253,7 +253,8 @@ func NewRadioList(nowPlayingIDPtr *string) *RadioList {
|
||||
a.ExtendBaseWidget(a)
|
||||
a.hdr = widgets.NewListHeader([]widgets.ListColumn{
|
||||
{Text: lang.L("Name"), Alignment: fyne.TextAlignLeading, CanToggleVisible: false},
|
||||
{Text: lang.L("Home Page"), Alignment: fyne.TextAlignLeading, CanToggleVisible: false}},
|
||||
{Text: lang.L("Home Page"), Alignment: fyne.TextAlignLeading, CanToggleVisible: false},
|
||||
},
|
||||
a.columnsLayout)
|
||||
a.hdr.DisableSorting = true
|
||||
a.list = widgets.NewFocusList(
|
||||
@@ -299,9 +300,8 @@ func NewRadioList(nowPlayingIDPtr *string) *RadioList {
|
||||
row.IsPlaying = isPlaying
|
||||
row.nameLabel.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying
|
||||
if isPlaying {
|
||||
row.Content.(*fyne.Container).Objects[0] =
|
||||
container.NewBorder(nil, nil, a.playingIcon, nil,
|
||||
container.New(layout.NewCustomPaddedLayout(0, 0, -5, 0), row.nameLabel))
|
||||
row.Content.(*fyne.Container).Objects[0] = container.NewBorder(nil, nil, a.playingIcon, nil,
|
||||
container.New(layout.NewCustomPaddedLayout(0, 0, -5, 0), row.nameLabel))
|
||||
} else {
|
||||
row.Content.(*fyne.Container).Objects[0] = row.nameLabel
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package controller
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
@@ -309,7 +310,6 @@ func (c *Controller) ShowAboutDialog() {
|
||||
}
|
||||
|
||||
func (c *Controller) ShowSettingsDialog(themeUpdateCallbk func(), themeFiles map[string]string) {
|
||||
|
||||
devs, err := c.App.LocalPlayer.ListAudioDevices()
|
||||
if err != nil {
|
||||
log.Printf("error listing audio devices: %v", err)
|
||||
@@ -358,7 +358,6 @@ func (c *Controller) ShowSettingsDialog(themeUpdateCallbk func(), themeFiles map
|
||||
c.ClosePopUpOnEscape(pop)
|
||||
c.haveModal = true
|
||||
pop.Show()
|
||||
|
||||
}
|
||||
|
||||
func (c *Controller) doModalClosed() {
|
||||
@@ -467,7 +466,6 @@ func (c *Controller) ShowDownloadDialog(tracks []*mediaprovider.Track, downloadN
|
||||
} else {
|
||||
go c.downloadTracks(tracks, file.URI().Path(), downloadName)
|
||||
}
|
||||
|
||||
},
|
||||
c.MainWindow)
|
||||
dg.SetFileName(fileName)
|
||||
@@ -549,7 +547,7 @@ func (c *Controller) sendNotification(title, content string) {
|
||||
|
||||
func (c *Controller) showError(content string) {
|
||||
// TODO: display an in-app toast message instead of a dialog.
|
||||
dialog.ShowError(fmt.Errorf(content), c.MainWindow)
|
||||
dialog.ShowError(errors.New(content), c.MainWindow)
|
||||
}
|
||||
|
||||
func (c *Controller) ShowAlbumInfoDialog(albumID, albumName string, albumCover image.Image) {
|
||||
|
||||
@@ -88,7 +88,6 @@ func (m *Controller) DoAddTracksToPlaylistWorkflow(trackIDs []string) {
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
m.ClosePopUpOnEscape(pop)
|
||||
m.haveModal = true
|
||||
|
||||
@@ -81,6 +81,7 @@ func GenresRoute() Route {
|
||||
func PlaylistRoute(id string) Route {
|
||||
return Route{Page: Playlist, Arg: id}
|
||||
}
|
||||
|
||||
func PlaylistsRoute() Route {
|
||||
return Route{Page: Playlists}
|
||||
}
|
||||
|
||||
@@ -200,7 +200,6 @@ func (m *Controller) PromptForLoginAndConnect() {
|
||||
pop.Show()
|
||||
}
|
||||
}, m.MainWindow)
|
||||
|
||||
}
|
||||
m.haveModal = true
|
||||
pop.Show()
|
||||
|
||||
@@ -374,5 +374,4 @@ func (q *searchEntry) TypedKey(e *fyne.KeyEvent) {
|
||||
default:
|
||||
q.SearchEntry.TypedKey(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -106,10 +106,7 @@ func (s *SettingsDialog) createGeneralTab(canSaveQueueToServer bool) *container.
|
||||
startupPage = widget.NewSelect(pages, func(_ string) {
|
||||
s.config.Application.StartupPage = backend.SupportedStartupPages[startupPage.SelectedIndex()]
|
||||
})
|
||||
initialIdx := slices.Index(backend.SupportedStartupPages, s.config.Application.StartupPage)
|
||||
if initialIdx < 0 {
|
||||
initialIdx = 0
|
||||
}
|
||||
initialIdx := max(slices.Index(backend.SupportedStartupPages, s.config.Application.StartupPage), 0)
|
||||
startupPage.SetSelectedIndex(initialIdx)
|
||||
if startupPage.Selected == "" {
|
||||
startupPage.SetSelectedIndex(0)
|
||||
@@ -430,12 +427,19 @@ func (s *SettingsDialog) createPlaybackTab(isLocalPlayer, isReplayGainPlayer boo
|
||||
disableTranscode,
|
||||
container.NewHBox(transcode, transcodeCodec, transcodeBitRate),
|
||||
s.newSectionSeparator(),
|
||||
widget.NewRichText(&widget.TextSegment{Text: "ReplayGain", Style: util.BoldRichTextStyle}),
|
||||
widget.NewLabelWithStyle("ReplayGain", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||
container.New(layout.NewFormLayout(),
|
||||
widget.NewLabel(lang.L("ReplayGain mode")), container.NewGridWithColumns(2, replayGainSelect),
|
||||
widget.NewLabel(lang.L("ReplayGain preamp")), container.NewHBox(preampGain, widget.NewLabel("dB")),
|
||||
widget.NewLabel(lang.L("Prevent clipping")), preventClipping,
|
||||
),
|
||||
s.newSectionSeparator(),
|
||||
widget.NewLabelWithStyle(lang.L("When enqueuing random"), fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||
widget.NewCheckWithData(lang.L("Skip one-star tracks"), binding.BindBool(&s.config.Playback.SkipOneStarWhenShuffling)),
|
||||
container.NewBorder(nil, nil,
|
||||
widget.NewLabel(lang.L("Skip tracks with keyword")), nil,
|
||||
widget.NewEntryWithData(binding.BindString(&s.config.Playback.SkipKeywordWhenShuffling)),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -491,7 +495,8 @@ func (s *SettingsDialog) createAppearanceTab(window fyne.Window) *container.TabI
|
||||
themeModeSelect := widget.NewSelect([]string{
|
||||
string(myTheme.AppearanceDark),
|
||||
string(myTheme.AppearanceLight),
|
||||
string(myTheme.AppearanceAuto)}, nil)
|
||||
string(myTheme.AppearanceAuto),
|
||||
}, nil)
|
||||
themeModeSelect.OnChanged = func(_ string) {
|
||||
s.config.Theme.Appearance = themeModeSelect.Options[themeModeSelect.SelectedIndex()]
|
||||
if s.OnThemeSettingChanged != nil {
|
||||
|
||||
@@ -23,7 +23,7 @@ func NewColumnsLayout(widths []float32) *ColumnsLayout {
|
||||
func (c *ColumnsLayout) MinSize(objects []fyne.CanvasObject) fyne.Size {
|
||||
var width float32
|
||||
var height float32
|
||||
for i := 0; i < len(objects); i++ {
|
||||
for i := range objects {
|
||||
if !objects[i].Visible() {
|
||||
continue
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func (c *ColumnsLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {
|
||||
expandObjW := extraW / float32(expandObjCount)
|
||||
|
||||
var x float32
|
||||
for i := 0; i < len(objects); i++ {
|
||||
for i := range objects {
|
||||
if !objects[i].Visible() {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ var (
|
||||
ShortcutNavSeven = desktop.CustomShortcut{KeyName: fyne.Key7, Modifier: fyne.KeyModifierShortcutDefault}
|
||||
ShortcutNavEight = desktop.CustomShortcut{KeyName: fyne.Key8, Modifier: fyne.KeyModifierShortcutDefault}
|
||||
|
||||
NavShortcuts = []desktop.CustomShortcut{ShortcutNavOne, ShortcutNavTwo, ShortcutNavThree,
|
||||
ShortcutNavFour, ShortcutNavFive, ShortcutNavSix, ShortcutNavSeven, ShortcutNavEight}
|
||||
NavShortcuts = []desktop.CustomShortcut{
|
||||
ShortcutNavOne, ShortcutNavTwo, ShortcutNavThree,
|
||||
ShortcutNavFour, ShortcutNavFive, ShortcutNavSix, ShortcutNavSeven, ShortcutNavEight,
|
||||
}
|
||||
)
|
||||
|
||||
+1
-4
@@ -341,10 +341,7 @@ func darkenColor(c color.Color, fraction float64) color.Color {
|
||||
}
|
||||
|
||||
func brightenComponent(component uint32, fraction float64) uint32 {
|
||||
brightened := component + uint32(float64(component)*fraction)
|
||||
if brightened > 0xffff {
|
||||
brightened = 0xffff
|
||||
}
|
||||
brightened := min(component+uint32(float64(component)*fraction), 0xffff)
|
||||
return brightened
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,6 @@ func (t *ToastOverlay) makeToastAnimFunc(endPos fyne.Position, dismissal bool) f
|
||||
t.cancelPreviousToast()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -225,7 +225,7 @@ func colorToHexAndOpacity(color color.Color) (hexStr, aStr string) {
|
||||
r, g, b, a := toNRGBA(color)
|
||||
cBytes := []byte{byte(r), byte(g), byte(b)}
|
||||
hexStr, aStr = "#"+hex.EncodeToString(cBytes), strconv.FormatFloat(float64(a)/0xff, 'f', 6, 64)
|
||||
return
|
||||
return hexStr, aStr
|
||||
}
|
||||
|
||||
// toNRGBA converts a color to RGBA values which are not premultiplied, unlike color.RGBA().
|
||||
@@ -300,7 +300,7 @@ func toNRGBA(c color.Color) (r, g, b, a int) {
|
||||
default: // RGBA, RGBA64, and unknown implementations of Color
|
||||
r, g, b, a = unmultiplyAlpha(c)
|
||||
}
|
||||
return
|
||||
return r, g, b, a
|
||||
}
|
||||
|
||||
// unmultiplyAlpha returns a color's RGBA components as 8-bit integers by calling c.RGBA() and then removing the alpha premultiplication.
|
||||
@@ -317,5 +317,5 @@ func unmultiplyAlpha(c color.Color) (r, g, b, a int) {
|
||||
g = int(green >> 8)
|
||||
b = int(blue >> 8)
|
||||
a = int(alpha >> 8)
|
||||
return
|
||||
return r, g, b, a
|
||||
}
|
||||
|
||||
+34
-1
@@ -55,7 +55,7 @@ func dateFormatForLocale(locale string) DateFormat {
|
||||
}
|
||||
|
||||
func shortMonthName(month int) string {
|
||||
var months = [12]string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}
|
||||
months := [12]string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}
|
||||
if month >= 1 && month <= 12 {
|
||||
return lang.L(months[month-1])
|
||||
}
|
||||
@@ -100,6 +100,39 @@ func FormatItemDate(date mediaprovider.ItemDate) string {
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func LastPlayedDisplayString(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return lang.L("never")
|
||||
}
|
||||
switch d := time.Since(t); {
|
||||
case d.Hours() < 1:
|
||||
mins := int(d.Minutes())
|
||||
return lang.LocalizePluralKey("x_minutes_ago",
|
||||
fmt.Sprintf("%d minutes ago", mins), mins,
|
||||
map[string]string{"minutes": strconv.Itoa(mins)})
|
||||
case d.Hours() < 24:
|
||||
hrs := int(d.Hours())
|
||||
return lang.LocalizePluralKey("x_hours_ago",
|
||||
fmt.Sprintf("%d hours ago", hrs), hrs,
|
||||
map[string]string{"hours": strconv.Itoa(hrs)})
|
||||
case d.Hours() < 24*31:
|
||||
days := int(d.Hours() / 24)
|
||||
return lang.LocalizePluralKey("x_days_ago",
|
||||
fmt.Sprintf("%d days ago", days), days,
|
||||
map[string]string{"days": strconv.Itoa(days)})
|
||||
case d.Hours() < 24*365:
|
||||
months := int(d.Hours() / (24 * 31))
|
||||
return lang.LocalizePluralKey("x_months_ago",
|
||||
fmt.Sprintf("%d months ago", months), months,
|
||||
map[string]string{"months": strconv.Itoa(months)})
|
||||
default:
|
||||
years := int(d.Hours() / (24 * 365))
|
||||
return lang.LocalizePluralKey("x_years_ago",
|
||||
fmt.Sprintf("%d years ago", years), years,
|
||||
map[string]string{"years": strconv.Itoa(years)})
|
||||
}
|
||||
}
|
||||
|
||||
var BoldRichTextStyle = widget.RichTextStyle{TextStyle: fyne.TextStyle{Bold: true}, Inline: true}
|
||||
|
||||
func MakeOpaque(c color.Color) color.Color {
|
||||
|
||||
@@ -210,7 +210,7 @@ func (l *peakMeterRenderer) Refresh() {
|
||||
|
||||
func (l *peakMeterRenderer) Objects() []fyne.CanvasObject {
|
||||
if l.objects == nil {
|
||||
l.objects = make([]fyne.CanvasObject, 0, 2*len(l.rulerLines) + 8)
|
||||
l.objects = make([]fyne.CanvasObject, 0, 2*len(l.rulerLines)+8)
|
||||
for i := range l.rulerLines {
|
||||
l.objects = append(l.objects, &l.rulerLines[i], &l.rulerLabels[i])
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ type GenreFilterSubsection struct {
|
||||
genreList []string
|
||||
onChanged func([]string)
|
||||
|
||||
selectedGenres map[string]interface{}
|
||||
selectedGenres map[string]any
|
||||
selectedGenresMutex sync.RWMutex
|
||||
|
||||
filterText *widget.Entry
|
||||
@@ -253,7 +253,7 @@ type GenreFilterSubsection struct {
|
||||
func NewGenreFilterSubsection(onChanged func([]string), initialSelectedGenres []string) *GenreFilterSubsection {
|
||||
g := &GenreFilterSubsection{
|
||||
onChanged: onChanged,
|
||||
selectedGenres: make(map[string]interface{}),
|
||||
selectedGenres: make(map[string]any),
|
||||
}
|
||||
g.ExtendBaseWidget(g)
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ func NewAuxControls(initialVolume int, initialLoopMode backend.LoopMode, initial
|
||||
a.cast.SetToolTip(lang.L("Cast to device"))
|
||||
|
||||
a.autoplay.Highlighted = initialAutoplay
|
||||
//a.autoplay.IconSize = IconButtonSizeSmaller
|
||||
// a.autoplay.IconSize = IconButtonSizeSmaller
|
||||
a.autoplay.SetToolTip(lang.L("Autoplay"))
|
||||
a.autoplay.OnTapped = func() {
|
||||
a.SetAutoplay(!a.autoplay.Highlighted)
|
||||
|
||||
@@ -71,10 +71,12 @@ func (g *FocusList) FocusNeighbor(curItem widget.ListItemID, up bool) {
|
||||
}
|
||||
}
|
||||
|
||||
var _ fyne.Tappable = (*FocusListRowBase)(nil)
|
||||
var _ fyne.Widget = (*FocusListRowBase)(nil)
|
||||
var _ fyne.Focusable = (*FocusListRowBase)(nil)
|
||||
var _ desktop.Hoverable = (*FocusListRowBase)(nil)
|
||||
var (
|
||||
_ fyne.Tappable = (*FocusListRowBase)(nil)
|
||||
_ fyne.Widget = (*FocusListRowBase)(nil)
|
||||
_ fyne.Focusable = (*FocusListRowBase)(nil)
|
||||
_ desktop.Hoverable = (*FocusListRowBase)(nil)
|
||||
)
|
||||
|
||||
// Base type used for all list rows in widgets such as Tracklist, etc.
|
||||
type FocusListRowBase struct {
|
||||
@@ -88,7 +90,7 @@ type FocusListRowBase struct {
|
||||
|
||||
OnTapped func()
|
||||
OnDoubleTapped func()
|
||||
OnFocusNeighbor func(up bool) //TODO: func(up, selecting bool)
|
||||
OnFocusNeighbor func(up bool) // TODO: func(up, selecting bool)
|
||||
|
||||
tappedAt int64 // unixMillis
|
||||
focusedRect canvas.Rectangle
|
||||
|
||||
@@ -21,8 +21,10 @@ import (
|
||||
"github.com/dweymouth/supersonic/ui/util"
|
||||
)
|
||||
|
||||
var _ fyne.Widget = (*GridViewItem)(nil)
|
||||
var _ fyne.Focusable = (*GridViewItem)(nil)
|
||||
var (
|
||||
_ fyne.Widget = (*GridViewItem)(nil)
|
||||
_ fyne.Focusable = (*GridViewItem)(nil)
|
||||
)
|
||||
|
||||
var _ fyne.Widget = (*coverImage)(nil)
|
||||
|
||||
@@ -376,11 +378,10 @@ func setPlayBtnTranslucency(f float32) {
|
||||
|
||||
// get theme Primary color as color.NRGBA
|
||||
var primary color.NRGBA
|
||||
switch pr := theme.Color(theme.ColorNamePrimary); pr.(type) {
|
||||
switch pr := theme.Color(theme.ColorNamePrimary).(type) {
|
||||
case color.NRGBA:
|
||||
primary = pr.(color.NRGBA)
|
||||
primary = pr
|
||||
case color.RGBA:
|
||||
pr := pr.(color.RGBA)
|
||||
primary = color.NRGBA{R: pr.R, G: pr.G, B: pr.B, A: pr.A}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,10 @@ type LoadingDots struct {
|
||||
func NewLoadingDots() *LoadingDots {
|
||||
l := &LoadingDots{}
|
||||
for i := range l.dots {
|
||||
l.dots[i] = minSizeCircle{Circle: canvas.Circle{
|
||||
FillColor: theme.DisabledColor()},
|
||||
l.dots[i] = minSizeCircle{
|
||||
Circle: canvas.Circle{
|
||||
FillColor: theme.DisabledColor(),
|
||||
},
|
||||
}
|
||||
}
|
||||
l.ExtendBaseWidget(l)
|
||||
|
||||
@@ -31,7 +31,7 @@ type MultiHyperlink struct {
|
||||
|
||||
// TODO: Once https://github.com/fyne-io/fyne/issues/4336 is resolved,
|
||||
// we can switch to the much cleaner RichText implementation
|
||||
//provider *widget.RichText
|
||||
// provider *widget.RichText
|
||||
|
||||
objects []fyne.CanvasObject
|
||||
suffixLabel *ttwidget.RichText
|
||||
@@ -45,11 +45,11 @@ type MultiHyperlinkSegment struct {
|
||||
|
||||
func NewMultiHyperlink() *MultiHyperlink {
|
||||
c := &MultiHyperlink{
|
||||
//provider: widget.NewRichText(),
|
||||
// provider: widget.NewRichText(),
|
||||
content: container.NewWithoutLayout(),
|
||||
}
|
||||
c.ExtendBaseWidget(c)
|
||||
//c.provider.Truncation = fyne.TextTruncateEllipsis
|
||||
// c.provider.Truncation = fyne.TextTruncateEllipsis
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -302,12 +302,12 @@ func (c *MultiHyperlink) Resize(size fyne.Size) {
|
||||
}
|
||||
|
||||
func (c *MultiHyperlink) Refresh() {
|
||||
//c.syncSegments()
|
||||
// c.syncSegments()
|
||||
c.layoutObjects()
|
||||
c.BaseWidget.Refresh()
|
||||
}
|
||||
|
||||
func (c *MultiHyperlink) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(c.content)
|
||||
//return widget.NewSimpleRenderer(c.provider)
|
||||
// return widget.NewSimpleRenderer(c.provider)
|
||||
}
|
||||
|
||||
@@ -264,7 +264,6 @@ func (p *PlayQueueList) onShowContextMenu(e *fyne.PointEvent, trackIdx int) {
|
||||
p.ensureRadiosMenu()
|
||||
p.radiosMenu.ShowAtPosition(e.AbsolutePosition)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (p *PlayQueueList) ensureTracksMenu() {
|
||||
|
||||
@@ -45,7 +45,7 @@ func NewStarRating() *StarRating {
|
||||
func (s *StarRating) createContainer() {
|
||||
s.container = container.New(layout.NewCustomPaddedHBoxLayout(0))
|
||||
var im *canvas.Image
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
if s.IsDisabled {
|
||||
im = canvas.NewImageFromResource(themedDisabledStarOutline)
|
||||
} else if s.Rating > i {
|
||||
@@ -103,7 +103,7 @@ var _ fyne.Tappable = (*StarRating)(nil)
|
||||
|
||||
func (s *StarRating) Tapped(*fyne.PointEvent) {
|
||||
if s.mouseHoverRating <= 0 {
|
||||
return //shouldn't happen
|
||||
return // shouldn't happen
|
||||
}
|
||||
if s.Rating == s.mouseHoverRating {
|
||||
s.Rating = 0
|
||||
@@ -126,7 +126,7 @@ func (s *StarRating) Refresh() {
|
||||
if !s.holdRating && s.mouseHoverRating > 0 {
|
||||
rating = s.mouseHoverRating
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
im := s.container.Objects[i].(*canvas.Image)
|
||||
im.SetMinSize(fyne.NewSize(s.StarSize, s.StarSize))
|
||||
if s.IsDisabled {
|
||||
|
||||
@@ -446,6 +446,8 @@ func (t *Tracklist) doSortTracks() {
|
||||
t.intSort(func(tr *util.TrackListModel) int64 { return tr.Track().Size })
|
||||
case ColumnPlays:
|
||||
t.intSort(func(tr *util.TrackListModel) int64 { return int64(tr.Track().PlayCount) })
|
||||
case ColumnLastPlayed:
|
||||
t.intSort(func(tr *util.TrackListModel) int64 { return tr.Track().LastPlayed.Unix() })
|
||||
case ColumnComment:
|
||||
t.stringSort(func(tr *util.TrackListModel) string { return tr.Track().Comment })
|
||||
case ColumnBPM:
|
||||
|
||||
@@ -58,7 +58,7 @@ func (t *TracklistLoader) loadMoreTracks(num int) {
|
||||
t.trackBuffer = make([]*mediaprovider.Track, 0, num)
|
||||
}
|
||||
t.trackBuffer = t.trackBuffer[:0]
|
||||
for i := 0; i < num; i++ {
|
||||
for range num {
|
||||
tr := t.iter.Next()
|
||||
if tr == nil {
|
||||
t.done = true
|
||||
|
||||
+39
-26
@@ -61,6 +61,7 @@ const (
|
||||
ColumnFavorite = "Favorite"
|
||||
ColumnRating = "Rating"
|
||||
ColumnPlays = "Plays"
|
||||
ColumnLastPlayed = "LastPlayed"
|
||||
ColumnComment = "Comment"
|
||||
ColumnBPM = "BPM"
|
||||
ColumnBitrate = "Bitrate"
|
||||
@@ -87,6 +88,7 @@ var (
|
||||
fav := lang.L("Fav.")
|
||||
rating := lang.L("Rating")
|
||||
plays := lang.L("Plays")
|
||||
lastPlayed := lang.L("Last played")
|
||||
comment := lang.L("Comment")
|
||||
bpm := lang.L("BPM")
|
||||
bitrate := lang.L("Bit rate")
|
||||
@@ -104,6 +106,7 @@ var (
|
||||
{Name: ColumnFavorite, Col: ListColumn{Text: " " + fav, Alignment: fyne.TextAlignCenter, CanToggleVisible: true}},
|
||||
{Name: ColumnRating, Col: ListColumn{Text: rating, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
||||
{Name: ColumnPlays, Col: ListColumn{Text: plays, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
||||
{Name: ColumnLastPlayed, Col: ListColumn{Text: lastPlayed, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
||||
{Name: ColumnComment, Col: ListColumn{Text: comment, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
||||
{Name: ColumnBPM, Col: ListColumn{Text: bpm, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
||||
{Name: ColumnBitrate, Col: ListColumn{Text: bitrate, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
||||
@@ -121,6 +124,7 @@ var (
|
||||
{Name: ColumnFavorite, Col: ListColumn{Text: fav, Alignment: fyne.TextAlignCenter, CanToggleVisible: true}},
|
||||
{Name: ColumnRating, Col: ListColumn{Text: rating, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
||||
{Name: ColumnPlays, Col: ListColumn{Text: plays, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
||||
{Name: ColumnLastPlayed, Col: ListColumn{Text: lastPlayed, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
||||
{Name: ColumnComment, Col: ListColumn{Text: comment, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
||||
{Name: ColumnBPM, Col: ListColumn{Text: bpm, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
||||
{Name: ColumnBitrate, Col: ListColumn{Text: bitrate, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
||||
@@ -144,6 +148,10 @@ var (
|
||||
playsColWidth := fyne.Max(
|
||||
widget.NewLabel("9999").MinSize().Width,
|
||||
widget.NewLabel(plays).MinSize().Width+sortIconWidth)
|
||||
lastPlayedColWidth := fyne.Max(
|
||||
widget.NewLabel(lang.LocalizePluralKey("x_minutes_ago", "59 minutes ago", 59, map[string]string{"minutes": "59"})).MinSize().Width,
|
||||
widget.NewLabel(lastPlayed).MinSize().Width+sortIconWidth,
|
||||
)
|
||||
bpmColWidth := fyne.Max(
|
||||
widget.NewLabel(bpm+" ").MinSize().Width,
|
||||
widget.NewLabel("9999").MinSize().Width)
|
||||
@@ -154,10 +162,10 @@ var (
|
||||
widget.NewLabel("99.9 MB").MinSize().Width,
|
||||
widget.NewLabel(size).MinSize().Width+sortIconWidth)
|
||||
|
||||
// #, Title, Artist, Album, Composer, Time, Year, Favorite, Rating, Plays, Comment, BPM, Bitrate, Size, Path
|
||||
CompactTracklistRowColumnWidths = []float32{numColWidth, -1, -1, -1, -1, timeColWidth, yearColWidth, favColWidth, ratingColWidth, playsColWidth, -1, bpmColWidth, bitrateColWidth, sizeColWidth, -1}
|
||||
// #, Title/Artist, Album, Composer, Time, Year, Favorite, Rating, Plays, Comment, BPM, Bitrate, Size, Path
|
||||
ExpandedTracklistRowColumnWidths = []float32{numColWidth, -1, -1, -1, timeColWidth, yearColWidth, favColWidth, ratingColWidth, playsColWidth, -1, bpmColWidth, bitrateColWidth, sizeColWidth, -1}
|
||||
// #, Title, Artist, Album, Composer, Time, Year, Favorite, Rating, Plays, LastPlayed, Comment, BPM, Bitrate, Size, Path
|
||||
CompactTracklistRowColumnWidths = []float32{numColWidth, -1, -1, -1, -1, timeColWidth, yearColWidth, favColWidth, ratingColWidth, playsColWidth, lastPlayedColWidth, -1, bpmColWidth, bitrateColWidth, sizeColWidth, -1}
|
||||
// #, Title/Artist, Album, Composer, Time, Year, Favorite, Rating, Plays, LastPlayed, Comment, BPM, Bitrate, Size, Path
|
||||
ExpandedTracklistRowColumnWidths = []float32{numColWidth, -1, -1, -1, timeColWidth, yearColWidth, favColWidth, ratingColWidth, playsColWidth, lastPlayedColWidth, -1, bpmColWidth, bitrateColWidth, sizeColWidth, -1}
|
||||
})
|
||||
)
|
||||
|
||||
@@ -185,21 +193,22 @@ type tracklistRowBase struct {
|
||||
nextUpdateModel *util.TrackListModel
|
||||
nextUpdateRowNum int
|
||||
|
||||
num *widget.Label
|
||||
name *ttwidget.RichText
|
||||
artist *MultiHyperlink
|
||||
album *MultiHyperlink // for disabled support, if albumID is ""
|
||||
composer *MultiHyperlink
|
||||
dur *widget.Label
|
||||
year *widget.Label
|
||||
favorite *fyne.Container
|
||||
rating *StarRating
|
||||
bitrate *widget.Label
|
||||
plays *widget.Label
|
||||
comment *ttwidget.Label
|
||||
bpm *widget.Label
|
||||
size *widget.Label
|
||||
path *ttwidget.Label
|
||||
num *widget.Label
|
||||
name *ttwidget.RichText
|
||||
artist *MultiHyperlink
|
||||
album *MultiHyperlink // for disabled support, if albumID is ""
|
||||
composer *MultiHyperlink
|
||||
dur *widget.Label
|
||||
year *widget.Label
|
||||
favorite *fyne.Container
|
||||
rating *StarRating
|
||||
bitrate *widget.Label
|
||||
plays *widget.Label
|
||||
lastPlayed *widget.Label
|
||||
comment *ttwidget.Label
|
||||
bpm *widget.Label
|
||||
size *widget.Label
|
||||
path *ttwidget.Label
|
||||
|
||||
// must be injected by extending widget
|
||||
setColVisibility func(int, bool) bool
|
||||
@@ -253,7 +262,7 @@ func NewExpandedTracklistRow(tracklist *Tracklist, im *backend.ImageManager, pla
|
||||
|
||||
v := makeVerticallyCentered // func alias
|
||||
container := container.New(tracklist.colLayout,
|
||||
v(t.num), titleArtistImg, v(t.album), v(t.composer), v(t.dur), v(t.year), v(t.favorite), v(t.rating), v(t.plays), v(t.comment), v(t.bpm), v(t.bitrate), v(t.size), v(t.path))
|
||||
v(t.num), titleArtistImg, v(t.album), v(t.composer), v(t.dur), v(t.year), v(t.favorite), v(t.rating), v(t.plays), v(t.lastPlayed), v(t.comment), v(t.bpm), v(t.bitrate), v(t.size), v(t.path))
|
||||
t.Content = container
|
||||
t.setColVisibility = func(colNum int, vis bool) bool {
|
||||
c := container.Objects[colNum].(*fyne.Container)
|
||||
@@ -281,7 +290,7 @@ func NewCompactTracklistRow(tracklist *Tracklist, playingIcon fyne.CanvasObject)
|
||||
t.playingIcon = playingIcon
|
||||
|
||||
t.Content = container.New(tracklist.colLayout,
|
||||
t.num, t.name, t.artist, t.album, t.composer, t.dur, t.year, t.favorite, t.rating, t.plays, t.comment, t.bpm, t.bitrate, t.size, t.path)
|
||||
t.num, t.name, t.artist, t.album, t.composer, t.dur, t.year, t.favorite, t.rating, t.plays, t.lastPlayed, t.comment, t.bpm, t.bitrate, t.size, t.path)
|
||||
|
||||
colHiddenPtrMap := map[int]*bool{
|
||||
2: &t.artist.Hidden,
|
||||
@@ -292,11 +301,12 @@ func NewCompactTracklistRow(tracklist *Tracklist, playingIcon fyne.CanvasObject)
|
||||
7: &t.favorite.Hidden,
|
||||
8: &t.rating.Hidden,
|
||||
9: &t.plays.Hidden,
|
||||
10: &t.comment.Hidden,
|
||||
11: &t.bpm.Hidden,
|
||||
12: &t.bitrate.Hidden,
|
||||
13: &t.size.Hidden,
|
||||
14: &t.path.Hidden,
|
||||
10: &t.lastPlayed.Hidden,
|
||||
11: &t.comment.Hidden,
|
||||
12: &t.bpm.Hidden,
|
||||
13: &t.bitrate.Hidden,
|
||||
14: &t.size.Hidden,
|
||||
15: &t.path.Hidden,
|
||||
}
|
||||
t.setColVisibility = func(colNum int, vis bool) bool {
|
||||
ptr, ok := colHiddenPtrMap[colNum]
|
||||
@@ -338,6 +348,7 @@ func (t *tracklistRowBase) create(tracklist *Tracklist) {
|
||||
t.rating.StarSize = 16
|
||||
t.rating.OnRatingChanged = t.setTrackRating
|
||||
t.plays = util.NewTrailingAlignLabel()
|
||||
t.lastPlayed = util.NewTruncatingLabel()
|
||||
t.comment = util.NewTruncatingTooltipLabel()
|
||||
t.comment.OnMouseIn = t.MouseIn
|
||||
t.comment.OnMouseOut = t.MouseOut
|
||||
@@ -352,6 +363,7 @@ func (t *tracklistRowBase) create(tracklist *Tracklist) {
|
||||
func (t *tracklistRowBase) SetOnTappedSecondary(f func(*fyne.PointEvent, int)) {
|
||||
t.OnTappedSecondary = f
|
||||
}
|
||||
|
||||
func (t *tracklistRowBase) TrackID() string {
|
||||
return t.trackID
|
||||
}
|
||||
@@ -404,6 +416,7 @@ func (t *tracklistRowBase) doUpdate(tm *util.TrackListModel, rowNum int) {
|
||||
t.dur.Text = util.SecondsToMMSS(tr.Duration.Seconds())
|
||||
t.year.Text = strconv.Itoa(tr.Year)
|
||||
t.plays.Text = strconv.Itoa(int(tr.PlayCount))
|
||||
t.lastPlayed.Text = util.LastPlayedDisplayString(tr.LastPlayed)
|
||||
t.comment.Text = strings.ReplaceAll(tr.Comment, "\n", " ")
|
||||
t.comment.SetToolTip(tr.Comment)
|
||||
t.bpm.Text = strconv.Itoa(tr.BPM)
|
||||
|
||||
Reference in New Issue
Block a user