Make the branded GoSentry theme the default.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -74,7 +74,7 @@ portable application: moving the program folder also moves its configuration.
|
|||||||
"execution_mode": "parallel",
|
"execution_mode": "parallel",
|
||||||
"overlap_policy": "skip",
|
"overlap_policy": "skip",
|
||||||
"default_timeout_seconds": 0,
|
"default_timeout_seconds": 0,
|
||||||
"theme": "default",
|
"theme": "gosentry",
|
||||||
"job_list_view": "detailed"
|
"job_list_view": "detailed"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ dragged.**
|
|||||||
|
|
||||||
**Settings:**
|
**Settings:**
|
||||||
|
|
||||||
|
- **The branded GoSentry theme is now the default.** Fresh installs, the
|
||||||
|
**Defaults** button, and configs that omit `theme` all open in the teal/amber
|
||||||
|
look; users who prefer Fyne's built-in theme can still pick **Default** in
|
||||||
|
Settings.
|
||||||
- The **Save / Cancel / Restore defaults** row sits 4 px from the left edge, as
|
- The **Save / Cancel / Restore defaults** row sits 4 px from the left edge, as
|
||||||
its layout always intended, rather than 8.
|
its layout always intended, rather than 8.
|
||||||
- The caption column is as wide as the widest caption instead of a fixed width,
|
- The caption column is as wide as the widest caption instead of a fixed width,
|
||||||
|
|||||||
+1
-1
@@ -514,7 +514,7 @@ Tests the branded theme and the stored theme choice.
|
|||||||
|------|---------|
|
|------|---------|
|
||||||
| `TestGoSentryThemeBrandColors` | Verifies the brand colors land on the semantically correct `ColorName`s in both the light and dark variants. |
|
| `TestGoSentryThemeBrandColors` | Verifies the brand colors land on the semantically correct `ColorName`s in both the light and dark variants. |
|
||||||
| `TestGoSentryThemeDelegatesUnbrandedColors` | Verifies unbranded color names fall through to the base theme rather than rendering transparent. |
|
| `TestGoSentryThemeDelegatesUnbrandedColors` | Verifies unbranded color names fall through to the base theme rather than rendering transparent. |
|
||||||
| `TestThemeForChoice` | Verifies the GoSentry choice yields the branded primary and every other value — including the empty legacy one — yields the default theme. |
|
| `TestThemeForChoice` | Verifies the GoSentry choice and the empty legacy value yield the branded primary; only the explicit default choice yields Fyne's built-in theme. |
|
||||||
| `TestThemeLabelRoundTrip` | Verifies the dropdown labels round-trip and that the empty value maps to the Default label rather than a blank option. |
|
| `TestThemeLabelRoundTrip` | Verifies the dropdown labels round-trip and that the empty value maps to the Default label rather than a blank option. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -481,8 +481,8 @@ func validateConfig(config domain.Config) error {
|
|||||||
if config.DefaultTimeoutSeconds < 0 {
|
if config.DefaultTimeoutSeconds < 0 {
|
||||||
return errors.New("default timeout must not be negative (0 means no timeout)")
|
return errors.New("default timeout must not be negative (0 means no timeout)")
|
||||||
}
|
}
|
||||||
// Empty Theme is accepted and normalized to the default on load, so older
|
// Empty Theme is accepted and normalized to the branded theme on load, so
|
||||||
// configs (and hand-built ones) stay valid without an explicit theme.
|
// older configs (and hand-built ones) stay valid without an explicit theme.
|
||||||
if config.Theme != "" && config.Theme != domain.ThemeDefault && config.Theme != domain.ThemeGoSentry {
|
if config.Theme != "" && config.Theme != domain.ThemeDefault && config.Theme != domain.ThemeGoSentry {
|
||||||
return errors.New("theme must be 'default' or 'gosentry'")
|
return errors.New("theme must be 'default' or 'gosentry'")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ type Config struct {
|
|||||||
// omitempty would hide a deliberate choice from the hand-editable config.
|
// omitempty would hide a deliberate choice from the hand-editable config.
|
||||||
DefaultTimeoutSeconds int `json:"default_timeout_seconds"`
|
DefaultTimeoutSeconds int `json:"default_timeout_seconds"`
|
||||||
Paused bool `json:"paused,omitempty"`
|
Paused bool `json:"paused,omitempty"`
|
||||||
// Theme selects the visual appearance. Empty is treated as ThemeDefault so
|
// Theme selects the visual appearance. Empty is treated as ThemeGoSentry so
|
||||||
// configs written before this field existed keep the original look.
|
// configs written before this field existed pick up the branded look.
|
||||||
Theme Theme `json:"theme,omitempty"`
|
Theme Theme `json:"theme,omitempty"`
|
||||||
// JobListView selects the Jobs list density. Empty is treated as
|
// JobListView selects the Jobs list density. Empty is treated as
|
||||||
// JobListViewDetailed so configs written before this field existed keep the
|
// JobListViewDetailed so configs written before this field existed keep the
|
||||||
@@ -110,7 +110,7 @@ func DefaultConfig() Config {
|
|||||||
NotifyOnFailure: true,
|
NotifyOnFailure: true,
|
||||||
ExecutionMode: ExecutionModeParallel,
|
ExecutionMode: ExecutionModeParallel,
|
||||||
OverlapPolicy: OverlapPolicySkip,
|
OverlapPolicy: OverlapPolicySkip,
|
||||||
Theme: ThemeDefault,
|
Theme: ThemeGoSentry,
|
||||||
JobListView: JobListViewDetailed,
|
JobListView: JobListViewDetailed,
|
||||||
DefaultTimeoutSeconds: 0,
|
DefaultTimeoutSeconds: 0,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ func loadOrCreateConfig(paths Paths) (domain.Config, error) {
|
|||||||
// the setting impossible to persist. Negative values are rejected by
|
// the setting impossible to persist. Negative values are rejected by
|
||||||
// app.validateConfig before they can be saved.
|
// app.validateConfig before they can be saved.
|
||||||
if config.Theme == "" {
|
if config.Theme == "" {
|
||||||
config.Theme = domain.ThemeDefault
|
config.Theme = domain.ThemeGoSentry
|
||||||
}
|
}
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,8 +171,8 @@ func TestLoadOrCreateConfigCreatesDefaultsOnFirstRun(t *testing.T) {
|
|||||||
if got.DefaultTimeoutSeconds != 0 {
|
if got.DefaultTimeoutSeconds != 0 {
|
||||||
t.Errorf("default DefaultTimeoutSeconds = %d, want 0 (no timeout)", got.DefaultTimeoutSeconds)
|
t.Errorf("default DefaultTimeoutSeconds = %d, want 0 (no timeout)", got.DefaultTimeoutSeconds)
|
||||||
}
|
}
|
||||||
if got.Theme != domain.ThemeDefault {
|
if got.Theme != domain.ThemeGoSentry {
|
||||||
t.Errorf("default Theme = %q, want %q", got.Theme, domain.ThemeDefault)
|
t.Errorf("default Theme = %q, want %q", got.Theme, domain.ThemeGoSentry)
|
||||||
}
|
}
|
||||||
if got.JobListView != domain.JobListViewDetailed {
|
if got.JobListView != domain.JobListViewDetailed {
|
||||||
t.Errorf("default JobListView = %q, want %q", got.JobListView, domain.JobListViewDetailed)
|
t.Errorf("default JobListView = %q, want %q", got.JobListView, domain.JobListViewDetailed)
|
||||||
|
|||||||
@@ -263,10 +263,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func themeLabel(choice domain.Theme) string {
|
func themeLabel(choice domain.Theme) string {
|
||||||
if choice == domain.ThemeGoSentry {
|
if choice == domain.ThemeDefault {
|
||||||
return themeLabelGoSentry
|
return themeLabelDefault
|
||||||
}
|
}
|
||||||
return themeLabelDefault
|
return themeLabelGoSentry
|
||||||
}
|
}
|
||||||
|
|
||||||
func themeFromLabel(label string) domain.Theme {
|
func themeFromLabel(label string) domain.Theme {
|
||||||
|
|||||||
+6
-6
@@ -103,14 +103,14 @@ func (t gosentryTheme) Font(style fyne.TextStyle) fyne.Resource { return t.base.
|
|||||||
func (t gosentryTheme) Icon(name fyne.ThemeIconName) fyne.Resource { return t.base.Icon(name) }
|
func (t gosentryTheme) Icon(name fyne.ThemeIconName) fyne.Resource { return t.base.Icon(name) }
|
||||||
func (t gosentryTheme) Size(name fyne.ThemeSizeName) float32 { return t.base.Size(name) }
|
func (t gosentryTheme) Size(name fyne.ThemeSizeName) float32 { return t.base.Size(name) }
|
||||||
|
|
||||||
// themeFor maps a stored Theme choice to a concrete fyne.Theme. Anything other
|
// themeFor maps a stored Theme choice to a concrete fyne.Theme. Only the
|
||||||
// than the explicit GoSentry choice (including the empty/legacy value) keeps
|
// explicit default choice keeps Fyne's built-in theme; everything else
|
||||||
// Fyne's built-in theme.
|
// (including the empty/legacy value) uses the branded GoSentry theme.
|
||||||
func themeFor(choice domain.Theme) fyne.Theme {
|
func themeFor(choice domain.Theme) fyne.Theme {
|
||||||
if choice == domain.ThemeGoSentry {
|
if choice == domain.ThemeDefault {
|
||||||
return newGoSentryTheme()
|
return theme.DefaultTheme()
|
||||||
}
|
}
|
||||||
return theme.DefaultTheme()
|
return newGoSentryTheme()
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyTheme installs the theme for the given choice on the running app. Fyne
|
// applyTheme installs the theme for the given choice on the running app. Fyne
|
||||||
|
|||||||
+14
-14
@@ -54,24 +54,24 @@ func TestGoSentryThemeDelegatesUnbrandedColors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// themeFor maps the stored choice to the right theme: the GoSentry choice yields
|
// themeFor maps the stored choice to the right theme: the GoSentry choice and the
|
||||||
// the branded teal primary; every other value (including the empty legacy value)
|
// empty legacy value yield the branded teal primary; only the explicit default
|
||||||
// yields the default theme, whose primary is not the brand teal.
|
// choice yields Fyne's built-in theme.
|
||||||
func TestThemeForChoice(t *testing.T) {
|
func TestThemeForChoice(t *testing.T) {
|
||||||
gosentry := themeFor(domain.ThemeGoSentry)
|
for _, choice := range []domain.Theme{domain.ThemeGoSentry, ""} {
|
||||||
if got := gosentry.Color(theme.ColorNamePrimary, theme.VariantLight); got != brandTeal {
|
branded := themeFor(choice)
|
||||||
t.Errorf("themeFor(gosentry) primary = %v, want brand teal %v", got, brandTeal)
|
if got := branded.Color(theme.ColorNamePrimary, theme.VariantLight); got != brandTeal {
|
||||||
}
|
t.Errorf("themeFor(%q) primary = %v, want brand teal %v", choice, got, brandTeal)
|
||||||
for _, choice := range []domain.Theme{domain.ThemeDefault, ""} {
|
|
||||||
def := themeFor(choice)
|
|
||||||
if got := def.Color(theme.ColorNamePrimary, theme.VariantLight); got == brandTeal {
|
|
||||||
t.Errorf("themeFor(%q) should not use the brand teal primary", choice)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
def := themeFor(domain.ThemeDefault)
|
||||||
|
if got := def.Color(theme.ColorNamePrimary, theme.VariantLight); got == brandTeal {
|
||||||
|
t.Errorf("themeFor(default) should not use the brand teal primary")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The dropdown label helpers must round-trip, and the empty/legacy value must map
|
// The dropdown label helpers must round-trip, and the empty/legacy value must map
|
||||||
// to the Default label so the select never shows a blank option.
|
// to the GoSentry label so the select never shows a blank option.
|
||||||
func TestThemeLabelRoundTrip(t *testing.T) {
|
func TestThemeLabelRoundTrip(t *testing.T) {
|
||||||
if got := themeFromLabel(themeLabel(domain.ThemeGoSentry)); got != domain.ThemeGoSentry {
|
if got := themeFromLabel(themeLabel(domain.ThemeGoSentry)); got != domain.ThemeGoSentry {
|
||||||
t.Errorf("round-trip gosentry = %q", got)
|
t.Errorf("round-trip gosentry = %q", got)
|
||||||
@@ -79,7 +79,7 @@ func TestThemeLabelRoundTrip(t *testing.T) {
|
|||||||
if got := themeFromLabel(themeLabel(domain.ThemeDefault)); got != domain.ThemeDefault {
|
if got := themeFromLabel(themeLabel(domain.ThemeDefault)); got != domain.ThemeDefault {
|
||||||
t.Errorf("round-trip default = %q", got)
|
t.Errorf("round-trip default = %q", got)
|
||||||
}
|
}
|
||||||
if got := themeLabel(""); got != themeLabelDefault {
|
if got := themeLabel(""); got != themeLabelGoSentry {
|
||||||
t.Errorf("empty theme label = %q, want %q", got, themeLabelDefault)
|
t.Errorf("empty theme label = %q, want %q", got, themeLabelGoSentry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user