I currently have an addon written with AI that fades frames on certain conditions. I'm struggling with it and think it'll just be easier if it were implemented in DragonUI.
I'm willing to help with this if needed. Here's the Lua I've been using:
-- FadeFrameUI (WoW 3.3.5a)
local mouseInterval = 0.05
local fadeInTime = 0.2
local fadeOutTime = 1.0
local fadeIntermediate = 0.4
local fadeIdleDelay = 5.0
local fadeOutDelay = .25
local ENABLED_FADE_GROUPS = {
actionBars = true,
microBar = true,
bagBar = true,
mainMenuBar = true,
}
local FrameRegistry = {
frames = {},
names = {},
groups = {},
}
local opacity = {
actionBars = 0,
mainMenuBar = 0.2,
bagBar = 0,
microBar = 0,
minimap = 0,
player = 0,
buffs = 0,
buffButtons = 0,
debuffs = 1,
combat = 1,
target = 1,
lowHealth = 1,
casting = 1,
watch = 0,
party = 0,
raid = 0,
chatButtons = 0,
}
local GroupVisibilityRules = {
player = {
combat = 1,
target = 1,
lowHP = 1,
casting = 1,
idle = 0,
},
party = {
combat = 1,
target = 1,
lowHP = 1,
casting = 1,
idle = 0,
},
raid = {
combat = 1,
target = 1,
lowHP = 1,
casting = 1,
idle = 0,
},
actionBars = {
combat = 1,
target = 1,
lowHP = 0,
casting = 0,
idle = 0,
},
microBar = {
combat = 1,
target = 1,
lowHP = 0,
casting = 0,
idle = 0,
},
minimap = {
combat = 1,
target = 1,
lowHP = 0,
casting = 0,
idle = 0,
},
}
local frameTargetAlpha = {}
local FrameTreeScanned = {}
local frameLastShown = {}
local MinimapBorderTop = _G["MinimapBorderTop"]
local watchRescanQueued = false
local HOVER_EXPAND = {
player = { "player", "party", "raid" },
party = { "player", "party", "raid" },
raid = { "player", "party", "raid" },
mainMenuBar = { "mainMenuBar" },
actionBars = { "actionBars", "mainMenuBar" },
microBar = { "microBar", "bagBar" },
bagBar = { "microBar", "bagBar" },
watch = { "watch", "minimap" },
minimap = { "watch", "minimap" },
buffs = { "buffs" },
}
function FrameRegistry:Register(frame, group)
local name = frame:GetName()
if not frame then return end
if not ENABLED_FADE_GROUPS[group] then
return
end
if group == "microBar" and self.frames[frame] then
return
end
self.frames[frame] = group
if group then
self.groups[group] = self.groups[group] or {
set = {},
list = {}
}
local g = self.groups[group]
if g.set[frame] then return end
g.set[frame] = true
table.insert(g.list, frame)
end
if name then
self.names[name] = frame
end
end
local function GetPriorityState(state)
-- highest priority first
if state.inCombat then
return "combat"
end
if state.targetExists then
return "target"
end
if state.lowHP then
return "lowHP"
end
if state.casting then
return "casting"
end
return "idle"
end
local function IsUnitLowHP(unit)
if not UnitExists(unit) then return false end
local hp = UnitHealth(unit)
local max = UnitHealthMax(unit)
if not max or max == 0 then return false end
return (hp / max) < 0.8
end
local function AreRunesOnCooldown()
if not RuneFrame or not RuneButtonIndividual1 then return false end
for i = 1, 6 do
local start, duration = GetRuneCooldown(i)
if start and duration and duration > 0 then
if (start + duration - GetTime()) > 0.1 then
return true
end
end
end
return false
end
local function IsDescendantOf(frame, parent)
if not frame or not parent then return false end
while frame do
if frame == parent then
return true
end
frame = frame:GetParent()
end
return false
end
local function SafeWatchScan(frame, group)
if not frame then return end
if FrameTreeScanned[frame] then return end
FrameTreeScanned[frame] = true
local objType = frame:GetObjectType()
FrameRegistry:Register(frame, group)
if objType == "FontString" and frame.GetParent then
FrameRegistry.frames[frame] = group
end
if frame.GetChildren then
local children = { frame:GetChildren() }
for _, child in ipairs(children) do
SafeWatchScan(child, group)
end
end
end
local function RegisterWatchFrameTree()
if not WatchFrame then return end
SafeWatchScan(WatchFrame, "watch")
end
local function GetGroupOpacity(group)
if not group then return 0 end
local p = opacity[group]
if p ~= nil then return p end
if group:sub(1,5) == "party" then
return opacity.party
end
if group:sub(1,11) == "raid_member"
or group:sub(1,10) == "raid_group" then
return opacity.raid
end
return 0
end
local function HasExpiringBuff()
for i=1,40 do
local buff = _G["BuffButton"..i]
if buff and buff:IsVisible() then
if buff.flash and buff.flash:IsShown() then
return true
end
if buff.flashing then
return true
end
local _,_,_,_,_,duration,expirationTime = UnitBuff("player", i)
if expirationTime and duration and duration > 0 then
local remain = expirationTime - GetTime()
if remain > 0 and remain <= 50 then
return true
end
end
end
end
return false
end
local function SafeRegisterRaid(frame)
if not frame then return end
FrameRegistry:Register(frame, "raid")
if frame.GetChildren then
local children = { frame:GetChildren() }
for _, child in ipairs(children) do
if child then
SafeRegisterRaid(child)
end
end
end
end
local function IsHovered(group, hoveredGroup)
if not hoveredGroup then return false end
local set = HOVER_EXPAND[hoveredGroup]
if set then
for _, g in ipairs(set) do
if group == g then return true end
if g == "party" and group:sub(1,5) == "party" then return true end
if g == "raid" and group:sub(1,4) == "raid" then return true end
end
end
return false
end
local function NormalizeGroup(group)
if not group then return nil end
if group:match("^raid") then return "raid" end
if group:match("^party") then return "party" end
return group
end
local function ResetFrameAlphaCache()
frameTargetAlpha = {}
end
local function GetTargetAlpha(frame, group, state)
-- =========================
-- 1. PRIORITY STATE ENGINE
-- =========================
local priority = GetPriorityState(state)
-- if group == "buffButtons" then
-- group = "buffs"
-- end
local rules = GroupVisibilityRules[group]
local stateValue = nil
if rules then
stateValue = rules[priority]
end
-- default fallback state
if stateValue == nil then
stateValue = 0
end
-- =========================
-- 2. HARD OVERRIDES (absolute visibility)
-- =========================
if state.inCombat
or state.targetExists then
stateValue = math.max(stateValue, 1)
end
if group == "player" then
if IsUnitLowHP("player") or AreRunesOnCooldown() then
stateValue = 1
end
end
if group == "debuffs" then
return 1
end
if group == "minimapCore" then
return 1
end
if group == "buffs" then
if HasExpiringBuff() then
return 1
end
end
-- =========================
-- 3. HOVER OVERRIDE
-- =========================
if state.hoveredGroup then
if IsHovered(group, state.hoveredGroup) then
stateValue = 1
end
end
-- =========================
-- 4. FADE ENGINE (CRITICAL FIX)
-- =========================
if stateValue >= 1 then
return 1
end
-- THIS restores your original behavior:
-- idle -> fadeIntermediate -> hidden
local base = GetGroupOpacity(group)
-- restore idle visibility scaling properly
if stateValue > 0 then
return fadeIntermediate
end
return base
end
local function RegisterFrameTree(root, group)
if not root then return end
if FrameTreeScanned[root] then return end
local normGroup = NormalizeGroup(group)
local stack = { root }
while #stack > 0 do
local frame = table.remove(stack)
if frame and not FrameTreeScanned[frame] then
FrameTreeScanned[frame] = true
FrameRegistry:Register(frame, normGroup)
local children = { frame:GetChildren() }
for _, child in ipairs(children) do
table.insert(stack, child)
end
end
end
end
local function FadeFrameTo(frame, targetAlpha)
if not frame then return end
frame:SetScript("OnUpdate", nil)
local current = frame:GetAlpha()
if math.abs(current - targetAlpha) < 0.01 then return end
if InCombatLockdown() then
frame:SetAlpha(targetAlpha)
return
end
local fadeTime = (targetAlpha > current) and fadeInTime or fadeOutTime
local startTime = GetTime()
frame:SetScript("OnUpdate", function(self)
local elapsed = GetTime() - startTime
local progress = elapsed / fadeTime
if progress >= 1 then
self:SetAlpha(targetAlpha)
self:SetScript("OnUpdate", nil)
else
self:SetAlpha(current + (targetAlpha - current) * progress)
end
end)
end
local function InitializeFadeFrameUI()
-- =========================
-- MAIN ACTION BAR SYSTEM
-- =========================
FrameRegistry:Register(MainMenuBar, "mainMenuBar")
for i = 1, 12 do
local btn = _G["ActionButton" .. i]
if btn then
FrameRegistry:Register(btn, "mainMenuBar")
end
end
if MultiBarBottomLeft then
FrameRegistry:Register(MultiBarBottomLeft, "actionBars")
end
if MultiBarBottomRight then
FrameRegistry:Register(MultiBarBottomRight, "actionBars")
end
if MultiBarLeft then
FrameRegistry:Register(MultiBarLeft, "actionBars")
end
if MultiBarRight then
FrameRegistry:Register(MultiBarRight, "actionBars")
end
if StanceBarFrame then
FrameRegistry:Register(StanceBarFrame, "mainMenuBar")
end
if MainMenuBarArtFrame then
FrameRegistry:Register(MainMenuBarArtFrame,"mainMenuBar")
end
if MainMenuExpBar then
FrameRegistry:Register(MainMenuExpBar,"mainMenuBar")
end
-- =========================
-- BAG SYSTEM
-- =========================
if MainMenuBarBackpackButton then
FrameRegistry:Register(MainMenuBarBackpackButton, "bagBar")
end
for i = 0, 3 do
local bag = _G["CharacterBag" .. i .. "Slot"]
if bag then
FrameRegistry:Register(bag, "bagBar")
end
end
if KeyRingButton then
FrameRegistry:Register(KeyRingButton, "bagBar")
end
-- =========================
-- MICRO MENU
-- =========================
if MICRO_BUTTONS then
for _, btn in ipairs(MICRO_BUTTONS) do
if btn then
FrameRegistry:Register(btn, "microBar")
end
end
end
local function SafeRegisterMicro(frame)
if not frame then return end
if frame:IsObjectType("Button") then
FrameRegistry:Register(frame, "microBar")
end
for i = 1, frame:GetNumChildren() do
local child = select(i, frame:GetChildren())
if child and child:IsObjectType("Button") then
FrameRegistry:Register(child, "microBar")
end
end
end
-- SafeRegisterMicro(pUiMicroMenu)
-- SafeRegisterMicro(DragonUI_MicroMenu)
-- SafeRegisterMicro(DragonUI_MicroMenu_FULLSCREEN)
-- =========================
-- PLAYER SYSTEM
-- =========================
if PlayerFrame then
FrameRegistry:Register(PlayerFrame, "player")
end
if PetFrame then
FrameRegistry:Register(PetFrame, "player")
end
if RuneFrame then
FrameRegistry:Register(RuneFrame, "player")
end
-- =========================
-- BUFFS / WATCH
-- =========================
-- BUFFS / WATCH (SAFE MODE)
-- Do NOT fade BuffFrame; Blizzard controls buff animation
if BuffFrame and BuffFrame.toggleButton then
FrameRegistry:Register(BuffFrame.toggleButton, "buffs")
end
-- Register individual buff buttons again so they can fade independently
for i = 1,40 do
local buff = _G["BuffButton"..i]
if buff then
FrameRegistry:Register(buff, "buffButtons")
end
end
if RUI_BuffCollapseButton then
FrameRegistry:Register(RUI_BuffCollapseButton, "buffs")
end
if ObjectiveTrackerBlocksFrame then
FrameRegistry:Register(ObjectiveTrackerBlocksFrame, "watch")
end
-- if DragonUI_QuestTrackerFrame then
-- FrameRegistry:Register(DragonUI_QuestTrackerFrame, "watch")
-- end
RegisterWatchFrameTree()
-- =========================
-- PARTY FRAMES
-- =========================
for i = 1, 4 do
local f = _G["PartyMemberFrame" .. i]
if f then
FrameRegistry:Register(f, "party" .. i)
end
end
-- =========================
-- RAID / PULL OUT FRAMES
-- =========================
for i = 1, 10 do
local pullout = _G["RaidPullout" .. i]
if pullout then
SafeRegisterRaid(pullout)
end
end
-- =========================
-- MINIMAP CORE (NON-FADING) The minimap has a bug where if it is affected by alpha, it may disappear completely. I disabled the minimap from the minimap group and added it to the core.
-- =========================
if Minimap then
FrameRegistry:Register(Minimap, "minimapCore")
end
local minimapFrames = {
MinimapZoomIn,
MinimapZoomOut,
MinimapZoneTextButton,
MiniMapTracking,
MiniMapMailFrame,
MiniMapBattlefieldFrame,
GameTimeFrame,
TimeManagerClockButton,
}
if MinimapBorderTop then
FrameRegistry:Register(MinimapBorderTop, "watch")
end
for _, f in ipairs(minimapFrames) do
if f then
FrameRegistry:Register(f, "watch")
end
end
if RUI_MinimapFrame then
FrameRegistry:Register(RUI_MinimapFrame, "minimap")
end
if MinimapZoneTextButton and MinimapZoneTextButton.GetRegions then
local regions = { MinimapZoneTextButton:GetRegions() }
for _, r in ipairs(regions) do
if r then
FrameRegistry:Register(r, "watch")
end
end
end
-- =========================
-- DRAGON UI / CUSTOM UI
-- =========================
if pUiMainBar then
FrameRegistry:Register(pUiMainBar, "mainMenuBar")
end
if DragonUI_MainBar then
FrameRegistry:Register(DragonUI_MainBar, "mainMenuBar")
end
if pUiMainBarArt then
FrameRegistry:Register(pUiMainBarArt, "mainMenuBar")
end
if DragonUI_XPBar then
FrameRegistry:Register(DragonUI_XPBar, "mainMenuBar")
end
if pUiStanceBar then
FrameRegistry:Register(pUiStanceBar, "mainMenuBar")
end
-- =========================
-- CHAT
-- =========================
-- FrameRegistry:Register(ChatFrameMenuButton, "chatButtons")
-- FrameRegistry:Register(FriendsMicroButton, "chatButtons")
-- FrameRegistry:Register(ChatFrame1ButtonFrameUpButton, "chatButtons")
-- FrameRegistry:Register(ChatFrame1ButtonFrameDownButton, "chatButtons")
-- FrameRegistry:Register(ChatFrame1ButtonFrameBottomButton, "chatButtons")
-- =========================
-- RETAIL UI COMPAT (RUI)
-- =========================
if RUI_BagsBar then
for i = 1, RUI_BagsBar:GetNumChildren() do
local child = select(i, RUI_BagsBar:GetChildren())
if child and child:IsObjectType("Button") then
FrameRegistry:Register(child, "bagBar")
end
end
end
if RUI_ActionBar1 then
FrameRegistry:Register(RUI_ActionBar1, "mainMenuBar")
end
if RUI_ActionBar6 then
FrameRegistry:Register(RUI_ActionBar6, "mainMenuBar")
end
end
-- =========================
-- ONUPDATE LOOP
-- =========================
local function OnUpdate(self, elapsed)
local now = GetTime()
-- =========================
-- HOVER DETECTION
-- =========================
local function GetHoveredGroup()
local f = GetMouseFocus()
if not f or f == WorldFrame then
return nil
end
while f do
if WatchFrame and (
f == WatchFrame
or f == WatchFrameLines
or f == WatchFrameScrollChildFrame
or IsDescendantOf(f, WatchFrame)
) then
return "watch"
end
local group = FrameRegistry.frames[f]
if not group then
local p = f:GetParent()
while p and not group do
group = FrameRegistry.frames[p]
p = p:GetParent()
end
end
if group then
return NormalizeGroup(group)
end
f = f:GetParent()
end
return nil
end
local hoveredGroup = GetHoveredGroup()
-- =========================
-- STATE
-- =========================
local hp = UnitHealth("player") or 0
local maxhp = UnitHealthMax("player") or 1
local state = {
now = now,
inCombat = UnitAffectingCombat("player") and true or false,
casting = (UnitCastingInfo("player") or UnitChannelInfo("player")) and true or false,
targetExists = UnitExists("target") and true or false,
lowHP = (hp / maxhp) < 0.8,
runes = AreRunesOnCooldown(),
hoveredGroup = hoveredGroup,
}
-- =========================
-- APPLY FADES
-- =========================
if UnitInRaid("player") then
for i=1,10 do
local pullout = _G["RaidPullout"..i]
if pullout and not FrameRegistry.frames[pullout] then
SafeRegisterRaid(pullout)
end
end
end
if RUI_BuffCollapseButton and not FrameRegistry.frames[RUI_BuffCollapseButton] then
FrameRegistry:Register(RUI_BuffCollapseButton, "buffs")
end
for group, data in pairs(FrameRegistry.groups) do
for _, frame in ipairs(data.list) do
local target = GetTargetAlpha(frame, group, state)
if frameTargetAlpha[frame] ~= target then
frameTargetAlpha[frame] = target
FadeFrameTo(frame, target)
end
end
end
-- SyncDragonUIGlows()
end
-- =========================
-- Addon Protection
-- =========================
local function OnAddonLoaded(self, event, addon)
if addon == "Blizzard_TimeManager" and TimeManagerClockButton then
FrameRegistry:Register(TimeManagerClockButton, "minimap")
end
end
local clockWatcher = CreateFrame("Frame")
clockWatcher:RegisterEvent("ADDON_LOADED")
clockWatcher:SetScript("OnEvent", OnAddonLoaded)
local watchDirty = false
hooksecurefunc("WatchFrame_Update", function()
watchDirty = true
watchRescanQueued = true
end)
-- local function OnUpdateWatch()
-- if watchDirty then
-- watchDirty = false
-- SafeWatchRescan()
-- end
-- end
local function ReconcileMicroButtons()
local names = {
"CharacterMicroButton",
"SpellbookMicroButton",
"TalentMicroButton",
"AchievementMicroButton",
"QuestLogMicroButton",
"SocialsMicroButton",
"LFDMicroButton",
"MainMenuMicroButton",
"HelpMicroButton",
"CollectionsMicroButton",
"PVPMicroButton",
}
for _, name in ipairs(names) do
local f = _G[name]
if f then
FrameRegistry:Register(f, "microBar")
end
end
end
local loginFrame = CreateFrame("Frame")
loginFrame:RegisterEvent("PLAYER_LOGIN")
loginFrame:SetScript("OnEvent", function()
ZoneTextFrame:ClearAllPoints()
ZoneTextFrame:SetPoint("TOP", UIParent, "TOP", 0, -90)
InitializeFadeFrameUI()
ReconcileMicroButtons()
WatchFrame:SetUserPlaced(true)
WatchFrame:SetMovable(true)
WatchFrame:ClearAllPoints()
WatchFrame:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT", -40, -200)
WatchFrame:SetHeight(600)
WatchFrame:SetAlpha(1)
WatchFrameLines:SetAlpha(1)
ResetFrameAlphaCache()
end)
local updater = CreateFrame("Frame")
updater:SetScript("OnUpdate", OnUpdate)
As of now, Actionbars -> Visibility has the options to
Show on Hover OnlyandShow in Combat Only. The feature hide the bars chosen, and reveals immediately with a pop-in effect, like so:20260505-1331-45.1594432.mp4
Current DragonUI visibility options
The DragonUI visibility options also are limited to
Hover,Combat, orHover AND in Combat, but notHover OR in Combat. This is a bit limited.My requests are:
Fade as Groupoption that when selected for an action bar, fades and shows all frames with that checkbox as a group. An example: Bottom and Right Action Bars, if selected to fade as a group, both show and hide together based on other conditions.I currently have an addon written with AI that fades frames on certain conditions. I'm struggling with it and think it'll just be easier if it were implemented in DragonUI.
20260505-1329-41.4014563.mp4
Proper fade goal with my addon the AI made
I'm willing to help with this if needed. Here's the Lua I've been using: