mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-22 07:36:56 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c7c91fdf7 | |||
| 7f5d18bc30 | |||
| 7efc3334ae | |||
| f322fbd6c6 | |||
| 41b77a5728 | |||
| 06fea6c889 | |||
| 940a1adae0 | |||
| d13710c5a4 | |||
| c3dd7cb601 | |||
| 9190b88974 | |||
| fe0ec82ede | |||
| 350b1d63ae | |||
| c7f42491ef | |||
| 6b458c9e27 | |||
| f338deebe7 |
@@ -2,11 +2,11 @@
|
||||
|
||||
[](https://octowow.st/)
|
||||
[](https://github.com/brues-code/ClassicAPI)
|
||||
[](https://github.com/balakethelock/SuperWoW)
|
||||
[](https://github.com/brues-code/nampower)
|
||||
[](https://codeberg.org/konaka/UnitXP_SP3)
|
||||
[](https://github.com/balakethelock/SuperWoW)
|
||||
[](https://github.com/brues-code/UnitXP_SP3)
|
||||
|
||||
**A pfUI fork specifically optimized for ClassicAPI on [Octo WoW](https://octowow.st/) which requires Nampower and SuperWoW with optional UnitXP_SP3 DLL integration.**
|
||||
**A pfUI fork specifically optimized for ClassicAPI on [Octo WoW](https://octowow.st/)**
|
||||
|
||||
This version includes significant performance improvements and DLL-enhanced features.
|
||||
|
||||
@@ -16,7 +16,7 @@ This version includes significant performance improvements and DLL-enhanced feat
|
||||
4. Copy "pfUI" into Wow-Directory\Interface\AddOns
|
||||
5. Restart Wow
|
||||
|
||||
## Optional DLL Enhancements
|
||||
## DLL Enhancements
|
||||
|
||||
Since pfUI 6.0.0 includes integrations with client-side DLLs for enhanced functionality. These DLLs are permitted on Octo WoW:
|
||||
|
||||
@@ -36,23 +36,24 @@ Provides:
|
||||
- Faster, safer profile sharing - Engine-side serialize/compress/base64 via C_EncodingUtil
|
||||
- C_Timer - After / NewTicker replacing hand-rolled OnUpdate throttles
|
||||
- Feign death, shapeshift and quest-item detection via real API calls
|
||||
- Mouseover Unit Frames
|
||||
- Click-casting
|
||||
- Loot Roll History (/loothistory)
|
||||
- New item highlighting
|
||||
- Plenty other functions
|
||||
|
||||
### [SuperWoW](https://github.com/balakethelock/SuperWoW)
|
||||
|
||||
Provides:
|
||||
- UnitPosition for distance calculations
|
||||
- SetMouseoverUnit for improved targeting
|
||||
|
||||
### [Nampower](https://github.com/brues-code/nampower)
|
||||
|
||||
Provides:
|
||||
- Spell queue indicator
|
||||
- GCD indicator
|
||||
- Reactive spell detection
|
||||
- Enhanced cast information
|
||||
|
||||
### [UnitXP_SP3](https://codeberg.org/konaka/UnitXP_SP3)
|
||||
### [SuperWoW](https://github.com/balakethelock/SuperWoW)
|
||||
|
||||
Provides:
|
||||
- Tracks party/raid units on the minimap
|
||||
|
||||
### [UnitXP_SP3](https://github.com/brues-code/UnitXP_SP3)
|
||||
|
||||
Provides:
|
||||
- Line of Sight detection
|
||||
@@ -79,6 +80,7 @@ Use `/pfdll` in-game to check which DLLs are detected.
|
||||
/swapfocus Toggle Focus and Target-Frame
|
||||
/pftest Toggle pfUI Unitframe Test Mode
|
||||
/abp Addon Button Panel
|
||||
/loothistory Show Loot Roll History
|
||||
|
||||
## Languages
|
||||
pfUI supports and contains language specific code for the following gameclients.
|
||||
@@ -91,7 +93,7 @@ pfUI supports and contains language specific code for the following gameclients.
|
||||
* Russian (ruRU)
|
||||
|
||||
## Recommended Addons
|
||||
* [pfQuest](https://shagu.org/pfQuest) A simple database and quest helper
|
||||
* [pfQuest](https://github.com/brues-code/pfQuest) A simple database and quest helper
|
||||
* [SuperCleveRoidMacros](https://github.com/brues-code/SuperCleveRoidMacros) Supports modern macro formats
|
||||
|
||||
## Plugins
|
||||
|
||||
@@ -155,6 +155,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("appearance", "cd", "dynamicsize", "1")
|
||||
pfUI:UpdateConfig("appearance", "castbar", "castbarcolor", ".7,.7,.9,.8")
|
||||
pfUI:UpdateConfig("appearance", "castbar", "channelcolor", ".9,.9,.7,.8")
|
||||
pfUI:UpdateConfig("appearance", "castbar", "failcolor", "1,.2,.2,.8")
|
||||
pfUI:UpdateConfig("appearance", "castbar", "texture", "Interface\\AddOns\\pfUI\\img\\bar")
|
||||
pfUI:UpdateConfig("appearance", "infight", "screen", "0")
|
||||
pfUI:UpdateConfig("appearance", "infight", "aggro", "0")
|
||||
@@ -784,6 +785,7 @@ function pfUI:LoadConfig()
|
||||
pfUI:UpdateConfig("tooltip", nil, "extguild", "1")
|
||||
pfUI:UpdateConfig("tooltip", nil, "itemid", "0")
|
||||
pfUI:UpdateConfig("tooltip", nil, "movespeed", "0")
|
||||
pfUI:UpdateConfig("tooltip", nil, "aurasource", "0")
|
||||
pfUI:UpdateConfig("tooltip", nil, "alpha", "0.8")
|
||||
pfUI:UpdateConfig("tooltip", nil, "alwaysperc", "0")
|
||||
pfUI:UpdateConfig("tooltip", "compare", "basestats", "1")
|
||||
|
||||
@@ -1246,3 +1246,249 @@ function pfUI.api.SkinMoneyInputFrame(frame)
|
||||
copperIcon:ClearAllPoints()
|
||||
copperIcon:SetPoint("LEFT", copper_editbox, "RIGHT", 2, 0)
|
||||
end
|
||||
|
||||
-- Shared icon picker widget. Builds a name field, a "currently selected"
|
||||
-- preview, and a 10x8 icon grid backed by IconDataProviderMixin with an
|
||||
-- All/Spells/Items filter and a name search. Used by the macro icon picker
|
||||
-- and the equipment manager's name/icon popup. The widgets attach to
|
||||
-- `parent`; the caller owns the surrounding frame, its OK/Cancel buttons,
|
||||
-- and the save flow. Selection is tracked by texture PATH (not index) so it
|
||||
-- survives filter changes -- a spell icon you picked still saves after you
|
||||
-- switch the filter to "Items" and it drops out of the visible grid.
|
||||
--
|
||||
-- name global-name prefix for the scroll frame and editboxes
|
||||
-- parent the popup frame to attach the widgets to
|
||||
-- extraType IconDataProviderExtraType.* seed (Spellbook / Equipment)
|
||||
-- nameLabelText header shown above the name field
|
||||
--
|
||||
-- Returns a handle:
|
||||
-- .editbox name EditBox (caller wires enter/escape)
|
||||
-- .search search EditBox
|
||||
-- .GetIcon() current selected texture path
|
||||
-- .SetIcon(path) set selection (nil -> question mark)
|
||||
-- .Refresh() ensure provider, redraw grid + preview
|
||||
-- .SetIconAreaShown(shown) show/hide the grid, filter, search, and labels
|
||||
function pfUI.api.CreateIconPicker(name, parent, extraType, nameLabelText)
|
||||
local QUESTION_MARK = "INTERFACE\\ICONS\\INV_MISC_QUESTIONMARK"
|
||||
|
||||
local ICON_GRID_COLS = 10
|
||||
local ICON_GRID_ROWS = 8
|
||||
local ICON_BTN_SIZE = 36
|
||||
local ICON_BTN_PAD = 6
|
||||
|
||||
local provider = nil
|
||||
local selectedIconPath = QUESTION_MARK
|
||||
local currentFilter = "all"
|
||||
local searchText = ""
|
||||
local filtered = nil -- provider indices matching the search, or nil when empty
|
||||
local RefreshIconGrid, RebuildFilter
|
||||
|
||||
local function EnsureProvider()
|
||||
if not provider then
|
||||
provider = CreateAndInitFromMixin(IconDataProviderMixin, extraType)
|
||||
end
|
||||
end
|
||||
|
||||
-- Normalize a texture to an uppercase basename so matching works across
|
||||
-- sources: GetMacroInfo / stored set icons return "Interface\Icons\<name>"
|
||||
-- while provider paths are uppercase-prefixed for base icons and
|
||||
-- mixed-case for spellbook extras.
|
||||
local function IconKey(path)
|
||||
if type(path) ~= "string" then return path end
|
||||
return string.gsub(string.upper(path), "^.*[\\/]", "")
|
||||
end
|
||||
|
||||
parent.nameLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
parent.nameLabel:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -20)
|
||||
parent.nameLabel:SetText(nameLabelText)
|
||||
|
||||
parent.editbox = CreateFrame("EditBox", name.."Name", parent, "InputBoxTemplate")
|
||||
parent.editbox:SetSize(280, 20)
|
||||
parent.editbox:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -38)
|
||||
parent.editbox:SetAutoFocus(false)
|
||||
parent.editbox:SetMaxLetters(16)
|
||||
CreateBackdrop(parent.editbox)
|
||||
|
||||
parent.selectedLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
parent.selectedLabel:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -14, -14)
|
||||
parent.selectedLabel:SetText(ICON_SELECTION_TITLE_CURRENT)
|
||||
parent.selectedLabel:SetTextColor(1, 0.82, 0)
|
||||
|
||||
parent.selectedPreview = CreateFrame("Frame", nil, parent)
|
||||
parent.selectedPreview:SetSize(42, 42)
|
||||
parent.selectedPreview:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -14, -30)
|
||||
CreateBackdrop(parent.selectedPreview)
|
||||
parent.selectedPreview.tex = parent.selectedPreview:CreateTexture(nil, "ARTWORK")
|
||||
parent.selectedPreview.tex:SetAllPoints(parent.selectedPreview)
|
||||
parent.selectedPreview.tex:SetTexCoord(.08, .92, .08, .92)
|
||||
|
||||
parent.iconLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
parent.iconLabel:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -86)
|
||||
parent.iconLabel:SetText(MACRO_POPUP_CHOOSE_ICON)
|
||||
|
||||
-- Icon grid + scroll
|
||||
local iconScroll = CreateFrame("ScrollFrame", name.."Scroll", parent, "FauxScrollFrameTemplate")
|
||||
iconScroll:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -116)
|
||||
iconScroll:SetWidth(ICON_GRID_COLS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
|
||||
iconScroll:SetHeight(ICON_GRID_ROWS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
|
||||
|
||||
local scrollbar = _G[name.."ScrollScrollBar"]
|
||||
scrollbar:ClearAllPoints()
|
||||
scrollbar:SetPoint("TOPLEFT", iconScroll, "TOPRIGHT", 8, -16)
|
||||
scrollbar:SetPoint("BOTTOMLEFT", iconScroll, "BOTTOMRIGHT", 8, 16)
|
||||
SkinScrollbar(scrollbar)
|
||||
|
||||
-- Filter dropdown: All / Spells / Items
|
||||
local filterDropdown = CreateFrame("Frame", name.."Filter", parent, "UIDropDownMenuTemplate")
|
||||
filterDropdown:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, -80)
|
||||
local function ApplyFilter(value)
|
||||
currentFilter = value
|
||||
UIDropDownMenu_SetSelectedValue(filterDropdown, value)
|
||||
if provider then
|
||||
if value == "spells" then provider:SetIconTypes({ IconDataProviderIconType.Spell })
|
||||
elseif value == "items" then provider:SetIconTypes({ IconDataProviderIconType.Item })
|
||||
else provider:SetIconTypes(nil) end
|
||||
RebuildFilter()
|
||||
RefreshIconGrid()
|
||||
end
|
||||
end
|
||||
UIDropDownMenu_Initialize(filterDropdown, function()
|
||||
local info
|
||||
info = {}; info.text = ICON_FILTER_ALL; info.value = "all"
|
||||
info.func = function() ApplyFilter("all") end
|
||||
info.checked = currentFilter == "all"
|
||||
UIDropDownMenu_AddButton(info)
|
||||
info = {}; info.text = ICON_FILTER_SPELL; info.value = "spells"
|
||||
info.func = function() ApplyFilter("spells") end
|
||||
info.checked = currentFilter == "spells"
|
||||
UIDropDownMenu_AddButton(info)
|
||||
info = {}; info.text = ICON_FILTER_ITEM; info.value = "items"
|
||||
info.func = function() ApplyFilter("items") end
|
||||
info.checked = currentFilter == "items"
|
||||
UIDropDownMenu_AddButton(info)
|
||||
end)
|
||||
UIDropDownMenu_SetWidth(120, filterDropdown)
|
||||
UIDropDownMenu_SetSelectedValue(filterDropdown, "all")
|
||||
SkinDropDown(filterDropdown)
|
||||
|
||||
local iconButtons = {}
|
||||
for r = 1, ICON_GRID_ROWS do
|
||||
for c = 1, ICON_GRID_COLS do
|
||||
local i = (r - 1) * ICON_GRID_COLS + c
|
||||
local btn = CreateFrame("Button", nil, parent)
|
||||
btn:SetSize(ICON_BTN_SIZE, ICON_BTN_SIZE)
|
||||
btn:SetPoint("TOPLEFT", iconScroll, "TOPLEFT", (c-1) * (ICON_BTN_SIZE + ICON_BTN_PAD), -(r-1) * (ICON_BTN_SIZE + ICON_BTN_PAD))
|
||||
CreateBackdrop(btn)
|
||||
btn.texture = btn:CreateTexture(nil, "ARTWORK")
|
||||
btn.texture:SetAllPoints(btn)
|
||||
btn.texture:SetTexCoord(.08, .92, .08, .92)
|
||||
btn:SetScript("OnClick", function()
|
||||
if this.iconIndex and provider then
|
||||
local path = provider:GetIconByIndex(this.iconIndex)
|
||||
if path then selectedIconPath = path end
|
||||
RefreshIconGrid()
|
||||
end
|
||||
end)
|
||||
btn:SetScript("OnEnter", function()
|
||||
if not this.iconIndex or not provider then return end
|
||||
local path = provider:GetIconByIndex(this.iconIndex)
|
||||
if type(path) == "string" then
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText(IconKey(path), 1, 1, 1)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
end)
|
||||
btn:SetScript("OnLeave", GameTooltip_Hide)
|
||||
iconButtons[i] = btn
|
||||
end
|
||||
end
|
||||
|
||||
-- Rebuild the search-filtered index list (provider indices whose icon
|
||||
-- basename contains the search text); nil when the search box is empty.
|
||||
function RebuildFilter()
|
||||
EnsureProvider()
|
||||
if searchText == "" then
|
||||
filtered = nil
|
||||
return
|
||||
end
|
||||
filtered = {}
|
||||
for i = 1, provider:GetNumIcons() do
|
||||
local path = provider:GetIconByIndex(i)
|
||||
if type(path) == "string" and string.find(IconKey(path), searchText, 1, true) then
|
||||
table.insert(filtered, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function RefreshIconGrid()
|
||||
EnsureProvider()
|
||||
local numIcons = filtered and table.getn(filtered) or provider:GetNumIcons()
|
||||
local numRows = math.ceil(numIcons / ICON_GRID_COLS)
|
||||
FauxScrollFrame_Update(iconScroll, numRows, ICON_GRID_ROWS, ICON_BTN_SIZE + ICON_BTN_PAD)
|
||||
local offset = FauxScrollFrame_GetOffset(iconScroll)
|
||||
for i = 1, ICON_GRID_ROWS * ICON_GRID_COLS do
|
||||
local listIdx = i + offset * ICON_GRID_COLS
|
||||
local btn = iconButtons[i]
|
||||
if listIdx <= numIcons then
|
||||
btn:Show()
|
||||
local providerIdx = filtered and filtered[listIdx] or listIdx
|
||||
btn.iconIndex = providerIdx
|
||||
local path = provider:GetIconByIndex(providerIdx)
|
||||
btn.texture:SetTexture(path)
|
||||
if IconKey(path) == IconKey(selectedIconPath) then
|
||||
btn.backdrop:SetBackdropBorderColor(1, 0.82, 0, 1)
|
||||
else
|
||||
btn.backdrop:SetBackdropBorderColor(pfUI.cache.er, pfUI.cache.eg, pfUI.cache.eb, pfUI.cache.ea)
|
||||
end
|
||||
else
|
||||
btn:Hide()
|
||||
btn.iconIndex = nil
|
||||
end
|
||||
end
|
||||
parent.selectedPreview.tex:SetTexture(selectedIconPath)
|
||||
end
|
||||
|
||||
iconScroll:SetScript("OnVerticalScroll", function()
|
||||
FauxScrollFrame_OnVerticalScroll(ICON_BTN_SIZE + ICON_BTN_PAD, function() RefreshIconGrid() end)
|
||||
end)
|
||||
|
||||
-- Search box (bottom-left) filters the grid by icon name.
|
||||
parent.searchLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
parent.searchLabel:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", 16, 18)
|
||||
parent.searchLabel:SetText(SEARCH)
|
||||
|
||||
parent.search = CreateFrame("EditBox", name.."Search", parent, "InputBoxTemplate")
|
||||
parent.search:SetSize(180, 20)
|
||||
parent.search:SetPoint("LEFT", parent.searchLabel, "RIGHT", 10, 0)
|
||||
parent.search:SetAutoFocus(false)
|
||||
CreateBackdrop(parent.search)
|
||||
parent.search:SetScript("OnEscapePressed", function() this:ClearFocus() end)
|
||||
parent.search:SetScript("OnTextChanged", function()
|
||||
searchText = strupper(strtrim(this:GetText()))
|
||||
RebuildFilter()
|
||||
scrollbar:SetValue(0)
|
||||
RefreshIconGrid()
|
||||
end)
|
||||
|
||||
-- Release the provider when the popup closes so its icon cache GCs.
|
||||
parent:HookScript("OnHide", function()
|
||||
if provider then provider:Release(); provider = nil end
|
||||
end)
|
||||
|
||||
local handle = { editbox = parent.editbox, search = parent.search }
|
||||
function handle.GetIcon() return selectedIconPath end
|
||||
function handle.SetIcon(path) selectedIconPath = path or QUESTION_MARK end
|
||||
function handle.Refresh() RefreshIconGrid() end
|
||||
function handle.SetIconAreaShown(shown)
|
||||
local method = shown and "Show" or "Hide"
|
||||
iconScroll[method](iconScroll)
|
||||
for _, b in ipairs(iconButtons) do b[method](b) end
|
||||
parent.iconLabel[method](parent.iconLabel)
|
||||
parent.selectedLabel[method](parent.selectedLabel)
|
||||
parent.selectedPreview[method](parent.selectedPreview)
|
||||
filterDropdown[method](filterDropdown)
|
||||
parent.searchLabel[method](parent.searchLabel)
|
||||
parent.search[method](parent.search)
|
||||
end
|
||||
return handle
|
||||
end
|
||||
|
||||
+90
-160
@@ -238,6 +238,7 @@ function pfUI.uf:UpdateVisibility()
|
||||
end
|
||||
|
||||
local unitstr = string.format("%s%s", self.label or "", self.id or "")
|
||||
self:SetAttribute("unit", unitstr ~= "" and unitstr or nil)
|
||||
local visibility = string.format("[target=%s,exists] show; hide", unitstr)
|
||||
|
||||
-- Group frames are redundant when the group is already shown as a raid grid:
|
||||
@@ -401,7 +402,7 @@ function pfUI.uf:UpdateConfig()
|
||||
if tonumber(f.config.pheight) < 0 then f.power:Hide() end
|
||||
|
||||
pfUI.api.CreateBackdrop(f.power, default_border)
|
||||
f.power.bar:SetFrameLevel(f.power:GetFrameLevel() + 1)
|
||||
f.power.bar:SetFrameLevel(f.power.backdrop:GetFrameLevel() + 1)
|
||||
f.power.bar:SetStatusBarTexture(pfUI.media[f.config.pbartexture])
|
||||
f.power.bar:SetAllPoints(f.power)
|
||||
|
||||
@@ -1110,11 +1111,7 @@ function pfUI.uf.OnUpdate()
|
||||
|
||||
-- update ressurections
|
||||
local ress = libpredict:UnitHasIncomingResurrection(unit)
|
||||
if ress and UnitIsDeadOrGhost(unit) then
|
||||
this.ressIcon:Show()
|
||||
else
|
||||
this.ressIcon:Hide()
|
||||
end
|
||||
this.ressIcon:SetShown(ress and UnitIsDeadOrGhost(unit))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1177,52 +1174,6 @@ function pfUI.uf.OnUpdate()
|
||||
end
|
||||
end
|
||||
|
||||
function pfUI.uf.OnEnter()
|
||||
if not this.label then return end
|
||||
|
||||
local unitstr = this.label .. this.id
|
||||
if UnitExists(unitstr) then
|
||||
SetMouseoverUnit(unitstr)
|
||||
end
|
||||
|
||||
if this.config.showtooltip == "0" then return end
|
||||
GameTooltip_SetDefaultAnchor(GameTooltip, this)
|
||||
GameTooltip:SetUnit(this.label .. this.id)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
|
||||
function pfUI.uf.OnLeave()
|
||||
SetMouseoverUnit("")
|
||||
|
||||
GameTooltip:FadeOut()
|
||||
end
|
||||
|
||||
function pfUI.uf.OnClick()
|
||||
if not this.label and this.unitname then
|
||||
TargetByName(this.unitname, true)
|
||||
else
|
||||
pfUI.uf:ClickAction(arg1)
|
||||
end
|
||||
end
|
||||
|
||||
function pfUI.uf:RightClickAction(unit)
|
||||
if unit == "player" then
|
||||
ToggleDropDownMenu(1, nil, PlayerFrameDropDown, "cursor")
|
||||
elseif unit == "target" then
|
||||
ToggleDropDownMenu(1, nil, TargetFrameDropDown, "cursor")
|
||||
elseif unit == "pet" then
|
||||
ToggleDropDownMenu(1, nil, PetFrameDropDown, "cursor")
|
||||
elseif unit == "party" or strfind(unit, "party%d") then
|
||||
ToggleDropDownMenu(1, nil, getglobal("PartyMemberFrame" .. this.id .. "DropDown"), "cursor")
|
||||
elseif unit == "raid" or strfind(unit, "raid%d") then
|
||||
local name = this.lastUnit
|
||||
local unitstr = this.label .. this.id
|
||||
FriendsDropDown.displayMode = "MENU"
|
||||
FriendsDropDown.initialize = function() UnitPopup_ShowMenu(_G[UIDROPDOWNMENU_OPEN_MENU], "PARTY", unitstr, name, id) end
|
||||
ToggleDropDownMenu(1, nil, FriendsDropDown, "cursor")
|
||||
end
|
||||
end
|
||||
|
||||
function pfUI.uf:EnableEvents()
|
||||
local f = self
|
||||
|
||||
@@ -1258,17 +1209,72 @@ function pfUI.uf:EnableEvents()
|
||||
'MiddleButtonUp', 'Button4Up', 'Button5Up')
|
||||
end
|
||||
|
||||
-- ============================================================
|
||||
-- Clique compatibility
|
||||
-- Clique's pfUI plugin replaces pfUI.uf.ClickAction and expects an
|
||||
-- OnClick script to call it (with `this` = frame, `arg1` = button). The
|
||||
-- secure attribute path replaced that dispatch, so when Clique is loaded
|
||||
-- EnableScripts routes clicks through this legacy Lua path instead. The
|
||||
-- secure path is skipped there because type1="target" switches the target
|
||||
-- before Clique can cast.
|
||||
-- ============================================================
|
||||
|
||||
function pfUI.uf.OnClick()
|
||||
if not this.label and this.unitname then
|
||||
TargetByName(this.unitname, true)
|
||||
else
|
||||
pfUI.uf:ClickAction(arg1)
|
||||
end
|
||||
end
|
||||
|
||||
function pfUI.uf:ClickAction(button)
|
||||
local label = this.label or ""
|
||||
local id = this.id or ""
|
||||
local unitstr = label .. id
|
||||
|
||||
if SpellIsTargeting() and button == "RightButton" then
|
||||
SpellStopTargeting()
|
||||
return
|
||||
end
|
||||
|
||||
if SpellIsTargeting() and button == "LeftButton" then
|
||||
SpellTargetUnit(unitstr)
|
||||
elseif CursorHasItem() then
|
||||
DropItemOnUnit(unitstr)
|
||||
end
|
||||
|
||||
-- right-click opens the standard unit menu (ClassicAPI resolves the type)
|
||||
if button == "RightButton" then
|
||||
ClassicAPI_ToggleUnitMenu(unitstr)
|
||||
return
|
||||
end
|
||||
|
||||
-- drop food on petframe
|
||||
if label == "pet" and CursorHasItem() then
|
||||
if UnitClassBase("player") == "HUNTER" then
|
||||
DropItemOnUnit("pet")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- default click
|
||||
TargetUnit(unitstr)
|
||||
end
|
||||
|
||||
function pfUI.uf:EnableScripts()
|
||||
local f = self
|
||||
|
||||
f:SetScript("OnClick", pfUI.uf.OnClick)
|
||||
if IsAddOnLoaded("Clique") then
|
||||
f:SetScript("OnClick", pfUI.uf.OnClick)
|
||||
else
|
||||
f:SetAttribute("type1", "target")
|
||||
f:SetAttribute("type2", "menu")
|
||||
f:EnableClickCast()
|
||||
end
|
||||
|
||||
f:SetScript("OnShow", pfUI.uf.OnShow)
|
||||
f:SetScript("OnEvent", pfUI.uf.OnEvent)
|
||||
f:SetScript("OnUpdate", pfUI.uf.OnUpdate)
|
||||
f:SetScript("OnEnter", pfUI.uf.OnEnter)
|
||||
f:SetScript("OnLeave", pfUI.uf.OnLeave)
|
||||
f:EnableClickCast()
|
||||
|
||||
-- add frame to visibility refresh handler
|
||||
visibilityscan.frames[f] = true
|
||||
@@ -1296,7 +1302,7 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
|
||||
unit, id = "target", ""
|
||||
end
|
||||
|
||||
local f = CreateFrame("Button", "pf" .. fname, UIParent, UNITFRAME_SECURE_TEMPLATE)
|
||||
local f = CreateFrame("Button", "pf" .. fname, UIParent)
|
||||
|
||||
-- add unitframe functions
|
||||
f.UpdateFrameSize = pfUI.uf.UpdateFrameSize
|
||||
@@ -1423,6 +1429,10 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
|
||||
f:Hide()
|
||||
end
|
||||
|
||||
if f.label ~= "" then
|
||||
f:SetAttribute("unit", f.label .. f.id)
|
||||
end
|
||||
|
||||
-- register frame for clique
|
||||
_G.ClickCastFrames = ClickCastFrames or {}
|
||||
ClickCastFrames[f] = true
|
||||
@@ -1477,11 +1487,7 @@ function pfUI.uf:RefreshIndicators(unit)
|
||||
local unitstr = unit.label .. unit.id
|
||||
|
||||
if unit.leaderIcon then -- Leader Icon
|
||||
if unit.config.leadericon == "1" and UnitIsPartyLeader(unitstr) and IsInGroup() then
|
||||
unit.leaderIcon:Show()
|
||||
else
|
||||
unit.leaderIcon:Hide()
|
||||
end
|
||||
unit.leaderIcon:SetShown(unit.config.leadericon == "1" and UnitIsPartyLeader(unitstr) and IsInGroup())
|
||||
end
|
||||
|
||||
if unit.lootIcon then -- Loot Icon
|
||||
@@ -1491,29 +1497,16 @@ function pfUI.uf:RefreshIndicators(unit)
|
||||
-- no third return value here.. but leaving this as a hint
|
||||
local method, group, raid = GetLootMethod()
|
||||
local name = group and UnitName(group == 0 and "player" or "party"..group) or raid and UnitName("raid"..raid) or nil
|
||||
|
||||
if name and name == UnitName(unitstr) then
|
||||
unit.lootIcon:Show()
|
||||
else
|
||||
unit.lootIcon:Hide()
|
||||
end
|
||||
unit.lootIcon:SetShown(name and name == UnitName(unitstr))
|
||||
end
|
||||
end
|
||||
|
||||
if unit.pvpIcon then -- PvP Icon
|
||||
if unit.config.showPVP == "1" and UnitIsPVP(unitstr) then
|
||||
unit.pvpIcon:Show()
|
||||
else
|
||||
unit.pvpIcon:Hide()
|
||||
end
|
||||
unit.pvpIcon:SetShown(unit.config.showPVP == "1" and UnitIsPVP(unitstr))
|
||||
end
|
||||
|
||||
if unit.restIcon and unit:GetName() == "pfPlayer" then -- Rest Icon
|
||||
if C.unitframes.player.showRest == "1" and UnitIsUnit(unitstr, "player") and IsResting() then
|
||||
unit.restIcon:Show()
|
||||
else
|
||||
unit.restIcon:Hide()
|
||||
end
|
||||
unit.restIcon:SetShown(C.unitframes.player.showRest == "1" and UnitIsUnit(unitstr, "player") and IsResting())
|
||||
end
|
||||
|
||||
if unit.happinessIcon and unit:GetName() == "pfPet" then -- Happiness Icon
|
||||
@@ -1583,7 +1576,7 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
if not unit.hp then return end
|
||||
if not unit.power then return end
|
||||
if not unit.id then unit.id = "" end
|
||||
local component = component or ""
|
||||
component = component or ""
|
||||
|
||||
-- don't update scanner activity
|
||||
if unit.label == "target" or unit.label == "targettarget" or unit.label == "targettargettarget" then
|
||||
@@ -2126,14 +2119,6 @@ function pfUI.uf:RefreshUnit(unit, component)
|
||||
end
|
||||
end
|
||||
|
||||
local buttons = {
|
||||
[1] = "LeftButton",
|
||||
[2] = "RightButton",
|
||||
[3] = "MiddleButton",
|
||||
[4] = "Button4",
|
||||
[5] = "Button5",
|
||||
}
|
||||
|
||||
local modifiers = {
|
||||
[""] = "",
|
||||
["alt"] = "_alt",
|
||||
@@ -2143,86 +2128,31 @@ local modifiers = {
|
||||
|
||||
function pfUI.uf:EnableClickCast()
|
||||
if self.config.clickcast ~= "1" then return end
|
||||
for bid, button in pairs(buttons) do
|
||||
for bid = 1, 5 do -- LeftButton, RightButton, MiddleButton, Button4, Button5
|
||||
for modifier, mconf in pairs(modifiers) do
|
||||
local bconf = bid == 1 and "" or bid
|
||||
if pfUI_config.unitframes["clickcast"..bconf..mconf] ~= "" then
|
||||
-- fill clickaction table
|
||||
self.clickactions = self.clickactions or {}
|
||||
self.clickactions[modifier..button] = pfUI_config.unitframes["clickcast"..bconf..mconf]
|
||||
local action = pfUI_config.unitframes["clickcast"..bconf..mconf]
|
||||
if action and action ~= "" then
|
||||
local prefix = modifier ~= "" and (modifier .. "-") or ""
|
||||
local low = string.lower(action)
|
||||
if low == "menu" then
|
||||
self:SetAttribute(prefix .. "type" .. bid, "menu")
|
||||
elseif low == "target" then
|
||||
self:SetAttribute(prefix .. "type" .. bid, "target")
|
||||
elseif low == "focus" then
|
||||
self:SetAttribute(prefix .. "type" .. bid, "focus")
|
||||
elseif string.find(action, "^/") then
|
||||
self:SetAttribute(prefix .. "type" .. bid, "macro")
|
||||
self:SetAttribute(prefix .. "macrotext" .. bid, action)
|
||||
else
|
||||
self:SetAttribute(prefix .. "type" .. bid, "spell")
|
||||
self:SetAttribute(prefix .. "spell" .. bid, action)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pfUI.uf:ClickAction(button)
|
||||
local label = this.label or ""
|
||||
local id = this.id or ""
|
||||
local unitstr = label .. id
|
||||
local showmenu = button == "RightButton" and true or nil
|
||||
if SpellIsTargeting() and button == "RightButton" then
|
||||
SpellStopTargeting()
|
||||
return
|
||||
end
|
||||
|
||||
if SpellIsTargeting() and button == "LeftButton" then
|
||||
SpellTargetUnit(unitstr)
|
||||
elseif CursorHasItem() then
|
||||
DropItemOnUnit(unitstr)
|
||||
end
|
||||
|
||||
-- run click casting if enabled
|
||||
local modstring = ""
|
||||
modstring = IsAltKeyDown() and modstring.."alt" or modstring
|
||||
modstring = IsControlKeyDown() and modstring.."ctrl" or modstring
|
||||
modstring = IsShiftKeyDown() and modstring.."shift" or modstring
|
||||
modstring = modstring..button
|
||||
if this.clickactions and this.clickactions[modstring] then
|
||||
local action_lower = string.lower(this.clickactions[modstring])
|
||||
if string.find(action_lower, "^menu") then
|
||||
-- show menu
|
||||
showmenu = true
|
||||
elseif string.find(action_lower, "^target") then
|
||||
-- target unit
|
||||
showmenu = nil
|
||||
else
|
||||
-- run click cast action
|
||||
local is_macro = string.find(this.clickactions[modstring], "^%/(.+)")
|
||||
|
||||
local tswitch = UnitIsUnit(unitstr, "target")
|
||||
TargetUnit(unitstr)
|
||||
|
||||
if is_macro then
|
||||
RunMacroText(this.clickactions[modstring])
|
||||
else
|
||||
CastSpellByName(this.clickactions[modstring])
|
||||
end
|
||||
|
||||
if not tswitch then TargetLastTarget() end
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- dropdown menus
|
||||
if showmenu then
|
||||
pfUI.uf:RightClickAction(label)
|
||||
return
|
||||
end
|
||||
|
||||
-- drop food on petframe
|
||||
if label == "pet" and CursorHasItem() then
|
||||
local playerClass = UnitClassBase("player")
|
||||
if playerClass == "HUNTER" then
|
||||
DropItemOnUnit("pet")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- default click
|
||||
TargetUnit(unitstr)
|
||||
end
|
||||
|
||||
function pfUI.uf:AddIcon(frame, pos, icon, timeleft, stacks, start, duration)
|
||||
local showtime = frame.config.indicator_time == "1" and true or nil
|
||||
local showstacks = frame.config.indicator_stacks == "1" and true or nil
|
||||
|
||||
@@ -25,7 +25,6 @@ PLAYER_BUFF_START_ID = -1
|
||||
|
||||
ACTIONBAR_SECURE_TEMPLATE_BAR = nil
|
||||
ACTIONBAR_SECURE_TEMPLATE_BUTTON = nil
|
||||
UNITFRAME_SECURE_TEMPLATE = nil
|
||||
|
||||
--[[ Vanilla API Extensions ]]--
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
<Include file="..\modules\energytick.lua"/>
|
||||
<Include file="..\modules\totems.lua"/>
|
||||
<Include file="..\modules\macrotweak.lua"/>
|
||||
<Include file="..\modules\macroicons.lua"/>
|
||||
<Include file="..\modules\turtle-wow.lua"/>
|
||||
<Include file="..\modules\superwow.lua"/>
|
||||
<Include file="..\modules\innervatecall.lua"/>
|
||||
|
||||
+35
-51
@@ -600,17 +600,17 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
-- update usable [out-of-range = 1, oom = 2, not-usable = 3, default = 0]
|
||||
if self.outofrange and C.bars.glowrange == "1" then
|
||||
if self.vertexstate ~= 1 then
|
||||
self.icon:SetVertexColor(self.rangeColor[1], self.rangeColor[2], self.rangeColor[3], self.rangeColor[4])
|
||||
self.icon:SetVertexColor(self.rangeColor:GetRGBA())
|
||||
self.vertexstate = 1
|
||||
end
|
||||
elseif oom and C.bars.showoom == "1" then
|
||||
if self.vertexstate ~= 2 then
|
||||
self.icon:SetVertexColor(self.oomColor[1], self.oomColor[2], self.oomColor[3], self.oomColor[4])
|
||||
self.icon:SetVertexColor(self.oomColor:GetRGBA())
|
||||
self.vertexstate = 2
|
||||
end
|
||||
elseif not usable and C.bars.showna == "1" then
|
||||
if self.vertexstate ~= 3 then
|
||||
self.icon:SetVertexColor(self.naColor[1], self.naColor[2], self.naColor[3], self.naColor[4])
|
||||
self.icon:SetVertexColor(self.naColor:GetRGBA())
|
||||
self.vertexstate = 3
|
||||
end
|
||||
else
|
||||
@@ -992,13 +992,13 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
local font_offset = tonumber(C.bars.font_offset)
|
||||
|
||||
local macro_size = tonumber(C.bars.macro_size)
|
||||
local macro_color = { strsplit(",", C.bars.macro_color) }
|
||||
local macro_color = { GetStringColor(C.bars.macro_color) }
|
||||
|
||||
local count_size = tonumber(C.bars.count_size)
|
||||
local count_color = { strsplit(",", C.bars.count_color) }
|
||||
local count_color = { GetStringColor(C.bars.count_color) }
|
||||
|
||||
local bind_size = tonumber(C.bars.bind_size)
|
||||
local bind_color = { strsplit(",", C.bars.bind_color) }
|
||||
local bind_color = { GetStringColor(C.bars.bind_color) }
|
||||
|
||||
local cd_size = tonumber(C.bars.cd_size)
|
||||
|
||||
@@ -1157,17 +1157,17 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
end
|
||||
|
||||
-- range glow color
|
||||
f.rangeColor = { strsplit(",", C.bars.rangecolor) }
|
||||
f.rangeColor = GetStringColorObject(C.bars.rangecolor)
|
||||
|
||||
-- out of mana color
|
||||
f.oomColor = { strsplit(",", C.bars.oomcolor) }
|
||||
f.oomColor = GetStringColorObject(C.bars.oomcolor)
|
||||
|
||||
-- not usable color
|
||||
f.naColor = { strsplit(",", C.bars.nacolor) }
|
||||
f.naColor = GetStringColorObject(C.bars.nacolor)
|
||||
|
||||
-- equipped color
|
||||
if f.equipped then
|
||||
f.equipped:SetTexture(strsplit(",", C.bars.eqcolor))
|
||||
f.equipped:SetTexture(GetStringColor(C.bars.eqcolor))
|
||||
end
|
||||
|
||||
-- general appearance
|
||||
@@ -1546,7 +1546,7 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
-- via GetActionInfo + C_Spell.GetSpellReagents. Macro actions and
|
||||
-- bag-item actions are skipped (their reagent resolution would need a
|
||||
-- macro-body parse / item-effect lookup that we don't bother with).
|
||||
local UpdateSlot = function(slot)
|
||||
local function UpdateSlot(slot)
|
||||
local newID = nil
|
||||
if HasAction(slot) then
|
||||
local kind, spellID = GetActionInfo(slot)
|
||||
@@ -1561,60 +1561,44 @@ pfUI:RegisterModule("actionbar", function ()
|
||||
if reagent_slots[slot] ~= newID then
|
||||
reagent_slots[slot] = newID
|
||||
if newID then
|
||||
reagent_counts[newID] = reagent_counts[newID] or 0
|
||||
reagent_counts[newID] = reagent_counts[newID] or C_Item.GetItemCount(newID)
|
||||
end
|
||||
updatecache[slot] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Recount every tracked reagent and flag its buttons for a refresh.
|
||||
local RecountReagents = function()
|
||||
for itemID in pairs(reagent_counts) do
|
||||
reagent_counts[itemID] = C_Item.GetItemCount(itemID)
|
||||
end
|
||||
for slot in pairs(reagent_slots) do
|
||||
updatecache[slot] = true
|
||||
end
|
||||
end
|
||||
|
||||
local reagentcounter = CreateFrame("Frame", "pfReagentCounter", UIParent)
|
||||
reagentcounter:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
reagentcounter:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
|
||||
reagentcounter:RegisterEvent("BAG_UPDATE_DELAYED")
|
||||
reagentcounter:SetScript("OnEvent", function()
|
||||
if event == "BAG_UPDATE_DELAYED" then
|
||||
this.event = true
|
||||
if event == "ACTIONBAR_SLOT_CHANGED" then
|
||||
-- arg1 is the changed slot; 0 (or nil) means "all slots"
|
||||
if arg1 and arg1 > 0 then
|
||||
UpdateSlot(arg1)
|
||||
else
|
||||
for slot = 1, 120 do UpdateSlot(slot) end
|
||||
end
|
||||
elseif event == "BAG_UPDATE_DELAYED" then
|
||||
-- inventory changed: refresh the counts we already track
|
||||
RecountReagents()
|
||||
else
|
||||
this.scan = 1
|
||||
-- PLAYER_ENTERING_WORLD: seed the full reagent map (UpdateSlot seeds
|
||||
-- each new reagent's count; a BAG_UPDATE_DELAYED follows during login)
|
||||
for slot = 1, 120 do UpdateSlot(slot) end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Reagent counter update with throttle for performance optimization
|
||||
reagentcounter:SetScript("OnUpdate", function()
|
||||
-- Throttle entire function to 10 FPS for smooth scanning
|
||||
if (this.tick_update or 0) > GetTime() then return end
|
||||
this.tick_update = GetTime() + 0.1
|
||||
|
||||
-- scan one action slot per update
|
||||
if this.scan and this.scan <= 120 then
|
||||
UpdateSlot(this.scan)
|
||||
this.scan = this.scan + 1
|
||||
end
|
||||
|
||||
-- trigger reagent count updates after action scans
|
||||
if this.scan and this.scan >= 120 then
|
||||
this.event = true
|
||||
this.scan = nil
|
||||
end
|
||||
|
||||
-- queue events to fire only once per second
|
||||
if not this.event then return end
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end
|
||||
|
||||
-- scan for all reagent item counts
|
||||
for itemID in pairs(reagent_counts) do
|
||||
reagent_counts[itemID] = C_Item.GetItemCount(itemID)
|
||||
end
|
||||
|
||||
-- update all actionbar buttons
|
||||
for slot in pairs(reagent_slots) do
|
||||
updatecache[slot] = true
|
||||
end
|
||||
|
||||
-- remove event trigger
|
||||
this.event = nil
|
||||
end)
|
||||
|
||||
function IsReagentAction(slot)
|
||||
return reagent_slots[slot] and true or nil
|
||||
end
|
||||
|
||||
+16
-13
@@ -397,22 +397,25 @@ pfUI:RegisterModule("addonbuttons", function ()
|
||||
pfUI.addonbuttons:ProcessButtons()
|
||||
end)
|
||||
|
||||
pfUI.addonbuttons:SetScript("OnUpdate", function()
|
||||
-- Initial setup on the next frame, once other addons' minimap buttons exist
|
||||
RunNextFrame(function()
|
||||
-- check if the panel should be shown by default
|
||||
if not this.initialized then
|
||||
if C.abuttons.showdefault == "1" and GetNumButtons() > 0 then
|
||||
pfUI.addonbuttons:Show()
|
||||
else
|
||||
pfUI.addonbuttons:Hide()
|
||||
end
|
||||
|
||||
-- update all buttons
|
||||
pfUI.addonbuttons:ProcessButtons()
|
||||
this.initialized = true
|
||||
if C.abuttons.showdefault == "1" and GetNumButtons() > 0 then
|
||||
pfUI.addonbuttons:Show()
|
||||
else
|
||||
pfUI.addonbuttons:Hide()
|
||||
end
|
||||
|
||||
-- throttle updates to once per 5 seconds
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 5 end
|
||||
-- update all buttons and apply workarounds
|
||||
pfUI.addonbuttons:ProcessButtons()
|
||||
for k, v in pairs(pfUI.addonbuttons.overrides) do
|
||||
_G[k] = v
|
||||
end
|
||||
end)
|
||||
|
||||
-- Rescan minimap buttons every 5 seconds while the panel is shown
|
||||
C_Timer.NewTicker(5, function()
|
||||
if not pfUI.addonbuttons:IsShown() then return end
|
||||
|
||||
-- reload/rescan minimap buttons
|
||||
pfUI.addonbuttons:ProcessButtons()
|
||||
|
||||
+2
-8
@@ -138,16 +138,10 @@ pfUI:RegisterModule("buff", function ()
|
||||
end
|
||||
end)
|
||||
|
||||
buff:SetScript("OnLeave", function()
|
||||
GameTooltip:Hide()
|
||||
end)
|
||||
buff:SetScript("OnLeave", GameTooltip_Hide)
|
||||
|
||||
buff:SetScript("OnClick", function()
|
||||
if CancelItemTempEnchantment and this.mode and this.mode == "MAINHAND" then
|
||||
CancelItemTempEnchantment(1)
|
||||
elseif CancelItemTempEnchantment and this.mode and this.mode == "OFFHAND" then
|
||||
CancelItemTempEnchantment(2)
|
||||
elseif this.spellId then
|
||||
if this.spellId then
|
||||
C_Spell.CancelSpellByID(this.spellId)
|
||||
end
|
||||
end)
|
||||
|
||||
+112
-137
@@ -16,9 +16,13 @@ pfUI:RegisterModule("castbar", function ()
|
||||
end
|
||||
end
|
||||
|
||||
-- Clear cast state on the bar. Shows the bar full for one frame, then
|
||||
-- OnUpdate fades it out.
|
||||
local function ClearBar(cb)
|
||||
-- Clear cast state on the bar and start the fade-out. On a normal end the bar
|
||||
-- is left at its current fill (a completed cast is already ~full; nothing
|
||||
-- snaps to full, which used to flash for a frame when the next cast stamped
|
||||
-- the bar). When `failed` is set for a cast that was actually in progress, the
|
||||
-- bar flashes full red before fading — the cancelled-cast indicator.
|
||||
local function ClearBar(cb, failed)
|
||||
local wasActive = cb.endTime ~= nil
|
||||
cb.startTime, cb.endTime, cb.isChannel = nil, nil, nil
|
||||
cb.activeName, cb.spellID = nil, nil
|
||||
cb.isTradeskill = nil
|
||||
@@ -26,8 +30,11 @@ pfUI:RegisterModule("castbar", function ()
|
||||
cb.tradeskillSingleMs, cb.currentCraftStart = nil, nil
|
||||
cb.lastMax = nil
|
||||
cb.delay = 0
|
||||
cb.bar:SetMinMaxValues(1, 100)
|
||||
cb.bar:SetValue(100)
|
||||
if failed and wasActive then
|
||||
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar.failcolor))
|
||||
cb.bar:SetMinMaxValues(0, 1)
|
||||
cb.bar:SetValue(1)
|
||||
end
|
||||
if cb.bar.spark then cb.bar.spark:Hide() end
|
||||
cb.fadeout = 1
|
||||
end
|
||||
@@ -65,8 +72,8 @@ pfUI:RegisterModule("castbar", function ()
|
||||
end
|
||||
|
||||
-- Mark the start of craft 2..N within an active merge (called from the
|
||||
-- SPELLCAST_START / SPELL_START_SELF handlers). Resets the per-craft
|
||||
-- spark to the left edge of the bar.
|
||||
-- UNIT_SPELLCAST_START handler). Resets the per-craft spark to the left
|
||||
-- edge of the bar.
|
||||
local function StartTradeskillCraft(cb)
|
||||
cb.currentCraftStart = GetTime() * 1000
|
||||
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0)
|
||||
@@ -79,7 +86,7 @@ pfUI:RegisterModule("castbar", function ()
|
||||
|
||||
-- Stamp the bar with cast data and render text/icon/lag once. OnUpdate
|
||||
-- then animates the fill from this state without touching C_Spell.
|
||||
local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill)
|
||||
local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill, rank)
|
||||
cb.startTime = startMs
|
||||
cb.endTime = endMs
|
||||
cb.isChannel = isChannel
|
||||
@@ -92,10 +99,13 @@ pfUI:RegisterModule("castbar", function ()
|
||||
|
||||
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"]))
|
||||
|
||||
local rank = ""
|
||||
if spellID then
|
||||
-- Rank: prefer the value the UNIT_SPELLCAST_* event delivered (arg5, passed
|
||||
-- through by RefreshBar). Only the retarget re-poll has no event in hand, so
|
||||
-- it falls back to a lookup.
|
||||
if not rank and spellID then
|
||||
rank = C_Spell.GetSpellSubtext(spellID) or ""
|
||||
end
|
||||
rank = rank or ""
|
||||
local spellname = (cb.showname and name) and (name .. " ") or ""
|
||||
local rankstr = (cb.showrank and rank ~= "") and string.format("|cffaaffcc[%s]|r", rank) or ""
|
||||
cb.bar.left:SetText(spellname .. rankstr)
|
||||
@@ -122,11 +132,21 @@ pfUI:RegisterModule("castbar", function ()
|
||||
|
||||
cb.bar:SetMinMaxValues(0, duration)
|
||||
cb.lastMax = duration
|
||||
|
||||
-- Prime the fill on this frame. StampBar otherwise leaves the previous
|
||||
-- value in place (ClearBar, run on the prior cast's STOP, leaves it full),
|
||||
-- so the bar would flash full for the frame between here and the next
|
||||
-- OnUpdate tick. Reset the throttle too so the timer text updates promptly.
|
||||
local nowSec = GetTime()
|
||||
local cur = isChannel and (endMs / 1000 - nowSec) or (nowSec - startMs / 1000)
|
||||
if cur < 0 then cur = 0 elseif cur > duration then cur = duration end
|
||||
cb.bar:SetValue(cur)
|
||||
cb.tick = 0
|
||||
end
|
||||
|
||||
-- One-shot poll: read C_Spell for the bar's unit, stamp or clear. Called
|
||||
-- from event handlers (cast start, target/focus change), never per-frame.
|
||||
local function RefreshBar(cb)
|
||||
local function RefreshBar(cb, rank)
|
||||
local query = cb.unitstr ~= "" and cb.unitstr or cb.unitname
|
||||
if not query or (cb.unitstr ~= "" and not UnitExists(cb.unitstr)) then
|
||||
ClearBar(cb)
|
||||
@@ -149,7 +169,7 @@ pfUI:RegisterModule("castbar", function ()
|
||||
end
|
||||
end
|
||||
if name and startMs and endMs then
|
||||
StampBar(cb, name, tex, startMs, endMs, spellID, isChan, delayMs, isTradeskill)
|
||||
StampBar(cb, name, tex, startMs, endMs, spellID, isChan, delayMs, isTradeskill, rank)
|
||||
else
|
||||
ClearBar(cb)
|
||||
end
|
||||
@@ -294,35 +314,27 @@ pfUI:RegisterModule("castbar", function ()
|
||||
end
|
||||
end)
|
||||
|
||||
-- Cast lifecycle events. Player bars react to vanilla SPELLCAST_*; non-
|
||||
-- player bars also react to Nampower SPELL_*_OTHER (gated by the
|
||||
-- NP_EnableSpell{Start,Go}Events CVars, enabled by libdebuff) plus the
|
||||
-- retarget event. Player events also feed non-player bars for the
|
||||
-- target=self case.
|
||||
cb:RegisterEvent("SPELLCAST_START")
|
||||
cb:RegisterEvent("SPELLCAST_STOP")
|
||||
cb:RegisterEvent("SPELLCAST_FAILED")
|
||||
cb:RegisterEvent("SPELLCAST_INTERRUPTED")
|
||||
cb:RegisterEvent("SPELLCAST_CHANNEL_START")
|
||||
cb:RegisterEvent("SPELLCAST_CHANNEL_STOP")
|
||||
cb:RegisterEvent("SPELLCAST_CHANNEL_UPDATE")
|
||||
cb:RegisterEvent("SPELL_DELAYED_SELF")
|
||||
-- Chained same-spell recasts never run the client cast path (the 1.12
|
||||
-- engine short-circuits at spellID == current-cast), so vanilla
|
||||
-- SPELLCAST_START never fires for them. nampower's SPELL_START_SELF
|
||||
-- (server-driven) is the only signal that shows them.
|
||||
cb:RegisterEvent("SPELL_START_SELF")
|
||||
if unitstr == "player" then
|
||||
cb:RegisterEvent("SPELL_GO_SELF")
|
||||
end
|
||||
if unitstr ~= "player" and unitstr ~= "" then
|
||||
cb:RegisterEvent("SPELL_START_OTHER")
|
||||
cb:RegisterEvent("SPELL_FAILED_OTHER")
|
||||
if unitstr == "target" then
|
||||
cb:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
elseif unitstr == "focus" then
|
||||
cb:RegisterEvent("PLAYER_FOCUS_CHANGED")
|
||||
end
|
||||
-- Cast lifecycle, entirely on ClassicAPI's UNIT_SPELLCAST_* events. They
|
||||
-- fire per unit token: arg1=="player" for the player's own casts, and the
|
||||
-- remote token(s) ("target", "focus", ...) for other units -- so one set of
|
||||
-- events drives every bar with no Nampower dependency. The handler routes an
|
||||
-- event to this bar when arg1 matches its unit, or -- since the player's own
|
||||
-- casts only ever fire arg1=="player" -- when the bar's unit resolves to the
|
||||
-- player (target=self). PLAYER_TARGET/FOCUS_CHANGED re-polls so a unit
|
||||
-- already mid-cast when it becomes the target/focus still shows.
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_START")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_STOP")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_FAILED")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_DELAYED")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
|
||||
cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
|
||||
if unitstr == "target" then
|
||||
cb:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
elseif unitstr == "focus" then
|
||||
cb:RegisterEvent("PLAYER_FOCUS_CHANGED")
|
||||
end
|
||||
|
||||
cb:SetScript("OnEvent", function()
|
||||
@@ -333,83 +345,33 @@ pfUI:RegisterModule("castbar", function ()
|
||||
return
|
||||
end
|
||||
|
||||
if event == "SPELL_START_OTHER" then
|
||||
-- arg3=casterGuid. Defer one frame so ClassicAPI's UnitChannelInfo
|
||||
-- can see the engine's +0x228 broadcast for remote-unit channels
|
||||
-- (the cohook+packet handler runs in the same frame; the broadcast
|
||||
-- propagates after).
|
||||
if arg3 == UnitGUID(unit) then
|
||||
local target = this
|
||||
RunNextFrame(function() RefreshBar(target) end)
|
||||
end
|
||||
-- UNIT_SPELLCAST_* fire per unit token (arg1). Handle an event when it's
|
||||
-- for this bar's unit, or -- since the player's own casts only ever fire
|
||||
-- arg1=="player" -- when this bar's unit currently resolves to the player
|
||||
-- (target=self / focus=self).
|
||||
-- Args: arg1=unit, arg2=castGUID, arg3=spellID, arg4=name, arg5=rank.
|
||||
if arg1 ~= unit and not (arg1 == "player" and UnitIsUnit(unit, "player")) then
|
||||
return
|
||||
end
|
||||
|
||||
if event == "SPELL_FAILED_OTHER" then
|
||||
if arg1 == UnitGUID(unit) then ClearBar(this) end
|
||||
return
|
||||
end
|
||||
|
||||
-- Vanilla SPELLCAST_* + SPELL_DELAYED_SELF fire only for the local
|
||||
-- player. Non-player bars handle them only when their unit currently
|
||||
-- resolves to the player (target=self / focus=self).
|
||||
if not UnitIsUnit(unit, 'player') then return end
|
||||
|
||||
if event == "SPELLCAST_START" or event == "SPELLCAST_CHANNEL_START" then
|
||||
if event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" then
|
||||
-- START also fires per craft in a same-spell chain, so an active merge
|
||||
-- just resyncs the current craft's spark/label instead of restamping.
|
||||
if this.tradeskillTotal then
|
||||
-- Mid-chain craft N+1 of N. Keep the merged bar; resync the spark
|
||||
-- to the new craft's start and refresh the "(N)" count label.
|
||||
StartTradeskillCraft(this)
|
||||
else
|
||||
RefreshBar(this)
|
||||
RefreshBar(this, arg5)
|
||||
if this.isTradeskill and (this.pendingTradeskillCount or 0) > 1
|
||||
and C.castbar.player.mergetradeskill == "1" then
|
||||
EnterTradeskillMerge(this, this.startTime, this.endTime, this.pendingTradeskillCount)
|
||||
end
|
||||
this.pendingTradeskillCount = nil
|
||||
end
|
||||
elseif event == "SPELL_START_SELF" then
|
||||
-- Catches chained same-spell recasts (no SPELLCAST_START fires) —
|
||||
-- including tradeskill chaining where craft 2..N reuse one spell.
|
||||
-- Defer one frame so ClassicAPI's SMSG_SPELL_START co-hook has
|
||||
-- stamped g_cast before we poll, regardless of co-hook order.
|
||||
if this.tradeskillTotal then
|
||||
StartTradeskillCraft(this)
|
||||
else
|
||||
local target = this
|
||||
RunNextFrame(function()
|
||||
-- Re-check: SPELL_START_SELF (nampower co-hook) fires before vanilla
|
||||
-- SPELLCAST_START on the same packet, so SPELLCAST_START may have
|
||||
-- entered merge in this same frame. Don't restamp over it.
|
||||
if target.tradeskillTotal then
|
||||
StartTradeskillCraft(target)
|
||||
return
|
||||
end
|
||||
RefreshBar(target)
|
||||
if target.unitstr == "player" and target.isTradeskill
|
||||
and (target.pendingTradeskillCount or 0) > 1
|
||||
and C.castbar.player.mergetradeskill == "1" then
|
||||
EnterTradeskillMerge(target, target.startTime, target.endTime, target.pendingTradeskillCount)
|
||||
end
|
||||
target.pendingTradeskillCount = nil
|
||||
end)
|
||||
end
|
||||
elseif event == "SPELLCAST_CHANNEL_STOP" then
|
||||
-- A channel's stop can arrive after a following cast already claimed
|
||||
-- the bar (channel->cast transition); only clear if a channel is
|
||||
-- actually being shown, so it doesn't wipe an active cast bar.
|
||||
if this.isChannel then ClearBar(this) end
|
||||
elseif event == "SPELLCAST_STOP" then
|
||||
-- During a tradeskill chain, SPELL_GO_SELF already counted this craft
|
||||
-- and either cleared the bar (chain done) or kept it running. Only a
|
||||
-- non-merge cast clears here.
|
||||
if not this.tradeskillTotal then ClearBar(this) end
|
||||
elseif event == "SPELLCAST_FAILED" or event == "SPELLCAST_INTERRUPTED" then
|
||||
ClearBar(this)
|
||||
elseif event == "SPELL_GO_SELF" then
|
||||
-- arg2 = spellId. In a tradeskill merge, count each successful craft
|
||||
-- and clear when the chain is done.
|
||||
if this.tradeskillTotal and arg2 == this.tradeskillSpellID then
|
||||
elseif event == "UNIT_SPELLCAST_SUCCEEDED" then
|
||||
-- Per-craft completion during a tradeskill merge (arg3 = spellID):
|
||||
-- count and clear when the chain is done. A no-op for normal casts,
|
||||
-- which are cleared by UNIT_SPELLCAST_STOP.
|
||||
if this.tradeskillTotal and arg3 == this.tradeskillSpellID then
|
||||
this.tradeskillCompleted = (this.tradeskillCompleted or 0) + 1
|
||||
if this.tradeskillCompleted >= this.tradeskillTotal then
|
||||
ClearBar(this)
|
||||
@@ -417,33 +379,46 @@ pfUI:RegisterModule("castbar", function ()
|
||||
UpdateTradeskillLabel(this)
|
||||
end
|
||||
end
|
||||
elseif event == "SPELL_DELAYED_SELF" then
|
||||
-- Cast pushback. nampower's event carries the delay (arg2); apply it
|
||||
-- locally rather than re-polling, so the bar doesn't depend on
|
||||
-- ClassicAPI's SMSG_SPELL_DELAYED co-hook having bumped g_cast before
|
||||
-- this fires (co-hook order vs nampower is not guaranteed).
|
||||
if not this.endTime or not arg2 then return end
|
||||
local delayMs = tonumber(arg2) or 0
|
||||
if delayMs > 0 then
|
||||
this.delay = (this.delay or 0) + delayMs / 1000
|
||||
this.endTime = this.endTime + delayMs
|
||||
local newDuration = (this.endTime - this.startTime) / 1000
|
||||
this.bar:SetMinMaxValues(0, newDuration)
|
||||
this.lastMax = newDuration
|
||||
end
|
||||
elseif event == "SPELLCAST_CHANNEL_UPDATE" then
|
||||
-- Channel pushback. ClassicAPI doesn't track channel delay in
|
||||
-- g_channel, so we adjust endTime + delay locally and resize the
|
||||
-- bar so OnUpdate animates against the new total.
|
||||
if not this.endTime or not arg1 then return end
|
||||
local newEndMs = GetTime() * 1000 + arg1
|
||||
local diff = this.endTime - newEndMs
|
||||
if diff > 50 then
|
||||
this.delay = (this.delay or 0) + diff / 1000
|
||||
this.endTime = newEndMs
|
||||
local newDuration = (this.endTime - this.startTime) / 1000
|
||||
this.bar:SetMinMaxValues(0, newDuration)
|
||||
this.lastMax = newDuration
|
||||
elseif event == "UNIT_SPELLCAST_CHANNEL_STOP" then
|
||||
-- A channel's stop can arrive after a following cast already claimed
|
||||
-- the bar (channel->cast transition); only clear if a channel is
|
||||
-- actually being shown, so it doesn't wipe an active cast bar.
|
||||
if this.isChannel then ClearBar(this) end
|
||||
elseif event == "UNIT_SPELLCAST_STOP" then
|
||||
-- STOP fires between crafts in a merge too (each craft is a new cast);
|
||||
-- UNIT_SPELLCAST_SUCCEEDED owns the count, so only a non-merge cast
|
||||
-- clears here.
|
||||
if not this.tradeskillTotal then ClearBar(this) end
|
||||
elseif event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" then
|
||||
ClearBar(this, true)
|
||||
elseif event == "UNIT_SPELLCAST_DELAYED" or event == "UNIT_SPELLCAST_CHANNEL_UPDATE" then
|
||||
-- Pushback: a cast delayed later or a channel shortened. Following
|
||||
-- Quartz, re-poll just the times and accumulate the shift into a running
|
||||
-- this.delay for the +/- indicator, rather than a full restamp (which
|
||||
-- would reset that total and re-render icon/text). ClassicAPI moves
|
||||
-- endMs (SpellDelayed_h bumps g_cast.endMs; the MSG_CHANNEL_UPDATE
|
||||
-- co-hook rewrites g_channel.endMs) while startMs stays put, so we diff
|
||||
-- endMs. delay is a positive magnitude; OnUpdate signs it "+" for casts
|
||||
-- and "-" for channels. Skipped during a tradeskill merge.
|
||||
if this.endTime and not this.tradeskillTotal then
|
||||
local query = this.unitstr ~= "" and this.unitstr or this.unitname
|
||||
local startMs, endMs
|
||||
if this.isChannel then
|
||||
local _, _, _, s, e = C_Spell.UnitChannelInfo(query)
|
||||
startMs, endMs = s, e
|
||||
else
|
||||
local _, _, _, s, e = C_Spell.UnitCastingInfo(query)
|
||||
startMs, endMs = s, e
|
||||
end
|
||||
if startMs and endMs then
|
||||
local shift = this.isChannel and (this.endTime - endMs) or (endMs - this.endTime)
|
||||
this.delay = (this.delay or 0) + shift / 1000
|
||||
this.startTime = startMs
|
||||
this.endTime = endMs
|
||||
local newDuration = (endMs - startMs) / 1000
|
||||
this.bar:SetMinMaxValues(0, newDuration)
|
||||
this.lastMax = newDuration
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -489,10 +464,10 @@ pfUI:RegisterModule("castbar", function ()
|
||||
UpdateMovable(pfUI.castbar.player)
|
||||
|
||||
-- Tradeskill merge: hook DoTradeSkill so the player castbar knows the
|
||||
-- requested count before the first SPELLCAST_START fires. Always-on hook
|
||||
-- (the config knob is read at event time so toggling takes effect on the
|
||||
-- next craft without a /reload). DoTradeSkill is synchronous; the server
|
||||
-- roundtrip to SPELLCAST_START gives us plenty of time after this hook.
|
||||
-- requested count before the first UNIT_SPELLCAST_START fires. Always-on
|
||||
-- hook (the config knob is read at event time so toggling takes effect on
|
||||
-- the next craft without a /reload). DoTradeSkill is synchronous; the
|
||||
-- server roundtrip to UNIT_SPELLCAST_START gives us plenty of time.
|
||||
hooksecurefunc("DoTradeSkill", function(index, num)
|
||||
if pfUI.castbar.player then
|
||||
pfUI.castbar.player.pendingTradeskillCount = tonumber(num) or 1
|
||||
|
||||
+7
-7
@@ -82,10 +82,10 @@ pfUI:RegisterModule("chat", function ()
|
||||
end
|
||||
|
||||
if C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
pfUI.chat.left.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
pfUI.chat.left.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
|
||||
@@ -257,10 +257,10 @@ pfUI:RegisterModule("chat", function ()
|
||||
end
|
||||
|
||||
if C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
pfUI.chat.right.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
pfUI.chat.right.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
|
||||
@@ -675,7 +675,7 @@ pfUI:RegisterModule("chat", function ()
|
||||
end
|
||||
|
||||
-- read and parse whisper color settings
|
||||
local cr, cg, cb, ca = strsplit(",", C.chat.global.whisper)
|
||||
local cr, cg, cb, ca = GetStringColor(C.chat.global.whisper)
|
||||
cr, cg, cb = tonumber(cr), tonumber(cg), tonumber(cb)
|
||||
local wcol = rgbhex(cr, cg, cb)
|
||||
|
||||
@@ -706,10 +706,10 @@ pfUI:RegisterModule("chat", function ()
|
||||
_G.CHAT_WHISPER_INFORM_GET = '[W]' .. default
|
||||
end
|
||||
|
||||
local r,g,b,a = strsplit(",", C.chat.text.timecolor)
|
||||
local r,g,b,a = GetStringColor(C.chat.text.timecolor)
|
||||
local timecolorhex = rgbhex(r,g,b,a)
|
||||
|
||||
local r,g,b = strsplit(",", C.chat.text.unknowncolor)
|
||||
local r,g,b = GetStringColor(C.chat.text.unknowncolor)
|
||||
local unknowncolorhex = rgbhex(r,g,b)
|
||||
|
||||
-- Suppress FriendsFrame's WHO_LIST_UPDATE handling for our DLL-issued
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
pfUI:RegisterModule("cooldown", function ()
|
||||
-- cache values
|
||||
local lowcolor = {strsplit(",", C.appearance.cd.lowcolor)}
|
||||
local normalcolor = {strsplit(",", C.appearance.cd.normalcolor)}
|
||||
local minutecolor = {strsplit(",", C.appearance.cd.minutecolor)}
|
||||
local hourcolor = {strsplit(",", C.appearance.cd.hourcolor)}
|
||||
local daycolor = {strsplit(",", C.appearance.cd.daycolor)}
|
||||
-- local lowcolor = {strsplit(",", C.appearance.cd.lowcolor)}
|
||||
-- local normalcolor = {strsplit(",", C.appearance.cd.normalcolor)}
|
||||
-- local minutecolor = {strsplit(",", C.appearance.cd.minutecolor)}
|
||||
-- local hourcolor = {strsplit(",", C.appearance.cd.hourcolor)}
|
||||
-- local daycolor = {strsplit(",", C.appearance.cd.daycolor)}
|
||||
|
||||
local parent, parent_name
|
||||
local function pfCooldownOnUpdate()
|
||||
|
||||
+70
-268
@@ -1,11 +1,10 @@
|
||||
-- Equipment Manager module
|
||||
-- Backport of the 4.3.4 GearManagerDialog UI on top of ClassicAPI's
|
||||
-- C_EquipmentSet.* API. Adds a 6th tab to CharacterFrame.
|
||||
-- Backport of the 4.3.4 GearManagerDialog UI on top of ClassicAPI's C_EquipmentSet.* API.
|
||||
|
||||
pfUI:RegisterModule("equipmentmanager", function()
|
||||
if not C_EquipmentSet or not C_EquipmentSet.CanUseEquipmentSets() then return end
|
||||
|
||||
pfUI.equipmentmanager = pfUI.equipmentmanager or {}
|
||||
pfUI.equipmentmanager = {}
|
||||
|
||||
local SET_ROW_HEIGHT = 36
|
||||
|
||||
@@ -55,7 +54,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
local function EquipSet(setID)
|
||||
if not setID then return end
|
||||
if C_EquipmentSet.EquipmentSetContainsLockedItems(setID) then
|
||||
UIErrorsFrame:AddMessage(ERR_CLIENT_LOCKED_OUT or "Locked items in set", 1, .1, .1, 1)
|
||||
UIErrorsFrame:AddMessage(ERR_CLIENT_LOCKED_OUT, 1, .1, .1, 1)
|
||||
return
|
||||
end
|
||||
ClearCursor()
|
||||
@@ -68,8 +67,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
-- ============================================================
|
||||
|
||||
local frame = CreateFrame("Frame", "pfEquipmentManagerFrame", CharacterFrame)
|
||||
frame:SetWidth(220)
|
||||
frame:SetHeight(350)
|
||||
frame:SetSize(220, 350)
|
||||
frame:SetFrameStrata("HIGH")
|
||||
frame:SetScript("OnShow", function()
|
||||
this:ClearAllPoints()
|
||||
@@ -86,7 +84,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
|
||||
frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
frame.title:SetPoint("TOP", frame, "TOP", 0, -10)
|
||||
frame.title:SetText(T["Equipment Manager"] or "Equipment Manager")
|
||||
frame.title:SetText(EQUIPMENT_MANAGER)
|
||||
|
||||
local closeBtn = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
|
||||
closeBtn:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -4, -4)
|
||||
@@ -98,15 +96,14 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
-- ============================================================
|
||||
|
||||
local toggleBtn = CreateFrame("Button", "pfEqMgrToggleButton", PaperDollFrame)
|
||||
toggleBtn:SetWidth(28)
|
||||
toggleBtn:SetHeight(28)
|
||||
toggleBtn:SetSize(28, 28)
|
||||
toggleBtn:SetPoint("BOTTOM", CharacterHandsSlot, "TOP", 0, 4)
|
||||
toggleBtn:SetNormalTexture(pfUI.path.."\\img\\UI-GearManager-Button")
|
||||
toggleBtn:SetPushedTexture(pfUI.path.."\\img\\UI-GearManager-Button-Pushed")
|
||||
toggleBtn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD")
|
||||
toggleBtn:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText(T["Equipment Manager"] or "Equipment Manager")
|
||||
GameTooltip:SetText(PAPERDOLL_EQUIPMENTMANAGER)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
toggleBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
@@ -126,13 +123,13 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
-- ============================================================
|
||||
|
||||
local btnEquip = CreateFrame("Button", "pfEqMgrEquip", frame, "UIPanelButtonTemplate")
|
||||
btnEquip:SetWidth(86); btnEquip:SetHeight(22); btnEquip:SetText(T["Equip"] or "Equip")
|
||||
btnEquip:SetSize(86, 22); btnEquip:SetText(EQUIPSET_EQUIP)
|
||||
btnEquip:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -30)
|
||||
SkinButton(btnEquip)
|
||||
btnEquip:SetScript("OnClick", function() EquipSet(selectedSetID) end)
|
||||
|
||||
local btnSave = CreateFrame("Button", "pfEqMgrSave", frame, "UIPanelButtonTemplate")
|
||||
btnSave:SetWidth(86); btnSave:SetHeight(22); btnSave:SetText(T["Save"] or "Save")
|
||||
btnSave:SetSize(86, 22); btnSave:SetText(SAVE)
|
||||
btnSave:SetPoint("LEFT", btnEquip, "RIGHT", 6, 0)
|
||||
SkinButton(btnSave)
|
||||
btnSave:SetScript("OnClick", function()
|
||||
@@ -141,7 +138,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
if not name then return end
|
||||
local targetID = selectedSetID
|
||||
StaticPopupDialogs["PFUI_EQMGR_SAVE_CONFIRM"] = {
|
||||
text = string.format(T["Would you like to save the equipment set '%s'?"] or "Would you like to save the equipment set '%s'?", name),
|
||||
text = string.format(CONFIRM_SAVE_EQUIPMENT_SET, name),
|
||||
button1 = YES, button2 = NO,
|
||||
OnAccept = function()
|
||||
C_EquipmentSet.ClearIgnoredSlotsForSave()
|
||||
@@ -166,7 +163,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
|
||||
local rowMenu = CreateFrame("Frame", "pfEqMgrRowMenu", UIParent)
|
||||
rowMenu:SetFrameStrata("DIALOG")
|
||||
rowMenu:SetWidth(140); rowMenu:SetHeight(50)
|
||||
rowMenu:SetSize(140, 50);
|
||||
rowMenu:Hide()
|
||||
CreateBackdrop(rowMenu, nil, nil, .95)
|
||||
CreateBackdropShadow(rowMenu)
|
||||
@@ -185,9 +182,9 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
tinsert(UISpecialFrames, "pfEqMgrRowMenu") -- Escape closes it
|
||||
|
||||
rowMenu.changeBtn = CreateFrame("Button", nil, rowMenu, "UIPanelButtonTemplate")
|
||||
rowMenu.changeBtn:SetWidth(130); rowMenu.changeBtn:SetHeight(20)
|
||||
rowMenu.changeBtn:SetSize(130, 20);
|
||||
rowMenu.changeBtn:SetPoint("TOPLEFT", rowMenu, "TOPLEFT", 5, -3)
|
||||
rowMenu.changeBtn:SetText(T["Change Name/Icon"] or "Change Name/Icon")
|
||||
rowMenu.changeBtn:SetText(EQUIPMENT_SET_EDIT)
|
||||
SkinButton(rowMenu.changeBtn)
|
||||
rowMenu.changeBtn:SetScript("OnClick", function()
|
||||
rowMenu:Hide()
|
||||
@@ -198,9 +195,9 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
end)
|
||||
|
||||
rowMenu.deleteBtn = CreateFrame("Button", nil, rowMenu, "UIPanelButtonTemplate")
|
||||
rowMenu.deleteBtn:SetWidth(130); rowMenu.deleteBtn:SetHeight(20)
|
||||
rowMenu.deleteBtn:SetSize(130, 20);
|
||||
rowMenu.deleteBtn:SetPoint("TOP", rowMenu.changeBtn, "BOTTOM", 0, -2)
|
||||
rowMenu.deleteBtn:SetText(T["Delete"] or "Delete")
|
||||
rowMenu.deleteBtn:SetText(DELETE)
|
||||
SkinButton(rowMenu.deleteBtn)
|
||||
rowMenu.deleteBtn:SetScript("OnClick", function()
|
||||
rowMenu:Hide()
|
||||
@@ -208,7 +205,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
local targetID = rowMenu.targetSetID
|
||||
local name = C_EquipmentSet.GetEquipmentSetInfo(targetID)
|
||||
StaticPopupDialogs["PFUI_EQMGR_DELETE"] = {
|
||||
text = string.format(T["Delete equipment set '%s'?"] or "Delete equipment set '%s'?", name or "?"),
|
||||
text = string.format(CONFIRM_DELETE_EQUIPMENT_SET, name or "?"),
|
||||
button1 = YES, button2 = NO,
|
||||
OnAccept = function()
|
||||
pendingIgnoredToggles[targetID] = nil
|
||||
@@ -229,9 +226,8 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
local LIST_ROW_STRIDE = SET_ROW_HEIGHT + 2
|
||||
local listFrame = CreateFrame("Frame", nil, frame)
|
||||
listFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -58)
|
||||
listFrame:SetWidth(180)
|
||||
-- Height fits N rows + (N-1) inter-row gaps + 3px top/bottom padding.
|
||||
listFrame:SetHeight(LIST_VISIBLE_ROWS * LIST_ROW_STRIDE + 4)
|
||||
listFrame:SetSize(180, LIST_VISIBLE_ROWS * LIST_ROW_STRIDE + 4)
|
||||
CreateBackdrop(listFrame, nil, nil, .75)
|
||||
|
||||
-- Mouse-wheel scroll: list of [sets..., newSetRow] is virtualized
|
||||
@@ -247,15 +243,13 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
local setRows = {}
|
||||
local function CreateSetRow()
|
||||
local row = CreateFrame("Button", nil, listFrame)
|
||||
row:SetWidth(170)
|
||||
row:SetHeight(SET_ROW_HEIGHT)
|
||||
row:SetSize(170, SET_ROW_HEIGHT)
|
||||
-- Position is set per-Refresh based on the row's visible slot;
|
||||
-- start anchored to avoid uninitialized geometry before first Refresh.
|
||||
row:SetPoint("TOPLEFT", listFrame, "TOPLEFT", 5, -3)
|
||||
|
||||
row.icon = row:CreateTexture(nil, "ARTWORK")
|
||||
row.icon:SetWidth(30)
|
||||
row.icon:SetHeight(30)
|
||||
row.icon:SetSize(30, 30)
|
||||
row.icon:SetPoint("LEFT", row, "LEFT", 2, 0)
|
||||
row.icon:SetTexCoord(.08, .92, .08, .92)
|
||||
|
||||
@@ -270,7 +264,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
row.highlight:Hide()
|
||||
|
||||
row.gear = CreateFrame("Button", nil, row)
|
||||
row.gear:SetWidth(16); row.gear:SetHeight(16)
|
||||
row.gear:SetSize(16, 16);
|
||||
row.gear:SetPoint("RIGHT", row, "RIGHT", -4, 0)
|
||||
row.gear.tex = row.gear:CreateTexture(nil, "ARTWORK")
|
||||
row.gear.tex:SetAllPoints(row.gear)
|
||||
@@ -306,27 +300,27 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
pfUI.equipmentmanager.Refresh()
|
||||
end)
|
||||
|
||||
-- Hover handling: OnLeave fires when the cursor moves onto a child
|
||||
-- (the gear button becomes the topmost mouse target). Use an
|
||||
-- OnUpdate poll so the gear stays shown while the cursor is over
|
||||
-- the row OR the gear itself.
|
||||
-- Show the gear and tooltip while the cursor is over the row or its
|
||||
-- gear child. The gear sits inside the row's rectangle, so a single
|
||||
-- MouseIsOver(row) check covers both. OnLeave on the row and the gear
|
||||
-- catches every exit path, so no OnUpdate poll is needed.
|
||||
local function HideRowHover()
|
||||
if MouseIsOver(row) then return end
|
||||
GameTooltip:Hide()
|
||||
row.gear:Hide()
|
||||
end
|
||||
row:SetScript("OnEnter", function()
|
||||
if not this.setID then return end
|
||||
local name = C_EquipmentSet.GetEquipmentSetInfo(this.setID)
|
||||
if not row.setID then return end
|
||||
local name = C_EquipmentSet.GetEquipmentSetInfo(row.setID)
|
||||
if name then
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetOwner(row, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetEquipmentSet(name)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
row.gear:Show()
|
||||
this:SetScript("OnUpdate", function()
|
||||
if not MouseIsOver(this) and not MouseIsOver(row.gear) then
|
||||
this:SetScript("OnUpdate", nil)
|
||||
GameTooltip:Hide()
|
||||
row.gear:Hide()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
row:SetScript("OnLeave", HideRowHover)
|
||||
row.gear:SetScript("OnLeave", HideRowHover)
|
||||
|
||||
return row
|
||||
end
|
||||
@@ -338,10 +332,10 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
-- specific set index (row[i] always shows ids[i]).
|
||||
|
||||
local newSetRow = CreateFrame("Button", nil, listFrame)
|
||||
newSetRow:SetWidth(170); newSetRow:SetHeight(SET_ROW_HEIGHT)
|
||||
newSetRow:SetSize(170, SET_ROW_HEIGHT);
|
||||
|
||||
newSetRow.icon = newSetRow:CreateTexture(nil, "ARTWORK")
|
||||
newSetRow.icon:SetWidth(24); newSetRow.icon:SetHeight(24)
|
||||
newSetRow.icon:SetSize(24, 24);
|
||||
newSetRow.icon:SetPoint("LEFT", newSetRow, "LEFT", 5, 0)
|
||||
newSetRow.icon:SetTexture(pfUI.path.."\\img\\Character-Plus")
|
||||
|
||||
@@ -349,7 +343,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
newSetRow.text:SetPoint("LEFT", newSetRow.icon, "RIGHT", 8, 0)
|
||||
newSetRow.text:SetPoint("RIGHT", newSetRow, "RIGHT", -4, 0)
|
||||
newSetRow.text:SetJustifyH("LEFT")
|
||||
newSetRow.text:SetText(T["New Set"] or "New Set")
|
||||
newSetRow.text:SetText(PAPERDOLL_NEWEQUIPMENTSET)
|
||||
newSetRow.text:SetTextColor(0.2, 1, 0.2)
|
||||
|
||||
newSetRow.highlight = newSetRow:CreateTexture(nil, "BACKGROUND")
|
||||
@@ -362,8 +356,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
|
||||
local function MakeButton(name, label, parent, anchor, ax, ay, width)
|
||||
local b = CreateFrame("Button", name, parent, "UIPanelButtonTemplate")
|
||||
b:SetWidth(width or 70)
|
||||
b:SetHeight(22)
|
||||
b:SetSize(width or 70, 22)
|
||||
b:SetText(label)
|
||||
b:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", ax, ay)
|
||||
SkinButton(b)
|
||||
@@ -376,8 +369,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
|
||||
local namePopup = CreateFrame("Frame", "pfEqMgrNamePopup", UIParent)
|
||||
namePopup:SetFrameStrata("DIALOG")
|
||||
namePopup:SetWidth(472)
|
||||
namePopup:SetHeight(498)
|
||||
namePopup:SetSize(472, 484)
|
||||
namePopup:SetPoint("CENTER", UIParent, "CENTER")
|
||||
namePopup:Hide()
|
||||
CreateBackdrop(namePopup, nil, nil, .9)
|
||||
@@ -388,200 +380,28 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
namePopup:SetScript("OnDragStart", function() this:StartMoving() end)
|
||||
namePopup:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
||||
|
||||
namePopup.title = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
namePopup.title:SetPoint("TOP", namePopup, "TOP", 0, -10)
|
||||
namePopup.title:SetText(T["Save Set"] or "Save Set")
|
||||
-- Equipment seed leads the grid with gear-relevant item icons. The widget
|
||||
-- owns the name field, preview, grid, filter, and search; this module keeps
|
||||
-- the OK/Cancel buttons and the create/save/rename flow below.
|
||||
local iconPicker = CreateIconPicker("pfEqMgrIcon", namePopup,
|
||||
IconDataProviderExtraType.Equipment, GEARSETS_POPUP_TEXT)
|
||||
|
||||
namePopup.nameLabel = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
namePopup.nameLabel:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -34)
|
||||
namePopup.nameLabel:SetText(T["Enter Set Name (Max 16 Characters):"] or "Enter Set Name (Max 16 Characters):")
|
||||
local btnPopupOK = MakeButton("pfEqMgrPopupOK", OKAY, namePopup, namePopup, 14, -340, 80)
|
||||
|
||||
namePopup.editbox = CreateFrame("EditBox", "pfEqMgrNameEdit", namePopup, "InputBoxTemplate")
|
||||
namePopup.editbox:SetWidth(280)
|
||||
namePopup.editbox:SetHeight(20)
|
||||
namePopup.editbox:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -52)
|
||||
namePopup.editbox:SetAutoFocus(false)
|
||||
namePopup.editbox:SetMaxLetters(16)
|
||||
CreateBackdrop(namePopup.editbox)
|
||||
|
||||
namePopup.selectedLabel = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
namePopup.selectedLabel:SetPoint("TOPRIGHT", namePopup, "TOPRIGHT", -14, -28)
|
||||
namePopup.selectedLabel:SetText(T["Currently Selected"] or "Currently Selected")
|
||||
namePopup.selectedLabel:SetTextColor(1, 0.82, 0)
|
||||
|
||||
namePopup.selectedPreview = CreateFrame("Frame", nil, namePopup)
|
||||
namePopup.selectedPreview:SetWidth(42)
|
||||
namePopup.selectedPreview:SetHeight(42)
|
||||
namePopup.selectedPreview:SetPoint("TOPRIGHT", namePopup, "TOPRIGHT", -14, -44)
|
||||
CreateBackdrop(namePopup.selectedPreview)
|
||||
namePopup.selectedPreview.tex = namePopup.selectedPreview:CreateTexture(nil, "ARTWORK")
|
||||
namePopup.selectedPreview.tex:SetAllPoints(namePopup.selectedPreview)
|
||||
namePopup.selectedPreview.tex:SetTexCoord(.08, .92, .08, .92)
|
||||
|
||||
namePopup.iconLabel = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
namePopup.iconLabel:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -100)
|
||||
namePopup.iconLabel:SetText(T["Choose an Icon:"] or "Choose an Icon:")
|
||||
|
||||
-- Icon picker: 10×8 grid of buttons + scroll
|
||||
local ICON_GRID_COLS = 10
|
||||
local ICON_GRID_ROWS = 8
|
||||
local ICON_BTN_SIZE = 36
|
||||
local ICON_BTN_PAD = 6
|
||||
|
||||
local iconScroll = CreateFrame("ScrollFrame", "pfEqMgrIconScroll", namePopup, "FauxScrollFrameTemplate")
|
||||
iconScroll:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -130)
|
||||
iconScroll:SetWidth(ICON_GRID_COLS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
|
||||
iconScroll:SetHeight(ICON_GRID_ROWS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
|
||||
|
||||
-- IconDataProviderMixin owns the icon DB, dedup, and lazy load.
|
||||
-- Init on first picker open; release on hide so the cache GCs.
|
||||
local provider = nil
|
||||
-- Selection is tracked by PATH (not index) so it survives filter
|
||||
-- changes: a spell icon you picked still saves correctly even after
|
||||
-- you switch the filter to "Items" and it's no longer in the visible list.
|
||||
local QUESTION_MARK = "INTERFACE\\ICONS\\INV_MISC_QUESTIONMARK"
|
||||
local selectedIconPath = QUESTION_MARK
|
||||
|
||||
local function EnsureProvider()
|
||||
if not provider then
|
||||
provider = CreateAndInitFromMixin(IconDataProviderMixin,
|
||||
IconDataProviderExtraType.Equipment)
|
||||
end
|
||||
end
|
||||
|
||||
-- Anchor scrollbar to iconScroll's right edge so its position tracks
|
||||
-- the icon grid rather than the popup. -16/+16 vertical insets are the
|
||||
-- standard up/down arrow spacing for UIPanelScrollBarTemplate.
|
||||
local scrollbar = _G["pfEqMgrIconScrollScrollBar"]
|
||||
if scrollbar then
|
||||
scrollbar:ClearAllPoints()
|
||||
scrollbar:SetPoint("TOPLEFT", iconScroll, "TOPRIGHT", 8, -16)
|
||||
scrollbar:SetPoint("BOTTOMLEFT", iconScroll, "BOTTOMRIGHT", 8, 16)
|
||||
SkinScrollbar(scrollbar)
|
||||
end
|
||||
|
||||
-- Filter dropdown: "All Icons" / "Spells" / "Items" (top right of icon area).
|
||||
local filterDropdown = CreateFrame("Frame", "pfEqMgrIconFilter", namePopup, "UIDropDownMenuTemplate")
|
||||
filterDropdown:SetPoint("TOPRIGHT", namePopup, "TOPRIGHT", 0, -94)
|
||||
local currentFilter = "all"
|
||||
local function ApplyFilter(value)
|
||||
currentFilter = value
|
||||
UIDropDownMenu_SetSelectedValue(filterDropdown, value)
|
||||
if provider then
|
||||
if value == "spells" then provider:SetIconTypes({ IconDataProviderIconType.Spell })
|
||||
elseif value == "items" then provider:SetIconTypes({ IconDataProviderIconType.Item })
|
||||
else provider:SetIconTypes(nil) end
|
||||
-- Don't reset selection — selectedIconPath persists. If the
|
||||
-- selected icon isn't in the new filter, no grid entry will be
|
||||
-- highlighted but Save will still write the chosen icon.
|
||||
pfUI.equipmentmanager.RefreshIconGrid()
|
||||
end
|
||||
end
|
||||
UIDropDownMenu_Initialize(filterDropdown, function()
|
||||
local info
|
||||
info = {}; info.text = T["All Icons"] or "All Icons"; info.value = "all"
|
||||
info.func = function() ApplyFilter("all") end
|
||||
info.checked = currentFilter == "all"
|
||||
UIDropDownMenu_AddButton(info)
|
||||
info = {}; info.text = T["Spells"] or "Spells"; info.value = "spells"
|
||||
info.func = function() ApplyFilter("spells") end
|
||||
info.checked = currentFilter == "spells"
|
||||
UIDropDownMenu_AddButton(info)
|
||||
info = {}; info.text = T["Items"] or "Items"; info.value = "items"
|
||||
info.func = function() ApplyFilter("items") end
|
||||
info.checked = currentFilter == "items"
|
||||
UIDropDownMenu_AddButton(info)
|
||||
end)
|
||||
UIDropDownMenu_SetWidth(120, filterDropdown)
|
||||
UIDropDownMenu_SetSelectedValue(filterDropdown, "all")
|
||||
SkinDropDown(filterDropdown)
|
||||
|
||||
local iconButtons = {}
|
||||
for r = 1, ICON_GRID_ROWS do
|
||||
for c = 1, ICON_GRID_COLS do
|
||||
local i = (r - 1) * ICON_GRID_COLS + c
|
||||
local btn = CreateFrame("Button", nil, namePopup)
|
||||
btn:SetWidth(ICON_BTN_SIZE)
|
||||
btn:SetHeight(ICON_BTN_SIZE)
|
||||
btn:SetPoint("TOPLEFT", iconScroll, "TOPLEFT", (c-1) * (ICON_BTN_SIZE + ICON_BTN_PAD), -(r-1) * (ICON_BTN_SIZE + ICON_BTN_PAD))
|
||||
CreateBackdrop(btn)
|
||||
btn.texture = btn:CreateTexture(nil, "ARTWORK")
|
||||
btn.texture:SetAllPoints(btn)
|
||||
btn.texture:SetTexCoord(.08, .92, .08, .92)
|
||||
btn.gridIndex = i
|
||||
btn:SetScript("OnClick", function()
|
||||
if this.iconIndex and provider then
|
||||
local path = provider:GetIconByIndex(this.iconIndex)
|
||||
if path then selectedIconPath = path end
|
||||
pfUI.equipmentmanager.RefreshIconGrid()
|
||||
end
|
||||
end)
|
||||
btn:SetScript("OnEnter", function()
|
||||
if not this.iconIndex or not provider then return end
|
||||
local path = provider:GetIconByIndex(this.iconIndex)
|
||||
if type(path) == "string" then
|
||||
local name = string.gsub(path, "^.-INTERFACE\\\\ICONS\\\\", "")
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText(name)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
end)
|
||||
btn:SetScript("OnLeave", GameTooltip_Hide)
|
||||
iconButtons[i] = btn
|
||||
end
|
||||
end
|
||||
|
||||
function pfUI.equipmentmanager.RefreshIconGrid()
|
||||
EnsureProvider()
|
||||
local numIcons = provider:GetNumIcons()
|
||||
local numRows = math.ceil(numIcons / ICON_GRID_COLS)
|
||||
FauxScrollFrame_Update(iconScroll, numRows, ICON_GRID_ROWS, ICON_BTN_SIZE + ICON_BTN_PAD)
|
||||
local offset = FauxScrollFrame_GetOffset(iconScroll)
|
||||
for i = 1, ICON_GRID_ROWS * ICON_GRID_COLS do
|
||||
local listIdx = i + offset * ICON_GRID_COLS
|
||||
local btn = iconButtons[i]
|
||||
if listIdx <= numIcons then
|
||||
btn:Show()
|
||||
btn.iconIndex = listIdx
|
||||
local path = provider:GetIconByIndex(listIdx)
|
||||
btn.texture:SetTexture(path)
|
||||
if path == selectedIconPath then
|
||||
btn.backdrop:SetBackdropBorderColor(1, 0.82, 0, 1)
|
||||
else
|
||||
btn.backdrop:SetBackdropBorderColor(pfUI.cache.er, pfUI.cache.eg, pfUI.cache.eb, pfUI.cache.ea)
|
||||
end
|
||||
else
|
||||
btn:Hide()
|
||||
btn.iconIndex = nil
|
||||
end
|
||||
end
|
||||
-- Sync the "Currently Selected" preview from the path directly so it
|
||||
-- still shows the chosen icon when filtered out of the grid.
|
||||
namePopup.selectedPreview.tex:SetTexture(selectedIconPath)
|
||||
end
|
||||
|
||||
iconScroll:SetScript("OnVerticalScroll", function()
|
||||
FauxScrollFrame_OnVerticalScroll(ICON_BTN_SIZE + ICON_BTN_PAD, function() pfUI.equipmentmanager.RefreshIconGrid() end)
|
||||
end)
|
||||
|
||||
namePopup:SetScript("OnHide", function()
|
||||
if provider then provider:Release(); provider = nil end
|
||||
end)
|
||||
|
||||
local btnPopupOK = MakeButton("pfEqMgrPopupOK", T["OK"] or "OK", namePopup, namePopup, 14, -340, 80)
|
||||
btnPopupOK:ClearAllPoints()
|
||||
btnPopupOK:SetPoint("BOTTOMLEFT", namePopup, "BOTTOMLEFT", 14, 12)
|
||||
|
||||
local btnPopupCancel = MakeButton("pfEqMgrPopupCancel", T["Cancel"] or "Cancel", namePopup, namePopup, 0, 0, 80)
|
||||
local btnPopupCancel = MakeButton("pfEqMgrPopupCancel", CANCEL, namePopup, namePopup, 0, 0, 80)
|
||||
btnPopupCancel:ClearAllPoints()
|
||||
btnPopupCancel:SetPoint("BOTTOMRIGHT", namePopup, "BOTTOMRIGHT", -14, 12)
|
||||
btnPopupCancel:SetScript("OnClick", function() namePopup:Hide() end)
|
||||
|
||||
-- Okay | Cancel, both anchored to the bottom-right corner.
|
||||
btnPopupOK:ClearAllPoints()
|
||||
btnPopupOK:SetPoint("BOTTOMRIGHT", btnPopupCancel, "BOTTOMLEFT", -6, 0)
|
||||
|
||||
btnPopupOK:SetScript("OnClick", function()
|
||||
local name = namePopup.editbox:GetText()
|
||||
if not name or name == "" then return end
|
||||
-- Strip the prefix to match ClassicAPI's persisted short-form basenames.
|
||||
local iconForSave = string.gsub(selectedIconPath, "INTERFACE\\ICONS\\", "")
|
||||
local iconForSave = string.gsub(iconPicker.GetIcon(), "INTERFACE\\ICONS\\", "")
|
||||
if pendingAction == "new" then
|
||||
C_EquipmentSet.CreateEquipmentSet(name, iconForSave)
|
||||
C_EquipmentSet.ClearIgnoredSlotsForSave()
|
||||
@@ -610,31 +430,20 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
function OpenNamePopup(action, prefillName, prefillIcon)
|
||||
pendingAction = action
|
||||
namePopup.editbox:SetText(prefillName or "")
|
||||
EnsureProvider()
|
||||
if prefillIcon then
|
||||
local short = string.gsub(prefillIcon, "INTERFACE\\ICONS\\", "")
|
||||
selectedIconPath = "INTERFACE\\ICONS\\" .. strupper(short)
|
||||
-- Stored icons come back either as a full path or a bare basename
|
||||
-- (see the row rendering in Refresh). Only prepend the prefix when
|
||||
-- there's no path separator, so a full path isn't double-prefixed.
|
||||
iconPicker.SetIcon(string.find(prefillIcon, "\\") and prefillIcon
|
||||
or ("INTERFACE\\ICONS\\" .. prefillIcon))
|
||||
else
|
||||
selectedIconPath = QUESTION_MARK
|
||||
end
|
||||
if action == "rename" then
|
||||
namePopup.title:SetText(T["Rename Set"] or "Rename Set")
|
||||
iconScroll:Hide()
|
||||
for _, b in ipairs(iconButtons) do b:Hide() end
|
||||
namePopup.iconLabel:Hide()
|
||||
namePopup.selectedLabel:Hide()
|
||||
namePopup.selectedPreview:Hide()
|
||||
filterDropdown:Hide()
|
||||
else
|
||||
namePopup.title:SetText(action == "new" and (T["Name Set"] or "Name Set") or (T["Save Set"] or "Save Set"))
|
||||
iconScroll:Show()
|
||||
namePopup.iconLabel:Show()
|
||||
namePopup.selectedLabel:Show()
|
||||
namePopup.selectedPreview:Show()
|
||||
filterDropdown:Show()
|
||||
iconPicker.SetIcon(nil)
|
||||
end
|
||||
namePopup.search:SetText("")
|
||||
-- Rename only changes the name, so hide the whole icon-picking area.
|
||||
iconPicker.SetIconAreaShown(action ~= "rename")
|
||||
namePopup:Show()
|
||||
if action ~= "rename" then pfUI.equipmentmanager.RefreshIconGrid() end
|
||||
if action ~= "rename" then iconPicker.Refresh() end
|
||||
namePopup.editbox:SetFocus()
|
||||
end
|
||||
|
||||
@@ -643,9 +452,13 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
-- ============================================================
|
||||
|
||||
local flyout = CreateFrame("Frame", "pfEqMgrFlyout", UIParent)
|
||||
-- Everything tied to the EM sidecar closes with it: the popout arrows,
|
||||
-- the per-slot flyout, and the name/icon popup. (OnShow re-shows the
|
||||
-- popout arrows.)
|
||||
frame:SetScript("OnHide", function()
|
||||
for _, b in ipairs(popoutButtons) do b:Hide() end
|
||||
flyout:Hide()
|
||||
namePopup:Hide()
|
||||
end)
|
||||
flyout:SetFrameStrata("DIALOG")
|
||||
flyout:Hide()
|
||||
@@ -676,7 +489,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
end
|
||||
end
|
||||
ClearCursor()
|
||||
UIErrorsFrame:AddMessage(EQUIPMENT_MANAGER_BAGS_FULL or "Your bags are full.", 1, .1, .1, 1)
|
||||
UIErrorsFrame:AddMessage(ERR_EQUIPMENT_MANAGER_BAGS_FULL, 1, .1, .1, 1)
|
||||
end
|
||||
|
||||
local function MakeFlyoutButton(i)
|
||||
@@ -692,11 +505,11 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
b:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
if this.specialAction == "placeInBags" then
|
||||
GameTooltip:SetText(EQUIPMENT_MANAGER_PLACE_IN_BAGS or "Place in Bags", 1, 1, 1)
|
||||
GameTooltip:SetText(EQUIPMENT_MANAGER_PLACE_IN_BAGS, 1, 1, 1)
|
||||
elseif this.specialAction == "ignore" then
|
||||
GameTooltip:SetText(EQUIPMENT_MANAGER_IGNORE_SLOT or "Ignore this slot", 1, 1, 1)
|
||||
GameTooltip:SetText(EQUIPMENT_MANAGER_IGNORE_SLOT, 1, 1, 1)
|
||||
elseif this.specialAction == "unignore" then
|
||||
GameTooltip:SetText(EQUIPMENT_MANAGER_UNIGNORE_SLOT or "Stop ignoring this slot", 1, 1, 1)
|
||||
GameTooltip:SetText(EQUIPMENT_MANAGER_UNIGNORE_SLOT, 1, 1, 1)
|
||||
elseif this.bag then
|
||||
GameTooltip:SetBagItem(this.bag, this.slot)
|
||||
elseif this.invSlot then
|
||||
@@ -704,7 +517,7 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
end
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
b:SetScript("OnLeave", GameTooltip_Hide)
|
||||
b:SetScript("OnClick", function()
|
||||
if this.specialAction == "placeInBags" then
|
||||
UnequipToBags(flyout.targetInvSlot)
|
||||
@@ -809,7 +622,6 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
|
||||
if num == 0 then
|
||||
flyout:Hide()
|
||||
UIErrorsFrame:AddMessage(T["No matching items in bags"] or "No matching items in bags", 1, 1, 0, 1)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -938,16 +750,6 @@ pfUI:RegisterModule("equipmentmanager", function()
|
||||
end
|
||||
end
|
||||
|
||||
-- Tie popout visibility to the EM sidecar. They appear when the
|
||||
-- sidecar opens, hide when it closes, and the flyout closes too.
|
||||
frame:HookScript("OnShow", function()
|
||||
for _, b in ipairs(popoutButtons) do b:Show() end
|
||||
end)
|
||||
frame:HookScript("OnHide", function()
|
||||
for _, b in ipairs(popoutButtons) do b:Hide() end
|
||||
if flyout then flyout:Hide() end
|
||||
end)
|
||||
|
||||
-- ============================================================
|
||||
-- Refresh
|
||||
-- ============================================================
|
||||
|
||||
@@ -2772,6 +2772,7 @@ pfUI:RegisterModule("gui", function ()
|
||||
CreateConfig(nil, T["Always Show Health In Percent"], C.tooltip, "alwaysperc", "checkbox")
|
||||
CreateConfig(nil, T["Show Item IDs"], C.tooltip, "itemid", "checkbox")
|
||||
CreateConfig(nil, T["Show Movement Speed"], C.tooltip, "movespeed", "checkbox")
|
||||
CreateConfig(nil, T["Show Aura Caster"], C.tooltip, "aurasource", "checkbox")
|
||||
CreateConfig(nil, T["Custom Transparency"], C.tooltip, "alpha")
|
||||
CreateConfig(nil, T["Status Bar Texture"], C.tooltip.statusbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
|
||||
CreateConfig(nil, T["Always Show Item Comparison"], C.tooltip.compare, "showalways", "checkbox")
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
-- Macro icon picker
|
||||
-- Replaces Blizzard's MacroPopupFrame (the "name + icon" dialog opened by the
|
||||
-- New / Change-Icon buttons) with a pfUI-native picker driven by
|
||||
-- IconDataProviderMixin, so the full spell + item + loose icon set is
|
||||
-- available instead of the stock spell-only list. Saving goes through
|
||||
-- C_Macro.CreateMacro / C_Macro.EditMacro, which take the icon as a texture
|
||||
-- string -- so arbitrary icons (including index-less INV_* item icons) persist
|
||||
-- on both the modern (Turtle/Octo) and stock-vanilla macro UIs. The surrounding
|
||||
-- MacroFrame (list + body editor) is Blizzard's, already skinned elsewhere.
|
||||
pfUI:RegisterModule("macroicons", function ()
|
||||
HookAddonOrVariable("Blizzard_MacroUI", function()
|
||||
-- ============================================================
|
||||
-- Popup frame
|
||||
-- ============================================================
|
||||
|
||||
local picker = CreateFrame("Frame", "pfMacroIconPicker", UIParent)
|
||||
picker:SetFrameStrata("DIALOG")
|
||||
picker:SetSize(472, 484)
|
||||
-- Starts anchored to the right of the macro panel (re-anchored at open
|
||||
-- once the skin's backdrop exists); dragging pins it in place after.
|
||||
picker:SetPoint("BOTTOMLEFT", MacroFrame, "BOTTOMRIGHT", 8, 0)
|
||||
picker:Hide()
|
||||
CreateBackdrop(picker, nil, nil, .9)
|
||||
CreateBackdropShadow(picker)
|
||||
picker:EnableMouse(true)
|
||||
picker:SetMovable(true)
|
||||
picker:RegisterForDrag("LeftButton")
|
||||
picker:SetScript("OnDragStart", function() this:StartMoving() end)
|
||||
picker:SetScript("OnDragStop", function()
|
||||
this:StopMovingOrSizing()
|
||||
this.userMoved = true
|
||||
end)
|
||||
|
||||
-- Spellbook seed leads the grid with class-relevant spell/talent icons.
|
||||
local iconPicker = CreateIconPicker("pfMacroIcon", picker,
|
||||
IconDataProviderExtraType.Spellbook, MACRO_POPUP_TEXT)
|
||||
|
||||
-- ============================================================
|
||||
-- Open / save
|
||||
-- ============================================================
|
||||
|
||||
-- Blizzard's popup disables these while it is open; mirror that so the
|
||||
-- underlying frame can't be double-driven, then let MacroFrame_Update
|
||||
-- restore the correct states on close.
|
||||
local function SetMacroButtons(enabled)
|
||||
local m = enabled and "Enable" or "Disable"
|
||||
MacroNewButton[m](MacroNewButton)
|
||||
MacroEditButton[m](MacroEditButton)
|
||||
MacroDeleteButton[m](MacroDeleteButton)
|
||||
end
|
||||
|
||||
local function OpenMacroPopup(mode)
|
||||
if mode == "edit" and MacroFrame.selectedMacro then
|
||||
picker.mode = "edit"
|
||||
local name, texture = GetMacroInfo(MacroFrame.selectedMacro)
|
||||
picker.editbox:SetText(name or "")
|
||||
iconPicker.SetIcon(texture)
|
||||
else
|
||||
picker.mode = "new"
|
||||
picker.editbox:SetText("")
|
||||
iconPicker.SetIcon(nil)
|
||||
end
|
||||
picker.search:SetText("")
|
||||
if not picker.userMoved then
|
||||
picker:ClearAllPoints()
|
||||
picker:SetPoint("BOTTOMLEFT", MacroFrame.backdrop or MacroFrame, "BOTTOMRIGHT", 8, 0)
|
||||
end
|
||||
SetMacroButtons(false)
|
||||
picker:Show()
|
||||
picker.editbox:SetFocus()
|
||||
iconPicker.Refresh()
|
||||
end
|
||||
|
||||
local function SaveMacroPopup()
|
||||
local text = strtrim(picker.editbox:GetText())
|
||||
if text == "" then return end
|
||||
|
||||
local icon = iconPicker.GetIcon()
|
||||
if picker.mode == "edit" and MacroFrame.selectedMacro then
|
||||
C_Macro.EditMacro(MacroFrame.selectedMacro, text, icon, nil)
|
||||
MacroFrame_SelectMacro(MacroFrame.selectedMacro)
|
||||
else
|
||||
local idx = C_Macro.CreateMacro(text, icon, nil, (MacroFrame.macroBase or 0) > 0)
|
||||
if idx then MacroFrame_SelectMacro(idx) end
|
||||
end
|
||||
picker:Hide()
|
||||
end
|
||||
|
||||
picker:SetScript("OnHide", function() MacroFrame_Update() end)
|
||||
|
||||
picker.editbox:SetScript("OnEnterPressed", SaveMacroPopup)
|
||||
picker.editbox:SetScript("OnEscapePressed", function() picker:Hide() end)
|
||||
|
||||
-- OK / Cancel
|
||||
local okay = CreateFrame("Button", "pfMacroIconPickerOkay", picker, "UIPanelButtonTemplate")
|
||||
okay:SetSize(80, 22)
|
||||
okay:SetText(OKAY)
|
||||
okay:SetScript("OnClick", SaveMacroPopup)
|
||||
SkinButton(okay)
|
||||
|
||||
local cancel = CreateFrame("Button", "pfMacroIconPickerCancel", picker, "UIPanelButtonTemplate")
|
||||
cancel:SetSize(80, 22)
|
||||
cancel:SetText(CANCEL)
|
||||
cancel:SetPoint("BOTTOMRIGHT", picker, "BOTTOMRIGHT", -14, 12)
|
||||
cancel:SetScript("OnClick", function() picker:Hide() end)
|
||||
SkinButton(cancel)
|
||||
|
||||
-- Okay | Cancel, both anchored to the bottom-right corner.
|
||||
okay:SetPoint("BOTTOMRIGHT", cancel, "BOTTOMLEFT", -6, 0)
|
||||
|
||||
-- ============================================================
|
||||
-- Take over the New / Change-Icon buttons; retire Blizzard's popup
|
||||
-- ============================================================
|
||||
|
||||
MacroNewButton:SetScript("OnClick", function()
|
||||
MacroFrame_SaveMacro()
|
||||
OpenMacroPopup("new")
|
||||
end)
|
||||
|
||||
MacroEditButton:SetScript("OnClick", function()
|
||||
MacroFrame_SaveMacro()
|
||||
OpenMacroPopup("edit")
|
||||
end)
|
||||
|
||||
-- Closing the macro window takes the picker with it.
|
||||
MacroFrame:HookScript("OnHide", function() picker:Hide() end)
|
||||
end)
|
||||
end)
|
||||
+54
-59
@@ -1,57 +1,32 @@
|
||||
pfUI:RegisterModule("marktracking", function ()
|
||||
-- Requires mark1-mark8 unit tokens (Turtle WoW / Nampower)
|
||||
if not UnitExists("mark1") and not UnitExists("mark8") then
|
||||
if not pcall(function() UnitExists("mark1") end) then return end
|
||||
end
|
||||
|
||||
local rawborder, border = GetBorderSize()
|
||||
|
||||
-- Parse color strings "r,g,b,a" into components
|
||||
local function ParseColor(str, dr, dg, db, da)
|
||||
if not str or str == "" then return dr, dg, db, da end
|
||||
local _, _, r, g, b, a = string.find(str, "([%d%.]+),([%d%.]+),([%d%.]+),([%d%.]+)")
|
||||
if r then
|
||||
return tonumber(r) or dr, tonumber(g) or dg, tonumber(b) or db, tonumber(a) or da
|
||||
end
|
||||
return dr, dg, db, da
|
||||
end
|
||||
|
||||
local markerOrder = { 8, 7, 6, 5, 4, 3, 2, 1 } -- skull, cross, square, moon, triangle, diamond, circle, star
|
||||
local markerTokens = {}
|
||||
for i = 1, 8 do markerTokens[i] = "mark" .. i end
|
||||
|
||||
-- Default colors per marker
|
||||
local defaultColors = {
|
||||
[1] = { 1.0, 0.9, 0.0, 1 }, -- star: yellow
|
||||
[2] = { 1.0, 0.5, 0.0, 1 }, -- circle: orange
|
||||
[3] = { 0.8, 0.0, 0.8, 1 }, -- diamond: purple
|
||||
[4] = { 0.0, 0.8, 0.0, 1 }, -- triangle: green
|
||||
[5] = { 0.7, 0.7, 0.7, 1 }, -- moon: silver
|
||||
[6] = { 0.0, 0.4, 0.9, 1 }, -- square: blue
|
||||
[7] = { 0.9, 0.0, 0.0, 1 }, -- cross: red
|
||||
[8] = { 1.0, 1.0, 1.0, 1 }, -- skull: white
|
||||
}
|
||||
|
||||
local markerConfigKeys = {
|
||||
[1] = "raidmarkercolor_star",
|
||||
[2] = "raidmarkercolor_circle",
|
||||
[3] = "raidmarkercolor_diamond",
|
||||
[4] = "raidmarkercolor_triangle",
|
||||
[5] = "raidmarkercolor_moon",
|
||||
[6] = "raidmarkercolor_square",
|
||||
[7] = "raidmarkercolor_cross",
|
||||
[8] = "raidmarkercolor_skull",
|
||||
"raidmarkercolor_star",
|
||||
"raidmarkercolor_circle",
|
||||
"raidmarkercolor_diamond",
|
||||
"raidmarkercolor_triangle",
|
||||
"raidmarkercolor_moon",
|
||||
"raidmarkercolor_square",
|
||||
"raidmarkercolor_cross",
|
||||
"raidmarkercolor_skull",
|
||||
}
|
||||
|
||||
local markerColors = {}
|
||||
for i = 1, 8 do
|
||||
local d = defaultColors[i]
|
||||
local r, g, b, a = ParseColor(C.unitframes[markerConfigKeys[i]], d[1], d[2], d[3], d[4])
|
||||
markerColors[i] = { r, g, b, a }
|
||||
for i, markKey in ipairs(markerConfigKeys) do
|
||||
markerTokens[i] = "mark" .. i
|
||||
local r, g, b, a = GetStringColor(C.unitframes[markKey])
|
||||
markerColors[i] = { tonumber(r), tonumber(g), tonumber(b), tonumber(a) }
|
||||
end
|
||||
|
||||
local FALLBACK_INTERVAL = 1.0 -- safety net for units that come into range after marker was set
|
||||
local elapsed = 0
|
||||
local isUnlocked = false
|
||||
local ROW_HEIGHT = tonumber(C.unitframes.raidmarkerheight) or 14
|
||||
local BAR_WIDTH = tonumber(C.unitframes.raidmarkerwidth) or 80
|
||||
@@ -125,19 +100,16 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
|
||||
local i = markerOrder[idx]
|
||||
|
||||
local row = CreateFrame("Button", nil, pfUI.marktracking)
|
||||
row:SetWidth(TOTAL_ROW_WIDTH)
|
||||
row:SetHeight(ROW_HEIGHT)
|
||||
row:SetSize(TOTAL_ROW_WIDTH, ROW_HEIGHT)
|
||||
row:Hide()
|
||||
|
||||
row:RegisterForClicks("LeftButtonUp")
|
||||
row:SetScript("OnClick", function()
|
||||
TargetUnit(markerTokens[this.markerIndex])
|
||||
end)
|
||||
row:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
||||
row:SetAttribute("type1", "target")
|
||||
row:SetAttribute("type2", "menu")
|
||||
|
||||
-- raid icon
|
||||
row.icon = row:CreateTexture(nil, "ARTWORK")
|
||||
row.icon:SetWidth(ROW_HEIGHT)
|
||||
row.icon:SetHeight(ROW_HEIGHT)
|
||||
row.icon:SetSize(ROW_HEIGHT, ROW_HEIGHT)
|
||||
row.icon:SetPoint("LEFT", row, "LEFT", 1, 0)
|
||||
local markTex = C.unitframes.blizzard_raidicons == "1" and "Interface\\TargetingFrame\\UI-RaidTargetingIcons" or pfUI.media["img:raidicons"]
|
||||
row.icon:SetTexture(markTex)
|
||||
@@ -145,8 +117,7 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
|
||||
|
||||
-- portrait (right side)
|
||||
row.portrait = row:CreateTexture(nil, "ARTWORK")
|
||||
row.portrait:SetWidth(PORTRAIT_SIZE)
|
||||
row.portrait:SetHeight(PORTRAIT_SIZE)
|
||||
row.portrait:SetSize(PORTRAIT_SIZE, PORTRAIT_SIZE)
|
||||
row.portrait:SetPoint("RIGHT", row, "RIGHT", -1, 0)
|
||||
row.portrait:SetTexCoord(.1, .9, .1, .9)
|
||||
if not rm_showportrait then row.portrait:Hide() end
|
||||
@@ -204,8 +175,8 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
|
||||
local token = markerTokens[i]
|
||||
|
||||
if UnitExists(token) and not UnitIsDead(token) then
|
||||
local hp = UnitHealth(token)
|
||||
local maxhp = UnitHealthMax(token)
|
||||
local hp, maxhp = UnitHealth(token), UnitHealthMax(token)
|
||||
row:SetAttribute('unit', token)
|
||||
|
||||
if hp and maxhp and maxhp > 0 and hp > 0 then
|
||||
local pct = hp / maxhp
|
||||
@@ -267,6 +238,27 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
|
||||
end
|
||||
end
|
||||
|
||||
-- Fast per-row refresh for a single mark's UNIT_HEALTH/UNIT_MAXHEALTH: moves
|
||||
-- just the bar + hp text. If the mark's visibility flips (comes into range,
|
||||
-- dies, hp crosses 0) the visible rows re-pack, so hand off to UpdateDisplay.
|
||||
local function UpdateRow(i)
|
||||
if isUnlocked then return end
|
||||
local row = pfUI.marktracking.rows[i]
|
||||
if not row then return end
|
||||
local token = markerTokens[i]
|
||||
local hp, maxhp = UnitHealth(token), UnitHealthMax(token)
|
||||
local shouldShow = UnitExists(token) and not UnitIsDead(token)
|
||||
and hp and maxhp and maxhp > 0 and hp > 0 and true or false
|
||||
if shouldShow ~= (row:IsShown() and true or false) then
|
||||
UpdateDisplay()
|
||||
return
|
||||
end
|
||||
if not shouldShow then return end
|
||||
local pct = hp / maxhp
|
||||
row.health:SetValue(pct)
|
||||
if rm_showpct then row.hptext:SetText(math.ceil(pct * 100) .. "%") end
|
||||
end
|
||||
|
||||
-- Unlock mode: show fixed 1-row placeholder so positioning works correctly
|
||||
if pfUI.unlock then
|
||||
local origShow = pfUI.unlock:GetScript("OnShow")
|
||||
@@ -292,24 +284,27 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
|
||||
-- Event-driven scanner frame
|
||||
local scanner = CreateFrame("Frame")
|
||||
|
||||
-- RAID_TARGET_UPDATE: fires when a raid marker is set/cleared
|
||||
-- PLAYER_ENTERING_WORLD: fires on login, reload, and zone transitions
|
||||
-- UNIT_HEALTH/UNIT_MAXHEALTH: fires on HP changes for real-time bar updates
|
||||
-- RAID_TARGET_UPDATE: a raid marker was set/cleared -> full refresh
|
||||
-- PLAYER_ENTERING_WORLD: login/reload/zone -> full refresh
|
||||
-- UNIT_HEALTH/UNIT_MAXHEALTH: ClassicAPI fires these per token; with the mark
|
||||
-- tokens observed they arrive as arg1 == "markN", so we refresh just that
|
||||
-- one row (UpdateRow) instead of rescanning all eight.
|
||||
scanner:RegisterEvent("RAID_TARGET_UPDATE")
|
||||
scanner:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
scanner:RegisterEvent("UNIT_HEALTH")
|
||||
scanner:RegisterEvent("UNIT_MAXHEALTH")
|
||||
|
||||
scanner:SetScript("OnEvent", function()
|
||||
if event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" then
|
||||
-- arg1 is the token; "markN" -> N, nil for any non-mark token.
|
||||
local i = arg1 and tonumber(string.match(arg1, "^mark(%d)"))
|
||||
if i then UpdateRow(i) end
|
||||
return
|
||||
end
|
||||
UpdateDisplay()
|
||||
end)
|
||||
|
||||
-- Fallback poll at 1s: catches units that come into range AFTER a marker was set
|
||||
-- Fallback poll: catches units that come into range AFTER a marker was set
|
||||
-- (no event fires for that case, so we need this safety net)
|
||||
scanner:SetScript("OnUpdate", function()
|
||||
elapsed = elapsed + arg1
|
||||
if elapsed < FALLBACK_INTERVAL then return end
|
||||
elapsed = 0
|
||||
UpdateDisplay()
|
||||
end)
|
||||
C_Timer.NewTicker(FALLBACK_INTERVAL, UpdateDisplay)
|
||||
end)
|
||||
+7
-9
@@ -131,18 +131,16 @@ pfUI:RegisterModule("minimap", function ()
|
||||
|
||||
-- Create coordinates text frame with location configurable
|
||||
pfUI.minimapCoordinates = CreateFrame("Frame", "pfMinimapCoord", pfUI.minimap)
|
||||
pfUI.minimapCoordinates:SetScript("OnUpdate", function()
|
||||
-- Throttle to update coords every 0.1 seconds
|
||||
if ( this.tick or 0) > GetTime() then return end
|
||||
this.tick = GetTime() + .1
|
||||
|
||||
-- Update coords every 0.1 seconds
|
||||
C_Timer.NewTicker(0.1, function()
|
||||
if C.appearance.minimap.coordstext == "off" then return end
|
||||
|
||||
this.posX, this.posY = GetPlayerMapPosition("player")
|
||||
if this.posX ~= 0 and this.posY ~= 0 then
|
||||
this.text:SetText(string.format("%.1f, %.1f", round(this.posX * 100, 1), round(this.posY * 100, 1)))
|
||||
local coord = pfUI.minimapCoordinates
|
||||
coord.posX, coord.posY = GetPlayerMapPosition("player")
|
||||
if coord.posX ~= 0 and coord.posY ~= 0 then
|
||||
coord.text:SetText(string.format("%.1f, %.1f", round(coord.posX * 100, 1), round(coord.posY * 100, 1)))
|
||||
else
|
||||
this.text:SetText("|cffffaaaaN/A")
|
||||
coord.text:SetText("|cffffaaaaN/A")
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
+46
-44
@@ -55,6 +55,7 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
local savedHostileState = nil
|
||||
local savedFriendlyState = nil
|
||||
local inFriendlyZone = false
|
||||
local myGuild = nil
|
||||
local platecount = 0
|
||||
local registry = {}
|
||||
-- Subset of registry that currently has a unit assigned (between
|
||||
@@ -64,18 +65,19 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
|
||||
local raidGuidCache = {} -- guid -> name (rebuilt on RAID_ROSTER_UPDATE/PARTY_MEMBERS_CHANGED)
|
||||
|
||||
-- Per-GUID cast state, populated by nampower's SPELL_START_OTHER events and
|
||||
-- cleared on SPELL_FAILED_OTHER / plate removal / expiry. This replaces the
|
||||
-- old per-tick C_Spell poll on every visible plate: cast detection is now
|
||||
-- event driven, and GetCastInfo just reads this cache.
|
||||
-- Per-GUID cast state, populated from ClassicAPI's UNIT_SPELLCAST_* events
|
||||
-- (which now fire for nameplate tokens) and cleared on STOP / plate removal /
|
||||
-- expiry. This replaces the old per-tick C_Spell poll on every visible plate:
|
||||
-- cast detection is event driven, and GetCastInfo just reads this cache.
|
||||
local castState = {}
|
||||
-- guid -> nameplate, maintained on NAME_PLATE_UNIT_ADDED/_REMOVED so a cast
|
||||
-- event can find its plate in O(1) and only cache casts we actually show.
|
||||
local plateByGuid = {}
|
||||
|
||||
-- One-shot C_Spell poll. Only used to seed a plate that spawns while its
|
||||
-- unit is already mid-cast (its SPELL_START_OTHER fired before the plate
|
||||
-- existed). Never called per frame.
|
||||
-- One-shot C_Spell poll. Builds the cast struct for a UNIT_SPELLCAST_* event
|
||||
-- (the payload carries no timing) and seeds a plate that spawns while its unit
|
||||
-- is already mid-cast (its START fired before the plate existed). Picks cast
|
||||
-- vs channel itself. Never called per frame.
|
||||
local function PollCastInfo(unit)
|
||||
if not unit then return nil end
|
||||
local name, _, texture, startMs, endMs, _, _, _, spellID = C_Spell.UnitCastingInfo(unit)
|
||||
@@ -477,8 +479,11 @@ nameplates:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
|
||||
nameplates:RegisterEvent("UNIT_AURA")
|
||||
nameplates:RegisterEvent("UNIT_FLAGS")
|
||||
nameplates:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
|
||||
nameplates:RegisterEvent("SPELL_START_OTHER")
|
||||
nameplates:RegisterEvent("SPELL_FAILED_OTHER")
|
||||
nameplates:RegisterEvent("UNIT_SPELLCAST_START")
|
||||
nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
|
||||
nameplates:RegisterEvent("UNIT_SPELLCAST_STOP")
|
||||
nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
|
||||
nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
|
||||
nameplates:SetScript("OnEvent", function()
|
||||
-- Stop event handling during logout to prevent crash 132
|
||||
@@ -490,13 +495,17 @@ nameplates:RegisterEvent("SPELL_FAILED_OTHER")
|
||||
nameplates.mouselook:SetScript("OnUpdate", nil)
|
||||
end
|
||||
return
|
||||
|
||||
|
||||
elseif event == "PLAYER_GUILD_UPDATE" and arg1 == 'player' then
|
||||
myGuild = GetGuildInfo("player")
|
||||
|
||||
elseif event == "PLAYER_ENTERING_WORLD" or event == "ZONE_CHANGED_NEW_AREA" then
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
CacheConfig()
|
||||
this:SetGameVariables()
|
||||
RebuildRaidGuidCache()
|
||||
frameState.targetGuid = UnitGUID("target")
|
||||
myGuild = GetGuildInfo("player")
|
||||
end
|
||||
|
||||
-- Handle friendly zone nameplate disable feature
|
||||
@@ -562,9 +571,9 @@ nameplates:RegisterEvent("SPELL_FAILED_OTHER")
|
||||
plate.nameplate.unit = arg1
|
||||
if guid then
|
||||
plateByGuid[guid] = plate.nameplate
|
||||
-- Seed: the unit may already be mid-cast (its SPELL_START_OTHER fired
|
||||
-- before this plate existed). One poll here catches that; ongoing
|
||||
-- casts arrive via the event.
|
||||
-- Seed: the unit may already be mid-cast (its UNIT_SPELLCAST_START
|
||||
-- fired before this plate existed). One poll here catches that;
|
||||
-- ongoing casts arrive via the event.
|
||||
castState[guid] = PollCastInfo(arg1)
|
||||
end
|
||||
nameplates.OnShow(plate)
|
||||
@@ -609,39 +618,32 @@ nameplates:RegisterEvent("SPELL_FAILED_OTHER")
|
||||
if pn then pn.eventcache = true end
|
||||
end
|
||||
|
||||
elseif event == "SPELL_START_OTHER" then
|
||||
-- nampower: arg2=spellId, arg3=casterGuid, arg6=castTime(ms),
|
||||
-- arg7=channel duration(ms, 0 if not a channel), arg8=spellType
|
||||
-- (1 = channel). Cache the cast only for a unit we have a plate for, so
|
||||
elseif event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" then
|
||||
-- ClassicAPI fires UNIT_SPELLCAST_* per unit token, including the caster's
|
||||
-- "nameplateN". The payload has no timing, so poll it (PollCastInfo picks
|
||||
-- cast vs channel) and cache -- only for a unit we have a plate for, so
|
||||
-- the table stays bounded to on-screen casters.
|
||||
local casterGuid = arg3
|
||||
local plate = casterGuid and plateByGuid[casterGuid]
|
||||
if plate then
|
||||
local isChannel = arg8 == 1
|
||||
local durationMs = isChannel and arg7 or arg6
|
||||
if durationMs and durationMs > 0 then
|
||||
local spellId = arg2
|
||||
local now = GetTime()
|
||||
castState[casterGuid] = {
|
||||
spellName = C_Spell.GetSpellName(spellId),
|
||||
spellID = spellId,
|
||||
icon = C_Spell.GetSpellTexture(spellId),
|
||||
startTime = now,
|
||||
endTime = now + durationMs / 1000,
|
||||
duration = durationMs / 1000,
|
||||
isChannel = isChannel,
|
||||
}
|
||||
plate.castUpdate = true -- bypass the throttle so the bar shows now
|
||||
if arg1 and strfind(arg1, "^nameplate") then
|
||||
local guid = UnitGUID(arg1)
|
||||
local plate = guid and plateByGuid[guid]
|
||||
if plate then
|
||||
castState[guid] = PollCastInfo(arg1)
|
||||
if castState[guid] then
|
||||
plate.castUpdate = true -- bypass the throttle so the bar shows now
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif event == "SPELL_FAILED_OTHER" then
|
||||
-- nampower: arg1=casterGuid, arg2=spellId. Clear on interrupt/failure.
|
||||
local casterGuid = arg1
|
||||
if casterGuid and castState[casterGuid] then
|
||||
castState[casterGuid] = nil
|
||||
local plate = plateByGuid[casterGuid]
|
||||
if plate then plate.castUpdate = true end
|
||||
elseif event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP" then
|
||||
-- Cast/channel ended (natural, interrupted, or cancelled -- the poll fires
|
||||
-- STOP for all three). Clear the cached cast and refresh its plate.
|
||||
if arg1 and strfind(arg1, "^nameplate") then
|
||||
local guid = UnitGUID(arg1)
|
||||
if guid and castState[guid] then
|
||||
castState[guid] = nil
|
||||
local plate = plateByGuid[guid]
|
||||
if plate then plate.castUpdate = true end
|
||||
end
|
||||
end
|
||||
|
||||
elseif event == "UNIT_AURA" then
|
||||
@@ -952,7 +954,7 @@ nameplates:RegisterEvent("SPELL_FAILED_OTHER")
|
||||
-- Use the same castbar texture and color as the unit frame castbar (castbar.lua)
|
||||
local cbtexture = pfUI.media[C.appearance.castbar.texture]
|
||||
nameplate.castbar:SetStatusBarTexture(cbtexture or hptexture)
|
||||
local cbr, cbg, cbb, cba = strsplit(",", C.appearance.castbar.castbarcolor)
|
||||
local cbr, cbg, cbb, cba = GetStringColor(C.appearance.castbar.castbarcolor)
|
||||
nameplate.castbar:SetStatusBarColor(cbr, cbg, cbb, cba)
|
||||
-- reset endTime cache so color/texture refresh takes effect on next cast
|
||||
nameplate.castbar.lastEndTime = nil
|
||||
@@ -1125,7 +1127,7 @@ nameplates:RegisterEvent("SPELL_FAILED_OTHER")
|
||||
|
||||
if guild and C.nameplates.showguildname == "1" then
|
||||
plate.guild:SetText(guild)
|
||||
if UnitIsInMyGuild(plate.unit) then
|
||||
if guild == myGuild then
|
||||
plate.guild:SetTextColor(0, 0.9, 0, 1)
|
||||
else
|
||||
plate.guild:SetTextColor(0.8, 0.8, 0.8, 1)
|
||||
|
||||
+17
-36
@@ -37,18 +37,12 @@ pfUI:RegisterModule("panel", function()
|
||||
return
|
||||
end
|
||||
if arg1 == "LeftButton" then
|
||||
if widget.timerFrame:IsShown() then
|
||||
widget.timerFrame:Hide()
|
||||
else
|
||||
widget.timerFrame:Show()
|
||||
end
|
||||
widget.timerFrame:SetShown(not widget.timerFrame:IsShown())
|
||||
elseif arg1 == "RightButton" then
|
||||
widget.timerFrame.Snapshot = GetTime()
|
||||
end
|
||||
end
|
||||
widget:SetScript("OnUpdate",function()
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end
|
||||
|
||||
C_Timer.NewTicker(1, function()
|
||||
local secondsenabled = C.panel.seconds == "1"
|
||||
local fmt
|
||||
if C.global.twentyfour == "0" then
|
||||
@@ -62,8 +56,7 @@ pfUI:RegisterModule("panel", function()
|
||||
|
||||
widget.timerFrame = CreateFrame("Frame", "pfUITimer", UIParent)
|
||||
widget.timerFrame:Hide()
|
||||
widget.timerFrame:SetWidth(120)
|
||||
widget.timerFrame:SetHeight(35)
|
||||
widget.timerFrame:SetSize(120, 35)
|
||||
widget.timerFrame:SetPoint("TOP", 0, -100)
|
||||
UpdateMovable(widget.timerFrame)
|
||||
|
||||
@@ -103,11 +96,9 @@ pfUI:RegisterModule("panel", function()
|
||||
pfUI.panel:OutputPanel("combat", T["Combat"] .. ": " .. NOT_APPLICABLE)
|
||||
end
|
||||
end)
|
||||
widget.combat:SetScript("OnUpdate", function()
|
||||
if not this.tick then this.tick = GetTime() end
|
||||
if GetTime() <= this.tick + 1 then return else this.tick = GetTime() end
|
||||
if this.combat then
|
||||
pfUI.panel:OutputPanel("combat", "|cffffaaaa" .. SecondsToTime(ceil(GetTime() - this.combat)))
|
||||
C_Timer.NewTicker(1, function()
|
||||
if widget.combat.combat then
|
||||
pfUI.panel:OutputPanel("combat", "|cffffaaaa" .. SecondsToTime(ceil(GetTime() - widget.combat.combat)))
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -153,9 +144,7 @@ pfUI:RegisterModule("panel", function()
|
||||
pfUI.addons:Show()
|
||||
end
|
||||
end
|
||||
widget:SetScript("OnUpdate",function()
|
||||
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end
|
||||
|
||||
C_Timer.NewTicker(1, function()
|
||||
fps = floor(GetFramerate())
|
||||
_, _, lag = GetNetStats()
|
||||
|
||||
@@ -346,7 +335,7 @@ pfUI:RegisterModule("panel", function()
|
||||
widget:RegisterEvent("PLAYER_GUILD_UPDATE")
|
||||
widget.Tooltip = function()
|
||||
-- skip without guild
|
||||
if not GetGuildInfo("player") then return end
|
||||
if not IsInGuild() then return end
|
||||
|
||||
local raidparty = {}
|
||||
for i=1,4 do -- detect people in group
|
||||
@@ -398,7 +387,7 @@ pfUI:RegisterModule("panel", function()
|
||||
end
|
||||
widget.Click = function() ToggleFriendsFrame(3) end
|
||||
widget:SetScript("OnEvent", function()
|
||||
if GetGuildInfo("player") then
|
||||
if IsInGuild() then
|
||||
local count = 0
|
||||
for i = 1, GetNumGuildMembers() do
|
||||
local _, _, _, _, _, _, _, _, online = GetGuildRosterInfo(i)
|
||||
@@ -411,9 +400,8 @@ pfUI:RegisterModule("panel", function()
|
||||
end
|
||||
end)
|
||||
|
||||
widget:SetScript("OnUpdate",function()
|
||||
if ( this.tick or 60) > GetTime() then return else this.tick = GetTime() + 60 end
|
||||
if GetGuildInfo("player") then GuildRoster() end
|
||||
C_Timer.NewTicker(60, function()
|
||||
if IsInGuild() then GuildRoster() end
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -492,11 +480,7 @@ pfUI:RegisterModule("panel", function()
|
||||
GameTooltip:Show()
|
||||
end
|
||||
widget.Click = function()
|
||||
if WorldMapFrame:IsShown() then
|
||||
WorldMapFrame:Hide()
|
||||
else
|
||||
WorldMapFrame:Show()
|
||||
end
|
||||
WorldMapFrame:SetShown(not WorldMapFrame:IsShown())
|
||||
end
|
||||
widget:SetScript("OnEvent", function()
|
||||
pfUI.panel:OutputPanel("zone", GetMinimapZoneText(), widget.Tooltip, widget.Click)
|
||||
@@ -645,8 +629,7 @@ pfUI:RegisterModule("panel", function()
|
||||
local frame = CreateFrame("Button", nil, parent)
|
||||
frame:SetFrameLevel(0)
|
||||
frame:ClearAllPoints()
|
||||
frame:SetWidth(width)
|
||||
frame:SetHeight(parent:GetHeight())
|
||||
frame:SetSize(width, parent:GetHeight())
|
||||
frame:SetPoint(location, 0, 0)
|
||||
frame.text = frame:CreateFontString("Status", "LOW", "GameFontNormal")
|
||||
frame.text:ClearAllPoints()
|
||||
@@ -687,8 +670,7 @@ pfUI:RegisterModule("panel", function()
|
||||
local imgstring = "img:" .. leftright
|
||||
parent.texture:SetTexture(pfUI.media[imgstring])
|
||||
parent.texture:SetPoint("CENTER", 0, 0)
|
||||
parent.texture:SetWidth(8)
|
||||
parent.texture:SetHeight(8)
|
||||
parent.texture:SetSize(8, 8)
|
||||
parent.texture:SetVertexColor(.25,.25,.25,1)
|
||||
return parent.texture
|
||||
end
|
||||
@@ -710,7 +692,7 @@ pfUI:RegisterModule("panel", function()
|
||||
UpdateMovable(pfUI.panel.left)
|
||||
|
||||
pfUI.panel.left.hide:SetScript("OnClick", function()
|
||||
if pfUI.chat.left:IsShown() then pfUI.chat.left:Hide() else pfUI.chat.left:Show() end
|
||||
pfUI.chat.left:SetShown(not pfUI.chat.left:IsShown())
|
||||
end)
|
||||
|
||||
if not pfUI.chat then pfUI.panel.left.hide:Hide() end
|
||||
@@ -747,7 +729,7 @@ pfUI:RegisterModule("panel", function()
|
||||
UpdateMovable(pfUI.panel.right)
|
||||
|
||||
pfUI.panel.right.hide:SetScript("OnClick", function()
|
||||
if pfUI.chat.right:IsShown() then pfUI.chat.right:Hide() else pfUI.chat.right:Show() end
|
||||
pfUI.chat.right:SetShown(not pfUI.chat.right:IsShown())
|
||||
end)
|
||||
|
||||
if not pfUI.chat then pfUI.panel.right.hide:Hide() end
|
||||
@@ -796,8 +778,7 @@ pfUI:RegisterModule("panel", function()
|
||||
pfUI.panel.microbutton = CreateFrame("Frame", "pfPanelMicroButton", UIParent)
|
||||
pfUI.panel.microbutton:SetPoint("TOP", pfUI.panel.minimap, "BOTTOM", 0, -2*default_border)
|
||||
UpdateMovable(pfUI.panel.microbutton)
|
||||
pfUI.panel.microbutton:SetHeight(23)
|
||||
pfUI.panel.microbutton:SetWidth(145)
|
||||
pfUI.panel.microbutton:SetSize(145, 23)
|
||||
pfUI.panel.microbutton:SetFrameStrata("MEDIUM")
|
||||
|
||||
for i=1,table.getn(MICRO_BUTTONS) do
|
||||
|
||||
+28
-30
@@ -39,17 +39,20 @@ pfUI:RegisterModule("player", function ()
|
||||
playerFrame.isSpellCaster = myclass ~= "WARRIOR" and myclass ~= "ROGUE" and myclass ~= "HUNTER"
|
||||
|
||||
-- Convert "r,g,b,a" config color string to a 6-char hex string, or nil if unset
|
||||
local function cfgColorToHex(colorStr)
|
||||
if not colorStr or colorStr == "" then return nil end
|
||||
local r, g, b = strsplit(",", colorStr)
|
||||
r, g, b = tonumber(r), tonumber(g), tonumber(b)
|
||||
if not r or not g or not b then return nil end
|
||||
return string.format("%02X%02X%02X", r * 255, g * 255, b * 255)
|
||||
end
|
||||
local cfgColorToHexTable = setmetatable({}, {
|
||||
__index = function (t, colorStr)
|
||||
if not colorStr or colorStr == "" then return nil end
|
||||
local r, g, b = GetStringColor(colorStr)
|
||||
if not r or not g or not b then return nil end
|
||||
local hex = C_ColorUtil.GenerateTextColorCode({ r=r, g = g, b=b})
|
||||
rawset(t, colorStr, hex)
|
||||
return hex
|
||||
end
|
||||
})
|
||||
|
||||
-- SP school colors indexed by GetSpellBonusDamage's 1-based school order
|
||||
-- (1=phys, 2=holy, 3=fire, 4=nature, 5=frost, 6=shadow, 7=arcane)
|
||||
local spColors = { "FFFFFF", "FFFF80", "FF8000", "4DFF4D", "80FFFF", "9482C9", "FFFFFF" }
|
||||
local spColors = { "FFFFFFFF", "FFFFFF80", "FFFF8000", "FF4DFF4D", "FF80FFFF", "FF9482C9", "FFFFFFFF" }
|
||||
|
||||
-- Default SP school per class used as tiebreaker when multiple schools are equal
|
||||
local spDefaultSchool = {
|
||||
@@ -68,42 +71,37 @@ pfUI:RegisterModule("player", function ()
|
||||
-- from UNIT_MOD_CAST_SPEED). Talent/spell-specific cast-time reductions
|
||||
-- show up in the actual cast bar via C_Spell.UnitCastingInfo; folding them
|
||||
-- in here too was mixing two different concepts into one number.
|
||||
local showHaste = cfg.display_haste == "1"
|
||||
local showSP = cfg.display_spellpower == "1"
|
||||
|
||||
local isSpellCaster = playerFrame.isSpellCaster
|
||||
if (not showHaste or not isSpellCaster) and not showSP then
|
||||
local showHaste = isSpellCaster and cfg.display_haste == "1"
|
||||
local showSP = isSpellCaster and cfg.display_spellpower == "1"
|
||||
|
||||
if not showHaste and not showSP then
|
||||
playerFrame.infoTopCenterText:SetText("")
|
||||
return
|
||||
end
|
||||
|
||||
local haste = UnitSpellHaste("player")
|
||||
local text = ""
|
||||
local parts = {}
|
||||
|
||||
if showHaste and isSpellCaster and haste then
|
||||
local hasteHex = cfgColorToHex(cfg.display_haste_color) or "FFFFFF"
|
||||
text = string.format("|cff%s%.1f%%|r", hasteHex, haste)
|
||||
if showHaste then
|
||||
local hex = cfgColorToHexTable[cfg.display_haste_color] or "FFFFFFFF"
|
||||
table.insert(parts, string.format("|c%s%.1f%%|r", hex, UnitSpellHaste("player")))
|
||||
end
|
||||
|
||||
if showSP and isSpellCaster then
|
||||
local defSchool = spDefaultSchool[myclass] or 2
|
||||
local maxSP = GetSpellBonusDamage(defSchool) or 0
|
||||
local maxColor = spColors[defSchool]
|
||||
for i = 2, 7 do -- skip physical (1); default school seeds the tiebreak
|
||||
if showSP then
|
||||
local school = spDefaultSchool[myclass] or 2
|
||||
local maxSP = GetSpellBonusDamage(school) or 0
|
||||
for i = 2, 7 do -- skip physical (1)
|
||||
local v = GetSpellBonusDamage(i) or 0
|
||||
if v > maxSP then
|
||||
maxSP = v
|
||||
maxColor = spColors[i]
|
||||
end
|
||||
if v > maxSP then maxSP, school = v, i end
|
||||
end
|
||||
|
||||
if maxSP > 0 then
|
||||
local spHex = (cfg.display_sp_color_override == "1" and cfgColorToHex(cfg.display_sp_color)) or maxColor
|
||||
if text ~= "" then text = text .. " " end
|
||||
text = text .. string.format("|cff%s+%d SP|r", spHex, maxSP)
|
||||
local hex = (cfg.display_sp_color_override == "1" and cfgColorToHexTable[cfg.display_sp_color]) or spColors[school]
|
||||
table.insert(parts, string.format("|c%s+%d SP|r", hex, maxSP))
|
||||
end
|
||||
end
|
||||
|
||||
playerFrame.infoTopCenterText:SetText(text)
|
||||
playerFrame.infoTopCenterText:SetText(table.concat(parts, " "))
|
||||
end
|
||||
|
||||
-- Keep a reference to the generic UF UpdateConfig so we can chain it
|
||||
|
||||
+1
-1
@@ -234,7 +234,7 @@ pfUI:RegisterModule("roll", function ()
|
||||
f.time.bar:SetAllPoints(f.time)
|
||||
f.time.bar:SetStatusBarTexture(pfUI.media["img:bar"])
|
||||
f.time.bar:SetMinMaxValues(0, 100)
|
||||
local r, g, b, a = strsplit(",", C.appearance.border.color)
|
||||
local r, g, b, a = GetStringColor(C.appearance.border.color)
|
||||
f.time.bar:SetStatusBarColor(r, g, b)
|
||||
f.time.bar:SetValue(20)
|
||||
f.time.bar:SetScript("OnUpdate", function()
|
||||
|
||||
@@ -100,10 +100,10 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
|
||||
CreateBackdropShadow(KLHTM_Frame)
|
||||
|
||||
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
KLHTM_Frame.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
KLHTM_Frame.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
|
||||
@@ -199,10 +199,10 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
|
||||
CreateBackdropShadow(TWTMain)
|
||||
|
||||
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
TWTMain.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
TWTMain.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
|
||||
@@ -328,10 +328,10 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
|
||||
CreateBackdropShadow(SW_BarFrame1)
|
||||
|
||||
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
SW_BarFrame1.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
SW_BarFrame1.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
|
||||
|
||||
@@ -244,10 +244,10 @@ pfUI:RegisterModule("thirdparty", function()
|
||||
end
|
||||
|
||||
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
window.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
window.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
|
||||
@@ -350,10 +350,10 @@ pfUI:RegisterModule("thirdparty", function()
|
||||
CreateBackdropShadow(frame)
|
||||
|
||||
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
|
||||
local r, g, b, a = strsplit(",", C.chat.global.background)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.background)
|
||||
frame.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
|
||||
local r, g, b, a = strsplit(",", C.chat.global.border)
|
||||
local r, g, b, a = GetStringColor(C.chat.global.border)
|
||||
frame.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -266,4 +266,18 @@ pfUI:RegisterModule("tooltip", function ()
|
||||
end
|
||||
GameTooltip:Show()
|
||||
end
|
||||
|
||||
if C.tooltip.aurasource == "1" then
|
||||
hooksecurefunc(GameTooltip, "SetUnitAura", function(self, ...)
|
||||
local aura = C_UnitAuras.GetAuraDataByIndex(unpack(arg))
|
||||
if not aura then return end
|
||||
local caster = aura.sourceUnit and UnitName(aura.sourceUnit)
|
||||
if not caster and aura.sourceGUID then
|
||||
caster = UnitNameFromGUID(aura.sourceGUID)
|
||||
end
|
||||
if not caster or caster == "" then return end
|
||||
self:AddLine(T["Cast by"] .. ": " .. caster, .7, .7, 1)
|
||||
self:Show()
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -355,6 +355,25 @@ pfUI:RegisterModule("turtle-wow", function ()
|
||||
end
|
||||
end)
|
||||
|
||||
if C.disabled["macroicons"] == "1" then
|
||||
HookAddonOrVariable("Blizzard_MacroUI", function()
|
||||
if type(UpdateMacroIconFilenames) ~= "function" then return end
|
||||
function _G.UpdateMacroIconFilenames()
|
||||
wipe(MACRO_ICON_FILENAMES)
|
||||
local provider = CreateAndInitFromMixin(IconDataProviderMixin, IconDataProviderExtraType.Spellbook)
|
||||
local seen = {}
|
||||
for i = 1, provider:GetNumIcons() do
|
||||
-- uppercase, prefix-stripped basename, matching Blizzard's original
|
||||
local icon = string.gsub(string.upper(provider:GetIconByIndex(i)), "INTERFACE\\ICONS\\", "")
|
||||
if not seen[icon] then
|
||||
seen[icon] = true
|
||||
table.insert(MACRO_ICON_FILENAMES, icon)
|
||||
end
|
||||
end
|
||||
provider:Release()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- add turtle-wow sell values
|
||||
pfSellData = {
|
||||
|
||||
@@ -23,7 +23,7 @@ do
|
||||
-- ClassicAPI dependency check.
|
||||
-- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
|
||||
-- API surface that ClassicAPI polyfills, so presence is required.
|
||||
local PFUI_CLASSIC_API_MIN = 10802 -- (X*10000 + Y*100 + Z)
|
||||
local PFUI_CLASSIC_API_MIN = 10903 -- (X*10000 + Y*100 + Z)
|
||||
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
|
||||
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
|
||||
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
|
||||
|
||||
+26
-24
@@ -60,33 +60,35 @@ pfUI:RegisterSkin("Macro", function ()
|
||||
MacroFrameScrollFrame:ClearAllPoints()
|
||||
MacroFrameScrollFrame:SetPoint("TOPLEFT", MacroFrameSelectedMacroBackground, "BOTTOMLEFT", 11, -13)
|
||||
|
||||
-- The macroicons module replaces MacroPopupFrame with its own picker.
|
||||
-- Only skin the stock popup when that module is disabled.
|
||||
if pfUI_config["disabled"] and pfUI_config["disabled"]["macroicons"] == "1" then
|
||||
StripTextures(MacroPopupFrame)
|
||||
CreateBackdrop(MacroPopupFrame, nil, nil, .75)
|
||||
MacroPopupFrame:SetFrameStrata("DIALOG")
|
||||
MacroPopupFrame:ClearAllPoints()
|
||||
MacroPopupFrame:SetPoint("TOPLEFT", MacroFrame.backdrop, "TOPRIGHT", 2*border, 0)
|
||||
|
||||
StripTextures(MacroPopupScrollFrame)
|
||||
SkinScrollbar(MacroPopupScrollFrameScrollBar)
|
||||
|
||||
StripTextures(MacroPopupFrame)
|
||||
CreateBackdrop(MacroPopupFrame, nil, nil, .75)
|
||||
MacroPopupFrame:SetFrameStrata("DIALOG")
|
||||
MacroPopupFrame:ClearAllPoints()
|
||||
MacroPopupFrame:SetPoint("TOPLEFT", MacroFrame.backdrop, "TOPRIGHT", 2*border, 0)
|
||||
MacroPopupEditBox:DisableDrawLayer("BACKGROUND")
|
||||
CreateBackdrop(MacroPopupEditBox, nil, true)
|
||||
MacroPopupEditBox:SetScript("OnEscapePressed", function()
|
||||
MacroPopupFrame:Hide()
|
||||
MacroFrame_Update()
|
||||
end)
|
||||
|
||||
StripTextures(MacroPopupScrollFrame)
|
||||
SkinScrollbar(MacroPopupScrollFrameScrollBar)
|
||||
|
||||
MacroPopupEditBox:DisableDrawLayer("BACKGROUND")
|
||||
CreateBackdrop(MacroPopupEditBox, nil, true)
|
||||
MacroPopupEditBox:SetScript("OnEscapePressed", function()
|
||||
MacroPopupFrame:Hide()
|
||||
MacroFrame_Update()
|
||||
end)
|
||||
|
||||
for i=1, NUM_MACRO_ICONS_SHOWN do
|
||||
local button = _G["MacroPopupButton"..i]
|
||||
local icon = _G["MacroPopupButton"..i..'Icon']
|
||||
StripTextures(button)
|
||||
SkinButton(button, nil, nil, nil, icon)
|
||||
for i=1, NUM_MACRO_ICONS_SHOWN do
|
||||
local button = _G["MacroPopupButton"..i]
|
||||
local icon = _G["MacroPopupButton"..i..'Icon']
|
||||
StripTextures(button)
|
||||
SkinButton(button, nil, nil, nil, icon)
|
||||
end
|
||||
SkinButton(MacroPopupCancelButton)
|
||||
SkinButton(MacroPopupOkayButton)
|
||||
MacroPopupOkayButton:ClearAllPoints()
|
||||
MacroPopupOkayButton:SetPoint("RIGHT", MacroPopupCancelButton, "LEFT", -2*bpad, 0)
|
||||
end
|
||||
SkinButton(MacroPopupCancelButton)
|
||||
SkinButton(MacroPopupOkayButton)
|
||||
MacroPopupOkayButton:ClearAllPoints()
|
||||
MacroPopupOkayButton:SetPoint("RIGHT", MacroPopupCancelButton, "LEFT", -2*bpad, 0)
|
||||
end)
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user