option to use blurred cover art for Now Playing page background
This commit is contained in:
+4
-2
@@ -111,7 +111,8 @@ type TracksPageConfig struct {
|
||||
}
|
||||
|
||||
type NowPlayingPageConfig struct {
|
||||
InitialView string
|
||||
InitialView string
|
||||
UseBackgroundImage bool
|
||||
}
|
||||
|
||||
type PlaybackConfig struct {
|
||||
@@ -242,7 +243,8 @@ func DefaultConfig(appVersionTag string) *Config {
|
||||
InitialView: "List",
|
||||
},
|
||||
NowPlayingConfig: NowPlayingPageConfig{
|
||||
InitialView: "Play Queue",
|
||||
InitialView: "Play Queue",
|
||||
UseBackgroundImage: true,
|
||||
},
|
||||
TracksPage: TracksPageConfig{
|
||||
TracklistColumns: []string{"Album", "Time", "Plays"},
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
module github.com/dweymouth/supersonic
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
fyne.io/fyne/v2 v2.6.1
|
||||
github.com/20after4/configdir v0.1.1
|
||||
github.com/Microsoft/go-winio v0.6.2
|
||||
github.com/boxes-ltd/imaging v1.7.1
|
||||
github.com/cenkalti/dominantcolor v1.0.3
|
||||
github.com/charlievieth/strcase v0.0.5
|
||||
github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1
|
||||
@@ -27,7 +28,7 @@ require (
|
||||
golang.org/x/net v0.38.0
|
||||
golang.org/x/sys v0.31.0
|
||||
golang.org/x/term v0.30.0
|
||||
golang.org/x/text v0.23.0
|
||||
golang.org/x/text v0.30.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -64,7 +65,7 @@ require (
|
||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/yuin/goldmark v1.7.8 // indirect
|
||||
golang.org/x/image v0.24.0 // indirect
|
||||
golang.org/x/image v0.32.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/boxes-ltd/imaging v1.7.1 h1:JlfPQkr9sowLuPY24OLNw1Wa0UjriPVo2BN0YyxdCf0=
|
||||
github.com/boxes-ltd/imaging v1.7.1/go.mod h1:m89K2cMB8PXek+mUi/9SAha2A86+ZgfAOKjGypjMuDk=
|
||||
github.com/cenkalti/dominantcolor v1.0.3 h1:Pt0vfRZ8enkZh1n22RvoboA53SMM/v2aEwNQTZKSqww=
|
||||
github.com/cenkalti/dominantcolor v1.0.3/go.mod h1:mGpFMbWUnyXaGN48Zbf9bU9HJP1eCCD7dnsscb4lyR4=
|
||||
github.com/charlievieth/strcase v0.0.5 h1:gV4iXVyD6eI5KdfOV+/vIVCKXZwtCWOmDMcu7Uy00Rs=
|
||||
@@ -142,16 +144,16 @@ github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
||||
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
|
||||
github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
|
||||
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
||||
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
|
||||
golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ=
|
||||
golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -254,7 +254,9 @@
|
||||
"Unable to play artist radio": "Unable to play artist radio",
|
||||
"Unable to play random tracks": "Unable to play random tracks",
|
||||
"URL": "URL",
|
||||
"Use blurred album cover for Now Playing page background": "Use blurred album cover for Now Playing page background",
|
||||
"Use legacy authentication": "Use legacy authentication",
|
||||
"Use waveform seekbar": "Use waveform seekbar",
|
||||
"Username": "Username",
|
||||
"version": "version",
|
||||
"Visualizations": "Visualizations",
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/boxes-ltd/imaging"
|
||||
"github.com/cenkalti/dominantcolor"
|
||||
"github.com/dweymouth/supersonic/backend"
|
||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||
@@ -49,16 +50,18 @@ type NowPlayingPage struct {
|
||||
alreadyLoaded bool
|
||||
|
||||
// widgets for render
|
||||
background *canvas.LinearGradient
|
||||
queueList *widgets.PlayQueueList
|
||||
relatedList *widgets.PlayQueueList
|
||||
lyricsViewer *widgets.LyricsViewer
|
||||
card *widgets.LargeNowPlayingCard
|
||||
statusLabel *widget.Label
|
||||
tabs *container.AppTabs
|
||||
lyricsLoading *widgets.LoadingDots
|
||||
relatedLoading *widgets.LoadingDots
|
||||
container *fyne.Container
|
||||
backgroundImgA *canvas.Image
|
||||
backgroundImgB *canvas.Image
|
||||
backgroundGradient *canvas.LinearGradient
|
||||
queueList *widgets.PlayQueueList
|
||||
relatedList *widgets.PlayQueueList
|
||||
lyricsViewer *widgets.LyricsViewer
|
||||
card *widgets.LargeNowPlayingCard
|
||||
statusLabel *widget.Label
|
||||
tabs *container.AppTabs
|
||||
lyricsLoading *widgets.LoadingDots
|
||||
relatedLoading *widgets.LoadingDots
|
||||
container *fyne.Container
|
||||
|
||||
// cancel funcs for background fetch tasks
|
||||
imageLoadCancel context.CancelFunc
|
||||
@@ -232,7 +235,9 @@ func (a *NowPlayingPage) CreateRenderer() fyne.WidgetRenderer {
|
||||
a.updateRelatedList()
|
||||
}
|
||||
c := theme.Color(myTheme.ColorNamePageBackground)
|
||||
a.background = canvas.NewLinearGradient(c, c, 0)
|
||||
a.backgroundGradient = canvas.NewLinearGradient(c, c, 0)
|
||||
a.backgroundImgA = canvas.NewImageFromImage(nil)
|
||||
a.backgroundImgB = canvas.NewImageFromImage(nil)
|
||||
|
||||
mainContent := container.NewGridWithColumns(2,
|
||||
container.New(paddedLayout, a.card),
|
||||
@@ -240,7 +245,9 @@ func (a *NowPlayingPage) CreateRenderer() fyne.WidgetRenderer {
|
||||
util.AddHeaderBackgroundWithColorName(
|
||||
a.tabs, myTheme.ColorNameNowPlayingPanel)))
|
||||
a.container = container.NewStack(
|
||||
a.background,
|
||||
a.backgroundImgA,
|
||||
a.backgroundImgB,
|
||||
a.backgroundGradient,
|
||||
mainContent,
|
||||
container.NewVBox(
|
||||
layout.NewSpacer(),
|
||||
@@ -318,20 +325,46 @@ func (a *NowPlayingPage) onImageLoaded(img image.Image, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
c := dominantcolor.Find(img)
|
||||
if c == a.background.StartColor {
|
||||
return
|
||||
}
|
||||
|
||||
// Fyne animation starting is currently thread-safe,
|
||||
// despite not being marked as such
|
||||
// TODO: if this changes, use fyne.Do
|
||||
anim := canvas.NewColorRGBAAnimation(
|
||||
a.background.StartColor, c, myTheme.AnimationDurationMedium, func(c color.Color) {
|
||||
a.background.StartColor = c
|
||||
a.background.Refresh()
|
||||
if a.conf.UseBackgroundImage {
|
||||
resized := imaging.Resize(img, 300, 0, imaging.NearestNeighbor)
|
||||
blurred := imaging.Blur(resized, 10.0)
|
||||
fyne.Do(func() {
|
||||
if a.backgroundGradient.StartColor != color.Transparent {
|
||||
a.backgroundGradient.StartColor = color.Transparent
|
||||
a.backgroundGradient.Refresh()
|
||||
}
|
||||
a.backgroundImgA.Hidden = false
|
||||
a.backgroundImgB.Hidden = false
|
||||
a.backgroundImgA.Image = a.backgroundImgB.Image
|
||||
a.backgroundImgB.Image = blurred
|
||||
fyne.NewAnimation(myTheme.AnimationDurationMedium, func(f float32) {
|
||||
a.backgroundImgA.Translucency = float64(f)
|
||||
a.backgroundImgB.Translucency = float64(1 - f)
|
||||
a.backgroundImgA.Refresh()
|
||||
a.backgroundImgB.Refresh()
|
||||
}).Start()
|
||||
})
|
||||
anim.Start()
|
||||
} else {
|
||||
c := dominantcolor.Find(img)
|
||||
if c == a.backgroundGradient.StartColor {
|
||||
return
|
||||
}
|
||||
if !a.backgroundImgA.Hidden {
|
||||
a.backgroundImgA.Hide()
|
||||
}
|
||||
if !a.backgroundImgB.Hidden {
|
||||
a.backgroundImgB.Hide()
|
||||
}
|
||||
// Fyne animation starting is currently thread-safe,
|
||||
// despite not being marked as such
|
||||
// TODO: if this changes, use fyne.Do
|
||||
anim := canvas.NewColorRGBAAnimation(
|
||||
a.backgroundGradient.StartColor, c, myTheme.AnimationDurationMedium, func(c color.Color) {
|
||||
a.backgroundGradient.StartColor = c
|
||||
a.backgroundGradient.Refresh()
|
||||
})
|
||||
anim.Start()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *NowPlayingPage) updateLyrics() {
|
||||
@@ -486,11 +519,11 @@ func (a *NowPlayingPage) UnselectAll() {
|
||||
}
|
||||
|
||||
func (a *NowPlayingPage) Refresh() {
|
||||
if a.background != nil {
|
||||
if a.backgroundGradient != nil {
|
||||
c := theme.Color(myTheme.ColorNamePageBackground)
|
||||
if c != a.background.EndColor {
|
||||
a.background.EndColor = c
|
||||
a.background.Refresh()
|
||||
if c != a.backgroundGradient.EndColor {
|
||||
a.backgroundGradient.EndColor = c
|
||||
a.backgroundGradient.Refresh()
|
||||
}
|
||||
}
|
||||
a.BaseWidget.Refresh()
|
||||
|
||||
@@ -571,6 +571,8 @@ func (s *SettingsDialog) createAppearanceTab(window fyne.Window) *container.TabI
|
||||
})
|
||||
useWaveformSeekbar.Checked = s.config.Playback.UseWaveformSeekbar
|
||||
|
||||
nowPlayingBackground := widget.NewCheckWithData(lang.L("Use blurred album cover for Now Playing page background"), binding.BindBool(&s.config.NowPlayingConfig.UseBackgroundImage))
|
||||
|
||||
return container.NewTabItem(lang.L("Appearance"), container.NewVBox(
|
||||
util.NewHSpace(0), // insert a theme.Padding amount of space at top
|
||||
container.NewBorder(nil, nil, widget.NewLabel(lang.L("Theme")), /*left*/
|
||||
@@ -583,6 +585,7 @@ func (s *SettingsDialog) createAppearanceTab(window fyne.Window) *container.TabI
|
||||
disableDPI,
|
||||
s.newSectionSeparator(),
|
||||
useWaveformSeekbar,
|
||||
nowPlayingBackground,
|
||||
s.newSectionSeparator(),
|
||||
widget.NewRichText(&widget.TextSegment{Text: lang.L("Application font"), Style: util.BoldRichTextStyle}),
|
||||
container.New(layout.NewFormLayout(),
|
||||
|
||||
Reference in New Issue
Block a user