diff --git a/handler/menu.go b/handler/menu.go index 0bd5d07..fa32d8b 100644 --- a/handler/menu.go +++ b/handler/menu.go @@ -5,6 +5,7 @@ import ( "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/localizer" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/menu" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/theme" "github.com/nicksnyder/go-i18n/v2/i18n" ) @@ -20,6 +21,7 @@ type MenuHandler struct { menuViewSetting menu.ViewSettingContract localizerView localizer.ViewContract localizerRepository localizer.RepositoryContract + themeService theme.ThemeContract } func NewMenuHandler( @@ -29,6 +31,7 @@ func NewMenuHandler( menuViewSetting menu.ViewSettingContract, localizerView localizer.ViewContract, localizerRepository localizer.RepositoryContract, + themeService theme.ThemeContract, ) *MenuHandler { return &MenuHandler{ app: app, @@ -37,6 +40,7 @@ func NewMenuHandler( menuViewSetting: menuViewSetting, localizerView: localizerView, localizerRepository: localizerRepository, + themeService: themeService, } } @@ -154,6 +158,12 @@ func (h MenuHandler) settingsSelection() { if err != nil { return err } + + err = h.themeService.SetCurrentTheme(setting.ThemeInfo) + if err != nil { + return err + } + h.convertorHandler.MainConvertor() return nil } diff --git a/languages/active.en.toml b/languages/active.en.toml index 491dca3..e89a35c 100644 --- a/languages/active.en.toml +++ b/languages/active.en.toml @@ -426,6 +426,10 @@ other = "Licenses from other products used in the program" hash = "sha1-ed3f0e507a5b4ed0649d7c768fe0d47413d839ba" other = "Language" +[menuSettingsTheme] +hash = "sha1-553c45f1b84a92b08dc1f088c13f924cde95765e" +other = "Theme" + [or] hash = "sha1-30bb0333ca1583110e4ced513b5d2455b86f529b" other = "or" @@ -522,6 +526,18 @@ other = "Settings" hash = "sha1-f5b8ed88e9609963035d2235be0a79bbec619976" other = "Checking FFmpeg for serviceability..." +[themesNameDark] +hash = "sha1-bd16b234708a2515a9f2d0ca41fb11e7fe8a38a2" +other = "Dark" + +[themesNameDefault] +hash = "sha1-469631cb165dcbbfea9e747056c25fbccb28c481" +other = "Default" + +[themesNameLight] +hash = "sha1-8080010c5e7d7edf56e89a99d8a2422898417845" +other = "Light" + [titleDownloadLink] hash = "sha1-92df86371f6c3a06ca1e4754f113142776a32d49" other = "You can download it from here" diff --git a/languages/active.kk.toml b/languages/active.kk.toml index a59a702..72e3e16 100644 --- a/languages/active.kk.toml +++ b/languages/active.kk.toml @@ -426,6 +426,10 @@ other = "Бағдарламада пайдаланылатын басқа өні hash = "sha1-ed3f0e507a5b4ed0649d7c768fe0d47413d839ba" other = "Тіл" +[menuSettingsTheme] +hash = "sha1-553c45f1b84a92b08dc1f088c13f924cde95765e" +other = "Тақырып" + [or] hash = "sha1-30bb0333ca1583110e4ced513b5d2455b86f529b" other = "немесе" @@ -522,6 +526,18 @@ other = "Параметрлер" hash = "sha1-f5b8ed88e9609963035d2235be0a79bbec619976" other = "FFmpeg функционалдығы тексерілуде..." +[themesNameDark] +hash = "sha1-bd16b234708a2515a9f2d0ca41fb11e7fe8a38a2" +other = "Қараңғы тақырып" + +[themesNameDefault] +hash = "sha1-469631cb165dcbbfea9e747056c25fbccb28c481" +other = "Әдепкі бойынша" + +[themesNameLight] +hash = "sha1-8080010c5e7d7edf56e89a99d8a2422898417845" +other = "Жеңіл тақырып" + [titleDownloadLink] hash = "sha1-92df86371f6c3a06ca1e4754f113142776a32d49" other = "Сіз оны осы жерден жүктей аласыз" diff --git a/languages/active.ru.toml b/languages/active.ru.toml index 2827dd5..638bde7 100644 --- a/languages/active.ru.toml +++ b/languages/active.ru.toml @@ -105,6 +105,7 @@ languageSelectionHead = "Выберите язык" licenseLink = "Сведения о лицензии" licenseLinkOther = "Лицензии от других продуктов, которые используются в программе" menuSettingsLanguage = "Язык" +menuSettingsTheme = "Тема" or = "или" parameterCheckbox = "Включить параметр" pathToFfmpeg = "Путь к FFmpeg:" @@ -129,6 +130,9 @@ selectFFPathTitle = "Укажите путь к FFmpeg и к FFprobe" selectFormat = "Расширение файла:" settings = "Настройки" testFF = "Проверка FFmpeg на работоспособность..." +themesNameDark = "Тёмная" +themesNameDefault = "По умолчанию" +themesNameLight = "Светлая" titleDownloadLink = "Скачать можно от сюда" total = "Всего" unzipRun = "Распаковывается..." diff --git a/main.go b/main.go index 31c5879..18d96ff 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( "git.kor-elf.net/kor-elf/gui-for-ffmpeg/menu" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/migration" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/theme" "go.etcd.io/bbolt" "golang.org/x/text/language" "os" @@ -109,10 +110,13 @@ func main() { convertorView := convertor.NewView(application) convertorHandler := handler.NewConvertorHandler(application, convertorView, errorView, convertorRepository, settingDirectoryForSaving) + themeRepository := theme.NewRepository(settingRepository) + themeService := theme.NewTheme(application, themeRepository) + localizerRepository := localizer.NewRepository(settingRepository) menuView := menu.NewView(application) - menuSettingView := menu.NewViewSetting(application) - mainMenu := handler.NewMenuHandler(application, convertorHandler, menuView, menuSettingView, localizerView, localizerRepository) + menuSettingView := menu.NewViewSetting(application, themeService) + mainMenu := handler.NewMenuHandler(application, convertorHandler, menuView, menuSettingView, localizerView, localizerRepository, themeService) mainHandler := handler.NewMainHandler(application, convertorHandler, mainMenu, localizerRepository) mainHandler.Start() diff --git a/menu/view_setting.go b/menu/view_setting.go index d7d6433..045662e 100644 --- a/menu/view_setting.go +++ b/menu/view_setting.go @@ -5,6 +5,7 @@ import ( "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/widget" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/theme" "github.com/nicksnyder/go-i18n/v2/i18n" "image/color" ) @@ -17,16 +18,19 @@ type ViewSettingContract interface { } type SettingForm struct { - Language kernel.Lang + Language kernel.Lang + ThemeInfo theme.ThemeInfoContract } type ViewSetting struct { - app kernel.AppContract + app kernel.AppContract + themeService theme.ThemeContract } -func NewViewSetting(app kernel.AppContract) *ViewSetting { +func NewViewSetting(app kernel.AppContract, themeService theme.ThemeContract) *ViewSetting { return &ViewSetting{ - app: app, + app: app, + themeService: themeService, } } @@ -36,7 +40,8 @@ func (v ViewSetting) Main(save func(*SettingForm) error, cancel func()) { errorMessage.TextStyle = fyne.TextStyle{Bold: true} viewSettingForm := &SettingForm{ - Language: v.app.GetLocalizerService().GetCurrentLanguage().Lang, + Language: v.app.GetLocalizerService().GetCurrentLanguage().Lang, + ThemeInfo: v.themeService.GetCurrentThemeInfo(), } languageItems := []string{} @@ -45,12 +50,25 @@ func (v ViewSetting) Main(save func(*SettingForm) error, cancel func()) { languageItems = append(languageItems, language.Title) langByTitle[language.Title] = language } - selectLanguages := widget.NewSelect(languageItems, func(s string) { + selectLanguage := widget.NewSelect(languageItems, func(s string) { if lang, ok := langByTitle[s]; ok { viewSettingForm.Language = lang } }) - selectLanguages.Selected = v.app.GetLocalizerService().GetCurrentLanguage().Lang.Title + selectLanguage.Selected = v.app.GetLocalizerService().GetCurrentLanguage().Lang.Title + + themeItems := []string{} + themeByTitle := map[string]theme.ThemeInfoContract{} + for _, themeInfo := range v.themeService.List() { + themeItems = append(themeItems, themeInfo.GetTitle()) + themeByTitle[themeInfo.GetTitle()] = themeInfo + } + selectTheme := widget.NewSelect(themeItems, func(s string) { + if themeInfo, ok := themeByTitle[s]; ok { + viewSettingForm.ThemeInfo = themeInfo + } + }) + selectTheme.Selected = v.themeService.GetCurrentThemeInfo().GetTitle() form := &widget.Form{ Items: []*widget.FormItem{ @@ -58,7 +76,13 @@ func (v ViewSetting) Main(save func(*SettingForm) error, cancel func()) { Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ MessageID: "menuSettingsLanguage", }), - Widget: selectLanguages, + Widget: selectLanguage, + }, + { + Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ + MessageID: "menuSettingsTheme", + }), + Widget: selectTheme, }, { Widget: errorMessage, diff --git a/theme/repository.go b/theme/repository.go new file mode 100644 index 0000000..91b2351 --- /dev/null +++ b/theme/repository.go @@ -0,0 +1,28 @@ +package theme + +import "git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting" + +type RepositoryContract interface { + GetCode() string + Save(code string) (setting.Setting, error) +} + +type Repository struct { + settingRepository setting.RepositoryContract +} + +func NewRepository(settingRepository setting.RepositoryContract) *Repository { + return &Repository{settingRepository: settingRepository} +} + +func (r Repository) GetCode() string { + name, err := r.settingRepository.GetValue("theme") + if err != nil { + return "default" + } + return name +} + +func (r Repository) Save(code string) (setting.Setting, error) { + return r.settingRepository.CreateOrUpdate("theme", code) +} diff --git a/theme/theme.go b/theme/theme.go new file mode 100644 index 0000000..ba0dfa0 --- /dev/null +++ b/theme/theme.go @@ -0,0 +1,158 @@ +package theme + +import ( + "fyne.io/fyne/v2" + fyneTheme "fyne.io/fyne/v2/theme" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" + "github.com/nicksnyder/go-i18n/v2/i18n" + "image/color" +) + +type ThemeContract interface { + List() map[string]ThemeInfoContract + GetCurrentThemeInfo() ThemeInfoContract + SetCurrentTheme(themeInfo ThemeInfoContract) error +} + +type theme struct { + app kernel.AppContract + repository RepositoryContract + list map[string]ThemeInfoContract +} + +func NewTheme(app kernel.AppContract, repository RepositoryContract) ThemeContract { + theme := &theme{ + app: app, + repository: repository, + list: getThemes(app.GetLocalizerService()), + } + + theme.init() + + return theme +} + +func (t theme) init() { + themeInfo := t.GetCurrentThemeInfo() + if themeInfo.GetName() == "default" { + t.app.GetAppFyne().Settings().SetTheme(fyneTheme.DefaultTheme()) + return + } + t.app.GetAppFyne().Settings().SetTheme(&forcedVariant{theme: fyneTheme.DefaultTheme(), variant: themeInfo.GetVariant()}) +} + +func (t theme) GetCurrentThemeInfo() ThemeInfoContract { + themes := t.List() + if themeInfo, ok := themes[t.repository.GetCode()]; ok { + return themeInfo + } + + return themes["default"] +} + +func (t theme) List() map[string]ThemeInfoContract { + return t.list +} + +func (t theme) SetCurrentTheme(themeInfo ThemeInfoContract) error { + _, err := t.repository.Save(themeInfo.GetName()) + if err != nil { + return err + } + + if themeInfo.GetName() == "default" { + t.app.GetAppFyne().Settings().SetTheme(fyneTheme.DefaultTheme()) + return nil + } + t.app.GetAppFyne().Settings().SetTheme(&forcedVariant{theme: fyneTheme.DefaultTheme(), variant: themeInfo.GetVariant()}) + + return nil +} + +type ThemeInfoContract interface { + GetName() string + GetTitle() string + GetVariant() fyne.ThemeVariant +} + +type themeInfo struct { + name string + title string + variant fyne.ThemeVariant +} + +func (inf themeInfo) GetName() string { + return inf.name +} + +func (inf themeInfo) GetTitle() string { + return inf.title +} + +func (inf themeInfo) GetVariant() fyne.ThemeVariant { + return inf.variant +} + +func getThemes(localizer kernel.LocalizerContract) map[string]ThemeInfoContract { + themesNameDefault := &themeInfo{ + name: "default", + title: localizer.GetMessage(&i18n.LocalizeConfig{ + MessageID: "themesNameDefault", + }), + } + + themesNameLight := &themeInfo{ + name: "light", + title: localizer.GetMessage(&i18n.LocalizeConfig{ + MessageID: "themesNameLight", + }), + variant: fyneTheme.VariantLight, + } + + themesNameDark := &themeInfo{ + name: "dark", + title: localizer.GetMessage(&i18n.LocalizeConfig{ + MessageID: "themesNameDark", + }), + variant: fyneTheme.VariantDark, + } + + list := map[string]ThemeInfoContract{ + "default": themesNameDefault, + "light": themesNameLight, + "dark": themesNameDark, + } + + localizer.AddChangeCallback("themesNameDefault", func(text string) { + themesNameDefault.title = text + }) + localizer.AddChangeCallback("themesNameLight", func(text string) { + themesNameLight.title = text + }) + localizer.AddChangeCallback("themesNameDark", func(text string) { + themesNameDark.title = text + }) + + return list +} + +type forcedVariant struct { + theme fyne.Theme + variant fyne.ThemeVariant +} + +func (f *forcedVariant) Color(name fyne.ThemeColorName, _ fyne.ThemeVariant) color.Color { + return f.theme.Color(name, f.variant) +} + +func (f *forcedVariant) Font(style fyne.TextStyle) fyne.Resource { + return fyneTheme.DefaultTheme().Font(style) +} + +func (f *forcedVariant) Icon(name fyne.ThemeIconName) fyne.Resource { + return fyneTheme.DefaultTheme().Icon(name) +} + +func (f *forcedVariant) Size(name fyne.ThemeSizeName) float32 { + return fyneTheme.DefaultTheme().Size(name) +}