Merge pull request #424 from dweymouth/feature/track-info-dialog
Add track info dialog
This commit is contained in:
@@ -14,7 +14,11 @@ func GetSimilarSongsFallback(mp mediaprovider.MediaProvider, track *mediaprovide
|
|||||||
tracks, _ = mp.GetSimilarTracks(track.ArtistIDs[0], count)
|
tracks, _ = mp.GetSimilarTracks(track.ArtistIDs[0], count)
|
||||||
}
|
}
|
||||||
if len(tracks) == 0 {
|
if len(tracks) == 0 {
|
||||||
tracks, _ = mp.GetRandomTracks(track.Genre, count)
|
genre := ""
|
||||||
|
if len(track.Genres) > 0 {
|
||||||
|
genre = track.Genres[0]
|
||||||
|
}
|
||||||
|
tracks, _ = mp.GetRandomTracks(genre, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure to exclude the song itself from the similar list
|
// make sure to exclude the song itself from the similar list
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package mediaprovider
|
package mediaprovider
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
// Bit field flag for the ReleaseTypes property
|
// Bit field flag for the ReleaseTypes property
|
||||||
type ReleaseType = int32
|
type ReleaseType = int32
|
||||||
|
|
||||||
@@ -90,7 +92,7 @@ type Track struct {
|
|||||||
Duration int
|
Duration int
|
||||||
TrackNumber int
|
TrackNumber int
|
||||||
DiscNumber int
|
DiscNumber int
|
||||||
Genre string
|
Genres []string
|
||||||
ArtistIDs []string
|
ArtistIDs []string
|
||||||
ArtistNames []string
|
ArtistNames []string
|
||||||
Album string
|
Album string
|
||||||
@@ -100,9 +102,20 @@ type Track struct {
|
|||||||
Favorite bool
|
Favorite bool
|
||||||
Size int64
|
Size int64
|
||||||
PlayCount int
|
PlayCount int
|
||||||
|
LastPlayed time.Time
|
||||||
FilePath string
|
FilePath string
|
||||||
BitRate int
|
BitRate int
|
||||||
|
ContentType string
|
||||||
Comment string
|
Comment string
|
||||||
|
BPM int
|
||||||
|
ReplayGain ReplayGainInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReplayGainInfo struct {
|
||||||
|
TrackGain float64
|
||||||
|
AlbumGain float64
|
||||||
|
TrackPeak float64
|
||||||
|
AlbumPeak float64
|
||||||
}
|
}
|
||||||
|
|
||||||
type Playlist struct {
|
type Playlist struct {
|
||||||
|
|||||||
@@ -493,6 +493,22 @@ func toTrack(ch *subsonic.Child) *mediaprovider.Track {
|
|||||||
artistIDs = append(artistIDs, ch.ArtistID)
|
artistIDs = append(artistIDs, ch.ArtistID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rGain mediaprovider.ReplayGainInfo
|
||||||
|
if rg := ch.ReplayGain; rg != nil {
|
||||||
|
rGain.AlbumGain = rg.AlbumGain
|
||||||
|
rGain.TrackGain = rg.TrackGain
|
||||||
|
rGain.AlbumPeak = rg.AlbumPeak
|
||||||
|
rGain.TrackPeak = rg.TrackPeak
|
||||||
|
}
|
||||||
|
var genres []string
|
||||||
|
if len(ch.Genres) > 0 {
|
||||||
|
genres = sharedutil.MapSlice(ch.Genres, func(idName subsonic.IDName) string {
|
||||||
|
return idName.Name
|
||||||
|
})
|
||||||
|
} else if ch.Genre != "" {
|
||||||
|
genres = []string{ch.Genre}
|
||||||
|
}
|
||||||
|
|
||||||
return &mediaprovider.Track{
|
return &mediaprovider.Track{
|
||||||
ID: ch.ID,
|
ID: ch.ID,
|
||||||
CoverArtID: ch.CoverArt,
|
CoverArtID: ch.CoverArt,
|
||||||
@@ -501,7 +517,7 @@ func toTrack(ch *subsonic.Child) *mediaprovider.Track {
|
|||||||
Duration: ch.Duration,
|
Duration: ch.Duration,
|
||||||
TrackNumber: ch.Track,
|
TrackNumber: ch.Track,
|
||||||
DiscNumber: ch.DiscNumber,
|
DiscNumber: ch.DiscNumber,
|
||||||
Genre: ch.Genre,
|
Genres: genres,
|
||||||
ArtistIDs: artistIDs,
|
ArtistIDs: artistIDs,
|
||||||
ArtistNames: artistNames,
|
ArtistNames: artistNames,
|
||||||
Album: ch.Album,
|
Album: ch.Album,
|
||||||
@@ -510,10 +526,14 @@ func toTrack(ch *subsonic.Child) *mediaprovider.Track {
|
|||||||
Rating: ch.UserRating,
|
Rating: ch.UserRating,
|
||||||
Favorite: !ch.Starred.IsZero(),
|
Favorite: !ch.Starred.IsZero(),
|
||||||
PlayCount: int(ch.PlayCount),
|
PlayCount: int(ch.PlayCount),
|
||||||
|
LastPlayed: ch.Played,
|
||||||
FilePath: ch.Path,
|
FilePath: ch.Path,
|
||||||
Size: ch.Size,
|
Size: ch.Size,
|
||||||
BitRate: ch.BitRate,
|
BitRate: ch.BitRate,
|
||||||
|
ContentType: ch.ContentType,
|
||||||
Comment: ch.Comment,
|
Comment: ch.Comment,
|
||||||
|
BPM: ch.BPM,
|
||||||
|
ReplayGain: rGain,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-5
@@ -254,7 +254,7 @@ func (m *MPRISHandler) Metadata() (types.Metadata, error) {
|
|||||||
var meta mediaprovider.MediaItemMetadata
|
var meta mediaprovider.MediaItemMetadata
|
||||||
// metadata that can come only from tracks
|
// metadata that can come only from tracks
|
||||||
var discNumber, trackNumber, userRating, playCount, year int
|
var discNumber, trackNumber, userRating, playCount, year int
|
||||||
var genre string
|
var genres []string
|
||||||
|
|
||||||
if np := m.pm.NowPlaying(); np != nil && status.State != player.Stopped {
|
if np := m.pm.NowPlaying(); np != nil && status.State != player.Stopped {
|
||||||
meta = np.Metadata()
|
meta = np.Metadata()
|
||||||
@@ -264,7 +264,7 @@ func (m *MPRISHandler) Metadata() (types.Metadata, error) {
|
|||||||
userRating = track.Rating
|
userRating = track.Rating
|
||||||
playCount = track.PlayCount
|
playCount = track.PlayCount
|
||||||
year = track.Year
|
year = track.Year
|
||||||
genre = track.Genre
|
genres = track.Genres
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var artURL string
|
var artURL string
|
||||||
@@ -284,9 +284,7 @@ func (m *MPRISHandler) Metadata() (types.Metadata, error) {
|
|||||||
UserRating: float64(userRating) / 5,
|
UserRating: float64(userRating) / 5,
|
||||||
UseCount: playCount,
|
UseCount: playCount,
|
||||||
ArtUrl: artURL,
|
ArtUrl: artURL,
|
||||||
}
|
Genre: genres,
|
||||||
if genre != "" {
|
|
||||||
mprisMeta.Genre = []string{genre}
|
|
||||||
}
|
}
|
||||||
if year != 0 {
|
if year != 0 {
|
||||||
mprisMeta.ContentCreated = strconv.Itoa(year)
|
mprisMeta.ContentCreated = strconv.Itoa(year)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ require (
|
|||||||
github.com/dweymouth/fyne-lyrics v0.0.0-20240528234907-15eee7ce5e64
|
github.com/dweymouth/fyne-lyrics v0.0.0-20240528234907-15eee7ce5e64
|
||||||
github.com/dweymouth/go-jellyfin v0.0.0-20240517151952-5ceca61cb645
|
github.com/dweymouth/go-jellyfin v0.0.0-20240517151952-5ceca61cb645
|
||||||
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee
|
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee
|
||||||
github.com/dweymouth/go-subsonic v0.0.0-20240603150834-605046e7c78a
|
github.com/dweymouth/go-subsonic v0.0.0-20240716154859-c40a4519e1e0
|
||||||
github.com/godbus/dbus/v5 v5.1.0
|
github.com/godbus/dbus/v5 v5.1.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/pelletier/go-toml/v2 v2.0.8
|
github.com/pelletier/go-toml/v2 v2.0.8
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee h1:ZGyJ6wp7CAfT31
|
|||||||
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee/go.mod h1:Ov0ieN90M7i+0k3OxhA/g1dozGs+UcPHDsMKqPgRDk0=
|
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee/go.mod h1:Ov0ieN90M7i+0k3OxhA/g1dozGs+UcPHDsMKqPgRDk0=
|
||||||
github.com/dweymouth/go-subsonic v0.0.0-20240603150834-605046e7c78a h1:jcHDGzvpsyvEt8i0vqB92jHIfWJUt106QITrPoYjSWo=
|
github.com/dweymouth/go-subsonic v0.0.0-20240603150834-605046e7c78a h1:jcHDGzvpsyvEt8i0vqB92jHIfWJUt106QITrPoYjSWo=
|
||||||
github.com/dweymouth/go-subsonic v0.0.0-20240603150834-605046e7c78a/go.mod h1:OWtcumdQsan8uM6wmx6PqKhldaCthH10CQ+vb+94kzo=
|
github.com/dweymouth/go-subsonic v0.0.0-20240603150834-605046e7c78a/go.mod h1:OWtcumdQsan8uM6wmx6PqKhldaCthH10CQ+vb+94kzo=
|
||||||
|
github.com/dweymouth/go-subsonic v0.0.0-20240716154859-c40a4519e1e0 h1:Zf27NWT1N889/toYK7/x7y7ERw8iJfAHBHGiMyYxaLY=
|
||||||
|
github.com/dweymouth/go-subsonic v0.0.0-20240716154859-c40a4519e1e0/go.mod h1:OWtcumdQsan8uM6wmx6PqKhldaCthH10CQ+vb+94kzo=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ func (m *Controller) connectTracklistActionsWithReplayGainMode(tracklist *widget
|
|||||||
tracklist.OnShare = func(trackID string) {
|
tracklist.OnShare = func(trackID string) {
|
||||||
go m.ShowShareDialog(trackID)
|
go m.ShowShareDialog(trackID)
|
||||||
}
|
}
|
||||||
|
tracklist.OnShowTrackInfo = m.ShowTrackInfoDialog
|
||||||
tracklist.OnPlaySongRadio = func(track *mediaprovider.Track) {
|
tracklist.OnPlaySongRadio = func(track *mediaprovider.Track) {
|
||||||
go func() {
|
go func() {
|
||||||
tracks, err := m.GetSongRadioTracks(track)
|
tracks, err := m.GetSongRadioTracks(track)
|
||||||
@@ -858,6 +859,38 @@ func (c *Controller) ShowAlbumInfoDialog(albumID, albumName string, albumCover i
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ShowTrackInfoDialog(track *mediaprovider.Track) {
|
||||||
|
info := dialogs.NewTrackInfoDialog(track)
|
||||||
|
pop := widget.NewModalPopUp(info, c.MainWindow.Canvas())
|
||||||
|
info.OnDismiss = func() {
|
||||||
|
pop.Hide()
|
||||||
|
c.doModalClosed()
|
||||||
|
}
|
||||||
|
info.OnNavigateToAlbum = func(albumID string) {
|
||||||
|
info.OnDismiss()
|
||||||
|
c.NavigateTo(AlbumRoute(albumID))
|
||||||
|
}
|
||||||
|
info.OnNavigateToArtist = func(artistID string) {
|
||||||
|
info.OnDismiss()
|
||||||
|
c.NavigateTo(ArtistRoute(artistID))
|
||||||
|
}
|
||||||
|
info.OnNavigateToGenre = func(genre string) {
|
||||||
|
info.OnDismiss()
|
||||||
|
c.NavigateTo(GenreRoute(genre))
|
||||||
|
}
|
||||||
|
info.OnCopyFilePath = func() {
|
||||||
|
c.MainWindow.Clipboard().SetContent(track.FilePath)
|
||||||
|
}
|
||||||
|
c.ClosePopUpOnEscape(pop)
|
||||||
|
winSize := c.MainWindow.Canvas().Size()
|
||||||
|
popMin := pop.MinSize()
|
||||||
|
width := fyne.Max(700, fyne.Max(popMin.Width, winSize.Width*0.6))
|
||||||
|
height := fyne.Max(500, fyne.Max(popMin.Height, winSize.Height*0.7))
|
||||||
|
pop.Resize(fyne.NewSize(width, height))
|
||||||
|
c.haveModal = true
|
||||||
|
pop.Show()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Controller) GetSongRadioTracks(sourceTrack *mediaprovider.Track) ([]*mediaprovider.Track, error) {
|
func (c *Controller) GetSongRadioTracks(sourceTrack *mediaprovider.Track) ([]*mediaprovider.Track, error) {
|
||||||
radioTracks, err := c.App.ServerManager.Server.GetSongRadio(sourceTrack.ID, 100)
|
radioTracks, err := c.App.ServerManager.Server.GetSongRadio(sourceTrack.ID, 100)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
package dialogs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/layout"
|
||||||
|
"fyne.io/fyne/v2/theme"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||||
|
"github.com/dweymouth/supersonic/ui/util"
|
||||||
|
"github.com/dweymouth/supersonic/ui/widgets"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TrackInfoDialog struct {
|
||||||
|
widget.BaseWidget
|
||||||
|
|
||||||
|
OnDismiss func()
|
||||||
|
OnNavigateToArtist func(artistID string)
|
||||||
|
OnNavigateToAlbum func(albumID string)
|
||||||
|
OnNavigateToGenre func(genre string)
|
||||||
|
OnCopyFilePath func()
|
||||||
|
|
||||||
|
track *mediaprovider.Track
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTrackInfoDialog(track *mediaprovider.Track) *TrackInfoDialog {
|
||||||
|
t := &TrackInfoDialog{track: track}
|
||||||
|
t.ExtendBaseWidget(t)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TrackInfoDialog) CreateRenderer() fyne.WidgetRenderer {
|
||||||
|
c := container.New(layout.NewFormLayout())
|
||||||
|
|
||||||
|
addFormRow(c, "Title", t.track.Title)
|
||||||
|
|
||||||
|
c.Add(newFormText("Artist", true))
|
||||||
|
artists := widgets.NewMultiHyperlink()
|
||||||
|
artists.BuildSegments(t.track.ArtistNames, t.track.ArtistIDs)
|
||||||
|
artists.OnTapped = func(id string) {
|
||||||
|
if t.OnNavigateToArtist != nil {
|
||||||
|
t.OnNavigateToArtist(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Add(artists)
|
||||||
|
|
||||||
|
c.Add(newFormText("Album", true))
|
||||||
|
album := widget.NewHyperlink(t.track.Album, nil)
|
||||||
|
album.OnTapped = func() {
|
||||||
|
if t.OnNavigateToAlbum != nil {
|
||||||
|
t.OnNavigateToAlbum(t.track.AlbumID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Add(album)
|
||||||
|
|
||||||
|
if len(t.track.Genres) > 0 {
|
||||||
|
c.Add(newFormText("Genres", true))
|
||||||
|
genres := widgets.NewMultiHyperlink()
|
||||||
|
genres.BuildSegments(t.track.Genres, t.track.Genres)
|
||||||
|
genres.OnTapped = func(g string) {
|
||||||
|
if t.OnNavigateToGenre != nil {
|
||||||
|
t.OnNavigateToGenre(g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Add(genres)
|
||||||
|
}
|
||||||
|
|
||||||
|
addFormRow(c, "Duration", util.SecondsToTimeString(float64(t.track.Duration)))
|
||||||
|
|
||||||
|
copyBtn := widgets.NewIconButton(theme.ContentCopyIcon(), func() {
|
||||||
|
if t.OnCopyFilePath != nil {
|
||||||
|
t.OnCopyFilePath()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
copyBtn.IconSize = widgets.IconButtonSizeSmaller
|
||||||
|
btnCtr := container.New(layout.NewCustomPaddedLayout(8, 0, 10, 0),
|
||||||
|
container.NewVBox(copyBtn, layout.NewSpacer()))
|
||||||
|
c.Add(container.NewHBox(btnCtr, newFormText("File path", true)))
|
||||||
|
c.Add(newFormText(t.track.FilePath, false))
|
||||||
|
|
||||||
|
addFormRow(c, "Comment", t.track.Comment)
|
||||||
|
addFormRow(c, "Year", strconv.Itoa(t.track.Year))
|
||||||
|
addFormRow(c, "Track number", strconv.Itoa(t.track.TrackNumber))
|
||||||
|
addFormRow(c, "Disc number", strconv.Itoa(t.track.DiscNumber))
|
||||||
|
|
||||||
|
if t.track.BPM > 0 {
|
||||||
|
addFormRow(c, "BPM", strconv.Itoa(t.track.BPM))
|
||||||
|
}
|
||||||
|
|
||||||
|
addFormRow(c, "Content type", t.track.ContentType)
|
||||||
|
addFormRow(c, "Bit rate", fmt.Sprintf("%d kbps", t.track.BitRate))
|
||||||
|
addFormRow(c, "File size", util.BytesToSizeString(t.track.Size))
|
||||||
|
addFormRow(c, "Play count", strconv.Itoa(t.track.PlayCount))
|
||||||
|
|
||||||
|
if !t.track.LastPlayed.IsZero() {
|
||||||
|
addFormRow(c, "Last played", t.track.LastPlayed.Format(time.RFC1123))
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.track.ReplayGain.TrackPeak > 0 {
|
||||||
|
addFormRow(c, "Track gain", fmt.Sprintf("%0.2f dB", t.track.ReplayGain.TrackGain))
|
||||||
|
addFormRow(c, "Track peak", fmt.Sprintf("%0.6f", t.track.ReplayGain.TrackPeak))
|
||||||
|
}
|
||||||
|
if t.track.ReplayGain.AlbumPeak > 0 {
|
||||||
|
addFormRow(c, "Album gain", fmt.Sprintf("%0.2f dB", t.track.ReplayGain.AlbumGain))
|
||||||
|
addFormRow(c, "Album peak", fmt.Sprintf("%0.6f", t.track.ReplayGain.AlbumPeak))
|
||||||
|
}
|
||||||
|
|
||||||
|
title := widget.NewRichTextWithText("Track Info")
|
||||||
|
title.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = true
|
||||||
|
dismissBtn := widget.NewButton("Close", func() {
|
||||||
|
if t.OnDismiss != nil {
|
||||||
|
t.OnDismiss()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return widget.NewSimpleRenderer(
|
||||||
|
container.NewBorder(
|
||||||
|
/*top*/ container.NewHBox(layout.NewSpacer(), title, layout.NewSpacer()),
|
||||||
|
/*bottom*/ container.NewVBox(
|
||||||
|
widget.NewSeparator(),
|
||||||
|
container.NewHBox(layout.NewSpacer(), dismissBtn),
|
||||||
|
),
|
||||||
|
/*left/right*/ nil, nil,
|
||||||
|
/*center*/ container.New(layout.NewCustomPaddedLayout(10, 10, 15, 15),
|
||||||
|
container.NewScroll(c)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFormRow(c *fyne.Container, left, right string) {
|
||||||
|
if right == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Add(newFormText(left, true))
|
||||||
|
c.Add(newFormText(right, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFormText(text string, leftCol bool) *widget.RichText {
|
||||||
|
alignment := fyne.TextAlignLeading
|
||||||
|
if leftCol {
|
||||||
|
alignment = fyne.TextAlignTrailing
|
||||||
|
}
|
||||||
|
rt := widget.NewRichText(
|
||||||
|
&widget.TextSegment{
|
||||||
|
Text: text,
|
||||||
|
Style: widget.RichTextStyle{
|
||||||
|
TextStyle: fyne.TextStyle{Bold: leftCol},
|
||||||
|
Alignment: alignment,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if !leftCol {
|
||||||
|
rt.Wrapping = fyne.TextWrapWord
|
||||||
|
}
|
||||||
|
return rt
|
||||||
|
}
|
||||||
+10
-1
@@ -78,6 +78,7 @@ type Tracklist struct {
|
|||||||
OnShare func(trackID string)
|
OnShare func(trackID string)
|
||||||
OnPlaySongRadio func(track *mediaprovider.Track)
|
OnPlaySongRadio func(track *mediaprovider.Track)
|
||||||
OnReorderTracks func(trackIDs []string, insertPos int)
|
OnReorderTracks func(trackIDs []string, insertPos int)
|
||||||
|
OnShowTrackInfo func(track *mediaprovider.Track)
|
||||||
|
|
||||||
OnShowArtistPage func(artistID string)
|
OnShowArtistPage func(artistID string)
|
||||||
OnShowAlbumPage func(albumID string)
|
OnShowAlbumPage func(albumID string)
|
||||||
@@ -101,6 +102,7 @@ type Tracklist struct {
|
|||||||
ratingSubmenu *fyne.MenuItem
|
ratingSubmenu *fyne.MenuItem
|
||||||
shareMenuItem *fyne.MenuItem
|
shareMenuItem *fyne.MenuItem
|
||||||
songRadioMenuItem *fyne.MenuItem
|
songRadioMenuItem *fyne.MenuItem
|
||||||
|
infoMenuItem *fyne.MenuItem
|
||||||
container *fyne.Container
|
container *fyne.Container
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,6 +561,12 @@ func (t *Tracklist) onShowContextMenu(e *fyne.PointEvent, trackIdx int) {
|
|||||||
t.onDownload(t.selectedTracks(), "Selected tracks")
|
t.onDownload(t.selectedTracks(), "Selected tracks")
|
||||||
})
|
})
|
||||||
download.Icon = theme.DownloadIcon()
|
download.Icon = theme.DownloadIcon()
|
||||||
|
t.infoMenuItem = fyne.NewMenuItem("Show info...", func() {
|
||||||
|
if t.OnShowTrackInfo != nil {
|
||||||
|
t.OnShowTrackInfo(t.selectedTracks()[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.infoMenuItem.Icon = theme.InfoIcon()
|
||||||
favorite := fyne.NewMenuItem("Set favorite", func() {
|
favorite := fyne.NewMenuItem("Set favorite", func() {
|
||||||
t.onSetFavorites(t.selectedTracks(), true, true)
|
t.onSetFavorites(t.selectedTracks(), true, true)
|
||||||
})
|
})
|
||||||
@@ -568,7 +576,7 @@ func (t *Tracklist) onShowContextMenu(e *fyne.PointEvent, trackIdx int) {
|
|||||||
})
|
})
|
||||||
unfavorite.Icon = myTheme.NotFavoriteIcon
|
unfavorite.Icon = myTheme.NotFavoriteIcon
|
||||||
t.ctxMenu.Items = append(t.ctxMenu.Items, fyne.NewMenuItemSeparator(),
|
t.ctxMenu.Items = append(t.ctxMenu.Items, fyne.NewMenuItemSeparator(),
|
||||||
playlist, download)
|
playlist, download, t.infoMenuItem)
|
||||||
t.shareMenuItem = fyne.NewMenuItem("Share...", func() {
|
t.shareMenuItem = fyne.NewMenuItem("Share...", func() {
|
||||||
t.onShare(t.selectedTracks())
|
t.onShare(t.selectedTracks())
|
||||||
})
|
})
|
||||||
@@ -587,6 +595,7 @@ func (t *Tracklist) onShowContextMenu(e *fyne.PointEvent, trackIdx int) {
|
|||||||
}
|
}
|
||||||
t.ratingSubmenu.Disabled = t.Options.DisableRating
|
t.ratingSubmenu.Disabled = t.Options.DisableRating
|
||||||
t.shareMenuItem.Disabled = t.Options.DisableSharing || len(t.selectedTracks()) != 1
|
t.shareMenuItem.Disabled = t.Options.DisableSharing || len(t.selectedTracks()) != 1
|
||||||
|
t.infoMenuItem.Disabled = len(t.selectedTracks()) != 1
|
||||||
widget.ShowPopUpMenuAtPosition(t.ctxMenu, fyne.CurrentApp().Driver().CanvasForObject(t), e.AbsolutePosition)
|
widget.ShowPopUpMenuAtPosition(t.ctxMenu, fyne.CurrentApp().Driver().CanvasForObject(t), e.AbsolutePosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user