Files
NoM0Re 5eb2db89f7 (fix/texture): Desaturate state lost on SetTexture
Fixes #93
Also normalize SetDesaturated calls to use booleans instead of numeric flags.

(cherry picked from commit 47dce3a9d5ba23c170f002b323a11ed2a5c5e206)
2026-02-16 11:43:50 -07:00

684 lines
19 KiB
Lua

if not WeakAuras.IsLibsOK() then return end
local AddonName = ...
local Private = select(2, ...)
local L = WeakAuras.L
local MSQ, MSQ_Version = LibStub("Masque", true);
if MSQ then
if MSQ_Version <= 80100 then
MSQ = nil
WeakAuras.prettyPrint(L["Please upgrade your Masque version"])
else
MSQ:AddType("WA_Aura", {"Icon", "Cooldown"})
end
end
-- WoW API
local _G = _G
local default = {
icon = true,
desaturate = false,
iconSource = -1,
progressSource = {-1, "" },
adjustedMax = "",
adjustedMin = "",
inverse = false,
width = 64,
height = 64,
color = {1, 1, 1, 1},
selfPoint = "CENTER",
anchorPoint = "CENTER",
anchorFrameType = "SCREEN",
xOffset = 0,
yOffset = 0,
zoom = 0,
keepAspectRatio = false,
frameStrata = 1,
cooldown = true,
cooldownEdge = false
};
Private.regionPrototype.AddProgressSourceToDefault(default)
Private.regionPrototype.AddAlphaToDefault(default);
local screenWidth, screenHeight = math.ceil(GetScreenWidth() / 20) * 20, math.ceil(GetScreenHeight() / 20) * 20;
local properties = {
desaturate = {
display = L["Desaturate"],
setter = "SetDesaturated",
type = "bool",
},
width = {
display = L["Width"],
setter = "SetRegionWidth",
type = "number",
min = 1,
softMax = screenWidth,
bigStep = 1,
default = 32
},
height = {
display = L["Height"],
setter = "SetRegionHeight",
type = "number",
min = 1,
softMax = screenHeight,
bigStep = 1,
default = 32
},
color = {
display = L["Color"],
setter = "Color",
type = "color"
},
inverse = {
display = L["Inverse"],
setter = "SetInverse",
type = "bool"
},
cooldownEdge = {
display = { L["Cooldown"], L["Edge"]},
setter = "SetCooldownEdge",
type = "bool",
},
zoom = {
display = L["Zoom"],
setter = "SetZoom",
type = "number",
min = 0,
max = 1,
step = 0.01,
default = 0,
isPercent = true
},
iconSource = {
display = {L["Icon"], L["Source"]},
setter = "SetIconSource",
type = "list",
values = {}
},
displayIcon = {
display = {L["Icon"], L["Manual"]},
setter = "SetIcon",
type = "icon",
}
};
Private.regionPrototype.AddProperties(properties, default);
local function GetProperties(data)
local result = CopyTable(properties)
result.iconSource.values = Private.IconSources(data)
result.progressSource.values = Private.GetProgressSourcesForUi(data)
return result
end
local function GetTexCoord(region, texWidth, aspectRatio, xOffset, yOffset)
region.currentCoord = region.currentCoord or {}
local usesMasque = false
if region.MSQGroup then
local db = region.MSQGroup.db
if db and not db.Disabled then
usesMasque = true
region.currentCoord[1], region.currentCoord[2], region.currentCoord[3], region.currentCoord[4],
region.currentCoord[5], region.currentCoord[6], region.currentCoord[7], region.currentCoord[8]
= region.icon:GetTexCoord()
end
end
if (not usesMasque) then
region.currentCoord[1], region.currentCoord[2], region.currentCoord[3], region.currentCoord[4],
region.currentCoord[5], region.currentCoord[6], region.currentCoord[7], region.currentCoord[8]
= 0, 0, 0, 1, 1, 0, 1, 1;
end
local xRatio = aspectRatio < 1 and aspectRatio or 1;
local yRatio = aspectRatio > 1 and 1 / aspectRatio or 1;
for i, coord in ipairs(region.currentCoord) do
if(i % 2 == 1) then
region.currentCoord[i] = (coord - 0.5) * texWidth * xRatio + 0.5 - xOffset;
else
region.currentCoord[i] = (coord - 0.5) * texWidth * yRatio + 0.5 - yOffset;
end
end
return unpack(region.currentCoord)
end
local function AnchorSubRegion(self, subRegion, anchorType, anchorPoint, selfPoint, anchorXOffset, anchorYOffset)
if type(anchorPoint) == "string" and anchorPoint:sub(1, 4) == "sub." then
Private.regionPrototype.AnchorSubRegion(self, subRegion, anchorType, anchorPoint, selfPoint, anchorXOffset, anchorYOffset)
return
end
if anchorType == "area" then
Private.regionPrototype.AnchorSubRegion(selfPoint == "region" and self or self.icon,
subRegion, anchorType, anchorPoint, selfPoint, anchorXOffset, anchorYOffset)
else
subRegion:ClearAllPoints()
anchorPoint = anchorPoint or "CENTER"
local anchorRegion = self.icon
if anchorPoint:sub(1, 6) == "INNER_" then
if not self.inner then
self.inner = CreateFrame("Frame", nil, self)
self.inner:SetPoint("CENTER")
self.UpdateInnerOuterSize()
end
anchorRegion = self.inner
anchorPoint = anchorPoint:sub(7)
elseif anchorPoint:sub(1, 6) == "OUTER_" then
if not self.outer then
self.outer = CreateFrame("Frame", nil, self)
self.outer:SetPoint("CENTER")
self.UpdateInnerOuterSize()
end
anchorRegion = self.outer
anchorPoint = anchorPoint:sub(7)
end
anchorXOffset = anchorXOffset or 0
anchorYOffset = anchorYOffset or 0
if not Private.point_types[selfPoint] then
selfPoint = "CENTER"
end
if not Private.point_types[anchorPoint] then
anchorPoint = "CENTER"
end
subRegion:SetPoint(selfPoint, anchorRegion, anchorPoint, anchorXOffset, anchorYOffset)
end
end
local function setDesaturated(self, desaturated, ...)
self.isDesaturated = desaturated and 1 or 0
return self._SetDesaturated(self, desaturated, ...)
end
local function setTexture(self, ...)
local apply = self._SetTexture(self, ...)
if self.isDesaturated ~= nil then
self:_SetDesaturated(self.isDesaturated == 1)
end
return apply
end
local function cooldown_onUpdate(self, e)
if self._duration then
if self._delay then
if not self._paused then
self._delay = self._delay - e
end
self:_SetCooldown(GetTime(), self._duration)
if self._delay <= 0 then
self._delay = nil
if not self._paused then
self:SetScript("OnUpdate", nil)
end
end
return
end
if self._paused then
self:_SetCooldown(GetTime() - self._paused, self._duration)
end
end
end
local function cooldown_pause(self)
if not self._paused then
self._paused = GetTime() - (self._start or 0)
self:SetScript("OnUpdate", cooldown_onUpdate)
end
end
local function cooldown_resume(self)
if self._paused then
self._start = GetTime() - self._paused
end
self._paused = nil
self:SetScript("OnUpdate", nil)
end
local function cooldown_setCooldown(self, start, duration, ...)
if self._start == start and self._duration == duration then
return
end
self._start = start
self._duration = duration
local getTime = GetTime()
local delay = start - getTime
if delay > 0 then
self._delay = delay
self:SetScript("OnUpdate", cooldown_onUpdate)
end
if self._paused then
self._paused = getTime - self._start
end
return self._SetCooldown(self, math.min(getTime, start), duration, ...)
end
local function create(parent, data)
local font = "GameFontHighlight";
local region = CreateFrame("Frame", nil, parent);
region.regionType = "icon"
region:SetMovable(true);
region:SetResizable(true);
region:SetMinResize(1, 1);
function region.UpdateInnerOuterSize()
local width = region.width * math.abs(region.scalex);
local height = region.height * math.abs(region.scaley);
local iconWidth
local iconHeight
if MSQ then
iconWidth = region.button:GetWidth()
iconHeight = region.button:GetHeight()
else
iconWidth = region:GetWidth()
iconHeight = region:GetHeight()
end
if region.inner then
region.inner:SetSize(iconWidth - 0.2 * width, iconHeight - 0.2 * height)
end
if region.outer then
region.outer:SetSize(iconWidth + 0.1 * width, iconHeight + 0.1 * height)
end
end
local button
if MSQ then
button = CreateFrame("Button", nil, region)
button.data = data
region.button = button;
button:EnableMouse(false);
button:Disable();
button:SetAllPoints();
end
local icon = region:CreateTexture(nil, "BACKGROUND");
if MSQ then
icon:SetAllPoints(button);
button:SetScript("OnSizeChanged", region.UpdateInnerOuterSize);
else
icon:SetAllPoints(region);
region:SetScript("OnSizeChanged", region.UpdateInnerOuterSize);
end
region.icon = icon;
icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark");
icon._SetDesaturated = icon.SetDesaturated
icon.SetDesaturated = setDesaturated
icon._SetTexture = icon.SetTexture
icon.SetTexture = setTexture
--This section creates a unique frame id for the cooldown frame so that it can be created with a global reference
--The reason is so that WeakAuras cooldown frames can interact properly with OmniCC
-- (i.e., put on its ignore list for timer overlays)
local id = data.id;
local frameId = id:lower():gsub(" ", "_");
if(_G["WeakAurasCooldown"..frameId]) then
local baseFrameId = frameId;
local num = 2;
while(_G["WeakAurasCooldown"..frameId]) do
frameId = baseFrameId..num;
num = num + 1;
end
end
region.frameId = frameId;
local cooldown = CreateFrame("Cooldown", "WeakAurasCooldown"..frameId, region, "CooldownFrameTemplate");
region.cooldown = cooldown;
cooldown:SetAllPoints(icon);
cooldown._SetCooldown = cooldown.SetCooldown;
cooldown.SetCooldown = cooldown_setCooldown;
cooldown.Pause = cooldown_pause;
cooldown.Resume = cooldown_resume;
local SetFrameLevel = region.SetFrameLevel;
function region.SetFrameLevel(self, level)
SetFrameLevel(region, level);
cooldown:SetFrameLevel(level);
if button then
button:SetFrameLevel(level);
end
end
Private.regionPrototype.create(region);
region.AnchorSubRegion = AnchorSubRegion
return region;
end
local function modify(parent, region, data)
-- Legacy members stacks and text2
region.stacks = nil
region.text2 = nil
Private.regionPrototype.modify(parent, region, data);
local button, icon, cooldown = region.button, region.icon, region.cooldown;
region.iconSource = data.iconSource
region.displayIcon = data.displayIcon
if MSQ then
local masqueId = data.id:lower():gsub(" ", "_");
if region.masqueId ~= masqueId then
region.masqueId = masqueId
region.MSQGroup = MSQ:Group("WeakAuras", region.masqueId, data.uid);
region.MSQGroup:SetName(data.id)
region.MSQGroup:AddButton(button, {Icon = icon, Cooldown = cooldown}, "WA_Aura", true);
button.data = data
end
end
function region:UpdateSize()
local width = region.width * math.abs(region.scalex);
local height = region.height * math.abs(region.scaley);
region:SetWidth(width);
region:SetHeight(height);
if MSQ then
button:SetWidth(width);
button:SetHeight(height);
button:SetAllPoints();
end
region:UpdateTexCoords();
end
function region:UpdateTexCoords()
local mirror_h = region.scalex < 0;
local mirror_v = region.scaley < 0;
local texWidth = 1 - 0.5 * region.zoom;
local aspectRatio
if not region.keepAspectRatio then
aspectRatio = 1;
else
local width = region.width * math.abs(region.scalex);
local height = region.height * math.abs(region.scaley);
if width == 0 or height == 0 then
aspectRatio = 1;
else
aspectRatio = width / height;
end
end
if region.MSQGroup then
if region.MSQGroup.ReSkin then
region.MSQGroup:ReSkin(button)
else
region.MSQGroup:RemoveButton(button)
region.MSQGroup:AddButton(button, {Icon = icon, Cooldown = cooldown}, "WA_Aura", true)
end
end
local ulx, uly, llx, lly, urx, ury, lrx, lry
= GetTexCoord(region, texWidth, aspectRatio, region.texXOffset, region.texYOffset and -region.texYOffset)
if(mirror_h) then
if(mirror_v) then
icon:SetTexCoord(lrx, lry, urx, ury, llx, lly, ulx, uly)
else
icon:SetTexCoord(urx, ury, lrx, lry, ulx, uly, llx, lly)
end
else
if(mirror_v) then
icon:SetTexCoord(llx, lly, ulx, uly, lrx, lry, urx, ury)
else
icon:SetTexCoord(ulx, uly, llx, lly, urx, ury, lrx, lry)
end
end
end
region.width = data.width;
region.height = data.height;
region.scalex = 1;
region.scaley = 1;
region.keepAspectRatio = data.keepAspectRatio;
region.zoom = data.zoom;
region.texXOffset = data.texXOffset or 0
region.texYOffset = data.texYOffset or 0
region:UpdateSize()
icon:SetDesaturated(data.desaturate);
local tooltipType = Private.CanHaveTooltip(data);
if(tooltipType and data.useTooltip) then
if not region.tooltipFrame then
region.tooltipFrame = CreateFrame("Frame", nil, region);
region.tooltipFrame:SetAllPoints(region);
region.tooltipFrame:SetScript("OnEnter", function()
Private.ShowMouseoverTooltip(region, region);
end);
region.tooltipFrame:SetScript("OnLeave", Private.HideTooltip);
end
region.tooltipFrame:EnableMouse(true);
elseif region.tooltipFrame then
region.tooltipFrame:EnableMouse(false);
end
function region:SetInverse(inverse)
if region.inverseDirection == inverse then
return
end
region.inverseDirection = inverse
region:UpdateEffectiveInverse()
end
function region:UpdateEffectiveInverse()
-- If cooldown.inverse == false then effectiveReverse = not inverse
-- If cooldown.inverse == true then effectiveReverse = inverse
local effectiveReverse = not region.inverseDirection == not cooldown.inverse
local hasChanged = cooldown:GetReverse() ~= effectiveReverse
if hasChanged then
cooldown:SetReverse(effectiveReverse)
end
if (cooldown.expirationTime and cooldown.duration and cooldown:IsShown()) then
if hasChanged then
-- WORKAROUND SetReverse not applying until next frame
cooldown:SetCooldown(0, 0)
end
cooldown:SetCooldown(cooldown.expirationTime - cooldown.duration,
cooldown.duration)
end
end
region:SetInverse(data.inverse)
function region:Color(r, g, b, a)
region.color_r = r;
region.color_g = g;
region.color_b = b;
region.color_a = a;
if (r or g or b) then
a = a or 1;
end
icon:SetVertexColor(region.color_anim_r or r, region.color_anim_g or g,
region.color_anim_b or b, region.color_anim_a or a)
if region.button then
region.button:SetAlpha(region.color_anim_a or a or 1);
end
end
function region:ColorAnim(r, g, b, a)
region.color_anim_r = r;
region.color_anim_g = g;
region.color_anim_b = b;
region.color_anim_a = a;
if (r or g or b) then
a = a or 1;
end
icon:SetVertexColor(r or region.color_r, g or region.color_g, b or region.color_b, a or region.color_a);
if MSQ then
region.button:SetAlpha(a or region.color_a or 1);
end
end
function region:GetColor()
return region.color_r or data.color[1], region.color_g or data.color[2],
region.color_b or data.color[3], region.color_a or data.color[4];
end
region:Color(data.color[1], data.color[2], data.color[3], data.color[4]);
function region:SetIcon(iconPath)
if self.displayIcon == iconPath then
return
end
self.displayIcon = iconPath
self:UpdateIcon()
end
function region:SetIconSource(source)
if self.iconSource == source then
return
end
self.iconSource = source
self:UpdateIcon()
end
function region:UpdateIcon()
local iconPath
if self.iconSource == -1 then
iconPath = self.state.icon
elseif self.iconSource == 0 then
iconPath = self.displayIcon
else
local triggernumber = self.iconSource
if triggernumber and self.states[triggernumber] then
iconPath = self.states[triggernumber].icon
end
end
iconPath = iconPath or self.displayIcon or "Interface\\Icons\\INV_Misc_QuestionMark"
Private.SetTextureOrSpellTexture(icon, iconPath)
end
function region:Scale(scalex, scaley)
if region.scalex == scalex and region.scaley == scaley then
return
end
region.scalex = scalex;
region.scaley = scaley;
region:UpdateSize();
end
function region:SetDesaturated(b)
icon:SetDesaturated(b);
end
function region:SetRegionWidth(width)
region.width = width
region:UpdateSize();
end
function region:SetRegionHeight(height)
region.height = height
region:UpdateSize();
end
function region:SetCooldownEdge(cooldownEdge)
region.cooldownEdge = cooldownEdge;
cooldown:SetDrawEdge(cooldownEdge);
end
region:SetCooldownEdge(data.cooldownEdge)
function region:SetZoom(zoom)
region.zoom = zoom;
region:UpdateTexCoords();
end
cooldown.expirationTime = nil;
cooldown.duration = nil;
cooldown:Hide()
if(data.cooldown) then
function region:UpdateValue()
cooldown.value = self.value
cooldown.total = self.total
if (self.value >= 0 and self.value <= self.total) then
cooldown:Show()
cooldown:SetCooldown(GetTime() - (self.total - self.value), self.total)
cooldown:Pause()
else
cooldown:Hide();
end
end
function region:UpdateTime()
if self.paused then
cooldown:Pause()
else
cooldown:Resume()
end
if (self.duration > 0 and self.expirationTime > GetTime() and self.expirationTime ~= math.huge) then
cooldown:Show();
cooldown.expirationTime = self.expirationTime
cooldown.duration = self.duration
cooldown.inverse = self.inverse
region:UpdateEffectiveInverse()
cooldown:SetCooldown(self.expirationTime - self.duration, self.duration)
else
cooldown.expirationTime = self.expirationTime
cooldown.duration = self.duration
cooldown:Hide();
end
end
function region:PreShow()
if (cooldown.duration and cooldown.duration > 0.01 and cooldown.duration ~= math.huge and cooldown.expirationTime ~= math.huge) then
cooldown:Show();
cooldown:SetCooldown(cooldown.expirationTime - cooldown.duration, cooldown.duration);
cooldown:Resume()
end
end
function region:Update()
region:UpdateProgress()
region:UpdateIcon()
end
else
region.UpdateValue = nil
region.UpdateTime = nil
function region:Update()
region:UpdateProgress()
region:UpdateIcon()
end
end
-- Backwards compability function
function region:SetGlow(glow)
for index, subRegion in ipairs(self.subRegions) do
if subRegion.type == "subglow" then
subRegion:SetVisible(glow)
end
end
end
Private.regionPrototype.modifyFinish(parent, region, data);
--- WORKAROUND
-- This fixes a issue with barmodels not appearing on icons if the
-- icon is shown delayed
region:SetWidth(region:GetWidth())
region:SetHeight(region:GetHeight())
end
local function validate(data)
Private.EnforceSubregionExists(data, "subbackground")
end
Private.RegisterRegionType("icon", create, modify, default, GetProperties, validate)