Files
pfUI/api/api.lua
T
shagu 3a6140224b api: add capture index aware matcher
* optimize/reduce regexes
* add cache to SanitizePattern
* add function to determine capture indexes
* add capture index aware string.match function
2018-12-02 14:46:59 +01:00

906 lines
30 KiB
Lua

pfUI.api = { }
-- load pfUI environment
setfenv(1, pfUI:GetEnvironment())
-- Client API shortcuts
gfind = string.gmatch or string.gfind
mod = math.mod or mod
-- [ strsplit ]
-- Splits a string using a delimiter.
-- 'delimiter' [string] characters that will be interpreted as delimiter
-- characters (bytes) in the string.
-- 'subject' [string] String to split.
-- return: [list] a list of strings.
function pfUI.api.strsplit(delimiter, subject)
local delimiter, fields = delimiter or ":", {}
local pattern = string.format("([^%s]+)", delimiter)
string.gsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end)
return unpack(fields)
end
-- [ UnitInRange ]
-- Returns whether a party/raid member is nearby.
-- It takes care of the rangecheck module if existing.
-- unit [string] A unit to query (string, unitID)
-- return: [bool] "1" if in range otherwise "nil"
local RangeCache = {}
function pfUI.api.UnitInRange(unit)
if not UnitExists(unit) or not UnitIsVisible(unit) then
return nil
elseif CheckInteractDistance(unit, 4) then
return 1
else
return librange:UnitInSpellRange(unit)
end
end
-- [ UnitHasBuff ]
-- Returns whether a unit has the given buff or not.
-- unit [string] A unit to query (string, unitID)
-- buff [string] The texture of the buff.
-- return: [bool] true if unit has buff otherwise "nil"
function pfUI.api.UnitHasBuff(unit, buff)
local hasbuff = nil
for i=1,32 do
if UnitBuff(unit, i) == buff then
hasbuff = true
break
end
end
return hasbuff
end
-- [[ GetUnitColor ]]
-- Returns an escape string for the unit aswell as the RGB values
-- unit [string] the unitstring
-- return: [table] string, r, g, b
function pfUI.api.GetUnitColor(unitstr)
local _, class = UnitClass(unitstr)
local r, g, b = .8, .8, .8
if RAID_CLASS_COLORS[class] then
r, g, b = RAID_CLASS_COLORS[class].r, RAID_CLASS_COLORS[class].g, RAID_CLASS_COLORS[class].b
end
return string.format("|cff%02x%02x%02x", r * 255, g * 255, b * 255), r, g, b
end
-- [ strvertical ]
-- Creates vertical text using linebreaks. Multibyte char friendly.
-- 'str' [string] String to columnize.
-- return: [string] the string tranformed to a column.
function pfUI.api.strvertical(str)
local _, len = string.gsub(str,"[^\128-\193]", "")
if (len == string.len(str)) then
return string.gsub(str, "(.)", "%1\n")
else
return string.gsub(str,"([%z\1-\127\194-\244][\128-\191]*)", "%1\n")
end
end
-- [ round ]
-- Rounds a float number into specified places after comma.
-- 'input' [float] the number that should be rounded.
-- 'places' [int] amount of places after the comma.
-- returns: [float] rounded number.
function pfUI.api.round(input, places)
if not places then places = 0 end
if type(input) == "number" and type(places) == "number" then
local pow = 1
for i = 1, places do pow = pow * 10 end
return floor(input * pow + 0.5) / pow
end
end
-- [ clamp ]
-- Clamps a number between given range.
-- 'x' [number] the number that should be clamped.
-- 'min' [number] minimum value.
-- 'max' [number] maximum value.
-- returns: [number] clamped value: 'x', 'min' or 'max' value itself.
function pfUI.api.clamp(x, min, max)
if type(x) == "number" and type(min) == "number" and type(max) == "number" then
return x < min and min or x > max and max or x
else
return x
end
end
-- [ modf ]
-- Returns integral and fractional part of a number.
-- 'f' [float] the number to breakdown.
-- returns: [int],[float] whole and fractional part.
function pfUI.api.modf(f)
if modf then return modf(f) end
if f > 0 then
return math.floor(f), mod(f,1)
end
return math.ceil(f), mod(f,1)
end
-- [ GetCaptures ]
-- Returns the indexes of a given regex pattern
-- 'pat' [string] unformatted pattern
-- returns: [numbers] capture indexes
local capture_cache = {}
function pfUI.api.GetCaptures(pat)
local r = capture_cache
if not r[pat] then
for a, b, c, d, e in string.gfind(pat, gsub(pat, "%d%$", "%%(.-)$")) do
r[pat] = { a, b, c, d, e}
end
end
if not r[pat] then return nil, nil, nil, nil end
return r[pat][1], r[pat][2], r[pat][3], r[pat][4], r[pat][5]
end
-- [ SanitizePattern ]
-- Sanitizes and convert patterns into gfind compatible ones.
-- 'pattern' [string] unformatted pattern
-- returns: [string] simplified gfind compatible pattern
local sanitize_cache = {}
function pfUI.api.SanitizePattern(pattern)
if not sanitize_cache[pattern] then
local ret = pattern
-- escape brackets
ret = gsub(ret, "%((.+)%)", "%%(%1%%)")
-- remove capture indexes
ret = gsub(ret, "%d%$","")
-- catch all characters
ret = gsub(ret, "%%%a","(.+)")
sanitize_cache[pattern] = ret
end
return sanitize_cache[pattern]
end
-- [ cmatch ]
-- Same as string.match but aware of capture indexes (up to 5)
-- 'str' [string] input string that should be matched
-- 'pat' [string] unformatted pattern
-- returns: [strings] matched string in capture order
function pfUI.api.cmatch(str, pat)
-- read capture indexes
local a,b,c,d,e = GetCaptures(pat)
for va, vb, vc, vd, ve in string.gfind(str, pfUI.api.SanitizePattern(pat)) do
-- initialize return values
local ra, rb, rc, rd, re
-- put entries into the proper return values
ra = e == "1" and ve or d == "1" and vd or c == "1" and vc or b == "1" and vb or va
rb = e == "2" and ve or d == "2" and vd or c == "2" and vc or a == "2" and va or vb
rc = e == "3" and ve or d == "3" and vd or a == "3" and va or b == "3" and vb or vc
rd = e == "4" and ve or a == "4" and va or c == "4" and vc or b == "4" and vb or vd
re = a == "5" and va or d == "5" and vd or c == "5" and vc or b == "5" and vb or ve
return ra, rb, rc, rd, re
end
end
-- [ GetItemLinkByName ]
-- Returns an itemLink for the given itemname
-- 'name' [string] name of the item
-- returns: [string] entire itemLink for the given item
function pfUI.api.GetItemLinkByName(name)
for itemID = 1, 25818 do
local itemName, hyperLink, itemQuality = GetItemInfo(itemID)
if (itemName and itemName == name) then
local _, _, _, hex = GetItemQualityColor(tonumber(itemQuality))
return hex.. "|H"..hyperLink.."|h["..itemName.."]|h|r"
end
end
end
-- [ GetItemCount ]
-- Returns information about how many of a given item the player has.
-- 'itemName' [string] name of the item
-- returns: [int] the number of the given item
function pfUI.api.GetItemCount(itemName)
local count = 0
for bag = 4, 0, -1 do
for slot = 1, GetContainerNumSlots(bag) do
local _, itemCount = GetContainerItemInfo(bag, slot);
if itemCount then
local itemLink = GetContainerItemLink(bag,slot)
local _, _, itemParse = strfind(itemLink, "(%d+):")
local queryName, _, _, _, _, _ = GetItemInfo(itemParse)
if queryName and queryName ~= "" then
if queryName == itemName then
count = count + itemCount;
end
end
end
end
end
return count
end
-- [ GetBagFamily ]
-- Returns information about the type of a bag such as Soul Bags or Quivers.
-- Available bagtypes are "BAG", "KEYRING", "SOULBAG", "QUIVER" and "SPECIAL"
-- 'bag' [int] the bag id
-- returns: [string] the type of the bag, e.g "QUIVER"
function pfUI.api.GetBagFamily(bag)
if bag == -2 then return "KEYRING" end
if bag == 0 then return "BAG" end -- backpack
if bag == -1 then return "BAG" end -- bank
local _, _, id = strfind(GetInventoryItemLink("player", ContainerIDToInventoryID(bag)) or "", "item:(%d+)")
if id then
local _, _, _, _, itemType, subType = GetItemInfo(id)
local bagsubtype = L["bagtypes"][subType]
if bagsubtype == "DEFAULT" then return "BAG" end
if bagsubtype == "SOULBAG" then return "SOULBAG" end
if bagsubtype == "QUIVER" then return "QUIVER" end
if bagsubtype == nil then return "SPECIAL" end
end
return nil
end
-- [ Abbreviate ]
-- Abbreviates a number from 1234 to 1.23k
-- 'number' [number] the number that should be abbreviated
-- 'returns: [string] the abbreviated value
function pfUI.api.Abbreviate(number)
if pfUI_config.unitframes.abbrevnum == "1" then
if number > 1000000 then
return pfUI.api.round(number/1000000,2) .. "m"
elseif number > 1000 then
return pfUI.api.round(number/1000,2) .. "k"
end
end
return number
end
-- [ SendChatMessageWide ]
-- Sends a message to widest audience the player can broadcast to
-- 'msg' [string] the message to send
function pfUI.api.SendChatMessageWide(msg)
local channel = "SAY"
if UnitInRaid("player") then
if ( IsRaidLeader() or IsRaidOfficer() ) then
channel = "RAID_WARNING"
else
channel = "RAID"
end
elseif UnitExists("party1") then
channel = "PARTY"
end
SendChatMessage(msg,channel)
end
-- [ GroupInfoByName ]
-- Gets the unit id by name
-- 'name' [string] party or raid member
-- 'group' [string] "raid" or "party"
-- returns: [table] {name='name',unitId='unitId',Id=Id,lclass='lclass',class='class'}
do -- create a scope so we don't have to worry about upvalue collisions
local party, raid, unitinfo = {}, {}, {}
party[0] = "player" -- fake unit
for i=1, MAX_PARTY_MEMBERS do
party[i] = "party"..i
end
for i=1, MAX_RAID_MEMBERS do
raid[i] = "raid"..i
end
function pfUI.api.GroupInfoByName(name,group)
unitinfo = pfUI.api.wipe(unitinfo)
if group == "party" then
for i=0, MAX_PARTY_MEMBERS do
local unitName = UnitName(party[i])
if unitName == name then
local lclass,class = UnitClass(party[i])
if not (lclass and class) then
lclass,class = _G.UNKNOWN, "UNKNOWN"
end
unitinfo.name,unitinfo.unitId,unitinfo.Id,unitinfo.lclass,unitinfo.class =
unitName,party[i],i,lclass,class
return unitinfo
end
end
elseif group == "raid" then
for i=1, MAX_RAID_MEMBERS do
local unitName = UnitName(raid[i])
if unitName == name then
local lclass,class = UnitClass(raid[i])
if not (lclass and class) then
lclass,class = _G.UNKNOWN, "UNKNOWN"
end
unitinfo.name,unitinfo.unitId,unitinfo.Id,unitinfo.lclass,unitinfo.class =
unitName,raid[i],i,lclass,class
return unitinfo
end
end
end
-- fallback for GetMasterLootCandidate not updating immediately for leavers
unitinfo.lclass,unitinfo.class = _G.UNKNOWN, "UNKNOWN"
return unitinfo
end
end
-- [ HookAddonOrVariable ]
-- Sets a function to be called automatically once an addon gets loaded
-- 'addon' [string] addon or variable name
-- 'func' [function] function that should run
function pfUI.api.HookAddonOrVariable(addon, func)
local lurker = CreateFrame("Frame", nil)
lurker.func = func
lurker:RegisterEvent("ADDON_LOADED")
lurker:RegisterEvent("VARIABLES_LOADED")
lurker:RegisterEvent("PLAYER_ENTERING_WORLD")
lurker:SetScript("OnEvent",function()
if IsAddOnLoaded(addon) or _G[addon] then
this:func()
this:UnregisterAllEvents()
end
end)
end
-- [ QueueFunction ]
-- Add functions to a FIFO queue for execution after a short delay.
-- '...' [vararg] function, [arguments]
local timer
function pfUI.api.QueueFunction(...)
if not (timer) then
timer = CreateFrame("frame")
timer.queue = {}
timer.interval = TOOLTIP_UPDATE_TIME
timer.DeQueue = function()
local item = table.remove(timer.queue,1)
if (item) then
item[1](unpack(item[2]))
end
if table.getn(timer.queue) == 0 then
timer:Hide() -- no need to run the OnUpdate when the queue is empty
end
end
timer:SetScript("OnUpdate",function()
this.sinceLast = (this.sinceLast or 0) + arg1
while (this.sinceLast > this.interval) do
this.DeQueue()
this.sinceLast = this.sinceLast - this.interval
end
end)
end
assert(type(arg[1])=="function","QueueFunction: arg1 is not a function")
local func = table.remove(arg, 1)
table.insert(timer.queue,{func,arg})
timer:Show() -- start the OnUpdate
end
-- [ Create Gold String ]
-- Transforms a amount of copper into a fully fledged gold string
-- 'money' [int] the amount of coppy (GetMoney())
-- return: [string] a colorized string which is split into
-- gold,silver and copper values.
function pfUI.api.CreateGoldString(money)
if type(money) ~= "number" then return "-" end
local gold = floor(money/ 100 / 100)
local silver = floor(mod((money/100),100))
local copper = floor(mod(money,100))
local string = ""
if gold > 0 then string = string .. "|cffffffff" .. gold .. "|cffffd700g" end
if silver > 0 then string = string .. "|cffffffff " .. silver .. "|cffc7c7cfs" end
string = string .. "|cffffffff " .. copper .. "|cffeda55fc"
return string
end
-- [ Enable Movable ]
-- Set all necessary functions to make a already existing frame movable.
-- 'name' [frame/string] Name of the Frame that should be movable
-- 'addon' [string] Addon that must be loaded before being able to access the frame
-- 'blacklist' [table] A list of frames that should be deactivated for mouse usage
function pfUI.api.EnableMovable(name, addon, blacklist)
if addon then
local scan = CreateFrame("Frame")
scan:RegisterEvent("ADDON_LOADED")
scan:SetScript("OnEvent", function()
if arg1 == addon then
local frame = _G[name]
if blacklist then
for _, disable in pairs(blacklist) do
_G[disable]:EnableMouse(false)
end
end
frame:SetMovable(true)
frame:EnableMouse(true)
frame:SetScript("OnMouseDown",function()
this:StartMoving()
end)
frame:SetScript("OnMouseUp",function()
this:StopMovingOrSizing()
end)
this:UnregisterAllEvents()
end
end)
else
if blacklist then
for _, disable in pairs(blacklist) do
_G[disable]:EnableMouse(false)
end
end
local frame = name
if type(name) == "string" then frame = _G[name] end
frame:SetMovable(true)
frame:EnableMouse(true)
frame:SetScript("OnMouseDown",function()
this:StartMoving()
end)
frame:SetScript("OnMouseUp",function()
this:StopMovingOrSizing()
end)
end
end
-- [ Copy Table ]
-- By default a table assignment only will be a reference instead of a copy.
-- This is used to create a replicate of the actual table.
-- 'src' [table] the table that should be copied.
-- return: [table] the replicated table.
function pfUI.api.CopyTable(src)
local lookup_table = {}
local function _copy(src)
if type(src) ~= "table" then
return src
elseif lookup_table[src] then
return lookup_table[src]
end
local new_table = {}
lookup_table[src] = new_table
for index, value in pairs(src) do
new_table[_copy(index)] = _copy(value)
end
return setmetatable(new_table, getmetatable(src))
end
return _copy(src)
end
-- [ Wipe Table ]
-- Empties a table and returns it
-- 'src' [table] the table that should be emptied.
-- return: [table] the emptied table.
function pfUI.api.wipe(src)
-- notes: table.insert, table.remove will have undefined behavior
-- when used on tables emptied this way because Lua removes nil
-- entries from tables after an indeterminate time.
-- Instead of table.insert(t,v) use t[table.getn(t)+1]=v as table.getn collapses nil entries.
-- There are no issues with hash tables, t[k]=v where k is not a number behaves as expected.
local mt = getmetatable(src) or {}
if mt.__mode == nil or mt.__mode ~= "kv" then
mt.__mode = "kv"
src=setmetatable(src,mt)
end
for k in pairs(src) do
src[k] = nil
end
return src
end
-- [ Update Movable ]
-- Loads and update the configured position of the specified frame.
-- It also creates an entry in the movables table.
-- 'frame' [frame] the frame that should be updated.
-- 'init' [bool] treats the current position as initial data
function pfUI.api.UpdateMovable(frame, init)
local name = frame:GetName()
if pfUI_config.global.offscreen == "0" then
frame:SetClampedToScreen(true)
end
if not pfUI.movables[name] then
pfUI.movables[name] = frame
end
-- update position data
if not frame.posdata or init then
frame.posdata = { scale = frame:GetScale(), pos = {} }
for i=1,frame:GetNumPoints() do
frame.posdata.pos[i] = { frame:GetPoint(i) }
end
end
if pfUI_config["position"][frame:GetName()] then
if pfUI_config["position"][frame:GetName()]["scale"] then
frame:SetScale(pfUI_config["position"][frame:GetName()].scale)
end
if pfUI_config["position"][frame:GetName()]["xpos"] then
frame:ClearAllPoints()
frame:SetPoint("TOPLEFT", pfUI_config["position"][frame:GetName()].xpos, pfUI_config["position"][frame:GetName()].ypos)
end
elseif frame.posdata and frame.posdata.pos[1] then
frame:ClearAllPoints()
frame:SetScale(frame.posdata.scale)
for id, point in pairs(frame.posdata.pos) do
frame:SetPoint(unpack(point))
end
end
end
-- [ Remove Movable ]
-- Removes a Frame from the movable list.
-- 'frame' [frame] the frame that should be removed.
function pfUI.api.RemoveMovable(frame)
local name = frame:GetName()
pfUI.movables[name] = nil
end
-- [ Load Movable ]
-- Loads the positions of a Frame.
-- 'frame' [frame] the frame that should be positioned.
function pfUI.api.LoadMovable(frame)
if pfUI_config["position"][frame:GetName()] then
if pfUI_config["position"][frame:GetName()]["scale"] then
frame:SetScale(pfUI_config["position"][frame:GetName()].scale)
end
if pfUI_config["position"][frame:GetName()]["xpos"] then
frame:ClearAllPoints()
frame:SetPoint("TOPLEFT", pfUI_config["position"][frame:GetName()].xpos, pfUI_config["position"][frame:GetName()].ypos)
end
end
end
-- [ Save Movable ]
-- Save the positions of a Frame.
-- 'frame' [frame] the frame that should be saved.
function pfUI.api.SaveMovable(frame)
local _, _, _, xpos, ypos = frame:GetPoint()
if not C.position[frame:GetName()] then
C.position[frame:GetName()] = {}
end
C.position[frame:GetName()]["xpos"] = round(xpos)
C.position[frame:GetName()]["ypos"] = round(ypos)
end
-- [ SetAutoPoint ]
-- Automatically places the frame according to screen position of the parent.
-- 'frame' [frame] the frame that should be moved.
-- 'parent' [frame] the frame's anchor point
-- 'spacing' [number] the padding that should be used between the
-- frame and its parent frame
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()
if not x or not y then return end
local off = spacing or 0
frame:ClearAllPoints()
if x < a and y > c then
-- TOPLEFT
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)
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)
elseif x < a and y < d then
-- 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
-- [ GetDefaultColors ]
-- Queries the pfUI setting strings and extract its color codes
-- returns r,g,b,a
local color_cache = {}
function pfUI.api.GetStringColor(colorstr)
if not color_cache[colorstr] then
local r, g, b, a = pfUI.api.strsplit(",", colorstr)
color_cache[colorstr] = { r, g, b, a }
end
return unpack(color_cache[colorstr])
end
-- [ Create Backdrop ]
-- Creates a pfUI compatible frame as backdrop element
-- 'f' [frame] the frame which should get a backdrop.
-- 'inset' [int] backdrop inset, defaults to border size.
-- 'legacy' [bool] use legacy backdrop instead of creating frames.
-- 'transp' [number] set default transparency
function pfUI.api.CreateBackdrop(f, inset, legacy, transp, backdropSetting)
-- exit if now frame was given
if not f then return end
-- use default inset if nothing is given
local border = inset
if not border then
border = tonumber(pfUI_config.appearance.border.default)
end
local br, bg, bb, ba = pfUI.api.GetStringColor(pfUI_config.appearance.border.background)
local er, eg, eb, ea = pfUI.api.GetStringColor(pfUI_config.appearance.border.color)
if transp and transp < tonumber(ba) then ba = transp end
-- use legacy backdrop handling
if legacy then
local backdrop = pfUI.backdrop
if backdropSetting then backdrop = backdropSetting end
f:SetBackdrop(backdrop)
f:SetBackdropColor(br, bg, bb, ba)
f:SetBackdropBorderColor(er, eg, eb , ea)
else
-- increase clickable area if available
if f.SetHitRectInsets then
f:SetHitRectInsets(-border,-border,-border,-border)
end
-- use new backdrop behaviour
if not f.backdrop then
f:SetBackdrop(nil)
local backdrop = pfUI.backdrop
local b = CreateFrame("Frame", nil, f)
if tonumber(border) > 1 then
local border = tonumber(border) - 1
backdrop.insets = {left = -1, right = -1, top = -1, bottom = -1}
b:SetPoint("TOPLEFT", f, "TOPLEFT", -border, border)
b:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", border, -border)
else
local border = tonumber(border)
backdrop.insets = {left = 0, right = 0, top = 0, bottom = 0}
b:SetPoint("TOPLEFT", f, "TOPLEFT", -border, border)
b:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", border, -border)
end
local level = f:GetFrameLevel()
if level < 1 then
b:SetFrameLevel(level)
else
b:SetFrameLevel(level - 1)
end
f.backdrop = b
b:SetBackdrop(backdrop)
end
local b = f.backdrop
b:SetBackdropColor(br, bg, bb, ba)
b:SetBackdropBorderColor(er, eg, eb , ea)
end
-- add shadow
if not f.backdrop_shadow and pfUI_config.appearance.border.shadow == "1" then
local size = 8
local inset = size-1
local anchor = f.backdrop or f
local intensity = tonumber(pfUI_config.appearance.border.shadow_intensity)
f.backdrop_shadow = CreateFrame("Frame", nil, anchor)
f.backdrop_shadow:SetFrameStrata("BACKGROUND")
f.backdrop_shadow:SetFrameLevel(1)
f.backdrop_shadow:SetPoint("TOPLEFT", anchor, "TOPLEFT", -inset, inset)
f.backdrop_shadow:SetPoint("BOTTOMRIGHT", anchor, "BOTTOMRIGHT", inset, -inset)
f.backdrop_shadow:SetBackdrop({
edgeFile = "Interface\\AddOns\\pfUI\\img\\glow2", edgeSize = size,
insets = {left = 0, right = 0, top = 0, bottom = 0},
})
f.backdrop_shadow:SetBackdropBorderColor(0,0,0,intensity)
end
end
-- [ Bar Layout Options ] --
-- 'barsize' size of bar in number of buttons
-- returns: array of options as strings for pfUI.gui.bar
function pfUI.api.BarLayoutOptions(barsize)
assert(barsize > 0 and barsize <= NUM_ACTIONBAR_BUTTONS,"BarLayoutOptions: barsize "..tostring(barsize).." is invalid")
local options = {}
for i,layout in ipairs(pfGridmath[barsize]) do
options[i] = string.format("%d x %d",layout[1],layout[2])
end
return options
end
-- [ Bar Layout Formfactor ] --
-- 'option' string option as used in pfUI_config.bars[bar].option
-- returns: integer formfactor
local formfactors = {} -- we'll use memoization so we only compute once, then lookup.
setmetatable(formfactors, {__mode = "v"}) -- weak table so values not referenced are collected on next gc
function pfUI.api.BarLayoutFormfactor(option)
if formfactors[option] then
return formfactors[option]
else
for barsize,_ in ipairs(pfGridmath) do
local options = pfUI.api.BarLayoutOptions(barsize)
for i,opt in ipairs(options) do
if opt == option then
formfactors[option] = i
return formfactors[option]
end
end
end
end
end
-- [ Bar Layout Size ] --
-- 'bar' frame reference,
-- 'barsize' integer number of buttons,
-- 'formfactor' string formfactor in cols x rows,
-- 'visiblesize' integer buttons actually spawned
function pfUI.api.BarLayoutSize(bar,barsize,formfactor,iconsize,bordersize,visiblesize)
assert(barsize > 0 and barsize <= NUM_ACTIONBAR_BUTTONS,"BarLayoutSize: barsize "..tostring(barsize).." is invalid")
local formfactor = pfUI.api.BarLayoutFormfactor(formfactor)
local cols, rows = unpack(pfGridmath[barsize][formfactor])
if (visiblesize) and (visiblesize < barsize) then
cols = math.min(cols,visiblesize)
rows = math.min(math.ceil(visiblesize/cols),visiblesize)
end
local width = (iconsize + bordersize*3) * cols - bordersize
local height = (iconsize + bordersize*3) * rows - bordersize
bar._size = {width,height}
return bar._size
end
-- [ Bar Button Anchor ] --
-- 'button' frame reference
-- 'basename' name of button frame without index
-- 'buttonindex' index number of button on bar
-- 'formfactor' string formfactor in cols x rows
function pfUI.api.BarButtonAnchor(button,basename,buttonindex,barsize,formfactor,iconsize,bordersize)
assert(barsize > 0 and barsize <= NUM_ACTIONBAR_BUTTONS,"BarButtonAnchor: barsize "..tostring(barsize).." is invalid")
local formfactor = pfUI.api.BarLayoutFormfactor(formfactor)
local parent = button:GetParent()
local cols, rows = unpack(pfGridmath[barsize][formfactor])
if buttonindex == 1 then
button._anchor = {"TOPLEFT", parent, "TOPLEFT", bordersize, -bordersize}
else
local col = buttonindex-((math.ceil(buttonindex/cols)-1)*cols)
button._anchor = col==1 and {"TOP",getglobal(basename..(buttonindex-cols)),"BOTTOM",0,-(bordersize*3)} or {"LEFT",getglobal(basename..(buttonindex-1)),"RIGHT",(bordersize*3),0}
end
return button._anchor
end
-- [ Create Autohide ] --
-- 'frame' the frame that should be hidden
function pfUI.api.CreateAutohide(frame)
if not frame then return end
frame.hover = CreateFrame("Frame", frame:GetName() .. "Autohide", frame)
frame.hover:SetParent(frame)
frame.hover:SetAllPoints(frame)
frame.hover.parent = frame
frame.hover:RegisterEvent("PLAYER_LEAVING_WORLD")
frame.hover:SetScript("OnEvent", function()
this:Hide()
end)
frame.hover:SetScript("OnUpdate", function()
if MouseIsOver(this, 10, -10, -10, 10) then
this.activeTo = GetTime() + tonumber(pfUI_config.bars.hide_time)
this.parent:SetAlpha(1)
elseif this.activeTo then
if this.activeTo < GetTime() and this.parent:GetAlpha() > 0 then
this.parent:SetAlpha(this.parent:GetAlpha() - 0.1)
end
else
this.activeTo = GetTime() + tonumber(pfUI_config.bars.hide_time)
end
end)
end
-- [ GetColoredTime ] --
-- 'remaining' the time in seconds that should be converted
-- return a colored string including a time unit (m/h/d)
function pfUI.api.GetColoredTimeString(remaining)
if not remaining then return "" end
if remaining > 99 * 60 * 60 then
local r,g,b,a = pfUI.api.GetStringColor(C.appearance.cd.daycolor)
return "|cff" .. string.format("%02x%02x%02x", r*255, g*255, b*255) .. round(remaining / 60 / 60 / 24) .. "|rd"
elseif remaining > 99 * 60 then
local r,g,b,a = pfUI.api.GetStringColor(C.appearance.cd.hourcolor)
return "|cff" .. string.format("%02x%02x%02x", r*255, g*255, b*255) .. round(remaining / 60 / 60) .. "|rh"
elseif remaining > 99 then
local r,g,b,a = pfUI.api.GetStringColor(C.appearance.cd.minutecolor)
return "|cff" .. string.format("%02x%02x%02x", r*255, g*255, b*255) .. round(remaining / 60) .. "|rm"
elseif remaining <= 5 then
local r,g,b,a = pfUI.api.GetStringColor(C.appearance.cd.lowcolor)
return "|cff" .. string.format("%02x%02x%02x", r*255, g*255, b*255) .. string.format("%.1f", round(remaining,1))
elseif remaining >= 0 then
local r, g, b, a = pfUI.api.GetStringColor(C.appearance.cd.normalcolor)
return "|cff" .. string.format("%02x%02x%02x", r*255, g*255, b*255) .. round(remaining)
else
return ""
end
end
-- [ GetColorGradient ] --
-- 'perc' percentage (0-1)
-- return r,g,b and hexcolor
local gradientcolors = {}
function pfUI.api.GetColorGradient(perc)
-- fallback to black on bad numbers
if perc < 0 or perc > 1 then return 0,0,0,"|cff000000" end
if not gradientcolors[perc] then
local r1, g1, b1, r2, g2, b2
if perc <= 0.5 then
perc = perc * 2
r1, g1, b1 = 1, 0, 0
r2, g2, b2 = 1, 1, 0
else
perc = perc * 2 - 1
r1, g1, b1 = 1, 1, 0
r2, g2, b2 = 0, 1, 0
end
local r = r1 + (r2 - r1)*perc
local g = g1 + (g2 - g1)*perc
local b = b1 + (b2 - b1)*perc
local h = string.format("|cff%02x%02x%02x", r*255, g*255, b*255)
gradientcolors[perc] = {}
gradientcolors[perc].r = r
gradientcolors[perc].g = g
gradientcolors[perc].b = b
gradientcolors[perc].h = h
end
return gradientcolors[perc].r,
gradientcolors[perc].g,
gradientcolors[perc].b,
gradientcolors[perc].h
end