diff --git a/EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua b/EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua index 92cdce4a2..99c8c1a27 100644 --- a/EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua +++ b/EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua @@ -15,6 +15,9 @@ local RANK_STRINGS = {} for i = 1, 40 do RANK_STRINGS[i] = i .. "." end local MIN_W, MIN_H = 150, 50 local TICK_COMBAT = 1 +local REFRESH_RATE_FLOOR = 0.5 -- default floor; skipped when unsafeRefreshRate is on +local REFRESH_RATE_HARD_FLOOR = 0.05 -- absolute floor regardless of unsafeRefreshRate: guards against a corrupt/imported 0 or negative value reaching the ticker +ns._REFRESH_RATE_FLOOR = REFRESH_RATE_FLOOR -- published so the options page's toggle-off snap-back uses the same value instead of a second hardcoded 0.5 local PEAK_BUDGET = 1.5 local BAR_TEX = "Interface\\Buttons\\WHITE8X8" local MEDIA = "Interface\\AddOns\\EllesmereUIDamageMeters\\Media\\" @@ -131,6 +134,7 @@ local DM_DEFAULTS = { standaloneTimerShowOOC = false, standaloneTimerDesatOOC = false, refreshRate = 1, + unsafeRefreshRate = false, -- opt-in: lets refreshRate go below the 0.5s floor hideResetButton = false, -- display the "reset data" button on the damage meter header -- toggleWindowsKey (unset by default) is the hotkey that hides/shows every -- meter window at once. Runtime only: the hidden state is never saved, so a @@ -258,14 +262,18 @@ local function EnsureDB() -- snapshot per window, so sub-0.5 rates multiply allocation churn far -- past any visual gain. Clamp every stored profile once per session -- (idempotent; imports of old exports are caught by the ticker clamp - -- until their next login). + -- until their next login). Skipped for a profile with unsafeRefreshRate + -- on; the hard floor still applies regardless, so a corrupt/imported + -- value can't reach the ticker as 0 or negative. local sv = _G.EllesmereUIDamageMetersDB if type(sv) == "table" and type(sv.profiles) == "table" then for _, p in pairs(sv.profiles) do local dm = type(p) == "table" and p.dm - if type(dm) == "table" and type(dm.refreshRate) == "number" - and dm.refreshRate < 0.5 then - dm.refreshRate = 0.5 + if type(dm) == "table" and type(dm.refreshRate) == "number" then + local floor = dm.unsafeRefreshRate and REFRESH_RATE_HARD_FLOOR or REFRESH_RATE_FLOOR + if dm.refreshRate < floor then + dm.refreshRate = floor + end end end end @@ -5236,8 +5244,11 @@ StartSharedTicker = function() if _sharedTicker then _sharedTicker:Cancel() end local rate = DB().refreshRate or TICK_COMBAT -- Belt for values the login clamp has not seen yet (a profile imported - -- mid-session from an old export can carry a sub-floor rate). - if rate < 0.5 then rate = 0.5 end + -- mid-session from an old export can carry a sub-floor rate). Respects + -- unsafeRefreshRate the same way the login clamp does; the hard floor + -- applies either way so 0 or negative can never reach the ticker. + local floor = DB().unsafeRefreshRate and REFRESH_RATE_HARD_FLOOR or REFRESH_RATE_FLOOR + if rate < floor then rate = floor end _sharedTicker = C_Timer.NewTicker(rate, SharedRefreshTick) StopTimerTicker() _timerTicker = C_Timer.NewTicker(0.5, TimerTick) diff --git a/EllesmereUIOptions/EUI_DamageMeters_Options.lua b/EllesmereUIOptions/EUI_DamageMeters_Options.lua index 133d0946b..a1b7501d8 100644 --- a/EllesmereUIOptions/EUI_DamageMeters_Options.lua +++ b/EllesmereUIOptions/EUI_DamageMeters_Options.lua @@ -217,13 +217,64 @@ initFrame:SetScript("OnEvent", function(self) onChanged = VisApply, onOptionChanged = VisApply }, -- Refresh Rate moved up into the slot the Visibility Options dropdown left - -- behind; its "(seconds)" suffix is attached below. + -- behind; its "(seconds)" suffix is attached below. Range widens once the + -- cog's Unsafe Refresh Rate toggle is on (see below); that toggle rebuilds + -- the page so this table is re-evaluated with the new min/tooltip. + (Cfg("unsafeRefreshRate") and + { type="slider", text="Refresh Rate", + tooltip = "Below 0.5s multiplies memory allocation per window every tick. Not recommended with several windows open.", + min = 0.1, max = 2, step = 0.05, + getValue = function() return Cfg("refreshRate") or 1 end, + setValue = function(v) Set("refreshRate", v) end, + fmt = function(v) return format("%.2fs", v) end } + or { type="slider", text="Refresh Rate", tooltip = "Increase to improve performance, Decrease to update meters faster", min = 0.5, max = 2, step = 0.1, getValue = function() return Cfg("refreshRate") or 1 end, setValue = function(v) Set("refreshRate", v) end, - fmt = function(v) return format("%.2fs", v) end }) + fmt = function(v) return format("%.2fs", v) end })) + if not EllesmereUI._prebuilding then + local rgn = visRow._rightRegion + -- Forward-declared so the row's set() can close the popup before the page + -- rebuild below tears down the button it's anchored to: RefreshPage(true) + -- recreates this whole block's frames, and a still-open popup left anchored + -- to the old (now orphaned) cog button drifts to wherever that frame lands. + local unsafeCogShow + local _, _unsafeCogShow = EllesmereUI.BuildCogPopup({ + title = "Refresh Rate", + rows = { + { type = "toggle", label = "Unsafe Refresh Rate", + tooltip = "Allows Refresh Rate below 0.5s. Each tick fetches a full session snapshot per window, so lower values multiply allocation cost far past any visual gain, especially with several windows open.", + get = function() return Cfg("unsafeRefreshRate") == true end, + set = function(v) + Set("unsafeRefreshRate", v) + if not v then + local floor = ns._REFRESH_RATE_FLOOR or 0.5 + local r = Cfg("refreshRate") + if r and r < floor then Set("refreshRate", floor) end + end + if unsafeCogShow and unsafeCogShow._popupFrame then + unsafeCogShow._popupFrame:Hide() + end + EllesmereUI:RefreshPage(true) + end }, + }, + }) + unsafeCogShow = _unsafeCogShow + local unsafeCogBtn = CreateFrame("Button", nil, rgn) + unsafeCogBtn:SetSize(26, 26) + unsafeCogBtn:SetPoint("RIGHT", rgn._control, "LEFT", -8, 0) + rgn._lastInline = unsafeCogBtn + unsafeCogBtn:SetFrameLevel(rgn:GetFrameLevel() + 5) + unsafeCogBtn:SetAlpha(0.4) + local unsafeCogTex = unsafeCogBtn:CreateTexture(nil, "OVERLAY") + unsafeCogTex:SetAllPoints() + unsafeCogTex:SetTexture(EllesmereUI.COGS_ICON) + unsafeCogBtn:SetScript("OnEnter", function(self) self:SetAlpha(0.7) end) + unsafeCogBtn:SetScript("OnLeave", function(self) self:SetAlpha(0.4) end) + unsafeCogBtn:SetScript("OnClick", function(self) unsafeCogShow(self) end) + end y = y - h -- Window Border Style (+ directions submenu) | Border Size (+ color)