Skip to content

Commit 711133f

Browse files
Release v3.8.5
1 parent c2687d9 commit 711133f

28 files changed

Lines changed: 2289 additions & 831 deletions

EUI_PartyMode_Options.lua

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,6 @@ do
308308
local totalW = parent:GetWidth() - EllesmereUI.CONTENT_PAD * 2
309309
local sep = parent:CreateTexture(nil, "ARTWORK", nil, 7)
310310
sep:SetColorTexture(EllesmereUI.BORDER_R, EllesmereUI.BORDER_G, EllesmereUI.BORDER_B, 0.02)
311-
EllesmereUI.DisablePixelSnap(sep)
312311
PP.Size(sep, totalW, 1)
313312
PP.Point(sep, "TOPLEFT", parent, "TOPLEFT", EllesmereUI.CONTENT_PAD, y + 1)
314313
end

EUI_UnlockMode.lua

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -846,9 +846,6 @@ local function GetGuide(idx)
846846
if guidePool[idx] then return guidePool[idx] end
847847
local tex = unlockFrame:CreateTexture(nil, "OVERLAY", nil, 6)
848848
tex:SetColorTexture(1, 1, 1, 1)
849-
local PP = EllesmereUI and EllesmereUI.PP
850-
if PP then PP.DisablePixelSnap(tex)
851-
elseif tex.SetSnapToPixelGrid then tex:SetSnapToPixelGrid(false); tex:SetTexelSnappingBias(0) end
852849
guidePool[idx] = tex
853850
return tex
854851
end

EUI__General_Options.lua

Lines changed: 175 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ local PAGE_CORE = "Quick Setup"
2222
local PAGE_COLORS = "Fonts & Colors"
2323
local PAGE_PROFILES = "Profiles"
2424

25+
-- CDM Layout import pending state (persists across page rebuilds)
26+
local _cdmPendingLayout = nil
27+
local _cdmMissingSpells = nil
28+
2529
-------------------------------------------------------------------------------
2630
-- FCT font — handled by EllesmereUI_Startup.lua which runs earlier.
2731
-------------------------------------------------------------------------------
@@ -503,7 +507,7 @@ initFrame:SetScript("OnEvent", function(self)
503507
-- Optimized graphics CVar table + buttons (above all sections)
504508
-------------------------------------------------------------------
505509
local OPTIMIZED_CVARS = {
506-
{ "graphicsShadowQuality", "0" },
510+
{ "graphicsShadowQuality", "1" },
507511
{ "graphicsLiquidDetail", "0" },
508512
{ "graphicsParticleDensity", "5" },
509513
{ "graphicsSSAO", "0" },
@@ -621,7 +625,7 @@ initFrame:SetScript("OnEvent", function(self)
621625
EllesmereUI:ShowInfoPopup({
622626
title = "FPS & Graphics Optimization",
623627
content = "This feature optimizes your in-game graphics settings to give you the best combination of high FPS and visual clarity.\n\nYou can revert all changes at any time by clicking \"Restore My Settings\" which will appear after optimizing.\n\n\nWhat we change:\n\n"
624-
.. "Shadow Quality - Disabled (large FPS gain)\n"
628+
.. "Shadow Quality - Fair (balanced quality/FPS)\n"
625629
.. "Liquid Detail - Disabled\n"
626630
.. "Particle Density - Set to Ultra (keeps important spell effects)\n"
627631
.. "SSAO (Ambient Occlusion) - Disabled\n"
@@ -3351,15 +3355,181 @@ initFrame:SetScript("OnEvent", function(self)
33513355
end
33523356
end
33533357

3358+
-------------------------------------------------------------------
3359+
-- CDM LAYOUT PROFILES section
3360+
-------------------------------------------------------------------
3361+
if C_AddOns.IsAddOnLoaded("EllesmereUICooldownManager") then
3362+
_, h = W:SectionHeader(parent, "CDM LAYOUT PROFILES", y); y = y - h
3363+
3364+
-- Export CDM layout button
3365+
_, h = W:WideButton(parent, "Export CDM Layout", y, function()
3366+
local str, err = EllesmereUI.ExportCDMLayout()
3367+
if str then
3368+
EllesmereUI:ShowExportPopup(str)
3369+
else
3370+
EllesmereUI:ShowInfoPopup({
3371+
title = "Export Failed",
3372+
content = err or "Unknown error",
3373+
})
3374+
end
3375+
end); y = y - h
3376+
3377+
-- Import CDM layout button
3378+
_, h = W:WideButton(parent, "Import CDM Layout", y, function()
3379+
EllesmereUI:ShowImportPopup(function(importStr)
3380+
local layoutData, err = EllesmereUI.DecodeCDMLayoutString(importStr)
3381+
if not layoutData then
3382+
EllesmereUI:ShowInfoPopup({
3383+
title = "Import Failed",
3384+
content = err or "Unknown error",
3385+
})
3386+
return
3387+
end
3388+
3389+
-- Analyze which spells are missing
3390+
local missing, allPresent = EllesmereUI.AnalyzeCDMLayoutSpells(layoutData)
3391+
3392+
if allPresent then
3393+
-- All spells tracked, apply immediately
3394+
local ok, applyErr = EllesmereUI.ApplyCDMLayout(layoutData)
3395+
if ok then
3396+
print("|cff0cd29fEllesmereUI|r: CDM layout imported successfully. /reload to apply.")
3397+
EllesmereUI:ShowInfoPopup({
3398+
title = "CDM Layout Imported",
3399+
content = "Layout applied successfully.\n\nPlease /reload to see the changes.",
3400+
})
3401+
else
3402+
EllesmereUI:ShowInfoPopup({
3403+
title = "Import Failed",
3404+
content = applyErr or "Unknown error",
3405+
})
3406+
end
3407+
else
3408+
-- Missing spells: print to chat and store pending
3409+
_cdmPendingLayout = layoutData
3410+
_cdmMissingSpells = missing
3411+
EllesmereUI.PrintCDMLayoutMissingSpells(missing)
3412+
EllesmereUI:ShowInfoPopup({
3413+
title = "Spells Required",
3414+
content = #missing .. " spell(s) need to be enabled in CDM before this layout can be imported.\n\n"
3415+
.. "Check your chat window for the full list of required spells.\n\n"
3416+
.. "Enable them in CDM Cooldowns/Utility/Buffs settings, then click \"Apply Pending CDM Layout\" below.",
3417+
})
3418+
EllesmereUI:RefreshPage()
3419+
end
3420+
end)
3421+
end); y = y - h
3422+
3423+
-- "Apply Pending CDM Layout" button (only visible when there's a pending import)
3424+
if _cdmPendingLayout then
3425+
_, h = W:WideButton(parent, "Apply Pending CDM Layout", y, function()
3426+
if not _cdmPendingLayout then return end
3427+
3428+
-- Re-check missing spells
3429+
local missing, allPresent = EllesmereUI.AnalyzeCDMLayoutSpells(_cdmPendingLayout)
3430+
3431+
if allPresent then
3432+
local ok, applyErr = EllesmereUI.ApplyCDMLayout(_cdmPendingLayout)
3433+
if ok then
3434+
_cdmPendingLayout = nil
3435+
_cdmMissingSpells = nil
3436+
print("|cff0cd29fEllesmereUI|r: CDM layout imported successfully. /reload to apply.")
3437+
EllesmereUI:ShowInfoPopup({
3438+
title = "CDM Layout Imported",
3439+
content = "Layout applied successfully.\n\nPlease /reload to see the changes.",
3440+
})
3441+
EllesmereUI:RefreshPage()
3442+
else
3443+
EllesmereUI:ShowInfoPopup({
3444+
title = "Import Failed",
3445+
content = applyErr or "Unknown error",
3446+
})
3447+
end
3448+
else
3449+
-- Still missing spells
3450+
_cdmMissingSpells = missing
3451+
EllesmereUI.PrintCDMLayoutMissingSpells(missing)
3452+
EllesmereUI:ShowInfoPopup({
3453+
title = "Still Missing Spells",
3454+
content = #missing .. " spell(s) still need to be enabled.\n\nCheck chat for the updated list.",
3455+
})
3456+
end
3457+
end); y = y - h
3458+
3459+
-- "Re-check Spells" button
3460+
_, h = W:WideButton(parent, "Re-check Required Spells", y, function()
3461+
if not _cdmPendingLayout then return end
3462+
local missing, allPresent = EllesmereUI.AnalyzeCDMLayoutSpells(_cdmPendingLayout)
3463+
_cdmMissingSpells = missing
3464+
EllesmereUI.PrintCDMLayoutMissingSpells(missing)
3465+
if allPresent then
3466+
EllesmereUI:ShowInfoPopup({
3467+
title = "All Spells Ready",
3468+
content = "All required spells are now tracked. Click \"Apply Pending CDM Layout\" to finish the import.",
3469+
})
3470+
end
3471+
end); y = y - h
3472+
3473+
-- "Cancel Pending Import" button
3474+
_, h = W:WideButton(parent, "Cancel Pending Import", y, function()
3475+
_cdmPendingLayout = nil
3476+
_cdmMissingSpells = nil
3477+
print("|cff0cd29fEllesmereUI|r: CDM layout import cancelled.")
3478+
EllesmereUI:RefreshPage()
3479+
end); y = y - h
3480+
end
3481+
3482+
-- "How does this work?" link for CDM layouts
3483+
do
3484+
local ROW_H = 30
3485+
local infoFrame = CreateFrame("Frame", nil, parent)
3486+
local totalW = parent:GetWidth() - EllesmereUI.CONTENT_PAD * 2
3487+
PP.Size(infoFrame, totalW, ROW_H)
3488+
PP.Point(infoFrame, "TOPLEFT", parent, "TOPLEFT", EllesmereUI.CONTENT_PAD, y)
3489+
3490+
local infoBtn = CreateFrame("Button", nil, infoFrame)
3491+
infoBtn:SetFrameLevel(infoFrame:GetFrameLevel() + 1)
3492+
local infoFS = infoBtn:CreateFontString(nil, "OVERLAY")
3493+
infoFS:SetFont(FONT, 12, EllesmereUI.GetFontOutlineFlag())
3494+
infoFS:SetTextColor(EG.r, EG.g, EG.b, 0.70)
3495+
infoFS:SetText("How does this work?")
3496+
infoFS:SetPoint("CENTER")
3497+
infoBtn:SetSize(infoFS:GetStringWidth() + 10, 18)
3498+
PP.Point(infoBtn, "LEFT", infoFrame, "LEFT", 0, 0)
3499+
infoBtn:SetScript("OnEnter", function()
3500+
infoFS:SetTextColor(EG.r, EG.g, EG.b, 1)
3501+
end)
3502+
infoBtn:SetScript("OnLeave", function()
3503+
infoFS:SetTextColor(EG.r, EG.g, EG.b, 0.70)
3504+
end)
3505+
infoBtn:SetScript("OnClick", function()
3506+
EllesmereUI:ShowInfoPopup({
3507+
title = "CDM Layout Profiles",
3508+
content = "CDM Layout Profiles let you share which abilities are assigned to which CDM bars, separate from your visual settings.\n\n"
3509+
.. "WHAT'S INCLUDED\n"
3510+
.. "Spell assignments for all CDM bars (Cooldowns, Utility, Buffs, and custom bars), plus tracked buff bar spell assignments.\n\n"
3511+
.. "WHAT'S NOT INCLUDED\n"
3512+
.. "Visual styling (icon size, borders, colors, shapes), bar positions, bar glows, and all other appearance settings. Those stay in your main profile.\n\n"
3513+
.. "IMPORTING\n"
3514+
.. "When you import a CDM layout, the system checks which spells need to be tracked in CDM. "
3515+
.. "If any spells are missing, they'll be listed in chat. Enable them in CDM settings first, then apply the layout.\n\n"
3516+
.. "This is spec-specific. Export from the spec you want to share, and import on the spec you want to apply it to.",
3517+
})
3518+
end)
3519+
3520+
y = y - ROW_H
3521+
end
3522+
end
3523+
33543524
return math.abs(y)
33553525
end
33563526

33573527
---------------------------------------------------------------------------
33583528
-- Enabled Addons page
33593529
---------------------------------------------------------------------------
33603530

3361-
local disabledList = { PAGE_CORE, PAGE_PROFILES }
3362-
local disabledTips = { [PAGE_CORE] = "Coming Soon", [PAGE_PROFILES] = "Coming Soon" }
3531+
local disabledList = { PAGE_CORE }
3532+
local disabledTips = { [PAGE_CORE] = "Coming Soon" }
33633533

33643534
EllesmereUI:RegisterModule(GLOBAL_KEY, {
33653535
title = "Global Settings",
@@ -3405,6 +3575,7 @@ initFrame:SetScript("OnEvent", function(self)
34053575
EllesmereUIDB.errorSound = false
34063576
EllesmereUIDB.showSpellID = false
34073577
EllesmereUIDB.suppressErrors = true
3578+
EllesmereUIDB.crosshairSize = "None"
34083579
end
34093580
if EllesmereUI._applyRightClickTarget then
34103581
EllesmereUI._applyRightClickTarget()

EllesmereUI.lua

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,25 @@ do
652652
return pixels * m
653653
end
654654

655+
---------------------------------------------------------------------------
656+
-- SnapForES(x, effectiveScale)
657+
--
658+
-- Snap a value to a whole number of physical pixels at the given
659+
-- effective scale. Uses the same approach as the border system:
660+
-- onePixel = perfect / es (size of 1 physical pixel in frame coords)
661+
-- physPixels = floor(x / onePixel + 0.5) (round to nearest whole pixel)
662+
-- result = physPixels * onePixel
663+
--
664+
-- This guarantees every element sized through this function is exactly
665+
-- N physical pixels, eliminating sub-pixel drift between siblings.
666+
---------------------------------------------------------------------------
667+
function PP.SnapForES(x, es)
668+
if x == 0 then return 0 end
669+
local onePixel = PP.perfect / es
670+
local physPixels = math.floor(x / onePixel + 0.5)
671+
return physPixels * onePixel
672+
end
673+
655674
---------------------------------------------------------------------------
656675
-- Convenience wrappers — pixel-snapped frame geometry
657676
---------------------------------------------------------------------------
@@ -723,6 +742,70 @@ do
723742
obj.PixelSnapDisabled = true
724743
end
725744

745+
---------------------------------------------------------------------------
746+
-- Global Pixel Snap Prevention
747+
--
748+
-- WoW re-enables pixel snapping whenever a texture's properties change
749+
-- (SetTexture, SetColorTexture, SetVertexColor, SetTexCoord, etc.).
750+
-- This hooks the widget metatables so that every texture/statusbar in
751+
-- the game automatically has pixel snapping disabled after any property
752+
-- change. Without this, manual DisablePixelSnap calls get undone by
753+
-- Blizzard's code on spell swaps, page changes, combat transitions, etc.
754+
---------------------------------------------------------------------------
755+
local function WatchPixelSnap(frame, snap)
756+
if (frame and not frame:IsForbidden()) and frame.PixelSnapDisabled and snap then
757+
frame.PixelSnapDisabled = nil
758+
end
759+
end
760+
761+
local function HookPixelSnap(object)
762+
local mk = getmetatable(object)
763+
if not mk then return end
764+
mk = mk.__index
765+
if not mk or mk.DisabledPixelSnap then return end
766+
767+
if mk.SetSnapToPixelGrid or mk.SetStatusBarTexture or mk.SetColorTexture
768+
or mk.SetVertexColor or mk.CreateTexture or mk.SetTexCoord or mk.SetTexture then
769+
if mk.SetSnapToPixelGrid then hooksecurefunc(mk, "SetSnapToPixelGrid", WatchPixelSnap) end
770+
if mk.SetStatusBarTexture then hooksecurefunc(mk, "SetStatusBarTexture", PP.DisablePixelSnap) end
771+
if mk.SetColorTexture then hooksecurefunc(mk, "SetColorTexture", PP.DisablePixelSnap) end
772+
if mk.SetVertexColor then hooksecurefunc(mk, "SetVertexColor", PP.DisablePixelSnap) end
773+
if mk.CreateTexture then hooksecurefunc(mk, "CreateTexture", PP.DisablePixelSnap) end
774+
if mk.SetTexCoord then hooksecurefunc(mk, "SetTexCoord", PP.DisablePixelSnap) end
775+
if mk.SetTexture then hooksecurefunc(mk, "SetTexture", PP.DisablePixelSnap) end
776+
mk.DisabledPixelSnap = true
777+
end
778+
end
779+
780+
-- Hook all known widget types by creating one of each and hooking its metatable
781+
local hookFrame = CreateFrame("Frame")
782+
HookPixelSnap(hookFrame)
783+
HookPixelSnap(hookFrame:CreateTexture())
784+
HookPixelSnap(hookFrame:CreateFontString())
785+
HookPixelSnap(hookFrame:CreateMaskTexture())
786+
787+
-- Enumerate all existing frame types to catch any we missed
788+
local hookedTypes = { Frame = true }
789+
local enumObj = EnumerateFrames()
790+
while enumObj do
791+
local objType = enumObj:GetObjectType()
792+
if not enumObj:IsForbidden() and not hookedTypes[objType] then
793+
HookPixelSnap(enumObj)
794+
hookedTypes[objType] = true
795+
end
796+
enumObj = EnumerateFrames(enumObj)
797+
end
798+
799+
-- Also hook ScrollFrame and StatusBar metatables
800+
HookPixelSnap(CreateFrame("ScrollFrame"))
801+
do
802+
local sb = CreateFrame("StatusBar")
803+
sb:SetStatusBarTexture("Interface\\Buttons\\WHITE8x8")
804+
HookPixelSnap(sb)
805+
local sbt = sb:GetStatusBarTexture()
806+
if sbt then HookPixelSnap(sbt) end
807+
end
808+
726809
---------------------------------------------------------------------------
727810
-- UNIFIED BORDER SYSTEM
728811
--
@@ -1742,6 +1825,13 @@ local function WirePopupEscape(popup, dimmer)
17421825
self:SetPropagateKeyboardInput(true)
17431826
end
17441827
end)
1828+
-- Release keyboard capture when the popup is dismissed
1829+
dimmer:HookScript("OnHide", function()
1830+
popup:EnableKeyboard(false)
1831+
end)
1832+
dimmer:HookScript("OnShow", function()
1833+
popup:EnableKeyboard(true)
1834+
end)
17451835
end
17461836

17471837
local function CreateConfirmPopup()
@@ -5191,7 +5281,7 @@ end
51915281
-------------------------------------------------------------------------------
51925282
-- Slash commands
51935283
-------------------------------------------------------------------------------
5194-
EllesmereUI.VERSION = "3.8"
5284+
EllesmereUI.VERSION = "3.8.5"
51955285

51965286
-- Register this addon's version into a shared global table (taint-free at load time)
51975287
if not _G._EUI_AddonVersions then _G._EUI_AddonVersions = {} end

EllesmereUI.toc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
## Title: |cff0cd29fEllesmereUI|r
33
## Notes: Shared framework for the EllesmereUI addon suite
44
## Author: Ellesmere
5-
## Version: 3.8
5+
## Version: 3.8.5
66
## SavedVariables: EllesmereUIDB
77
## IconTexture: Interface\AddOns\EllesmereUI\media\eg-logo.tga
88
## X-Curse-Project-ID: 1477613

0 commit comments

Comments
 (0)