Add separate GTK UI font setting
- commit
- 08a0a9d478e8378205426ab6b84f7bbb92667afd
- author
- brice · · 2026-05-17 05:58 UTC
- parent
- 7bebba6
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
18 files · +364 -296
M internal/application/dto/webui_config.go +1 -0
| @@ -70,6 +70,7 @@ type WebUIAppearanceConfig struct { | |||
| 70 | 70 | SansFont string `json:"sans_font"` | |
| 71 | 71 | SerifFont string `json:"serif_font"` | |
| 72 | 72 | MonospaceFont string `json:"monospace_font"` | |
| 73 | + | GtkFont string `json:"gtk_font"` | |
| 73 | 74 | DefaultFontSize int `json:"default_font_size"` | |
| 74 | 75 | ColorScheme string `json:"color_scheme"` | |
| 75 | 76 | LightPalette ColorPalette `json:"light_palette"` | |
| @@ -70,6 +70,7 @@ type WebUIAppearanceConfig struct { | |||||
| 70 | SansFont string `json:"sans_font"` | 70 | SansFont string `json:"sans_font"` | ||
| 71 | SerifFont string `json:"serif_font"` | 71 | SerifFont string `json:"serif_font"` | ||
| 72 | MonospaceFont string `json:"monospace_font"` | 72 | MonospaceFont string `json:"monospace_font"` | ||
| 73 | + | GtkFont string `json:"gtk_font"` | |||
| 73 | DefaultFontSize int `json:"default_font_size"` | 74 | DefaultFontSize int `json:"default_font_size"` | ||
| 74 | ColorScheme string `json:"color_scheme"` | 75 | ColorScheme string `json:"color_scheme"` | ||
| 75 | LightPalette ColorPalette `json:"light_palette"` | 76 | LightPalette ColorPalette `json:"light_palette"` | ||
M internal/application/usecase/save_webui_config.go +2 -0
| @@ -55,6 +55,7 @@ func normalizeWebUIConfig(cfg dto.WebUIConfig) dto.WebUIConfig { | |||
| 55 | 55 | cfg.Appearance.SansFont = strings.TrimSpace(cfg.Appearance.SansFont) | |
| 56 | 56 | cfg.Appearance.SerifFont = strings.TrimSpace(cfg.Appearance.SerifFont) | |
| 57 | 57 | cfg.Appearance.MonospaceFont = strings.TrimSpace(cfg.Appearance.MonospaceFont) | |
| 58 | + | cfg.Appearance.GtkFont = strings.TrimSpace(cfg.Appearance.GtkFont) | |
| 58 | 59 | cfg.Appearance.ColorScheme = strings.TrimSpace(cfg.Appearance.ColorScheme) | |
| 59 | 60 | cfg.DefaultSearchEngine = strings.TrimSpace(cfg.DefaultSearchEngine) | |
| 60 | 61 | if len(cfg.SearchShortcuts) > 0 { | |
| @@ -86,6 +87,7 @@ func validateWebUIConfig(cfg dto.WebUIConfig) error { | |||
| 86 | 87 | errs = append(errs, validation.ValidateFontFamily("appearance.sans_font", cfg.Appearance.SansFont)...) | |
| 87 | 88 | errs = append(errs, validation.ValidateFontFamily("appearance.serif_font", cfg.Appearance.SerifFont)...) | |
| 88 | 89 | errs = append(errs, validation.ValidateFontFamily("appearance.monospace_font", cfg.Appearance.MonospaceFont)...) | |
| 90 | + | errs = append(errs, validation.ValidateFontFamily("appearance.gtk_font", cfg.Appearance.GtkFont)...) | |
| 89 | 91 | ||
| 90 | 92 | for _, err := range validation.ValidateShortcutURL(cfg.DefaultSearchEngine) { | |
| 91 | 93 | errs = append(errs, fmt.Sprintf("default_search_engine: %s", err)) | |
| @@ -55,6 +55,7 @@ func normalizeWebUIConfig(cfg dto.WebUIConfig) dto.WebUIConfig { | |||||
| 55 | cfg.Appearance.SansFont = strings.TrimSpace(cfg.Appearance.SansFont) | 55 | cfg.Appearance.SansFont = strings.TrimSpace(cfg.Appearance.SansFont) | ||
| 56 | cfg.Appearance.SerifFont = strings.TrimSpace(cfg.Appearance.SerifFont) | 56 | cfg.Appearance.SerifFont = strings.TrimSpace(cfg.Appearance.SerifFont) | ||
| 57 | cfg.Appearance.MonospaceFont = strings.TrimSpace(cfg.Appearance.MonospaceFont) | 57 | cfg.Appearance.MonospaceFont = strings.TrimSpace(cfg.Appearance.MonospaceFont) | ||
| 58 | + | cfg.Appearance.GtkFont = strings.TrimSpace(cfg.Appearance.GtkFont) | |||
| 58 | cfg.Appearance.ColorScheme = strings.TrimSpace(cfg.Appearance.ColorScheme) | 59 | cfg.Appearance.ColorScheme = strings.TrimSpace(cfg.Appearance.ColorScheme) | ||
| 59 | cfg.DefaultSearchEngine = strings.TrimSpace(cfg.DefaultSearchEngine) | 60 | cfg.DefaultSearchEngine = strings.TrimSpace(cfg.DefaultSearchEngine) | ||
| 60 | if len(cfg.SearchShortcuts) > 0 { | 61 | if len(cfg.SearchShortcuts) > 0 { | ||
| @@ -86,6 +87,7 @@ func validateWebUIConfig(cfg dto.WebUIConfig) error { | |||||
| 86 | errs = append(errs, validation.ValidateFontFamily("appearance.sans_font", cfg.Appearance.SansFont)...) | 87 | errs = append(errs, validation.ValidateFontFamily("appearance.sans_font", cfg.Appearance.SansFont)...) | ||
| 87 | errs = append(errs, validation.ValidateFontFamily("appearance.serif_font", cfg.Appearance.SerifFont)...) | 88 | errs = append(errs, validation.ValidateFontFamily("appearance.serif_font", cfg.Appearance.SerifFont)...) | ||
| 88 | errs = append(errs, validation.ValidateFontFamily("appearance.monospace_font", cfg.Appearance.MonospaceFont)...) | 89 | errs = append(errs, validation.ValidateFontFamily("appearance.monospace_font", cfg.Appearance.MonospaceFont)...) | ||
| 90 | + | errs = append(errs, validation.ValidateFontFamily("appearance.gtk_font", cfg.Appearance.GtkFont)...) | |||
| 89 | 91 | ||||
| 90 | for _, err := range validation.ValidateShortcutURL(cfg.DefaultSearchEngine) { | 92 | for _, err := range validation.ValidateShortcutURL(cfg.DefaultSearchEngine) { | ||
| 91 | errs = append(errs, fmt.Sprintf("default_search_engine: %s", err)) | 93 | errs = append(errs, fmt.Sprintf("default_search_engine: %s", err)) | ||
M internal/application/usecase/save_webui_config_test.go +1 -0
| @@ -112,6 +112,7 @@ func validWebUIConfig() dto.WebUIConfig { | |||
| 112 | 112 | SansFont: "Inter", | |
| 113 | 113 | SerifFont: "Georgia", | |
| 114 | 114 | MonospaceFont: "JetBrains Mono", | |
| 115 | + | GtkFont: "Adwaita Sans", | |
| 115 | 116 | DefaultFontSize: 16, | |
| 116 | 117 | ColorScheme: "prefer-dark", | |
| 117 | 118 | LightPalette: palette, | |
| @@ -112,6 +112,7 @@ func validWebUIConfig() dto.WebUIConfig { | |||||
| 112 | SansFont: "Inter", | 112 | SansFont: "Inter", | ||
| 113 | SerifFont: "Georgia", | 113 | SerifFont: "Georgia", | ||
| 114 | MonospaceFont: "JetBrains Mono", | 114 | MonospaceFont: "JetBrains Mono", | ||
| 115 | + | GtkFont: "Adwaita Sans", | |||
| 115 | DefaultFontSize: 16, | 116 | DefaultFontSize: 16, | ||
| 116 | ColorScheme: "prefer-dark", | 117 | ColorScheme: "prefer-dark", | ||
| 117 | LightPalette: palette, | 118 | LightPalette: palette, | ||
M internal/domain/entity/config_types.go +1 -0
| @@ -9,6 +9,7 @@ type AppearanceConfig struct { | |||
| 9 | 9 | SansFont string `mapstructure:"sans_font" yaml:"sans_font" toml:"sans_font" json:"sans_font"` | |
| 10 | 10 | SerifFont string `mapstructure:"serif_font" yaml:"serif_font" toml:"serif_font" json:"serif_font"` | |
| 11 | 11 | MonospaceFont string `mapstructure:"monospace_font" yaml:"monospace_font" toml:"monospace_font" json:"monospace_font"` | |
| 12 | + | GtkFont string `mapstructure:"gtk_font" yaml:"gtk_font" toml:"gtk_font" json:"gtk_font"` | |
| 12 | 13 | DefaultFontSize int `mapstructure:"default_font_size" yaml:"default_font_size" toml:"default_font_size" json:"default_font_size"` | |
| 13 | 14 | LightPalette ColorPalette `mapstructure:"light_palette" yaml:"light_palette" toml:"light_palette" json:"light_palette"` | |
| 14 | 15 | DarkPalette ColorPalette `mapstructure:"dark_palette" yaml:"dark_palette" toml:"dark_palette" json:"dark_palette"` | |
| @@ -9,6 +9,7 @@ type AppearanceConfig struct { | |||||
| 9 | SansFont string `mapstructure:"sans_font" yaml:"sans_font" toml:"sans_font" json:"sans_font"` | 9 | SansFont string `mapstructure:"sans_font" yaml:"sans_font" toml:"sans_font" json:"sans_font"` | ||
| 10 | SerifFont string `mapstructure:"serif_font" yaml:"serif_font" toml:"serif_font" json:"serif_font"` | 10 | SerifFont string `mapstructure:"serif_font" yaml:"serif_font" toml:"serif_font" json:"serif_font"` | ||
| 11 | MonospaceFont string `mapstructure:"monospace_font" yaml:"monospace_font" toml:"monospace_font" json:"monospace_font"` | 11 | MonospaceFont string `mapstructure:"monospace_font" yaml:"monospace_font" toml:"monospace_font" json:"monospace_font"` | ||
| 12 | + | GtkFont string `mapstructure:"gtk_font" yaml:"gtk_font" toml:"gtk_font" json:"gtk_font"` | |||
| 12 | DefaultFontSize int `mapstructure:"default_font_size" yaml:"default_font_size" toml:"default_font_size" json:"default_font_size"` | 13 | DefaultFontSize int `mapstructure:"default_font_size" yaml:"default_font_size" toml:"default_font_size" json:"default_font_size"` | ||
| 13 | LightPalette ColorPalette `mapstructure:"light_palette" yaml:"light_palette" toml:"light_palette" json:"light_palette"` | 14 | LightPalette ColorPalette `mapstructure:"light_palette" yaml:"light_palette" toml:"light_palette" json:"light_palette"` | ||
| 14 | DarkPalette ColorPalette `mapstructure:"dark_palette" yaml:"dark_palette" toml:"dark_palette" json:"dark_palette"` | 15 | DarkPalette ColorPalette `mapstructure:"dark_palette" yaml:"dark_palette" toml:"dark_palette" json:"dark_palette"` | ||
M internal/infrastructure/config/defaults.go +1 -0
| @@ -136,6 +136,7 @@ func DefaultConfig() *Config { | |||
| 136 | 136 | SansFont: "Fira Sans", | |
| 137 | 137 | SerifFont: "Fira Sans", | |
| 138 | 138 | MonospaceFont: "Fira Code", | |
| 139 | + | GtkFont: "Adwaita Sans", | |
| 139 | 140 | DefaultFontSize: defaultFontSize, | |
| 140 | 141 | LightPalette: ColorPalette{ | |
| 141 | 142 | Background: "#fafafa", | |
| @@ -136,6 +136,7 @@ func DefaultConfig() *Config { | |||||
| 136 | SansFont: "Fira Sans", | 136 | SansFont: "Fira Sans", | ||
| 137 | SerifFont: "Fira Sans", | 137 | SerifFont: "Fira Sans", | ||
| 138 | MonospaceFont: "Fira Code", | 138 | MonospaceFont: "Fira Code", | ||
| 139 | + | GtkFont: "Adwaita Sans", | |||
| 139 | DefaultFontSize: defaultFontSize, | 140 | DefaultFontSize: defaultFontSize, | ||
| 140 | LightPalette: ColorPalette{ | 141 | LightPalette: ColorPalette{ | ||
| 141 | Background: "#fafafa", | 142 | Background: "#fafafa", | ||
M internal/infrastructure/config/loader.go +1 -0
| @@ -584,6 +584,7 @@ func (m *Manager) setAppearanceDefaults(defaults *Config) { | |||
| 584 | 584 | m.viper.SetDefault("appearance.sans_font", defaults.Appearance.SansFont) | |
| 585 | 585 | m.viper.SetDefault("appearance.serif_font", defaults.Appearance.SerifFont) | |
| 586 | 586 | m.viper.SetDefault("appearance.monospace_font", defaults.Appearance.MonospaceFont) | |
| 587 | + | m.viper.SetDefault("appearance.gtk_font", defaults.Appearance.GtkFont) | |
| 587 | 588 | m.viper.SetDefault("appearance.default_font_size", defaults.Appearance.DefaultFontSize) | |
| 588 | 589 | m.viper.SetDefault("appearance.light_palette", defaults.Appearance.LightPalette) | |
| 589 | 590 | m.viper.SetDefault("appearance.dark_palette", defaults.Appearance.DarkPalette) | |
| @@ -584,6 +584,7 @@ func (m *Manager) setAppearanceDefaults(defaults *Config) { | |||||
| 584 | m.viper.SetDefault("appearance.sans_font", defaults.Appearance.SansFont) | 584 | m.viper.SetDefault("appearance.sans_font", defaults.Appearance.SansFont) | ||
| 585 | m.viper.SetDefault("appearance.serif_font", defaults.Appearance.SerifFont) | 585 | m.viper.SetDefault("appearance.serif_font", defaults.Appearance.SerifFont) | ||
| 586 | m.viper.SetDefault("appearance.monospace_font", defaults.Appearance.MonospaceFont) | 586 | m.viper.SetDefault("appearance.monospace_font", defaults.Appearance.MonospaceFont) | ||
| 587 | + | m.viper.SetDefault("appearance.gtk_font", defaults.Appearance.GtkFont) | |||
| 587 | m.viper.SetDefault("appearance.default_font_size", defaults.Appearance.DefaultFontSize) | 588 | m.viper.SetDefault("appearance.default_font_size", defaults.Appearance.DefaultFontSize) | ||
| 588 | m.viper.SetDefault("appearance.light_palette", defaults.Appearance.LightPalette) | 589 | m.viper.SetDefault("appearance.light_palette", defaults.Appearance.LightPalette) | ||
| 589 | m.viper.SetDefault("appearance.dark_palette", defaults.Appearance.DarkPalette) | 590 | m.viper.SetDefault("appearance.dark_palette", defaults.Appearance.DarkPalette) | ||
M internal/infrastructure/config/schema_provider.go +7 -0
| @@ -138,6 +138,13 @@ func (*SchemaProvider) getAppearanceKeys(defaults *Config) []entity.ConfigKeyInf | |||
| 138 | 138 | Description: "Monospace font family for code", | |
| 139 | 139 | Section: SectionAppearance, | |
| 140 | 140 | }, | |
| 141 | + | { | |
| 142 | + | Key: "appearance.gtk_font", | |
| 143 | + | Type: "string", | |
| 144 | + | Default: defaults.Appearance.GtkFont, | |
| 145 | + | Description: "Native GTK UI font family for app chrome", | |
| 146 | + | Section: SectionAppearance, | |
| 147 | + | }, | |
| 141 | 148 | { | |
| 142 | 149 | Key: "default_webpage_zoom", | |
| 143 | 150 | Type: "float64", | |
| @@ -138,6 +138,13 @@ func (*SchemaProvider) getAppearanceKeys(defaults *Config) []entity.ConfigKeyInf | |||||
| 138 | Description: "Monospace font family for code", | 138 | Description: "Monospace font family for code", | ||
| 139 | Section: SectionAppearance, | 139 | Section: SectionAppearance, | ||
| 140 | }, | 140 | }, | ||
| 141 | + | { | |||
| 142 | + | Key: "appearance.gtk_font", | |||
| 143 | + | Type: "string", | |||
| 144 | + | Default: defaults.Appearance.GtkFont, | |||
| 145 | + | Description: "Native GTK UI font family for app chrome", | |||
| 146 | + | Section: SectionAppearance, | |||
| 147 | + | }, | |||
| 141 | { | 148 | { | ||
| 142 | Key: "default_webpage_zoom", | 149 | Key: "default_webpage_zoom", | ||
| 143 | Type: "float64", | 150 | Type: "float64", | ||
M internal/infrastructure/config/validation.go +1 -0
| @@ -72,6 +72,7 @@ func validateAppearance(config *Config) []string { | |||
| 72 | 72 | } | |
| 73 | 73 | validationErrors = append(validationErrors, domainvalidation.ValidateFontFamily("appearance.sans_font", config.Appearance.SansFont)...) | |
| 74 | 74 | validationErrors = append(validationErrors, domainvalidation.ValidateFontFamily("appearance.serif_font", config.Appearance.SerifFont)...) | |
| 75 | + | validationErrors = append(validationErrors, domainvalidation.ValidateFontFamily("appearance.gtk_font", config.Appearance.GtkFont)...) | |
| 75 | 76 | validationErrors = append( | |
| 76 | 77 | validationErrors, | |
| 77 | 78 | domainvalidation.ValidateFontFamily("appearance.monospace_font", config.Appearance.MonospaceFont)..., | |
| @@ -72,6 +72,7 @@ func validateAppearance(config *Config) []string { | |||||
| 72 | } | 72 | } | ||
| 73 | validationErrors = append(validationErrors, domainvalidation.ValidateFontFamily("appearance.sans_font", config.Appearance.SansFont)...) | 73 | validationErrors = append(validationErrors, domainvalidation.ValidateFontFamily("appearance.sans_font", config.Appearance.SansFont)...) | ||
| 74 | validationErrors = append(validationErrors, domainvalidation.ValidateFontFamily("appearance.serif_font", config.Appearance.SerifFont)...) | 74 | validationErrors = append(validationErrors, domainvalidation.ValidateFontFamily("appearance.serif_font", config.Appearance.SerifFont)...) | ||
| 75 | + | validationErrors = append(validationErrors, domainvalidation.ValidateFontFamily("appearance.gtk_font", config.Appearance.GtkFont)...) | |||
| 75 | validationErrors = append( | 76 | validationErrors = append( | ||
| 76 | validationErrors, | 77 | validationErrors, | ||
| 77 | domainvalidation.ValidateFontFamily("appearance.monospace_font", config.Appearance.MonospaceFont)..., | 78 | domainvalidation.ValidateFontFamily("appearance.monospace_font", config.Appearance.MonospaceFont)..., | ||
M internal/infrastructure/config/webui_gateway.go +1 -0
| @@ -43,6 +43,7 @@ func (g *WebUIConfigGateway) SaveWebUIConfig(ctx context.Context, cfg dto.WebUIC | |||
| 43 | 43 | current.Appearance.SansFont = cfg.Appearance.SansFont | |
| 44 | 44 | current.Appearance.SerifFont = cfg.Appearance.SerifFont | |
| 45 | 45 | current.Appearance.MonospaceFont = cfg.Appearance.MonospaceFont | |
| 46 | + | current.Appearance.GtkFont = cfg.Appearance.GtkFont | |
| 46 | 47 | current.Appearance.DefaultFontSize = cfg.Appearance.DefaultFontSize | |
| 47 | 48 | current.Appearance.ColorScheme = cfg.Appearance.ColorScheme | |
| 48 | 49 | current.Appearance.LightPalette = ColorPalette{ | |
| @@ -43,6 +43,7 @@ func (g *WebUIConfigGateway) SaveWebUIConfig(ctx context.Context, cfg dto.WebUIC | |||||
| 43 | current.Appearance.SansFont = cfg.Appearance.SansFont | 43 | current.Appearance.SansFont = cfg.Appearance.SansFont | ||
| 44 | current.Appearance.SerifFont = cfg.Appearance.SerifFont | 44 | current.Appearance.SerifFont = cfg.Appearance.SerifFont | ||
| 45 | current.Appearance.MonospaceFont = cfg.Appearance.MonospaceFont | 45 | current.Appearance.MonospaceFont = cfg.Appearance.MonospaceFont | ||
| 46 | + | current.Appearance.GtkFont = cfg.Appearance.GtkFont | |||
| 46 | current.Appearance.DefaultFontSize = cfg.Appearance.DefaultFontSize | 47 | current.Appearance.DefaultFontSize = cfg.Appearance.DefaultFontSize | ||
| 47 | current.Appearance.ColorScheme = cfg.Appearance.ColorScheme | 48 | current.Appearance.ColorScheme = cfg.Appearance.ColorScheme | ||
| 48 | current.Appearance.LightPalette = ColorPalette{ | 49 | current.Appearance.LightPalette = ColorPalette{ | ||
M internal/infrastructure/config/webui_payload.go +1 -0
| @@ -53,6 +53,7 @@ func buildSystemviewAppearancePayload(appearance AppearanceConfig) dto.WebUIAppe | |||
| 53 | 53 | SansFont: appearance.SansFont, | |
| 54 | 54 | SerifFont: appearance.SerifFont, | |
| 55 | 55 | MonospaceFont: appearance.MonospaceFont, | |
| 56 | + | GtkFont: appearance.GtkFont, | |
| 56 | 57 | DefaultFontSize: appearance.DefaultFontSize, | |
| 57 | 58 | ColorScheme: appearance.ColorScheme, | |
| 58 | 59 | LightPalette: buildSystemviewPalettePayload(appearance.LightPalette, defaults.LightPalette), | |
| @@ -53,6 +53,7 @@ func buildSystemviewAppearancePayload(appearance AppearanceConfig) dto.WebUIAppe | |||||
| 53 | SansFont: appearance.SansFont, | 53 | SansFont: appearance.SansFont, | ||
| 54 | SerifFont: appearance.SerifFont, | 54 | SerifFont: appearance.SerifFont, | ||
| 55 | MonospaceFont: appearance.MonospaceFont, | 55 | MonospaceFont: appearance.MonospaceFont, | ||
| 56 | + | GtkFont: appearance.GtkFont, | |||
| 56 | DefaultFontSize: appearance.DefaultFontSize, | 57 | DefaultFontSize: appearance.DefaultFontSize, | ||
| 57 | ColorScheme: appearance.ColorScheme, | 58 | ColorScheme: appearance.ColorScheme, | ||
| 58 | LightPalette: buildSystemviewPalettePayload(appearance.LightPalette, defaults.LightPalette), | 59 | LightPalette: buildSystemviewPalettePayload(appearance.LightPalette, defaults.LightPalette), | ||
M internal/ui/systemviews/config.templ +1 -0
| @@ -31,6 +31,7 @@ templ AppearanceForm(config dto.SystemviewConfigPayload) { | |||
| 31 | 31 | <label><span>Sans font</span><input name="sans_font" type="text" value={ config.Appearance.SansFont } required/></label> | |
| 32 | 32 | <label><span>Serif font</span><input name="serif_font" type="text" value={ config.Appearance.SerifFont } required/></label> | |
| 33 | 33 | <label><span>Monospace font</span><input name="monospace_font" type="text" value={ config.Appearance.MonospaceFont } required/></label> | |
| 34 | + | <label><span>GTK UI font</span><input name="gtk_font" type="text" value={ config.Appearance.GtkFont } required/></label> | |
| 34 | 35 | <label><span>Default font size</span><input name="default_font_size" type="number" min="1" max="72" value={ formatConfigInt(config.Appearance.DefaultFontSize) } required/></label> | |
| 35 | 36 | <label><span>UI scale</span><input name="default_ui_scale" type="number" min="0.5" max="3" step="0.05" value={ formatConfigFloat(config.DefaultUIScale) } required/></label> | |
| 36 | 37 | <label><span>Color scheme</span><select name="color_scheme"> | |
| @@ -31,6 +31,7 @@ templ AppearanceForm(config dto.SystemviewConfigPayload) { | |||||
| 31 | <label><span>Sans font</span><input name="sans_font" type="text" value={ config.Appearance.SansFont } required/></label> | 31 | <label><span>Sans font</span><input name="sans_font" type="text" value={ config.Appearance.SansFont } required/></label> | ||
| 32 | <label><span>Serif font</span><input name="serif_font" type="text" value={ config.Appearance.SerifFont } required/></label> | 32 | <label><span>Serif font</span><input name="serif_font" type="text" value={ config.Appearance.SerifFont } required/></label> | ||
| 33 | <label><span>Monospace font</span><input name="monospace_font" type="text" value={ config.Appearance.MonospaceFont } required/></label> | 33 | <label><span>Monospace font</span><input name="monospace_font" type="text" value={ config.Appearance.MonospaceFont } required/></label> | ||
| 34 | + | <label><span>GTK UI font</span><input name="gtk_font" type="text" value={ config.Appearance.GtkFont } required/></label> | |||
| 34 | <label><span>Default font size</span><input name="default_font_size" type="number" min="1" max="72" value={ formatConfigInt(config.Appearance.DefaultFontSize) } required/></label> | 35 | <label><span>Default font size</span><input name="default_font_size" type="number" min="1" max="72" value={ formatConfigInt(config.Appearance.DefaultFontSize) } required/></label> | ||
| 35 | <label><span>UI scale</span><input name="default_ui_scale" type="number" min="0.5" max="3" step="0.05" value={ formatConfigFloat(config.DefaultUIScale) } required/></label> | 36 | <label><span>UI scale</span><input name="default_ui_scale" type="number" min="0.5" max="3" step="0.05" value={ formatConfigFloat(config.DefaultUIScale) } required/></label> | ||
| 36 | <label><span>Color scheme</span><select name="color_scheme"> | 37 | <label><span>Color scheme</span><select name="color_scheme"> | ||
M internal/ui/systemviews/config_actions.go +1 -0
| @@ -99,6 +99,7 @@ func (a *App) saveAppearanceConfig(ctx context.Context, data map[string]string) | |||
| 99 | 99 | cfg.Appearance.SansFont = strings.TrimSpace(data["sans_font"]) | |
| 100 | 100 | cfg.Appearance.SerifFont = strings.TrimSpace(data["serif_font"]) | |
| 101 | 101 | cfg.Appearance.MonospaceFont = strings.TrimSpace(data["monospace_font"]) | |
| 102 | + | cfg.Appearance.GtkFont = strings.TrimSpace(data["gtk_font"]) | |
| 102 | 103 | cfg.Appearance.DefaultFontSize = fontSize | |
| 103 | 104 | cfg.Appearance.ColorScheme = strings.TrimSpace(data["color_scheme"]) | |
| 104 | 105 | cfg.Appearance.LightPalette = paletteFromForm(data, "light") | |
| @@ -99,6 +99,7 @@ func (a *App) saveAppearanceConfig(ctx context.Context, data map[string]string) | |||||
| 99 | cfg.Appearance.SansFont = strings.TrimSpace(data["sans_font"]) | 99 | cfg.Appearance.SansFont = strings.TrimSpace(data["sans_font"]) | ||
| 100 | cfg.Appearance.SerifFont = strings.TrimSpace(data["serif_font"]) | 100 | cfg.Appearance.SerifFont = strings.TrimSpace(data["serif_font"]) | ||
| 101 | cfg.Appearance.MonospaceFont = strings.TrimSpace(data["monospace_font"]) | 101 | cfg.Appearance.MonospaceFont = strings.TrimSpace(data["monospace_font"]) | ||
| 102 | + | cfg.Appearance.GtkFont = strings.TrimSpace(data["gtk_font"]) | |||
| 102 | cfg.Appearance.DefaultFontSize = fontSize | 103 | cfg.Appearance.DefaultFontSize = fontSize | ||
| 103 | cfg.Appearance.ColorScheme = strings.TrimSpace(data["color_scheme"]) | 104 | cfg.Appearance.ColorScheme = strings.TrimSpace(data["color_scheme"]) | ||
| 104 | cfg.Appearance.LightPalette = paletteFromForm(data, "light") | 105 | cfg.Appearance.LightPalette = paletteFromForm(data, "light") | ||
M internal/ui/systemviews/config_templ.go +304 -291 generated file: collapsed
M internal/ui/systemviews/theme.go +1 -0
| @@ -184,6 +184,7 @@ func isZeroAppearance(appearance dto.WebUIAppearanceConfig) bool { | |||
| 184 | 184 | appearance.SansFont == "" && | |
| 185 | 185 | appearance.SerifFont == "" && | |
| 186 | 186 | appearance.MonospaceFont == "" && | |
| 187 | + | appearance.GtkFont == "" && | |
| 187 | 188 | appearance.DefaultFontSize == 0 && | |
| 188 | 189 | appearance.LightPalette == (dto.ColorPalette{}) && | |
| 189 | 190 | appearance.DarkPalette == (dto.ColorPalette{}) | |
| @@ -184,6 +184,7 @@ func isZeroAppearance(appearance dto.WebUIAppearanceConfig) bool { | |||||
| 184 | appearance.SansFont == "" && | 184 | appearance.SansFont == "" && | ||
| 185 | appearance.SerifFont == "" && | 185 | appearance.SerifFont == "" && | ||
| 186 | appearance.MonospaceFont == "" && | 186 | appearance.MonospaceFont == "" && | ||
| 187 | + | appearance.GtkFont == "" && | |||
| 187 | appearance.DefaultFontSize == 0 && | 188 | appearance.DefaultFontSize == 0 && | ||
| 188 | appearance.LightPalette == (dto.ColorPalette{}) && | 189 | appearance.LightPalette == (dto.ColorPalette{}) && | ||
| 189 | appearance.DarkPalette == (dto.ColorPalette{}) | 190 | appearance.DarkPalette == (dto.ColorPalette{}) | ||
M internal/ui/theme/css.go +5 -1
| @@ -21,6 +21,7 @@ func GenerateCSSWithScale(p Palette, scale float64) string { | |||
| 21 | 21 | type FontConfig struct { | |
| 22 | 22 | SansFont string | |
| 23 | 23 | MonospaceFont string | |
| 24 | + | GtkFont string | |
| 24 | 25 | } | |
| 25 | 26 | ||
| 26 | 27 | // DefaultFontConfig returns safe font fallbacks. | |
| @@ -28,6 +29,7 @@ func DefaultFontConfig() FontConfig { | |||
| 28 | 29 | return FontConfig{ | |
| 29 | 30 | SansFont: "sans-serif", | |
| 30 | 31 | MonospaceFont: "monospace", | |
| 32 | + | GtkFont: DefaultGTKFont(), | |
| 31 | 33 | } | |
| 32 | 34 | } | |
| 33 | 35 | ||
| @@ -37,13 +39,14 @@ func FontCSSVars(fonts FontConfig) string { | |||
| 37 | 39 | // Quote the configured family; include generic fallback. | |
| 38 | 40 | fmt.Fprintf(&sb, " --font-sans: %q, sans-serif;\n", fonts.SansFont) | |
| 39 | 41 | fmt.Fprintf(&sb, " --font-mono: %q, monospace;\n", fonts.MonospaceFont) | |
| 42 | + | fmt.Fprintf(&sb, " --font-gtk: %q, sans-serif;\n", fonts.GtkFont) | |
| 40 | 43 | return sb.String() | |
| 41 | 44 | } | |
| 42 | 45 | ||
| 43 | 46 | func generateFontCSS() string { | |
| 44 | 47 | return `/* Font styling */ | |
| 45 | 48 | window, tooltip, popover { | |
| 46 | - | font-family: var(--font-sans); | |
| 49 | + | font-family: var(--font-gtk); | |
| 47 | 50 | } | |
| 48 | 51 | ` | |
| 49 | 52 | } | |
| @@ -62,6 +65,7 @@ func GenerateCSSFull(p Palette, _ float64, fonts FontConfig, modeColors ModeColo | |||
| 62 | 65 | fonts = FontConfig{ | |
| 63 | 66 | SansFont: Coalesce(fonts.SansFont, defaults.SansFont), | |
| 64 | 67 | MonospaceFont: Coalesce(fonts.MonospaceFont, defaults.MonospaceFont), | |
| 68 | + | GtkFont: Coalesce(fonts.GtkFont, defaults.GtkFont), | |
| 65 | 69 | } | |
| 66 | 70 | ||
| 67 | 71 | var sb strings.Builder | |
| @@ -21,6 +21,7 @@ func GenerateCSSWithScale(p Palette, scale float64) string { | |||||
| 21 | type FontConfig struct { | 21 | type FontConfig struct { | ||
| 22 | SansFont string | 22 | SansFont string | ||
| 23 | MonospaceFont string | 23 | MonospaceFont string | ||
| 24 | + | GtkFont string | |||
| 24 | } | 25 | } | ||
| 25 | 26 | ||||
| 26 | // DefaultFontConfig returns safe font fallbacks. | 27 | // DefaultFontConfig returns safe font fallbacks. | ||
| @@ -28,6 +29,7 @@ func DefaultFontConfig() FontConfig { | |||||
| 28 | return FontConfig{ | 29 | return FontConfig{ | ||
| 29 | SansFont: "sans-serif", | 30 | SansFont: "sans-serif", | ||
| 30 | MonospaceFont: "monospace", | 31 | MonospaceFont: "monospace", | ||
| 32 | + | GtkFont: DefaultGTKFont(), | |||
| 31 | } | 33 | } | ||
| 32 | } | 34 | } | ||
| 33 | 35 | ||||
| @@ -37,13 +39,14 @@ func FontCSSVars(fonts FontConfig) string { | |||||
| 37 | // Quote the configured family; include generic fallback. | 39 | // Quote the configured family; include generic fallback. | ||
| 38 | fmt.Fprintf(&sb, " --font-sans: %q, sans-serif;\n", fonts.SansFont) | 40 | fmt.Fprintf(&sb, " --font-sans: %q, sans-serif;\n", fonts.SansFont) | ||
| 39 | fmt.Fprintf(&sb, " --font-mono: %q, monospace;\n", fonts.MonospaceFont) | 41 | fmt.Fprintf(&sb, " --font-mono: %q, monospace;\n", fonts.MonospaceFont) | ||
| 42 | + | fmt.Fprintf(&sb, " --font-gtk: %q, sans-serif;\n", fonts.GtkFont) | |||
| 40 | return sb.String() | 43 | return sb.String() | ||
| 41 | } | 44 | } | ||
| 42 | 45 | ||||
| 43 | func generateFontCSS() string { | 46 | func generateFontCSS() string { | ||
| 44 | return `/* Font styling */ | 47 | return `/* Font styling */ | ||
| 45 | window, tooltip, popover { | 48 | window, tooltip, popover { | ||
| 46 | - | font-family: var(--font-sans); | 49 | + | font-family: var(--font-gtk); |
| 47 | } | 50 | } | ||
| 48 | ` | 51 | ` | ||
| 49 | } | 52 | } | ||
| @@ -62,6 +65,7 @@ func GenerateCSSFull(p Palette, _ float64, fonts FontConfig, modeColors ModeColo | |||||
| 62 | fonts = FontConfig{ | 65 | fonts = FontConfig{ | ||
| 63 | SansFont: Coalesce(fonts.SansFont, defaults.SansFont), | 66 | SansFont: Coalesce(fonts.SansFont, defaults.SansFont), | ||
| 64 | MonospaceFont: Coalesce(fonts.MonospaceFont, defaults.MonospaceFont), | 67 | MonospaceFont: Coalesce(fonts.MonospaceFont, defaults.MonospaceFont), | ||
| 68 | + | GtkFont: Coalesce(fonts.GtkFont, defaults.GtkFont), | |||
| 65 | } | 69 | } | ||
| 66 | 70 | ||||
| 67 | var sb strings.Builder | 71 | var sb strings.Builder | ||
M internal/ui/theme/css_test.go +14 -0
| @@ -8,6 +8,20 @@ import ( | |||
| 8 | 8 | "github.com/stretchr/testify/assert" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | + | func TestGenerateCSS_UsesGTKFontForNativeWidgets(t *testing.T) { | |
| 12 | + | css := GenerateCSSWithScaleAndFonts(DefaultDarkPalette(), 1.0, FontConfig{ | |
| 13 | + | SansFont: "Fira Sans", | |
| 14 | + | MonospaceFont: "Fira Code", | |
| 15 | + | GtkFont: "Adwaita Sans", | |
| 16 | + | }) | |
| 17 | + | ||
| 18 | + | assert.Contains(t, css, `--font-sans: "Fira Sans", sans-serif;`) | |
| 19 | + | assert.Contains(t, css, `--font-mono: "Fira Code", monospace;`) | |
| 20 | + | assert.Contains(t, css, `--font-gtk: "Adwaita Sans", sans-serif;`) | |
| 21 | + | assert.Contains(t, css, "window, tooltip, popover {\n\tfont-family: var(--font-gtk);\n}") | |
| 22 | + | assert.NotContains(t, css, "window, tooltip, popover {\n\tfont-family: var(--font-sans);\n}") | |
| 23 | + | } | |
| 24 | + | ||
| 11 | 25 | func TestGenerateCSS_StandaloneOmniboxWindowUsesTransparentBackground(t *testing.T) { | |
| 12 | 26 | css := GenerateCSS(DefaultDarkPalette()) | |
| 13 | 27 | ||
| @@ -8,6 +8,20 @@ import ( | |||||
| 8 | "github.com/stretchr/testify/assert" | 8 | "github.com/stretchr/testify/assert" | ||
| 9 | ) | 9 | ) | ||
| 10 | 10 | ||||
| 11 | + | func TestGenerateCSS_UsesGTKFontForNativeWidgets(t *testing.T) { | |||
| 12 | + | css := GenerateCSSWithScaleAndFonts(DefaultDarkPalette(), 1.0, FontConfig{ | |||
| 13 | + | SansFont: "Fira Sans", | |||
| 14 | + | MonospaceFont: "Fira Code", | |||
| 15 | + | GtkFont: "Adwaita Sans", | |||
| 16 | + | }) | |||
| 17 | + | ||||
| 18 | + | assert.Contains(t, css, `--font-sans: "Fira Sans", sans-serif;`) | |||
| 19 | + | assert.Contains(t, css, `--font-mono: "Fira Code", monospace;`) | |||
| 20 | + | assert.Contains(t, css, `--font-gtk: "Adwaita Sans", sans-serif;`) | |||
| 21 | + | assert.Contains(t, css, "window, tooltip, popover {\n\tfont-family: var(--font-gtk);\n}") | |||
| 22 | + | assert.NotContains(t, css, "window, tooltip, popover {\n\tfont-family: var(--font-sans);\n}") | |||
| 23 | + | } | |||
| 24 | + | ||||
| 11 | func TestGenerateCSS_StandaloneOmniboxWindowUsesTransparentBackground(t *testing.T) { | 25 | func TestGenerateCSS_StandaloneOmniboxWindowUsesTransparentBackground(t *testing.T) { | ||
| 12 | css := GenerateCSS(DefaultDarkPalette()) | 26 | css := GenerateCSS(DefaultDarkPalette()) | ||
| 13 | 27 | ||||
M internal/ui/theme/manager.go +13 -2
| @@ -21,6 +21,7 @@ type Manager struct { | |||
| 21 | 21 | darkPalette Palette // Dark theme colors | |
| 22 | 22 | uiScale float64 // UI scaling factor (1.0 = 100%) | |
| 23 | 23 | fonts FontConfig | |
| 24 | + | gtkFont string | |
| 24 | 25 | modeColors ModeColors // Modal mode indicator colors | |
| 25 | 26 | cssProvider *gtk.CssProvider | |
| 26 | 27 | appliedFont string | |
| @@ -68,9 +69,12 @@ func NewManager( | |||
| 68 | 69 | } | |
| 69 | 70 | ||
| 70 | 71 | fonts := DefaultFontConfig() | |
| 72 | + | gtkFont := DefaultGTKFont() | |
| 71 | 73 | if appearance != nil { | |
| 72 | 74 | fonts.SansFont = Coalesce(appearance.SansFont, fonts.SansFont) | |
| 73 | 75 | fonts.MonospaceFont = Coalesce(appearance.MonospaceFont, fonts.MonospaceFont) | |
| 76 | + | gtkFont = Coalesce(appearance.GtkFont, gtkFont) | |
| 77 | + | fonts.GtkFont = gtkFont | |
| 74 | 78 | } | |
| 75 | 79 | ||
| 76 | 80 | // Build mode colors from config | |
| @@ -82,6 +86,7 @@ func NewManager( | |||
| 82 | 86 | Float64("ui_scale", uiScale). | |
| 83 | 87 | Str("sans_font", fonts.SansFont). | |
| 84 | 88 | Str("monospace_font", fonts.MonospaceFont). | |
| 89 | + | Str("gtk_font", gtkFont). | |
| 85 | 90 | Msg("theme manager initialized") | |
| 86 | 91 | ||
| 87 | 92 | return &Manager{ | |
| @@ -91,6 +96,7 @@ func NewManager( | |||
| 91 | 96 | darkPalette: darkPalette, | |
| 92 | 97 | uiScale: uiScale, | |
| 93 | 98 | fonts: fonts, | |
| 99 | + | gtkFont: gtkFont, | |
| 94 | 100 | modeColors: modeColors, | |
| 95 | 101 | colorResolver: resolver, | |
| 96 | 102 | } | |
| @@ -155,10 +161,12 @@ func (m *Manager) GetWebUIThemeCSS() string { | |||
| 155 | 161 | ||
| 156 | 162 | const gtkDefaultFontPointSize = 11 | |
| 157 | 163 | ||
| 164 | + | func DefaultGTKFont() string { return "Adwaita Sans" } | |
| 165 | + | ||
| 158 | 166 | func formatGTKFontName(family string, uiScale float64) string { | |
| 159 | 167 | family = strings.TrimSpace(family) | |
| 160 | 168 | if family == "" { | |
| 161 | - | family = DefaultFontConfig().SansFont | |
| 169 | + | family = DefaultGTKFont() | |
| 162 | 170 | } | |
| 163 | 171 | if uiScale <= 0 { | |
| 164 | 172 | uiScale = 1.0 | |
| @@ -186,7 +194,7 @@ func (m *Manager) ApplyToDisplay(ctx context.Context, display *gdk.Display) { | |||
| 186 | 194 | // Generate CSS with current palette, UI scale, fonts, and mode colors | |
| 187 | 195 | palette := m.GetCurrentPalette() | |
| 188 | 196 | css := GenerateCSSFull(palette, m.uiScale, m.fonts, m.modeColors) | |
| 189 | - | fontName := formatGTKFontName(m.fonts.SansFont, m.uiScale) | |
| 197 | + | fontName := formatGTKFontName(m.gtkFont, m.uiScale) | |
| 190 | 198 | ||
| 191 | 199 | settings := gtk.SettingsGetForDisplay(display) | |
| 192 | 200 | if settings == nil { | |
| @@ -279,9 +287,11 @@ func (m *Manager) UpdateFromConfig( | |||
| 279 | 287 | } | |
| 280 | 288 | ||
| 281 | 289 | defaults := DefaultFontConfig() | |
| 290 | + | m.gtkFont = Coalesce(appearance.GtkFont, DefaultGTKFont()) | |
| 282 | 291 | m.fonts = FontConfig{ | |
| 283 | 292 | SansFont: Coalesce(appearance.SansFont, defaults.SansFont), | |
| 284 | 293 | MonospaceFont: Coalesce(appearance.MonospaceFont, defaults.MonospaceFont), | |
| 294 | + | GtkFont: m.gtkFont, | |
| 285 | 295 | } | |
| 286 | 296 | ||
| 287 | 297 | // Update mode colors | |
| @@ -295,6 +305,7 @@ func (m *Manager) UpdateFromConfig( | |||
| 295 | 305 | Float64("ui_scale", m.uiScale). | |
| 296 | 306 | Str("sans_font", m.fonts.SansFont). | |
| 297 | 307 | Str("monospace_font", m.fonts.MonospaceFont). | |
| 308 | + | Str("gtk_font", m.gtkFont). | |
| 298 | 309 | Msg("theme manager updated from config") | |
| 299 | 310 | ||
| 300 | 311 | // Re-apply CSS if display is available | |
| @@ -21,6 +21,7 @@ type Manager struct { | |||||
| 21 | darkPalette Palette // Dark theme colors | 21 | darkPalette Palette // Dark theme colors | ||
| 22 | uiScale float64 // UI scaling factor (1.0 = 100%) | 22 | uiScale float64 // UI scaling factor (1.0 = 100%) | ||
| 23 | fonts FontConfig | 23 | fonts FontConfig | ||
| 24 | + | gtkFont string | |||
| 24 | modeColors ModeColors // Modal mode indicator colors | 25 | modeColors ModeColors // Modal mode indicator colors | ||
| 25 | cssProvider *gtk.CssProvider | 26 | cssProvider *gtk.CssProvider | ||
| 26 | appliedFont string | 27 | appliedFont string | ||
| @@ -68,9 +69,12 @@ func NewManager( | |||||
| 68 | } | 69 | } | ||
| 69 | 70 | ||||
| 70 | fonts := DefaultFontConfig() | 71 | fonts := DefaultFontConfig() | ||
| 72 | + | gtkFont := DefaultGTKFont() | |||
| 71 | if appearance != nil { | 73 | if appearance != nil { | ||
| 72 | fonts.SansFont = Coalesce(appearance.SansFont, fonts.SansFont) | 74 | fonts.SansFont = Coalesce(appearance.SansFont, fonts.SansFont) | ||
| 73 | fonts.MonospaceFont = Coalesce(appearance.MonospaceFont, fonts.MonospaceFont) | 75 | fonts.MonospaceFont = Coalesce(appearance.MonospaceFont, fonts.MonospaceFont) | ||
| 76 | + | gtkFont = Coalesce(appearance.GtkFont, gtkFont) | |||
| 77 | + | fonts.GtkFont = gtkFont | |||
| 74 | } | 78 | } | ||
| 75 | 79 | ||||
| 76 | // Build mode colors from config | 80 | // Build mode colors from config | ||
| @@ -82,6 +86,7 @@ func NewManager( | |||||
| 82 | Float64("ui_scale", uiScale). | 86 | Float64("ui_scale", uiScale). | ||
| 83 | Str("sans_font", fonts.SansFont). | 87 | Str("sans_font", fonts.SansFont). | ||
| 84 | Str("monospace_font", fonts.MonospaceFont). | 88 | Str("monospace_font", fonts.MonospaceFont). | ||
| 89 | + | Str("gtk_font", gtkFont). | |||
| 85 | Msg("theme manager initialized") | 90 | Msg("theme manager initialized") | ||
| 86 | 91 | ||||
| 87 | return &Manager{ | 92 | return &Manager{ | ||
| @@ -91,6 +96,7 @@ func NewManager( | |||||
| 91 | darkPalette: darkPalette, | 96 | darkPalette: darkPalette, | ||
| 92 | uiScale: uiScale, | 97 | uiScale: uiScale, | ||
| 93 | fonts: fonts, | 98 | fonts: fonts, | ||
| 99 | + | gtkFont: gtkFont, | |||
| 94 | modeColors: modeColors, | 100 | modeColors: modeColors, | ||
| 95 | colorResolver: resolver, | 101 | colorResolver: resolver, | ||
| 96 | } | 102 | } | ||
| @@ -155,10 +161,12 @@ func (m *Manager) GetWebUIThemeCSS() string { | |||||
| 155 | 161 | ||||
| 156 | const gtkDefaultFontPointSize = 11 | 162 | const gtkDefaultFontPointSize = 11 | ||
| 157 | 163 | ||||
| 164 | + | func DefaultGTKFont() string { return "Adwaita Sans" } | |||
| 165 | + | ||||
| 158 | func formatGTKFontName(family string, uiScale float64) string { | 166 | func formatGTKFontName(family string, uiScale float64) string { | ||
| 159 | family = strings.TrimSpace(family) | 167 | family = strings.TrimSpace(family) | ||
| 160 | if family == "" { | 168 | if family == "" { | ||
| 161 | - | family = DefaultFontConfig().SansFont | 169 | + | family = DefaultGTKFont() |
| 162 | } | 170 | } | ||
| 163 | if uiScale <= 0 { | 171 | if uiScale <= 0 { | ||
| 164 | uiScale = 1.0 | 172 | uiScale = 1.0 | ||
| @@ -186,7 +194,7 @@ func (m *Manager) ApplyToDisplay(ctx context.Context, display *gdk.Display) { | |||||
| 186 | // Generate CSS with current palette, UI scale, fonts, and mode colors | 194 | // Generate CSS with current palette, UI scale, fonts, and mode colors | ||
| 187 | palette := m.GetCurrentPalette() | 195 | palette := m.GetCurrentPalette() | ||
| 188 | css := GenerateCSSFull(palette, m.uiScale, m.fonts, m.modeColors) | 196 | css := GenerateCSSFull(palette, m.uiScale, m.fonts, m.modeColors) | ||
| 189 | - | fontName := formatGTKFontName(m.fonts.SansFont, m.uiScale) | 197 | + | fontName := formatGTKFontName(m.gtkFont, m.uiScale) |
| 190 | 198 | ||||
| 191 | settings := gtk.SettingsGetForDisplay(display) | 199 | settings := gtk.SettingsGetForDisplay(display) | ||
| 192 | if settings == nil { | 200 | if settings == nil { | ||
| @@ -279,9 +287,11 @@ func (m *Manager) UpdateFromConfig( | |||||
| 279 | } | 287 | } | ||
| 280 | 288 | ||||
| 281 | defaults := DefaultFontConfig() | 289 | defaults := DefaultFontConfig() | ||
| 290 | + | m.gtkFont = Coalesce(appearance.GtkFont, DefaultGTKFont()) | |||
| 282 | m.fonts = FontConfig{ | 291 | m.fonts = FontConfig{ | ||
| 283 | SansFont: Coalesce(appearance.SansFont, defaults.SansFont), | 292 | SansFont: Coalesce(appearance.SansFont, defaults.SansFont), | ||
| 284 | MonospaceFont: Coalesce(appearance.MonospaceFont, defaults.MonospaceFont), | 293 | MonospaceFont: Coalesce(appearance.MonospaceFont, defaults.MonospaceFont), | ||
| 294 | + | GtkFont: m.gtkFont, | |||
| 285 | } | 295 | } | ||
| 286 | 296 | ||||
| 287 | // Update mode colors | 297 | // Update mode colors | ||
| @@ -295,6 +305,7 @@ func (m *Manager) UpdateFromConfig( | |||||
| 295 | Float64("ui_scale", m.uiScale). | 305 | Float64("ui_scale", m.uiScale). | ||
| 296 | Str("sans_font", m.fonts.SansFont). | 306 | Str("sans_font", m.fonts.SansFont). | ||
| 297 | Str("monospace_font", m.fonts.MonospaceFont). | 307 | Str("monospace_font", m.fonts.MonospaceFont). | ||
| 308 | + | Str("gtk_font", m.gtkFont). | |||
| 298 | Msg("theme manager updated from config") | 309 | Msg("theme manager updated from config") | ||
| 299 | 310 | ||||
| 300 | // Re-apply CSS if display is available | 311 | // Re-apply CSS if display is available | ||
M internal/ui/theme/manager_test.go +8 -2
| @@ -276,6 +276,7 @@ func TestManager_Fonts(t *testing.T) { | |||
| 276 | 276 | appearance := &entity.AppearanceConfig{ | |
| 277 | 277 | SansFont: "Inter", | |
| 278 | 278 | MonospaceFont: "JetBrains Mono", | |
| 279 | + | GtkFont: "Adwaita Sans", | |
| 279 | 280 | } | |
| 280 | 281 | ||
| 281 | 282 | mockResolver := mocks.NewMockColorSchemeResolver(t) | |
| @@ -314,13 +315,18 @@ func TestManager_ModeColors(t *testing.T) { | |||
| 314 | 315 | assert.Equal(t, "#ffff00", modeColors.ResizeMode) | |
| 315 | 316 | } | |
| 316 | 317 | ||
| 317 | - | func TestFormatGTKFontName_UsesScaledConfiguredSansFont(t *testing.T) { | |
| 318 | - | assert.Equal(t, "Fira Sans 14", formatGTKFontName("Fira Sans", 1.3)) | |
| 318 | + | func TestDefaultGTKFont(t *testing.T) { | |
| 319 | + | assert.Equal(t, "Adwaita Sans", DefaultGTKFont()) | |
| 320 | + | } | |
| 321 | + | ||
| 322 | + | func TestFormatGTKFontName_UsesScaledConfiguredGTKFont(t *testing.T) { | |
| 323 | + | assert.Equal(t, "Adwaita Sans 14", formatGTKFontName("Adwaita Sans", 1.3)) | |
| 319 | 324 | } | |
| 320 | 325 | ||
| 321 | 326 | func TestFormatGTKFontName_DefaultsScaleAndRoundsToNearestPoint(t *testing.T) { | |
| 322 | 327 | assert.Equal(t, "Inter 11", formatGTKFontName("Inter", 0)) | |
| 323 | 328 | assert.Equal(t, "Inter 12", formatGTKFontName("Inter", 1.05)) | |
| 329 | + | assert.Equal(t, "Adwaita Sans 11", formatGTKFontName("", 1.0)) | |
| 324 | 330 | } | |
| 325 | 331 | ||
| 326 | 332 | func TestShouldApplyGTKFontName(t *testing.T) { | |
| @@ -276,6 +276,7 @@ func TestManager_Fonts(t *testing.T) { | |||||
| 276 | appearance := &entity.AppearanceConfig{ | 276 | appearance := &entity.AppearanceConfig{ | ||
| 277 | SansFont: "Inter", | 277 | SansFont: "Inter", | ||
| 278 | MonospaceFont: "JetBrains Mono", | 278 | MonospaceFont: "JetBrains Mono", | ||
| 279 | + | GtkFont: "Adwaita Sans", | |||
| 279 | } | 280 | } | ||
| 280 | 281 | ||||
| 281 | mockResolver := mocks.NewMockColorSchemeResolver(t) | 282 | mockResolver := mocks.NewMockColorSchemeResolver(t) | ||
| @@ -314,13 +315,18 @@ func TestManager_ModeColors(t *testing.T) { | |||||
| 314 | assert.Equal(t, "#ffff00", modeColors.ResizeMode) | 315 | assert.Equal(t, "#ffff00", modeColors.ResizeMode) | ||
| 315 | } | 316 | } | ||
| 316 | 317 | ||||
| 317 | - | func TestFormatGTKFontName_UsesScaledConfiguredSansFont(t *testing.T) { | 318 | + | func TestDefaultGTKFont(t *testing.T) { |
| 318 | - | assert.Equal(t, "Fira Sans 14", formatGTKFontName("Fira Sans", 1.3)) | 319 | + | assert.Equal(t, "Adwaita Sans", DefaultGTKFont()) |
| 320 | + | } | |||
| 321 | + | ||||
| 322 | + | func TestFormatGTKFontName_UsesScaledConfiguredGTKFont(t *testing.T) { | |||
| 323 | + | assert.Equal(t, "Adwaita Sans 14", formatGTKFontName("Adwaita Sans", 1.3)) | |||
| 319 | } | 324 | } | ||
| 320 | 325 | ||||
| 321 | func TestFormatGTKFontName_DefaultsScaleAndRoundsToNearestPoint(t *testing.T) { | 326 | func TestFormatGTKFontName_DefaultsScaleAndRoundsToNearestPoint(t *testing.T) { | ||
| 322 | assert.Equal(t, "Inter 11", formatGTKFontName("Inter", 0)) | 327 | assert.Equal(t, "Inter 11", formatGTKFontName("Inter", 0)) | ||
| 323 | assert.Equal(t, "Inter 12", formatGTKFontName("Inter", 1.05)) | 328 | assert.Equal(t, "Inter 12", formatGTKFontName("Inter", 1.05)) | ||
| 329 | + | assert.Equal(t, "Adwaita Sans 11", formatGTKFontName("", 1.0)) | |||
| 324 | } | 330 | } | ||
| 325 | 331 | ||||
| 326 | func TestShouldApplyGTKFontName(t *testing.T) { | 332 | func TestShouldApplyGTKFontName(t *testing.T) { | ||