Skip to content

Commit 1a48168

Browse files
author
mergetest
committed
Merge main: click-cast fix round (stale-handle hardening, international keys, Always Cast, taint + ClickCastFrames takeover fixes)
Conflict resolutions: - Config.lua SafeSetFont: both lines had fixed the secret-compare taint independently; unified on the 12.1 form (issecretvalue-gated skip) with the redundant ~= "" dropped. - ClickCasting/Frames.lua RunBindingRepair: took main's #213 version (string mirror reset + header-side override wipe). - AuraDesigner/Indicators.lua / Frames/Expiring.lua: kept deleted — the 12.1 engine replaced both; main's aura-refresh expiring fix does not apply to the container-era code. - CHANGELOG.md: folded main's Unreleased entries into the 5.0.0 section.
2 parents 698a094 + e3673ae commit 1a48168

6 files changed

Lines changed: 261 additions & 80 deletions

File tree

CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

77
* (Click Casting) Fixed a Lua error walking Blizzard unit frames after combat that could stop click-casting bindings from working on the default frames until reload — the frame scan now skips protected (secret) values introduced by recent client versions. (by Krathe)
88
* (Click Casting) Keyboard binds that stop working mid-session now recover automatically after combat ends or a loading screen, instead of staying broken until a reload.
9+
* (Click Casting) **Fixed keyboard and extra-mouse-button hover binds dying addon-wide mid-combat until a /reload** — hover binds are now owned by one permanent frame instead of the unit frame under the cursor, removing the cleanup step that could hit a dead frame reference and silently break every later hover. The automatic self-repair stays as a safety net, but the cause is gone. (by Krathe)
10+
* (Click Casting) Fixed keyboard and mouse-wheel click-cast binds being silently dropped mid-hover during combat — the safety check that removes hover binds could misread the cursor as off the frame while the frame's position was briefly unreadable, wiping the binds until the frame was re-hovered. Binds are now only removed when the cursor is provably off the frame. (by Krathe)
11+
* (Click Casting) Fixed binds on keys from international keyboard layouts (such as æ, ø or å) not casting on the frame under the cursor.
12+
* (Click Casting) New "Always Cast" option in a binding's Advanced settings. When enabled, the key still casts with the spell's normal targeting if no other rule matches — so ground-targeted spells (like aimable Shaman totems) show their aiming circle when pressed while hovering nothing.
13+
* (Click Casting) Frames that other addons register for click-casting are now only taken over if they are real unit frames — non-unit buttons (such as toy or action buttons) are left alone, and frames an addon explicitly unregisters stay unregistered.
14+
* (Auras) Fixed a stream of blocked-action taint warnings in PvP instances (triggered while styling aura duration text) that could spill over and break Blizzard's chat or other addons until a /reload.
915
* (Bars) **Fixed health bars rendering solid green when a profile references a bar texture you don't have** — imported profiles often point at another addon's texture files; if that addon isn't installed (or its files changed), the bar showed WoW's green missing-texture state and class colours appeared broken. All bar textures now fall back to the stock texture with a one-time warning, on every update path (the fallback previously only applied when a frame was first created and was immediately overwritten). (by Krathe)
1016
* (Bars) Fixed class-coloured health bars staying stuck on the gradient colour (usually green) after using or switching away from the Percent colour mode — the class/custom colour is now written directly to the bar texture, so it can no longer be masked by a leftover gradient tint. (by Krathe)
1117

@@ -130,6 +136,28 @@ DandersFrames has been rebuilt for WoW 12.1 (Midnight), which fundamentally chan
130136
* Aura Designer text colouring is drawn as a cover over the text: it ignores the out-of-range text fade, and group parts with their own inline colours keep them.
131137
* Dragging certain aura sliders can briefly stutter.
132138

139+
## [4.7.3]
140+
141+
### Bug Fixes
142+
143+
* (Bars) **Fixed health bars rendering solid green when a profile references a bar texture you don't have** — imported profiles often point at another addon's texture files; if that addon isn't installed (or its files changed), the bar showed WoW's green missing-texture state and class colours appeared broken. All bar textures now fall back to the stock texture with a one-time warning, on every update path (the fallback previously only applied when a frame was first created and was immediately overwritten). (by Krathe)
144+
* (Bars) Fixed class-coloured health bars staying stuck on the gradient colour (usually green) after using or switching away from the Percent colour mode — the class/custom colour is now written directly to the bar texture, so it can no longer be masked by a leftover gradient tint. (by Krathe)
145+
146+
## [4.7.2]
147+
148+
### Bug Fixes
149+
150+
* (Click Casting) Fixed a Lua error walking Blizzard unit frames after combat that could stop click-casting bindings from working on the default frames until reload — the frame scan now skips protected (secret) values introduced by recent client versions. (by Krathe)
151+
* (Click Casting) Keyboard binds that stop working mid-session now recover automatically after combat ends or a loading screen, instead of staying broken until a reload.
152+
153+
## [4.7.1]
154+
155+
### Bug Fixes
156+
157+
* (Text Designer) Element colour changes now take effect immediately while the colour picker is open, instead of only after closing it with OK. (by Krathe)
158+
* (Interface) Opening a colour picker no longer counts as a colour change — the picker fired its change handlers once during setup, which could commit settings (such as a Text Designer element's colour override) without any edit. (by Krathe)
159+
* (Aura Designer) Fixed expiring border animations staying stuck on after the tracked aura was refreshed (for example re-casting a HoT in its pandemic window). (by Krathe)
160+
133161
## [4.7.0]
134162

135163
### Bug Fixes

ClickCasting/Bindings.lua

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -511,12 +511,22 @@ function CC:ApplyBindings()
511511
self.needsBindingRefresh = true
512512
return
513513
end
514-
514+
515515
-- Cancel any pending batch binding pass from a previous call
516516
if self.batchBindingTimer then
517517
self.batchBindingTimer:Cancel()
518518
self.batchBindingTimer = nil
519519
end
520+
521+
-- Hover keyboard/scroll binds are owned by the click-cast header, so a
522+
-- full rebuild starts by wiping the header's override bindings — any
523+
-- bind from the outgoing set that is still active (e.g. the user is
524+
-- hovering a frame right now) would otherwise survive with its OLD
525+
-- action until the next leave/enter cycle. The next OnEnter re-applies
526+
-- from the freshly built snippets.
527+
if self.header then
528+
pcall(ClearOverrideBindings, self.header)
529+
end
520530

521531
-- Migrate existing macro bindings to have no fallbacks
522532
if self.db and self.db.bindings then
@@ -773,8 +783,11 @@ function CC:GetHovercastSuffix(binding)
773783
-- Mouse button
774784
return "dfmouse" .. modKey .. buttonNum
775785
else
776-
-- Keyboard/scroll key
777-
return "dfbutton" .. modKey .. key:lower()
786+
-- Keyboard/scroll key. EncodeKeyToken: identical to the old :lower()
787+
-- for alphanumeric keys; international and punctuation keys get an
788+
-- ASCII-safe encoding so their bytes never enter attribute names
789+
-- (bug #977).
790+
return "dfbutton" .. modKey .. self:EncodeKeyToken(key)
778791
end
779792
end
780793

@@ -812,7 +825,9 @@ function CC:BuildHovercastSetupScript()
812825
-- These fallbacks only work when NOT hovering a frame, so we need
813826
-- the key binding to be active globally, not just when hovering
814827
local fallback = binding.fallback or {}
815-
local hasFallbackThatNeedsGlobal = fallback.mouseover or fallback.target or fallback.selfCast
828+
-- alwaysCast needs the key active everywhere too — its whole point
829+
-- is casting while hovering nothing (bug #991)
830+
local hasFallbackThatNeedsGlobal = fallback.mouseover or fallback.target or fallback.selfCast or fallback.alwaysCast
816831

817832
-- Check for useGlobalBind flag (for items/macros that need to work everywhere)
818833
local hasGlobalBindFlag = binding.useGlobalBind == true
@@ -960,6 +975,33 @@ function CC:ClearGlobalBindings()
960975
end
961976
end
962977

978+
-- Encode a captured key name into an ASCII-only token for use inside derived
979+
-- names (virtual mouse button names -> secure attribute names). Keys from
980+
-- non-US keyboard layouts (æ, ø, å, ñ, ü, ...) arrive from OnKeyDown as
981+
-- multibyte UTF-8; embedding those raw bytes in attribute names is the one
982+
-- structural difference between this pipeline and the systems that handle
983+
-- such keys correctly (Blizzard's own bindings, Dominos, EllesmereUI — none
984+
-- of which put key characters into derived names). The BINDING key itself is
985+
-- always passed byte-exact as captured; only derived names go through this.
986+
-- a-z / 0-9 pass through and A-Z lowercases, so alphanumeric keys (the vast
987+
-- majority) produce the identical name the old :lower() did. Every other
988+
-- byte — multibyte sequences AND ASCII punctuation — becomes "_" plus its
989+
-- zero-padded byte value: fixed width, so two distinct keys can never encode
990+
-- to the same token. Punctuation is deliberately encoded too: characters
991+
-- like "*" carry wildcard meaning in secure attribute names, so raw
992+
-- punctuation in a derived name was never safe either. (bug #977)
993+
function CC:EncodeKeyToken(key)
994+
return (tostring(key or ""):gsub(".", function(c)
995+
local b = c:byte()
996+
if (b >= 97 and b <= 122) or (b >= 48 and b <= 57) then
997+
return c -- a-z, 0-9 unchanged
998+
elseif b >= 65 and b <= 90 then
999+
return string.char(b + 32) -- A-Z -> a-z (legacy casing)
1000+
end
1001+
return string.format("_%03d", b) -- everything else: byte-encoded
1002+
end))
1003+
end
1004+
9631005
-- Get the WoW key string for a binding
9641006
function CC:GetBindingKeyString(binding)
9651007
local key = ""
@@ -1969,7 +2011,18 @@ function CC:BuildMacroTextForBinding(binding, forGlobalBinding)
19692011
if hasSelf then
19702012
table.insert(parts, "[@player" .. combatStr .. mountedStr .. "] " .. spellName)
19712013
end
1972-
2014+
2015+
-- Always Cast: terminal unconditional clause — when no clause above
2016+
-- matches (nothing hovered / ineligible unit), cast with WoW's default
2017+
-- targeting so ground-targeted spells show their aiming circle
2018+
-- (bug #991). With Self enabled the [@player] clause above always
2019+
-- resolves first, so Self takes precedence. Combat/mounted gating
2020+
-- still applies.
2021+
if fallback.alwaysCast then
2022+
local conds = (combatStr .. mountedStr):sub(2) -- strip leading comma; "" when ungated
2023+
table.insert(parts, (conds ~= "" and ("[" .. conds .. "] ") or "") .. spellName)
2024+
end
2025+
19732026
-- If no fallbacks enabled, just cast normally (will use WoW's default targeting)
19742027
if #parts == 0 then
19752028
table.insert(parts, spellName)
@@ -2287,7 +2340,20 @@ function CC:BuildCombinedMacroForBindings(bindings, forGlobalBinding)
22872340
table.insert(parts, "[@player" .. combatStr .. "] " .. friendlySpell)
22882341
end
22892342
end
2290-
2343+
2344+
-- Always Cast (bug #991): terminal unconditional clause, mirroring the
2345+
-- single-binding builder. First contributing binding with the flag wins;
2346+
-- the self-cast clause above resolves first when enabled.
2347+
for _, b in ipairs({friendlyBinding, hostileBinding, anyBinding}) do
2348+
if b and b.fallback and b.fallback.alwaysCast and b.spellName then
2349+
local spell = GetLocalizedSpellName(b.spellId) or b.spellName
2350+
local combatCond = GetCombatCondition(b)
2351+
local combatStr = combatCond == "combat" and ",combat" or (combatCond == "nocombat" and ",nocombat" or "")
2352+
table.insert(parts, (combatStr ~= "" and ("[" .. combatStr:sub(2) .. "] ") or "") .. spell)
2353+
break
2354+
end
2355+
end
2356+
22912357
if #parts == 0 then return nil end
22922358

22932359
-- Check if any contributing binding has stopSpellTarget enabled

ClickCasting/Constants.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ local DEFAULT_BINDING = {
303303
target = false,
304304
selfCast = false,
305305
stopSpellTarget = false,
306+
alwaysCast = false,
306307
},
307308
-- Load conditions
308309
loadSpec = nil, -- nil = all specs, or table of spec IDs
@@ -453,6 +454,10 @@ local FALLBACK_INFO = {
453454
name = "Self",
454455
desc = "Cast on yourself as a last resort if no other valid target is found.",
455456
},
457+
alwaysCast = {
458+
name = "Always Cast",
459+
desc = "If no rule above matches (hovering nothing, or an ineligible unit), cast anyway using the spell's normal targeting. Lets ground-targeted spells show their aiming circle when pressed in the open. If Self is also enabled, Self applies first.",
460+
},
456461
stopSpellTarget = {
457462
name = "Cancel Targeting",
458463
desc = "Adds /stopspelltarget to the macro which cancels the blue targeting hand after casting. Disable this for spells like Rescue that require a targeting phase to complete.",

0 commit comments

Comments
 (0)