Merge pull request #154 from dweymouth/feature/lighttheme
Add light theme
This commit is contained in:
@@ -87,6 +87,10 @@ type ReplayGainConfig struct {
|
||||
PreventClipping bool
|
||||
}
|
||||
|
||||
type ThemeConfig struct {
|
||||
Appearance string
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Application AppConfig
|
||||
Servers []*ServerConfig
|
||||
@@ -101,6 +105,7 @@ type Config struct {
|
||||
LocalPlayback LocalPlaybackConfig
|
||||
Scrobbling ScrobbleConfig
|
||||
ReplayGain ReplayGainConfig
|
||||
Theme ThemeConfig
|
||||
}
|
||||
|
||||
var SupportedStartupPages = []string{"Albums", "Favorites", "Playlists"}
|
||||
@@ -158,6 +163,9 @@ func DefaultConfig(appVersionTag string) *Config {
|
||||
PreampGainDB: 0.0,
|
||||
PreventClipping: true,
|
||||
},
|
||||
Theme: ThemeConfig{
|
||||
Appearance: "Dark",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"log"
|
||||
"supersonic/backend"
|
||||
"supersonic/ui"
|
||||
"supersonic/ui/theme"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
@@ -27,10 +26,7 @@ func main() {
|
||||
}
|
||||
|
||||
fyneApp := app.New()
|
||||
fyneApp.Settings().SetTheme(&theme.MyTheme{
|
||||
NormalFont: myApp.Config.Application.FontNormalTTF,
|
||||
BoldFont: myApp.Config.Application.FontBoldTTF,
|
||||
})
|
||||
|
||||
w := float32(myApp.Config.Application.WindowWidth)
|
||||
if w <= 1 {
|
||||
w = 1000
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"supersonic/backend"
|
||||
"supersonic/res"
|
||||
"supersonic/sharedutil"
|
||||
"supersonic/ui/controller"
|
||||
"supersonic/ui/layouts"
|
||||
myTheme "supersonic/ui/theme"
|
||||
"supersonic/ui/util"
|
||||
"supersonic/ui/widgets"
|
||||
|
||||
@@ -170,7 +170,7 @@ func NewAlbumPageHeader(page *AlbumPage) *AlbumPageHeader {
|
||||
playButton := widget.NewButtonWithIcon("Play", theme.MediaPlayIcon(), func() {
|
||||
go page.pm.PlayAlbum(page.albumID, 0, false)
|
||||
})
|
||||
shuffleBtn := widget.NewButtonWithIcon(" Shuffle", res.ResShuffleInvertSvg, func() {
|
||||
shuffleBtn := widget.NewButtonWithIcon(" Shuffle", myTheme.ShuffleIcon, func() {
|
||||
page.pm.LoadTracks(page.tracklist.Tracks, false, true)
|
||||
page.pm.PlayFromBeginning()
|
||||
})
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"supersonic/sharedutil"
|
||||
"supersonic/ui/controller"
|
||||
"supersonic/ui/layouts"
|
||||
myTheme "supersonic/ui/theme"
|
||||
"supersonic/ui/util"
|
||||
"supersonic/ui/widgets"
|
||||
|
||||
@@ -266,7 +267,7 @@ func NewArtistPageHeader(page *ArtistPage) *ArtistPageHeader {
|
||||
}
|
||||
a.favoriteBtn = widgets.NewFavoriteButton(func() { go a.toggleFavorited() })
|
||||
a.playBtn = widget.NewButtonWithIcon("Play Discography", theme.MediaPlayIcon(), page.playAllTracks)
|
||||
a.playRadioBtn = widget.NewButtonWithIcon("Play Artist Radio", res.ResShuffleInvertSvg, page.playArtistRadio)
|
||||
a.playRadioBtn = widget.NewButtonWithIcon(" Play Artist Radio", myTheme.ShuffleIcon, page.playArtistRadio)
|
||||
a.biographyDisp.Wrapping = fyne.TextWrapWord
|
||||
a.ExtendBaseWidget(a)
|
||||
a.createContainer()
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
myTheme "supersonic/ui/theme"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
@@ -67,11 +66,8 @@ func NewBrowsingPane(app *backend.App) *BrowsingPane {
|
||||
b.forward = widget.NewButtonWithIcon("", theme.NavigateNextIcon(), b.GoForward)
|
||||
b.reload = widget.NewButtonWithIcon("", theme.ViewRefreshIcon(), b.Reload)
|
||||
b.app.PlaybackManager.OnSongChange(b.onSongChange)
|
||||
b.pageContainer = container.NewMax(
|
||||
canvas.NewRectangle(fyne.CurrentApp().Settings().Theme().Color(
|
||||
myTheme.ColorNamePageBackground, theme.VariantDark,
|
||||
)),
|
||||
layout.NewSpacer())
|
||||
bkgrnd := myTheme.NewThemedRectangle(myTheme.ColorNamePageBackground)
|
||||
b.pageContainer = container.NewMax(bkgrnd, layout.NewSpacer())
|
||||
b.settingsBtn = widget.NewButtonWithIcon("", theme.SettingsIcon(), func() {
|
||||
p := widget.NewPopUpMenu(b.settingsMenu,
|
||||
fyne.CurrentApp().Driver().CanvasForObject(b.settingsBtn))
|
||||
@@ -110,8 +106,8 @@ func (b *BrowsingPane) AddSettingsMenuItem(label string, action func()) {
|
||||
fyne.NewMenuItem(label, action))
|
||||
}
|
||||
|
||||
func (b *BrowsingPane) AddNavigationButton(iconRes fyne.Resource, action func()) {
|
||||
b.navBtnsContainer.Add(widget.NewButtonWithIcon("", iconRes, action))
|
||||
func (b *BrowsingPane) AddNavigationButton(icon fyne.Resource, action func()) {
|
||||
b.navBtnsContainer.Add(widget.NewButtonWithIcon("", icon, action))
|
||||
}
|
||||
|
||||
func (b *BrowsingPane) DisableNavigationButtons() {
|
||||
|
||||
@@ -3,9 +3,9 @@ package browsing
|
||||
import (
|
||||
"log"
|
||||
"supersonic/backend"
|
||||
"supersonic/res"
|
||||
"supersonic/ui/controller"
|
||||
"supersonic/ui/layouts"
|
||||
myTheme "supersonic/ui/theme"
|
||||
"supersonic/ui/util"
|
||||
"supersonic/ui/widgets"
|
||||
"time"
|
||||
@@ -73,9 +73,9 @@ func (a *FavoritesPage) createHeader(activeBtnIdx int, searchText string) {
|
||||
SizeName: theme.SizeNameHeadingText,
|
||||
}
|
||||
a.toggleBtns = widgets.NewToggleButtonGroup(activeBtnIdx,
|
||||
widget.NewButtonWithIcon("", res.ResDiscInvertPng, a.onShowFavoriteAlbums),
|
||||
widget.NewButtonWithIcon("", res.ResPeopleInvertPng, a.onShowFavoriteArtists),
|
||||
widget.NewButtonWithIcon("", res.ResMusicnotesInvertPng, a.onShowFavoriteSongs))
|
||||
widget.NewButtonWithIcon("", myTheme.AlbumIcon, a.onShowFavoriteAlbums),
|
||||
widget.NewButtonWithIcon("", myTheme.ArtistIcon, a.onShowFavoriteArtists),
|
||||
widget.NewButtonWithIcon("", myTheme.TracksIcon, a.onShowFavoriteSongs))
|
||||
a.searcher = widgets.NewSearcher()
|
||||
a.searcher.OnSearched = a.OnSearched
|
||||
a.searcher.Entry.Text = searchText
|
||||
|
||||
@@ -2,8 +2,8 @@ package browsing
|
||||
|
||||
import (
|
||||
"supersonic/backend"
|
||||
"supersonic/res"
|
||||
"supersonic/ui/controller"
|
||||
myTheme "supersonic/ui/theme"
|
||||
"supersonic/ui/util"
|
||||
"supersonic/ui/widgets"
|
||||
|
||||
@@ -50,7 +50,7 @@ func NewGenrePage(genre string, contr *controller.Controller, pm *backend.Playba
|
||||
g.titleDisp.Segments[0].(*widget.TextSegment).Style = widget.RichTextStyle{
|
||||
SizeName: theme.SizeNameHeadingText,
|
||||
}
|
||||
g.playRandom = widget.NewButtonWithIcon("Play random", res.ResShuffleInvertSvg, g.playRandomSongs)
|
||||
g.playRandom = widget.NewButtonWithIcon(" Play random", myTheme.ShuffleIcon, g.playRandomSongs)
|
||||
iter := g.lm.GenreIter(g.genre)
|
||||
g.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), g.im)
|
||||
g.contr.ConnectAlbumGridActions(g.grid)
|
||||
@@ -91,7 +91,7 @@ func restoreGenrePage(saved *savedGenrePage) *GenrePage {
|
||||
g.titleDisp.Segments[0].(*widget.TextSegment).Style = widget.RichTextStyle{
|
||||
SizeName: theme.SizeNameHeadingText,
|
||||
}
|
||||
g.playRandom = widget.NewButtonWithIcon("Play random", res.ResShuffleInvertSvg, g.playRandomSongs)
|
||||
g.playRandom = widget.NewButtonWithIcon(" Play random", myTheme.ShuffleIcon, g.playRandomSongs)
|
||||
g.grid = widgets.NewGridViewFromState(saved.gridState)
|
||||
g.searcher = widgets.NewSearcher()
|
||||
g.searcher.OnSearched = g.OnSearched
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"supersonic/sharedutil"
|
||||
"supersonic/ui/controller"
|
||||
"supersonic/ui/layouts"
|
||||
myTheme "supersonic/ui/theme"
|
||||
"supersonic/ui/util"
|
||||
"supersonic/ui/widgets"
|
||||
|
||||
@@ -205,7 +206,7 @@ func NewPlaylistPageHeader(page *PlaylistPage) *PlaylistPageHeader {
|
||||
page.pm.PlayFromBeginning()
|
||||
})
|
||||
// TODO: find way to pad shuffle svg rather than using a space in the label string
|
||||
shuffleBtn := widget.NewButtonWithIcon(" Shuffle", res.ResShuffleInvertSvg, func() {
|
||||
shuffleBtn := widget.NewButtonWithIcon(" Shuffle", myTheme.ShuffleIcon, func() {
|
||||
page.pm.LoadTracks(page.tracklist.Tracks, false /*append*/, true /*shuffle*/)
|
||||
page.pm.PlayFromBeginning()
|
||||
})
|
||||
|
||||
@@ -2,10 +2,10 @@ package browsing
|
||||
|
||||
import (
|
||||
"supersonic/backend"
|
||||
"supersonic/res"
|
||||
"supersonic/sharedutil"
|
||||
"supersonic/ui/controller"
|
||||
"supersonic/ui/layouts"
|
||||
"supersonic/ui/theme"
|
||||
"supersonic/ui/widgets"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
@@ -56,7 +56,7 @@ func NewTracksPage(contr *controller.Controller, conf *backend.TracksPageConfig,
|
||||
|
||||
t.title = widget.NewRichTextWithText("All Tracks")
|
||||
t.title.Segments[0].(*widget.TextSegment).Style.SizeName = widget.RichTextStyleHeading.SizeName
|
||||
t.playRandom = widget.NewButtonWithIcon("Play random", res.ResShuffleInvertSvg, t.playRandomSongs)
|
||||
t.playRandom = widget.NewButtonWithIcon("Play random", theme.ShuffleIcon, t.playRandomSongs)
|
||||
t.searcher = widgets.NewSearcher()
|
||||
t.searcher.OnSearched = t.OnSearched
|
||||
t.createContainer()
|
||||
|
||||
@@ -339,7 +339,7 @@ func (c *Controller) ShowAboutDialog() {
|
||||
pop.Show()
|
||||
}
|
||||
|
||||
func (c *Controller) ShowSettingsDialog() {
|
||||
func (c *Controller) ShowSettingsDialog(themeUpdateCallbk func()) {
|
||||
devs, err := c.App.Player.ListAudioDevices()
|
||||
if err != nil {
|
||||
log.Printf("error listing audio devices: %v", err)
|
||||
@@ -356,6 +356,7 @@ func (c *Controller) ShowSettingsDialog() {
|
||||
dlg.OnAudioDeviceSettingChanged = func() {
|
||||
c.App.Player.SetAudioDevice(c.App.Config.LocalPlayback.AudioDeviceName)
|
||||
}
|
||||
dlg.OnThemeSettingChanged = themeUpdateCallbk
|
||||
pop := widget.NewModalPopUp(dlg, c.MainWindow.Canvas())
|
||||
dlg.OnDismiss = func() {
|
||||
pop.Hide()
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"supersonic/backend"
|
||||
"supersonic/player"
|
||||
"supersonic/ui/layouts"
|
||||
myTheme "supersonic/ui/theme"
|
||||
"supersonic/ui/util"
|
||||
"supersonic/ui/widgets"
|
||||
"unicode"
|
||||
@@ -31,6 +32,7 @@ type SettingsDialog struct {
|
||||
OnReplayGainSettingsChanged func()
|
||||
OnAudioExclusiveSettingChanged func()
|
||||
OnAudioDeviceSettingChanged func()
|
||||
OnThemeSettingChanged func()
|
||||
OnDismiss func()
|
||||
|
||||
config *backend.Config
|
||||
@@ -50,6 +52,10 @@ func NewSettingsDialog(config *backend.Config, audioDeviceList []player.AudioDev
|
||||
s.createPlaybackTab(),
|
||||
s.createExperimentalTab(window),
|
||||
)
|
||||
// workaround issue where inactivated tabs don't fully update when theme setting is changed
|
||||
tabs.OnSelected = func(ti *container.TabItem) {
|
||||
ti.Content.Refresh()
|
||||
}
|
||||
s.promptText = widget.NewRichTextWithText("")
|
||||
s.content = container.NewVBox(tabs, widget.NewSeparator(),
|
||||
container.NewHBox(s.promptText, layout.NewSpacer(), widget.NewButton("Close", func() {
|
||||
@@ -62,6 +68,21 @@ func NewSettingsDialog(config *backend.Config, audioDeviceList []player.AudioDev
|
||||
}
|
||||
|
||||
func (s *SettingsDialog) createGeneralTab() *container.TabItem {
|
||||
themeSelect := widget.NewSelect([]string{
|
||||
string(myTheme.AppearanceDark),
|
||||
string(myTheme.AppearanceLight),
|
||||
string(myTheme.AppearanceAuto)}, nil)
|
||||
themeSelect.OnChanged = func(_ string) {
|
||||
s.config.Theme.Appearance = themeSelect.Options[themeSelect.SelectedIndex()]
|
||||
if s.OnThemeSettingChanged != nil {
|
||||
s.OnThemeSettingChanged()
|
||||
}
|
||||
}
|
||||
themeSelect.SetSelected(s.config.Theme.Appearance)
|
||||
if themeSelect.Selected == "" {
|
||||
themeSelect.SetSelectedIndex(0)
|
||||
}
|
||||
|
||||
startupPage := widget.NewSelect(backend.SupportedStartupPages, func(choice string) {
|
||||
s.config.Application.StartupPage = choice
|
||||
})
|
||||
@@ -161,7 +182,10 @@ func (s *SettingsDialog) createGeneralTab() *container.TabItem {
|
||||
scrobbleEnabled.Checked = s.config.Scrobbling.Enabled
|
||||
|
||||
return container.NewTabItem("General", container.NewVBox(
|
||||
container.New(layout.NewFormLayout(), widget.NewLabel("Startup page"), startupPage),
|
||||
container.New(layout.NewFormLayout(),
|
||||
widget.NewLabel("Appearance"), container.NewGridWithColumns(2, themeSelect),
|
||||
widget.NewLabel("Startup page"), container.NewGridWithColumns(2, startupPage),
|
||||
),
|
||||
container.NewHBox(systemTrayEnable, closeToTray),
|
||||
s.newSectionSeparator(),
|
||||
|
||||
|
||||
+19
-8
@@ -7,6 +7,7 @@ import (
|
||||
"supersonic/ui/browsing"
|
||||
"supersonic/ui/controller"
|
||||
"supersonic/ui/os"
|
||||
"supersonic/ui/theme"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
@@ -42,6 +43,7 @@ type MainWindow struct {
|
||||
BrowsingPane *browsing.BrowsingPane
|
||||
BottomPanel *BottomPanel
|
||||
|
||||
theme *theme.MyTheme
|
||||
haveSystemTray bool
|
||||
container *fyne.Container
|
||||
}
|
||||
@@ -51,8 +53,13 @@ func NewMainWindow(fyneApp fyne.App, appName, appVersion string, app *backend.Ap
|
||||
App: app,
|
||||
Window: fyneApp.NewWindow(appName),
|
||||
BrowsingPane: browsing.NewBrowsingPane(app),
|
||||
theme: theme.NewMyTheme(&app.Config.Theme),
|
||||
}
|
||||
|
||||
m.theme.NormalFont = app.Config.Application.FontNormalTTF
|
||||
m.theme.BoldFont = app.Config.Application.FontBoldTTF
|
||||
fyneApp.Settings().SetTheme(m.theme)
|
||||
|
||||
if app.Config.Application.EnableSystemTray {
|
||||
m.SetupSystemTrayMenu(appName, fyneApp)
|
||||
}
|
||||
@@ -117,7 +124,11 @@ func NewMainWindow(fyneApp fyne.App, appName, appVersion string, app *backend.Ap
|
||||
}
|
||||
}()
|
||||
})
|
||||
m.BrowsingPane.AddSettingsMenuItem("Settings...", m.Controller.ShowSettingsDialog)
|
||||
m.BrowsingPane.AddSettingsMenuItem("Settings...", func() {
|
||||
m.Controller.ShowSettingsDialog(func() {
|
||||
fyneApp.Settings().SetTheme(m.theme)
|
||||
})
|
||||
})
|
||||
m.BrowsingPane.AddSettingsMenuItem("About...", m.Controller.ShowAboutDialog)
|
||||
m.addNavigationButtons()
|
||||
m.BrowsingPane.DisableNavigationButtons()
|
||||
@@ -178,25 +189,25 @@ func (m *MainWindow) ShowNewVersionDialog(appName, versionTag string) {
|
||||
}
|
||||
|
||||
func (m *MainWindow) addNavigationButtons() {
|
||||
m.BrowsingPane.AddNavigationButton(res.ResHeadphonesInvertPng, func() {
|
||||
m.BrowsingPane.AddNavigationButton(theme.NowPlayingIcon, func() {
|
||||
m.Router.NavigateTo(controller.NowPlayingRoute(""))
|
||||
})
|
||||
m.BrowsingPane.AddNavigationButton(res.ResHeartFilledInvertPng, func() {
|
||||
m.BrowsingPane.AddNavigationButton(theme.FavoriteIcon, func() {
|
||||
m.Router.NavigateTo(controller.FavoritesRoute())
|
||||
})
|
||||
m.BrowsingPane.AddNavigationButton(res.ResDiscInvertPng, func() {
|
||||
m.BrowsingPane.AddNavigationButton(theme.AlbumIcon, func() {
|
||||
m.Router.NavigateTo(controller.AlbumsRoute())
|
||||
})
|
||||
m.BrowsingPane.AddNavigationButton(res.ResPeopleInvertPng, func() {
|
||||
m.BrowsingPane.AddNavigationButton(theme.ArtistIcon, func() {
|
||||
m.Router.NavigateTo(controller.ArtistsRoute())
|
||||
})
|
||||
m.BrowsingPane.AddNavigationButton(res.ResTheatermasksInvertPng, func() {
|
||||
m.BrowsingPane.AddNavigationButton(theme.GenreIcon, func() {
|
||||
m.Router.NavigateTo(controller.GenresRoute())
|
||||
})
|
||||
m.BrowsingPane.AddNavigationButton(res.ResPlaylistInvertPng, func() {
|
||||
m.BrowsingPane.AddNavigationButton(theme.PlaylistIcon, func() {
|
||||
m.Router.NavigateTo(controller.PlaylistsRoute())
|
||||
})
|
||||
m.BrowsingPane.AddNavigationButton(res.ResMusicnotesInvertPng, func() {
|
||||
m.BrowsingPane.AddNavigationButton(theme.TracksIcon, func() {
|
||||
m.Router.NavigateTo(controller.TracksRoute())
|
||||
})
|
||||
}
|
||||
|
||||
+113
-8
@@ -6,6 +6,9 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
"supersonic/backend"
|
||||
"supersonic/res"
|
||||
"supersonic/sharedutil"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
@@ -13,6 +16,16 @@ import (
|
||||
|
||||
const ColorNamePageBackground fyne.ThemeColorName = "PageBackground"
|
||||
|
||||
type AppearanceMode string
|
||||
|
||||
const (
|
||||
AppearanceLight AppearanceMode = "Light"
|
||||
AppearanceDark AppearanceMode = "Dark"
|
||||
AppearanceAuto AppearanceMode = "Auto"
|
||||
|
||||
DefaultAppearance AppearanceMode = AppearanceDark
|
||||
)
|
||||
|
||||
var (
|
||||
normalFont fyne.Resource
|
||||
boldFont fyne.Resource
|
||||
@@ -21,30 +34,107 @@ var (
|
||||
type MyTheme struct {
|
||||
NormalFont string
|
||||
BoldFont string
|
||||
config *backend.ThemeConfig
|
||||
}
|
||||
|
||||
var _ fyne.Theme = (*MyTheme)(nil)
|
||||
|
||||
func (m *MyTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
|
||||
func NewMyTheme(config *backend.ThemeConfig) *MyTheme {
|
||||
m := &MyTheme{config: config}
|
||||
m.createThemeIcons()
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MyTheme) Color(name fyne.ThemeColorName, _ fyne.ThemeVariant) color.Color {
|
||||
variant := m.getVariant()
|
||||
switch name {
|
||||
case ColorNamePageBackground:
|
||||
return color.RGBA{R: 15, G: 15, B: 15, A: 255}
|
||||
if variant == theme.VariantDark {
|
||||
return color.RGBA{R: 15, G: 15, B: 15, A: 255}
|
||||
}
|
||||
return color.RGBA{R: 250, G: 250, B: 250, A: 255}
|
||||
case theme.ColorNameBackground:
|
||||
return color.RGBA{R: 30, G: 30, B: 30, A: 255}
|
||||
if variant == theme.VariantDark {
|
||||
return color.RGBA{R: 35, G: 35, B: 35, A: 255}
|
||||
}
|
||||
return color.RGBA{R: 225, G: 223, B: 225, A: 255}
|
||||
case theme.ColorNameScrollBar:
|
||||
return theme.DarkTheme().Color(theme.ColorNameForeground, variant)
|
||||
if variant == theme.VariantDark {
|
||||
return theme.DarkTheme().Color(theme.ColorNameForeground, variant)
|
||||
}
|
||||
return theme.LightTheme().Color(theme.ColorNameForeground, variant)
|
||||
case theme.ColorNameButton:
|
||||
return color.RGBA{R: 20, G: 20, B: 20, A: 50}
|
||||
if variant == theme.VariantDark {
|
||||
return color.RGBA{R: 20, G: 20, B: 20, A: 50}
|
||||
}
|
||||
return color.RGBA{R: 200, G: 200, B: 200, A: 240}
|
||||
case theme.ColorNameInputBackground:
|
||||
return color.RGBA{R: 20, G: 20, B: 20, A: 50}
|
||||
if variant == theme.VariantDark {
|
||||
return color.RGBA{R: 20, G: 20, B: 20, A: 50}
|
||||
}
|
||||
case theme.ColorNameForeground:
|
||||
if variant == theme.VariantLight {
|
||||
return color.RGBA{R: 10, G: 10, B: 10, A: 255}
|
||||
}
|
||||
case theme.ColorNamePrimary:
|
||||
if variant == theme.VariantLight {
|
||||
return color.RGBA{R: 25, G: 25, B: 250, A: 255}
|
||||
}
|
||||
}
|
||||
return theme.DarkTheme().Color(name, variant)
|
||||
return theme.DefaultTheme().Color(name, variant)
|
||||
}
|
||||
|
||||
func (m *MyTheme) Icon(name fyne.ThemeIconName) fyne.Resource {
|
||||
return theme.DefaultTheme().Icon(name)
|
||||
}
|
||||
|
||||
type myThemedResource struct {
|
||||
myTheme MyTheme
|
||||
darkVariant *fyne.StaticResource
|
||||
lightVariant *fyne.StaticResource
|
||||
}
|
||||
|
||||
var _ fyne.Resource = myThemedResource{}
|
||||
|
||||
func (p myThemedResource) Content() []byte {
|
||||
if p.myTheme.getVariant() == theme.VariantDark {
|
||||
return p.darkVariant.StaticContent
|
||||
}
|
||||
return p.lightVariant.StaticContent
|
||||
}
|
||||
|
||||
func (p myThemedResource) Name() string {
|
||||
if p.myTheme.getVariant() == theme.VariantDark {
|
||||
return p.darkVariant.StaticName
|
||||
}
|
||||
return p.lightVariant.StaticName
|
||||
}
|
||||
|
||||
var (
|
||||
AlbumIcon fyne.Resource
|
||||
ArtistIcon fyne.Resource
|
||||
FavoriteIcon fyne.Resource
|
||||
NotFavoriteIcon fyne.Resource
|
||||
GenreIcon fyne.Resource
|
||||
NowPlayingIcon fyne.Resource
|
||||
PlaylistIcon fyne.Resource
|
||||
ShuffleIcon fyne.Resource
|
||||
TracksIcon fyne.Resource
|
||||
)
|
||||
|
||||
// MUST be called at startup!
|
||||
func (m MyTheme) createThemeIcons() {
|
||||
AlbumIcon = myThemedResource{myTheme: m, darkVariant: res.ResDiscInvertPng, lightVariant: res.ResDiscPng}
|
||||
ArtistIcon = myThemedResource{myTheme: m, darkVariant: res.ResPeopleInvertPng, lightVariant: res.ResPeoplePng}
|
||||
FavoriteIcon = myThemedResource{myTheme: m, darkVariant: res.ResHeartFilledInvertPng, lightVariant: res.ResHeartFilledPng}
|
||||
NotFavoriteIcon = myThemedResource{myTheme: m, darkVariant: res.ResHeartOutlineInvertPng, lightVariant: res.ResHeartOutlinePng}
|
||||
GenreIcon = myThemedResource{myTheme: m, darkVariant: res.ResTheatermasksInvertPng, lightVariant: res.ResTheatermasksPng}
|
||||
NowPlayingIcon = myThemedResource{myTheme: m, darkVariant: res.ResHeadphonesInvertPng, lightVariant: res.ResHeadphonesPng}
|
||||
PlaylistIcon = myThemedResource{myTheme: m, darkVariant: res.ResPlaylistInvertPng, lightVariant: res.ResPlaylistPng}
|
||||
ShuffleIcon = myThemedResource{myTheme: m, darkVariant: res.ResShuffleInvertSvg, lightVariant: res.ResShuffleSvg}
|
||||
TracksIcon = myThemedResource{myTheme: m, darkVariant: res.ResMusicnotesInvertPng, lightVariant: res.ResMusicnotesPng}
|
||||
}
|
||||
|
||||
func (m *MyTheme) Font(style fyne.TextStyle) fyne.Resource {
|
||||
switch style {
|
||||
case fyne.TextStyle{}:
|
||||
@@ -74,7 +164,6 @@ func (m *MyTheme) Font(style fyne.TextStyle) fyne.Resource {
|
||||
return normalFont
|
||||
}
|
||||
}
|
||||
|
||||
return theme.DefaultTheme().Font(style)
|
||||
}
|
||||
|
||||
@@ -82,6 +171,22 @@ func (m *MyTheme) Size(name fyne.ThemeSizeName) float32 {
|
||||
return theme.DefaultTheme().Size(name)
|
||||
}
|
||||
|
||||
func (m *MyTheme) getVariant() fyne.ThemeVariant {
|
||||
v := DefaultAppearance // default if config has invalid or missing setting
|
||||
if sharedutil.SliceContains(
|
||||
[]string{string(AppearanceLight), string(AppearanceDark), string(AppearanceAuto)},
|
||||
m.config.Appearance) {
|
||||
v = AppearanceMode(m.config.Appearance)
|
||||
}
|
||||
|
||||
if AppearanceMode(v) == AppearanceDark {
|
||||
return theme.VariantDark
|
||||
} else if AppearanceMode(v) == AppearanceLight {
|
||||
return theme.VariantLight
|
||||
}
|
||||
return fyne.CurrentApp().Settings().ThemeVariant()
|
||||
}
|
||||
|
||||
func readTTFFile(filepath string) ([]byte, error) {
|
||||
if !strings.HasSuffix(filepath, ".ttf") {
|
||||
err := errors.New("only .ttf fonts are supported")
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
type ThemedRectangle struct {
|
||||
widget.BaseWidget
|
||||
|
||||
rect *canvas.Rectangle
|
||||
|
||||
ColorName fyne.ThemeColorName
|
||||
}
|
||||
|
||||
func NewThemedRectangle(colorName fyne.ThemeColorName) *ThemedRectangle {
|
||||
t := &ThemedRectangle{
|
||||
ColorName: colorName,
|
||||
rect: canvas.NewRectangle(fyne.CurrentApp().Settings().Theme().Color(colorName,
|
||||
fyne.CurrentApp().Settings().ThemeVariant())),
|
||||
}
|
||||
t.ExtendBaseWidget(t)
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *ThemedRectangle) Refresh() {
|
||||
t.rect.FillColor = fyne.CurrentApp().Settings().Theme().Color(t.ColorName,
|
||||
fyne.CurrentApp().Settings().ThemeVariant())
|
||||
t.BaseWidget.Refresh()
|
||||
}
|
||||
|
||||
func (t *ThemedRectangle) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(t.rect)
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package widgets
|
||||
|
||||
import (
|
||||
"supersonic/res"
|
||||
"supersonic/ui/theme"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
@@ -32,9 +33,9 @@ func (f *FavoriteButton) Tapped(e *fyne.PointEvent) {
|
||||
|
||||
func (f *FavoriteButton) Refresh() {
|
||||
if f.IsFavorited {
|
||||
f.Icon = res.ResHeartFilledInvertPng
|
||||
f.Icon = theme.FavoriteIcon
|
||||
} else {
|
||||
f.Icon = res.ResHeartOutlineInvertPng
|
||||
f.Icon = theme.NotFavoriteIcon
|
||||
}
|
||||
f.Button.Refresh()
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package widgets
|
||||
import (
|
||||
"log"
|
||||
"supersonic/ui/layouts"
|
||||
myTheme "supersonic/ui/theme"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
@@ -42,7 +42,7 @@ func NewListHeader(cols []ListColumn, layout *layouts.ColumnsLayout) *ListHeader
|
||||
for i, _ := range l.columnVisible {
|
||||
l.columnVisible[i] = true
|
||||
}
|
||||
l.container = container.NewMax(canvas.NewRectangle(theme.BackgroundColor()), l.columnsContainer)
|
||||
l.container = container.NewMax(myTheme.NewThemedRectangle(theme.ColorNameBackground), l.columnsContainer)
|
||||
l.ExtendBaseWidget(l)
|
||||
l.buildColumns()
|
||||
return l
|
||||
|
||||
@@ -4,13 +4,14 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"supersonic/res"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"supersonic/sharedutil"
|
||||
"supersonic/ui/layouts"
|
||||
"supersonic/ui/os"
|
||||
myTheme "supersonic/ui/theme"
|
||||
"supersonic/ui/util"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
@@ -524,7 +525,7 @@ func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow
|
||||
t.album.OnTapped = func() { tracklist.onAlbumTapped(t.albumID) }
|
||||
t.dur = newTrailingAlignRichText()
|
||||
t.year = newTrailingAlignRichText()
|
||||
favorite := NewTappbaleIcon(res.ResHeartOutlineInvertPng)
|
||||
favorite := NewTappbaleIcon(myTheme.NotFavoriteIcon)
|
||||
favorite.OnTapped = t.toggleFavorited
|
||||
t.favorite = container.NewCenter(favorite)
|
||||
t.rating = NewStarRating()
|
||||
@@ -623,10 +624,10 @@ func (t *TrackRow) Update(tr *subsonic.Child, rowNum int) {
|
||||
// Render favorite column
|
||||
if tr.Starred.IsZero() {
|
||||
t.isFavorite = false
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = res.ResHeartOutlineInvertPng
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = myTheme.NotFavoriteIcon
|
||||
} else {
|
||||
t.isFavorite = true
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = res.ResHeartFilledInvertPng
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = myTheme.FavoriteIcon
|
||||
}
|
||||
|
||||
t.rating.Rating = tr.UserRating
|
||||
@@ -648,12 +649,12 @@ func (t *TrackRow) Update(tr *subsonic.Child, rowNum int) {
|
||||
|
||||
func (t *TrackRow) toggleFavorited() {
|
||||
if t.isFavorite {
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = res.ResHeartOutlineInvertPng
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = myTheme.NotFavoriteIcon
|
||||
t.favorite.Refresh()
|
||||
t.isFavorite = false
|
||||
t.tracklist.onSetFavorite(t.trackID, false)
|
||||
} else {
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = res.ResHeartFilledInvertPng
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = myTheme.FavoriteIcon
|
||||
t.favorite.Refresh()
|
||||
t.isFavorite = true
|
||||
t.tracklist.onSetFavorite(t.trackID, true)
|
||||
|
||||
Reference in New Issue
Block a user