fix(input): parse '+' key bindings
- commit
- f1c91596b9b308822fb4fa9a0ef9db733f649e27
- author
- brice · · 2025-12-26 08:47 UTC
- parent
- 51d3a48
- signature
- unsigned
2 files · +27 -0
M internal/ui/input/shortcuts.go +15 -0
| @@ -453,6 +453,10 @@ func ParseKeyString(s string) (KeyBinding, bool) { | |||
| 453 | 453 | } | |
| 454 | 454 | ||
| 455 | 455 | s = strings.ToLower(strings.TrimSpace(s)) | |
| 456 | + | if s == "+" { | |
| 457 | + | return KeyBinding{Keyval: uint(gdk.KEY_plus), Modifiers: ModNone}, true | |
| 458 | + | } | |
| 459 | + | ||
| 456 | 460 | parts := strings.Split(s, "+") | |
| 457 | 461 | ||
| 458 | 462 | var modifiers Modifier | |
| @@ -460,6 +464,9 @@ func ParseKeyString(s string) (KeyBinding, bool) { | |||
| 460 | 464 | ||
| 461 | 465 | for _, part := range parts { | |
| 462 | 466 | part = strings.TrimSpace(part) | |
| 467 | + | if part == "" { | |
| 468 | + | continue | |
| 469 | + | } | |
| 463 | 470 | switch part { | |
| 464 | 471 | case "ctrl", "control": | |
| 465 | 472 | modifiers |= ModCtrl | |
| @@ -468,10 +475,18 @@ func ParseKeyString(s string) (KeyBinding, bool) { | |||
| 468 | 475 | case "alt": | |
| 469 | 476 | modifiers |= ModAlt | |
| 470 | 477 | default: | |
| 478 | + | if keyPart != "" { | |
| 479 | + | return KeyBinding{}, false | |
| 480 | + | } | |
| 471 | 481 | keyPart = part | |
| 472 | 482 | } | |
| 473 | 483 | } | |
| 474 | 484 | ||
| 485 | + | // Allow parsing "ctrl++" / "alt+shift++" where the key is "+". | |
| 486 | + | if keyPart == "" && strings.HasSuffix(s, "++") { | |
| 487 | + | keyPart = "+" | |
| 488 | + | } | |
| 489 | + | ||
| 475 | 490 | if keyPart == "" { | |
| 476 | 491 | return KeyBinding{}, false | |
| 477 | 492 | } | |
| @@ -453,6 +453,10 @@ func ParseKeyString(s string) (KeyBinding, bool) { | |||||
| 453 | } | 453 | } | ||
| 454 | 454 | ||||
| 455 | s = strings.ToLower(strings.TrimSpace(s)) | 455 | s = strings.ToLower(strings.TrimSpace(s)) | ||
| 456 | + | if s == "+" { | |||
| 457 | + | return KeyBinding{Keyval: uint(gdk.KEY_plus), Modifiers: ModNone}, true | |||
| 458 | + | } | |||
| 459 | + | ||||
| 456 | parts := strings.Split(s, "+") | 460 | parts := strings.Split(s, "+") | ||
| 457 | 461 | ||||
| 458 | var modifiers Modifier | 462 | var modifiers Modifier | ||
| @@ -460,6 +464,9 @@ func ParseKeyString(s string) (KeyBinding, bool) { | |||||
| 460 | 464 | ||||
| 461 | for _, part := range parts { | 465 | for _, part := range parts { | ||
| 462 | part = strings.TrimSpace(part) | 466 | part = strings.TrimSpace(part) | ||
| 467 | + | if part == "" { | |||
| 468 | + | continue | |||
| 469 | + | } | |||
| 463 | switch part { | 470 | switch part { | ||
| 464 | case "ctrl", "control": | 471 | case "ctrl", "control": | ||
| 465 | modifiers |= ModCtrl | 472 | modifiers |= ModCtrl | ||
| @@ -468,10 +475,18 @@ func ParseKeyString(s string) (KeyBinding, bool) { | |||||
| 468 | case "alt": | 475 | case "alt": | ||
| 469 | modifiers |= ModAlt | 476 | modifiers |= ModAlt | ||
| 470 | default: | 477 | default: | ||
| 478 | + | if keyPart != "" { | |||
| 479 | + | return KeyBinding{}, false | |||
| 480 | + | } | |||
| 471 | keyPart = part | 481 | keyPart = part | ||
| 472 | } | 482 | } | ||
| 473 | } | 483 | } | ||
| 474 | 484 | ||||
| 485 | + | // Allow parsing "ctrl++" / "alt+shift++" where the key is "+". | |||
| 486 | + | if keyPart == "" && strings.HasSuffix(s, "++") { | |||
| 487 | + | keyPart = "+" | |||
| 488 | + | } | |||
| 489 | + | ||||
| 475 | if keyPart == "" { | 490 | if keyPart == "" { | ||
| 476 | return KeyBinding{}, false | 491 | return KeyBinding{}, false | ||
| 477 | } | 492 | } | ||
M internal/ui/input/shortcuts_test.go +12 -0
| @@ -49,6 +49,12 @@ func TestParseKeyString_SingleKeys(t *testing.T) { | |||
| 49 | 49 | want: KeyBinding{Keyval: uint(gdk.KEY_space), Modifiers: ModNone}, | |
| 50 | 50 | wantOk: true, | |
| 51 | 51 | }, | |
| 52 | + | { | |
| 53 | + | name: "plus symbol", | |
| 54 | + | input: "+", | |
| 55 | + | want: KeyBinding{Keyval: uint(gdk.KEY_plus), Modifiers: ModNone}, | |
| 56 | + | wantOk: true, | |
| 57 | + | }, | |
| 52 | 58 | { | |
| 53 | 59 | name: "f5", | |
| 54 | 60 | input: "f5", | |
| @@ -150,6 +156,12 @@ func TestParseKeyString_WithModifiers(t *testing.T) { | |||
| 150 | 156 | want: KeyBinding{Keyval: uint('r'), Modifiers: ModCtrl}, | |
| 151 | 157 | wantOk: true, | |
| 152 | 158 | }, | |
| 159 | + | { | |
| 160 | + | name: "ctrl++", | |
| 161 | + | input: "ctrl++", | |
| 162 | + | want: KeyBinding{Keyval: uint(gdk.KEY_plus), Modifiers: ModCtrl}, | |
| 163 | + | wantOk: true, | |
| 164 | + | }, | |
| 153 | 165 | } | |
| 154 | 166 | ||
| 155 | 167 | for _, tt := range tests { | |
| @@ -49,6 +49,12 @@ func TestParseKeyString_SingleKeys(t *testing.T) { | |||||
| 49 | want: KeyBinding{Keyval: uint(gdk.KEY_space), Modifiers: ModNone}, | 49 | want: KeyBinding{Keyval: uint(gdk.KEY_space), Modifiers: ModNone}, | ||
| 50 | wantOk: true, | 50 | wantOk: true, | ||
| 51 | }, | 51 | }, | ||
| 52 | + | { | |||
| 53 | + | name: "plus symbol", | |||
| 54 | + | input: "+", | |||
| 55 | + | want: KeyBinding{Keyval: uint(gdk.KEY_plus), Modifiers: ModNone}, | |||
| 56 | + | wantOk: true, | |||
| 57 | + | }, | |||
| 52 | { | 58 | { | ||
| 53 | name: "f5", | 59 | name: "f5", | ||
| 54 | input: "f5", | 60 | input: "f5", | ||
| @@ -150,6 +156,12 @@ func TestParseKeyString_WithModifiers(t *testing.T) { | |||||
| 150 | want: KeyBinding{Keyval: uint('r'), Modifiers: ModCtrl}, | 156 | want: KeyBinding{Keyval: uint('r'), Modifiers: ModCtrl}, | ||
| 151 | wantOk: true, | 157 | wantOk: true, | ||
| 152 | }, | 158 | }, | ||
| 159 | + | { | |||
| 160 | + | name: "ctrl++", | |||
| 161 | + | input: "ctrl++", | |||
| 162 | + | want: KeyBinding{Keyval: uint(gdk.KEY_plus), Modifiers: ModCtrl}, | |||
| 163 | + | wantOk: true, | |||
| 164 | + | }, | |||
| 153 | } | 165 | } | ||
| 154 | 166 | ||||
| 155 | for _, tt := range tests { | 167 | for _, tt := range tests { | ||