Skip to content

Commit

Permalink
Add support for keyboard layouts
Browse files Browse the repository at this point in the history
  • Loading branch information
nikitabobko committed Mar 9, 2024
1 parent c614c56 commit 7aa2ac6
Show file tree
Hide file tree
Showing 11 changed files with 368 additions and 52 deletions.
4 changes: 4 additions & 0 deletions AeroSpace.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
43E3628E37D2439B820FFC82 /* server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796713A1B3AEEBF4D0D180C7 /* server.swift */; };
45AA5FD4A023AF751922BC22 /* BundleEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B7A2DF0D1F72B80B1F04240 /* BundleEx.swift */; };
45EA2D1C90430C432E123B51 /* keysMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0D40CBD65704BA9595C2FA /* keysMap.swift */; };
4A11D06CCDAD05595AB67239 /* parseKeyMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64EBF5912CC4BBEC0E5E4B1C /* parseKeyMapping.swift */; };
4CC374136F60299FB672662D /* DebugWindowsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71908440CD1ADBE13AD58E26 /* DebugWindowsCommand.swift */; };
4E0BC093AD1FBCCA718A26EC /* parseCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07ADCBFC2E29A2AD5C1B7984 /* parseCommand.swift */; };
51AB4C0992703B2E9D0F55E2 /* MonitorDescriptionEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C1D626244E63437F1CA24C3 /* MonitorDescriptionEx.swift */; };
Expand Down Expand Up @@ -153,6 +154,7 @@
5F5F52E346D024960EAF5938 /* TrayMenuModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrayMenuModel.swift; sourceTree = "<group>"; };
5F7387A5BA6D187A00B00170 /* LocalPackage */ = {isa = PBXFileReference; lastKnownFileType = folder; name = LocalPackage; path = LocalPackage; sourceTree = SOURCE_ROOT; };
6352ADEE6625D9703CFCA99A /* Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
64EBF5912CC4BBEC0E5E4B1C /* parseKeyMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = parseKeyMapping.swift; sourceTree = "<group>"; };
67DBAF4ECF8A0B931FC34EAD /* parseConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = parseConfig.swift; sourceTree = "<group>"; };
69CB1289E3FA51A35F839238 /* HotkeyBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeyBinding.swift; sourceTree = "<group>"; };
6D9C5ED5AC77D80F1CCD103F /* JoinWithCommandTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinWithCommandTest.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -329,6 +331,7 @@
1C0D40CBD65704BA9595C2FA /* keysMap.swift */,
67DBAF4ECF8A0B931FC34EAD /* parseConfig.swift */,
CCDFB9D7321F08B5CCEF6AFB /* parseGaps.swift */,
64EBF5912CC4BBEC0E5E4B1C /* parseKeyMapping.swift */,
0A9DFF8980BB3F90A3793BE9 /* parseOnWindowDetected.swift */,
9164C9401F7DDCACE9278DA4 /* startAtLogin.swift */,
);
Expand Down Expand Up @@ -664,6 +667,7 @@
4E0BC093AD1FBCCA718A26EC /* parseCommand.swift in Sources */,
A0765C31043BCFB0420BF1C9 /* parseConfig.swift in Sources */,
5BA537EABFE48178D6BD2544 /* parseGaps.swift in Sources */,
4A11D06CCDAD05595AB67239 /* parseKeyMapping.swift in Sources */,
21D0512B48E0E3C28F8CA42A /* parseOnWindowDetected.swift in Sources */,
B3702BB393A9B03CCAE4C60E /* refresh.swift in Sources */,
8086A22EDCDC4C906C337D0B /* resizeWithMouse.swift in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions LocalPackage/Sources/Common/util/commonUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ public extension Int {
func toDouble() -> Double { Double(self) }
}

public func +<K, V>(lhs: [K: V], rhs: [K: V]) -> [K: V] {
lhs.merging(rhs) { _, r in r }
}

public extension String {
func removePrefix(_ prefix: String) -> String {
hasPrefix(prefix) ? String(dropFirst(prefix.count)) : self
Expand Down
14 changes: 9 additions & 5 deletions docs/config-examples/default-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ default-root-container-layout = 'tiles'
# tall monitor (anything higher than wide) gets vertical orientation
default-root-container-orientation = 'auto'

# Visual indent makes it easier to understand that containers of the same orientation are nested.
# If you have 'enable-normalization-opposite-orientation-for-nested-containers' enabled then
# there is no way to observe the indent
indent-for-nested-containers-with-the-same-orientation = 30

# Possible values: (qwerty|dvorak)
# See https://nikitabobko.github.io/AeroSpace/guide.html#key-mapping
key-mapping.preset = 'qwerty'

# Gaps between windows (inner-*) and between monitor edges (outer-*).
# Possible values:
# - Constant: gaps.outer.top = 8
Expand All @@ -48,11 +57,6 @@ gaps.outer.bottom = 0
gaps.outer.top = 0
gaps.outer.right = 0

# Visual indent makes it easier to understand that containers of the same orientation are nested.
# If you have 'enable-normalization-opposite-orientation-for-nested-containers' enabled then
# there is no way to observe the indent
indent-for-nested-containers-with-the-same-orientation = 30

# 'main' binding mode declaration
# See: https://nikitabobko.github.io/AeroSpace/guide#binding-modes
# 'main' binding mode must be always presented
Expand Down
55 changes: 55 additions & 0 deletions docs/goodness.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,61 @@ end tell
EOF'''
----
[#disable-hide-app]
== Disable annoying and useless "hide application"

.~/.aerospace.toml
[source,toml]
----
[mode.main.binding]
cmd-h = []
----

[#colemak-keys-remap]
== Colemak keys remap

.~/.aerospace.toml
[source,toml]
----
[key-mapping.key-notation-to-key-code]
q = 'q'
w = 'w'
f = 'e'
p = 'r'
g = 't'
j = 'y'
l = 'u'
u = 'i'
y = 'o'
semicolon = 'p'
leftSquareBracket = 'leftSquareBracket'
rightSquareBracket = 'rightSquareBracket'
backslash = 'backslash'
a = 'a'
r = 's'
s = 'd'
t = 'f'
d = 'g'
h = 'h'
n = 'j'
e = 'k'
i = 'l'
o = 'semicolon'
quote = 'quote'
z = 'z'
x = 'x'
c = 'c'
v = 'v'
b = 'b'
k = 'n'
r = 'm'
comma = 'comma'
period = 'period'
slash = 'slash'
----

[#popular-apps-ids]
== List of popular and built-in applications IDs

Expand Down
27 changes: 27 additions & 0 deletions docs/guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,33 @@ aerospace workspace 1

For the list of available commands see: xref:commands.adoc[]

[#key-mapping]
=== Keyboard layouts and key mapping

By default, key bindings in the config are perceived as `qwerty` layout.

If you use different layout, different alphabet, or you just want to have a fancy alias for the existing key, you can use `key-mapping.key-notation-to-key-code`.

[source,toml]
----
# Define my fancy unicorn key notation
[key-mapping.key-notation-to-key-code]
unicorn = 'u'
[mode.main.binding]
alt-unicorn = 'workspace unicorn' # (⁀ᗢ⁀)
----

* For `dvorak` users, AeroSpace offers a preconfigured preset.
+
[source,toml]
----
key-mapping.preset = 'dvorak'
----

* For `colemak` users, there is xref:goodness.adoc#colemak-keys-remap[a compiled mapping].
`colemak` may be added as preconfigured preset similar to `dvorak` in the future, if there is enough demand

[#tree]
== Tree

Expand Down
1 change: 1 addition & 0 deletions src/config/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct Config: Copyable {
var accordionPadding: Int = 30
var enableNormalizationOppositeOrientationForNestedContainers: Bool = true
var execOnWorkspaceChange: [String] = []
var keyMapping = KeyMapping()

var gaps: Gaps = .zero
var workspaceToMonitorForceAssignment: [String: [MonitorDescription]] = [:]
Expand Down
2 changes: 1 addition & 1 deletion src/config/HotkeyBinding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ struct HotkeyBinding {
self.modifiers = modifiers
self.key = key
self.commands = commands
self.binding = modifiers.toString() + "-\(key)"
self.binding = modifiers.isEmpty ? key.description : modifiers.toString() + "-\(key)"
}
}
169 changes: 132 additions & 37 deletions src/config/keysMap.swift
Original file line number Diff line number Diff line change
@@ -1,32 +1,95 @@
import HotKey
import Common

let keysMap: [String: Key] = [
"a": .a,
"b": .b,
"c": .c,
"d": .d,
"e": .e,
"f": .f,
"g": .g,
"h": .h,
"i": .i,
"j": .j,
"k": .k,
"l": .l,
"m": .m,
"n": .n,
"o": .o,
"p": .p,
"q": .q,
"r": .r,
"s": .s,
"t": .t,
"u": .u,
"v": .v,
"w": .w,
"x": .x,
"y": .y,
"z": .z,
private let minus = "minus"
private let equal = "equal"

private let q = "q"
private let w = "w"
private let e = "e"
private let r = "r"
private let t = "t"
private let y = "y"
private let u = "u"
private let i = "i"
private let o = "o"
private let p = "p"
private let leftSquareBracket = "leftSquareBracket"
private let rightSquareBracket = "rightSquareBracket"
private let backslash = "backslash"

private let a = "a"
private let s = "s"
private let d = "d"
private let f = "f"
private let g = "g"
private let h = "h"
private let j = "j"
private let k = "k"
private let l = "l"
private let semicolon = "semicolon"
private let quote = "quote"

private let z = "z"
private let x = "x"
private let c = "c"
private let v = "v"
private let b = "b"
private let n = "n"
private let m = "m"
private let comma = "comma"
private let period = "period"
private let slash = "slash"

func getKeysPreset(_ layout: KeyMapping.Preset) -> [String: Key] {
switch layout {
case .qwerty:
return keyNotationToKeyCode
case .dvorak:
return dvorakMap
}
}

let keyNotationToKeyCode: [String: Key] = [
minus: .minus,
equal: .equal,

q: .q,
w: .w,
e: .e,
r: .r,
t: .t,
y: .y,
u: .u,
i: .i,
o: .o,
p: .p,
leftSquareBracket: .leftBracket,
rightSquareBracket: .rightBracket,
backslash: .backslash,

a: .a,
s: .s,
d: .d,
f: .f,
g: .g,
h: .h,
j: .j,
k: .k,
l: .l,
semicolon: .semicolon,
quote: .quote,

z: .z,
x: .x,
c: .c,
v: .v,
b: .b,
n: .n,
m: .m,
comma: .comma,
period: .period,
slash: .slash,

"0": .zero,
"1": .one,
Expand Down Expand Up @@ -79,17 +142,7 @@ let keysMap: [String: Key] = [
"f19": .f19,
"f20": .f20,

"minus": .minus,
"equal": .equal,
"period": .period,
"comma": .comma,
"slash": .slash,
"backslash": .backslash,
"quote": .quote,
"semicolon": .semicolon,
"backtick": .grave,
"leftSquareBracket": .leftBracket,
"rightSquareBracket": .rightBracket,
"space": .space,
"enter": .return,
"esc": .escape,
Expand All @@ -102,6 +155,48 @@ let keysMap: [String: Key] = [
"right": .rightArrow,
]

private let dvorakMap: [String: Key] = keyNotationToKeyCode + [
leftSquareBracket: .minus,
rightSquareBracket: .equal,

quote: .q,
comma: .w,
period: .e,
p: .r,
y: .t,
f: .y,
g: .u,
c: .i,
r: .o,
l: .p,
slash: .leftBracket,
equal: .rightBracket,
backslash: .backslash,

a: .a,
o: .s,
e: .d,
u: .f,
i: .g,
d: .h,
h: .j,
t: .k,
n: .l,
s: .semicolon,
minus: .quote,

semicolon: .z,
q: .x,
j: .c,
k: .v,
x: .b,
b: .n,
m: .m,
w: .comma,
v: .period,
z: .slash,
]

let modifiersMap: [String: NSEvent.ModifierFlags] = [
"shift": .shift,
"alt": .option,
Expand Down
Loading

0 comments on commit 7aa2ac6

Please sign in to comment.