Repository navigation
Expand file tree
/
Copy pathLayouts.lua
More file actions
192 lines (182 loc) · 9.54 KB
/
Copy pathLayouts.lua
File metadata and controls
192 lines (182 loc) · 9.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
-- Keystance keyboard layouts, as data. Each row is a list of keys: { key, w = width in key
-- units (default 1), gap = space before it in key units, mod = true for Shift/Ctrl/Alt }.
-- `key` is the game's name for the key (what GetBindingAction takes). The function row
-- sits a little apart from the rest, like on a real keyboard.
-- Keys the game names differently on some keyboards simply don't match; bound keys that
-- aren't drawn are listed under the keyboard, so nothing is ever hidden.
local ADDON, ns = ...
if ns.disabled then return end -- another copy of Keystance is running (Core.lua)
local L = ns.L
local FROW = {
{ "ESCAPE" }, { "F1", gap = 1 }, { "F2" }, { "F3" }, { "F4" }, { "F5", gap = 0.5 }, { "F6" }, { "F7" }, { "F8" },
{ "F9", gap = 0.5 }, { "F10" }, { "F11" }, { "F12" },
}
local NUMBERS = {
{ "`" }, { "1" }, { "2" }, { "3" }, { "4" }, { "5" }, { "6" }, { "7" }, { "8" }, { "9" }, { "0" }, { "-" }, { "=" },
{ "BACKSPACE", w = 2 },
}
local BOTTOM = {
{ "CTRL", w = 1.25, mod = true }, { "", w = 1.25 }, { "ALT", w = 1.25, mod = true }, { "SPACE", w = 6.25 },
{ "ALT", w = 1.25, mod = true }, { "", w = 1.25 }, { "", w = 1.25 }, { "CTRL", w = 1.25, mod = true },
}
ns.LAYOUTS = {
-- US and most of the world.
ansi = {
name = L["QWERTY (US)"],
rows = {
FROW, NUMBERS,
{ { "TAB", w = 1.5 }, { "Q" }, { "W" }, { "E" }, { "R" }, { "T" }, { "Y" }, { "U" }, { "I" }, { "O" }, { "P" },
{ "[" }, { "]" }, { "\\", w = 1.5 } },
{ { "CAPSLOCK", w = 1.75 }, { "A" }, { "S" }, { "D" }, { "F" }, { "G" }, { "H" }, { "J" }, { "K" }, { "L" },
{ ";" }, { "'" }, { "ENTER", w = 2.25 } },
{ { "SHIFT", w = 2.25, mod = true }, { "Z" }, { "X" }, { "C" }, { "V" }, { "B" }, { "N" }, { "M" }, { "," },
{ "." }, { "/" }, { "SHIFT", w = 2.75, mod = true } },
BOTTOM,
},
},
-- UK: a # key by Enter and a \ key by the left Shift (their game names aren't verified).
iso = {
name = L["QWERTY (UK)"],
rows = {
FROW, NUMBERS,
{ { "TAB", w = 1.5 }, { "Q" }, { "W" }, { "E" }, { "R" }, { "T" }, { "Y" }, { "U" }, { "I" }, { "O" }, { "P" },
{ "[" }, { "]" }, { "ENTER", w = 1.5 } },
{ { "CAPSLOCK", w = 1.75 }, { "A" }, { "S" }, { "D" }, { "F" }, { "G" }, { "H" }, { "J" }, { "K" }, { "L" },
{ ";" }, { "'" }, { "#" }, { "", w = 1.25 } },
{ { "SHIFT", w = 1.25, mod = true }, { "\\" }, { "Z" }, { "X" }, { "C" }, { "V" }, { "B" }, { "N" }, { "M" },
{ "," }, { "." }, { "/" }, { "SHIFT", w = 2.75, mod = true } },
BOTTOM,
},
},
}
-- A game controller, drawn as a diagram: an outline of one in the middle (media/controller.tga,
-- drawn by tools/make_controller.py in the same PAD_W x PAD_H pixels), and each button's
-- binding in a column to its left or right, joined to the button by a line. `at` is the
-- button's spot on the drawing, in those pixels from its top-left. Button names measured on
-- Forever (C_GamePad.ButtonIndexToBinding, 2026-09-28); their symbols come from
-- GetBindingText, which gives the connected controller's own icons. Stick directions
-- (usually movement), the extra buttons and paddles aren't drawn; bound ones show under
-- "Also bound".
ns.PAD_W, ns.PAD_H = 300, 218
ns.LAYOUTS.pad = {
name = L["Controller"], pad = true,
left = {
{ "PADLTRIGGER", at = { 72, 8 } }, { "PADLSHOULDER", at = { 40, 30 } }, { "PADBACK", at = { 126, 56 } },
{ "PADLSTICK", at = { 80, 84 } }, { "PADDUP", at = { 115, 124 } }, { "PADDLEFT", at = { 101, 138 } },
{ "PADDRIGHT", at = { 129, 138 } }, { "PADDDOWN", at = { 115, 152 } },
},
-- `via`: a bend on the way, where the straight line would cross another button.
right = {
{ "PADRTRIGGER", at = { 228, 8 } }, { "PADRSHOULDER", at = { 260, 30 } }, { "PADFORWARD", at = { 174, 56 } },
{ "PAD4", at = { 228, 70 } }, { "PAD2", at = { 246, 88 } }, { "PAD1", at = { 228, 106 } },
{ "PAD3", at = { 210, 88 }, via = { 207, 112 } }, { "PADRSTICK", at = { 190, 138 } },
},
}
-- The controller buttons set to act as Shift, Ctrl and Alt (the game's GamePadEmulate
-- settings): { [button] = "SHIFT"|"CTRL"|"ALT" }.
local padMods = {}
local EMULATE = { { "GamePadEmulateShift", "SHIFT" }, { "GamePadEmulateCtrl", "CTRL" }, { "GamePadEmulateAlt", "ALT" } }
function ns.PadModifiers()
for k in pairs(padMods) do padMods[k] = nil end
if not GetCVar then return padMods end
for _, m in ipairs(EMULATE) do
local ok, button = pcall(GetCVar, m[1])
if ok and type(button) == "string" and button ~= "" and button ~= "none" then padMods[button] = m[2] end
end
return padMods
end
-- True while the player is playing with a controller (not merely one being set up:
-- gamepad support on with a controller known was true while playing with the keyboard).
local function PadActive()
if not (C_GamePad and C_GamePad.IsEnabled and IsUsingGamepad) then return false end
local ok, enabled = pcall(C_GamePad.IsEnabled)
if not (ok and enabled) then return false end
local okUsing, using = pcall(IsUsingGamepad)
return okUsing and using and true or false
end
-- The navigation block and the numpad, drawn with the Numpad option. Keys are placed by
-- row (as the main keyboard's rows) and column `x` in key units within their block; `h` is
-- a key two rows tall. Most names were seen bound in game (NUMPAD0, NUMPAD5, NUMLOCK,
-- NUMPADDIVIDE, NUMPADMINUS, NUMPADPLUS, PRINTSCREEN, INSERT, DELETE, HOME, END, PAGEUP,
-- PAGEDOWN, UP, DOWN, LEFT, RIGHT); NUMPADMULTIPLY, NUMPADDECIMAL and NUMPADENTER aren't
-- confirmed (a wrong one just shows under "Also bound").
ns.NAV_KEYS = {
{ "PRINTSCREEN", row = 1, x = 0 }, { "SCROLLLOCK", row = 1, x = 1 }, { "PAUSE", row = 1, x = 2 },
{ "INSERT", row = 2, x = 0 }, { "HOME", row = 2, x = 1 }, { "PAGEUP", row = 2, x = 2 },
{ "DELETE", row = 3, x = 0 }, { "END", row = 3, x = 1 }, { "PAGEDOWN", row = 3, x = 2 },
{ "UP", row = 5, x = 1 },
{ "LEFT", row = 6, x = 0 }, { "DOWN", row = 6, x = 1 }, { "RIGHT", row = 6, x = 2 },
}
ns.NUMPAD_KEYS = {
{ "NUMLOCK", row = 2, x = 0 }, { "NUMPADDIVIDE", row = 2, x = 1 }, { "NUMPADMULTIPLY", row = 2, x = 2 },
{ "NUMPADMINUS", row = 2, x = 3 },
{ "NUMPAD7", row = 3, x = 0 }, { "NUMPAD8", row = 3, x = 1 }, { "NUMPAD9", row = 3, x = 2 },
{ "NUMPADPLUS", row = 3, x = 3, h = 2 },
{ "NUMPAD4", row = 4, x = 0 }, { "NUMPAD5", row = 4, x = 1 }, { "NUMPAD6", row = 4, x = 2 },
{ "NUMPAD1", row = 5, x = 0 }, { "NUMPAD2", row = 5, x = 1 }, { "NUMPAD3", row = 5, x = 2 },
{ "NUMPADENTER", row = 5, x = 3, h = 2 },
{ "NUMPAD0", row = 6, x = 0, w = 2 }, { "NUMPADDECIMAL", row = 6, x = 2 },
}
-- The mouse: middle button, side buttons and the wheel.
ns.MOUSE_KEYS = { "BUTTON3", "BUTTON4", "BUTTON5", "MOUSEWHEELUP", "MOUSEWHEELDOWN" }
-- The layout for the setting: "auto" picks the controller while one is in use, else UK for
-- an English (UK) game client, else US.
function ns.LayoutKey(setting)
if ns.LAYOUTS[setting] then return setting end
if PadActive() then return "pad" end
return GetLocale() == "enGB" and "iso" or "ansi"
end
-- Short labels for key caps and bar buttons.
local SHORT = {
ESCAPE = "Esc", BACKSPACE = "Bksp", CAPSLOCK = "Caps", ENTER = "Enter", SPACE = "Space", TAB = "Tab",
SHIFT = "Shift", CTRL = "Ctrl", ALT = "Alt", BUTTON3 = "M3", BUTTON4 = "M4", BUTTON5 = "M5",
MOUSEWHEELUP = "WhUp", MOUSEWHEELDOWN = "WhDn",
PRINTSCREEN = "PrtSc", SCROLLLOCK = "ScrLk", PAUSE = "Pause", INSERT = "Ins", HOME = "Home",
PAGEUP = "PgUp", DELETE = "Del", END = "End", PAGEDOWN = "PgDn", UP = "Up", DOWN = "Down",
LEFT = "Left", RIGHT = "Right", NUMLOCK = "Num", NUMPADDIVIDE = "N/", NUMPADMULTIPLY = "N*",
NUMPADMINUS = "N-", NUMPADPLUS = "N+", NUMPADENTER = "NEnt", NUMPADDECIMAL = "N.",
NUMPAD0 = "N0", NUMPAD1 = "N1", NUMPAD2 = "N2", NUMPAD3 = "N3", NUMPAD4 = "N4", NUMPAD5 = "N5",
NUMPAD6 = "N6", NUMPAD7 = "N7", NUMPAD8 = "N8", NUMPAD9 = "N9",
}
function ns.KeyLabel(key)
if SHORT[key] then return SHORT[key] end
-- A controller button: the game's own icon for it.
if key:sub(1, 3) == "PAD" and GetBindingText then
local text = GetBindingText(key)
if type(text) == "string" and text ~= "" then return text end
end
return key
end
-- A bound key as bar buttons show it: modifiers as s, c, a ("SHIFT-1" is "s1"). Cached.
local short = {}
function ns.ShortKey(key)
if not key then return nil end
local s = short[key]
if s then return s end
local rest = key
local mods = ""
for _, m in ipairs({ { "ALT%-", "a" }, { "CTRL%-", "c" }, { "SHIFT%-", "s" } }) do
local stripped, n = rest:gsub("^" .. m[1], "", 1)
if n > 0 and stripped ~= "" then mods, rest = mods .. m[2], stripped end
end
s = mods .. ns.KeyLabel(rest)
short[key] = s
return s
end
-- Splits a bound key into its modifier prefix ("ALT-CTRL-SHIFT-", in the game's order)
-- and its base key. A lone "-" is a key, not a modifier.
-- Cached: keys are few and split every time the keyboard is redrawn.
local MODS = { "ALT-", "CTRL-", "SHIFT-" }
local splitPrefix, splitBase = {}, {}
function ns.SplitKey(key)
local base = splitBase[key]
if base then return splitPrefix[key], base end
local prefix, rest = "", key
for _, m in ipairs(MODS) do
if rest:sub(1, #m) == m and #rest > #m then
prefix, rest = prefix .. m, rest:sub(#m + 1)
end
end
splitPrefix[key], splitBase[key] = prefix, rest
return prefix, rest
end