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.
339 lines
13 KiB
339 lines
13 KiB
local COMPAT, _, T = select(4,GetBuildInfo()), ...
|
|
local L, EV, PC, TS, XU, config, KR = T.L, T.Evie, T.OPieCore, T.TenSettings, T.exUI, T.config, OPie.ActionBook:compatible("Kindred", 1, 0)
|
|
local MODERN = COMPAT >= 10e4
|
|
|
|
local frame = TS:CreateOptionsPanel(L"Ring Bindings", "OPie")
|
|
frame.desc:SetText(L"Customize OPie key bindings below. Hover over a binding button for additional information and options."
|
|
.. (MODERN and "\n" .. L"Profiles activate automatically when you switch character specializations." or ""))
|
|
local OBC_Profile = CreateFrame("Frame", "OBC_Profile", frame, "UIDropDownMenuTemplate")
|
|
OBC_Profile:SetPoint("TOPLEFT", 0, -80)
|
|
UIDropDownMenu_SetWidth(OBC_Profile, 200)
|
|
OBC_Profile.initialize, OBC_Profile.text = OPC_Profile.initialize, OPC_Profile.text
|
|
local bindSet = CreateFrame("Frame", "OBC_BindingSet", frame, "UIDropDownMenuTemplate")
|
|
bindSet:SetPoint("LEFT", OBC_Profile, "RIGHT", 44, 0)
|
|
UIDropDownMenu_SetWidth(bindSet, 250)
|
|
local bindLines, bindLines2, bindZone, bindZoneOrigin = {}, {}, CreateFrame("Frame", nil, frame) do
|
|
bindZone:SetClipsChildren(true)
|
|
bindZone:SetHitRectInsets(0, -22, 0, 0)
|
|
bindZoneOrigin = CreateFrame("Frame", nil, bindZone)
|
|
bindZoneOrigin:SetHeight(1)
|
|
bindZoneOrigin:SetPoint("TOPLEFT")
|
|
bindZoneOrigin:SetPoint("TOPRIGHT")
|
|
bindZoneOrigin:Hide()
|
|
bindZone.clipContainer, bindZone.bindingContainerFrame = bindZone, frame
|
|
for i=1,19 do
|
|
local bind = config.createBindingButton(bindZone, 170)
|
|
bind:SetPoint("TOPLEFT", bindZoneOrigin, "TOPLEFT", 220, 22-24*i)
|
|
local bind2 = config.createBindingButton(bindZone, 170)
|
|
bind2:SetPoint("LEFT", bind, "RIGHT", 4, 0)
|
|
local label = bind:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
|
|
label:SetPoint("TOPLEFT", bindZoneOrigin, 5, 18-24*i)
|
|
bind.warn = bind:CreateTexture(nil, "ARTWORK")
|
|
bind.warn:SetTexture("Interface/EncounterJournal/UI-EJ-WarningTextIcon")
|
|
bind.warn:SetSize(14, 14)
|
|
bind.warn:SetPoint("RIGHT", bind, "LEFT", -3, 0)
|
|
bindLines[i], bind.label, bindLines2[i] = bind, label, bind2
|
|
end
|
|
bindZone:SetPoint("TOP", OBC_Profile, "BOTTOM", 0, -4)
|
|
bindZone:SetPoint("LEFT", 15, 0)
|
|
bindZone:SetPoint("RIGHT", -30, 0)
|
|
bindZone:SetHeight((#bindLines-1)*24+3)
|
|
end
|
|
local bindZoneBar = XU:Create("ScrollBar", nil, frame)
|
|
bindZoneBar:SetPoint("TOPLEFT", bindZone, "TOPRIGHT", 1, 14)
|
|
bindZoneBar:SetPoint("BOTTOMLEFT", bindZone, "BOTTOMRIGHT", 1, -10)
|
|
bindZoneBar:SetWindowRange(#bindLines-1)
|
|
bindZoneBar:SetStepsPerPage(#bindLines-5)
|
|
bindZoneBar:SetCoverTarget(bindZone)
|
|
|
|
local ringBindings = {map={}, name=L"Ring Bindings"}
|
|
function ringBindings:refresh()
|
|
local pos, map = 1, self.map
|
|
for key in PC:IterateRings(IsAltKeyDown()) do
|
|
map[pos], pos = key, pos + 1
|
|
end
|
|
for i=#map,pos,-1 do
|
|
map[i] = nil
|
|
end
|
|
self.count = #map
|
|
end
|
|
local function ringBindings_getInner(key, bidx)
|
|
local bind, cBind, isOverride, isActiveInt, isActiveExt = PC:GetRingBinding(key, bidx)
|
|
local showWarning, prefix, tipTitle, tipText = false
|
|
local cebind = cBind or (bind and KR:EvaluateCmdOptions(bind))
|
|
local BC_HEADER_PREFIX = "|TInterface/EncounterJournal/UI-EJ-WarningTextIcon:0:0:0:2|t "
|
|
if not isOverride and not PC:GetOption("UseDefaultBindings", key) then
|
|
if bind then
|
|
showWarning, prefix, tipTitle = true, "|cffa0a0a0", BC_HEADER_PREFIX .. L"Default binding disabled"
|
|
tipText = (L"Choose a binding for this ring, or enable the %s option in OPie options."):format(HIGHLIGHT_FONT_COLOR_CODE .. L"Use default ring bindings" .. "|r")
|
|
end
|
|
elseif cBind and isActiveExt ~= true then
|
|
showWarning, tipTitle = true, BC_HEADER_PREFIX .. L"Binding conflict"
|
|
if isActiveInt == false then
|
|
prefix = isOverride and "|cfffa2800" or "|cffa0a0a0"
|
|
tipText = L"This binding is not currently active because it conflicts with another."
|
|
else
|
|
prefix, tipText = "|cfffa2800", L"This binding is currently used by another addon."
|
|
end
|
|
if isActiveExt then
|
|
local lab = _G["BINDING_NAME_" .. isActiveExt]
|
|
if not (lab and type(lab) == "string" and lab:match("%S")) then lab = tostring(isActiveExt) end
|
|
tipText = tipText .. "\n\n" .. (L"Conflicts with: %s"):format("|cffe0e0e0" .. lab .. "|r")
|
|
end
|
|
elseif cBind == nil and cebind and not isActiveInt then
|
|
showWarning, tipTitle = true, BC_HEADER_PREFIX .. L"Binding conflict"
|
|
prefix, tipText = "|cffa0a0a0", L"This binding is not currently active because it conflicts with another."
|
|
elseif isOverride and bind ~= nil then
|
|
prefix = "|cffffffff"
|
|
end
|
|
return bind, prefix, cBind, tipTitle, tipText, showWarning
|
|
end
|
|
function ringBindings:get(id)
|
|
local name, key = PC:GetRingInfo(self.map[id])
|
|
local text = name or key or "?"
|
|
local bind, prefix, cBind, title, tip, warning = ringBindings_getInner(key, 1)
|
|
local bind2, prefix2, _cBind2, title2, tip2, warning2 = ringBindings_getInner(key, 2)
|
|
return bind, text, prefix, cBind, title, tip, warning, bind2, prefix2, title2, tip2, warning2
|
|
end
|
|
function ringBindings:set(id, key, bidx)
|
|
id = self.map[id]
|
|
config.undo:saveActiveProfile()
|
|
PC:SetRingBinding(id, bidx, key)
|
|
end
|
|
function ringBindings:default()
|
|
PC:ResetRingBindings()
|
|
end
|
|
function ringBindings:altClick() -- self is the binding button
|
|
local id, bidx = self:GetID()
|
|
id, bidx = id < 0 and -id or id, id < 0 and 2 or 1
|
|
self:ToggleAlternateEditor(PC:GetRingBinding(ringBindings.map[id], bidx))
|
|
end
|
|
function ringBindings:shiftClick()
|
|
local name, key, macro = PC:GetRingInfo(ringBindings.map[math.abs(self:GetID())])
|
|
TS:ShowPromptOverlay(frame, name or key, (L"The following macro command opens this ring:"):format("|cffFFD029" .. (name or key) .. "|r"), false, false, nil, 0.90, nil, macro)
|
|
end
|
|
|
|
local subBindings = { name=L"In-Ring Bindings",
|
|
options={"ScrollNestedRingUpButton", "ScrollNestedRingDownButton", "OpenNestedRingButton", "SelectedSliceBind"},
|
|
optionNames={L"Scroll nested ring (up)", L"Scroll nested ring (down)", L"Open nested ring", L"Selected slice (keep ring open)"},
|
|
count=0, t={}
|
|
}
|
|
function subBindings.allowWheel(btn)
|
|
return btn:GetID() <= 2 and not subBindings.scope
|
|
end
|
|
function subBindings:refresh(scope)
|
|
local ringName = scope and PC:GetRingInfo(scope)
|
|
scope = ringName and scope or nil
|
|
self.scope, self.nameSuffix = scope, scope and (" (|cffacd7e6" .. ringName .. "|r)") or (" (" .. L"Defaults" .. ")")
|
|
local t, ni = {}, 1
|
|
for s, s2 in PC:GetOption("SliceBindingString", scope):gmatch("([^%s\31]+)\31?(%S*)") do
|
|
t[ni], t[ni+0.5], ni = s, s2, ni + 1
|
|
end
|
|
subBindings.t, subBindings.count = t, ni+(scope and 1 or 4)
|
|
end
|
|
function subBindings:get(id)
|
|
local firstListSize = self.scope and 1 or 4
|
|
if id <= firstListSize then
|
|
id = (self.scope and 3 or 0) + id
|
|
local value, setting = PC:GetOption(self.options[id], self.scope)
|
|
local value2, setting2 = PC:GetOption(self.options[id] .. "2", self.scope)
|
|
return value, self.optionNames[id], setting and "|cffffffff" or nil, nil, nil, nil, nil, value2, setting2 and "|cffffffff" or nil
|
|
end
|
|
id = id - firstListSize
|
|
local b, b2 = self.t[id], self.t[id+0.5]
|
|
b, b2 = b ~= "false" and b or "", b2 ~= "false" and b2 or ""
|
|
return b, (L"Slice #%d"):format(id), nil, nil, nil, nil, nil, b2
|
|
end
|
|
function subBindings:set(id, bind, bidx)
|
|
local firstListSize = self.scope and 1 or 4
|
|
if id > firstListSize then
|
|
return subBindings:setSliceBinding(id - firstListSize, bind, bidx, firstListSize)
|
|
end
|
|
id = (self.scope and 3 or 0) + id
|
|
config.undo:saveActiveProfile()
|
|
local opt = self.options[id] .. (bidx == 2 and "2" or "")
|
|
PC:SetOption(opt, bind or nil, self.scope)
|
|
if bind == false and PC:GetOption(opt, self.scope) ~= "" then
|
|
PC:SetOption(opt, "", self.scope)
|
|
end
|
|
end
|
|
function subBindings:setSliceBinding(sliceIdx, bind, bidx, firstListSize)
|
|
if bind == nil then
|
|
local i, s, s2 = 1, select(self.scope == nil and 5 or 4, PC:GetOption("SliceBindingString", self.scope))
|
|
for f, f2 in (s or s2):gmatch("([^%s\31]+)\31?(%S*)") do
|
|
if i == sliceIdx then
|
|
bind = bidx == 2 and f2 or f
|
|
bind = bind ~= "" and bind or nil
|
|
break
|
|
end
|
|
i = i + 1
|
|
end
|
|
end
|
|
|
|
local t, setIdx, setIdx2 = self.t, sliceIdx + (bidx == 2 and 0.5 or 0)
|
|
t[setIdx], setIdx2 = bind or "false", setIdx - 0.5
|
|
local o, nt, finalIndex = {}, {}
|
|
for j=math.max(sliceIdx, #t), 1, -1 do
|
|
local b1, b2 = t[j], t[j+0.5]
|
|
local h1, h2 = b1 and b1 ~= "false" and b1 ~= "" and (b1 ~= bind or j == setIdx), b2 and b2 ~= "false" and b2 ~= "" and (b2 ~= bind or j == setIdx2)
|
|
if (h1 and h2) then
|
|
o[j], finalIndex = b1 .. "\31" .. b2, finalIndex or j
|
|
nt[j], nt[j+0.5] = b1, b2
|
|
elseif h1 or h2 or finalIndex then
|
|
o[j], finalIndex = (h1 and b1 or h2 and b2 or "false"), finalIndex or j
|
|
nt[j], nt[j+0.5] = o[j], ""
|
|
end
|
|
end
|
|
|
|
self.t, self.count = nt, (finalIndex or 0) + firstListSize + 1
|
|
local _, _, _, global, default = PC:GetOption("SliceBindingString", self.scope)
|
|
local v = table.concat(o, " ")
|
|
if self.scope == nil and v == default or
|
|
self.scope ~= nil and v == (global or default) then
|
|
v = nil
|
|
end
|
|
config.undo:saveActiveProfile()
|
|
PC:SetOption("SliceBindingString", v, self.scope)
|
|
end
|
|
local subBindings_List = {}
|
|
local function subBindings_ScopeClick(_, key)
|
|
return bindSet.set(nil, subBindings, key or nil)
|
|
end
|
|
local function subBindings_ScopeFormat(key, list)
|
|
return list[key], list[0] and (key or nil) == subBindings.scope
|
|
end
|
|
function subBindings:scopes(level, checked)
|
|
local list = subBindings_List
|
|
wipe(list) -- Reusing the table to maintain the scroll position key
|
|
list[0], list[1], list[false] = checked, false, L"Defaults for all rings"
|
|
local ct = T.OPC_RingScopePrefixes
|
|
for key, name, scope in PC:IterateRings(true) do
|
|
local color = ct and ct[scope] or "|cffacd7e6"
|
|
list[#list+1], list[key] = key, (L"Ring: %s"):format(color .. (name or key) .. "|r")
|
|
end
|
|
XU:Create("ScrollableDropDownList", level, list, subBindings_ScopeFormat, subBindings_ScopeClick)
|
|
end
|
|
function subBindings:default()
|
|
for i=0,#self.options do
|
|
local on = i == 0 and "SliceBindingString" or self.options[i]
|
|
PC:SetOption(on, nil)
|
|
if self.scope then
|
|
PC:SetOption(on, nil, self.scope)
|
|
end
|
|
end
|
|
end
|
|
|
|
local currentOwner, bindingTypes = ringBindings, {ringBindings, subBindings}
|
|
local function updatePanelContent()
|
|
local m = currentOwner.count
|
|
bindZoneBar:SetShown(m >= #bindLines)
|
|
bindZoneBar:SetMinMaxValues(0, m > #bindLines and m - #bindLines + 1 or 1)
|
|
local csv = bindZoneBar:GetValue()
|
|
local csPartial = csv % 1
|
|
local csBase = csv - csPartial
|
|
bindZoneOrigin:SetPoint("TOPLEFT", 0, csPartial*24)
|
|
for i=1,#bindLines do
|
|
local j, e, e2 = csBase+i, bindLines[i], bindLines2[i]
|
|
if j > m then
|
|
e:Hide()
|
|
e2:Hide()
|
|
else
|
|
local binding, text, prefix, _, title, tip, showWarningIcon, binding2, prefix2, title2, tip2, warning2 = currentOwner:get(j)
|
|
e.bindingName, e.tooltipTitle, e.tooltipText = text, title, tip
|
|
e.label:SetText(text)
|
|
e.warn:SetShown(showWarningIcon or warning2)
|
|
e:SetBindingText(binding, prefix)
|
|
e:SetID(j) e:Hide() e:Show()
|
|
e2.bindingName, e2.tooltipTitle, e2.tooltipText = text, title2, tip2
|
|
e2:SetBindingText(binding2, prefix2)
|
|
e2:SetID(-j) e2:Hide() e2:Show()
|
|
end
|
|
end
|
|
bindZone.OnBindingAltClick = currentOwner.altClick
|
|
bindZone.OnBindingShiftClick = currentOwner.shiftClick
|
|
UIDropDownMenu_SetText(bindSet, currentOwner.name .. (currentOwner.nameSuffix or ""))
|
|
end
|
|
function bindZone.SetBinding(buttonOrId, binding)
|
|
local id, bidx = type(buttonOrId) == "number" and buttonOrId or buttonOrId:GetID()
|
|
id, bidx = id < 0 and -id or id, id < 0 and 2 or 1
|
|
currentOwner:set(id, binding, bidx)
|
|
updatePanelContent()
|
|
end
|
|
bindZone:SetScript("OnMouseWheel", function(_, delta)
|
|
bindZoneBar:Step(-delta*6, true)
|
|
updatePanelContent()
|
|
end)
|
|
bindZoneBar:SetScript("OnValueChanged", function(_, _, userEvent)
|
|
if userEvent then
|
|
updatePanelContent()
|
|
end
|
|
end)
|
|
|
|
function bindSet:initialize(level)
|
|
local info = {func=bindSet.set, minWidth=bindSet:GetWidth()-40}
|
|
for i=1,#bindingTypes do
|
|
local v = bindingTypes[i]
|
|
if v.scopes then
|
|
UIDropDownMenu_AddSeparator(level)
|
|
info.text, info.isTitle, info.notCheckable, info.justifyH = v.name, true, true, "CENTER"
|
|
UIDropDownMenu_AddButton(info, level)
|
|
v:scopes(level, currentOwner == v)
|
|
else
|
|
info.notCheckable, info.isTitle, info.justifyH = nil
|
|
info.text, info.arg1, info.checked = v.name, v, v == currentOwner
|
|
UIDropDownMenu_AddButton(info, level)
|
|
end
|
|
end
|
|
end
|
|
function bindSet:set(owner, scope)
|
|
currentOwner, bindZone.AllowWheelBinding = owner, owner and owner.allowWheel
|
|
bindZoneBar:SetValue(0)
|
|
if owner.refresh then owner:refresh(scope) end
|
|
updatePanelContent()
|
|
CloseDropDownMenus()
|
|
frame.resetOnHide = nil
|
|
end
|
|
|
|
function frame.refresh()
|
|
for _, v in pairs(bindingTypes) do
|
|
if v.refresh then v:refresh(v.scope) end
|
|
end
|
|
OBC_Profile:text()
|
|
updatePanelContent()
|
|
config.checkSVState(frame)
|
|
end
|
|
function frame.default()
|
|
config.undo:saveActiveProfile()
|
|
for _, v in pairs(bindingTypes) do
|
|
if v.default then v:default() end
|
|
end
|
|
frame.refresh()
|
|
end
|
|
local function resetView()
|
|
currentOwner, frame.resetOnHide = ringBindings, nil
|
|
bindZoneBar:SetValue(0)
|
|
for _, v in pairs(bindingTypes) do
|
|
v.scope = nil
|
|
end
|
|
end
|
|
frame.okay, frame.cancel = resetView, resetView
|
|
frame:SetScript("OnShow", frame.refresh)
|
|
frame:SetScript("OnHide", function()
|
|
if frame.resetOnHide then
|
|
resetView()
|
|
end
|
|
end)
|
|
|
|
T.AddSlashSuffix(function() frame:OpenPanel() end, "bind", "binding", "bindings")
|
|
|
|
function T.ShowSliceBindingPanel(ringKey)
|
|
frame:OpenPanel()
|
|
bindSet.set(nil, subBindings, ringKey)
|
|
frame.resetOnHide = true
|
|
config.pulseDropdown(bindSet)
|
|
end
|
|
function EV:OPIE_PROFILE_SWITCHED()
|
|
if frame:IsVisible() then
|
|
frame.refresh()
|
|
end
|
|
end
|