annotate SkeletonKey/KeyBinds.lua @ 1:cd7d06bcd98d

KeyBinds: set hotkey text for blizzard action buttons UnitFrame: prototype templates for the majority of units
author Nenue
date Tue, 21 Jun 2016 04:47:52 -0400
parents 69e828f4238a
children 07293831dd7b
rev   line source
Nenue@0 1 --------------------------------------------
Nenue@0 2 -- KrakTool
Nenue@0 3 -- Nick
Nenue@0 4 -- @project-revision@ @project-hash@
Nenue@0 5 -- @file-revision@ @file-hash@
Nenue@0 6 -- Created: 6/16/2016 3:47 AM
Nenue@0 7 --------------------------------------------
Nenue@0 8 -- kb
Nenue@0 9 -- .bind(button, key) bind current keystroke to command
Nenue@0 10 -- .assign(button, command, name, icon) set button command
Nenue@0 11 -- .release(button) clear button command
Nenue@0 12 -- .refresh(button) update button contents
Nenue@0 13 -- .ui() invoke interface
Nenue@0 14 -- .profile(name) set profile character
Nenue@0 15 -- .loadbinds(bindings) walk table with SetBinding()
Nenue@0 16
Nenue@1 17 local KT = LibKT.register(KeyBinder)
Nenue@0 18 local kb = KeyBinder
Nenue@1 19 local db
Nenue@1 20
Nenue@0 21 local MIN_BIND_SLOTS = 32
Nenue@0 22 local BINDS_PER_ROW = 8
Nenue@0 23 local KEY_BUTTON_SIZE = 40
Nenue@0 24 local TAB_OFFSET = 12
Nenue@0 25 local TAB_HEIGHT = 40
Nenue@0 26 local TAB_SPACING = 2
Nenue@0 27 local BUTTON_SPACING = 4
Nenue@0 28 local BUTTON_PADDING = 12
Nenue@1 29 local HEADER_OFFSET, FOOTER_OFFSET
Nenue@0 30 local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544;
Nenue@0 31 local BINDING_TYPE_SPECIALIZATION = 3
Nenue@0 32 local BINDING_TYPE_CHARACTER = 2
Nenue@0 33 local BINDING_TYPE_GLOBAL = 1
Nenue@0 34 local BINDING_ASSIGNED = '|cFF00FF00%s|r assigned to |cFFFFFF00%s|r (%s).'
Nenue@0 35 local BINDING_FAILED_PROTECTED = '|cFF00FF00%s|r used by |cFFFFFF00%s|r!'
Nenue@0 36 local BINDING_MODE = {
Nenue@0 37 [BINDING_TYPE_SPECIALIZATION] = 'Specialization: %s',
Nenue@0 38 [BINDING_TYPE_CHARACTER] = 'Character: %s',
Nenue@0 39 [BINDING_TYPE_GLOBAL] = 'Global Binds'
Nenue@0 40 }
Nenue@0 41 local BINDING_SCHEME_COLOR = {
Nenue@0 42 [BINDING_TYPE_SPECIALIZATION] = {0,0,0,0.5},
Nenue@0 43 [BINDING_TYPE_CHARACTER] = {0,0.25,0,0.5},
Nenue@0 44 [BINDING_TYPE_GLOBAL] = {0,.125,.5,.5}
Nenue@0 45 }
Nenue@0 46 local BINDING_SCHEME_VERTEX = {
Nenue@0 47
Nenue@0 48 [BINDING_TYPE_SPECIALIZATION] = {1,1,1,1},
Nenue@0 49 [BINDING_TYPE_CHARACTER] = {0,1,0,1},
Nenue@0 50 [BINDING_TYPE_GLOBAL] = {0,.5,1,1}
Nenue@0 51 }
Nenue@0 52
Nenue@0 53 local BINDING_SCHEME_TEXT = {
Nenue@0 54 [BINDING_TYPE_SPECIALIZATION] = {1, 1, 0},
Nenue@0 55 [BINDING_TYPE_CHARACTER] = {0, 1, 0},
Nenue@0 56 [BINDING_TYPE_GLOBAL] = {0, 1, 1}
Nenue@0 57 }
Nenue@0 58 local ACTION_SCRIPT = {
Nenue@0 59 ['mount'] = "/script C_MountJournal.SummonByID(%d)",
Nenue@0 60 ['equipset'] = "/script UseEquipmentSet(%d)",
Nenue@0 61 }
Nenue@0 62
Nenue@0 63 local COMMAND_SPELL = "^SPELL (%S.+)"
Nenue@0 64 local COMMAND_MACRO = "^MACRO (%S.+)"
Nenue@0 65 local COMMAND_ITEM = "^ITEM (%S.+)"
Nenue@0 66 local COMMAND_MOUNT = "^CLICK KeyBinderMacro:mount(%d+)"
Nenue@0 67 local COMMAND_EQUIPSET = "^CLICK KeyBinderMacro:equipset(%d+)"
Nenue@0 68
Nenue@0 69 local PICKUP_TYPES = {
Nenue@0 70 [COMMAND_SPELL] = PickupSpell,
Nenue@0 71 [COMMAND_MACRO] = PickupMacro,
Nenue@0 72 [COMMAND_ITEM] = PickupItem,
Nenue@0 73 [COMMAND_MOUNT] = C_MountJournal.Pickup,
Nenue@0 74 [COMMAND_EQUIPSET] = PickupEquipmentSet
Nenue@0 75 }
Nenue@0 76 local PICKUP_VALUES = {
Nenue@0 77 [COMMAND_SPELL] = function(name) return select(7, GetSpellInfo(name)) end
Nenue@0 78 }
Nenue@0 79 local CLASS_ICON_TEXTURE = "Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES"
Nenue@0 80 local BORDER_UNASSIGNED = {0.2,0.2,0.2,1 }
Nenue@0 81 local BORDER_ASSIGNED = {0.5,0.5,0.5,1 }
Nenue@0 82 local BORDER_PENDING = {1,0.5,0,1 }
Nenue@0 83
Nenue@1 84 local bindMode = 3
Nenue@0 85 local bindHeader = ''
Nenue@0 86 local specHeader, specTexture, characterHeader = 'SPEC_NAME', 'Interface\\ICONS\\INV_Misc_QuestionMark', 'PLAYER_NAME'
Nenue@1 87 local numButtons = BINDS_PER_ROW * 4
Nenue@0 88 local bindsCommitted = true
Nenue@0 89
Nenue@1 90 local profile, character, specialization, global
Nenue@0 91 local priority = {}
Nenue@0 92 local buttons = {}
Nenue@0 93 local reverts = {}
Nenue@0 94 local KeyButton = {} -- collection of KeyButton template handlers
Nenue@0 95 local Action = {} -- collection of special action buttons for special binds
Nenue@0 96 local protected = {
Nenue@0 97 ['OPENCHATSLASH'] = true,
Nenue@0 98 ['OPENCHAT'] = true,
Nenue@0 99 }
Nenue@0 100 local saveButton, restoreButton, clearButton
Nenue@0 101
Nenue@0 102 --- Returns a value for use with Texture:SetDesaturated()
Nenue@0 103 local CommandIsLocked = function(self)
Nenue@0 104 print('command check: 1-'..(bindMode-1))
Nenue@0 105 local desaturated, layer = false, 3
Nenue@0 106 for i = 1, bindMode-1 do
Nenue@0 107 local tier = priority[i]
Nenue@0 108 local existing = tier.commands[self.command]
Nenue@0 109 print(' ', i, tier.commands[self.command])
Nenue@0 110 if existing then
Nenue@0 111 if self:GetID() ~= existing then
Nenue@0 112 -- sanitize bad data
Nenue@0 113 tier.commands[self.command] = nil
Nenue@0 114 else
Nenue@0 115 layer = i
Nenue@0 116 desaturated = true
Nenue@0 117 break
Nenue@0 118 end
Nenue@0 119 end
Nenue@0 120 end
Nenue@0 121 return desaturated, layer
Nenue@0 122 end
Nenue@0 123
Nenue@0 124 --- Returns a value for use with Texture:SetDesaturated()
Nenue@0 125 local BindingIsLocked = function(key)
Nenue@0 126 local success = false
Nenue@0 127 for i = 1, bindMode-1 do
Nenue@0 128 local tier = priority[i]
Nenue@0 129 if tier.bindings[key] then
Nenue@0 130 success = true
Nenue@0 131 break
Nenue@0 132 end
Nenue@0 133 end
Nenue@0 134 return success
Nenue@0 135 end
Nenue@0 136
Nenue@0 137 --- Translates GetBindingKey() results into a printable string.
Nenue@0 138 local BindingString = function(...)
Nenue@0 139 local stack = {}
Nenue@0 140 for i = 1, select('#', ...) do
Nenue@0 141 local key = select(i, ...)
Nenue@0 142 stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp')
Nenue@0 143 end
Nenue@0 144
Nenue@0 145 if #stack >= 1 then
Nenue@0 146 return table.concat(stack, ',')
Nenue@0 147 else
Nenue@0 148 return nil
Nenue@0 149 end
Nenue@0 150 end
Nenue@0 151
Nenue@0 152 --- This keeps our KeyDown handler from getting stuck with game controls
Nenue@0 153 KeyButton.OnUpdate = function(self)
Nenue@0 154 if not self.command then
Nenue@0 155 return
Nenue@0 156 end
Nenue@0 157
Nenue@0 158 if self:IsMouseOver() then
Nenue@0 159 if not self.active then
Nenue@0 160 -- only set this handler when the button is activated/mouseOver
Nenue@0 161 self.active = true
Nenue@0 162 self:SetScript('OnKeyDown', kb.bind)
Nenue@0 163
Nenue@0 164 local bindText = self.command
Nenue@0 165 if self.bind:GetText() then
Nenue@0 166 bindText = bindText .. ': |cFF00FF00' .. self.bind:GetText()
Nenue@0 167 end
Nenue@0 168
Nenue@0 169 kb.bindlist:SetText(bindText)
Nenue@0 170 GameTooltip:SetOwner(self)
Nenue@0 171 GameTooltip:SetAnchorType('ANCHOR_BOTTOMRIGHT')
Nenue@0 172 GameTooltip:SetText(self.actionName)
Nenue@0 173 GameTooltip:Show()
Nenue@0 174 end
Nenue@0 175 else
Nenue@0 176 if self.active then
Nenue@0 177 GameTooltip:Hide()
Nenue@0 178 self.active = nil
Nenue@0 179 self:SetScript('OnKeyDown', nil)
Nenue@0 180 end
Nenue@0 181 end
Nenue@0 182 end
Nenue@0 183
Nenue@0 184 --- Cursor pickup handler
Nenue@0 185 -- Walks through PICKUP_TYPES and runs the function if match(command, key) turns up a result.
Nenue@0 186 -- Passes the result through PICKUP_VALUES[pattern]() if defined.
Nenue@0 187
Nenue@0 188 kb.pickup = function(self)
Nenue@0 189 for pattern, pickup in pairs(PICKUP_TYPES) do
Nenue@0 190 local value = self.command:match(pattern)
Nenue@0 191 if value then
Nenue@0 192 if PICKUP_VALUES[pattern] then
Nenue@0 193 value = PICKUP_VALUES[pattern](value)
Nenue@0 194 end
Nenue@0 195
Nenue@0 196 pickup(value)
Nenue@0 197 kb.release(self)
Nenue@0 198 break
Nenue@0 199 end
Nenue@0 200 end
Nenue@0 201 end
Nenue@0 202
Nenue@0 203 --- Setup an action button base on template info
Nenue@0 204 kb.action = function(type, id)
Nenue@0 205
Nenue@0 206 local macroName = type .. id
Nenue@0 207 macroName = macroName:gsub(' ', '')
Nenue@0 208
Nenue@0 209 local attribute = '*macrotext-'..macroName
Nenue@0 210 local value = ACTION_SCRIPT[type]:format(id)
Nenue@0 211 local command = 'CLICK KeyBinderMacro:'.. macroName
Nenue@0 212 profile.macros[attribute] = {value, command}
Nenue@0 213
Nenue@0 214 KeyBinderMacro:SetAttribute(attribute, value)
Nenue@0 215 return command
Nenue@0 216 end
Nenue@0 217
Nenue@0 218 KeyButton.OnDragStart = function(self)
Nenue@0 219 if not self.command then
Nenue@0 220 return
Nenue@0 221 end
Nenue@0 222 kb.pickup(self)
Nenue@0 223 end
Nenue@0 224
Nenue@0 225 KeyButton.OnReceiveDrag = function(self, ...)
Nenue@0 226 print(self:GetName(),'|cFF0088FFreceived|r', ...)
Nenue@0 227 local type, value, subType, subData = GetCursorInfo()
Nenue@0 228 print('GetCursorInfo', type, value, subType, subData)
Nenue@0 229 if type then
Nenue@0 230 if type == 'spell' then
Nenue@0 231 value = subData
Nenue@0 232 end
Nenue@0 233
Nenue@0 234 local command, name, icon, _
Nenue@0 235 if type == 'spell' then
Nenue@0 236 name, _, icon = GetSpellInfo(value)
Nenue@0 237 command = 'SPELL ' .. name
Nenue@0 238 name = ''
Nenue@0 239 elseif type == 'macro' then
Nenue@0 240 name, icon = GetMacroInfo(value)
Nenue@0 241 command = 'MACRO ' .. name
Nenue@0 242 elseif type == 'mount' then
Nenue@0 243 if subType == 0 then
Nenue@0 244 name, _, icon = GetSpellInfo(SUMMON_RANDOM_FAVORITE_MOUNT_SPELL)
Nenue@0 245 value= 0
Nenue@0 246 else
Nenue@0 247 name, _, icon = C_MountJournal.GetMountInfoByID(value)
Nenue@0 248 end
Nenue@0 249 command = kb.action(type, value)
Nenue@0 250 elseif type == 'item' then
Nenue@0 251 name = GetItemInfo(value)
Nenue@0 252 icon = GetItemIcon(value)
Nenue@0 253 command = 'ITEM ' .. name
Nenue@0 254 end
Nenue@0 255 kb.assign(self, command, name, icon)
Nenue@0 256 kb.refresh(self)
Nenue@0 257 ClearCursor()
Nenue@0 258 end
Nenue@0 259 end
Nenue@0 260
Nenue@0 261 KeyButton.OnMouseDown = function(self, click)
Nenue@0 262 print(self:GetName(), 'OnMouseDown', click)
Nenue@0 263 if click == 'LeftButton' then
Nenue@0 264 KeyButton.OnReceiveDrag(self)
Nenue@0 265 elseif click == 'RightButton' then
Nenue@0 266 kb.release(self)
Nenue@0 267 else
Nenue@0 268 kb.bind(self)
Nenue@0 269 end
Nenue@0 270 end
Nenue@0 271
Nenue@0 272
Nenue@0 273 --- Updates the current KeyBinding for the button's command
Nenue@0 274 kb.bind = function(self, key)
Nenue@0 275
Nenue@0 276 print('|cFFFFFF00bind|cFFFFFF00', self:GetID(), '|cFF00FFFF', key)
Nenue@0 277 if not self.command then
Nenue@0 278 return
Nenue@0 279 end
Nenue@0 280
Nenue@0 281 if key:match('[RL]SHIFT') or key:match('[RL]ALT') or key:match('[RL]CTRL') then
Nenue@0 282 return
Nenue@0 283 end
Nenue@0 284
Nenue@0 285 if protected[GetBindingAction(key)] then
Nenue@0 286 return
Nenue@0 287 kb.bindlist:SetText(BINDING_FAILED_PROTECTED:format(key, GetBindingAction(key)))
Nenue@0 288 end
Nenue@0 289
Nenue@0 290 if key == 'ESCAPE' then
Nenue@0 291 local key1, key2 = GetBindingKey(self.command)
Nenue@0 292 if key1 then
Nenue@0 293 SetBinding(key1, nil)
Nenue@0 294 print('Unbound', key1)
Nenue@0 295 end
Nenue@0 296 if key2 then
Nenue@0 297 SetBinding(key2, nil)
Nenue@0 298 print('Unbound', key2)
Nenue@0 299 end
Nenue@0 300 self.active = false
Nenue@0 301 return
Nenue@0 302 end
Nenue@0 303
Nenue@0 304 local modifier = ''
Nenue@0 305 if IsAltKeyDown() then
Nenue@0 306 modifier = 'ALT-'
Nenue@0 307 end
Nenue@0 308 if IsControlKeyDown() then
Nenue@0 309 modifier = modifier.. 'CTRL-'
Nenue@0 310 end
Nenue@0 311 if IsShiftKeyDown() then
Nenue@0 312 modifier = modifier..'SHIFT-'
Nenue@0 313 end
Nenue@0 314
Nenue@0 315 if self.command then
Nenue@0 316 self.binding = modifier..key
Nenue@0 317 self.pending = true
Nenue@0 318 self.border:SetColorTexture(1,.5,0, 1)
Nenue@0 319
Nenue@0 320 local old = GetBindingAction(self.binding)
Nenue@0 321 local binding1, binding2, new1, new2
Nenue@0 322 if old and old ~= self.command then
Nenue@0 323 print('Discarding keybind for', old)
Nenue@0 324 local binding1, binding2 = GetBindingKey(old)
Nenue@0 325 -- need to preserve argument order
Nenue@0 326 end
Nenue@0 327 tinsert(reverts, {old, binding1, binding2, new1, new2})
Nenue@0 328
Nenue@0 329
Nenue@0 330 bindsCommitted = false
Nenue@0 331 SetBinding(self.binding, self.command)
Nenue@0 332 for level, profile in ipairs(priority) do
Nenue@0 333 profile.bindings[self.binding] = (level == bindMode) and self.command or nil
Nenue@0 334 end
Nenue@0 335 print(BINDING_ASSIGNED:format(self.binding, self.command, BINDING_MODE[bindMode]:format(bindHeader)))
Nenue@0 336
Nenue@0 337 kb.refresh(self)
Nenue@0 338 end
Nenue@0 339 end
Nenue@0 340
Nenue@0 341 --- Resets button command
Nenue@0 342 kb.release = function(self)
Nenue@0 343 local index = self:GetID()
Nenue@0 344 self.command = nil
Nenue@0 345 self.actionName = nil
Nenue@0 346 self.macro:SetText(nil)
Nenue@0 347 self.profile = nil
Nenue@0 348 self.bind:SetText(nil)
Nenue@0 349 self.icon:SetTexture(nil)
Nenue@0 350 self.border:SetColorTexture(unpack(BORDER_UNASSIGNED))
Nenue@0 351 self:EnableKeyboard(false)
Nenue@0 352 self:SetScript('OnKeyDown', nil)
Nenue@0 353
Nenue@0 354
Nenue@0 355 if profile.buttons[index] then
Nenue@0 356 profile.buttons[index] = nil
Nenue@0 357 end
Nenue@0 358 end
Nenue@0 359
Nenue@0 360 -- Sets button command
Nenue@0 361
Nenue@0 362 kb.assign = function(self, command, name, icon)
Nenue@0 363 local index = self:GetID()
Nenue@0 364 print('|cFF00FFFFassign|cFF0088FF', index, '|cFFFFFF00'.. (command or 'none'), '|cFF00FF00'.. (name or ''), '|cFF00FFFF' .. (icon or ''))
Nenue@0 365
Nenue@0 366
Nenue@0 367 if command then
Nenue@0 368 if command:match(COMMAND_SPELL) then
Nenue@0 369 name = command:match(COMMAND_SPELL)
Nenue@0 370 end
Nenue@0 371
Nenue@0 372
Nenue@0 373 self:EnableKeyboard(true)
Nenue@0 374 print('profile.buttons['..index..'] |cFF00FFFF=|r ', command, name, icon)
Nenue@0 375 profile.buttons[index] = {command, name, icon}
Nenue@0 376
Nenue@0 377 --- Clean up any residual buttons
Nenue@0 378 local previous = profile.commands[command]
Nenue@0 379 if previous ~= index and buttons[previous] then
Nenue@0 380 kb.release(buttons[previous])
Nenue@0 381 end
Nenue@0 382
Nenue@0 383 profile.commands[command] = index
Nenue@0 384 end
Nenue@0 385
Nenue@0 386 self.profile = bindMode
Nenue@0 387 self.actionName = name
Nenue@0 388 self.command = command
Nenue@0 389 self.icon:SetTexture(icon)
Nenue@0 390 self:RegisterForDrag('LeftButton')
Nenue@0 391 end
Nenue@0 392
Nenue@0 393 --- Retrieves button at index; creates said button and instates any stored parameters
Nenue@0 394 kb.keyslot = function(index)
Nenue@0 395 if not buttons[index] then
Nenue@0 396 local button = CreateFrame('CheckButton', 'KeyBinderSlot'..index, kb, 'KeyButton')
Nenue@0 397 button:SetScript('OnMouseDown', KeyButton.OnMouseDown)
Nenue@0 398 button:SetScript('OnMouseUp', KeyButton.OnMouseUp)
Nenue@0 399 button:SetScript('OnUpdate', KeyButton.OnUpdate)
Nenue@0 400 button:SetScript('OnDragStart', KeyButton.OnDragStart)
Nenue@0 401 button:SetScript('OnReceiveDrag', KeyButton.OnReceiveDrag)
Nenue@0 402 button:SetID(index)
Nenue@0 403
Nenue@0 404 if profile.buttons[index] and type(profile.buttons[index] ) == 'table' then
Nenue@0 405 kb.assign(button, unpack(profile.buttons[index] ))
Nenue@0 406 else
Nenue@0 407 kb.release(button)
Nenue@0 408 end
Nenue@0 409
Nenue@0 410 local x, y = BUTTON_PADDING, - (BUTTON_PADDING + HEADER_OFFSET)
Nenue@0 411 if index ~= 1 then
Nenue@0 412 local col = mod(index, BINDS_PER_ROW)
Nenue@0 413 if col == 0 then
Nenue@0 414 col = BINDS_PER_ROW - 1
Nenue@0 415 else
Nenue@0 416 col = col - 1
Nenue@0 417 end
Nenue@0 418 x = col * (KEY_BUTTON_SIZE + BUTTON_SPACING) + BUTTON_PADDING
Nenue@0 419 y = (ceil(index/ BINDS_PER_ROW)-1) * - (KEY_BUTTON_SIZE + BUTTON_SPACING) - BUTTON_PADDING - HEADER_OFFSET
Nenue@0 420 end
Nenue@0 421 button:SetSize(KEY_BUTTON_SIZE, KEY_BUTTON_SIZE)
Nenue@0 422 button:SetPoint('TOPLEFT', kb, 'TOPLEFT', x, y)
Nenue@0 423 button:Show()
Nenue@0 424 buttons[index] = button
Nenue@0 425 end
Nenue@0 426 return buttons[index]
Nenue@0 427 end
Nenue@0 428
Nenue@0 429 --- Updates profile assignment and button contents
Nenue@0 430 kb.refresh = function(self)
Nenue@0 431 if self.profile ~= bindMode then
Nenue@0 432 if profile.buttons[self:GetID()] then
Nenue@0 433 kb.assign(self, unpack(profile.buttons[self:GetID()]))
Nenue@0 434 else
Nenue@0 435 kb.release(self)
Nenue@0 436 end
Nenue@0 437 end
Nenue@0 438
Nenue@0 439 if self.command then
Nenue@0 440 if self.pending then
Nenue@0 441 self.border:SetColorTexture(unpack(BORDER_PENDING))
Nenue@0 442 else
Nenue@0 443 self.border:SetColorTexture(unpack(BORDER_ASSIGNED))
Nenue@0 444 end
Nenue@0 445 --self.macro:SetText(self.actionName)
Nenue@0 446 self.bind:SetText(BindingString(GetBindingKey(self.command)))
Nenue@0 447 local locked, layer = CommandIsLocked(self)
Nenue@0 448 self.icon:SetDesaturated(locked)
Nenue@0 449 self.icon:SetVertexColor(unpack(BINDING_SCHEME_VERTEX[layer]))
Nenue@0 450 else
Nenue@0 451 self.border:SetColorTexture(unpack(BORDER_UNASSIGNED))
Nenue@0 452 --self.macro:SetText(nil)
Nenue@0 453 self.bind:SetText(nil)
Nenue@0 454 end
Nenue@0 455 end
Nenue@0 456
Nenue@0 457 local SetupUI = function()
Nenue@0 458
Nenue@0 459
Nenue@0 460 kb.tabAnchor = {'TOPLEFT', kb, 'TOPRIGHT', 2, -TAB_OFFSET}
Nenue@0 461 kb.tabGrowth = {'TOPLEFT', nil,'BOTTOMLEFT', 0, -TAB_SPACING}
Nenue@0 462 kb.tabSize = {TAB_HEIGHT, TAB_HEIGHT }
Nenue@0 463 kb.UIPanelAnchor = {'TOPLEFT', kb, 'TOPLEFT', BUTTON_PADDING + 12, -BUTTON_PADDING}
Nenue@0 464 kb.UIPanelGrowth = {'TOPLEFT', nil, 'TOPRIGHT', 14, 0 }
Nenue@0 465 kb.controlsAnchor = {'BOTTOMLEFT', kb, BUTTON_PADDING, BUTTON_PADDING }
Nenue@0 466 kb.controlsGrowth = {'BOTTOMLEFT', nil, 'BOTTOMRIGHT', BUTTON_SPACING, 0}
Nenue@0 467
Nenue@0 468 --tab() frame, name, tooltip, texture, coords
Nenue@0 469 kb:tab('KeyBinderGlobalTab', BINDING_MODE[1], "Interface\\ICONS\\item_azereansphere", {0.15,.85,.15,.85})
Nenue@0 470 kb:tab('KeyBinderCharacterTab', characterHeader, nil)
Nenue@0 471 kb:tab('KeyBinderSpecTab', specHeader, specTexture)
Nenue@0 472 SetPortraitTexture(KeyBinderCharacterTab.icon, 'player')
Nenue@0 473 KeyBinderCharacterTab.icon:SetTexCoord(0.15,.85,.15,.85)
Nenue@0 474
Nenue@0 475 saveButton = kb:button('KeyBinderSaveButton', 'Save', 'Commit all changes.', nil, kb.save)
Nenue@0 476 restoreButton = kb:button('KeyBinderRestoreButton', 'Discard', 'Revert all changes.', nil, kb.restore)
Nenue@0 477 clearButton = kb:button('KeyBinderClearButton', 'Clear Page', 'Release all buttons.', nil, kb.ResetProfile)
Nenue@0 478
Nenue@0 479 kb:uibutton(
Nenue@0 480 'KeyBinderSpellBookButton', 'SpellBook', nil,
Nenue@0 481 function() ToggleSpellBook(BOOKTYPE_SPELL) end,
Nenue@0 482 "Interface\\Spellbook\\Spellbook-Icon")
Nenue@0 483 kb:uibutton(
Nenue@0 484 'KeyBinderTalentFrameButton', 'Talents', nil,
Nenue@0 485 function() ToggleTalentFrame() end,
Nenue@0 486 "Interface\\TargetingFrame\\UI-Classes-Circles",
Nenue@0 487 CLASS_ICON_TCOORDS[strupper(select(2,UnitClass("player")))])
Nenue@0 488
Nenue@0 489 kb:uibutton(
Nenue@0 490 'KeyBinderMacroFrameButton', 'Macros', nil,
Nenue@0 491 function() if MacroFrame then HideUIPanel(MacroFrame) else ShowMacroFrame() end end,
Nenue@0 492 "Interface\\MacroFrame\\MacroFrame-Icon")
Nenue@0 493
Nenue@0 494 kb:uibutton(
Nenue@0 495 'KeyBinderInventoryButton', 'Bags', nil,
Nenue@0 496 function() OpenAllBags() end,
Nenue@0 497 "Interface\\BUTTONS\\Button-Backpack-Up")
Nenue@0 498
Nenue@0 499 kb.info:SetPoint('TOPLEFT', kb.UIPanels[1], 'BOTTOMLEFT', 0, -BUTTON_SPACING)
Nenue@0 500 HEADER_OFFSET = kb.UIPanels[1]:GetHeight() + BUTTON_PADDING
Nenue@0 501 FOOTER_OFFSET = saveButton:GetHeight() + BUTTON_PADDING
Nenue@0 502 end
Nenue@0 503
Nenue@0 504 --- Invokes the KeyBinder frame (from the /kb function or some other source)
Nenue@0 505 kb.ui = function()
Nenue@1 506 if not db.showUI then
Nenue@0 507 return
Nenue@0 508 end
Nenue@0 509
Nenue@0 510 if not kb:IsVisible() then
Nenue@0 511 kb:Show()
Nenue@1 512 db.showUI = true
Nenue@0 513 end
Nenue@0 514
Nenue@0 515 if not kb.loaded then
Nenue@0 516 SetupUI()
Nenue@0 517 kb.loaded = true
Nenue@0 518 end
Nenue@0 519
Nenue@0 520 for i = 1, numButtons do
Nenue@0 521 kb.refresh(kb.keyslot(i))
Nenue@0 522 end
Nenue@0 523
Nenue@0 524 if bindMode == BINDING_TYPE_SPECIALIZATION then
Nenue@0 525 bindHeader = select(2,GetSpecializationInfo(GetSpecialization()))
Nenue@0 526 elseif bindMode == BINDING_TYPE_CHARACTER then
Nenue@0 527 bindHeader = UnitName('player')
Nenue@0 528 else
Nenue@0 529 bindHeader = ''
Nenue@0 530 end
Nenue@0 531
Nenue@0 532 if bindsCommitted then
Nenue@0 533 KeyBinderSaveButton:Disable()
Nenue@0 534 KeyBinderRestoreButton:Disable()
Nenue@0 535 else
Nenue@0 536 KeyBinderSaveButton:Enable()
Nenue@0 537 KeyBinderRestoreButton:Enable()
Nenue@0 538 end
Nenue@0 539
Nenue@0 540 --- panel attributes
Nenue@0 541 local numRows = numButtons/BINDS_PER_ROW
Nenue@0 542 kb:SetHeight( numRows * (KEY_BUTTON_SIZE) + (numRows - 1) * BUTTON_SPACING + HEADER_OFFSET + FOOTER_OFFSET + BUTTON_PADDING * 2)
Nenue@0 543 kb:SetWidth((BINDS_PER_ROW - 1) * BUTTON_SPACING + BINDS_PER_ROW * KEY_BUTTON_SIZE + BUTTON_PADDING * 2)
Nenue@0 544 kb.bg:SetColorTexture(unpack(BINDING_SCHEME_COLOR[bindMode]))
Nenue@0 545
Nenue@0 546
Nenue@0 547 for i, tab in ipairs(kb.tabButtons) do
Nenue@0 548
Nenue@0 549 local n = tab:GetNormalTexture()
Nenue@0 550 local tabTexture = "Interface\\Buttons\\UI-Quickslot2"
Nenue@0 551 local left, top, right, bottom = -12, 12, 13, -13
Nenue@0 552 if i == bindMode then
Nenue@0 553 tabTexture = "Interface\\Buttons\\CheckButtonGlow"
Nenue@0 554 left, top, right, bottom = -14, 14, 15, -15
Nenue@0 555 end
Nenue@0 556 n:SetTexture(tabTexture)
Nenue@0 557 n:SetPoint('TOPLEFT', tab, 'TOPLEFT', left, top)
Nenue@0 558 n:SetPoint('BOTTOMRIGHT', tab, 'BOTTOMRIGHT', right, bottom)
Nenue@0 559 end
Nenue@0 560 end
Nenue@0 561
Nenue@0 562 kb.loadbinds = function (bindings)
Nenue@0 563 for key, command in pairs(bindings) do
Nenue@0 564 -- store for reversion
Nenue@0 565 local oldAction = GetBindingAction(key)
Nenue@0 566 if oldAction ~= command then
Nenue@0 567 local bind1, bind2 = GetBindingKey(oldAction)
Nenue@0 568 if bind1 and not reverts[bind1] then
Nenue@0 569 reverts[bind1] = oldAction
Nenue@0 570 end
Nenue@0 571 if bind2 and not reverts[bind2] then
Nenue@0 572 reverts[bind2] = oldAction
Nenue@0 573 end
Nenue@0 574 end
Nenue@0 575 SetBindings(key, command)
Nenue@0 576 end
Nenue@0 577 SaveBindings()
Nenue@0 578 end
Nenue@0 579
Nenue@0 580 local ACTION_BARS = {
Nenue@1 581 {'ActionButton', 0},
Nenue@1 582 {'MultiBarLeftButton', 24},
Nenue@1 583 {'MultiBarRightButton', 36},
Nenue@1 584 {'MultiBarBottomRighttButton', 48},
Nenue@1 585 {'MultiBarBottomLeftButton', 60},
Nenue@0 586 }
Nenue@1 587 kb.HotKeyText = function (slot)
Nenue@1 588 local i, offset = 0, 0
Nenue@1 589 local actionbar
Nenue@1 590
Nenue@1 591 -- figure out which bar the slot belongs to
Nenue@1 592 for i, bar in ipairs(ACTION_BARS) do
Nenue@1 593 actionbar, offset = unpack(ACTION_BARS[i])
Nenue@1 594 if bar[2] > slot then
Nenue@1 595 break
Nenue@0 596 end
Nenue@0 597 end
Nenue@1 598 local button = _G[actionbar .. (slot - offset)]
Nenue@1 599
Nenue@1 600 if not button then
Nenue@1 601 return
Nenue@1 602 end
Nenue@1 603
Nenue@1 604 local type, id, subType, subID = GetActionInfo(slot)
Nenue@1 605
Nenue@1 606 if not type then
Nenue@1 607 return
Nenue@1 608 end
Nenue@1 609
Nenue@1 610 local bind, command
Nenue@1 611 if type == 'spell' then
Nenue@1 612 local name = GetSpellInfo(id)
Nenue@1 613 command = 'SPELL '..name
Nenue@1 614 elseif type == 'macro' then
Nenue@1 615 command = 'MACRO ' .. id
Nenue@1 616 else
Nenue@1 617 return
Nenue@1 618 end
Nenue@1 619 bind = GetBindingKey(command)
Nenue@1 620 print(slot, bind, '->', command)
Nenue@1 621 if bind then
Nenue@1 622 button.HotKey:SetText(BindingString(bind))
Nenue@1 623 button.HotKey:Show()
Nenue@1 624 end
Nenue@0 625 end
Nenue@0 626
Nenue@0 627 kb.InitProfile = function(profile)
Nenue@0 628 profile.buttons = profile.buttons or {}
Nenue@0 629 profile.commands = profile.commands or {}
Nenue@0 630 profile.bindings = profile.bindings or {}
Nenue@0 631 profile.macros = profile.macros or {}
Nenue@0 632 return profile
Nenue@0 633 end
Nenue@0 634 kb.ResetProfile = function()
Nenue@0 635
Nenue@0 636 for i, button in pairs(buttons) do
Nenue@0 637 kb.release(button)
Nenue@0 638 end
Nenue@0 639
Nenue@0 640 profile.commands = {}
Nenue@0 641 profile.bindings = {}
Nenue@0 642 profile.macros = {}
Nenue@0 643 end
Nenue@0 644
Nenue@0 645 --- Gives us the profile structure to work with while instating data
Nenue@0 646 kb.profile = function(name)
Nenue@1 647 global = kb.InitProfile(db)
Nenue@0 648 profile = global
Nenue@0 649 local subtitle
Nenue@0 650 if name then
Nenue@1 651 db[name] = db[name] or {}
Nenue@1 652 db[name] = kb.InitProfile(db[name])
Nenue@1 653 character = db[name]
Nenue@0 654 local spec = GetSpecialization()
Nenue@0 655 if spec then
Nenue@1 656 db[name][spec] = db[name][spec] or {}
Nenue@1 657 profile = kb.InitProfile(db[name][spec])
Nenue@0 658 bindMode = BINDING_TYPE_SPECIALIZATION
Nenue@0 659 subtitle = select(2,GetSpecializationInfo(spec))
Nenue@1 660 specialization = db[name][spec]
Nenue@0 661 else
Nenue@1 662 profile = kb.InitProfile(db[name])
Nenue@0 663 bindMode = BINDING_TYPE_CHARACTER
Nenue@0 664 subtitle = name
Nenue@0 665 specialization = character
Nenue@0 666 end
Nenue@0 667 end
Nenue@0 668 priority = {global, character, specialization }
Nenue@0 669
Nenue@0 670
Nenue@0 671
Nenue@1 672 if not db.bindsPage then
Nenue@1 673 db.bindsPage = bindMode
Nenue@0 674 end
Nenue@1 675 bindMode = db.bindsPage
Nenue@0 676
Nenue@0 677
Nenue@0 678 if not BINDING_MODE[bindMode] then
Nenue@0 679 bindMode = 3
Nenue@1 680 db.bindsPage = 3
Nenue@0 681 print('overriding', bindMode)
Nenue@0 682 end
Nenue@0 683
Nenue@0 684 profile = priority[bindMode]
Nenue@0 685
Nenue@0 686
Nenue@0 687 local _
Nenue@0 688 _, specHeader, _, specTexture = GetSpecializationInfo(GetSpecialization())
Nenue@0 689 print(GetSpecializationInfo(GetSpecialization()))
Nenue@0 690 specHeader = BINDING_MODE[2]:format(specHeader)
Nenue@0 691 characterHeader = BINDING_MODE[2]:format(UnitName('player'))
Nenue@0 692
Nenue@0 693 print('Using binding profile |cFF00FF88'..BINDING_MODE[bindMode]:format(subtitle)..'|r')
Nenue@0 694 end
Nenue@0 695
Nenue@0 696 kb.SelectTab = function(self)
Nenue@0 697 bindMode = self:GetID()
Nenue@0 698 profile = priority[self:GetID()]
Nenue@1 699 db.bindsPage = self:GetID()
Nenue@0 700 kb.ui()
Nenue@0 701 end
Nenue@0 702 kb.save = function()
Nenue@0 703 SaveBindings(GetCurrentBindingSet())
Nenue@0 704 bindsCommitted = true
Nenue@0 705 for i, button in ipairs(buttons) do
Nenue@0 706 button.pending = false
Nenue@0 707 end
Nenue@0 708
Nenue@0 709 kb.ui()
Nenue@0 710 print('Bindings saved.')
Nenue@0 711 end
Nenue@0 712 kb.restore = function()
Nenue@0 713 for i, button in pairs(buttons) do
Nenue@0 714 button.pending = false
Nenue@0 715 end
Nenue@0 716 bindsCommitted = true
Nenue@0 717 LoadBindings(GetCurrentBindingSet())
Nenue@0 718 print('All changes discarded.')
Nenue@0 719 end
Nenue@0 720
Nenue@0 721 --- Tells all the hud buttons what to do
Nenue@0 722 kb.init = function()
Nenue@0 723 KeyBinderMacro:SetAttribute('*type*', 'macro')
Nenue@0 724 end
Nenue@0 725
Nenue@0 726 --- Get started
Nenue@0 727 kb.variables = function()
Nenue@1 728 SkeletonKeyDB = SkeletonKeyDB or {}
Nenue@1 729 db = SkeletonKeyDB
Nenue@0 730 kb.profile(GetUnitName('player', true))
Nenue@0 731 for i = 1, 3 do
Nenue@0 732 for attribute, data in pairs(priority[i].macros) do
Nenue@0 733 KeyBinderMacro:SetAttribute(attribute, data[1])
Nenue@0 734 end
Nenue@0 735 end
Nenue@0 736
Nenue@0 737 kb.UPDATE_BINDINGS()
Nenue@0 738 kb:RegisterEvent('UPDATE_BINDINGS')
Nenue@0 739 kb:RegisterEvent('UPDATE_MACROS')
Nenue@0 740 kb:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED')
Nenue@0 741 kb:RegisterEvent('PLAYER_EQUIPMENT_CHANGED')
Nenue@0 742 kb:RegisterEvent('PLAYER_REGEN_DISABLED')
Nenue@0 743 kb:RegisterEvent('PLAYER_REGEN_ENABLED')
Nenue@0 744 kb:RegisterEvent('ACTIONBAR_SLOT_CHANGED')
Nenue@0 745 end
Nenue@0 746
Nenue@0 747 kb.close = function()
Nenue@1 748 db.showUI = false
Nenue@0 749 kb:Hide()
Nenue@0 750 end
Nenue@0 751
Nenue@0 752 kb.PLAYER_REGEN_DISABLED = function()
Nenue@1 753 if db.showUI then
Nenue@0 754 kb:Hide()
Nenue@0 755 end
Nenue@0 756 end
Nenue@0 757
Nenue@0 758 kb.PLAYER_REGEN_ENABLED = function()
Nenue@1 759 if db.showUI then
Nenue@0 760 kb.ui()
Nenue@0 761 end
Nenue@0 762 end
Nenue@0 763 --- Refresh buttons if macros are updated
Nenue@0 764 kb.UPDATE_BINDINGS = function()
Nenue@1 765 for i = 1, 120 do
Nenue@1 766 print(i)
Nenue@1 767 kb.HotKeyText(i)
Nenue@1 768 end
Nenue@1 769 if db.showUI then
Nenue@0 770 kb.ui()
Nenue@0 771 end
Nenue@0 772 end
Nenue@0 773
Nenue@1 774 kb.ACTIONBAR_SLOT_CHANGED = function(self, event, slot)
Nenue@1 775 kb.HotKeyText(slot)
Nenue@0 776 end
Nenue@0 777
Nenue@0 778 kb.UPDATE_MACROS = kb.UPDATE_BINDINGS
Nenue@0 779 SLASH_KB1 = "/kb"
Nenue@1 780 SlashCmdList.KB = function(self, input)
Nenue@1 781 if db.showUI then
Nenue@1 782 db.showUI = false
Nenue@0 783 print('|cFFFFFF00KeyBinds|r trace, |cFFFF0000OFF|r.')
Nenue@0 784 kb:Hide()
Nenue@0 785 else
Nenue@1 786 db.showUI = true
Nenue@0 787 print('|cFFFFFF00KeyBinds|r trace, |cFF00FF00ON|r.')
Nenue@0 788 kb.ui()
Nenue@0 789 end
Nenue@1 790 end