mirror of
https://github.com/fuffc/WeakestAuras.git
synced 2026-11-10 20:07:35 +00:00
Fewer freezes Fixes - Fixed frequent freezes on characters with auras for another class or for items not carried: those auras no longer make every aura recompile whenever spell or item data arrives. - Trigger State Updater auras no longer lose their clones when an unrelated aura names a spell or item the client cannot look up. Performance - Much less garbage during play from dynamic groups, health, power and cast bars in raids, spell cooldowns, progress bars and looping animations. - /wa prof report shows memory allocated per entry, WeakestAuras' share of the client's memory churn, and garbage collections with how long their frame took. /wa prof report kb sorts by memory.
966 lines
39 KiB
Lua
966 lines
39 KiB
Lua
-- WeakestAuras -- the "subglow" sub-region: an animated proc-glow (the
|
|
-- "IconAlert" ant-march) drawn over an icon. Mirrors WA2's Glow (§8).
|
|
-- Upstream section refs (§N) point at design/architecture/weakauras2-reference.md
|
|
--
|
|
-- Masque and LibCustomGlow don't port to 1.12, so the animation is driven here.
|
|
-- Opt-in and default-OFF, since a glow is almost always condition-driven.
|
|
|
|
if WeakestAuras.disabled then return end
|
|
|
|
local WA = WeakestAuras
|
|
local proto = WA.regionPrototype
|
|
|
|
local TEX_ALERT = "Interface\\AddOns\\WeakestAuras\\textures\\IconAlert"
|
|
local TEX_ANTS = "Interface\\AddOns\\WeakestAuras\\textures\\IconAlertAnts"
|
|
local TEX_SOLID = "Interface\\Buttons\\WHITE8X8"
|
|
local TEX_STAR = "Interface\\Buttons\\GlowStar"
|
|
local TEX_SOFT = "Interface\\Buttons\\CheckButtonGlow"
|
|
local FRAME_INTERVAL = 0.04
|
|
|
|
-- Regions are written as source pixels and normalised here rather than as
|
|
-- decimal literals, so they stay checkable against the artwork.
|
|
local function rect(x, y, w, h, sheetWidth, sheetHeight)
|
|
return x / sheetWidth, (x + w) / sheetWidth, y / sheetHeight, (y + h) / sheetHeight
|
|
end
|
|
|
|
local ALERT_W, ALERT_H = 128, 256
|
|
local ANTS_W, ANTS_H = 256, 256
|
|
local GLOW_X, GLOW_Y, GLOW_W, GLOW_H = 0, 136, 66, 66
|
|
local GLOW_SCALE = 66 / 54
|
|
local SOFT_GLOW_SCALE = 64 / 34
|
|
local DEFAULT_GLOW_COLOR = { 248 / 255, 212 / 255, 72 / 255, 1 }
|
|
|
|
-- The ant sheet is a grid of 44px cells on a 48px pitch, inset 2px/1px from the
|
|
-- top-left corner; 22 frames form the loop. Cells 23-25 are the next cycle's
|
|
-- first three frames, so playing all 25 replays them and visibly hesitates.
|
|
local ANT_COLUMNS, ANT_FRAMES = 5, 22
|
|
local ANT_PITCH, ANT_CELL = 48, 44
|
|
local ANT_X0, ANT_Y0 = 2, 1
|
|
|
|
local antFrames = {}
|
|
do
|
|
local col, row = 0, 0
|
|
for i = 1, ANT_FRAMES do
|
|
antFrames[i] = { rect(ANT_X0 + col * ANT_PITCH, ANT_Y0 + row * ANT_PITCH, ANT_CELL, ANT_CELL, ANTS_W, ANTS_H) }
|
|
col = col + 1
|
|
if col >= ANT_COLUMNS then col = 0; row = row + 1 end
|
|
end
|
|
end
|
|
|
|
-- Frames cannot be destroyed, so a released overlay is pooled rather than
|
|
-- dropped.
|
|
local pools = {}
|
|
local numOverlays = 0
|
|
|
|
-- Every lit overlay advances off one shared ticker instead of carrying its own
|
|
-- OnUpdate: the cost stays one timer however many auras glow at once, and the
|
|
-- glows stay in phase rather than drifting apart. A newly lit overlay therefore
|
|
-- joins the cycle wherever it currently is, which is the point.
|
|
local lit = {}
|
|
local numLit = 0
|
|
local antIndex = 1
|
|
local ticker
|
|
|
|
local function applyButtonOverlay(overlay, now)
|
|
overlay.bgFrame:Show()
|
|
overlay.antTex:Show()
|
|
for i = 1, #(overlay.segments or {}) do
|
|
overlay.segments[i]:Hide()
|
|
if overlay.borders[i] then overlay.borders[i]:Hide() end
|
|
end
|
|
local f = antFrames[antIndex]
|
|
overlay.antTex:SetTexCoord(f[1], f[2], f[3], f[4])
|
|
end
|
|
|
|
local function applyPixel(overlay, now)
|
|
overlay.bgFrame:Hide()
|
|
overlay.antTex:Hide()
|
|
local p = overlay.params or {}
|
|
local width = overlay:GetWidth() or 0
|
|
local height = overlay:GetHeight() or 0
|
|
local lines = p.lines or 8
|
|
local frequency = p.frequency or 0
|
|
local phase = now * math.abs(frequency)
|
|
if frequency < 0 then phase = -phase end
|
|
local length = p.length or 10
|
|
local thickness = p.thickness or 1
|
|
local borderEnabled = p.border and true or false
|
|
local perimeter = 2 * (width + height)
|
|
if perimeter <= 0 then return end
|
|
for i = 1, lines do
|
|
local fraction = (phase + (i - 1) / lines) % 1
|
|
local distance = fraction * perimeter
|
|
local segment = overlay.segments[i]
|
|
local border = overlay.borders[i]
|
|
if border then border:Hide() end
|
|
local side, along, sideLength
|
|
if distance < width then
|
|
side, along, sideLength = "TOP", distance, width
|
|
elseif distance < width + height then
|
|
side, along, sideLength = "RIGHT", distance - width, height
|
|
elseif distance < width * 2 + height then
|
|
side, along, sideLength = "BOTTOM", distance - width - height, width
|
|
else
|
|
side, along, sideLength = "LEFT", distance - width * 2 - height, height
|
|
end
|
|
if along < 0 then along = 0 elseif along > sideLength then along = sideLength end
|
|
local vertical = side == "LEFT" or side == "RIGHT"
|
|
local segmentLength = math.min(length, sideLength)
|
|
segment:SetWidth(vertical and thickness or segmentLength)
|
|
segment:SetHeight(vertical and segmentLength or thickness)
|
|
segment:ClearAllPoints()
|
|
local x, y = 0, 0
|
|
if side == "TOP" then x, y = -width / 2 + along, height / 2
|
|
elseif side == "RIGHT" then x, y = width / 2, height / 2 - along
|
|
elseif side == "BOTTOM" then x, y = width / 2 - along, -height / 2
|
|
else x, y = -width / 2, -height / 2 + along end
|
|
segment:SetPoint("CENTER", overlay, "CENTER", x, y)
|
|
segment:Show()
|
|
if border and borderEnabled then
|
|
border:SetWidth(vertical and thickness + 2 or segmentLength + 2)
|
|
border:SetHeight(vertical and segmentLength + 2 or thickness + 2)
|
|
border:ClearAllPoints()
|
|
border:SetPoint("CENTER", overlay, "CENTER", x, y)
|
|
border:Show()
|
|
end
|
|
end
|
|
for i = lines + 1, #overlay.segments do
|
|
overlay.segments[i]:Hide()
|
|
if overlay.borders[i] then overlay.borders[i]:Hide() end
|
|
end
|
|
end
|
|
|
|
local function applyPixelColor(overlay, color)
|
|
local c = color or { 1, 1, 1, 1 }
|
|
for i = 1, #(overlay.segments or {}) do
|
|
overlay.segments[i]:SetVertexColor(c[1] or 1, c[2] or 1, c[3] or 1, c[4] or 1)
|
|
if overlay.borders[i] then overlay.borders[i]:SetVertexColor(0, 0, 0, c[4] or 1) end
|
|
end
|
|
end
|
|
|
|
-- Colours every art an overlay's renderer might be drawing with. `bg`/`antTex`
|
|
-- are buttonOverlay's and are the only two a caller reaches without this; the
|
|
-- other four types draw through textures created per pool type, so tinting the
|
|
-- pair alone leaves a Pixel or ACShine glow white whatever colour was asked for.
|
|
-- nil colour means "the default gold", which is not the same as white: the ants
|
|
-- texture is art rather than a tint surface and stays untinted there.
|
|
local function tintOverlay(overlay, color)
|
|
if not overlay then return end
|
|
if color then
|
|
overlay.bg:SetVertexColor(color[1] or 1, color[2] or 1, color[3] or 1, color[4] or 1)
|
|
overlay.antTex:SetVertexColor(color[1] or 1, color[2] or 1, color[3] or 1, color[4] or 1)
|
|
else
|
|
overlay.bg:SetVertexColor(DEFAULT_GLOW_COLOR[1], DEFAULT_GLOW_COLOR[2],
|
|
DEFAULT_GLOW_COLOR[3], DEFAULT_GLOW_COLOR[4])
|
|
overlay.antTex:SetVertexColor(1, 1, 1, 1)
|
|
end
|
|
local c = color or DEFAULT_GLOW_COLOR
|
|
if overlay.poolType == "Pixel" then
|
|
applyPixelColor(overlay, c)
|
|
elseif overlay.poolType == "ACShine" then
|
|
for i = 1, #(overlay.particles or {}) do
|
|
overlay.particles[i]:SetVertexColor(c[1] or 1, c[2] or 1, c[3] or 1, c[4] or 1)
|
|
end
|
|
elseif overlay.poolType == "Soft" or overlay.poolType == "Pulse" then
|
|
overlay.softTex:SetVertexColor(c[1] or 1, c[2] or 1, c[3] or 1, c[4] or 1)
|
|
end
|
|
end
|
|
|
|
local ACSHINE_SIZES = { 7, 6, 5, 4 }
|
|
|
|
local function applyACShine(overlay, now)
|
|
overlay.bgFrame:Hide()
|
|
overlay.antTex:Hide()
|
|
local p = overlay.params or {}
|
|
local count = p.lines or 8
|
|
local frequency = p.frequency or 0
|
|
local scale = p.scale or 1
|
|
local width = overlay:GetWidth() or 0
|
|
local height = overlay:GetHeight() or 0
|
|
if p.shape ~= "circular" then
|
|
local perimeter = 2 * (width + height)
|
|
if perimeter <= 0 then return end
|
|
local textureIndex = 0
|
|
for group = 1, 4 do
|
|
local phase = now * frequency / group
|
|
for i = 1, count do
|
|
textureIndex = textureIndex + 1
|
|
local fraction = (i / count + phase) % 1
|
|
local distance = fraction * perimeter
|
|
local x, y
|
|
if distance < width then
|
|
x, y = -width / 2 + distance, -height / 2
|
|
elseif distance < width + height then
|
|
x, y = width / 2, -height / 2 + distance - width
|
|
elseif distance < width * 2 + height then
|
|
x, y = width / 2 - distance + width + height, height / 2
|
|
else
|
|
x, y = -width / 2, height / 2 - distance + width * 2 + height
|
|
end
|
|
local particle = overlay.particles[textureIndex]
|
|
local size = ACSHINE_SIZES[group] * scale
|
|
particle:SetWidth(size)
|
|
particle:SetHeight(size)
|
|
particle:ClearAllPoints()
|
|
particle:SetPoint("CENTER", overlay, "CENTER", x, y)
|
|
particle:SetTexCoord(0, 1, 0, 1)
|
|
particle:Show()
|
|
end
|
|
end
|
|
for i = textureIndex + 1, #overlay.particles do overlay.particles[i]:Hide() end
|
|
return
|
|
end
|
|
local radiusX = width / 2
|
|
local radiusY = height / 2
|
|
local size = 16 * scale
|
|
local phase = now * frequency * 2 * math.pi
|
|
for i = 1, count do
|
|
local angle = phase + (i - 1) * 2 * math.pi / count
|
|
local particle = overlay.particles[i]
|
|
local x = math.cos(angle) * radiusX
|
|
local y = math.sin(angle) * radiusY
|
|
particle:SetWidth(size)
|
|
particle:SetHeight(size)
|
|
particle:ClearAllPoints()
|
|
particle:SetPoint("CENTER", overlay, "CENTER", x, y)
|
|
particle:SetTexCoord(0, 1, 0, 1)
|
|
particle:Show()
|
|
end
|
|
for i = count + 1, #overlay.particles do overlay.particles[i]:Hide() end
|
|
end
|
|
|
|
local function applySoft(overlay)
|
|
overlay.bgFrame:Hide()
|
|
overlay.antTex:Hide()
|
|
for i = 1, #(overlay.segments or {}) do
|
|
overlay.segments[i]:Hide()
|
|
if overlay.borders[i] then overlay.borders[i]:Hide() end
|
|
end
|
|
for i = 1, #(overlay.particles or {}) do overlay.particles[i]:Hide() end
|
|
overlay.softTex:SetAlpha(1)
|
|
overlay.softTex:Show()
|
|
end
|
|
|
|
local function applyPulse(overlay, now)
|
|
applySoft(overlay)
|
|
local frequency = (overlay.params and overlay.params.frequency) or 0.25
|
|
local phase = now * frequency * 2 * math.pi
|
|
overlay.softTex:SetAlpha(0.5 + 0.5 * math.sin(phase))
|
|
end
|
|
|
|
local function glowSummary(data)
|
|
local color = data.useGlowColor and "Custom color" or "Default color"
|
|
if data.glowType == "Pixel" then
|
|
local border = data.glowBorder and ", Border" or ""
|
|
return string.format("%s, %d lines, %.2f frequency, %d length, %d thickness%s",
|
|
color, data.glowLines or 8, data.glowFrequency or 0.25,
|
|
data.glowLength or 10, data.glowThickness or 1, border)
|
|
elseif data.glowType == "ACShine" then
|
|
return string.format("%s, %s, %d groups, %.2f frequency, %.2f scale",
|
|
color, data.glowShape == "circular" and "Circular" or "Rectangular",
|
|
data.glowLines or 8, data.glowFrequency or 0.25, data.glowScale or 1)
|
|
elseif data.glowType == "Pulse" then
|
|
return string.format("%s, %.2f frequency, %.2f scale",
|
|
color, data.glowFrequency or 0.25, data.glowScale or 1)
|
|
elseif data.glowType == "Soft" then
|
|
return string.format("%s, %.2f scale", color, data.glowScale or 1)
|
|
end
|
|
return color
|
|
end
|
|
|
|
local function step()
|
|
WA.ProfBegin("glow ticker")
|
|
antIndex = antIndex + 1
|
|
if antIndex > ANT_FRAMES then antIndex = 1 end
|
|
local now = GetTime()
|
|
for overlay in pairs(lit) do overlay.renderer.apply(overlay, now) end
|
|
WA.ProfEnd("glow ticker")
|
|
end
|
|
|
|
local function light(overlay)
|
|
if not overlay.renderer.animate then
|
|
overlay.renderer.apply(overlay, GetTime())
|
|
return
|
|
end
|
|
if lit[overlay] then return end
|
|
lit[overlay] = true
|
|
numLit = numLit + 1
|
|
overlay.renderer.apply(overlay, GetTime())
|
|
if not ticker then ticker = C_Timer.NewTicker(FRAME_INTERVAL, step) end
|
|
end
|
|
|
|
-- The ticker is stopped once nothing is glowing rather than left running, so an
|
|
-- idle session costs no timer at all.
|
|
local function douse(overlay)
|
|
if not lit[overlay] then return end
|
|
lit[overlay] = nil
|
|
numLit = numLit - 1
|
|
if numLit <= 0 then
|
|
numLit = 0
|
|
if ticker then ticker:Cancel(); ticker = nil end
|
|
end
|
|
end
|
|
|
|
local function getOverlay(glowType)
|
|
local pool = pools[glowType]
|
|
local overlay = table.remove(pool)
|
|
if not overlay then
|
|
numOverlays = numOverlays + 1
|
|
overlay = CreateFrame("Frame", "WeakestAurasGlowOverlay" .. numOverlays)
|
|
overlay.bgFrame = CreateFrame("Frame", "WeakestAurasGlowBackground" .. numOverlays)
|
|
|
|
overlay.bg = overlay.bgFrame:CreateTexture(nil, "ARTWORK")
|
|
overlay.bg:SetTexture(TEX_ALERT)
|
|
overlay.bg:SetTexCoord(rect(GLOW_X, GLOW_Y, GLOW_W, GLOW_H, ALERT_W, ALERT_H))
|
|
overlay.bg:SetAllPoints(overlay.bgFrame)
|
|
|
|
overlay.antTex = overlay:CreateTexture(nil, "OVERLAY")
|
|
overlay.antTex:SetTexture(TEX_ANTS)
|
|
overlay.antTex:SetAllPoints(overlay)
|
|
overlay.antTex:SetBlendMode("ADD")
|
|
overlay.antTex:SetVertexColor(1, 1, 1, 1)
|
|
end
|
|
if glowType == "Pixel" and not overlay.segments then
|
|
overlay.segments, overlay.borders = {}, {}
|
|
for i = 1, 30 do
|
|
local border = overlay:CreateTexture(nil, "ARTWORK")
|
|
border:SetTexture(TEX_SOLID)
|
|
border:SetVertexColor(0, 0, 0, 1)
|
|
overlay.borders[i] = border
|
|
local segment = overlay:CreateTexture(nil, "OVERLAY")
|
|
segment:SetTexture(TEX_SOLID)
|
|
segment:SetVertexColor(1, 1, 1, 1)
|
|
overlay.segments[i] = segment
|
|
end
|
|
end
|
|
if glowType == "ACShine" and not overlay.particles then
|
|
overlay.particles = {}
|
|
for i = 1, 120 do
|
|
local particle = overlay:CreateTexture(nil, "OVERLAY")
|
|
particle:SetTexture(TEX_STAR)
|
|
particle:SetBlendMode("ADD")
|
|
particle:SetVertexColor(1, 1, 1, 1)
|
|
overlay.particles[i] = particle
|
|
end
|
|
end
|
|
if (glowType == "Soft" or glowType == "Pulse") and not overlay.softTex then
|
|
overlay.softTex = overlay:CreateTexture(nil, "OVERLAY")
|
|
overlay.softTex:SetTexture(TEX_SOFT)
|
|
overlay.softTex:SetAllPoints(overlay)
|
|
overlay.softTex:SetBlendMode("ADD")
|
|
overlay.softTex:SetVertexColor(1, 1, 1, 1)
|
|
end
|
|
overlay.poolType = glowType
|
|
return overlay
|
|
end
|
|
|
|
local function releaseOverlay(glowType, overlay)
|
|
pools[glowType] = pools[glowType] or {}
|
|
table.insert(pools[glowType], overlay)
|
|
end
|
|
|
|
WA.glow_types = {
|
|
buttonOverlay = "Action Button Glow",
|
|
Pixel = "Pixel Glow",
|
|
ACShine = "Autocast Shine",
|
|
Soft = "Soft Glow",
|
|
Pulse = "Pulse Glow",
|
|
}
|
|
|
|
local glowRenderers = {}
|
|
for glowType in pairs(WA.glow_types) do
|
|
local typeName = glowType
|
|
pools[glowType] = {}
|
|
glowRenderers[glowType] = {
|
|
acquire = function() return getOverlay(typeName) end,
|
|
release = function(overlay) releaseOverlay(typeName, overlay) end,
|
|
animate = typeName ~= "Soft",
|
|
apply = typeName == "Pixel" and applyPixel
|
|
or typeName == "ACShine" and applyACShine
|
|
or typeName == "Soft" and applySoft
|
|
or typeName == "Pulse" and applyPulse
|
|
or applyButtonOverlay,
|
|
}
|
|
end
|
|
|
|
local function getRenderer(glowType)
|
|
return glowRenderers[glowType] or glowRenderers.buttonOverlay
|
|
end
|
|
|
|
local function glowAnchorArea(parentData, subData)
|
|
local requested = subData.anchor_area
|
|
local values = proto.GetSubRegionAnchors(parentData, "area")
|
|
for i = 1, #values do
|
|
if values[i] == requested then return requested end
|
|
end
|
|
return parentData.regionType == "progressbar" and "bar" or "region"
|
|
end
|
|
|
|
local function glowTarget(parent, key)
|
|
if parent.bar and parent.iconFrame then
|
|
if key == "bar" then return parent.bar end
|
|
if key == "icon" then return parent.iconFrame end
|
|
return parent.bar
|
|
end
|
|
return proto.GetSubRegionAnchorTarget(parent, key)
|
|
end
|
|
|
|
local function glowGeometryTarget(parent, key, target)
|
|
if parent.bar and (key == "region" or key == "bar") then
|
|
return parent
|
|
end
|
|
return target
|
|
end
|
|
|
|
-- SetParent does not redraw a frame that was already shown when it moved: it
|
|
-- keeps IsShown() true, IsVisible() false, and the engine never paints it
|
|
-- (design/client/gotchas.md), which on a glow reads as the effect silently
|
|
-- going missing while everything about it still says it is on. StartGlow is
|
|
-- re-entered with the overlay already lit often enough to matter -- a condition
|
|
-- writing any glow property, an options edit, the re-Add every WA.Add makes --
|
|
-- so the move is skipped when it would change nothing and settled with the
|
|
-- Hide/Show that forces the flag down the chain when it would.
|
|
local function reparentOverlayFrame(frame, target)
|
|
if not target or frame:GetParent() == target then return end
|
|
local wasShown = frame:IsShown()
|
|
frame:SetParent(target)
|
|
if wasShown then frame:Hide(); frame:Show() end
|
|
end
|
|
|
|
local function applyGeometry(region)
|
|
local overlay = region.overlay
|
|
if not overlay then return end
|
|
local area = glowAnchorArea(region.parentData, region.anchorData)
|
|
local target = glowTarget(region.parent, area)
|
|
local geometryTarget = glowGeometryTarget(region.parent, area, target)
|
|
if not target then target = region.host end
|
|
if not geometryTarget then geometryTarget = target end
|
|
local scale = region.glowScale or 1
|
|
local x = region.glowXOffset or 0
|
|
local y = region.glowYOffset or 0
|
|
local width = geometryTarget:GetWidth() or 0
|
|
local height = geometryTarget:GetHeight() or 0
|
|
local anchorX = region.anchorData.anchorXOffset or 0
|
|
local anchorY = region.anchorData.anchorYOffset or 0
|
|
local artScale = overlay.poolType == "buttonOverlay" and GLOW_SCALE
|
|
or (overlay.poolType == "Soft" or overlay.poolType == "Pulse") and SOFT_GLOW_SCALE
|
|
or 1
|
|
local geometryScale = overlay.poolType == "ACShine" and 1 or scale
|
|
local glowWidth = (width + anchorX * 2) * artScale * geometryScale
|
|
local glowHeight = (height + anchorY * 2) * artScale * geometryScale
|
|
overlay:SetWidth(glowWidth)
|
|
overlay:SetHeight(glowHeight)
|
|
overlay:ClearAllPoints()
|
|
overlay:SetPoint("CENTER", geometryTarget, "CENTER", x, y)
|
|
overlay.bgFrame:SetWidth(glowWidth)
|
|
overlay.bgFrame:SetHeight(glowHeight)
|
|
overlay.bgFrame:ClearAllPoints()
|
|
overlay.bgFrame:SetPoint("CENTER", geometryTarget, "CENTER", x, y)
|
|
if overlay.renderer and (overlay.poolType == "Pixel" or overlay.poolType == "ACShine") then
|
|
overlay.renderer.apply(overlay, GetTime())
|
|
end
|
|
end
|
|
|
|
-- Read-only pool stats for Debug.lua's glow-leak check (verification: a hidden
|
|
-- aura must release its overlay, not leave one ticking off-screen).
|
|
function WA.GetGlowPoolStats(glowType)
|
|
if glowType then
|
|
return #(pools[glowType] or {})
|
|
end
|
|
local free = 0
|
|
for _, pool in pairs(pools) do free = free + #pool end
|
|
return numOverlays, free
|
|
end
|
|
|
|
local function glowDefault(regionType)
|
|
local default = {
|
|
type = "subglow",
|
|
glow = false,
|
|
glowType = "buttonOverlay",
|
|
glowColor = { 1, 1, 1, 1 },
|
|
useGlowColor = false,
|
|
glowScale = 1,
|
|
glowShape = "rectangular",
|
|
glowXOffset = 0,
|
|
glowYOffset = 0,
|
|
glowLines = 8,
|
|
glowFrequency = 0.25,
|
|
glowLength = 10,
|
|
glowThickness = 1,
|
|
glowBorder = false,
|
|
anchor_mode = "area",
|
|
anchor_area = "region",
|
|
}
|
|
if regionType == "progressbar" then
|
|
default.glowType = "Pixel"
|
|
default.anchor_area = "bar"
|
|
end
|
|
return default
|
|
end
|
|
|
|
WA.RegisterSubRegionType("subglow", {
|
|
displayName = "Glow",
|
|
supports = function(regionType)
|
|
return regionType == "icon"
|
|
or regionType == "progressbar"
|
|
or regionType == "text"
|
|
or regionType == "texture"
|
|
or regionType == "progresstexture"
|
|
or regionType == "empty"
|
|
end,
|
|
default = glowDefault("icon"),
|
|
defaultFor = glowDefault,
|
|
-- The point of the feature: conditionable on/off + colour.
|
|
properties = {
|
|
glow = { display = "Glow", setter = "SetVisible", type = "bool" },
|
|
glowType = { display = "Glow Type", setter = "SetGlowType", type = "list", values = WA.glow_types },
|
|
glowLines = { display = "Lines", setter = "SetGlowLines", type = "number", min = 1, max = 30, step = 1 },
|
|
glowFrequency = { display = "Frequency", setter = "SetGlowFrequency", type = "number", min = -2, max = 2, step = 0.05 },
|
|
glowShape = { display = "Shape", setter = "SetGlowShape", type = "list", values = { rectangular = "Rectangular", circular = "Circular" } },
|
|
glowLength = { display = "Length", setter = "SetGlowLength", type = "number", min = 1, max = 20, step = 1 },
|
|
glowThickness = { display = "Thickness", setter = "SetGlowThickness", type = "number", min = 1, max = 20, step = 1 },
|
|
glowBorder = { display = "Border", setter = "SetGlowBorder", type = "bool" },
|
|
useGlowColor = { display = "Use Color", setter = "SetUseGlowColor", type = "bool" },
|
|
glowColor = { display = "Color", setter = "SetGlowColor", type = "color" },
|
|
glowScale = { display = "Scale", setter = "SetGlowScale", type = "number", min = 0.05, max = 10, step = 0.05 },
|
|
glowXOffset = { display = "X Offset", setter = "SetGlowXOffset", type = "number", min = -100, max = 100, step = 1 },
|
|
glowYOffset = { display = "Y Offset", setter = "SetGlowYOffset", type = "number", min = -100, max = 100, step = 1 },
|
|
},
|
|
create = function(parent)
|
|
local region = { parent = parent, host = parent }
|
|
|
|
function region:ReapplyTint()
|
|
tintOverlay(self.overlay, self.useGlowColor and self.glowColor or nil)
|
|
end
|
|
-- The overlay comes from a shared pool and is acquired on demand, so the
|
|
-- level has to be *stored* and re-applied to whatever the pool hands
|
|
-- back -- being told it once, at modify time, would only reach an
|
|
-- overlay that happened to already exist. The pair takes two levels
|
|
-- (backdrop under art), which is what proto.SUB_STEP reserves.
|
|
function region:ApplyFrameLevel(overlay)
|
|
overlay = overlay or self.overlay
|
|
if not overlay then return end
|
|
local level = self.frameLevel
|
|
or (proto.BaseFrameLevel(self.parent) + proto.SUB_LEVEL)
|
|
overlay.bgFrame:SetFrameLevel(level)
|
|
overlay:SetFrameLevel(level + 1)
|
|
end
|
|
function region:SetFrameLevel(level)
|
|
self.frameLevel = level
|
|
self:ApplyFrameLevel()
|
|
end
|
|
function region:StartGlow()
|
|
local glowType = self.glowType or "buttonOverlay"
|
|
local renderer = getRenderer(glowType)
|
|
local overlay = self.overlay
|
|
if overlay and overlay.poolType ~= glowType then
|
|
self:StopGlow()
|
|
overlay = nil
|
|
end
|
|
if not overlay then overlay = renderer.acquire() end
|
|
local target = glowTarget(self.parent, glowAnchorArea(self.parentData, self.anchorData))
|
|
reparentOverlayFrame(overlay, target or self.host)
|
|
reparentOverlayFrame(overlay.bgFrame, target or self.host)
|
|
self:ApplyFrameLevel(overlay)
|
|
overlay.renderer = renderer
|
|
self.overlay = overlay
|
|
overlay.params = {
|
|
lines = self.glowLines or 8,
|
|
frequency = self.glowFrequency or 0.25,
|
|
length = self.glowLength or 10,
|
|
thickness = self.glowThickness or 1,
|
|
border = self.glowBorder,
|
|
scale = self.glowScale or 1,
|
|
shape = self.glowShape or "rectangular",
|
|
}
|
|
overlay.target = target or self.host
|
|
applyGeometry(self)
|
|
self:ReapplyTint()
|
|
overlay.bgFrame:Show()
|
|
overlay:Show()
|
|
light(overlay)
|
|
end
|
|
function region:StopGlow()
|
|
if not self.overlay then return end
|
|
local overlay = self.overlay
|
|
local renderer = overlay.renderer or getRenderer(self.glowType)
|
|
douse(overlay)
|
|
overlay:Hide()
|
|
overlay.bgFrame:Hide()
|
|
overlay:SetParent(UIParent)
|
|
overlay.bgFrame:SetParent(UIParent)
|
|
self.overlay = nil
|
|
renderer.release(overlay)
|
|
end
|
|
function region:SetGlowType(glowType)
|
|
glowType = glowType or "buttonOverlay"
|
|
if not glowRenderers[glowType] then glowType = "buttonOverlay" end
|
|
if self.glowType == glowType then return end
|
|
self.glowType = glowType
|
|
if self.visible then self:StartGlow() end
|
|
end
|
|
function region:SetVisible(b)
|
|
self.visible = b and true or false
|
|
if self.visible then self:StartGlow() else self:StopGlow() end
|
|
end
|
|
function region:SetUseGlowColor(b) self.useGlowColor = b and true or false; self:ReapplyTint() end
|
|
function region:SetGlowColor(r, g, b, a) self.glowColor = { r, g, b, a or 1 }; self:ReapplyTint() end
|
|
function region:SetGlowScale(v) self.glowScale = v or 1; if self.overlay then self.overlay.params.scale = self.glowScale end; applyGeometry(self) end
|
|
function region:SetGlowShape(v) self.glowShape = v == "circular" and "circular" or "rectangular"; if self.overlay then self.overlay.params.shape = self.glowShape; self.overlay.renderer.apply(self.overlay, GetTime()) end end
|
|
function region:SetGlowXOffset(v) self.glowXOffset = v or 0; applyGeometry(self) end
|
|
function region:SetGlowYOffset(v) self.glowYOffset = v or 0; applyGeometry(self) end
|
|
function region:SetGlowLines(v) self.glowLines = v or 8; if self.overlay then self.overlay.params.lines = self.glowLines; self.overlay.renderer.apply(self.overlay, GetTime()) end end
|
|
function region:SetGlowFrequency(v) self.glowFrequency = v or 0.25; if self.overlay then self.overlay.params.frequency = self.glowFrequency; self.overlay.renderer.apply(self.overlay, GetTime()) end end
|
|
function region:SetGlowLength(v) self.glowLength = v or 10; if self.overlay then self.overlay.params.length = self.glowLength end end
|
|
function region:SetGlowThickness(v) self.glowThickness = v or 1; if self.overlay then self.overlay.params.thickness = self.glowThickness end end
|
|
function region:SetGlowBorder(v)
|
|
self.glowBorder = v and true or false
|
|
if self.overlay then
|
|
self.overlay.params.border = self.glowBorder
|
|
self.overlay.renderer.apply(self.overlay, GetTime())
|
|
end
|
|
end
|
|
-- modifyFinish's Show/Hide: honour the current visible flag, and always
|
|
-- release on Hide so a removed/unsupported instance can't leak a ticking
|
|
-- overlay.
|
|
function region:Show() if self.visible then self:StartGlow() end end
|
|
function region:Hide() self:StopGlow() end
|
|
return region
|
|
end,
|
|
modify = function(parent, region, parentData, subData)
|
|
region.host = parent
|
|
region.parentData = parentData
|
|
region.anchorData = subData
|
|
region.visible = subData.glow and true or false
|
|
region.useGlowColor = subData.useGlowColor and true or false
|
|
region.glowColor = subData.glowColor or { 1, 1, 1, 1 }
|
|
region.glowScale = subData.glowScale or 1
|
|
region.glowShape = subData.glowShape == "circular" and "circular" or "rectangular"
|
|
region.glowXOffset = subData.glowXOffset or 0
|
|
region.glowYOffset = subData.glowYOffset or 0
|
|
region.glowLines = subData.glowLines or 8
|
|
region.glowFrequency = subData.glowFrequency or 0.25
|
|
region.glowLength = subData.glowLength or 10
|
|
region.glowThickness = subData.glowThickness or 1
|
|
region.glowBorder = subData.glowBorder and true or false
|
|
region.glowType = nil
|
|
region:SetGlowType(subData.glowType or "buttonOverlay")
|
|
if region.visible then region:StartGlow() else region:StopGlow() end
|
|
region:ReapplyTint()
|
|
|
|
-- A hidden pooled overlay must leave the ticker's set or it animates
|
|
-- forever. PreHide releases it; PreShow re-arms one that's still on.
|
|
parent.subRegionEvents:AddSubscriber("PreHide", function() region:StopGlow() end)
|
|
parent.subRegionEvents:AddSubscriber("PreShow", function()
|
|
if region.visible then
|
|
region:StartGlow()
|
|
applyGeometry(region)
|
|
end
|
|
end)
|
|
end,
|
|
options = function(parentData, subData, index)
|
|
local fields = {
|
|
{
|
|
type = "select", name = "Glow type", key = "glowType",
|
|
values = { "buttonOverlay", "Pixel", "ACShine", "Soft", "Pulse" }, labels = WA.glow_types,
|
|
get = function() return subData.glowType or "buttonOverlay" end,
|
|
set = function(v)
|
|
subData.glowType = v
|
|
WA.Add(parentData, true)
|
|
WA.RefreshOptions()
|
|
end,
|
|
},
|
|
{
|
|
type = "toggle", name = "Glow", key = "glow",
|
|
get = function() return subData.glow and true or false end,
|
|
set = function(v) subData.glow = v and true or false; WA.Add(parentData, true) end,
|
|
},
|
|
{
|
|
type = "toggle", name = "Use custom color", key = "useGlowColor", half = true,
|
|
get = function() return subData.useGlowColor and true or false end,
|
|
set = function(v) subData.useGlowColor = v and true or false; WA.Add(parentData, true) end,
|
|
},
|
|
{
|
|
type = "color", name = "Color", key = "glowColor", half = true,
|
|
get = function() return subData.glowColor end,
|
|
set = function(v) subData.glowColor = v; WA.Add(parentData, true) end,
|
|
},
|
|
}
|
|
local S = WA.OptionsState
|
|
local key = "sub:" .. index .. ":glowextra"
|
|
local collapsed = S.isCollapsed(parentData, key, true)
|
|
table.insert(fields, {
|
|
type = "disclosure", name = "Extra Options", summary = glowSummary(subData),
|
|
collapsed = collapsed,
|
|
onToggle = function()
|
|
S.setCollapsed(parentData, key, not collapsed)
|
|
WA.RefreshOptions()
|
|
end,
|
|
})
|
|
if not collapsed then
|
|
if (subData.glowType or "buttonOverlay") == "Pixel"
|
|
or (subData.glowType or "buttonOverlay") == "ACShine" then
|
|
table.insert(fields, {
|
|
type = "range", name = "Lines", key = "glowLines", min = 1, max = 30, step = 1, half = true,
|
|
get = function() return subData.glowLines or 8 end,
|
|
set = function(v) subData.glowLines = v; WA.Add(parentData, true) end,
|
|
})
|
|
end
|
|
if (subData.glowType or "buttonOverlay") == "Pixel"
|
|
or (subData.glowType or "buttonOverlay") == "ACShine"
|
|
or (subData.glowType or "buttonOverlay") == "Pulse" then
|
|
table.insert(fields, {
|
|
type = "range", name = "Frequency", key = "glowFrequency", min = -2, max = 2, step = 0.05, half = true,
|
|
get = function() return subData.glowFrequency or 0.25 end,
|
|
set = function(v) subData.glowFrequency = v; WA.Add(parentData, true) end,
|
|
})
|
|
end
|
|
if (subData.glowType or "buttonOverlay") == "Pixel" then
|
|
table.insert(fields, {
|
|
type = "range", name = "Length", key = "glowLength", min = 1, max = 20, step = 1, half = true,
|
|
get = function() return subData.glowLength or 10 end,
|
|
set = function(v) subData.glowLength = v; WA.Add(parentData, true) end,
|
|
})
|
|
table.insert(fields, {
|
|
type = "range", name = "Thickness", key = "glowThickness", min = 1, max = 20, step = 1, half = true,
|
|
get = function() return subData.glowThickness or 1 end,
|
|
set = function(v) subData.glowThickness = v; WA.Add(parentData, true) end,
|
|
})
|
|
table.insert(fields, {
|
|
type = "toggle", name = "Border", key = "glowBorder", half = true,
|
|
get = function() return subData.glowBorder and true or false end,
|
|
set = function(v) subData.glowBorder = v and true or false; WA.Add(parentData, true) end,
|
|
})
|
|
end
|
|
table.insert(fields, {
|
|
type = "range", name = "Scale", key = "glowScale", min = 0.05, max = 10, step = 0.05,
|
|
get = function() return subData.glowScale or 1 end,
|
|
set = function(v) subData.glowScale = v; WA.Add(parentData, true) end,
|
|
})
|
|
if (subData.glowType or "buttonOverlay") == "ACShine" then
|
|
table.insert(fields, {
|
|
type = "select", name = "Shape", key = "glowShape",
|
|
values = { "rectangular", "circular" },
|
|
labels = { rectangular = "Rectangular", circular = "Circular" },
|
|
get = function() return subData.glowShape == "circular" and "circular" or "rectangular" end,
|
|
set = function(v) subData.glowShape = v == "circular" and "circular" or "rectangular"; WA.Add(parentData, true) end,
|
|
})
|
|
end
|
|
table.insert(fields, {
|
|
type = "range", name = "X Offset", key = "glowXOffset", min = -100, max = 100, step = 1, half = true,
|
|
get = function() return subData.glowXOffset or 0 end,
|
|
set = function(v) subData.glowXOffset = v; WA.Add(parentData, true) end,
|
|
})
|
|
table.insert(fields, {
|
|
type = "range", name = "Y Offset", key = "glowYOffset", min = -100, max = 100, step = 1, half = true,
|
|
get = function() return subData.glowYOffset or 0 end,
|
|
set = function(v) subData.glowYOffset = v; WA.Add(parentData, true) end,
|
|
})
|
|
end
|
|
local anchorFields = proto.SubRegionAnchorFields(parentData, subData, {
|
|
areaOnly = true,
|
|
areaTarget = glowAnchorArea(parentData, subData),
|
|
})
|
|
for i = 1, #anchorFields do
|
|
if anchorFields[i].key == "anchor_area" then
|
|
anchorFields[i].get = function() return glowAnchorArea(parentData, subData) end
|
|
end
|
|
table.insert(fields, anchorFields[i])
|
|
end
|
|
return fields
|
|
end,
|
|
})
|
|
|
|
function WA.CreateExternalGlow(frame)
|
|
if not frame then return nil end
|
|
local glow = getOverlay("buttonOverlay")
|
|
local region = {
|
|
parent = frame, host = frame, parentData = { regionType = "icon" },
|
|
anchorData = { anchor_area = "region", anchorXOffset = 0, anchorYOffset = 0 },
|
|
glowType = "buttonOverlay", glowScale = 1, glowShape = "rectangular",
|
|
glowXOffset = 0, glowYOffset = 0, glowLines = 8, glowFrequency = 0.25,
|
|
glowLength = 10, glowThickness = 1, glowBorder = false,
|
|
useGlowColor = false, glowColor = DEFAULT_GLOW_COLOR,
|
|
overlay = glow,
|
|
}
|
|
glow:SetParent(frame)
|
|
glow.bgFrame:SetParent(frame)
|
|
glow.renderer = getRenderer("buttonOverlay")
|
|
function region:StartGlow(options)
|
|
local types = { buttonOverlay = true, Pixel = true, ACShine = true, Soft = true, Pulse = true }
|
|
local glowType = types[options.glow_type] and options.glow_type or "buttonOverlay"
|
|
if self.overlay.poolType ~= glowType then
|
|
self:StopGlow()
|
|
self.overlay = getOverlay(glowType)
|
|
end
|
|
-- Parent and level are asserted on every start, not once at create time.
|
|
-- The overlay pool is shared with the sub-region glow and an overlay
|
|
-- comes back carrying whoever held it last -- a stranger's frame level,
|
|
-- UIParent from the release above, or no parent at all if the pool was
|
|
-- empty and it was made here. Size is written in the parent's
|
|
-- coordinates, so an overlay left off the target ignores the target's
|
|
-- scale: a parentless one draws at scale 1 against a UI at the user's
|
|
-- scale, and one on UIParent misses an aura's own `scale` setting.
|
|
reparentOverlayFrame(self.overlay, frame)
|
|
reparentOverlayFrame(self.overlay.bgFrame, frame)
|
|
local level = (frame.GetFrameLevel and frame:GetFrameLevel() or 0) + proto.SUB_LEVEL
|
|
self.overlay.bgFrame:SetFrameLevel(level)
|
|
self.overlay:SetFrameLevel(level + 1)
|
|
self.glowType = glowType
|
|
self.useGlowColor = options.use_glow_color and true or false
|
|
self.glowColor = options.glow_color or DEFAULT_GLOW_COLOR
|
|
self.glowScale = options.glow_scale or 1
|
|
self.glowXOffset = options.glow_XOffset or 0
|
|
self.glowYOffset = options.glow_YOffset or 0
|
|
-- Per-axis size trim, the one lever a foreign frame really needs: a
|
|
-- nameplate's frame is its bar *and* its name text, so a glow matching it
|
|
-- stands well clear of the bar the user was aiming at, and by a different
|
|
-- amount on each axis than scale can express. applyGeometry grows the box
|
|
-- by twice each of these, so a negative pulls the glow in.
|
|
self.anchorData.anchorXOffset = options.glow_extraWidth or 0
|
|
self.anchorData.anchorYOffset = options.glow_extraHeight or 0
|
|
self.glowLines = options.glow_lines or 8
|
|
self.glowFrequency = options.glow_frequency or 0.25
|
|
self.glowLength = options.glow_length or 10
|
|
self.glowThickness = options.glow_thickness or 1
|
|
self.glowBorder = options.glow_border and true or false
|
|
self.overlay.renderer = getRenderer(glowType)
|
|
self.overlay.params = { lines = self.glowLines, frequency = self.glowFrequency,
|
|
length = self.glowLength, thickness = self.glowThickness, border = self.glowBorder,
|
|
scale = self.glowScale, shape = "rectangular" }
|
|
applyGeometry(self)
|
|
self:ReapplyTint()
|
|
self.overlay:Show(); self.overlay.bgFrame:Show()
|
|
light(self.overlay)
|
|
end
|
|
function region:ReapplyTint()
|
|
tintOverlay(self.overlay, self.useGlowColor and self.glowColor or nil)
|
|
end
|
|
function region:StopGlow()
|
|
if not self.overlay then return end
|
|
douse(self.overlay)
|
|
self.overlay:Hide(); self.overlay.bgFrame:Hide()
|
|
self.overlay:SetParent(UIParent); self.overlay.bgFrame:SetParent(UIParent)
|
|
releaseOverlay(self.overlay.poolType, self.overlay)
|
|
self.overlay = nil
|
|
end
|
|
function region:Destroy()
|
|
self:StopGlow()
|
|
end
|
|
return region
|
|
end
|
|
|
|
-- The editor for one `glowexternal` condition change. It lives here rather than
|
|
-- in OptionsFrame.lua because which fields a glow type offers is decided by what
|
|
-- CreateExternalGlow's StartGlow above actually reads: `lines` means nothing to
|
|
-- Soft, `length`/`thickness`/`border` mean nothing to anything but Pixel, and a
|
|
-- field offered for a type that ignores it is a control that does nothing.
|
|
local GLOW_TYPE_ORDER = { "buttonOverlay", "Pixel", "ACShine", "Soft", "Pulse" }
|
|
local GLOW_FRAME_TYPES = { "PARENTFRAME", "FRAMESELECTOR", "UNITFRAME", "NAMEPLATE" }
|
|
local GLOW_FRAME_LABELS = {
|
|
PARENTFRAME = "This Aura", FRAMESELECTOR = "Named Frame",
|
|
UNITFRAME = "Unit Frame", NAMEPLATE = "Nameplate",
|
|
}
|
|
|
|
-- The same editor over an action block, whose glow keys sit on the block itself
|
|
-- rather than under a `value`. `finish` alone offers "Hide all glows": on show
|
|
-- there is nothing lit yet to clear.
|
|
function WA.ActionGlowFields(fields, data, block, when)
|
|
table.insert(fields, { type = "toggle", name = "Glow External Element", key = "do_glow",
|
|
get = function() return block.do_glow and true or false end,
|
|
set = function(v)
|
|
block.do_glow = v and true or false
|
|
WA.Add(data)
|
|
WA.RefreshOptions()
|
|
end })
|
|
if not block.do_glow then return end
|
|
WA.ConditionGlowFields(fields, data, { value = block })
|
|
if when == "finish" then
|
|
table.insert(fields, { type = "toggle", name = "Hide all glows", key = "hide_all_glows",
|
|
get = function() return block.hide_all_glows and true or false end,
|
|
set = function(v) block.hide_all_glows = v and true or false; WA.Add(data) end })
|
|
end
|
|
end
|
|
|
|
function WA.ConditionGlowFields(fields, data, change)
|
|
local function get(key, default)
|
|
local v = change.value and change.value[key]
|
|
if v == nil then return default end
|
|
return v
|
|
end
|
|
local function set(key, refresh)
|
|
return function(v)
|
|
change.value = change.value or {}
|
|
change.value[key] = v
|
|
WA.Add(data)
|
|
if refresh then WA.RefreshOptions() end
|
|
end
|
|
end
|
|
|
|
table.insert(fields, { type = "select", name = "Glow Action", key = "glow_action", values = { "show", "hide" },
|
|
labels = { show = "Show", hide = "Hide" },
|
|
get = function() return get("glow_action", "show") end,
|
|
set = set("glow_action", true) })
|
|
table.insert(fields, { type = "select", name = "Frame Type", key = "glow_frame_type",
|
|
values = GLOW_FRAME_TYPES, labels = GLOW_FRAME_LABELS,
|
|
get = function() return get("glow_frame_type", "PARENTFRAME") end,
|
|
set = set("glow_frame_type", true) })
|
|
if get("glow_frame_type", "PARENTFRAME") == "FRAMESELECTOR" then
|
|
table.insert(fields, { type = "input", name = "Frame Name", key = "glow_frame",
|
|
get = function() return get("glow_frame", "") end,
|
|
set = set("glow_frame") })
|
|
end
|
|
-- A hide carries no art: it puts out whatever the matching show started.
|
|
if get("glow_action", "show") ~= "show" then return end
|
|
|
|
local glowType = get("glow_type", "buttonOverlay")
|
|
table.insert(fields, { type = "select", name = "Glow Type", key = "glow_type",
|
|
values = GLOW_TYPE_ORDER, labels = WA.glow_types,
|
|
get = function() return glowType end,
|
|
set = set("glow_type", true) })
|
|
table.insert(fields, { type = "toggle", name = "Use custom color", key = "use_glow_color", half = true,
|
|
get = function() return get("use_glow_color", false) and true or false end,
|
|
set = set("use_glow_color", true) })
|
|
if get("use_glow_color", false) then
|
|
table.insert(fields, { type = "color", name = "Color", key = "glow_color", half = true,
|
|
get = function() return get("glow_color", nil) end,
|
|
set = set("glow_color") })
|
|
end
|
|
if glowType == "Pixel" or glowType == "ACShine" then
|
|
table.insert(fields, { type = "range", name = "Lines", key = "glow_lines", min = 1, max = 30, step = 1, half = true,
|
|
get = function() return get("glow_lines", 8) end,
|
|
set = set("glow_lines") })
|
|
end
|
|
if glowType == "Pixel" or glowType == "ACShine" or glowType == "Pulse" then
|
|
table.insert(fields, { type = "range", name = "Frequency", key = "glow_frequency", min = -2, max = 2, step = 0.05, half = true,
|
|
get = function() return get("glow_frequency", 0.25) end,
|
|
set = set("glow_frequency") })
|
|
end
|
|
if glowType == "Pixel" then
|
|
table.insert(fields, { type = "range", name = "Length", key = "glow_length", min = 1, max = 20, step = 1, half = true,
|
|
get = function() return get("glow_length", 10) end,
|
|
set = set("glow_length") })
|
|
table.insert(fields, { type = "range", name = "Thickness", key = "glow_thickness", min = 1, max = 20, step = 1, half = true,
|
|
get = function() return get("glow_thickness", 1) end,
|
|
set = set("glow_thickness") })
|
|
table.insert(fields, { type = "toggle", name = "Border", key = "glow_border", half = true,
|
|
get = function() return get("glow_border", false) and true or false end,
|
|
set = set("glow_border") })
|
|
end
|
|
table.insert(fields, { type = "range", name = "Scale", key = "glow_scale", min = 0.05, max = 10, step = 0.05,
|
|
get = function() return get("glow_scale", 1) end,
|
|
set = set("glow_scale") })
|
|
table.insert(fields, { type = "range", name = "Extra width", key = "glow_extraWidth", min = -200, max = 200, step = 1, half = true,
|
|
get = function() return get("glow_extraWidth", 0) end,
|
|
set = set("glow_extraWidth") })
|
|
table.insert(fields, { type = "range", name = "Extra height", key = "glow_extraHeight", min = -200, max = 200, step = 1, half = true,
|
|
get = function() return get("glow_extraHeight", 0) end,
|
|
set = set("glow_extraHeight") })
|
|
table.insert(fields, { type = "range", name = "X Offset", key = "glow_XOffset", min = -100, max = 100, step = 1, half = true,
|
|
get = function() return get("glow_XOffset", 0) end,
|
|
set = set("glow_XOffset") })
|
|
table.insert(fields, { type = "range", name = "Y Offset", key = "glow_YOffset", min = -100, max = 100, step = 1, half = true,
|
|
get = function() return get("glow_YOffset", 0) end,
|
|
set = set("glow_YOffset") })
|
|
end
|