feat(config): add animations and animations.slowdown keys
- commit
- 506f1e553be33b938dea2681d3e2f0d116b48c18
- author
- brice · · 2026-10-04 15:38 UTC
- committer
- brice · · 2026-10-04 19:08 UTC
- parent
- ae51138
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +42 -1
M internal/adapters/config/config.go +14 -0
| @@ -169,6 +169,8 @@ func Defaults() ports.Config { | |||
| 169 | 169 | c.Touchpad.ScrollFactor = 1 | |
| 170 | 170 | c.Mouse.AccelProfile = ports.AccelAdaptive | |
| 171 | 171 | c.Cursor.HideAfter = 5 * time.Second | |
| 172 | + | c.Animations.On = true | |
| 173 | + | c.Animations.Slowdown = 1 | |
| 172 | 174 | c.Focus.Animation = ports.FocusAnimationPulse | |
| 173 | 175 | c.Focus.Effect = ports.FocusEffectScreen | |
| 174 | 176 | c.Focus.Strength = 0.04 | |
| @@ -749,6 +751,18 @@ func set(c *ports.Config, key, v string) error { | |||
| 749 | 751 | return fmt.Errorf("must be off or a duration between 100ms and %s, e.g. 5s", maxCursorHideAfter) | |
| 750 | 752 | } | |
| 751 | 753 | c.Cursor.HideAfter = d | |
| 754 | + | case "animations": | |
| 755 | + | b, err := onOff(v) | |
| 756 | + | if err != nil { | |
| 757 | + | return err | |
| 758 | + | } | |
| 759 | + | c.Animations.On = b | |
| 760 | + | case "animations.slowdown": | |
| 761 | + | f, err := strconv.ParseFloat(v, 64) | |
| 762 | + | if err != nil || !(f >= 0.1 && f <= 10) { | |
| 763 | + | return fmt.Errorf("must be between 0.1 and 10") | |
| 764 | + | } | |
| 765 | + | c.Animations.Slowdown = f | |
| 752 | 766 | case "focus.follow-move": | |
| 753 | 767 | b, err := onOff(v) | |
| 754 | 768 | if err != nil { | |
| @@ -169,6 +169,8 @@ func Defaults() ports.Config { | |||||
| 169 | c.Touchpad.ScrollFactor = 1 | 169 | c.Touchpad.ScrollFactor = 1 | ||
| 170 | c.Mouse.AccelProfile = ports.AccelAdaptive | 170 | c.Mouse.AccelProfile = ports.AccelAdaptive | ||
| 171 | c.Cursor.HideAfter = 5 * time.Second | 171 | c.Cursor.HideAfter = 5 * time.Second | ||
| 172 | + | c.Animations.On = true | |||
| 173 | + | c.Animations.Slowdown = 1 | |||
| 172 | c.Focus.Animation = ports.FocusAnimationPulse | 174 | c.Focus.Animation = ports.FocusAnimationPulse | ||
| 173 | c.Focus.Effect = ports.FocusEffectScreen | 175 | c.Focus.Effect = ports.FocusEffectScreen | ||
| 174 | c.Focus.Strength = 0.04 | 176 | c.Focus.Strength = 0.04 | ||
| @@ -749,6 +751,18 @@ func set(c *ports.Config, key, v string) error { | |||||
| 749 | return fmt.Errorf("must be off or a duration between 100ms and %s, e.g. 5s", maxCursorHideAfter) | 751 | return fmt.Errorf("must be off or a duration between 100ms and %s, e.g. 5s", maxCursorHideAfter) | ||
| 750 | } | 752 | } | ||
| 751 | c.Cursor.HideAfter = d | 753 | c.Cursor.HideAfter = d | ||
| 754 | + | case "animations": | |||
| 755 | + | b, err := onOff(v) | |||
| 756 | + | if err != nil { | |||
| 757 | + | return err | |||
| 758 | + | } | |||
| 759 | + | c.Animations.On = b | |||
| 760 | + | case "animations.slowdown": | |||
| 761 | + | f, err := strconv.ParseFloat(v, 64) | |||
| 762 | + | if err != nil || !(f >= 0.1 && f <= 10) { | |||
| 763 | + | return fmt.Errorf("must be between 0.1 and 10") | |||
| 764 | + | } | |||
| 765 | + | c.Animations.Slowdown = f | |||
| 752 | case "focus.follow-move": | 766 | case "focus.follow-move": | ||
| 753 | b, err := onOff(v) | 767 | b, err := onOff(v) | ||
| 754 | if err != nil { | 768 | if err != nil { | ||
M internal/adapters/config/config_test.go +21 -0
| @@ -78,6 +78,27 @@ func TestFollowMove(t *testing.T) { | |||
| 78 | 78 | } | |
| 79 | 79 | } | |
| 80 | 80 | ||
| 81 | + | func TestAnimations(t *testing.T) { | |
| 82 | + | d := Defaults().Animations | |
| 83 | + | if !d.On || d.Slowdown != 1 { | |
| 84 | + | t.Fatal("animations defaults", d) | |
| 85 | + | } | |
| 86 | + | c, w := parseString(t, "animations = off\nanimations.slowdown = 2\n") | |
| 87 | + | if c.Animations.On || c.Animations.Slowdown != 2 || len(w) != 0 { | |
| 88 | + | t.Fatal(c.Animations, w) | |
| 89 | + | } | |
| 90 | + | for _, line := range []string{"animations = maybe", "animations.slowdown = 0", "animations.slowdown = 11"} { | |
| 91 | + | c, w = parseString(t, line+"\n") | |
| 92 | + | if c.Animations != d || len(w) != 1 { | |
| 93 | + | t.Fatal(line, c.Animations, w) | |
| 94 | + | } | |
| 95 | + | } | |
| 96 | + | _, w = parseString(t, "animations.slowdown = 11\n") | |
| 97 | + | if len(w) != 1 || !strings.Contains(w[0].Msg, "must be between 0.1 and 10") { | |
| 98 | + | t.Fatal(w) | |
| 99 | + | } | |
| 100 | + | } | |
| 101 | + | ||
| 81 | 102 | func TestFocusIndicator(t *testing.T) { | |
| 82 | 103 | d := Defaults().Focus | |
| 83 | 104 | if d.Animation != ports.FocusAnimationPulse || d.Effect != ports.FocusEffectScreen || d.Strength != 0.04 { | |
| @@ -78,6 +78,27 @@ func TestFollowMove(t *testing.T) { | |||||
| 78 | } | 78 | } | ||
| 79 | } | 79 | } | ||
| 80 | 80 | ||||
| 81 | + | func TestAnimations(t *testing.T) { | |||
| 82 | + | d := Defaults().Animations | |||
| 83 | + | if !d.On || d.Slowdown != 1 { | |||
| 84 | + | t.Fatal("animations defaults", d) | |||
| 85 | + | } | |||
| 86 | + | c, w := parseString(t, "animations = off\nanimations.slowdown = 2\n") | |||
| 87 | + | if c.Animations.On || c.Animations.Slowdown != 2 || len(w) != 0 { | |||
| 88 | + | t.Fatal(c.Animations, w) | |||
| 89 | + | } | |||
| 90 | + | for _, line := range []string{"animations = maybe", "animations.slowdown = 0", "animations.slowdown = 11"} { | |||
| 91 | + | c, w = parseString(t, line+"\n") | |||
| 92 | + | if c.Animations != d || len(w) != 1 { | |||
| 93 | + | t.Fatal(line, c.Animations, w) | |||
| 94 | + | } | |||
| 95 | + | } | |||
| 96 | + | _, w = parseString(t, "animations.slowdown = 11\n") | |||
| 97 | + | if len(w) != 1 || !strings.Contains(w[0].Msg, "must be between 0.1 and 10") { | |||
| 98 | + | t.Fatal(w) | |||
| 99 | + | } | |||
| 100 | + | } | |||
| 101 | + | ||||
| 81 | func TestFocusIndicator(t *testing.T) { | 102 | func TestFocusIndicator(t *testing.T) { | ||
| 82 | d := Defaults().Focus | 103 | d := Defaults().Focus | ||
| 83 | if d.Animation != ports.FocusAnimationPulse || d.Effect != ports.FocusEffectScreen || d.Strength != 0.04 { | 104 | if d.Animation != ports.FocusAnimationPulse || d.Effect != ports.FocusEffectScreen || d.Strength != 0.04 { | ||
M internal/ports/config.go +7 -1
| @@ -143,7 +143,13 @@ type Config struct { | |||
| 143 | 143 | // Cursor.HideAfter hides the pointer cursor after this long without | |
| 144 | 144 | // motion; the next motion shows it. 0 never hides it. | |
| 145 | 145 | Cursor struct{ HideAfter time.Duration } | |
| 146 | - | Focus struct { | |
| 146 | + | // Animations: On enables motion (off lands everything instantly); | |
| 147 | + | // Slowdown scales every duration, 0.1 to 10. | |
| 148 | + | Animations struct { | |
| 149 | + | On bool | |
| 150 | + | Slowdown float64 | |
| 151 | + | } | |
| 152 | + | Focus struct { | |
| 147 | 153 | // FollowMove shows the target workspace after a column or window | |
| 148 | 154 | // moves to it. | |
| 149 | 155 | FollowMove bool | |
| @@ -143,7 +143,13 @@ type Config struct { | |||||
| 143 | // Cursor.HideAfter hides the pointer cursor after this long without | 143 | // Cursor.HideAfter hides the pointer cursor after this long without | ||
| 144 | // motion; the next motion shows it. 0 never hides it. | 144 | // motion; the next motion shows it. 0 never hides it. | ||
| 145 | Cursor struct{ HideAfter time.Duration } | 145 | Cursor struct{ HideAfter time.Duration } | ||
| 146 | - | Focus struct { | 146 | + | // Animations: On enables motion (off lands everything instantly); |
| 147 | + | // Slowdown scales every duration, 0.1 to 10. | |||
| 148 | + | Animations struct { | |||
| 149 | + | On bool | |||
| 150 | + | Slowdown float64 | |||
| 151 | + | } | |||
| 152 | + | Focus struct { | |||
| 147 | // FollowMove shows the target workspace after a column or window | 153 | // FollowMove shows the target workspace after a column or window | ||
| 148 | // moves to it. | 154 | // moves to it. | ||
| 149 | FollowMove bool | 155 | FollowMove bool | ||