diff --git a/!Compatibility/api/wowAPI.lua b/!Compatibility/api/wowAPI.lua index 033ba85..56af38a 100644 --- a/!Compatibility/api/wowAPI.lua +++ b/!Compatibility/api/wowAPI.lua @@ -114,9 +114,9 @@ function hooksecurefunc(a1, a2, a3) local original_func = a1[a2] - a1[a2] = function(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) - local r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 = original_func(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) - a3(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) + a1[a2] = function(...) + local r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 = original_func(unpack(arg)) + a3(unpack(arg)) return r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 end diff --git a/ElvUI/Core/toolkit.lua b/ElvUI/Core/toolkit.lua index f696d77..d350565 100644 --- a/ElvUI/Core/toolkit.lua +++ b/ElvUI/Core/toolkit.lua @@ -45,22 +45,26 @@ local function GetTemplate(t, isUnitFrameElement) end function E:Size(frame, width, height) + if not frame then return end assert(width) frame:SetWidth(E:Scale(width)) frame:SetHeight(E:Scale(height or width)) end function E:Width(frame, width) + if not frame then return end assert(width) frame:SetWidth(E:Scale(width)) end function E:Height(frame, height) + if not frame then return end assert(height) frame:SetHeight(E:Scale(height)) end function E:Point(obj, arg1, arg2, arg3, arg4, arg5) + if not obj then return end if arg2 == nil then arg2 = obj:GetParent() end if type(arg2)=="number" then arg2 = E:Scale(arg2) end @@ -77,7 +81,7 @@ function E:SetOutside(obj, anchor, xOffset, yOffset, anchor2) yOffset = yOffset or E.Border anchor = anchor or obj:GetParent() - assert(anchor) + if not anchor then return end if obj:GetPoint() then obj:ClearAllPoints() end @@ -92,7 +96,7 @@ function E:SetInside(obj, anchor, xOffset, yOffset, anchor2) yOffset = yOffset or E.Border anchor = anchor or obj:GetParent() - assert(anchor) + if not anchor then return end if obj:GetPoint() then obj:ClearAllPoints() end @@ -102,7 +106,7 @@ function E:SetInside(obj, anchor, xOffset, yOffset, anchor2) end function E:SetTemplate(f, t, glossTex, ignoreUpdates, forcePixelMode, isUnitFrameElement) - if not f then return end + if not f or type(f) ~= "table" then return end GetTemplate(t, isUnitFrameElement) if t then @@ -187,6 +191,7 @@ function E:SetTemplate(f, t, glossTex, ignoreUpdates, forcePixelMode, isUnitFram end function E:CreateBackdrop(f, t, tex, ignoreUpdates, forcePixelMode, isUnitFrameElement) + if not f then return end if not t then t = "Default" end local parent = f @@ -199,8 +204,16 @@ function E:CreateBackdrop(f, t, tex, ignoreUpdates, forcePixelMode, isUnitFrameE if parent and parent.IsObjectType and not parent:IsObjectType("Frame") then parent = UIParent end + if not parent or type(parent) ~= "table" or not parent.GetObjectType then + parent = UIParent + end - local b = f.backdrop or CreateFrame("Frame", nil, parent) + local b = (type(f.backdrop) == "table" and f.backdrop) + if not b then + b = CreateFrame("Frame", nil) + pcall(function() b:SetParent(parent) end) + end + if f.forcePixelMode or forcePixelMode then E:SetOutside(b, nil, E.mult, E.mult) else @@ -247,7 +260,8 @@ function E:Kill(object) end function E:StripTextures(object, kill, alpha) - if object:IsObjectType("Texture") then + if not object or type(object) ~= "table" then return end + if object.IsObjectType and object:IsObjectType("Texture") then if kill then E:Kill(object) elseif alpha then diff --git a/ElvUI/Modules/Skins/Blizzard/Misc.lua b/ElvUI/Modules/Skins/Blizzard/Misc.lua index 048ebf4..ae253b0 100644 --- a/ElvUI/Modules/Skins/Blizzard/Misc.lua +++ b/ElvUI/Modules/Skins/Blizzard/Misc.lua @@ -225,14 +225,22 @@ local function LoadSkin() end -- if a button position is not really where we want, we move it here - OptionsFrameCancel:ClearAllPoints() - E:Point(OptionsFrameCancel, "BOTTOMLEFT",OptionsFrame,"BOTTOMRIGHT",-105,15) - OptionsFrameOkay:ClearAllPoints() - E:Point(OptionsFrameOkay, "RIGHT",OptionsFrameCancel,"LEFT",-4,0) - SoundOptionsFrameOkay:ClearAllPoints() - E:Point(SoundOptionsFrameOkay, "RIGHT",SoundOptionsFrameCancel,"LEFT",-4,0) - UIOptionsFrameOkay:ClearAllPoints() - E:Point(UIOptionsFrameOkay, "RIGHT",UIOptionsFrameCancel,"LEFT", -4,0) + if OptionsFrameCancel then + OptionsFrameCancel:ClearAllPoints() + E:Point(OptionsFrameCancel, "BOTTOMLEFT",OptionsFrame,"BOTTOMRIGHT",-105,15) + end + if OptionsFrameOkay and OptionsFrameCancel then + OptionsFrameOkay:ClearAllPoints() + E:Point(OptionsFrameOkay, "RIGHT",OptionsFrameCancel,"LEFT",-4,0) + end + if SoundOptionsFrameOkay and SoundOptionsFrameCancel then + SoundOptionsFrameOkay:ClearAllPoints() + E:Point(SoundOptionsFrameOkay, "RIGHT",SoundOptionsFrameCancel,"LEFT",-4,0) + end + if UIOptionsFrameOkay and UIOptionsFrameCancel then + UIOptionsFrameOkay:ClearAllPoints() + E:Point(UIOptionsFrameOkay, "RIGHT",UIOptionsFrameCancel,"LEFT", -4,0) + end -- others ZoneTextFrame:ClearAllPoints() @@ -261,12 +269,14 @@ local function LoadSkin() S:HandleSliderFrame(OpacityFrameSlider) -- Interface Options - UIOptionsFrame:SetParent(E.UIParent) - UIOptionsFrame:EnableMouse(false) + if UIOptionsFrame then + UIOptionsFrame:SetParent(E.UIParent) + UIOptionsFrame:EnableMouse(false) - hooksecurefunc("UIOptionsFrame_Load", function() - E:StripTextures(UIOptionsFrame) - end) + hooksecurefunc("UIOptionsFrame_Load", function() + E:StripTextures(UIOptionsFrame) + end) + end local UIOptions = { "BasicOptions", @@ -286,52 +296,62 @@ local function LoadSkin() E:SetTemplate(options, "Transparent") end - BasicOptions.backdrop = CreateFrame("Frame", nil, BasicOptions) - E:Point(BasicOptions.backdrop, "TOPLEFT", BasicOptionsGeneral, -20, 35) - E:Point(BasicOptions.backdrop, "BOTTOMRIGHT", BasicOptionsHelp, 20, -130) - E:SetTemplate(BasicOptions.backdrop, "Transparent") + if BasicOptions and BasicOptionsGeneral and BasicOptionsHelp then + BasicOptions.backdrop = CreateFrame("Frame", nil, BasicOptions) + E:Point(BasicOptions.backdrop, "TOPLEFT", BasicOptionsGeneral, -20, 35) + E:Point(BasicOptions.backdrop, "BOTTOMRIGHT", BasicOptionsHelp, 20, -130) + E:SetTemplate(BasicOptions.backdrop, "Transparent") + end - AdvancedOptions.backdrop = CreateFrame("Frame", nil, AdvancedOptions) - E:Point(AdvancedOptions.backdrop, "TOPLEFT", BasicOptionsGeneral, -20, 35) - E:Point(AdvancedOptions.backdrop, "BOTTOMRIGHT", BasicOptionsHelp, 20, -130) - E:SetTemplate(AdvancedOptions.backdrop, "Transparent") + if AdvancedOptions and BasicOptionsGeneral and AdvancedOptionsCombatText then + AdvancedOptions.backdrop = CreateFrame("Frame", nil, AdvancedOptions) + E:Point(AdvancedOptions.backdrop, "TOPLEFT", BasicOptionsGeneral, -20, 35) + E:Point(AdvancedOptions.backdrop, "BOTTOMRIGHT", AdvancedOptionsCombatText, 20, -130) + E:SetTemplate(AdvancedOptions.backdrop, "Transparent") + end for i = 1, 2 do local tab = _G["UIOptionsFrameTab"..i] - E:StripTextures(tab, true) - E:CreateBackdrop(tab, "Transparent") + if tab then + E:StripTextures(tab, true) + E:CreateBackdrop(tab, "Transparent") - tab:SetFrameLevel(tab:GetParent():GetFrameLevel() + 2) - tab.backdrop:SetFrameLevel(tab:GetParent():GetFrameLevel() + 1) + tab:SetFrameLevel(tab:GetParent():GetFrameLevel() + 2) + tab.backdrop:SetFrameLevel(tab:GetParent():GetFrameLevel() + 1) - E:Point(tab.backdrop, "TOPLEFT", 5, E.PixelMode and -14 or -16) - E:Point(tab.backdrop, "BOTTOMRIGHT", -5, E.PixelMode and -4 or -6) + E:Point(tab.backdrop, "TOPLEFT", 5, E.PixelMode and -14 or -16) + E:Point(tab.backdrop, "BOTTOMRIGHT", -5, E.PixelMode and -4 or -6) - tab:SetScript("OnClick", function() - PanelTemplates_Tab_OnClick(UIOptionsFrame) - if AdvancedOptions:IsShown() then - BasicOptions:Show() - AdvancedOptions:Hide() - else - BasicOptions:Hide() - AdvancedOptions:Show() - end - PlaySound("igCharacterInfoTab") - end) + tab:SetScript("OnClick", function() + PanelTemplates_Tab_OnClick(UIOptionsFrame) + if AdvancedOptions:IsShown() then + BasicOptions:Show() + AdvancedOptions:Hide() + else + BasicOptions:Hide() + AdvancedOptions:Show() + end + PlaySound("igCharacterInfoTab") + end) - HookScript(tab, "OnEnter", S.SetModifiedBackdrop) - HookScript(tab, "OnLeave", S.SetOriginalBackdrop) - end - - for _, child in ipairs({UIOptionsFrame:GetChildren()}) do - if child.GetPushedTexture and child:GetPushedTexture() and not child:GetName() then - child:SetFrameLevel(UIOptionsFrame:GetFrameLevel() + 2) - S:HandleCloseButton(child, UIOptionsFrame.backdrop) + HookScript(tab, "OnEnter", S.SetModifiedBackdrop) + HookScript(tab, "OnLeave", S.SetOriginalBackdrop) end end - OptionsFrameDefaults:ClearAllPoints() - E:Point(OptionsFrameDefaults, "TOPLEFT", OptionsFrame, "BOTTOMLEFT", 15, 36) + if UIOptionsFrame then + for _, child in ipairs({UIOptionsFrame:GetChildren()}) do + if child.GetPushedTexture and child:GetPushedTexture() and not child:GetName() then + child:SetFrameLevel(UIOptionsFrame:GetFrameLevel() + 2) + S:HandleCloseButton(child, UIOptionsFrame.backdrop) + end + end + end + + if OptionsFrameDefaults then + OptionsFrameDefaults:ClearAllPoints() + E:Point(OptionsFrameDefaults, "TOPLEFT", OptionsFrame, "BOTTOMLEFT", 15, 36) + end S:HandleButton(UIOptionsFrameResetTutorials) @@ -368,48 +388,69 @@ local function LoadSkin() end -- Interface Options Checkboxes - for _, value in UIOptionsFrameCheckButtons do - local UIOptionsFrameCheckBox = _G["UIOptionsFrameCheckButton"..value.index] - if value.index == 7 then - E:Kill(UIOptionsFrameCheckBox) - end - if value.index == 8 then - E:Point(UIOptionsFrameCheckBox, "TOPLEFT", 10, -8) - end - if UIOptionsFrameCheckBox then - S:HandleCheckBox(UIOptionsFrameCheckBox) + if type(UIOptionsFrameCheckButtons) == "table" then + for _, value in pairs(UIOptionsFrameCheckButtons) do + local index = type(value) == "table" and value.index or value + local UIOptionsFrameCheckBox = _G["UIOptionsFrameCheckButton"..index] + if index == 7 and UIOptionsFrameCheckBox then + E:Kill(UIOptionsFrameCheckBox) + end + if index == 8 and UIOptionsFrameCheckBox then + E:Point(UIOptionsFrameCheckBox, "TOPLEFT", 10, -8) + end + if UIOptionsFrameCheckBox then + S:HandleCheckBox(UIOptionsFrameCheckBox) + end end end -- Video Options Checkboxes - for _, value in OptionsFrameCheckButtons do - local OptionsFrameCheckButton = _G["OptionsFrameCheckButton"..value.index] - if OptionsFrameCheckButton then - S:HandleCheckBox(OptionsFrameCheckButton) + if type(OptionsFrameCheckButtons) == "table" then + for _, value in pairs(OptionsFrameCheckButtons) do + local index = type(value) == "table" and value.index or value + local OptionsFrameCheckButton = _G["OptionsFrameCheckButton"..index] + if OptionsFrameCheckButton then + S:HandleCheckBox(OptionsFrameCheckButton) + end end end -- Sound Options Checkboxes - for _, value in SoundOptionsFrameCheckButtons do - local SoundOptionsFrameCheckButton = _G["SoundOptionsFrameCheckButton"..value.index] - if SoundOptionsFrameCheckButton then - S:HandleCheckBox(SoundOptionsFrameCheckButton) + if type(SoundOptionsFrameCheckButtons) == "table" then + for _, value in pairs(SoundOptionsFrameCheckButtons) do + local index = type(value) == "table" and value.index or value + local SoundOptionsFrameCheckButton = _G["SoundOptionsFrameCheckButton"..index] + if SoundOptionsFrameCheckButton then + S:HandleCheckBox(SoundOptionsFrameCheckButton) + end end end -- Interface Options Sliders - for i in UIOptionsFrameSliders do - S:HandleSliderFrame(_G["UIOptionsFrameSlider"..i]) + if type(UIOptionsFrameSliders) == "table" then + for _, value in pairs(UIOptionsFrameSliders) do + local index = type(value) == "table" and value.index or value + local slider = _G["UIOptionsFrameSlider"..index] + if slider then S:HandleSliderFrame(slider) end + end end -- Video Options Sliders - for i in OptionsFrameSliders do - S:HandleSliderFrame(_G["OptionsFrameSlider"..i]) + if type(OptionsFrameSliders) == "table" then + for _, value in pairs(OptionsFrameSliders) do + local index = type(value) == "table" and value.index or value + local slider = _G["OptionsFrameSlider"..index] + if slider then S:HandleSliderFrame(slider) end + end end -- Sound Options Sliders - for i in SoundOptionsFrameSliders do - S:HandleSliderFrame(_G["SoundOptionsFrameSlider"..i]) + if type(SoundOptionsFrameSliders) == "table" then + for _, value in pairs(SoundOptionsFrameSliders) do + local index = type(value) == "table" and value.index or value + local slider = _G["SoundOptionsFrameSlider"..index] + if slider then S:HandleSliderFrame(slider) end + end end end diff --git a/ElvUI/Modules/Skins/Skins.lua b/ElvUI/Modules/Skins/Skins.lua index 62cb6c0..37f2336 100644 --- a/ElvUI/Modules/Skins/Skins.lua +++ b/ElvUI/Modules/Skins/Skins.lua @@ -47,6 +47,7 @@ function S:SetOriginalBackdrop() end function S:HandleButton(f, strip, isDeclineButton) + if not f then return end local name = f:GetName() if name then local left = _G[name.."Left"] diff --git a/README.md b/README.md index 736e435..b53cc97 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ - **Fix:** Made ElvUI's backported `table.wipe` resilient to `nil` values, fixing crashes with native Turtle WoW UI scripts (like `Turtle_ShopUI.lua`) and lazy addons. - **Fix:** Added type safety checks to `SetOutside` and `SetInside` to prevent crashes when other addons overwrite frame globals with dummy functions. - **Fix:** Added type safety check before calling `SetTexture("")` on Blizzard header frames, preventing crashes when headers are modified by other addons or the custom client. +- **Fix:** Wrapped `SetParent` in `pcall` within `CreateBackdrop` to prevent crashes on non-UI object dummy tables. +- **Fix:** Added graceful `nil` fallbacks to `E:Point`, `E:Size`, `E:Width`, and `E:Height` layout anchors, immunizing the UI against crashes when Turtle WoW or other addons delete elements. +- **Fix:** Rewrote `Misc.lua` options skinning loops using `pairs()` to resolve Lua 5.0 `attempt to call a nil value` crashes caused by missing options tabs/sliders/checkboxes. +- **Fix:** Added element existence checks in `Skins.lua` `HandleButton` to skip skinning gracefully instead of crashing on `nil`. This is the backported version of ElvUI for World of Warcraft - Vanilla (1.12.1)