You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
411 lines
11 KiB
411 lines
11 KiB
|
3 years ago
|
-- Neuron is a World of Warcraft® user interface addon.
|
||
|
|
-- Copyright (c) 2017-2021 Britt W. Yazel
|
||
|
|
-- Copyright (c) 2006-2014 Connor H. Chenoweth
|
||
|
|
-- This code is licensed under the MIT license (see LICENSE for details)
|
||
|
|
|
||
|
|
---@class KEYBINDER : Button @define class KEYBINDER is our keybinding object
|
||
|
|
local KEYBINDER = setmetatable({}, { __index = CreateFrame("Button") })
|
||
|
|
Neuron.KEYBINDER = KEYBINDER
|
||
|
|
|
||
|
|
|
||
|
|
local L = LibStub("AceLocale-3.0"):GetLocale("Neuron")
|
||
|
|
LibStub("AceTimer-3.0"):Embed(KEYBINDER)
|
||
|
|
|
||
|
|
|
||
|
|
local DEFAULT_VIRTUAL_KEY = "LeftButton"
|
||
|
|
local NEURON_VIRTUAL_KEY = "Hotkey"
|
||
|
|
|
||
|
|
----------------------------------------------------------
|
||
|
|
|
||
|
|
---Constructor: Create a new Neuron BUTTON object (this is the base object for all Neuron button types)
|
||
|
|
---@param button BUTTON @Parent BUTTON object for this given binder frame
|
||
|
|
---@return KEYBINDER @ A newly created KEYBINDER object
|
||
|
|
function KEYBINDER.new(button)
|
||
|
|
local newKeyBinder = CreateFrame("Button", button:GetName().."BindFrame", button, "NeuronBindFrameTemplate")
|
||
|
|
setmetatable(newKeyBinder, {__index = KEYBINDER})
|
||
|
|
|
||
|
|
newKeyBinder:EnableMouseWheel(true)
|
||
|
|
newKeyBinder:RegisterForClicks("AnyDown")
|
||
|
|
newKeyBinder:SetAllPoints(button)
|
||
|
|
newKeyBinder:SetScript("OnShow", function(self) self:OnShow() end)
|
||
|
|
newKeyBinder:SetScript("OnHide", function(self) self:OnHide() end)
|
||
|
|
newKeyBinder:SetScript("OnEnter", function(self) self:OnEnter() end)
|
||
|
|
newKeyBinder:SetScript("OnLeave", function(self) self:OnLeave() end)
|
||
|
|
newKeyBinder:SetScript("OnClick", function(self, mouseButton) self:OnClick(mouseButton) end)
|
||
|
|
newKeyBinder:SetScript("OnKeyDown", function(self, key) self:OnKeyDown(key) end)
|
||
|
|
newKeyBinder:SetScript("OnMouseWheel", function(self, delta) self:OnMouseWheel(delta) end)
|
||
|
|
|
||
|
|
newKeyBinder.Label:SetText(L["Bind"])
|
||
|
|
newKeyBinder.button = button
|
||
|
|
newKeyBinder.bindType = "button"
|
||
|
|
|
||
|
|
Neuron.BINDIndex[button.class..button.bar.DB.id.."_"..button.id] = newKeyBinder
|
||
|
|
|
||
|
|
button:SetAttribute("hotkeypri", button.keys.hotKeyPri)
|
||
|
|
button:SetAttribute("hotkeys", button.keys.hotKeys)
|
||
|
|
|
||
|
|
newKeyBinder:Hide()
|
||
|
|
|
||
|
|
return newKeyBinder
|
||
|
|
end
|
||
|
|
|
||
|
|
----------------------------------------------------------
|
||
|
|
|
||
|
|
|
||
|
|
--- Returns a string representation of the modifier that is currently being pressed down, if any
|
||
|
|
--- @return string @Field of the key modifiers currently being pressed
|
||
|
|
local function GetModifier()
|
||
|
|
local modifier
|
||
|
|
|
||
|
|
if IsAltKeyDown() then
|
||
|
|
modifier = "ALT-"
|
||
|
|
end
|
||
|
|
|
||
|
|
if IsControlKeyDown() then
|
||
|
|
if modifier then
|
||
|
|
modifier = modifier.."CTRL-";
|
||
|
|
else
|
||
|
|
modifier = "CTRL-";
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if IsShiftKeyDown() then
|
||
|
|
if modifier then
|
||
|
|
modifier = modifier.."SHIFT-";
|
||
|
|
else
|
||
|
|
modifier = "SHIFT-";
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return modifier
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
--- Returns the keybind for a given button
|
||
|
|
--- @return string @The current key that is bound to the selected button
|
||
|
|
function KEYBINDER:GetBindkeyList()
|
||
|
|
|
||
|
|
if not self.button.data then return L["None"] end
|
||
|
|
|
||
|
|
local bindkeys = self.button.keys.hotKeyText:gsub(":", ", ")
|
||
|
|
|
||
|
|
bindkeys = bindkeys:gsub("^, ", "")
|
||
|
|
bindkeys = bindkeys:gsub(", $", "")
|
||
|
|
|
||
|
|
if string.len(bindkeys) < 1 then
|
||
|
|
bindkeys = L["None"]
|
||
|
|
end
|
||
|
|
|
||
|
|
return bindkeys
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Returns the text value of a keybind
|
||
|
|
--- @param key string @The key to look up
|
||
|
|
--- @return string @The text value for the key
|
||
|
|
function KEYBINDER:GetKeyText(key)
|
||
|
|
local keytext
|
||
|
|
|
||
|
|
if key:find("Button") then
|
||
|
|
keytext = key:gsub("([Bb][Uu][Tt][Tt][Oo][Nn])(%d+)","m%2")
|
||
|
|
elseif key:find("NUMPAD") then
|
||
|
|
keytext = key:gsub("NUMPAD","n")
|
||
|
|
keytext = keytext:gsub("DIVIDE","/")
|
||
|
|
keytext = keytext:gsub("MULTIPLY","*")
|
||
|
|
keytext = keytext:gsub("MINUS","-")
|
||
|
|
keytext = keytext:gsub("PLUS","+")
|
||
|
|
keytext = keytext:gsub("DECIMAL",".")
|
||
|
|
elseif key:find("MOUSEWHEEL") then
|
||
|
|
keytext = key:gsub("MOUSEWHEEL","mw")
|
||
|
|
keytext = keytext:gsub("UP","U")
|
||
|
|
keytext = keytext:gsub("DOWN","D")
|
||
|
|
else
|
||
|
|
keytext = key
|
||
|
|
end
|
||
|
|
|
||
|
|
keytext = keytext:gsub("ALT%-","a")
|
||
|
|
keytext = keytext:gsub("CTRL%-","c")
|
||
|
|
keytext = keytext:gsub("SHIFT%-","s")
|
||
|
|
keytext = keytext:gsub("INSERT","Ins")
|
||
|
|
keytext = keytext:gsub("DELETE","Del")
|
||
|
|
keytext = keytext:gsub("HOME","Home")
|
||
|
|
keytext = keytext:gsub("END","End")
|
||
|
|
keytext = keytext:gsub("PAGEUP","PgUp")
|
||
|
|
keytext = keytext:gsub("PAGEDOWN","PgDn")
|
||
|
|
keytext = keytext:gsub("BACKSPACE","Bksp")
|
||
|
|
keytext = keytext:gsub("SPACE","Spc")
|
||
|
|
|
||
|
|
return keytext
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
--- Clears the bindings of a given button
|
||
|
|
--- @param key string @Which key was pressed
|
||
|
|
function KEYBINDER:ClearBindings(key)
|
||
|
|
|
||
|
|
if key then
|
||
|
|
|
||
|
|
local newkey = key:gsub("%-", "%%-")
|
||
|
|
self.button.keys.hotKeys = self.button.keys.hotKeys:gsub(newkey..":", "")
|
||
|
|
|
||
|
|
local keytext = self:GetKeyText(key)
|
||
|
|
self.button.keys.hotKeyText = self.button.keys.hotKeyText:gsub(keytext..":", "")
|
||
|
|
|
||
|
|
else
|
||
|
|
|
||
|
|
ClearOverrideBindings(self.button)
|
||
|
|
self.button.keys.hotKeys = ":"
|
||
|
|
self.button.keys.hotKeyText = ":"
|
||
|
|
end
|
||
|
|
|
||
|
|
self:ApplyBindings()
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
--- Applies binding to button
|
||
|
|
function KEYBINDER:ApplyBindings()
|
||
|
|
|
||
|
|
local virtualKey
|
||
|
|
|
||
|
|
---checks if the button is a Neuron action or a special Blizzard action (such as a zone ability)
|
||
|
|
---this is necessary because Blizzard buttons usually won't work and can give very weird results
|
||
|
|
---if clicked with a virtual key other than the default "LeftButton"
|
||
|
|
if self.button.class == "ActionBar" then
|
||
|
|
virtualKey = NEURON_VIRTUAL_KEY
|
||
|
|
else
|
||
|
|
virtualKey = DEFAULT_VIRTUAL_KEY
|
||
|
|
end
|
||
|
|
|
||
|
|
if self.button:IsVisible() or self.button:GetParent():GetAttribute("concealed") then
|
||
|
|
self.button.keys.hotKeys:gsub("[^:]+", function(key) SetOverrideBindingClick(self.button, self.button.keys.hotKeyPri, key, self.button:GetName(), virtualKey) end)
|
||
|
|
end
|
||
|
|
|
||
|
|
if not InCombatLockdown() then
|
||
|
|
self.button:SetAttribute("hotkeypri", self.button.keys.hotKeyPri)
|
||
|
|
self.button:SetAttribute("hotkeys", self.button.keys.hotKeys)
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
self.button.Hotkey:SetText(self.button.keys.hotKeyText:match("^:([^:]+)") or "")
|
||
|
|
|
||
|
|
if self.button.bindText then
|
||
|
|
self.button.Hotkey:Show()
|
||
|
|
else
|
||
|
|
self.button.Hotkey:Hide()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
--- Processes the change to a key bind
|
||
|
|
--- @param key string @The key to be used
|
||
|
|
function KEYBINDER:ProcessBinding(key)
|
||
|
|
|
||
|
|
--if the button is locked, warn the user as to the locked status
|
||
|
|
if self.button and self.button.keys and self.button.keys.hotKeyLock then
|
||
|
|
UIErrorsFrame:AddMessage(L["Bindings_Locked_Notice"], 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
--if the key being pressed is escape, clear the bindings on the button
|
||
|
|
if key == "ESCAPE" then
|
||
|
|
|
||
|
|
self:ClearBindings()
|
||
|
|
|
||
|
|
--if the key is anything else, keybind the button to this key
|
||
|
|
elseif key then --checks to see if another keybind already has that key, and if so clears it from the other button
|
||
|
|
|
||
|
|
--check to see if any other button has this key bound to it, ignoring locked buttons, and if so remove the key from the other button
|
||
|
|
for _,binder in pairs(Neuron.BINDIndex) do
|
||
|
|
if self.button ~= binder.button and not binder.button.keys.hotKeyLock then
|
||
|
|
binder.button.keys.hotKeys:gsub("[^:]+", function(binding) if key == binding then binder:ClearBindings(binding) binder:ApplyBindings() end end)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--search the current hotKeys to see if our new key is missing, and if so add it
|
||
|
|
local found
|
||
|
|
self.button.keys.hotKeys:gsub("[^:]+", function(binding) if binding == key then found = true end end)
|
||
|
|
|
||
|
|
if not found then
|
||
|
|
local keytext = self:GetKeyText(key)
|
||
|
|
|
||
|
|
self.button.keys.hotKeys = self.button.keys.hotKeys..key..":"
|
||
|
|
self.button.keys.hotKeyText = self.button.keys.hotKeyText..keytext..":"
|
||
|
|
end
|
||
|
|
|
||
|
|
self:ApplyBindings()
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
--update the tooltip to reflect the changes to the keybinds
|
||
|
|
if self:IsVisible() then
|
||
|
|
self:OnEnter()
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
--- OnShow Event handler
|
||
|
|
function KEYBINDER:OnShow()
|
||
|
|
|
||
|
|
if self.button.bar then
|
||
|
|
self:SetFrameLevel(self.button.bar:GetFrameLevel()+1)
|
||
|
|
end
|
||
|
|
|
||
|
|
local priority = ""
|
||
|
|
|
||
|
|
if self.button.keys.hotKeyPri then
|
||
|
|
priority = "|cff00ff00"..L["Priority"].."|r\n"
|
||
|
|
end
|
||
|
|
|
||
|
|
if self.button.keys.hotKeyLock then
|
||
|
|
self.Label:SetText(priority.."|cfff00000"..L["Locked"].."|r")
|
||
|
|
else
|
||
|
|
self.Label:SetText(priority.."|cffffffff"..L["Bind"].."|r")
|
||
|
|
end
|
||
|
|
|
||
|
|
--set a repeating timer when the keybinder is shown to enable or disable Keyboard input on mouseover.
|
||
|
|
self.keybindUpdateTimer = self:ScheduleRepeatingTimer(function()
|
||
|
|
if self:IsMouseOver() then
|
||
|
|
self:EnableKeyboard(true)
|
||
|
|
else
|
||
|
|
self:EnableKeyboard(false)
|
||
|
|
end
|
||
|
|
end, 0.1)
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
--- OnHide Event handler
|
||
|
|
function KEYBINDER:OnHide()
|
||
|
|
--Cancel the repeating time when hiding the bar
|
||
|
|
self:CancelTimer(self.keybindUpdateTimer)
|
||
|
|
end
|
||
|
|
|
||
|
|
--- OnEnter Event handler
|
||
|
|
function KEYBINDER:OnEnter()
|
||
|
|
local name
|
||
|
|
|
||
|
|
---TODO:we should definitely added name strings for pets/companions as well. This was just to get it going
|
||
|
|
if self.button.spellID then
|
||
|
|
name = GetSpellInfo(self.button.spellID)
|
||
|
|
elseif self.button.actionSpell then
|
||
|
|
name = self.button.actionSpell
|
||
|
|
elseif self.button.macroitem then
|
||
|
|
name = self.button.macroitem
|
||
|
|
elseif self.button.macrospell then
|
||
|
|
name = self.button.macrospell --this is kind of a catch-all
|
||
|
|
end
|
||
|
|
|
||
|
|
if not name then
|
||
|
|
name = "Button"
|
||
|
|
end
|
||
|
|
|
||
|
|
self.Select:Show()
|
||
|
|
|
||
|
|
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||
|
|
|
||
|
|
GameTooltip:ClearLines()
|
||
|
|
GameTooltip:SetText("Neuron", 1.0, 1.0, 1.0)
|
||
|
|
GameTooltip:AddLine(L["Keybind_Tooltip_1"] .. ": |cffffffff" .. name .. "|r")
|
||
|
|
GameTooltip:AddLine(L["Keybind_Tooltip_2"] .. ": |cffffffff" .. self:GetBindkeyList() .. "|r")
|
||
|
|
GameTooltip:AddLine(" ")
|
||
|
|
GameTooltip:AddLine(L["Keybind_Tooltip_3"])
|
||
|
|
GameTooltip:AddLine(L["Keybind_Tooltip_4"])
|
||
|
|
GameTooltip:AddLine(L["Keybind_Tooltip_5"])
|
||
|
|
|
||
|
|
GameTooltip:Show()
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
--- OnLeave Event handler
|
||
|
|
function KEYBINDER:OnLeave()
|
||
|
|
self.Select:Hide()
|
||
|
|
GameTooltip:Hide()
|
||
|
|
end
|
||
|
|
|
||
|
|
--- OnClick Event handler
|
||
|
|
--- @param mousebutton string @The button that was clicked
|
||
|
|
function KEYBINDER:OnClick(mousebutton)
|
||
|
|
|
||
|
|
if mousebutton == "LeftButton" then
|
||
|
|
|
||
|
|
if self.button.keys.hotKeyLock then
|
||
|
|
self.button.keys.hotKeyLock = false
|
||
|
|
else
|
||
|
|
self.button.keys.hotKeyLock = true
|
||
|
|
end
|
||
|
|
|
||
|
|
self:OnShow()
|
||
|
|
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
if mousebutton== "RightButton" then
|
||
|
|
if self.button.keys.hotKeyPri then
|
||
|
|
self.button.keys.hotKeyPri = false
|
||
|
|
else
|
||
|
|
self.button.keys.hotKeyPri = true
|
||
|
|
end
|
||
|
|
|
||
|
|
self:ApplyBindings()
|
||
|
|
|
||
|
|
self:OnShow()
|
||
|
|
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local modifier = GetModifier()
|
||
|
|
local key
|
||
|
|
|
||
|
|
if mousebutton == "MiddleButton" then
|
||
|
|
key = "Button3"
|
||
|
|
else
|
||
|
|
key = mousebutton
|
||
|
|
end
|
||
|
|
|
||
|
|
if modifier then
|
||
|
|
key = modifier..key
|
||
|
|
end
|
||
|
|
|
||
|
|
self:ProcessBinding(key)
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
--- OnKeyDown Event handler
|
||
|
|
--- @param key string @The key that was pressed
|
||
|
|
function KEYBINDER:OnKeyDown(key)
|
||
|
|
if key:find("ALT") or key:find("SHIFT") or key:find("CTRL") or key:find("PRINTSCREEN") then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local modifier = GetModifier()
|
||
|
|
|
||
|
|
if modifier then
|
||
|
|
key = modifier..key
|
||
|
|
end
|
||
|
|
|
||
|
|
self:ProcessBinding(key)
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
--- OnMouseWheel Event handler
|
||
|
|
--- @param delta number @direction mouse wheel moved
|
||
|
|
function KEYBINDER:OnMouseWheel(delta)
|
||
|
|
local modifier = GetModifier()
|
||
|
|
local key
|
||
|
|
local action
|
||
|
|
|
||
|
|
if delta > 0 then
|
||
|
|
key = "MOUSEWHEELUP"
|
||
|
|
action = "MousewheelUp"
|
||
|
|
else
|
||
|
|
key = "MOUSEWHEELDOWN"
|
||
|
|
action = "MousewheelDown"
|
||
|
|
end
|
||
|
|
|
||
|
|
if modifier then
|
||
|
|
key = modifier..key
|
||
|
|
end
|
||
|
|
|
||
|
|
self:ProcessBinding(key)
|
||
|
|
end
|