From f512deb96e8a811e76ebd0a9d1e9fb9e8e0fb653 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Fri, 23 Aug 2024 01:10:54 -0400 Subject: [PATCH] Icons and styles use xyz.holanet.Nasin config --- application.go | 36 ++++++++++++++++++++++--- internal/icons/fallback/icon.go | 27 ++++++++++++++----- internal/icons/xdg/icon.go | 40 +++++++++++++++++++--------- internal/registrar/registrar_unix.go | 40 +++++++++++++++++++--------- 4 files changed, 106 insertions(+), 37 deletions(-) diff --git a/application.go b/application.go index 1564d6f..7a48c1c 100644 --- a/application.go +++ b/application.go @@ -144,15 +144,43 @@ func RunApplication (application Application) { } flag.Parse() + // open config + globalConfig, err := ApplicationConfig(GlobalApplicationDescription()) + if err != nil { log.Fatalln("nasin: could not open config:", err) } + defer globalConfig.Close() + styleConfigKey := "Style" + iconSetConfigKey := "IconSet" + + // registry + // TODO: rebuild registry around the config reg := new(registrar.Registrar) backend, err := reg.SetBackend() if err != nil { log.Fatalln("nasin: could not register backend:", err) } - err = reg.SetTheme() - if err != nil { log.Fatalln("nasin: could not set theme:", err) } - err = reg.SetIconSet() - if err != nil { log.Fatalln("nasin: could not set icon set:", err) } err = reg.SetFaceSet() if err != nil { log.Fatalln("nasin: could not set face set:", err) } + updateStyle := func () { + value, err := globalConfig.GetString(styleConfigKey, "") + if err != nil { log.Fatalln("nasin: could not set theme:", err) } + err = reg.SetStyle(value) + if err != nil { log.Fatalln("nasin: could not set theme:", err) } + } + updateIconSet := func () { + value, err := globalConfig.GetString(iconSetConfigKey, "") + if err != nil { log.Fatalln("nasin: could not set icon set:", err) } + err = reg.SetIconSet(value) + if err != nil { log.Fatalln("nasin: could not set icon set:", err) } + } + updateStyle() + updateIconSet() + + globalConfig.OnChange(func (key string) { + switch key { + case styleConfigKey: updateStyle() + case iconSetConfigKey: updateIconSet() + } + }) + + // init application err = application.Init() if err != nil { log.Fatalln("nasin: could not run application:", err) } diff --git a/internal/icons/fallback/icon.go b/internal/icons/fallback/icon.go index 860c0b2..d697dde 100644 --- a/internal/icons/fallback/icon.go +++ b/internal/icons/fallback/icon.go @@ -6,9 +6,10 @@ import _ "embed" import _ "image/png" import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/tomo/data" -import "git.tebibyte.media/tomo/nasin/internal/util" +import "git.tebibyte.media/tomo/tomo/event" import "git.tebibyte.media/tomo/tomo/canvas" import "git.tebibyte.media/tomo/backend/style" +import "git.tebibyte.media/tomo/nasin/internal/util" //go:embed assets/icons-small.png var atlasSmallBytes []byte @@ -35,7 +36,7 @@ const ( iconXOfficeSpreadsheet = tomo.Icon("x-office-spreadsheet") ) -func generateSource (data []byte, width int) map[tomo.Icon] canvas.Texture { +func generateSource (data []byte, width int) (canvas.TextureCloser, map[tomo.Icon] canvas.Texture) { atlasImage, _, err := image.Decode(bytes.NewReader(data)) if err != nil { panic(err) } atlasTexture := tomo.NewTexture(atlasImage) @@ -448,23 +449,26 @@ func generateSource (data []byte, width int) map[tomo.Icon] canvas.Texture { col(tomo.IconWeatherSnow) col(tomo.IconWeatherStorm) - return source + return atlasTexture, source } type iconSet struct { + atlasSmall canvas.TextureCloser + atlasLarge canvas.TextureCloser texturesSmall map[tomo.Icon] canvas.Texture texturesLarge map[tomo.Icon] canvas.Texture } // New creates a new fallback icon set. -func New () style.IconSet { - return new(iconSet) +func New () (style.IconSet, event.Cookie) { + iconSet := new(iconSet) + return iconSet, iconSet } func (this *iconSet) ensure () { if this.texturesSmall != nil { return } - this.texturesSmall = generateSource(atlasSmallBytes, 16) - this.texturesLarge = generateSource(atlasLargeBytes, 32) + this.atlasSmall, this.texturesSmall = generateSource(atlasSmallBytes, 16) + this.atlasLarge, this.texturesLarge = generateSource(atlasLargeBytes, 32) } func (this *iconSet) selectSource (size tomo.IconSize) map[tomo.Icon] canvas.Texture { @@ -500,3 +504,12 @@ func (this *iconSet) MimeIcon (mime data.Mime, size tomo.IconSize) canvas.Textur return source[tomo.Icon(iconApplicationXGeneric)] } } + +func (this *iconSet) Close () { + if this.atlasSmall != nil { + this.atlasSmall.Close() + } + if this.atlasLarge != nil { + this.atlasLarge.Close() + } +} diff --git a/internal/icons/xdg/icon.go b/internal/icons/xdg/icon.go index 828c22e..e63c438 100644 --- a/internal/icons/xdg/icon.go +++ b/internal/icons/xdg/icon.go @@ -9,6 +9,7 @@ import "strings" import _ "image/png" import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/tomo/data" +import "git.tebibyte.media/tomo/tomo/event" import "git.tebibyte.media/tomo/tomo/canvas" import "git.tebibyte.media/tomo/backend/style" import "git.tebibyte.media/tomo/nasin/internal/util" @@ -17,27 +18,27 @@ import xdgIconTheme "git.tebibyte.media/tomo/xdg/icon-theme" type iconTheme struct { xdg xdgIconTheme.Theme fallback style.IconSet - texturesSmall map[tomo.Icon] canvas.Texture - texturesMedium map[tomo.Icon] canvas.Texture - texturesLarge map[tomo.Icon] canvas.Texture + texturesSmall map[tomo.Icon] canvas.TextureCloser + texturesMedium map[tomo.Icon] canvas.TextureCloser + texturesLarge map[tomo.Icon] canvas.TextureCloser } -func FindThemeWarn (name string, fallback style.IconSet, path ...string) (style.IconSet, error) { +func FindThemeWarn (name string, fallback style.IconSet, path ...string) (style.IconSet, event.Cookie, error) { this := &iconTheme { fallback: fallback, - texturesLarge: make(map[tomo.Icon] canvas.Texture), - texturesMedium: make(map[tomo.Icon] canvas.Texture), - texturesSmall: make(map[tomo.Icon] canvas.Texture), + texturesLarge: make(map[tomo.Icon] canvas.TextureCloser), + texturesMedium: make(map[tomo.Icon] canvas.TextureCloser), + texturesSmall: make(map[tomo.Icon] canvas.TextureCloser), } xdg, err := xdgIconTheme.FindThemeWarn(name, path...) - if err != nil { return nil, err } + if err != nil { return nil, nil, err } this.xdg = xdg - return this, nil + return this, this, nil } -func (this *iconTheme) selectSource (size tomo.IconSize) map[tomo.Icon] canvas.Texture { +func (this *iconTheme) selectSource (size tomo.IconSize) map[tomo.Icon] canvas.TextureCloser { switch size { case tomo.IconSizeMedium: return this.texturesMedium case tomo.IconSizeLarge: return this.texturesLarge @@ -45,7 +46,7 @@ func (this *iconTheme) selectSource (size tomo.IconSize) map[tomo.Icon] canvas.T } } -func (this *iconTheme) xdgIcon (name string, size tomo.IconSize) (canvas.Texture, bool) { +func (this *iconTheme) xdgIcon (name string, size tomo.IconSize) (canvas.TextureCloser, bool) { // TODO use scaling factor instead of 1 // find icon file icon, err := this.xdg.FindIcon(name, iconSizePixels(size), 1, xdgIconTheme.PNG) @@ -100,7 +101,20 @@ func (this *iconTheme) MimeIcon (mime data.Mime, size tomo.IconSize) canvas.Text } } -func (this *iconTheme) icon (icon tomo.Icon, size tomo.IconSize) canvas.Texture { +func (this *iconTheme) Close () { + closeAllIn := func (mp map[tomo.Icon] canvas.TextureCloser) { + for _, texture := range mp { + if texture != nil { + texture.Close() + } + } + } + closeAllIn(this.texturesSmall) + closeAllIn(this.texturesMedium) + closeAllIn(this.texturesLarge) +} + +func (this *iconTheme) icon (icon tomo.Icon, size tomo.IconSize) canvas.TextureCloser { if texture, ok := this.xdgIcon(XdgIconName(icon), size); ok { return texture } @@ -110,7 +124,7 @@ func (this *iconTheme) icon (icon tomo.Icon, size tomo.IconSize) canvas.Texture return nil } -func (this *iconTheme) mimeIcon (mime data.Mime, size tomo.IconSize) canvas.Texture { +func (this *iconTheme) mimeIcon (mime data.Mime, size tomo.IconSize) canvas.TextureCloser { if texture, ok := this.xdgIcon(xdgFormatMime(mime), size); ok { return texture } diff --git a/internal/registrar/registrar_unix.go b/internal/registrar/registrar_unix.go index ec7e63e..de4745b 100644 --- a/internal/registrar/registrar_unix.go +++ b/internal/registrar/registrar_unix.go @@ -1,10 +1,10 @@ //go:build unix && (!darwin) package registrar -import "os" import "log" import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/backend/x" +import "git.tebibyte.media/tomo/tomo/event" import "git.tebibyte.media/sashakoshka/goparse" import "git.tebibyte.media/tomo/nasin/internal/icons/xdg" import "git.tebibyte.media/tomo/nasin/internal/styles/tss" @@ -14,6 +14,8 @@ import "git.tebibyte.media/tomo/nasin/internal/faces/fallback" type Registrar struct { backend *x.Backend + iconSetCookie event.Cookie + styleCookie event.Cookie } func (this *Registrar) SetBackend () (tomo.Backend, error) { @@ -24,38 +26,50 @@ func (this *Registrar) SetBackend () (tomo.Backend, error) { return backend, nil } -func (this *Registrar) SetTheme () error { - styleSheetName := os.Getenv("TOMO_STYLE_SHEET") - if styleSheetName != "" { - styl, _, err := tss.LoadFile(styleSheetName) +func (this *Registrar) SetStyle (name string) error { + if this.styleCookie != nil { + this.styleCookie.Close() + this.styleCookie = nil + } + + if name != "" { + styl, cookie, err := tss.LoadFile(name) if err == nil { this.backend.SetStyle(styl) + this.styleCookie = cookie return nil } else { log.Printf ( "nasin: could not load style sheet '%s'\n%v", - styleSheetName, parse.Format(err)) + name, parse.Format(err)) } } - styl, _ := fallbackStyle.New() + styl, cookie := fallbackStyle.New() + this.styleCookie = cookie this.backend.SetStyle(styl) return nil } -func (this *Registrar) SetIconSet () error { - iconSet := fallbackIcons.New() - iconSetName := os.Getenv("TOMO_XDG_ICON_THEME") - if iconSetName != "" { - xdgIconSet, err := xdgIcons.FindThemeWarn(iconSetName, iconSet) +func (this *Registrar) SetIconSet (name string) error { + if this.iconSetCookie != nil { + this.iconSetCookie.Close() + this.iconSetCookie = nil + } + + iconSet, cookie := fallbackIcons.New() + if name != "" { + xdgIconSet, xdgCookie, err := xdgIcons.FindThemeWarn(name, iconSet) + cookie = event.MultiCookie(cookie, xdgCookie) if err == nil { iconSet = xdgIconSet } else { - log.Printf("nasin: could not load icon theme '%s': %v", iconSetName, err) + log.Printf("nasin: could not load icon theme '%s': %v", name, err) } } this.backend.SetIconSet(iconSet) + this.iconSetCookie = cookie return nil }