Compare commits

...
3 Commits
Author SHA1 Message Date
Brues 2e03928dd0 libunitscan: drop RememberByUnit, keep only the level DB
RememberByUnit fed C_PlayerCache.RememberPlayer from group, target, mouseover
and nameplate units. It added nothing the engine's name cache doesn't already
learn, and for out-of-range group members it passed a nil class and raised
RememberPlayer's usage error.
2026-11-06 09:09:25 -06:00
Brues 48d476b6e2 api: simplify GetNoNameObject and match filters as plain substrings
The hand-rolled pattern escaping missed '.', '$' and '%', so a title
or texture filter with those characters could match the wrong object.
Every caller passes a literal substring, so search with plain find
instead. One Matches helper replaces the three copies of the filter
check, and the doc header now lists the real arguments.
2026-11-06 09:01:44 -06:00
Brues b5626ceb92 api: simplify AlignToPosition, SetAutoPoint, GetBestAnchor and ConvertFrameAnchor
AlignToPosition reads its corner pairs from a table. SetAutoPoint and
GetBestAnchor share one 3x3 screen-grid helper, which also gives an
anchor to frames centered exactly on a grid line. ConvertFrameAnchor
resolves the x and y offsets independently.
2026-11-05 21:35:12 -06:00
2 changed files with 84 additions and 179 deletions
+83 -161
View File
@@ -784,25 +784,40 @@ end
-- 'anchor' [frame] the frame where it should be aligned to -- 'anchor' [frame] the frame where it should be aligned to
-- 'position' [string] where it should appear, takes the following: -- 'position' [string] where it should appear, takes the following:
-- "TOP", "RIGHT", "BOTTOM", "LEFT" -- "TOP", "RIGHT", "BOTTOM", "LEFT"
local alignments = {
-- frame corners anchor corners spacing x/y
TOP = { "BOTTOMLEFT", "BOTTOMRIGHT", "TOPLEFT", "TOPRIGHT", 0, 1 },
RIGHT = { "TOPLEFT", "BOTTOMLEFT", "TOPRIGHT", "BOTTOMRIGHT", 1, 0 },
BOTTOM = { "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT", 0, -1 },
LEFT = { "TOPRIGHT", "BOTTOMRIGHT", "TOPLEFT", "BOTTOMLEFT", -1, 0 },
}
function pfUI.api.AlignToPosition(frame, anchor, position, spacing) function pfUI.api.AlignToPosition(frame, anchor, position, spacing)
if frame == anchor then return end if frame == anchor then return end
frame:ClearAllPoints() frame:ClearAllPoints()
if position == "TOP" and anchor then
frame:SetPoint("BOTTOMLEFT", anchor, "TOPLEFT", 0, (spacing or 0)) local align = anchor and alignments[position]
frame:SetPoint("BOTTOMRIGHT", anchor, "TOPRIGHT", 0, (spacing or 0)) if not align then
elseif position == "RIGHT" and anchor then -- no usable anchor: stretch along the right screen edge
frame:SetPoint("TOPLEFT", anchor, "TOPRIGHT", (spacing or 0), 0) frame:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT")
frame:SetPoint("BOTTOMLEFT", anchor, "BOTTOMRIGHT", (spacing or 0), 0) frame:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT")
elseif position == "BOTTOM" and anchor then return
frame:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, -(spacing or 0))
frame:SetPoint("TOPRIGHT", anchor, "BOTTOMRIGHT", 0, -(spacing or 0))
elseif position == "LEFT" and anchor then
frame:SetPoint("TOPRIGHT", anchor, "TOPLEFT", -(spacing or 0), 0)
frame:SetPoint("BOTTOMRIGHT", anchor, "BOTTOMLEFT", -(spacing or 0), 0)
else
frame:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT", 0, 0)
frame:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", 0, 0)
end end
local point1, point2, rel1, rel2, dx, dy = unpack(align)
local x, y = dx * (spacing or 0), dy * (spacing or 0)
frame:SetPoint(point1, anchor, rel1, x, y)
frame:SetPoint(point2, anchor, rel2, x, y)
end
-- Splits the screen into a 3x3 grid and returns the cell that (x, y) falls in
-- as a row ("TOP", "" or "BOTTOM") and a column ("LEFT", "" or "RIGHT").
local function GetScreenCell(x, y, scale)
local w = GetScreenWidth() / scale / 3
local h = GetScreenHeight() / scale / 3
local row = y > 2 * h and "TOP" or y < h and "BOTTOM" or ""
local col = x < w and "LEFT" or x > 2 * w and "RIGHT" or ""
return row, col
end end
-- [ SetAutoPoint ] -- [ SetAutoPoint ]
@@ -812,67 +827,22 @@ end
-- 'spacing' [number] the padding that should be used between the -- 'spacing' [number] the padding that should be used between the
-- frame and its parent frame -- frame and its parent frame
function pfUI.api.SetAutoPoint(frame, parent, spacing) function pfUI.api.SetAutoPoint(frame, parent, spacing)
--[[
a b max
+-----------------+
| 1 | 2 | 3 |
|-----+-----+-----| c
| 4 | 5 | 6 |
|-----+-----+-----| d
| 7 | 8 | 9 |
+-----------------+
0
]]--
local a = GetScreenWidth() / 3
local b = GetScreenWidth() / 3 * 2
local c = GetScreenHeight() / 3 * 2
local d = GetScreenHeight() / 3
local x, y = parent:GetCenter() local x, y = parent:GetCenter()
if not x then return end
if not x or not y then return end -- open the frame toward the screen center
local row, col = GetScreenCell(x, y, 1)
local off = spacing or 0 local off = spacing or 0
frame:ClearAllPoints() frame:ClearAllPoints()
if row == "TOP" then
if x < a and y > c then frame:SetPoint("TOP" .. col, parent, "BOTTOM" .. col, 0, -off)
-- TOPLEFT elseif row == "" and col == "LEFT" then
frame:SetPoint("TOPLEFT", parent, "BOTTOMLEFT", 0, -off)
elseif x > a and x < b and y > c then
-- TOP
frame:SetPoint("TOP", parent, "BOTTOM", 0, -off)
elseif x > b and y > c then
-- TOPRIGHT
frame:SetPoint("TOPRIGHT", parent, "BOTTOMRIGHT", 0, -off)
elseif x < a and y > d and y < c then
-- LEFT
frame:SetPoint("LEFT", parent, "RIGHT", off, 0) frame:SetPoint("LEFT", parent, "RIGHT", off, 0)
elseif row == "" and col == "RIGHT" then
elseif x > a and x < b and y > d and y < c then
-- CENTER
frame:SetPoint("BOTTOM", parent, "TOP", 0, off)
elseif x > b and y > d and y < c then
-- RIGHT
frame:SetPoint("RIGHT", parent, "LEFT", -off, 0) frame:SetPoint("RIGHT", parent, "LEFT", -off, 0)
else
elseif x < a and y < d then frame:SetPoint("BOTTOM" .. col, parent, "TOP" .. col, 0, off)
-- BOTTOMLEFT
frame:SetPoint("BOTTOMLEFT", parent, "TOPLEFT", 0, off)
elseif x > a and x < b and y < d then
-- BOTTOM
frame:SetPoint("BOTTOM", parent, "TOP", 0, off)
elseif x > b and y < d then
-- BOTTOMRIGHT
frame:SetPoint("BOTTOMRIGHT", parent, "TOPRIGHT", 0, off)
end end
end end
@@ -881,33 +851,12 @@ end
-- 'self' [frame] the frame that should be checked -- 'self' [frame] the frame that should be checked
-- returns: [string] the name of the best anchor -- returns: [string] the name of the best anchor
function pfUI.api.GetBestAnchor(self) function pfUI.api.GetBestAnchor(self)
local scale = self:GetScale()
local x, y = self:GetCenter() local x, y = self:GetCenter()
local a = GetScreenWidth() / scale / 3 if not x then return end
local b = GetScreenWidth() / scale / 3 * 2
local c = GetScreenHeight() / scale / 3 * 2
local d = GetScreenHeight() / scale / 3
if not x or not y then return end
if x < a and y > c then local row, col = GetScreenCell(x, y, self:GetScale())
return "TOPLEFT" local anchor = row .. col
elseif x > a and x < b and y > c then return anchor ~= "" and anchor or "CENTER"
return "TOP"
elseif x > b and y > c then
return "TOPRIGHT"
elseif x < a and y > d and y < c then
return "LEFT"
elseif x > a and x < b and y > d and y < c then
return "CENTER"
elseif x > b and y > d and y < c then
return "RIGHT"
elseif x < a and y < d then
return "BOTTOMLEFT"
elseif x > a and x < b and y < d then
return "BOTTOM"
elseif x > b and y < d then
return "BOTTOMRIGHT"
end
end end
-- [ ConvertFrameAnchor ] -- [ ConvertFrameAnchor ]
@@ -916,31 +865,23 @@ end
-- 'anchor' [string] the new anchor that shall be used -- 'anchor' [string] the new anchor that shall be used
-- returns: anchor, x, y can directly be used in SetPoint() -- returns: anchor, x, y can directly be used in SetPoint()
function pfUI.api.ConvertFrameAnchor(self, anchor) function pfUI.api.ConvertFrameAnchor(self, anchor)
local scale, x, y, _ = self:GetScale(), nil, nil, nil local scale = self:GetScale()
local w, h = GetScreenWidth() / scale, GetScreenHeight() / scale
if anchor == "CENTER" then -- offsets from the matching point of the screen, axis by axis
x, y = self:GetCenter() local x, y = self:GetCenter()
x, y = x - GetScreenWidth()/2/scale, y - GetScreenHeight()/2/scale x, y = x - w / 2, y - h / 2
elseif anchor == "TOPLEFT" then
x, y = self:GetLeft(), self:GetTop() - GetScreenHeight()/scale if anchor:find("LEFT") then
elseif anchor == "TOP" then x = self:GetLeft()
x, _ = self:GetCenter() elseif anchor:find("RIGHT") then
x, y = x - GetScreenWidth()/2/scale, self:GetTop() - GetScreenHeight()/scale x = self:GetRight() - w
elseif anchor == "TOPRIGHT" then end
x, y = self:GetRight() - GetScreenWidth()/scale, self:GetTop() - GetScreenHeight()/scale
elseif anchor == "RIGHT" then if anchor:find("TOP") then
_, y = self:GetCenter() y = self:GetTop() - h
x, y = self:GetRight() - GetScreenWidth()/scale, y - GetScreenHeight()/2/scale elseif anchor:find("BOTTOM") then
elseif anchor == "BOTTOMRIGHT" then y = self:GetBottom()
x, y = self:GetRight() - GetScreenWidth()/scale, self:GetBottom()
elseif anchor == "BOTTOM" then
x, _ = self:GetCenter()
x, y = x - GetScreenWidth()/2/scale, self:GetBottom()
elseif anchor == "BOTTOMLEFT" then
x, y = self:GetLeft(), self:GetBottom()
elseif anchor == "LEFT" then
_, y = self:GetCenter()
x, y = self:GetLeft(), y - GetScreenHeight()/2/scale
end end
return anchor, round(x, 2), round(y, 2) return anchor, round(x, 2), round(y, 2)
@@ -1544,12 +1485,20 @@ function pfUI.api.GetColorGradient(perc)
end end
-- [ GetNoNameObject ] -- -- [ GetNoNameObject ] --
-- 'frame' [string] parent frame -- Finds the first unnamed region or child of a frame that matches the filters
-- 'scantype' [string] scanning Region or Children -- 'frame' [frame] parent frame
-- 'objtype' [string] -- 'objtype' [string] "Texture", "FontString" or "Button"
-- 'layer' [string] -- 'layer' [string] draw layer of a region (optional)
-- 'arg1' [string] -- 'arg1' [string] substring of the texture path (Texture, Button)
-- or the text (FontString) (optional)
-- 'arg2' [string] substring of a button's text (optional)
-- return object -- return object
-- a missing value or filter counts as a match
local function Matches(value, needle)
return not needle or not value or value:find(needle, 1, true)
end
function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2) function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2)
-- A nil/non-frame parent otherwise dies on frame:GetRegions()/:GetChildren() -- A nil/non-frame parent otherwise dies on frame:GetRegions()/:GetChildren()
-- below, where the message would name this line, shared by every caller; -- below, where the message would name this line, shared by every caller;
@@ -1558,46 +1507,19 @@ function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2)
error("GetNoNameObject: invalid parent frame", 2) error("GetNoNameObject: invalid parent frame", 2)
end end
local arg1 = arg1 and gsub(arg1, "([%+%-%*%(%)%?%[%]%^])", "%%%1") local isRegion = objtype == "Texture" or objtype == "FontString"
local arg2 = arg2 and gsub(arg2, "([%+%-%*%(%)%?%[%]%^])", "%%%1") local objects = isRegion and { frame:GetRegions() } or { frame:GetChildren() }
local objects
if objtype == "Texture" or objtype == "FontString" then
objects = {frame:GetRegions()}
else
objects = {frame:GetChildren()}
end
for _, object in ipairs(objects) do for _, object in ipairs(objects) do
local check = true if object:GetObjectType() == objtype and (not layer or object:GetDrawLayer() == layer) then
if object:GetObjectType() ~= objtype or (layer and object:GetDrawLayer() ~= layer) then check = false end if objtype == "Texture" then
local texture = object:GetTexture()
if check then if texture ~= "Interface\\BUTTONS\\WHITE8X8" and Matches(texture, arg1) then return object end
if objtype == "Texture" and object.SetTexture and object:GetTexture() ~= "Interface\\BUTTONS\\WHITE8X8" then elseif objtype == "FontString" then
if arg1 then if Matches(object:GetText(), arg1) then return object end
local texture = object:GetTexture() elseif objtype == "Button" then
if texture and not string.find(texture, arg1, 1) then check = false end local normal = object:GetNormalTexture()
end if normal and Matches(normal:GetTexture(), arg1) and Matches(object:GetText(), arg2) then return object end
if check then return object end
elseif objtype == "FontString" and object.SetText then
if arg1 then
local text = object:GetText()
if text and not string.find(text, arg1, 1) then check = false end
end
if check then return object end
elseif objtype == "Button" and object.GetNormalTexture and object:GetNormalTexture() then
if arg1 then
local texture = object:GetNormalTexture():GetTexture()
if texture and not string.find(texture, arg1, 1) then check = false end
end
if arg2 then
local text = object:GetText()
if text and not string.find(text, arg2, 1) then check = false end
end
if check then return object end
end end
end end
end end
+1 -18
View File
@@ -20,17 +20,6 @@ if pfUI.api.libunitscan then return end
local players = {} local players = {}
-- Feed a unit into ClassicAPI's persistent name cache. Roster-style packets
-- (party / raid / inspect on glance) carry class inline, so the engine often
-- doesn't issue a separate name query and the DLL's NameCache hook never
-- fires for them — this is where we close that gap.
local function RememberByUnit(unit)
local guid = UnitGUID(unit)
if not guid then return end
local _, race = UnitRace(unit)
C_PlayerCache.RememberPlayer(guid, UnitName(unit), UnitClassBase(unit), race, UnitSex(unit) - 2)
end
function GetPlayerLevel(name) function GetPlayerLevel(name)
local entry = players[name] local entry = players[name]
return entry and entry.level return entry and entry.level
@@ -66,7 +55,6 @@ libunitscan:SetScript("OnEvent", function()
-- update own character details -- update own character details
AddLevel(UnitName("player"), UnitLevel("player")) AddLevel(UnitName("player"), UnitLevel("player"))
RememberByUnit("player")
elseif event == "FRIENDLIST_UPDATE" then elseif event == "FRIENDLIST_UPDATE" then
for i = 1, GetNumFriends() do for i = 1, GetNumFriends() do
@@ -88,15 +76,11 @@ libunitscan:SetScript("OnEvent", function()
for i = 1, GetNumRaidMembers() do for i = 1, GetNumRaidMembers() do
name, _, _, level = GetRaidRosterInfo(i) name, _, _, level = GetRaidRosterInfo(i)
AddLevel(name, level) AddLevel(name, level)
RememberByUnit("raid" .. i)
end end
elseif event == "PARTY_MEMBERS_CHANGED" then elseif event == "PARTY_MEMBERS_CHANGED" then
local unit
for i = 1, GetNumPartyMembers() do for i = 1, GetNumPartyMembers() do
unit = "party" .. i AddLevel(UnitName("party" .. i), UnitLevel("party" .. i))
AddLevel(UnitName(unit), UnitLevel(unit))
RememberByUnit(unit)
end end
elseif event == "WHO_LIST_UPDATE" or event == "CHAT_MSG_SYSTEM" then elseif event == "WHO_LIST_UPDATE" or event == "CHAT_MSG_SYSTEM" then
@@ -113,7 +97,6 @@ libunitscan:SetScript("OnEvent", function()
or "mouseover" or "mouseover"
if UnitIsPlayer(scan) then if UnitIsPlayer(scan) then
AddLevel(UnitName(scan), UnitLevel(scan)) AddLevel(UnitName(scan), UnitLevel(scan))
RememberByUnit(scan)
end end
end end
end) end)