Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# DandersFrames Changelog

## [Unreleased]

### New Features

* (Frames) Missing textures now fall back to a bundled default instead of rendering black. If a profile you import references a custom or shared-media texture you don't have installed (or the addon that provided it was removed), the affected health bar, background or other bar now uses DandersFrames' default texture and shows a one-time notice — rather than a black/blank bar. (Requires WoW 12.0.7; on earlier versions there is no change.) (by Krathe)

## [4.4.0]

* Behind-the-scenes improvements and groundwork for upcoming features.
Expand Down
69 changes: 68 additions & 1 deletion Core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,73 @@ function DF:LightweightUpdateBackgroundAlpha()
IterateFramesInMode(mode, UpdateBG)
end

-- ============================================================
-- SAFE TEXTURE SETTERS — graceful missing-texture fallback
-- A configured texture can be missing when a profile imported from another user
-- references a 3rd-party/SharedMedia texture this client doesn't have (or the
-- providing addon was removed) — leaving a black/blank bar. C_UIFileAsset
-- (NEW in WoW 12.0.7) — IsKnownFile(asset) reports whether a path is known to
-- the client (shipped OR a known loose addon file); when it says the asset is
-- unknown we substitute a guaranteed-present stock texture. (Note: the API
-- doesn't verify a known loose file still exists on disk, but an uninstalled
-- addon's path is simply not "known", which is exactly the import case we want.)
-- The SetTexture/SetStatusBarTexture `success` bool does NOT work for this —
-- it returns true for any well-formed path even when the file is absent.
-- Feature-detected: on clients without C_UIFileAsset this is INERT (behaves
-- exactly as before), so it's safe to ship now and self-activates on 12.0.7.
-- ============================================================
-- DF's own bundled default bar texture — ships with the addon, so it's always
-- present when our code runs. This is the "fall back to our default" target.
DF.STOCK_BAR_TEXTURE = "Interface\\AddOns\\DandersFrames\\Media\\DF_Minimalist"
local _df_warnedMissingTexture = {}

-- false -> asset (texture path or fileID) is definitively NOT known to the client
-- true -> known/present
-- nil -> validation API unavailable (caller leaves the texture as-is)
local function textureKnown(asset)
if asset == nil then return nil end
local api = C_UIFileAsset
if not (api and api.IsKnownFile) then return nil end
local ok, known = pcall(api.IsKnownFile, asset)
if not ok then return nil end
return known and true or false
end

local function warnMissingTexture(path)
if not path or _df_warnedMissingTexture[path] then return end
_df_warnedMissingTexture[path] = true
if DF.Debug then DF:Debug("TEXTURE", "Missing texture '%s' — using stock fallback", tostring(path)) end
if not DF._warnedAnyMissingTexture then
DF._warnedAnyMissingTexture = true
print("|cff66ccffDandersFrames|r: a configured texture couldn't be loaded and was replaced with a stock texture. Check your texture settings (an imported profile may reference a texture you don't have).")
end
end

-- StatusBar texture with stock fallback. Returns true if the requested texture
-- loaded, false if the stock fallback was substituted, nil if bar was missing.
function DF:SafeSetStatusBarTexture(bar, path, stock)
if not bar then return end
if textureKnown(path) == false then
bar:SetStatusBarTexture(stock or DF.STOCK_BAR_TEXTURE)
warnMissingTexture(path)
return false
end
bar:SetStatusBarTexture(path)
return true
end

-- Plain Texture region with stock fallback (same semantics).
function DF:SafeSetTexture(region, path, stock)
if not region then return end
if textureKnown(path) == false then
region:SetTexture(stock or DF.STOCK_BAR_TEXTURE)
warnMissingTexture(path)
return false
end
region:SetTexture(path)
return true
end

-- Update only health bar texture
function DF:LightweightUpdateHealthTexture()
local mode = DF.GUI and DF.GUI.SelectedMode or "party"
Expand All @@ -442,7 +509,7 @@ function DF:LightweightUpdateHealthTexture()

local function UpdateTex(frame)
if frame and frame.healthBar then
frame.healthBar:SetStatusBarTexture(tex)
DF:SafeSetStatusBarTexture(frame.healthBar, tex)
end
end

Expand Down
10 changes: 5 additions & 5 deletions Frames/Bars.lua
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ function DF:UpdateAbsorb(frame, testIndex)
barTex:SetDrawLayer("ARTWORK", 2)
end
else
customBar:SetStatusBarTexture(tex)
DF:SafeSetStatusBarTexture(customBar, tex)
local barTex = customBar:GetStatusBarTexture()
if barTex then
barTex:SetHorizTile(false)
Expand Down Expand Up @@ -1106,7 +1106,7 @@ function DF:UpdateAbsorb(frame, testIndex)
if type(texture) == "table" then
texture = texture.path or "Interface\\TargetingFrame\\UI-StatusBar"
end
overflowBar:SetStatusBarTexture(texture)
DF:SafeSetStatusBarTexture(overflowBar, texture)

local color = db.absorbBarColor or {r = 1, g = 1, b = 1, a = 0.7}
overflowBar:SetStatusBarColor(color.r, color.g, color.b, color.a or 0.7)
Expand Down Expand Up @@ -1395,7 +1395,7 @@ function DF:UpdateHealAbsorb(frame, testIndex)
-- Apply texture only if changed to prevent flickering
if bar.currentTexture ~= tex then
bar.currentTexture = tex
bar:SetStatusBarTexture(tex)
DF:SafeSetStatusBarTexture(bar, tex)
local barTex = bar:GetStatusBarTexture()
if barTex then
barTex:SetHorizTile(false)
Expand Down Expand Up @@ -1674,7 +1674,7 @@ end
local function StyleHealPredSegment(seg, tex, blendMode, color)
if seg.currentTexture ~= tex then
seg.currentTexture = tex
seg:SetStatusBarTexture(tex)
DF:SafeSetStatusBarTexture(seg, tex)
local t = seg:GetStatusBarTexture()
if t then
t:SetHorizTile(false); t:SetVertTile(false)
Expand Down Expand Up @@ -1887,7 +1887,7 @@ function DF:UpdateHealPrediction(frame, testIndex)
-- Apply texture
if bar.currentTexture ~= tex then
bar.currentTexture = tex
bar:SetStatusBarTexture(tex)
DF:SafeSetStatusBarTexture(bar, tex)
local barTex = bar:GetStatusBarTexture()
if barTex then
barTex:SetHorizTile(false)
Expand Down
4 changes: 2 additions & 2 deletions Frames/Core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ local function SetMissingHealthBarValue(bar, unit, frame)
if not texture or texture == "" then
texture = db and db.healthTexture or "Interface\\TargetingFrame\\UI-StatusBar"
end
bar:SetStatusBarTexture(texture)
DF:SafeSetStatusBarTexture(bar, texture)

-- Update color based on color mode
local colorMode = db and db.missingHealthColorMode or "CUSTOM"
local r, g, b, a
Expand Down
8 changes: 4 additions & 4 deletions Frames/Create.lua
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ function DF:CreateFrameElements(frame, isRaid)
local padding = db.framePadding or 0
frame.missingHealthBar:SetPoint("TOPLEFT", padding, -padding)
frame.missingHealthBar:SetPoint("BOTTOMRIGHT", -padding, padding)
frame.missingHealthBar:SetStatusBarTexture(db.healthTexture or "Interface\\TargetingFrame\\UI-StatusBar")
DF:SafeSetStatusBarTexture(frame.missingHealthBar, db.healthTexture or "Interface\\TargetingFrame\\UI-StatusBar")
frame.missingHealthBar:SetMinMaxValues(0, 1)
frame.missingHealthBar:SetValue(0)
frame.missingHealthBar:SetReverseFill(true)
Expand All @@ -752,7 +752,7 @@ function DF:CreateFrameElements(frame, isRaid)
frame.healthBar = CreateFrame("StatusBar", nil, frame)
frame.healthBar:SetPoint("TOPLEFT", padding, -padding)
frame.healthBar:SetPoint("BOTTOMRIGHT", -padding, padding)
frame.healthBar:SetStatusBarTexture(db.healthTexture or "Interface\\TargetingFrame\\UI-StatusBar")
DF:SafeSetStatusBarTexture(frame.healthBar, db.healthTexture or "Interface\\TargetingFrame\\UI-StatusBar")
frame.healthBar:SetMinMaxValues(0, 1)
frame.healthBar:SetValue(1)

Expand Down Expand Up @@ -1421,7 +1421,7 @@ function DF:CreateUnitFrame(unit, index, isRaid)
local padding = db.framePadding or 0
frame.missingHealthBar:SetPoint("TOPLEFT", padding, -padding)
frame.missingHealthBar:SetPoint("BOTTOMRIGHT", -padding, padding)
frame.missingHealthBar:SetStatusBarTexture(db.healthTexture or "Interface\\TargetingFrame\\UI-StatusBar")
DF:SafeSetStatusBarTexture(frame.missingHealthBar, db.healthTexture or "Interface\\TargetingFrame\\UI-StatusBar")
frame.missingHealthBar:SetMinMaxValues(0, 1) -- Will be updated dynamically with UnitHealthMax
frame.missingHealthBar:SetValue(0)
frame.missingHealthBar:SetReverseFill(true) -- Fill from right side (where health is missing)
Expand All @@ -1437,7 +1437,7 @@ function DF:CreateUnitFrame(unit, index, isRaid)
frame.healthBar = CreateFrame("StatusBar", nil, frame)
frame.healthBar:SetPoint("TOPLEFT", padding, -padding)
frame.healthBar:SetPoint("BOTTOMRIGHT", -padding, padding)
frame.healthBar:SetStatusBarTexture(db.healthTexture or "Interface\\TargetingFrame\\UI-StatusBar")
DF:SafeSetStatusBarTexture(frame.healthBar, db.healthTexture or "Interface\\TargetingFrame\\UI-StatusBar")
frame.healthBar:SetMinMaxValues(0, 1)
frame.healthBar:SetValue(1)

Expand Down
4 changes: 2 additions & 2 deletions Frames/Pets.lua
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,8 @@ function DF:ApplyPetFrameStyle(frame)

-- Update health bar texture - use path directly (dropdown saves paths)
local texture = db.petTexture or "Interface\\TargetingFrame\\UI-StatusBar"
frame.healthBar:SetStatusBarTexture(texture)
frame.healthBar.bg:SetTexture(texture)
DF:SafeSetStatusBarTexture(frame.healthBar, texture)
DF:SafeSetTexture(frame.healthBar.bg, texture)

-- Background color
local bgColor = db.petBackgroundColor or {r = 0.1, g = 0.1, b = 0.1, a = 0.8}
Expand Down
2 changes: 1 addition & 1 deletion Frames/ReducedMaxHealth.lua
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ function DF:UpdateReducedMaxHealth(frame)
texturePath = DF:ResolveMediaTexture(texturePath) or texturePath
end
if texturePath then
bar:SetStatusBarTexture(texturePath)
DF:SafeSetStatusBarTexture(bar, texturePath)
end

local c = db.reducedMaxHealthColor or DEFAULT_BAR_COLOR
Expand Down
6 changes: 3 additions & 3 deletions Frames/Update.lua
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ function DF:ApplyFrameLayout(frame)
else
-- Textured background - only call SetTexture if texture path changed
if frame.dfCurrentBgTexture ~= bgTexture then
frame.background:SetTexture(bgTexture)
DF:SafeSetTexture(frame.background, bgTexture)
frame.background:SetHorizTile(false)
frame.background:SetVertTile(false)
frame.dfCurrentBgTexture = bgTexture
Expand Down Expand Up @@ -456,7 +456,7 @@ function DF:ApplyFrameLayout(frame)
else
-- Textured background - only call SetTexture if texture path changed
if frame.dfCurrentBgTexture ~= bgTexture then
frame.background:SetTexture(bgTexture)
DF:SafeSetTexture(frame.background, bgTexture)
frame.background:SetHorizTile(false)
frame.background:SetVertTile(false)
frame.dfCurrentBgTexture = bgTexture
Expand Down Expand Up @@ -760,7 +760,7 @@ function DF:UpdateUnitFrame(frame, source)
-- Textured background - only call SetTexture if texture path changed
-- This prevents flickering on every health update
if frame.dfCurrentBgTexture ~= bgTexture then
frame.background:SetTexture(bgTexture)
DF:SafeSetTexture(frame.background, bgTexture)
frame.background:SetHorizTile(false)
frame.background:SetVertTile(false)
frame.dfCurrentBgTexture = bgTexture
Expand Down