migrate to Fyne 2.6 - WIP - updates for new threading model
This commit is contained in:
@@ -21,7 +21,7 @@ require (
|
||||
github.com/zalando/go-keyring v0.2.6
|
||||
golang.org/x/net v0.25.0
|
||||
golang.org/x/sys v0.26.0
|
||||
golang.org/x/text v0.16.0
|
||||
golang.org/x/text v0.21.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -40,18 +40,27 @@ require (
|
||||
github.com/go-text/render v0.2.0 // indirect
|
||||
github.com/go-text/typesetting v0.2.0 // indirect
|
||||
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||
github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 // indirect
|
||||
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 // indirect
|
||||
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rymdport/portal v0.3.0 // indirect
|
||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
||||
<<<<<<< HEAD
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
=======
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
>>>>>>> 287a268 (migrate to Fyne 2.6 - WIP - updates for new threading model)
|
||||
github.com/yuin/goldmark v1.7.1 // indirect
|
||||
golang.org/x/image v0.18.0 // indirect
|
||||
golang.org/x/image v0.23.0 // indirect
|
||||
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
>>>>>>> 287a268 (migrate to Fyne 2.6 - WIP - updates for new threading model)
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace fyne.io/fyne/v2 v2.5.3 => github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20241221194247-e7d4715906d9
|
||||
replace fyne.io/fyne/v2 v2.5.3 => github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20250112004415-0d8cb89956b0
|
||||
|
||||
@@ -81,8 +81,8 @@ github.com/dweymouth/fyne-lyrics v0.0.0-20240528234907-15eee7ce5e64 h1:RUIrnGY03
|
||||
github.com/dweymouth/fyne-lyrics v0.0.0-20240528234907-15eee7ce5e64/go.mod h1:3YrjFDHMlhCsSZ/OvmJCxWm9QHSgOVWZBxnraZz9Z7c=
|
||||
github.com/dweymouth/fyne-tooltip v0.2.0 h1:6Zy3gryctuPoQfYf8Xp3tjenioebMt11NBGW/QXIvxE=
|
||||
github.com/dweymouth/fyne-tooltip v0.2.0/go.mod h1:zEgy7p9tSVIuy2GufFbOCoK3Q04zhyDPOotlU4G3Ma4=
|
||||
github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20241221194247-e7d4715906d9 h1:JZHVRX7qExB7H9ZZ5L9HO1qKl1Ej9M75pwVUa69Qdrg=
|
||||
github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20241221194247-e7d4715906d9/go.mod h1:0GOXKqyvNwk3DLmsFu9v0oYM0ZcD1ysGnlHCerKoAmo=
|
||||
github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20250112004415-0d8cb89956b0 h1:YoIIY7CcgUdlfaagC6C8ADwXu9BDJNbhJYQi6g/7xZY=
|
||||
github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20250112004415-0d8cb89956b0/go.mod h1:6/uEYg4FEhspAcWgsokutm9wFMHDNSYuEHCKTYWSho8=
|
||||
github.com/dweymouth/go-jellyfin v0.0.0-20240517151952-5ceca61cb645 h1:KzqSaQwG3HsTZQlEtkp0BeUy9vmYZ0rq0B15qIPSiBs=
|
||||
github.com/dweymouth/go-jellyfin v0.0.0-20240517151952-5ceca61cb645/go.mod h1:fcUagHBaQnt06GmBAllNE0J4O/7064zXRWdqnTTtVjI=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@@ -221,8 +221,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 h1:Po+wkNdMmN+Zj1tDsJQy7mJlPlwGNQd9JZoPjObagf8=
|
||||
github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49/go.mod h1:YiutDnxPRLk5DLUFj6Rw4pRBBURZY07GFr54NdV9mQg=
|
||||
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3mfHIWLZtDcSaGAe2I4PW9B/P5nMkRSwCAc=
|
||||
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
@@ -253,6 +253,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
@@ -308,8 +310,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/supersonic-app/go-mpv v0.1.0 h1:U+cCnLQxmpqx5mY6nMlC0J4uIdCCXUbAjpjS04XkFu8=
|
||||
github.com/supersonic-app/go-mpv v0.1.0/go.mod h1:1bQz6kBQumJopXEbkiqoLxIXLy7F7yWFBvknvpAtIC0=
|
||||
@@ -362,8 +364,8 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
|
||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
||||
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -506,8 +508,8 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -522,8 +524,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -81,9 +81,9 @@ func main() {
|
||||
go func() {
|
||||
defaultServer := myApp.ServerManager.GetDefaultServer()
|
||||
if defaultServer == nil {
|
||||
mainWindow.Controller.PromptForFirstServer()
|
||||
fyne.Do(mainWindow.Controller.PromptForFirstServer)
|
||||
} else {
|
||||
mainWindow.Controller.DoConnectToServerWorkflow(defaultServer)
|
||||
fyne.Do(func() { mainWindow.Controller.DoConnectToServerWorkflow(defaultServer) })
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -98,12 +98,12 @@ func main() {
|
||||
// slightly hacky workaround for https://github.com/fyne-io/fyne/issues/4964
|
||||
_, isWayland := ctx.(*driver.WaylandWindowContext)
|
||||
if runtime.GOOS == "linux" && !isWayland {
|
||||
s := mainWindow.DesiredSize()
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
s := mainWindow.DesiredSize()
|
||||
mainWindow.Window.Resize(s.Subtract(fyne.NewSize(4, 0)))
|
||||
fyne.Do(func() { mainWindow.Window.Resize(s.Subtract(fyne.NewSize(4, 0))) })
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
mainWindow.Window.Resize(s) // back to desired size
|
||||
fyne.Do(func() { mainWindow.Window.Resize(s) }) // back to desired size
|
||||
}()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"All Tracks": "All Tracks",
|
||||
"Alt. URL": "Alt. URL",
|
||||
"An error occurred adding tracks to the playlist": "An error occurred adding tracks to the playlist",
|
||||
"An error occurred updating the playlist": "An error occurred updating the playlist",
|
||||
"Are you sure you want to delete the server": "Are you sure you want to delete the server",
|
||||
"Artist": "Artist",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
|
||||
@@ -316,8 +316,10 @@ func (a *AlbumPageHeader) Update(album *mediaprovider.AlbumWithTracks, im *backe
|
||||
|
||||
go func() {
|
||||
if cover, err := im.GetCoverThumbnail(album.CoverArtID); err == nil {
|
||||
a.cover.SetImage(cover, true)
|
||||
a.cover.Refresh()
|
||||
fyne.Do(func() {
|
||||
a.cover.SetImage(cover, true)
|
||||
a.cover.Refresh()
|
||||
})
|
||||
} else {
|
||||
log.Printf("error fetching cover: %v", err)
|
||||
}
|
||||
|
||||
+61
-40
@@ -174,8 +174,10 @@ func (a *ArtistPage) playArtistRadio() {
|
||||
go func() {
|
||||
err := a.pm.PlaySimilarSongs(a.artistID)
|
||||
if err != nil {
|
||||
log.Println("error playing similar songs: %v", err)
|
||||
a.contr.ToastProvider.ShowErrorToast(lang.L("Unable to play artist radio"))
|
||||
fyne.Do(func() {
|
||||
log.Println("error playing similar songs: %v", err)
|
||||
a.contr.ToastProvider.ShowErrorToast(lang.L("Unable to play artist radio"))
|
||||
})
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -246,19 +248,23 @@ func (a *ArtistPage) load() {
|
||||
if a.disposed {
|
||||
return
|
||||
}
|
||||
a.artistInfo = artist
|
||||
a.header.Update(artist, a.im)
|
||||
if a.activeView == 0 {
|
||||
a.showAlbumGrid(false /*reSort*/)
|
||||
} else {
|
||||
a.showTopTracks()
|
||||
}
|
||||
|
||||
fyne.Do(func() {
|
||||
a.artistInfo = artist
|
||||
a.header.Update(artist, a.im)
|
||||
if a.activeView == 0 {
|
||||
a.showAlbumGrid(false /*reSort*/)
|
||||
} else {
|
||||
a.showTopTracks()
|
||||
}
|
||||
})
|
||||
|
||||
info, err := a.mp.GetArtistInfo(a.artistID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to get artist info: %s", err.Error())
|
||||
}
|
||||
if !a.disposed {
|
||||
a.header.UpdateInfo(info)
|
||||
fyne.Do(func() { a.header.UpdateInfo(info) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +293,15 @@ func (a *ArtistPage) showAlbumGrid(reSort bool) {
|
||||
a.container.Objects[0].Refresh()
|
||||
}
|
||||
|
||||
// should be called asynchronously
|
||||
func (a *ArtistPage) showTopTracks() {
|
||||
updated := false
|
||||
updatePage := func() {
|
||||
a.sortButton.Hide()
|
||||
a.container.Objects[0].(*fyne.Container).Objects[0] = a.tracklistCtr
|
||||
a.container.Objects[0].Refresh()
|
||||
}
|
||||
|
||||
if a.tracklistCtr == nil {
|
||||
if a.artistInfo == nil {
|
||||
// page not loaded yet or invalid artist
|
||||
@@ -302,33 +316,38 @@ func (a *ArtistPage) showTopTracks() {
|
||||
if a.disposed {
|
||||
return
|
||||
}
|
||||
var tl *widgets.Tracklist
|
||||
if t := a.pool.Obtain(util.WidgetTypeTracklist); t != nil {
|
||||
tl = t.(*widgets.Tracklist)
|
||||
tl.Reset()
|
||||
tl.SetTracks(ts)
|
||||
} else {
|
||||
tl = widgets.NewTracklist(ts, a.im, false)
|
||||
}
|
||||
tl.Options = widgets.TracklistOptions{AutoNumber: true}
|
||||
_, canRate := a.mp.(mediaprovider.SupportsRating)
|
||||
_, canShare := a.mp.(mediaprovider.SupportsSharing)
|
||||
tl.Options.DisableRating = !canRate
|
||||
tl.Options.DisableSharing = !canShare
|
||||
tl.SetVisibleColumns(a.cfg.TracklistColumns)
|
||||
tl.SetSorting(a.trackSort)
|
||||
tl.OnVisibleColumnsChanged = func(cols []string) {
|
||||
a.cfg.TracklistColumns = cols
|
||||
}
|
||||
tl.SetNowPlaying(a.nowPlayingID)
|
||||
a.contr.ConnectTracklistActions(tl)
|
||||
a.tracklistCtr = container.New(
|
||||
&layout.CustomPaddedLayout{LeftPadding: 15, RightPadding: 15, BottomPadding: 10},
|
||||
tl)
|
||||
updated = true // mark that updatePage() will be called here
|
||||
fyne.Do(func() {
|
||||
var tl *widgets.Tracklist
|
||||
if t := a.pool.Obtain(util.WidgetTypeTracklist); t != nil {
|
||||
tl = t.(*widgets.Tracklist)
|
||||
tl.Reset()
|
||||
tl.SetTracks(ts)
|
||||
} else {
|
||||
tl = widgets.NewTracklist(ts, a.im, false)
|
||||
}
|
||||
tl.Options = widgets.TracklistOptions{AutoNumber: true}
|
||||
_, canRate := a.mp.(mediaprovider.SupportsRating)
|
||||
_, canShare := a.mp.(mediaprovider.SupportsSharing)
|
||||
tl.Options.DisableRating = !canRate
|
||||
tl.Options.DisableSharing = !canShare
|
||||
tl.SetVisibleColumns(a.cfg.TracklistColumns)
|
||||
tl.SetSorting(a.trackSort)
|
||||
tl.OnVisibleColumnsChanged = func(cols []string) {
|
||||
a.cfg.TracklistColumns = cols
|
||||
}
|
||||
tl.SetNowPlaying(a.nowPlayingID)
|
||||
a.contr.ConnectTracklistActions(tl)
|
||||
a.tracklistCtr = container.New(
|
||||
&layout.CustomPaddedLayout{LeftPadding: 15, RightPadding: 15, BottomPadding: 10},
|
||||
tl)
|
||||
updatePage()
|
||||
})
|
||||
}
|
||||
|
||||
if !updated {
|
||||
fyne.Do(updatePage)
|
||||
}
|
||||
a.sortButton.Hide()
|
||||
a.container.Objects[0].(*fyne.Container).Objects[0] = a.tracklistCtr
|
||||
a.container.Objects[0].Refresh()
|
||||
}
|
||||
|
||||
func (a *ArtistPage) onViewChange(num int) {
|
||||
@@ -512,10 +531,12 @@ func (a *ArtistPageHeader) UpdateInfo(info *mediaprovider.ArtistInfo) {
|
||||
if a.artistImage.HaveImage() {
|
||||
_ = a.artistPage.im.RefreshCachedArtistImageIfExpired(a.artistID, info.ImageURL)
|
||||
} else {
|
||||
im, err := a.artistPage.im.FetchAndCacheArtistImage(a.artistID, info.ImageURL)
|
||||
if err == nil {
|
||||
a.artistImage.SetImage(im, true /*tappable*/)
|
||||
}
|
||||
go func() {
|
||||
im, err := a.artistPage.im.FetchAndCacheArtistImage(a.artistID, info.ImageURL)
|
||||
if err == nil {
|
||||
fyne.Do(func() { a.artistImage.SetImage(im, true /*tappable*/) })
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,22 +207,24 @@ func (a *FavoritesPage) Reload() {
|
||||
if a.disposed {
|
||||
return
|
||||
}
|
||||
if tr := a.tracklistOrNil(); tr != nil {
|
||||
// refresh favorite songs view
|
||||
tr.SetTracks(starred.Tracks)
|
||||
if a.toggleBtns.ActivatedButtonIndex() == 2 {
|
||||
// favorite songs view is visible
|
||||
tr.Refresh()
|
||||
fyne.Do(func() {
|
||||
if tr := a.tracklistOrNil(); tr != nil {
|
||||
// refresh favorite songs view
|
||||
tr.SetTracks(starred.Tracks)
|
||||
if a.toggleBtns.ActivatedButtonIndex() == 2 {
|
||||
// favorite songs view is visible
|
||||
tr.Refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
if a.artistGrid != nil {
|
||||
// refresh favorite artists view
|
||||
a.artistGrid.ResetFixed(buildArtistGridViewModel(starred.Artists))
|
||||
if a.toggleBtns.ActivatedButtonIndex() == 1 {
|
||||
// favorite artists view is visible
|
||||
a.artistGrid.Refresh()
|
||||
if a.artistGrid != nil {
|
||||
// refresh favorite artists view
|
||||
a.artistGrid.ResetFixed(buildArtistGridViewModel(starred.Artists))
|
||||
if a.toggleBtns.ActivatedButtonIndex() == 1 {
|
||||
// favorite artists view is visible
|
||||
a.artistGrid.Refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}()
|
||||
}
|
||||
}
|
||||
@@ -354,23 +356,25 @@ func (a *FavoritesPage) onShowFavoriteArtists() {
|
||||
if a.disposed {
|
||||
return
|
||||
}
|
||||
model := buildArtistGridViewModel(fav.Artists)
|
||||
if g := a.pool.Obtain(util.WidgetTypeGridView); g != nil {
|
||||
a.artistGrid = g.(*widgets.GridView)
|
||||
a.artistGrid.Placeholder = myTheme.ArtistIcon
|
||||
a.artistGrid.ResetFixed(model)
|
||||
} else {
|
||||
a.artistGrid = widgets.NewFixedGridView(model, a.im, myTheme.ArtistIcon)
|
||||
}
|
||||
canShareArtists := false
|
||||
if r, canShare := a.mp.(mediaprovider.SupportsSharing); canShare {
|
||||
canShareArtists = r.CanShareArtists()
|
||||
}
|
||||
a.artistGrid.DisableSharing = !canShareArtists
|
||||
a.contr.ConnectArtistGridActions(a.artistGrid)
|
||||
a.container.Objects[0] = a.artistGrid
|
||||
a.Refresh()
|
||||
a.pendingViewSwitch = false
|
||||
fyne.Do(func() {
|
||||
model := buildArtistGridViewModel(fav.Artists)
|
||||
if g := a.pool.Obtain(util.WidgetTypeGridView); g != nil {
|
||||
a.artistGrid = g.(*widgets.GridView)
|
||||
a.artistGrid.Placeholder = myTheme.ArtistIcon
|
||||
a.artistGrid.ResetFixed(model)
|
||||
} else {
|
||||
a.artistGrid = widgets.NewFixedGridView(model, a.im, myTheme.ArtistIcon)
|
||||
}
|
||||
canShareArtists := false
|
||||
if r, canShare := a.mp.(mediaprovider.SupportsSharing); canShare {
|
||||
canShareArtists = r.CanShareArtists()
|
||||
}
|
||||
a.artistGrid.DisableSharing = !canShareArtists
|
||||
a.contr.ConnectArtistGridActions(a.artistGrid)
|
||||
a.container.Objects[0] = a.artistGrid
|
||||
a.Refresh()
|
||||
a.pendingViewSwitch = false
|
||||
})
|
||||
}()
|
||||
} else {
|
||||
a.container.Objects[0] = a.artistGrid
|
||||
@@ -419,32 +423,34 @@ func (a *FavoritesPage) onShowFavoriteSongs() {
|
||||
if a.disposed {
|
||||
return
|
||||
}
|
||||
var tracklist *widgets.Tracklist
|
||||
if tl := a.pool.Obtain(util.WidgetTypeTracklist); tl != nil {
|
||||
tracklist = tl.(*widgets.Tracklist)
|
||||
tracklist.Reset()
|
||||
tracklist.SetTracks(fav.Tracks)
|
||||
} else {
|
||||
tracklist = widgets.NewTracklist(fav.Tracks, a.im, false)
|
||||
}
|
||||
tracklist.Options = widgets.TracklistOptions{AutoNumber: true}
|
||||
_, canRate := a.mp.(mediaprovider.SupportsRating)
|
||||
_, canShare := a.mp.(mediaprovider.SupportsSharing)
|
||||
tracklist.Options.DisableRating = !canRate
|
||||
tracklist.Options.DisableSharing = !canShare
|
||||
tracklist.SetVisibleColumns(a.cfg.TracklistColumns)
|
||||
tracklist.SetSorting(a.trackSort)
|
||||
tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
||||
a.cfg.TracklistColumns = cols
|
||||
}
|
||||
tracklist.SetNowPlaying(a.nowPlayingID)
|
||||
a.contr.ConnectTracklistActions(tracklist)
|
||||
a.tracklistCtr = container.New(
|
||||
&layout.CustomPaddedLayout{LeftPadding: 15, RightPadding: 15, TopPadding: 5, BottomPadding: 15},
|
||||
tracklist)
|
||||
a.container.Objects[0] = a.tracklistCtr
|
||||
a.Refresh()
|
||||
a.pendingViewSwitch = false
|
||||
fyne.Do(func() {
|
||||
var tracklist *widgets.Tracklist
|
||||
if tl := a.pool.Obtain(util.WidgetTypeTracklist); tl != nil {
|
||||
tracklist = tl.(*widgets.Tracklist)
|
||||
tracklist.Reset()
|
||||
tracklist.SetTracks(fav.Tracks)
|
||||
} else {
|
||||
tracklist = widgets.NewTracklist(fav.Tracks, a.im, false)
|
||||
}
|
||||
tracklist.Options = widgets.TracklistOptions{AutoNumber: true}
|
||||
_, canRate := a.mp.(mediaprovider.SupportsRating)
|
||||
_, canShare := a.mp.(mediaprovider.SupportsSharing)
|
||||
tracklist.Options.DisableRating = !canRate
|
||||
tracklist.Options.DisableSharing = !canShare
|
||||
tracklist.SetVisibleColumns(a.cfg.TracklistColumns)
|
||||
tracklist.SetSorting(a.trackSort)
|
||||
tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
||||
a.cfg.TracklistColumns = cols
|
||||
}
|
||||
tracklist.SetNowPlaying(a.nowPlayingID)
|
||||
a.contr.ConnectTracklistActions(tracklist)
|
||||
a.tracklistCtr = container.New(
|
||||
&layout.CustomPaddedLayout{LeftPadding: 15, RightPadding: 15, TopPadding: 5, BottomPadding: 15},
|
||||
tracklist)
|
||||
a.container.Objects[0] = a.tracklistCtr
|
||||
a.Refresh()
|
||||
a.pendingViewSwitch = false
|
||||
})
|
||||
}()
|
||||
} else {
|
||||
a.container.Objects[0] = a.tracklistCtr
|
||||
|
||||
@@ -65,7 +65,9 @@ func (g *genrePageAdapter) ActionButton() *widget.Button {
|
||||
err := g.pm.PlayRandomSongs(g.genre)
|
||||
if err != nil {
|
||||
log.Println("error playing random tracks: %v", err)
|
||||
g.contr.ToastProvider.ShowErrorToast(lang.L("Unable to play random tracks"))
|
||||
fyne.Do(func() {
|
||||
g.contr.ToastProvider.ShowErrorToast(lang.L("Unable to play random tracks"))
|
||||
})
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -193,20 +193,28 @@ func (a *PlaylistPage) doSetNewTrackOrder(ids []string, newPos int) {
|
||||
}
|
||||
}
|
||||
newTracks := sharedutil.ReorderItems(a.tracks, idxs, newPos)
|
||||
ids = sharedutil.TracksToIDs(newTracks)
|
||||
|
||||
// we can't block the UI waiting for the server so assume it will succeed
|
||||
go func() {
|
||||
ids = sharedutil.TracksToIDs(newTracks)
|
||||
if err := a.sm.Server.ReplacePlaylistTracks(a.playlistID, ids); err != nil {
|
||||
log.Printf("error updating playlist: %s", err.Error())
|
||||
fyne.Do(func() {
|
||||
a.contr.ToastProvider.ShowErrorToast(
|
||||
lang.L("An error occurred updating the playlist"),
|
||||
)
|
||||
})
|
||||
} else {
|
||||
fyne.Do(func() {
|
||||
renumberTracks(newTracks)
|
||||
// force-switch back to unsorted view to show new track order
|
||||
a.tracklist.SetSorting(widgets.TracklistSort{})
|
||||
a.tracklist.SetTracks(newTracks)
|
||||
a.tracklist.UnselectAll()
|
||||
a.tracks = newTracks
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
renumberTracks(newTracks)
|
||||
// force-switch back to unsorted view to show new track order
|
||||
a.tracklist.SetSorting(widgets.TracklistSort{})
|
||||
a.tracklist.SetTracks(newTracks)
|
||||
a.tracklist.UnselectAll()
|
||||
a.tracks = newTracks
|
||||
}
|
||||
|
||||
func (a *PlaylistPage) onRemoveSelectedFromPlaylist() {
|
||||
|
||||
@@ -150,7 +150,9 @@ func (a *PlaylistsPage) createGridView(playlists []*mediaprovider.Playlist) {
|
||||
log.Printf("error loading playlist: %s", err.Error())
|
||||
return
|
||||
}
|
||||
a.contr.DoAddTracksToPlaylistWorkflow(sharedutil.TracksToIDs(pl.Tracks))
|
||||
fyne.Do(func() {
|
||||
a.contr.DoAddTracksToPlaylistWorkflow(sharedutil.TracksToIDs(pl.Tracks))
|
||||
})
|
||||
}()
|
||||
}
|
||||
a.gridView.OnDownload = func(id string) {
|
||||
@@ -160,7 +162,7 @@ func (a *PlaylistsPage) createGridView(playlists []*mediaprovider.Playlist) {
|
||||
log.Printf("error loading playlist: %s", err.Error())
|
||||
return
|
||||
}
|
||||
a.contr.ShowDownloadDialog(pl.Tracks, pl.Name)
|
||||
fyne.Do(func() { a.contr.ShowDownloadDialog(pl.Tracks, pl.Name) })
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +211,9 @@ func (t *TracksPage) playRandomSongs() {
|
||||
err := t.contr.App.PlaybackManager.PlayRandomSongs("")
|
||||
if err != nil {
|
||||
log.Println("error playing random tracks: %v", err)
|
||||
t.contr.ToastProvider.ShowErrorToast(lang.L("Unable to play random tracks"))
|
||||
fyne.Do(func() {
|
||||
t.contr.ToastProvider.ShowErrorToast(lang.L("Unable to play random tracks"))
|
||||
})
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
+72
-62
@@ -88,48 +88,7 @@ func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string,
|
||||
|
||||
m.BottomPanel = NewBottomPanel(app.PlaybackManager, app.ImageManager, m.Controller)
|
||||
app.PlaybackManager.OnSongChange(func(item mediaprovider.MediaItem, _ *mediaprovider.Track) {
|
||||
if item == nil {
|
||||
m.Window.SetTitle(displayAppName)
|
||||
return
|
||||
}
|
||||
meta := item.Metadata()
|
||||
artistDisp := ""
|
||||
if tr, ok := item.(*mediaprovider.Track); ok {
|
||||
artistDisp = " – " + strings.Join(tr.ArtistNames, ", ")
|
||||
}
|
||||
m.Window.SetTitle(fmt.Sprintf("%s%s · %s", meta.Name, artistDisp, displayAppName))
|
||||
if m.App.Config.Application.ShowTrackChangeNotification {
|
||||
if runtime.GOOS == "linux" {
|
||||
if notifySend, err := exec.LookPath("notify-send"); err == nil {
|
||||
go func() {
|
||||
args := []string{
|
||||
"--app-name", "supersonic",
|
||||
"--urgency", "low",
|
||||
"--expire-time", "10000",
|
||||
// replace previous notification
|
||||
"--hint", "string:x-canonical-private-synchronous:supersonic-track",
|
||||
meta.Name, strings.TrimPrefix(artistDisp, " – "),
|
||||
}
|
||||
|
||||
app.ImageManager.GetCoverThumbnail(meta.CoverArtID)
|
||||
if path, err := app.ImageManager.GetCoverArtPath(meta.CoverArtID); err == nil {
|
||||
args = append([]string{"--icon", path}, args...)
|
||||
}
|
||||
|
||||
if out, err := exec.Command(notifySend, args...).CombinedOutput(); err != nil {
|
||||
log.Printf("notify-send error: %s %s", strings.TrimSpace(string(out)), err)
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Once Fyne issue #2935 is resolved, show album cover
|
||||
fyne.CurrentApp().SendNotification(&fyne.Notification{
|
||||
Title: meta.Name,
|
||||
Content: strings.TrimPrefix(artistDisp, " – "),
|
||||
})
|
||||
}
|
||||
fyne.Do(func() { m.UpdateOnTrackChange(item) })
|
||||
})
|
||||
app.ServerManager.OnServerConnected(func() {
|
||||
go m.RunOnServerConnectedTasks(app, displayAppName)
|
||||
@@ -200,6 +159,51 @@ func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string,
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MainWindow) UpdateOnTrackChange(item mediaprovider.MediaItem) {
|
||||
if item == nil {
|
||||
m.Window.SetTitle(res.DisplayName)
|
||||
return
|
||||
}
|
||||
meta := item.Metadata()
|
||||
artistDisp := ""
|
||||
if tr, ok := item.(*mediaprovider.Track); ok {
|
||||
artistDisp = " – " + strings.Join(tr.ArtistNames, ", ")
|
||||
}
|
||||
m.Window.SetTitle(fmt.Sprintf("%s%s · %s", meta.Name, artistDisp, res.DisplayName))
|
||||
if m.App.Config.Application.ShowTrackChangeNotification {
|
||||
if runtime.GOOS == "linux" {
|
||||
if notifySend, err := exec.LookPath("notify-send"); err == nil {
|
||||
go func() {
|
||||
args := []string{
|
||||
"--app-name", "supersonic",
|
||||
"--urgency", "low",
|
||||
"--expire-time", "10000",
|
||||
// replace previous notification
|
||||
"--hint", "string:x-canonical-private-synchronous:supersonic-track",
|
||||
meta.Name, strings.TrimPrefix(artistDisp, " – "),
|
||||
}
|
||||
|
||||
app.ImageManager.GetCoverThumbnail(meta.CoverArtID)
|
||||
if path, err := app.ImageManager.GetCoverArtPath(meta.CoverArtID); err == nil {
|
||||
args = append([]string{"--icon", path}, args...)
|
||||
}
|
||||
|
||||
if out, err := exec.Command(notifySend, args...).CombinedOutput(); err != nil {
|
||||
log.Printf("notify-send error: %s %s", strings.TrimSpace(string(out)), err)
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Once Fyne issue #2935 is resolved, show album cover
|
||||
fyne.CurrentApp().SendNotification(&fyne.Notification{
|
||||
Title: meta.Name,
|
||||
Content: artistDisp,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MainWindow) DesiredSize() fyne.Size {
|
||||
w := float32(m.App.Config.Application.WindowWidth)
|
||||
if w <= 1 {
|
||||
@@ -229,10 +233,6 @@ func (m *MainWindow) StartupPage() controller.Route {
|
||||
|
||||
func (m *MainWindow) RunOnServerConnectedTasks(app *backend.App, displayAppName string) {
|
||||
time.Sleep(1 * time.Millisecond) // ensure this runs after sync tasks
|
||||
m.BrowsingPane.EnableNavigationButtons()
|
||||
m.Router.NavigateTo(m.StartupPage())
|
||||
_, canRate := m.App.ServerManager.Server.(mediaprovider.SupportsRating)
|
||||
m.BottomPanel.NowPlaying.DisableRating = !canRate
|
||||
|
||||
if app.Config.Application.SavePlayQueue {
|
||||
go func() {
|
||||
@@ -242,12 +242,19 @@ func (m *MainWindow) RunOnServerConnectedTasks(app *backend.App, displayAppName
|
||||
}()
|
||||
}
|
||||
|
||||
_, supportsRadio := m.App.ServerManager.Server.(mediaprovider.RadioProvider)
|
||||
if supportsRadio {
|
||||
m.radioBtn.Show()
|
||||
} else {
|
||||
m.radioBtn.Hide()
|
||||
}
|
||||
fyne.Do(func() {
|
||||
m.BrowsingPane.EnableNavigationButtons()
|
||||
m.Router.NavigateTo(m.StartupPage())
|
||||
_, canRate := m.App.ServerManager.Server.(mediaprovider.SupportsRating)
|
||||
m.BottomPanel.NowPlaying.DisableRating = !canRate
|
||||
|
||||
_, supportsRadio := m.App.ServerManager.Server.(mediaprovider.RadioProvider)
|
||||
if supportsRadio {
|
||||
m.radioBtn.Show()
|
||||
} else {
|
||||
m.radioBtn.Hide()
|
||||
}
|
||||
})
|
||||
|
||||
m.App.SaveConfigFile()
|
||||
|
||||
@@ -257,17 +264,20 @@ func (m *MainWindow) RunOnServerConnectedTasks(app *backend.App, displayAppName
|
||||
}
|
||||
|
||||
// check if launching new version, else if found available update on startup
|
||||
if l := app.Config.Application.LastLaunchedVersion; app.VersionTag() != l {
|
||||
if !app.IsFirstLaunch() {
|
||||
m.ShowWhatsNewDialog()
|
||||
fyne.Do(func() {
|
||||
if l := app.Config.Application.LastLaunchedVersion; app.VersionTag() != l {
|
||||
if !app.IsFirstLaunch() {
|
||||
m.ShowWhatsNewDialog()
|
||||
}
|
||||
m.App.Config.Application.LastLaunchedVersion = app.VersionTag()
|
||||
} else if t := app.UpdateChecker.VersionTagFound(); t != "" && t != app.Config.Application.LastCheckedVersion {
|
||||
if t != app.VersionTag() {
|
||||
m.ShowNewVersionDialog(displayAppName, t)
|
||||
}
|
||||
m.App.Config.Application.LastCheckedVersion = t
|
||||
}
|
||||
m.App.Config.Application.LastLaunchedVersion = app.VersionTag()
|
||||
} else if t := app.UpdateChecker.VersionTagFound(); t != "" && t != app.Config.Application.LastCheckedVersion {
|
||||
if t != app.VersionTag() {
|
||||
m.ShowNewVersionDialog(displayAppName, t)
|
||||
}
|
||||
m.App.Config.Application.LastCheckedVersion = t
|
||||
}
|
||||
})
|
||||
|
||||
// register callback for the ongoing periodic update check
|
||||
m.App.UpdateChecker.OnUpdatedVersionFound = func() {
|
||||
t := m.App.UpdateChecker.VersionTagFound()
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"image"
|
||||
"log"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
// ThumbnailLoader is a utility type that exposes a single API to load
|
||||
@@ -50,7 +52,7 @@ func (i *ThumbnailLoader) Load(coverID string) {
|
||||
if err != nil {
|
||||
log.Printf("Error loading cover image: %s", err.Error())
|
||||
} else {
|
||||
i.callOnLoaded(img)
|
||||
fyne.Do(func() { i.callOnLoaded(img) })
|
||||
}
|
||||
i.prevLoadCancel() // Done. Release resources associated with un-cancelled ctx
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user