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 |
