Mercurial > wow > skeletonkey
comparison KeyButton.lua @ 74:9824d524a661
- binding slot mixin:
- store key binding definitions under their slot's data table
- apply action button attributes when a slot is assigned
- obtain correct macro body text when a macro is slotted
- fix algorithm for resolving renamed macro indices
- move spell detail lookup code out of mixin script
- event chains:
- initialize addon from PLAYER_LOGIN
- reload keybinds from PLAYER_SPECIALIZATION_CHANGED, after spec profile is resolved
- refresh interface content from SPELLS_CHANGED
- hotkey text:
- restore communication and detection of key binding updates and reflect them accordingly
- properly respond to dynamic bindings that result from talent updates
author | Nenue |
---|---|
date | Sat, 14 Jan 2017 02:29:33 -0500 |
parents | c48913c5924c |
children | 6623b7f2c1ca |
comparison
equal
deleted
inserted
replaced
73:68365bda5ab5 | 74:9824d524a661 |
---|---|
3 -- Created: 7/28/2016 11:26 PM | 3 -- Created: 7/28/2016 11:26 PM |
4 -- %file-revision% | 4 -- %file-revision% |
5 -- Deals with display and manipulation of binding slots | 5 -- Deals with display and manipulation of binding slots |
6 | 6 |
7 local _, kb = ... | 7 local _, kb = ... |
8 local print = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('SkeletonKey', ...) end or function() end | 8 local print = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('KeyButton', ...) end or function() end |
9 local cprint = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('Cfg', ...) end or function() end | 9 local cprint = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('Cfg', ...) end or function() end |
10 local L = kb.L | 10 local L = kb.L |
11 local type, tonumber, tostring, tinsert, tremove, ipairs, pairs = type, tonumber, tostring, tinsert, tremove, ipairs, pairs | 11 local type, tonumber, tostring, tinsert, tremove, ipairs, pairs = type, tonumber, tostring, tinsert, tremove, ipairs, pairs |
12 local _G, unpack, select, tostring = _G, unpack, select, tostring | 12 local _G, unpack, select, tostring = _G, unpack, select, tostring |
13 local GetSpellBookItemName, GetSpellBookItemTexture, GetSpellBookItemInfo, GetPetActionInfo = GetSpellBookItemName, GetSpellBookItemTexture, GetSpellBookItemInfo, GetPetActionInfo | 13 local GetSpellBookItemName, GetSpellBookItemTexture, GetSpellBookItemInfo, GetPetActionInfo = GetSpellBookItemName, GetSpellBookItemTexture, GetSpellBookItemInfo, GetPetActionInfo |
168 ResetCursor() | 168 ResetCursor() |
169 return | 169 return |
170 end | 170 end |
171 | 171 |
172 | 172 |
173 local name, icon, _ | 173 local name, icon, _, macroName, macroText |
174 local pickupID, pickupBook | 174 local pickupID, pickupBook |
175 | 175 |
176 if actionType == 'spell' then | 176 if actionType == 'spell' then |
177 actionID = subData | 177 actionID = subData |
178 name, _, icon = GetSpellInfo(actionID) | 178 name, _, icon = GetSpellInfo(actionID) |
179 | 179 |
180 elseif actionType == 'macro' then | 180 elseif actionType == 'macro' then |
181 name, icon = GetMacroInfo(actionID) | 181 name, icon, macroText = GetMacroInfo(actionID) |
182 macroName = name | |
182 elseif actionType == 'petaction' then | 183 elseif actionType == 'petaction' then |
183 if CURSOR_SPELLSLOT and CURSOR_BOOKTYPE then | 184 if CURSOR_SPELLSLOT and CURSOR_BOOKTYPE then |
184 | 185 |
185 local spellType, spellID = GetSpellBookItemInfo(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE) | 186 local spellType, spellID = GetSpellBookItemInfo(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE) |
186 local spellName, spellText = GetSpellBookItemName(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE) | 187 local spellName, spellText = GetSpellBookItemName(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE) |
215 local speciesID, customName, level, xp, maxXp, displayID, isFavorite, petName, petIcon, petType, creatureID = GetPetInfoByPetID(actionID) | 216 local speciesID, customName, level, xp, maxXp, displayID, isFavorite, petName, petIcon, petType, creatureID = GetPetInfoByPetID(actionID) |
216 name = customName or petName | 217 name = customName or petName |
217 icon = petIcon | 218 icon = petIcon |
218 | 219 |
219 end | 220 end |
220 local macroName, macroText, command = kb.RegisterAction(actionType, actionID, name) | 221 local _, macroBody, command = kb.RegisterAction(actionType, actionID, name) |
221 local slotInfo = { | 222 local slotInfo = { |
222 command = command, | 223 command = command, |
223 actionName = name, | 224 actionName = name, |
224 iconPath = icon, | 225 iconPath = icon, |
225 actionType = actionType, | 226 actionType = actionType, |
226 actionID = actionID, | 227 actionID = actionID, |
227 macroName = macroName, | 228 macroName = macroName, |
228 macroText = macroText, | 229 macroText = macroText or macroBody, |
229 spellbookSlot = pickupID, | 230 spellbookSlot = pickupID, |
230 spellbookType = pickupBook, | 231 spellbookType = pickupBook, |
231 assignedKeys = {GetBindingKey(command)} | 232 assignedKeys = {GetBindingKey(command)} |
232 } | 233 } |
233 | 234 |
240 popup.args = {slotInfo} | 241 popup.args = {slotInfo} |
241 SkeletonKey:SetScript('OnMouseWheel', nil) -- disable scrolling | 242 SkeletonKey:SetScript('OnMouseWheel', nil) -- disable scrolling |
242 StaticPopup_Show('SKELETONKEY_CONFIRM_ASSIGN_SLOT') | 243 StaticPopup_Show('SKELETONKEY_CONFIRM_ASSIGN_SLOT') |
243 else | 244 else |
244 kb.currentProfile.buttons[self:GetID()] = slotInfo | 245 kb.currentProfile.buttons[self:GetID()] = slotInfo |
246 if #slotInfo.assignedKeys >= 1 then | |
247 kb:print('Obtained following hotkeys:', table.concat(slotInfo.assignedKeys, ', ')) | |
248 | |
249 end | |
250 kb.LoadBinding(slotInfo) | |
245 self:SetSlot(slotInfo) | 251 self:SetSlot(slotInfo) |
246 self:UpdateSlot() | 252 self:UpdateSlot() |
247 self.active = nil | 253 self.active = nil |
248 ClearCursor() | 254 ClearCursor() |
249 ResetCursor() | 255 ResetCursor() |
310 | 316 |
311 local borderType = BORDER_UNASSIGNED | 317 local borderType = BORDER_UNASSIGNED |
312 | 318 |
313 if self.command then | 319 if self.command then |
314 | 320 |
315 print('|cFFFF4400', self.actionName, #self.assignedKeys, self.assignedKeys) | 321 print('|cFFFF4400', self.actionName, #self.assignedKeys, table.concat(self.assignedKeys, ',')) |
316 print(table.concat(self.assignedKeys, ',')) | 322 print(self.isAvailable) |
317 print(self.actionID) | 323 print(self.actionID) |
318 self.bindingText= kb.BindingString(unpack(self.assignedKeys)) | 324 self.bindingText= kb.BindingString(unpack(self.assignedKeys)) |
319 if not self.isAvailable then | 325 if not self.isAvailable then |
320 borderType = BORDER_DYNAMIC | 326 borderType = BORDER_DYNAMIC |
321 self.ignoreTexture:Show() | 327 self.ignoreTexture:Show() |
334 if self.actionType == 'macro' then | 340 if self.actionType == 'macro' then |
335 self.macro:Show() | 341 self.macro:Show() |
336 else | 342 else |
337 self.macro:Hide() | 343 self.macro:Hide() |
338 if self.actionType == 'spell' then | 344 if self.actionType == 'spell' then |
339 local dummy = GetSpellInfo(self.actionName) | 345 self.isAvailable = GetSpellInfo(self.actionName) and true or false |
340 if not dummy then | |
341 self.icon:SetDesaturated(true) | |
342 else | |
343 self.icon:SetDesaturated(false) | |
344 end | |
345 | |
346 end | 346 end |
347 end | 347 end |
348 | 348 |
349 | 349 |
350 if self.dynamicType == 'profession' then | 350 if self.dynamicType == 'profession' then |
376 kb.DeactivateSlot(self) | 376 kb.DeactivateSlot(self) |
377 end | 377 end |
378 | 378 |
379 end | 379 end |
380 | 380 |
381 self.ignoreTexture:SetShown(self.command and not self.isAvailable) | |
382 | |
383 if not self.isAvailable then | 381 if not self.isAvailable then |
384 self.bind:SetTextColor(0.7,0.7,0.7,1) | 382 self.bind:SetTextColor(.7,.7,.7,1) |
383 self.ignoreTexture:SetShown(self.command and true) | |
384 self.icon:SetVertexColor(.5,.5,.5) | |
385 else | 385 else |
386 self.ignoreTexture:SetShown(false) | |
386 self.bind:SetTextColor(1,1,1,1) | 387 self.bind:SetTextColor(1,1,1,1) |
388 self.icon:SetVertexColor(1,1,1) | |
387 end | 389 end |
388 | 390 |
389 | 391 |
390 if kb.saveTarget and kb.saveTarget ~= self then | 392 if kb.saveTarget and kb.saveTarget ~= self then |
391 self:SetAlpha(0.25) | 393 self:SetAlpha(0.25) |
457 self.icon:SetTexture(nil) | 459 self.icon:SetTexture(nil) |
458 self.ignoreTexture:Hide() | 460 self.ignoreTexture:Hide() |
459 | 461 |
460 end | 462 end |
461 | 463 |
462 local spells = {} | 464 local DoMacroCheck = function(name, macroText, searchID, roughResult) |
463 local SkeletonKey_GetGenericSpell = function(spellName, spellID, icon) | 465 |
464 if not spells[spellID] then | 466 return matchID, matchName, matchBody, endOfSearch |
465 spells[spellID] = {} | 467 end |
466 spells[spellID].actionType = 'spell' | |
467 spells[spellID].actionID = spellID | |
468 spells[spellID].actionName = spellName | |
469 spells[spellID].iconPath = icon | |
470 spells[spellID].statusText = '|cFFBBBBBBSpell|r' | |
471 spells[spellID].dynamicType = nil | |
472 end | |
473 return spells[spellID] | |
474 end | |
475 | |
476 local tempInfo = {} | |
477 -- tries to resolve spells from talent overrides/profession book/etc | |
478 local dynamicTypes = {['profession'] = 'ProfessionCache', ['talent'] = 'TalentCache', ['petaction'] = 'PetInfoCache'} | |
479 local SkeletonKey_GetSpellDetails = function(self) | |
480 | |
481 local spellName, spellID, command, icon = self.actionName, self.actionID, self.command, self.iconPath | |
482 | |
483 | |
484 print(' In:', spellName, spellID, command) | |
485 print(GetSpellInfo(spellName or spellID)) | |
486 local internalName, _, internalIcon, _, _, _, _ = GetSpellInfo(spellName or spellID) | |
487 local isAvailable = internalName and true | |
488 | |
489 if internalName and (internalName ~= spellName) then | |
490 -- it's a binding for the originating spell, leave it as is | |
491 print(' |cFFFF4400spell is an override(', internalName, '~=', spellName,') leave the name info alone') | |
492 self.statusText = '|cFFFFFF00Spell|r' | |
493 self.isAvailable = true | |
494 return | |
495 end | |
496 | |
497 -- let's us match spells replaced by talents | |
498 local info = kb.DynamicSpells[internalName or spellName] | |
499 if not info then | |
500 local dynamicType, dynamicIndex, dynamicSubIndex = command:match("(%a+)_(%S+)_(%S+)") | |
501 if kb.DynamicSpells[dynamicType] then | |
502 print('|cFFFF4400resolving dynamic type index:', internalName, spellName, command) | |
503 dynamicIndex = tonumber(dynamicIndex) | |
504 dynamicSubIndex = tonumber(dynamicSubIndex) | |
505 local cache = kb.DynamicSpells[dynamicType] | |
506 print('type:', dynamicType) | |
507 if dynamicIndex and cache[dynamicIndex] then | |
508 info = kb.DynamicSpells[dynamicType][dynamicIndex] | |
509 print('index:', dynamicIndex) | |
510 if dynamicSubIndex and info[dynamicSubIndex] then | |
511 info = info[dynamicSubIndex] | |
512 print('sub-index:', dynamicSubIndex) | |
513 end | |
514 isAvailable = true | |
515 end | |
516 end | |
517 if not info then | |
518 info = SkeletonKey_GetGenericSpell(spellName, spellID, internalIcon or icon) | |
519 end | |
520 end | |
521 info.isAvailable = isAvailable | |
522 | |
523 print('|cFF00FF88SpellDetails:|r', info.actionName, info.actionID, info.dynamicType, info.isAvailable) | |
524 for k,v in pairs(info) do | |
525 --cprint(' ',k,v) | |
526 self[k] = v | |
527 end | |
528 | |
529 return info | |
530 end | |
531 | |
532 --- Assigns the slot via table copy; any manipulations from this point are temporary and | 468 --- Assigns the slot via table copy; any manipulations from this point are temporary and |
533 function skb:SetSlot(slotInfo) | 469 function skb:SetSlot(slotInfo) |
534 print('slot info', self:GetID()) | 470 print('slot info', self:GetID()) |
535 | 471 |
536 for k,v in pairs(slotInfo) do | 472 for k,v in pairs(slotInfo) do |
547 print('|cFFFFFF00SetSlot|r:', self:GetID()) | 483 print('|cFFFFFF00SetSlot|r:', self:GetID()) |
548 if self.command then | 484 if self.command then |
549 | 485 |
550 isBound = kb.IsCommandBound(self, self.command) | 486 isBound = kb.IsCommandBound(self, self.command) |
551 if actionType == 'spell' then | 487 if actionType == 'spell' then |
552 local info = SkeletonKey_GetSpellDetails(self) | 488 local info = kb.ResolveSpellSlot(self) |
553 name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook = self.actionName, self.iconPath, self.actionType, self.actionID, self.macroName, self.macroText, self.spellbookSlot, self.spellbookType | 489 name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook = self.actionName, self.iconPath, self.actionType, self.actionID, self.macroName, self.macroText, self.spellbookSlot, self.spellbookType |
554 self.isAvailable = info and info.isAvailable | 490 self.isAvailable = info and info.isAvailable |
555 elseif actionType == 'petaction' then | 491 elseif actionType == 'petaction' then |
556 self.dynamicType = 'petaction' | 492 self.dynamicType = 'petaction' |
557 local specialType, specialNum = command:match(actionType..'_([%a%s]+)_(%d)') | 493 local specialType, specialNum = command:match(actionType..'_([%a%s]+)_(%d)') |
571 end | 507 end |
572 self.statusText = 'Pet Action' | 508 self.statusText = 'Pet Action' |
573 self.isAvailable = (kb.PetCache.spellslot[name]) | 509 self.isAvailable = (kb.PetCache.spellslot[name]) |
574 elseif actionType == 'macro' then | 510 elseif actionType == 'macro' then |
575 if actionID then | 511 if actionID then |
576 -- look for corruption | 512 |
513 -- Update stored information if it mis-matches | |
577 local nameByID, _, bodyByID = GetMacroInfo(actionID) | 514 local nameByID, _, bodyByID = GetMacroInfo(actionID) |
578 local nameByName, _, bodyByName = GetMacroInfo(name) | 515 --print(bodyByID, "\n", macroText) |
579 if (nameByID ~= name) or (bodyByID ~= macroText) then | 516 if (nameByID ~= name) or (bodyByID ~= macroText) then |
580 local prevIndex = actionID | 517 --kb:print('mismatches for slot', self:GetID(), actionID, ((nameByID ~= name) and 'name' or ''), ((bodyByID ~= macroText) and 'body' or '')) |
581 actionID = GetMacroIndexByName(name) | 518 local matchID, matchName, matchBody, hasMultiple |
582 local firstName, _, firstBody = GetMacroInfo(actionID) | 519 local roughResult = "" |
520 | |
521 local newID = GetMacroIndexByName(name) | |
522 local firstName, _, firstBody = GetMacroInfo(newID) | |
583 if (firstName ~= name) or (firstBody ~= macroText) then | 523 if (firstName ~= name) or (firstBody ~= macroText) then |
524 | |
525 | |
584 -- go even deeper | 526 -- go even deeper |
585 for i = 1, GetNumMacros() do | 527 local numAccount, numCharacter = GetNumMacros() |
586 local searchName, _ , searchBody = GetMacroInfo(i) | 528 local searchID = 1 |
529 while searchID <= (120+numCharacter) do | |
530 --kb:print(searchID) | |
531 | |
532 | |
533 local searchName, _ , searchBody = GetMacroInfo(searchID) | |
587 if (searchName == name) and (searchBody == macroText) then | 534 if (searchName == name) and (searchBody == macroText) then |
535 --kb:print('definitely', matchID, searchName, '\n', searchBody) | |
588 -- complete match | 536 -- complete match |
589 actionID = i | 537 matchID = searchID |
590 kb:print('Macro index changed: |cFFFFFF00', actionType, '|r', name, '(was '..tostring(prevIndex)..', now '..tostring(actionID)..')') | 538 matchName = searchName |
539 matchBody = searchBody | |
591 break | 540 break |
592 elseif (searchName == name) or (searchBody == macroText) then | 541 elseif (searchName == name) or (searchBody == macroText) then |
593 -- partial match, continue the search | 542 -- partial match, continue the search |
594 actionID = i | 543 |
595 kb:print('Macro index changed: |cFFFFFF00', actionType, '|r', name, '(was '..tostring(prevIndex)..', now '..tostring(actionID)..')') | 544 if matchID then |
545 hasMultiple = true | |
546 roughResult = roughResult .. "\n" .. tostring(searchID) .. ':'..tostring(searchName) | |
547 end | |
548 | |
549 matchID = searchID | |
550 matchName = searchName | |
551 matchBody = searchBody | |
552 --kb:print('possibly', matchID, matchName, '\n', matchBody) | |
596 end | 553 end |
554 | |
555 if searchID == numAccount then | |
556 searchID = 120 | |
557 end | |
558 searchID = searchID + 1 | |
597 end | 559 end |
560 else | |
561 matchID = newID | |
562 matchName = firstName | |
563 matchBody = firstBody | |
598 end | 564 end |
599 | 565 |
566 --kb:print(matchID, hasMultiple) | |
567 if hasMultiple then | |
568 kb:print('Macro assignment in slot #'..tostring(self:GetID())..' has multiple possible indexes:\nSaved Info: '..tostring(actionID)..'/'..tostring(name)..'\n|cFFFFFF00', roughResult) | |
569 elseif matchID then | |
570 kb:print('Macro for slot #'..tostring(self:GetID())..' ('..tostring(name)..') has probably changed:', ((actionID ~= newID) and (' |cFFFF4400'..tostring(actionID)..'|r to |cFF00FF88' .. newID .. '|r.') or ''), 'We\'re not sure, so you may want to re-do that assignment.') | |
571 actionID = matchID | |
572 name = matchName | |
573 macroName = matchName | |
574 macroText = matchBody | |
575 end | |
600 end | 576 end |
601 else | 577 else |
602 actionID = GetMacroIndexByName(name) | 578 actionID = GetMacroIndexByName(name) |
603 end | 579 end |
604 self.statusText = 'Macro' | 580 self.statusText = 'Macro ' .. tostring(actionID) |
605 self.isAvailable = true | 581 self.isAvailable = true |
606 else | 582 else |
607 if not actionID then | 583 if not actionID then |
608 actionID = command:match("^KeyBinderMacro:(.+)") | 584 actionID = command:match("^KeyBinderMacro:(.+)") |
609 end | 585 end |
690 self.actionName = name | 666 self.actionName = name |
691 self.command = command | 667 self.command = command |
692 self.iconPath = icon | 668 self.iconPath = icon |
693 self.profile = kb.db.bindMode | 669 self.profile = kb.db.bindMode |
694 self:RegisterForDrag('LeftButton') | 670 self:RegisterForDrag('LeftButton') |
671 | |
672 return slotInfo | |
695 end | 673 end |
696 | 674 |
697 kb.GetCommandAction = function(command) | 675 kb.GetCommandAction = function(command) |
698 for i, data in ipairs(kb.loadedProfiles) do | 676 for i, data in ipairs(kb.loadedProfiles) do |
699 if data.commands[command] then | 677 if data.commands[command] then |