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.
325 lines
11 KiB
325 lines
11 KiB
local _, T = ...
|
|
|
|
local AB = assert(T.ActionBook:compatible(2, 23), "A compatible version of ActionBook is required.")
|
|
local MODERN = select(4,GetBuildInfo()) >= 8e4
|
|
local L = AB:locale()
|
|
|
|
local CreateEdge do
|
|
local edgeSlices = {
|
|
{"TOPLEFT", 0, -1, "BOTTOMRIGHT", "BOTTOMLEFT", 1, 1}, -- L
|
|
{"TOPRIGHT", 0, -1, "BOTTOMLEFT", "BOTTOMRIGHT", -1, 1}, -- R
|
|
{"TOPLEFT", 1, 0, "BOTTOMRIGHT", "TOPRIGHT", -1, -1, ccw=true}, -- T
|
|
{"BOTTOMLEFT", 1, 0, "TOPRIGHT", "BOTTOMRIGHT", -1, 1, ccw=true}, -- B
|
|
{"TOPLEFT", 0, 0, "BOTTOMRIGHT", "TOPLEFT", 1, -1},
|
|
{"TOPRIGHT", 0, 0, "BOTTOMLEFT", "TOPRIGHT", -1, -1},
|
|
{"BOTTOMLEFT", 0, 0, "TOPRIGHT", "BOTTOMLEFT", 1, 1},
|
|
{"BOTTOMRIGHT", 0, 0, "TOPLEFT", "BOTTOMRIGHT", -1, 1}
|
|
}
|
|
function CreateEdge(f, info, bgColor, edgeColor)
|
|
local insets = info.insets
|
|
local es = info.edgeFile and (info.edgeSize or 39) or 0
|
|
if info.bgFile then
|
|
local bg = f:CreateTexture(nil, "BACKGROUND", nil, -7)
|
|
local tileBackground = not not info.tile
|
|
bg:SetTexture(info.bgFile, tileBackground, tileBackground)
|
|
bg:SetPoint("TOPLEFT", (insets and insets.left or 0), -(insets and insets.top or 0))
|
|
bg:SetPoint("BOTTOMRIGHT", -(insets and insets.right or 0), (insets and insets.bottom or 0))
|
|
local n = bgColor or 0xffffff
|
|
bg:SetVertexColor((n - n % 2^16) / 2^16 % 256 / 255, (n - n % 2^8) / 2^8 % 256 / 255, n % 256 / 255, n >= 2^24 and (n - n % 2^24) / 2^24 % 256 / 255 or 1)
|
|
end
|
|
if info.edgeFile then
|
|
local n = edgeColor or 0xffffff
|
|
local r,g,b,a = (n - n % 2^16) / 2^16 % 256 / 255, (n - n % 2^8) / 2^8 % 256 / 255, n % 256 / 255, n >= 2^24 and (n - n % 2^24) / 2^24 % 256 / 255 or 1
|
|
for i=1,#edgeSlices do
|
|
local t, s = f:CreateTexture(nil, "BORDER", nil, -7), edgeSlices[i]
|
|
t:SetTexture(info.edgeFile)
|
|
t:SetPoint(s[1], s[2]*es, s[3]*es)
|
|
t:SetPoint(s[4], f, s[5], s[6]*es, s[7]*es)
|
|
local x1, x2, y1, y2 = 1/128+(i-1)/8, i/8-1/128, 0.0625, 1-0.0625
|
|
if s.ccw then
|
|
t:SetTexCoord(x1,y2, x2,y2, x1,y1, x2,y1)
|
|
else
|
|
t:SetTexCoord(x1, x2, y1, y2)
|
|
end
|
|
t:SetVertexColor(r,g,b,a)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local multilineInput do
|
|
local function onNavigate(self, _x,y, _w,h)
|
|
local scroller = self.scroll
|
|
local occH, occP, y = scroller:GetHeight(), scroller:GetVerticalScroll(), -y
|
|
if occP > y then
|
|
occP = y -- too far
|
|
elseif (occP + occH) < (y+h) then
|
|
occP = y+h-occH -- not far enough
|
|
else
|
|
return
|
|
end
|
|
scroller:SetVerticalScroll(occP)
|
|
local _, mx = scroller.ScrollBar:GetMinMaxValues()
|
|
scroller.ScrollBar:SetMinMaxValues(0, occP < mx and mx or occP)
|
|
scroller.ScrollBar:SetValue(occP)
|
|
end
|
|
local function onClick(self)
|
|
self.input:SetFocus()
|
|
end
|
|
function multilineInput(name, parent, width)
|
|
local scroller = CreateFrame("ScrollFrame", name .. "Scroll", parent, "UIPanelScrollFrameTemplate")
|
|
local input = CreateFrame("Editbox", name, scroller)
|
|
input:SetWidth(width)
|
|
input:SetMultiLine(true)
|
|
input:SetAutoFocus(false)
|
|
input:SetTextInsets(2,4,0,2)
|
|
input:SetFontObject(GameFontHighlight)
|
|
input:SetScript("OnCursorChanged", onNavigate)
|
|
scroller:EnableMouse(1)
|
|
scroller:SetScript("OnMouseDown", onClick)
|
|
scroller:SetScrollChild(input)
|
|
input.scroll, scroller.input = scroller, input
|
|
return input, scroller
|
|
end
|
|
end
|
|
|
|
do -- .macrotext
|
|
local bg = CreateFrame("Frame")
|
|
CreateEdge(bg, {edgeFile="Interface/Tooltips/UI-Tooltip-Border", bgFile="Interface/DialogFrame/UI-DialogBox-Background-Dark", tile=true, edgeSize=16, tileSize=16, insets={left=4,right=4,bottom=4,top=4}}, 0xb2000000, 0xb2b2b2)
|
|
bg:Hide()
|
|
local eb, scroll = multilineInput("ABE_MacroInput", bg, 511)
|
|
eb:SetScript("OnEscapePressed", eb.ClearFocus)
|
|
eb:SetScript("OnEditFocusLost", function()
|
|
local p = bg:GetParent()
|
|
p = p and p.SaveAction and p:SaveAction()
|
|
end)
|
|
scroll:SetPoint("TOPLEFT", 5, -4)
|
|
scroll:SetPoint("BOTTOMRIGHT", -26, 4)
|
|
eb:SetHyperlinksEnabled(true)
|
|
eb:SetScript("OnHyperlinkClick", function(self, link, text, button)
|
|
local pos = string.find(self:GetText(), text, 1, 1)-1
|
|
self:HighlightText(pos, pos + #text)
|
|
if button == "RightButton" and link:match("^rk%d+:") then
|
|
local replace = IsAltKeyDown() and text:match("|h(.-)|h") or ("{{" .. link:match("^rk%d+:(.+)") .. "}}")
|
|
self:Insert(replace)
|
|
self:HighlightText(pos, pos + #replace)
|
|
else
|
|
self:SetCursorPosition(pos + #text)
|
|
end
|
|
self:SetFocus()
|
|
end)
|
|
local function removeEditorLinks(text)
|
|
return (text:gsub("|c%x+|Hrk%d+:([%a:%d/]+)|h.-|h|r", "{{%1}}"))
|
|
end
|
|
local function GetHighlightText(editBox)
|
|
local text, curPos = editBox:GetText(), editBox:GetCursorPosition()
|
|
editBox:Insert("")
|
|
local text2, selStart = editBox:GetText(), editBox:GetCursorPosition()
|
|
local selEnd = selStart + #text - #text2
|
|
if text ~= text2 then
|
|
editBox:SetText(text)
|
|
editBox:SetCursorPosition(curPos)
|
|
editBox:HighlightText(selStart, selEnd)
|
|
end
|
|
return text:sub(selStart+1, selEnd), selStart
|
|
end
|
|
local function ReplaceSelection(editBox, newSelText)
|
|
editBox:Insert(newSelText)
|
|
local cur = editBox:GetCursorPosition()
|
|
editBox:HighlightText(cur-#newSelText, cur)
|
|
end
|
|
eb:SetScript("OnKeyDown", function(self, key)
|
|
if (key == "C" or key == "X") and IsControlKeyDown() then
|
|
local stext = GetHighlightText(self)
|
|
if stext:match("[^|]|H.+|h.*|h") then
|
|
ReplaceSelection(self, removeEditorLinks(stext))
|
|
if key == "C" then
|
|
self._rsText = stext
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
eb:SetScript("OnUpdate", function(self)
|
|
if self._rsText then
|
|
ReplaceSelection(self, self._rsText)
|
|
self._rsText = nil
|
|
end
|
|
end)
|
|
|
|
local decodeSpellLink do
|
|
local names, tag = {}, 0
|
|
function decodeSpellLink(token, sid)
|
|
local forceRank, tname = token == "spellr"
|
|
for id in sid:gmatch("%d+") do
|
|
local name, sr = GetSpellInfo(tonumber(id)), GetSpellSubtext(tonumber(id))
|
|
if sr and sr ~= "" and (forceRank or MODERN) then name = name .. " (" .. sr .. ")" end
|
|
if name and names[name] ~= tag then
|
|
names[name], tname = tag, (tname and (tname .. " / ") or "") .. name
|
|
end
|
|
end
|
|
tag = tag + 1
|
|
return tname and ("|cff71d5ff|Hrk" .. token .. ":" .. sid .. "|h" .. tname .. "|h|r")
|
|
end
|
|
end
|
|
local tagCounter = 0
|
|
local function tagReplace()
|
|
tagCounter = tagCounter + 1
|
|
return "|Hrk" .. tagCounter .. ":"
|
|
end
|
|
function bg:SetAction(owner, action)
|
|
bg:SetParent(nil)
|
|
bg:ClearAllPoints()
|
|
bg:SetAllPoints(owner)
|
|
bg:SetParent(owner)
|
|
bg:Show()
|
|
tagCounter = 0
|
|
eb:SetText((
|
|
(action[1] == "macrotext" and type(action[2]) == "string" and action[2] or "")
|
|
:gsub("{{(spellr?):([%d/]+)}}", decodeSpellLink)
|
|
:gsub("{{mount:ground}}", "|cff71d5ff|Hrkmount:ground|h" .. L"Ground Mount" .. "|h|r")
|
|
:gsub("{{mount:air}}", "|cff71d5ff|Hrkmount:air|h" .. L"Flying Mount" .. "|h|r")
|
|
:gsub("|Hrk", tagReplace)
|
|
))
|
|
end
|
|
function bg:GetAction(into)
|
|
into[1], into[2] = "macrotext", removeEditorLinks(eb:GetText())
|
|
end
|
|
function bg:Release(owner)
|
|
if bg:IsOwned(owner) then
|
|
bg:SetParent(nil)
|
|
bg:ClearAllPoints()
|
|
bg:Hide()
|
|
end
|
|
end
|
|
function bg:IsOwned(owner)
|
|
return bg:GetParent() == owner
|
|
end
|
|
bg.editBox, bg.scrollFrame = bg, eb
|
|
do -- Hook linking
|
|
local old = ChatEdit_InsertLink
|
|
function ChatEdit_InsertLink(link, ...)
|
|
if GetCurrentKeyBoardFocus() == eb then
|
|
local isEmpty = eb:GetText() == ""
|
|
if link:match("item:") then
|
|
eb:Insert((isEmpty and (GetItemSpell(link) and SLASH_USE1 or SLASH_EQUIP1) or "") .. " " .. GetItemInfo(link))
|
|
elseif link:match("spell:") and not IsPassiveSpell(tonumber(link:match("spell:(%d+)"))) then
|
|
eb:Insert((isEmpty and SLASH_CAST1 or "") .. " " .. decodeSpellLink(link:match("(spell):(%d+)")):gsub("|Hrk", tagReplace))
|
|
else
|
|
eb:Insert(link:match("|h%[?(.-[^%]])%]?|h"))
|
|
end
|
|
return true
|
|
else
|
|
return old(link, ...)
|
|
end
|
|
end
|
|
end
|
|
AB:RegisterEditorPanel("macrotext", bg)
|
|
end
|
|
|
|
local RegisterSimpleOptionsPanel do
|
|
local f, e = CreateFrame("Frame")
|
|
f:Hide()
|
|
f.Options = {}
|
|
local function callSave()
|
|
local p = f:GetParent()
|
|
if p and p.SaveAction then
|
|
p:SaveAction()
|
|
end
|
|
end
|
|
for i=1,3 do
|
|
e = CreateFrame("CheckButton", nil, f, "InterfaceOptionsCheckButtonTemplate")
|
|
e:SetHitRectInsets(0, -200, 4, 4)
|
|
e:SetMotionScriptsWhileDisabled(1)
|
|
e:SetScript("OnClick", callSave)
|
|
e:SetPoint("TOPRIGHT", -261, 23-21*i)
|
|
f.Options[i] = e
|
|
end
|
|
|
|
local optionsForHandle, curHandle, curHandleID = {}
|
|
local function IsOwned(self, host)
|
|
return curHandle == optionsForHandle[self] and f:GetParent() == host
|
|
end
|
|
local function Release(self, host)
|
|
if IsOwned(self, host) then
|
|
curHandle, curHandleID = nil
|
|
f:SetParent(nil)
|
|
f:ClearAllPoints()
|
|
f:Hide()
|
|
end
|
|
end
|
|
local function SetAction(self, host, actionTable)
|
|
local opts = optionsForHandle[self]
|
|
assert(actionTable[1] == opts[0], "Invalid editor")
|
|
f:SetParent(nil)
|
|
f:ClearAllPoints()
|
|
f:SetAllPoints(host)
|
|
f:SetParent(host)
|
|
curHandle, curHandleID = opts, actionTable[2]
|
|
local getState = opts.getOptionState
|
|
for i=1,#opts do
|
|
local w, oi, isChecked = f.Options[i], opts[i], false
|
|
w.Text:SetText(opts[oi])
|
|
if getState then
|
|
isChecked = getState(actionTable, oi)
|
|
elseif actionTable[opts[i]] ~= nil then
|
|
isChecked = not not actionTable[oi]
|
|
end
|
|
w:SetChecked(isChecked)
|
|
w:Show()
|
|
end
|
|
for i=#opts+1,#f.Options do
|
|
f.Options[i]:Hide()
|
|
end
|
|
f:Show()
|
|
end
|
|
local function GetAction(self, into)
|
|
local opts = optionsForHandle[self]
|
|
into[1], into[2] = opts[0], curHandleID
|
|
for i=1,#opts do
|
|
into[opts[i]] = f.Options[i]:GetChecked() or nil
|
|
end
|
|
if opts.saveState then
|
|
opts.saveState(into)
|
|
end
|
|
end
|
|
function RegisterSimpleOptionsPanel(atype, opts)
|
|
local r = {IsOwned=IsOwned, Release=Release, SetAction=SetAction, GetAction=GetAction}
|
|
optionsForHandle[r], opts[0] = opts, atype
|
|
AB:RegisterEditorPanel(atype, r)
|
|
end
|
|
end
|
|
|
|
RegisterSimpleOptionsPanel("item", {"byName", "forceShow", "onlyEquipped",
|
|
byName=L"Also use items with the same name",
|
|
forceShow=L"Show a placeholder when unavailable",
|
|
onlyEquipped=L"Only show when equipped"
|
|
})
|
|
RegisterSimpleOptionsPanel("macro", {"forceShow",
|
|
forceShow=L"Show a placeholder when unavailable",
|
|
})
|
|
if MODERN then
|
|
RegisterSimpleOptionsPanel("extrabutton", {"forceShow",
|
|
forceShow=L"Show a placeholder when unavailable",
|
|
})
|
|
RegisterSimpleOptionsPanel("toy", {"forceShow",
|
|
forceShow=L"Show a placeholder when unavailable",
|
|
})
|
|
else
|
|
RegisterSimpleOptionsPanel("spell", {"upRank",
|
|
upRank=L"Use the highest known rank",
|
|
getOptionState=function(actionTable, _optKey)
|
|
return actionTable[3] ~= "lock-rank"
|
|
end,
|
|
saveState=function(intoTable)
|
|
intoTable[3], intoTable.upRank = not intoTable.upRank and "lock-rank" or nil
|
|
end,
|
|
})
|
|
end
|
|
|
|
|
|
--[[ These functions are not covered by the usual API warranty; do not rely on
|
|
them to exist or behave the way they do now in the future.
|
|
Writing directly to this table is also terrible.
|
|
--]]
|
|
T.ActionBook._CreateSimpleEditorPanel = RegisterSimpleOptionsPanel
|
|
T.ActionBook._CreateEdge = CreateEdge
|