expose more config file settings in the UI
This commit is contained in:
+4
-2
@@ -127,8 +127,10 @@ func StartupApp(appName, displayAppName, appVersion, appVersionTag, latestReleas
|
|||||||
log.Printf("Using config dir: %s", confDir)
|
log.Printf("Using config dir: %s", confDir)
|
||||||
log.Printf("Using cache dir: %s", cacheDir)
|
log.Printf("Using cache dir: %s", cacheDir)
|
||||||
|
|
||||||
a.UpdateChecker = NewUpdateChecker(appVersionTag, latestReleaseURL, &a.Config.Application.LastCheckedVersion)
|
if a.Config.Application.EnableAutoUpdateChecker {
|
||||||
a.UpdateChecker.Start(a.bgrndCtx, 24*time.Hour)
|
a.UpdateChecker = NewUpdateChecker(appVersionTag, latestReleaseURL, &a.Config.Application.LastCheckedVersion)
|
||||||
|
a.UpdateChecker.Start(a.bgrndCtx, 24*time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
if err := a.initMPV(); err != nil {
|
if err := a.initMPV(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ type AppConfig struct {
|
|||||||
EnqueueBatchSize int
|
EnqueueBatchSize int
|
||||||
Language string
|
Language string
|
||||||
DisableDPIDetection bool
|
DisableDPIDetection bool
|
||||||
|
EnableAutoUpdateChecker bool
|
||||||
|
|
||||||
// Experimental - may be removed in future
|
// Experimental - may be removed in future
|
||||||
FontNormalTTF string
|
FontNormalTTF string
|
||||||
@@ -189,6 +190,7 @@ func DefaultConfig(appVersionTag string) *Config {
|
|||||||
SkipSSLVerify: false,
|
SkipSSLVerify: false,
|
||||||
EnqueueBatchSize: 100,
|
EnqueueBatchSize: 100,
|
||||||
Language: "auto",
|
Language: "auto",
|
||||||
|
EnableAutoUpdateChecker: true,
|
||||||
},
|
},
|
||||||
AlbumPage: AlbumPageConfig{
|
AlbumPage: AlbumPageConfig{
|
||||||
TracklistColumns: []string{"Artist", "Time", "Plays", "Favorite", "Rating"},
|
TracklistColumns: []string{"Artist", "Time", "Plays", "Favorite", "Rating"},
|
||||||
|
|||||||
@@ -16,10 +16,13 @@
|
|||||||
"albums": "albums",
|
"albums": "albums",
|
||||||
"All": "All",
|
"All": "All",
|
||||||
"All Tracks": "All Tracks",
|
"All Tracks": "All Tracks",
|
||||||
|
"Allow multiple app instances": "Allow multiple app instances",
|
||||||
"Alt. URL": "Alt. URL",
|
"Alt. URL": "Alt. URL",
|
||||||
"An error occurred": "An error occurred",
|
"An error occurred": "An error occurred",
|
||||||
"An error occurred adding tracks to the playlist": "An error occurred adding tracks to the playlist",
|
"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",
|
"An error occurred updating the playlist": "An error occurred updating the playlist",
|
||||||
|
"Appearance": "Appearance",
|
||||||
|
"Application font": "Application font",
|
||||||
"Are you sure you want to delete the server": "Are you sure you want to delete the server",
|
"Are you sure you want to delete the server": "Are you sure you want to delete the server",
|
||||||
"Artist": "Artist",
|
"Artist": "Artist",
|
||||||
"Artist (A-Z)": "Artist (A-Z)",
|
"Artist (A-Z)": "Artist (A-Z)",
|
||||||
@@ -30,6 +33,7 @@
|
|||||||
"Audiobook": "Audiobook",
|
"Audiobook": "Audiobook",
|
||||||
"Authentication failed": "Authentication failed",
|
"Authentication failed": "Authentication failed",
|
||||||
"Auto": "Auto",
|
"Auto": "Auto",
|
||||||
|
"Automatically check for updates": "Automatically check for updates",
|
||||||
"Autoplay": "Autoplay",
|
"Autoplay": "Autoplay",
|
||||||
"Autoselect device": "Autoselect device",
|
"Autoselect device": "Autoselect device",
|
||||||
"Back": "Back",
|
"Back": "Back",
|
||||||
@@ -58,6 +62,7 @@
|
|||||||
"Demo": "Demo",
|
"Demo": "Demo",
|
||||||
"_Description": "Description",
|
"_Description": "Description",
|
||||||
"Disable server transcoding": "Disable server transcoding",
|
"Disable server transcoding": "Disable server transcoding",
|
||||||
|
"Disable automatic DPI adjustment": "Disable automatic DPI adjustment",
|
||||||
"Disc number": "Disc number",
|
"Disc number": "Disc number",
|
||||||
"Discography": "Discography",
|
"Discography": "Discography",
|
||||||
"discs": "discs",
|
"discs": "discs",
|
||||||
@@ -68,6 +73,7 @@
|
|||||||
"Edit": "Edit",
|
"Edit": "Edit",
|
||||||
"Edit Playlist": "Edit Playlist",
|
"Edit Playlist": "Edit Playlist",
|
||||||
"Edit server": "Edit server",
|
"Edit server": "Edit server",
|
||||||
|
"Enable LrcLib lyrics fetcher": "Enable LrcLib lyrics fetcher",
|
||||||
"Enable system tray": "Enable system tray",
|
"Enable system tray": "Enable system tray",
|
||||||
"Enabled": "Enabled",
|
"Enabled": "Enabled",
|
||||||
"Enter": "Enter",
|
"Enter": "Enter",
|
||||||
@@ -187,6 +193,7 @@
|
|||||||
"Single": "Single",
|
"Single": "Single",
|
||||||
"Size": "Size",
|
"Size": "Size",
|
||||||
"Skip duplicate tracks": "Skip duplicate tracks",
|
"Skip duplicate tracks": "Skip duplicate tracks",
|
||||||
|
"Skip SSL certificate verification": "Skip SSL certificate verification",
|
||||||
"Skip this version": "Skip this version",
|
"Skip this version": "Skip this version",
|
||||||
"Sort": "Sort",
|
"Sort": "Sort",
|
||||||
"Soundtrack": "Soundtrack",
|
"Soundtrack": "Soundtrack",
|
||||||
|
|||||||
@@ -65,21 +65,23 @@ func NewSettingsDialog(
|
|||||||
s := &SettingsDialog{config: config, audioDevices: audioDeviceList, themeFiles: themeFileList, clientDecidesScrobble: clientDecidesScrobble}
|
s := &SettingsDialog{config: config, audioDevices: audioDeviceList, themeFiles: themeFileList, clientDecidesScrobble: clientDecidesScrobble}
|
||||||
s.ExtendBaseWidget(s)
|
s.ExtendBaseWidget(s)
|
||||||
|
|
||||||
// TODO: Once Fyne supports disableable sliders, it's probably a nicer UX
|
// TODO: It may be a nicer UX to always create the equalizer tab,
|
||||||
// to create the equalizer tab but disable it if we are not using an equalizer player
|
// but disable it if we are not using an equalizer player
|
||||||
var tabs *container.AppTabs
|
var tabs *container.AppTabs
|
||||||
if isEqualizerPlayer {
|
if isEqualizerPlayer {
|
||||||
tabs = container.NewAppTabs(
|
tabs = container.NewAppTabs(
|
||||||
s.createGeneralTab(canSavePlayQueue),
|
s.createGeneralTab(canSavePlayQueue),
|
||||||
|
s.createAppearanceTab(window),
|
||||||
s.createPlaybackTab(isLocalPlayer, isReplayGainPlayer),
|
s.createPlaybackTab(isLocalPlayer, isReplayGainPlayer),
|
||||||
s.createEqualizerTab(equalizerBands),
|
s.createEqualizerTab(equalizerBands),
|
||||||
s.createExperimentalTab(window),
|
s.createAdvancedTab(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
tabs = container.NewAppTabs(
|
tabs = container.NewAppTabs(
|
||||||
s.createGeneralTab(canSavePlayQueue),
|
s.createGeneralTab(canSavePlayQueue),
|
||||||
|
s.createAppearanceTab(window),
|
||||||
s.createPlaybackTab(isLocalPlayer, isReplayGainPlayer),
|
s.createPlaybackTab(isLocalPlayer, isReplayGainPlayer),
|
||||||
s.createExperimentalTab(window),
|
s.createAdvancedTab(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,41 +101,6 @@ func NewSettingsDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SettingsDialog) createGeneralTab(canSaveQueueToServer bool) *container.TabItem {
|
func (s *SettingsDialog) createGeneralTab(canSaveQueueToServer bool) *container.TabItem {
|
||||||
themeNames := []string{"Default"}
|
|
||||||
themeFileNames := []string{""}
|
|
||||||
i, selIndex := 1, 0
|
|
||||||
for filename, displayname := range s.themeFiles {
|
|
||||||
themeFileNames = append(themeFileNames, filename)
|
|
||||||
themeNames = append(themeNames, displayname)
|
|
||||||
if strings.EqualFold(filename, s.config.Theme.ThemeFile) {
|
|
||||||
selIndex = i
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
themeFileSelect := widget.NewSelect(themeNames, nil)
|
|
||||||
themeFileSelect.SetSelectedIndex(selIndex)
|
|
||||||
themeFileSelect.OnChanged = func(_ string) {
|
|
||||||
s.config.Theme.ThemeFile = themeFileNames[themeFileSelect.SelectedIndex()]
|
|
||||||
if s.OnThemeSettingChanged != nil {
|
|
||||||
s.OnThemeSettingChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
themeModeSelect := widget.NewSelect([]string{
|
|
||||||
string(myTheme.AppearanceDark),
|
|
||||||
string(myTheme.AppearanceLight),
|
|
||||||
string(myTheme.AppearanceAuto)}, nil)
|
|
||||||
themeModeSelect.OnChanged = func(_ string) {
|
|
||||||
s.config.Theme.Appearance = themeModeSelect.Options[themeModeSelect.SelectedIndex()]
|
|
||||||
if s.OnThemeSettingChanged != nil {
|
|
||||||
s.OnThemeSettingChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
themeModeSelect.SetSelected(s.config.Theme.Appearance)
|
|
||||||
if themeModeSelect.Selected == "" {
|
|
||||||
themeModeSelect.SetSelectedIndex(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pages := util.LocalizeSlice(backend.SupportedStartupPages)
|
pages := util.LocalizeSlice(backend.SupportedStartupPages)
|
||||||
var startupPage *widget.Select
|
var startupPage *widget.Select
|
||||||
startupPage = widget.NewSelect(pages, func(_ string) {
|
startupPage = widget.NewSelect(pages, func(_ string) {
|
||||||
@@ -308,11 +275,8 @@ func (s *SettingsDialog) createGeneralTab(canSaveQueueToServer bool) *container.
|
|||||||
scrobbleEnabled.Checked = s.config.Scrobbling.Enabled
|
scrobbleEnabled.Checked = s.config.Scrobbling.Enabled
|
||||||
|
|
||||||
return container.NewTabItem(lang.L("General"), container.NewVBox(
|
return container.NewTabItem(lang.L("General"), container.NewVBox(
|
||||||
|
util.NewHSpace(0), // insert a theme.Padding amount of space at top
|
||||||
container.NewHBox(widget.NewLabel(lang.L("Language")), languageSelect),
|
container.NewHBox(widget.NewLabel(lang.L("Language")), languageSelect),
|
||||||
container.NewBorder(nil, nil, widget.NewLabel(lang.L("Theme")), /*left*/
|
|
||||||
container.NewHBox(widget.NewLabel(lang.L("Mode")), themeModeSelect, util.NewHSpace(5)), // right
|
|
||||||
themeFileSelect, // center
|
|
||||||
),
|
|
||||||
container.NewHBox(
|
container.NewHBox(
|
||||||
widget.NewLabel(lang.L("Startup page")), container.NewGridWithColumns(2, startupPage),
|
widget.NewLabel(lang.L("Startup page")), container.NewGridWithColumns(2, startupPage),
|
||||||
),
|
),
|
||||||
@@ -473,11 +437,41 @@ func (s *SettingsDialog) createEqualizerTab(eqBands []string) *container.TabItem
|
|||||||
return container.NewTabItem(lang.L("Equalizer"), cont)
|
return container.NewTabItem(lang.L("Equalizer"), cont)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SettingsDialog) createExperimentalTab(window fyne.Window) *container.TabItem {
|
func (s *SettingsDialog) createAppearanceTab(window fyne.Window) *container.TabItem {
|
||||||
warningLabel := widget.NewLabel("WARNING: these settings are experimental and may " +
|
themeNames := []string{"Default"}
|
||||||
"make the application buggy or increase system resource use. " +
|
themeFileNames := []string{""}
|
||||||
"They may be removed in future versions.")
|
i, selIndex := 1, 0
|
||||||
warningLabel.Wrapping = fyne.TextWrapWord
|
for filename, displayname := range s.themeFiles {
|
||||||
|
themeFileNames = append(themeFileNames, filename)
|
||||||
|
themeNames = append(themeNames, displayname)
|
||||||
|
if strings.EqualFold(filename, s.config.Theme.ThemeFile) {
|
||||||
|
selIndex = i
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
themeFileSelect := widget.NewSelect(themeNames, nil)
|
||||||
|
themeFileSelect.SetSelectedIndex(selIndex)
|
||||||
|
themeFileSelect.OnChanged = func(_ string) {
|
||||||
|
s.config.Theme.ThemeFile = themeFileNames[themeFileSelect.SelectedIndex()]
|
||||||
|
if s.OnThemeSettingChanged != nil {
|
||||||
|
s.OnThemeSettingChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
themeModeSelect := widget.NewSelect([]string{
|
||||||
|
string(myTheme.AppearanceDark),
|
||||||
|
string(myTheme.AppearanceLight),
|
||||||
|
string(myTheme.AppearanceAuto)}, nil)
|
||||||
|
themeModeSelect.OnChanged = func(_ string) {
|
||||||
|
s.config.Theme.Appearance = themeModeSelect.Options[themeModeSelect.SelectedIndex()]
|
||||||
|
if s.OnThemeSettingChanged != nil {
|
||||||
|
s.OnThemeSettingChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
themeModeSelect.SetSelected(s.config.Theme.Appearance)
|
||||||
|
if themeModeSelect.Selected == "" {
|
||||||
|
themeModeSelect.SetSelectedIndex(0)
|
||||||
|
}
|
||||||
|
|
||||||
normalFontEntry := widget.NewEntry()
|
normalFontEntry := widget.NewEntry()
|
||||||
normalFontEntry.SetPlaceHolder("path to .ttf or empty to use default")
|
normalFontEntry.SetPlaceHolder("path to .ttf or empty to use default")
|
||||||
@@ -518,13 +512,24 @@ func (s *SettingsDialog) createExperimentalTab(window fyne.Window) *container.Ta
|
|||||||
} else {
|
} else {
|
||||||
uiScaleRadio.Selected = "Normal"
|
uiScaleRadio.Selected = "Normal"
|
||||||
}
|
}
|
||||||
return container.NewTabItem("Experimental", container.NewVBox(
|
|
||||||
warningLabel,
|
disableDPI := widget.NewCheck(lang.L("Disable automatic DPI adjustment"), func(b bool) {
|
||||||
s.newSectionSeparator(),
|
s.config.Application.DisableDPIDetection = b
|
||||||
|
s.setRestartRequired()
|
||||||
|
})
|
||||||
|
disableDPI.Checked = s.config.Application.DisableDPIDetection
|
||||||
|
|
||||||
|
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*/
|
||||||
|
container.NewHBox(widget.NewLabel(lang.L("Mode")), themeModeSelect, util.NewHSpace(5)), // right
|
||||||
|
themeFileSelect, // center
|
||||||
|
),
|
||||||
widget.NewRichText(&widget.TextSegment{Text: lang.L("UI Scaling"), Style: util.BoldRichTextStyle}),
|
widget.NewRichText(&widget.TextSegment{Text: lang.L("UI Scaling"), Style: util.BoldRichTextStyle}),
|
||||||
uiScaleRadio,
|
uiScaleRadio,
|
||||||
|
disableDPI,
|
||||||
s.newSectionSeparator(),
|
s.newSectionSeparator(),
|
||||||
widget.NewRichText(&widget.TextSegment{Text: "Application Font", Style: util.BoldRichTextStyle}),
|
widget.NewRichText(&widget.TextSegment{Text: lang.L("Application font"), Style: util.BoldRichTextStyle}),
|
||||||
container.New(layout.NewFormLayout(),
|
container.New(layout.NewFormLayout(),
|
||||||
widget.NewLabel("Normal font"), container.NewBorder(nil, nil, nil, normalFontBrowse, normalFontEntry),
|
widget.NewLabel("Normal font"), container.NewBorder(nil, nil, nil, normalFontBrowse, normalFontEntry),
|
||||||
widget.NewLabel("Bold font"), container.NewBorder(nil, nil, nil, boldFontBrowse, boldFontEntry),
|
widget.NewLabel("Bold font"), container.NewBorder(nil, nil, nil, boldFontBrowse, boldFontEntry),
|
||||||
@@ -532,6 +537,40 @@ func (s *SettingsDialog) createExperimentalTab(window fyne.Window) *container.Ta
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingsDialog) createAdvancedTab() *container.TabItem {
|
||||||
|
multi := widget.NewCheckWithData(lang.L("Allow multiple app instances"), binding.BindBool(&s.config.Application.AllowMultiInstance))
|
||||||
|
sslSkip := widget.NewCheckWithData(lang.L("Skip SSL certificate verification"), binding.BindBool(&s.config.Application.SkipSSLVerify))
|
||||||
|
update := widget.NewCheckWithData(lang.L("Automatically check for updates"), binding.BindBool(&s.config.Application.EnableAutoUpdateChecker))
|
||||||
|
lrclib := widget.NewCheckWithData(lang.L("Enable LrcLib lyrics fetcher"), binding.BindBool(&s.config.Application.EnableLrcLib))
|
||||||
|
|
||||||
|
threeDigitValidator := func(text, selText string, r rune) bool {
|
||||||
|
return unicode.IsDigit(r) && len(text)-len(selText) < 3
|
||||||
|
}
|
||||||
|
|
||||||
|
percentEntry := widgets.NewTextRestrictedEntry(threeDigitValidator)
|
||||||
|
percentEntry.SetMinCharWidth(3)
|
||||||
|
percentEntry.OnChanged = func(str string) {
|
||||||
|
if i, err := strconv.Atoi(str); err == nil {
|
||||||
|
s.config.Application.MaxImageCacheSizeMB = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
percentEntry.Text = strconv.Itoa(s.config.Application.MaxImageCacheSizeMB)
|
||||||
|
|
||||||
|
imgCacheCfg := container.NewHBox(
|
||||||
|
widget.NewLabel(lang.L("Maximum image cache size")),
|
||||||
|
percentEntry,
|
||||||
|
widget.NewLabel("MB"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return container.NewTabItem(lang.L("Advanced"), container.NewVBox(
|
||||||
|
multi,
|
||||||
|
sslSkip,
|
||||||
|
update,
|
||||||
|
lrclib,
|
||||||
|
imgCacheCfg,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingsDialog) doChooseTTFFile(window fyne.Window, entry *widget.Entry) {
|
func (s *SettingsDialog) doChooseTTFFile(window fyne.Window, entry *widget.Entry) {
|
||||||
callback := func(urirc fyne.URIReadCloser, err error) {
|
callback := func(urirc fyne.URIReadCloser, err error) {
|
||||||
if err == nil && urirc != nil {
|
if err == nil && urirc != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user