聊天重做前缓存
This commit is contained in:
792
ExtraBar.lua
Normal file
792
ExtraBar.lua
Normal file
@@ -0,0 +1,792 @@
|
||||
--------------------------------------------------------------------------------
|
||||
-- Nanami-UI: ExtraBar
|
||||
--
|
||||
-- A fully configurable extra action bar using action slots from page 7+
|
||||
-- (slots 73-120). Buttons are created from scratch since no spare Blizzard
|
||||
-- ActionButton frames exist.
|
||||
--
|
||||
-- Supports: grid layout, per-row count, alignment, drag-and-drop, cooldown,
|
||||
-- range coloring, tooltip, and Mover integration for position saving.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
SFrames.ExtraBar = {}
|
||||
|
||||
local EB = SFrames.ExtraBar
|
||||
|
||||
local DEFAULTS = {
|
||||
enable = false,
|
||||
buttonCount = 12,
|
||||
perRow = 12,
|
||||
buttonSize = 36,
|
||||
buttonGap = 2,
|
||||
align = "center",
|
||||
startSlot = 73,
|
||||
alpha = 1.0,
|
||||
showHotkey = true,
|
||||
showCount = true,
|
||||
buttonRounded = false,
|
||||
buttonInnerShadow = false,
|
||||
}
|
||||
|
||||
local MAX_BUTTONS = 48
|
||||
|
||||
function EB:GetDB()
|
||||
if not SFramesDB then SFramesDB = {} end
|
||||
if type(SFramesDB.ExtraBar) ~= "table" then SFramesDB.ExtraBar = {} end
|
||||
local db = SFramesDB.ExtraBar
|
||||
for k, v in pairs(DEFAULTS) do
|
||||
if db[k] == nil then db[k] = v end
|
||||
end
|
||||
return db
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Layout helper (local LayoutGrid equivalent)
|
||||
--------------------------------------------------------------------------------
|
||||
local function LayoutGrid(buttons, parent, size, gap, perRow, count)
|
||||
if count == 0 then return end
|
||||
local numCols = math.min(perRow, count)
|
||||
local numRows = math.ceil(count / perRow)
|
||||
parent:SetWidth(numCols * size + math.max(numCols - 1, 0) * gap)
|
||||
parent:SetHeight(numRows * size + math.max(numRows - 1, 0) * gap)
|
||||
for i = 1, count do
|
||||
local b = buttons[i]
|
||||
if b then
|
||||
b:SetWidth(size)
|
||||
b:SetHeight(size)
|
||||
b:ClearAllPoints()
|
||||
local col = math.fmod(i - 1, perRow)
|
||||
local row = math.floor((i - 1) / perRow)
|
||||
b:SetPoint("TOPLEFT", parent, "TOPLEFT", col * (size + gap), -row * (size + gap))
|
||||
b:Show()
|
||||
end
|
||||
end
|
||||
for i = count + 1, MAX_BUTTONS do
|
||||
if buttons[i] then buttons[i]:Hide() end
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Button visual helpers (mirrors ActionBars.lua approach)
|
||||
--------------------------------------------------------------------------------
|
||||
local function CreateBackdropFor(btn)
|
||||
if btn.sfBackdrop then return end
|
||||
local level = btn:GetFrameLevel()
|
||||
local bd = CreateFrame("Frame", nil, btn)
|
||||
bd:SetFrameLevel(level > 0 and (level - 1) or 0)
|
||||
bd:SetAllPoints(btn)
|
||||
SFrames:CreateBackdrop(bd)
|
||||
btn.sfBackdrop = bd
|
||||
end
|
||||
|
||||
local function CreateInnerShadow(btn)
|
||||
if btn.sfInnerShadow then return btn.sfInnerShadow end
|
||||
local shadow = {}
|
||||
local thickness = 4
|
||||
|
||||
local top = btn:CreateTexture(nil, "OVERLAY")
|
||||
top:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
top:SetHeight(thickness)
|
||||
top:SetGradientAlpha("VERTICAL", 0, 0, 0, 0, 0, 0, 0, 0.5)
|
||||
shadow.top = top
|
||||
|
||||
local bot = btn:CreateTexture(nil, "OVERLAY")
|
||||
bot:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
bot:SetHeight(thickness)
|
||||
bot:SetGradientAlpha("VERTICAL", 0, 0, 0, 0.5, 0, 0, 0, 0)
|
||||
shadow.bottom = bot
|
||||
|
||||
local left = btn:CreateTexture(nil, "OVERLAY")
|
||||
left:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
left:SetWidth(thickness)
|
||||
left:SetGradientAlpha("HORIZONTAL", 0, 0, 0, 0.5, 0, 0, 0, 0)
|
||||
shadow.left = left
|
||||
|
||||
local right = btn:CreateTexture(nil, "OVERLAY")
|
||||
right:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
right:SetWidth(thickness)
|
||||
right:SetGradientAlpha("HORIZONTAL", 0, 0, 0, 0, 0, 0, 0, 0.5)
|
||||
shadow.right = right
|
||||
|
||||
btn.sfInnerShadow = shadow
|
||||
return shadow
|
||||
end
|
||||
|
||||
local function ApplyButtonVisuals(btn, rounded, innerShadow)
|
||||
local bd = btn.sfBackdrop
|
||||
if not bd then return end
|
||||
|
||||
local inset = rounded and 3 or 2
|
||||
btn.sfIconInset = inset
|
||||
|
||||
if rounded then
|
||||
bd:SetBackdrop({
|
||||
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true, tileSize = 16, edgeSize = 12,
|
||||
insets = { left = 3, right = 3, top = 3, bottom = 3 }
|
||||
})
|
||||
else
|
||||
bd:SetBackdrop({
|
||||
bgFile = "Interface\\Buttons\\WHITE8X8",
|
||||
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
||||
tile = false, tileSize = 0, edgeSize = 1,
|
||||
insets = { left = 1, right = 1, top = 1, bottom = 1 }
|
||||
})
|
||||
end
|
||||
local A = SFrames.ActiveTheme
|
||||
if A and A.panelBg then
|
||||
bd:SetBackdropColor(A.panelBg[1], A.panelBg[2], A.panelBg[3], A.panelBg[4] or 0.9)
|
||||
bd:SetBackdropBorderColor(A.panelBorder[1], A.panelBorder[2], A.panelBorder[3], A.panelBorder[4] or 1)
|
||||
else
|
||||
bd:SetBackdropColor(0.1, 0.1, 0.1, 0.9)
|
||||
bd:SetBackdropBorderColor(0, 0, 0, 1)
|
||||
end
|
||||
|
||||
local icon = btn.sfIcon
|
||||
if icon then
|
||||
icon:ClearAllPoints()
|
||||
icon:SetPoint("TOPLEFT", btn, "TOPLEFT", inset, -inset)
|
||||
icon:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -inset, inset)
|
||||
end
|
||||
if btn.sfCdOverlay then
|
||||
btn.sfCdOverlay:ClearAllPoints()
|
||||
btn.sfCdOverlay:SetPoint("TOPLEFT", btn, "TOPLEFT", inset, -inset)
|
||||
btn.sfCdOverlay:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -inset, inset)
|
||||
end
|
||||
if btn.sfRangeOverlay then
|
||||
btn.sfRangeOverlay:ClearAllPoints()
|
||||
btn.sfRangeOverlay:SetPoint("TOPLEFT", btn, "TOPLEFT", inset, -inset)
|
||||
btn.sfRangeOverlay:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -inset, inset)
|
||||
end
|
||||
|
||||
if innerShadow then
|
||||
if not btn.sfInnerShadow then CreateInnerShadow(btn) end
|
||||
local s = btn.sfInnerShadow
|
||||
s.top:ClearAllPoints()
|
||||
s.top:SetPoint("TOPLEFT", btn, "TOPLEFT", inset, -inset)
|
||||
s.top:SetPoint("TOPRIGHT", btn, "TOPRIGHT", -inset, -inset)
|
||||
s.bottom:ClearAllPoints()
|
||||
s.bottom:SetPoint("BOTTOMLEFT", btn, "BOTTOMLEFT", inset, inset)
|
||||
s.bottom:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -inset, inset)
|
||||
s.left:ClearAllPoints()
|
||||
s.left:SetPoint("TOPLEFT", btn, "TOPLEFT", inset, -inset)
|
||||
s.left:SetPoint("BOTTOMLEFT", btn, "BOTTOMLEFT", inset, inset)
|
||||
s.right:ClearAllPoints()
|
||||
s.right:SetPoint("TOPRIGHT", btn, "TOPRIGHT", -inset, -inset)
|
||||
s.right:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -inset, inset)
|
||||
s.top:Show(); s.bottom:Show(); s.left:Show(); s.right:Show()
|
||||
else
|
||||
if btn.sfInnerShadow then
|
||||
local s = btn.sfInnerShadow
|
||||
s.top:Hide(); s.bottom:Hide(); s.left:Hide(); s.right:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Single-button refresh helpers
|
||||
--------------------------------------------------------------------------------
|
||||
local function RefreshButtonIcon(btn)
|
||||
local slot = btn.sfActionSlot
|
||||
if not slot then return end
|
||||
if HasAction(slot) then
|
||||
local tex = GetActionTexture(slot)
|
||||
btn.sfIcon:SetTexture(tex)
|
||||
btn.sfIcon:Show()
|
||||
btn.sfIcon:SetVertexColor(1, 1, 1, 1)
|
||||
btn:SetAlpha(1)
|
||||
else
|
||||
btn.sfIcon:SetTexture(nil)
|
||||
btn.sfIcon:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function RefreshButtonCooldown(btn)
|
||||
local slot = btn.sfActionSlot
|
||||
if not slot or not HasAction(slot) then
|
||||
if btn.sfCdOverlay then btn.sfCdOverlay:Hide() end
|
||||
if btn.sfCdText then btn.sfCdText:SetText("") end
|
||||
btn.sfCdStart = 0
|
||||
btn.sfCdDuration = 0
|
||||
return
|
||||
end
|
||||
local start, duration, enable = GetActionCooldown(slot)
|
||||
if not start or not duration or duration == 0 or enable == 0 then
|
||||
if btn.sfCdOverlay then btn.sfCdOverlay:Hide() end
|
||||
if btn.sfCdText then btn.sfCdText:SetText("") end
|
||||
btn.sfCdStart = 0
|
||||
btn.sfCdDuration = 0
|
||||
return
|
||||
end
|
||||
btn.sfCdStart = start
|
||||
btn.sfCdDuration = duration
|
||||
local now = GetTime()
|
||||
local remaining = (start + duration) - now
|
||||
if remaining > 0 then
|
||||
if btn.sfCdOverlay then btn.sfCdOverlay:Show() end
|
||||
if btn.sfCdText then
|
||||
if remaining >= 60 then
|
||||
btn.sfCdText:SetText(math.floor(remaining / 60) .. "m")
|
||||
else
|
||||
btn.sfCdText:SetText(math.floor(remaining + 0.5) .. "")
|
||||
end
|
||||
end
|
||||
else
|
||||
if btn.sfCdOverlay then btn.sfCdOverlay:Hide() end
|
||||
if btn.sfCdText then btn.sfCdText:SetText("") end
|
||||
btn.sfCdStart = 0
|
||||
btn.sfCdDuration = 0
|
||||
end
|
||||
end
|
||||
|
||||
local function RefreshButtonUsable(btn)
|
||||
local slot = btn.sfActionSlot
|
||||
if not slot or not HasAction(slot) then return end
|
||||
local isUsable, notEnoughMana = IsUsableAction(slot)
|
||||
if isUsable then
|
||||
btn.sfIcon:SetVertexColor(1, 1, 1, 1)
|
||||
elseif notEnoughMana then
|
||||
btn.sfIcon:SetVertexColor(0.2, 0.2, 0.8, 1)
|
||||
else
|
||||
btn.sfIcon:SetVertexColor(0.4, 0.4, 0.4, 1)
|
||||
end
|
||||
end
|
||||
|
||||
local function RefreshButtonCount(btn)
|
||||
local slot = btn.sfActionSlot
|
||||
if not slot or not HasAction(slot) then
|
||||
btn.sfCount:SetText("")
|
||||
return
|
||||
end
|
||||
local count = GetActionCount(slot)
|
||||
if count and count > 0 then
|
||||
btn.sfCount:SetText(tostring(count))
|
||||
else
|
||||
btn.sfCount:SetText("")
|
||||
end
|
||||
end
|
||||
|
||||
local function RefreshButtonRange(btn)
|
||||
local slot = btn.sfActionSlot
|
||||
if not slot or not HasAction(slot) then
|
||||
if btn.sfRangeOverlay then btn.sfRangeOverlay:Hide() end
|
||||
return
|
||||
end
|
||||
local inRange = IsActionInRange(slot)
|
||||
if not btn.sfRangeOverlay then
|
||||
local inset = btn.sfIconInset or 2
|
||||
local ov = btn:CreateTexture(nil, "OVERLAY")
|
||||
ov:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
ov:SetPoint("TOPLEFT", btn, "TOPLEFT", inset, -inset)
|
||||
ov:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -inset, inset)
|
||||
ov:SetVertexColor(1.0, 0.1, 0.1, 0.35)
|
||||
ov:Hide()
|
||||
btn.sfRangeOverlay = ov
|
||||
end
|
||||
if inRange == 0 then
|
||||
btn.sfRangeOverlay:Show()
|
||||
else
|
||||
btn.sfRangeOverlay:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function RefreshButtonState(btn)
|
||||
local slot = btn.sfActionSlot
|
||||
if not slot or not HasAction(slot) then return end
|
||||
if IsCurrentAction(slot) then
|
||||
if btn.sfBackdrop then
|
||||
btn.sfBackdrop:SetBackdropBorderColor(1, 1, 1, 0.8)
|
||||
end
|
||||
else
|
||||
local A = SFrames.ActiveTheme
|
||||
if btn.sfBackdrop then
|
||||
if A and A.panelBorder then
|
||||
btn.sfBackdrop:SetBackdropBorderColor(A.panelBorder[1], A.panelBorder[2], A.panelBorder[3], A.panelBorder[4] or 1)
|
||||
else
|
||||
btn.sfBackdrop:SetBackdropBorderColor(0, 0, 0, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function RefreshButtonAll(btn)
|
||||
RefreshButtonIcon(btn)
|
||||
RefreshButtonCooldown(btn)
|
||||
RefreshButtonUsable(btn)
|
||||
RefreshButtonCount(btn)
|
||||
RefreshButtonState(btn)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Custom action button factory
|
||||
--------------------------------------------------------------------------------
|
||||
local btnIndex = 0
|
||||
|
||||
local function CreateExtraButton(parent)
|
||||
btnIndex = btnIndex + 1
|
||||
local name = "SFramesExtraBarButton" .. btnIndex
|
||||
local btn = CreateFrame("Button", name, parent)
|
||||
btn:SetWidth(36)
|
||||
btn:SetHeight(36)
|
||||
btn:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
||||
btn:RegisterForDrag("LeftButton")
|
||||
|
||||
CreateBackdropFor(btn)
|
||||
|
||||
local icon = btn:CreateTexture(name .. "Icon", "ARTWORK")
|
||||
icon:SetPoint("TOPLEFT", btn, "TOPLEFT", 2, -2)
|
||||
icon:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -2, 2)
|
||||
icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)
|
||||
btn.sfIcon = icon
|
||||
|
||||
local cdOverlay = btn:CreateTexture(nil, "OVERLAY")
|
||||
cdOverlay:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
cdOverlay:SetAllPoints(icon)
|
||||
cdOverlay:SetVertexColor(0, 0, 0, 0.6)
|
||||
cdOverlay:Hide()
|
||||
btn.sfCdOverlay = cdOverlay
|
||||
|
||||
local cdText = btn:CreateFontString(nil, "OVERLAY")
|
||||
cdText:SetFont(SFrames:GetFont(), 12, "OUTLINE")
|
||||
cdText:SetPoint("CENTER", btn, "CENTER", 0, 0)
|
||||
cdText:SetTextColor(1, 1, 0.2)
|
||||
cdText:SetText("")
|
||||
btn.sfCdText = cdText
|
||||
|
||||
btn.sfCdStart = 0
|
||||
btn.sfCdDuration = 0
|
||||
|
||||
local font = SFrames:GetFont()
|
||||
|
||||
local hotkey = btn:CreateFontString(name .. "HotKey", "OVERLAY")
|
||||
hotkey:SetFont(font, 9, "OUTLINE")
|
||||
hotkey:SetPoint("TOPRIGHT", btn, "TOPRIGHT", -2, -2)
|
||||
hotkey:SetJustifyH("RIGHT")
|
||||
btn.sfHotKey = hotkey
|
||||
|
||||
local count = btn:CreateFontString(name .. "Count", "OVERLAY")
|
||||
count:SetFont(font, 9, "OUTLINE")
|
||||
count:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -2, 2)
|
||||
count:SetJustifyH("RIGHT")
|
||||
btn.sfCount = count
|
||||
|
||||
-- Highlight texture
|
||||
local hl = btn:CreateTexture(nil, "HIGHLIGHT")
|
||||
hl:SetTexture("Interface\\Buttons\\ButtonHilight-Square")
|
||||
hl:SetBlendMode("ADD")
|
||||
hl:SetPoint("TOPLEFT", btn, "TOPLEFT", 2, -2)
|
||||
hl:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -2, 2)
|
||||
hl:SetAlpha(0.3)
|
||||
|
||||
-- Pushed texture overlay
|
||||
local pushed = btn:CreateTexture(nil, "OVERLAY")
|
||||
pushed:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
pushed:SetPoint("TOPLEFT", btn, "TOPLEFT", 2, -2)
|
||||
pushed:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -2, 2)
|
||||
pushed:SetVertexColor(1, 1, 1, 0.15)
|
||||
pushed:Hide()
|
||||
btn.sfPushed = pushed
|
||||
|
||||
btn.sfActionSlot = nil
|
||||
|
||||
btn:SetScript("OnClick", function()
|
||||
local slot = this.sfActionSlot
|
||||
if not slot then return end
|
||||
if arg1 == "LeftButton" then
|
||||
if IsShiftKeyDown() and ChatFrameEditBox and ChatFrameEditBox:IsVisible() then
|
||||
-- Shift-click: link to chat (fallback: pickup)
|
||||
pcall(PickupAction, slot)
|
||||
else
|
||||
UseAction(slot, 0, 1)
|
||||
end
|
||||
elseif arg1 == "RightButton" then
|
||||
UseAction(slot, 0, 1)
|
||||
end
|
||||
RefreshButtonAll(this)
|
||||
end)
|
||||
|
||||
btn:SetScript("OnDragStart", function()
|
||||
local slot = this.sfActionSlot
|
||||
if slot then
|
||||
pcall(PickupAction, slot)
|
||||
RefreshButtonAll(this)
|
||||
end
|
||||
end)
|
||||
|
||||
btn:SetScript("OnReceiveDrag", function()
|
||||
local slot = this.sfActionSlot
|
||||
if slot then
|
||||
pcall(PlaceAction, slot)
|
||||
RefreshButtonAll(this)
|
||||
end
|
||||
end)
|
||||
|
||||
btn:SetScript("OnEnter", function()
|
||||
local slot = this.sfActionSlot
|
||||
if slot and HasAction(slot) then
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetAction(slot)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
end)
|
||||
|
||||
btn:SetScript("OnLeave", function()
|
||||
GameTooltip:Hide()
|
||||
end)
|
||||
|
||||
btn:SetScript("OnMouseDown", function()
|
||||
if this.sfPushed then this.sfPushed:Show() end
|
||||
end)
|
||||
|
||||
btn:SetScript("OnMouseUp", function()
|
||||
if this.sfPushed then this.sfPushed:Hide() end
|
||||
end)
|
||||
|
||||
btn:Hide()
|
||||
return btn
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Create all buttons once
|
||||
--------------------------------------------------------------------------------
|
||||
function EB:CreateButtons()
|
||||
local db = self:GetDB()
|
||||
|
||||
self.holder = CreateFrame("Frame", "SFramesExtraBarHolder", UIParent)
|
||||
self.holder:SetWidth(200)
|
||||
self.holder:SetHeight(40)
|
||||
|
||||
local pos = SFramesDB and SFramesDB.Positions and SFramesDB.Positions["ExtraBar"]
|
||||
if pos and pos.point and pos.relativePoint then
|
||||
self.holder:SetPoint(pos.point, UIParent, pos.relativePoint, pos.xOfs or 0, pos.yOfs or 0)
|
||||
else
|
||||
self.holder:SetPoint("CENTER", UIParent, "CENTER", 0, -200)
|
||||
end
|
||||
|
||||
self.buttons = {}
|
||||
for i = 1, MAX_BUTTONS do
|
||||
local btn = CreateExtraButton(self.holder)
|
||||
table.insert(self.buttons, btn)
|
||||
end
|
||||
|
||||
if SFrames.ActionBars and SFrames.ActionBars.RegisterBindButton then
|
||||
for i = 1, MAX_BUTTONS do
|
||||
local bname = self.buttons[i]:GetName()
|
||||
SFrames.ActionBars:RegisterBindButton(bname, "NANAMI_EXTRABAR" .. i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Apply configuration
|
||||
--------------------------------------------------------------------------------
|
||||
function EB:ApplyConfig()
|
||||
if not self.holder then return end
|
||||
local db = self:GetDB()
|
||||
|
||||
if not db.enable then
|
||||
self.holder:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
local count = math.min(db.buttonCount or 12, MAX_BUTTONS)
|
||||
local perRow = db.perRow or 12
|
||||
local size = db.buttonSize or 36
|
||||
local gap = db.buttonGap or 2
|
||||
local startSlot = db.startSlot or 73
|
||||
|
||||
-- Assign action slots
|
||||
for i = 1, MAX_BUTTONS do
|
||||
local btn = self.buttons[i]
|
||||
btn.sfActionSlot = startSlot + i - 1
|
||||
end
|
||||
|
||||
-- Layout
|
||||
LayoutGrid(self.buttons, self.holder, size, gap, perRow, count)
|
||||
|
||||
-- Alignment via anchor
|
||||
local positions = SFramesDB and SFramesDB.Positions
|
||||
local pos = positions and positions["ExtraBar"]
|
||||
if not (pos and pos.point and pos.relativePoint) then
|
||||
self.holder:ClearAllPoints()
|
||||
local align = db.align or "center"
|
||||
if align == "left" then
|
||||
self.holder:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", 20, 200)
|
||||
elseif align == "right" then
|
||||
self.holder:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -20, 200)
|
||||
else
|
||||
self.holder:SetPoint("CENTER", UIParent, "CENTER", 0, -200)
|
||||
end
|
||||
end
|
||||
|
||||
-- Alpha
|
||||
local alpha = db.alpha or 1
|
||||
if alpha < 0.1 then alpha = 0.1 end
|
||||
if alpha > 1 then alpha = 1 end
|
||||
self.holder:SetAlpha(alpha)
|
||||
|
||||
-- Scale (inherit from main action bars if available)
|
||||
local abDb = SFrames.ActionBars and SFrames.ActionBars.GetDB and SFrames.ActionBars:GetDB()
|
||||
local scale = abDb and abDb.scale or 1.0
|
||||
self.holder:SetScale(scale)
|
||||
|
||||
-- Button visuals
|
||||
local isRounded = db.buttonRounded
|
||||
local isShadow = db.buttonInnerShadow
|
||||
local showHK = db.showHotkey
|
||||
local showCt = db.showCount
|
||||
local font = SFrames:GetFont()
|
||||
local fontSize = math.max(6, math.floor(size * 0.25 + 0.5))
|
||||
|
||||
for i = 1, count do
|
||||
local btn = self.buttons[i]
|
||||
ApplyButtonVisuals(btn, isRounded, isShadow)
|
||||
if btn.sfHotKey then
|
||||
btn.sfHotKey:SetFont(font, fontSize, "OUTLINE")
|
||||
if showHK then btn.sfHotKey:Show() else btn.sfHotKey:Hide() end
|
||||
end
|
||||
if btn.sfCount then
|
||||
btn.sfCount:SetFont(font, fontSize, "OUTLINE")
|
||||
if showCt then btn.sfCount:Show() else btn.sfCount:Hide() end
|
||||
end
|
||||
if btn.sfCdText then
|
||||
local cdFontSize = math.max(8, math.floor(size * 0.35 + 0.5))
|
||||
btn.sfCdText:SetFont(font, cdFontSize, "OUTLINE")
|
||||
end
|
||||
RefreshButtonAll(btn)
|
||||
end
|
||||
|
||||
self.holder:Show()
|
||||
|
||||
-- Update mover if in layout mode
|
||||
if SFrames.Movers and SFrames.Movers:IsLayoutMode() then
|
||||
SFrames.Movers:SyncMoverToFrame("ExtraBar")
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Refresh all visible buttons
|
||||
--------------------------------------------------------------------------------
|
||||
function EB:RefreshAll()
|
||||
if not self.buttons then return end
|
||||
local db = self:GetDB()
|
||||
if not db.enable then return end
|
||||
local count = math.min(db.buttonCount or 12, MAX_BUTTONS)
|
||||
for i = 1, count do
|
||||
RefreshButtonAll(self.buttons[i])
|
||||
end
|
||||
end
|
||||
|
||||
function EB:RefreshCooldowns()
|
||||
if not self.buttons then return end
|
||||
local db = self:GetDB()
|
||||
if not db.enable then return end
|
||||
local count = math.min(db.buttonCount or 12, MAX_BUTTONS)
|
||||
for i = 1, count do
|
||||
RefreshButtonCooldown(self.buttons[i])
|
||||
end
|
||||
end
|
||||
|
||||
function EB:RefreshUsable()
|
||||
if not self.buttons then return end
|
||||
local db = self:GetDB()
|
||||
if not db.enable then return end
|
||||
local count = math.min(db.buttonCount or 12, MAX_BUTTONS)
|
||||
for i = 1, count do
|
||||
RefreshButtonUsable(self.buttons[i])
|
||||
end
|
||||
end
|
||||
|
||||
function EB:RefreshStates()
|
||||
if not self.buttons then return end
|
||||
local db = self:GetDB()
|
||||
if not db.enable then return end
|
||||
local count = math.min(db.buttonCount or 12, MAX_BUTTONS)
|
||||
for i = 1, count do
|
||||
RefreshButtonState(self.buttons[i])
|
||||
end
|
||||
end
|
||||
|
||||
function EB:RefreshHotkeys()
|
||||
if not self.buttons then return end
|
||||
local db = self:GetDB()
|
||||
if not db.enable then return end
|
||||
local count = math.min(db.buttonCount or 12, MAX_BUTTONS)
|
||||
for i = 1, count do
|
||||
local btn = self.buttons[i]
|
||||
local cmd = "NANAMI_EXTRABAR" .. i
|
||||
local hotkey = btn.sfHotKey
|
||||
if hotkey then
|
||||
local key1 = GetBindingKey(cmd)
|
||||
if key1 then
|
||||
local text = key1
|
||||
if GetBindingText then
|
||||
text = GetBindingText(key1, "KEY_", 1) or key1
|
||||
end
|
||||
hotkey:SetText(text)
|
||||
else
|
||||
hotkey:SetText("")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- OnUpdate poller for cooldown / range / usability
|
||||
--------------------------------------------------------------------------------
|
||||
function EB:SetupPoller()
|
||||
local poller = CreateFrame("Frame", "SFramesExtraBarPoller", UIParent)
|
||||
poller.timer = 0
|
||||
self.poller = poller
|
||||
|
||||
poller:SetScript("OnUpdate", function()
|
||||
this.timer = this.timer + arg1
|
||||
if this.timer < 0.2 then return end
|
||||
this.timer = 0
|
||||
|
||||
local db = EB:GetDB()
|
||||
if not db.enable then return end
|
||||
local count = math.min(db.buttonCount or 12, MAX_BUTTONS)
|
||||
for i = 1, count do
|
||||
local btn = EB.buttons[i]
|
||||
if btn and btn:IsShown() then
|
||||
RefreshButtonCooldown(btn)
|
||||
RefreshButtonUsable(btn)
|
||||
RefreshButtonRange(btn)
|
||||
RefreshButtonState(btn)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Initialize
|
||||
--------------------------------------------------------------------------------
|
||||
function EB:Initialize()
|
||||
local db = self:GetDB()
|
||||
if not db.enable then return end
|
||||
|
||||
self:CreateButtons()
|
||||
self:ApplyConfig()
|
||||
self:SetupPoller()
|
||||
|
||||
-- Events
|
||||
SFrames:RegisterEvent("ACTIONBAR_SLOT_CHANGED", function()
|
||||
if not EB.buttons then return end
|
||||
local db = EB:GetDB()
|
||||
if not db.enable then return end
|
||||
local slot = arg1
|
||||
local startSlot = db.startSlot or 73
|
||||
local count = math.min(db.buttonCount or 12, MAX_BUTTONS)
|
||||
if slot then
|
||||
local idx = slot - startSlot + 1
|
||||
if idx >= 1 and idx <= count then
|
||||
RefreshButtonAll(EB.buttons[idx])
|
||||
end
|
||||
else
|
||||
EB:RefreshAll()
|
||||
end
|
||||
end)
|
||||
|
||||
SFrames:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN", function()
|
||||
EB:RefreshCooldowns()
|
||||
end)
|
||||
|
||||
SFrames:RegisterEvent("ACTIONBAR_UPDATE_USABLE", function()
|
||||
EB:RefreshUsable()
|
||||
end)
|
||||
|
||||
SFrames:RegisterEvent("ACTIONBAR_UPDATE_STATE", function()
|
||||
EB:RefreshStates()
|
||||
end)
|
||||
|
||||
SFrames:RegisterEvent("PLAYER_ENTERING_WORLD", function()
|
||||
EB:RefreshAll()
|
||||
EB:RefreshHotkeys()
|
||||
end)
|
||||
|
||||
SFrames:RegisterEvent("UPDATE_BINDINGS", function()
|
||||
EB:RefreshHotkeys()
|
||||
end)
|
||||
|
||||
-- Register mover
|
||||
if SFrames.Movers and SFrames.Movers.RegisterMover then
|
||||
SFrames.Movers:RegisterMover("ExtraBar", self.holder, "额外动作条",
|
||||
"CENTER", "UIParent", "CENTER", 0, -200)
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Late enable: called from ConfigUI when user toggles enable on
|
||||
--------------------------------------------------------------------------------
|
||||
function EB:Enable()
|
||||
local db = self:GetDB()
|
||||
db.enable = true
|
||||
if not self.holder then
|
||||
self:CreateButtons()
|
||||
self:SetupPoller()
|
||||
|
||||
SFrames:RegisterEvent("ACTIONBAR_SLOT_CHANGED", function()
|
||||
if not EB.buttons then return end
|
||||
local db2 = EB:GetDB()
|
||||
if not db2.enable then return end
|
||||
local slot = arg1
|
||||
local startSlot = db2.startSlot or 73
|
||||
local count = math.min(db2.buttonCount or 12, MAX_BUTTONS)
|
||||
if slot then
|
||||
local idx = slot - startSlot + 1
|
||||
if idx >= 1 and idx <= count then
|
||||
RefreshButtonAll(EB.buttons[idx])
|
||||
end
|
||||
else
|
||||
EB:RefreshAll()
|
||||
end
|
||||
end)
|
||||
SFrames:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN", function()
|
||||
EB:RefreshCooldowns()
|
||||
end)
|
||||
SFrames:RegisterEvent("ACTIONBAR_UPDATE_USABLE", function()
|
||||
EB:RefreshUsable()
|
||||
end)
|
||||
SFrames:RegisterEvent("ACTIONBAR_UPDATE_STATE", function()
|
||||
EB:RefreshStates()
|
||||
end)
|
||||
SFrames:RegisterEvent("UPDATE_BINDINGS", function()
|
||||
EB:RefreshHotkeys()
|
||||
end)
|
||||
|
||||
if SFrames.Movers and SFrames.Movers.RegisterMover then
|
||||
SFrames.Movers:RegisterMover("ExtraBar", self.holder, "额外动作条",
|
||||
"CENTER", "UIParent", "CENTER", 0, -200)
|
||||
end
|
||||
end
|
||||
self:ApplyConfig()
|
||||
self:RefreshHotkeys()
|
||||
end
|
||||
|
||||
function EB:Disable()
|
||||
local db = self:GetDB()
|
||||
db.enable = false
|
||||
if self.holder then
|
||||
self.holder:Hide()
|
||||
end
|
||||
if self.holder and SFrames.Movers and SFrames.Movers.IsLayoutMode
|
||||
and SFrames.Movers:IsLayoutMode() then
|
||||
pcall(SFrames.Movers.SyncMoverToFrame, SFrames.Movers, "ExtraBar")
|
||||
end
|
||||
end
|
||||
|
||||
function EB:RunButton(index)
|
||||
if not self.buttons then return end
|
||||
local db = self:GetDB()
|
||||
if not db.enable then return end
|
||||
if index < 1 or index > math.min(db.buttonCount or 12, MAX_BUTTONS) then return end
|
||||
local btn = self.buttons[index]
|
||||
if not btn or not btn:IsVisible() then return end
|
||||
local slot = btn.sfActionSlot
|
||||
if slot and HasAction(slot) then
|
||||
UseAction(slot)
|
||||
RefreshButtonAll(btn)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user