Make [button:N] mean the invoking button, as retail does

[button:N] was a held-state modifier: IsMouseButtonDown(name), true while you
physically hold that button. Retail's is a property of the activation -- which
button clicked the action button -- and a keybind press counts as button 1
because it activates through the left-click path. Different in kind, and the
visible break was [button:1] Rejuvenation; Regrowth casting Regrowth from a
keybind, where retail casts Rejuvenation.

It now reads ClassicAPI's GetMouseButtonClicked, defaulting to LeftButton when
no click dispatch is running. That default IS the keybind rule: Button:Click()
with no argument reports LeftButton, and a macro run from anywhere else answers
button 1 the same way.

This also reverts 891467c. That commit refreshed the action bar on
GLOBAL_MOUSE_DOWN/UP so the icon could track the held state -- under the real
semantics there is no such state to track, and the icon correctly sits on
whatever button 1 resolves to. PLAYER_TOTEM_UPDATE stays; it shared the comment
and inherits it.

Requires ClassicAPI v1.15.8, up from v1.15.0. v1.15.8 scoped
GetMouseButtonClicked to the click dispatch; before it a HELD button kept the
value alive (by design -- OnDragStart reads it), so turning the camera with
right-click down made every keybind press read RightButton. The function exists
below v1.15.8 and answers wrongly, and a conditional that is quietly wrong is
worse than one that warns, so the floor moved rather than the conditional being
gated.

Bare [button] / [nobutton] survive as an extension retail has no equivalent for,
now meaning "a click dispatch is running" -- the practical "activated by a
keybind, not a click" test, and no longer leaning on anything but the documented
nil.
This commit is contained in:
Brues
2026-09-15 01:17:06 -05:00
parent 54e3c3b3f4
commit eddc49fc60
6 changed files with 55 additions and 53 deletions
+2 -1
View File
@@ -3,7 +3,8 @@
ClassicAPI is a client mod (sibling to Nampower/SuperWoW) that backports the
modern C_* API into the 1.12.1 Lua environment. It is a HARD REQUIREMENT of
this addon (ClassicAPI v1.15.0+, which added frame:RegisterUnitEvent), so the
this addon (ClassicAPI v1.15.8+, which scoped GetMouseButtonClicked to the
click dispatch; v1.15.0 added frame:RegisterUnitEvent), so the
wrappers below call the API directly — no fallbacks. The load-time requirement
check (Core.lua) uses IsAvailable() to warn when the DLL is missing and
HasMinimumVersion() when it's too old; users who don't want ClassicAPI should
+17 -10
View File
@@ -5341,27 +5341,34 @@ end
-- A list of Conditionals and their functions to validate them
CleveRoids.Keywords = {
-- [button:N] — true while mouse button N is held (1=Left, 2=Right, 3=Middle,
-- 4/5=extra). Routed through Multi so OR/AND lists and repeated groups behave
-- like every other argument conditional ([button:1/2] = left or right).
-- [button] with no argument — true if any mapped mouse button is held.
-- [button:N] — N is the button that INVOKED this action (1=Left, 2=Right,
-- 3=Middle, 4/5=extra), not one held down as a modifier. That is retail's
-- meaning, and it is why a keybind press counts as button 1: retail activates
-- through the left-click path, so [button:1] passes for a keybind and a
-- left-click alike, while [button:2] passes only for an actual right-click.
-- Routed through Multi so OR/AND lists behave like every other argument
-- conditional ([button:1/2] = invoked by left or right).
--
-- Bare [button] / [nobutton] ask whether a click drove this at all -- the one
-- thing the underlying data says that retail's conditional cannot express,
-- and the practical "activated by a keybind, not a click" test.
button = function(conditionals)
if type(conditionals.button) ~= "table" then
return CleveRoids.AnyMouseButtonDown()
return CleveRoids.WasClickActivated()
end
local invoking = CleveRoids.GetActivatingButton()
return Multi(conditionals.button, function(button)
local name = CleveRoids.buttons[button]
return name and IsMouseButtonDown(name) or false
return CleveRoids.buttons[button] == invoking
end, conditionals, "button")
end,
nobutton = function(conditionals)
if type(conditionals.nobutton) ~= "table" then
return not CleveRoids.AnyMouseButtonDown()
return not CleveRoids.WasClickActivated()
end
local invoking = CleveRoids.GetActivatingButton()
return NegatedMulti(conditionals.nobutton, function(button)
local name = CleveRoids.buttons[button]
return not (name and IsMouseButtonDown(name))
return CleveRoids.buttons[button] ~= invoking
end, conditionals, "nobutton")
end,
+14 -31
View File
@@ -201,9 +201,13 @@ requirementCheckFrame:SetScript("OnEvent", function()
-- v1.15.0 added frame:RegisterUnitEvent, which Utility.lua calls at file scope.
-- On an older build that call raises, aborting the rest of the chunk -- so this
-- is not a degraded-features warning, it's "the addon did not finish loading".
local hasClassicAPI1150 = hasClassicAPI and CleveRoids.ClassicAPI.HasMinimumVersion(1, 15, 0)
-- v1.15.8 scoped GetMouseButtonClicked to the click dispatch. Below it the
-- function exists but a held button keeps its value, so [button:N] reads the
-- button you are turning the camera with -- wrong quietly, which is the worse
-- failure and why the floor moved rather than the conditional being gated.
local hasClassicAPI1158 = hasClassicAPI and CleveRoids.ClassicAPI.HasMinimumVersion(1, 15, 8)
if not hasNampower30 or not hasUnitXP or not hasClassicAPI or not hasClassicAPI1150 then
if not hasNampower30 or not hasUnitXP or not hasClassicAPI or not hasClassicAPI1158 then
-- Show warnings (don't disable — tearing down a partially-initialized addon causes hangs)
if not hasNampower then
CleveRoids.Print("|cFFFF9900WARNING:|r |cFF00FFFFAvitasia's Nampower v3.0.0+|r is required:")
@@ -223,9 +227,9 @@ requirementCheckFrame:SetScript("OnEvent", function()
CleveRoids.Print("|cFFFF9900WARNING:|r |cFF00FFFFClassicAPI|r is required:")
CleveRoids.Print("https://github.com/brues-code/ClassicAPI")
CleveRoids.Print("Dispel-type and movement conditionals will be unavailable without it.")
elseif not hasClassicAPI1150 then
elseif not hasClassicAPI1158 then
local major, minor, patch = CleveRoids.ClassicAPI.GetVersion()
CleveRoids.Print(format("|cFFFF9900WARNING:|r |cFF00FFFFClassicAPI v1.15.0+|r is required (you have v%d.%d.%d):", major, minor, patch))
CleveRoids.Print(format("|cFFFF9900WARNING:|r |cFF00FFFFClassicAPI v1.15.8+|r is required (you have v%d.%d.%d):", major, minor, patch))
CleveRoids.Print("https://github.com/brues-code/ClassicAPI")
CleveRoids.Print("The addon cannot finish loading on this version -- update ClassicAPI.")
end
@@ -4527,14 +4531,9 @@ if CleveRoids.NampowerAPI.features.hasKeyEvents then
CleveRoids.Frame:RegisterEvent("KEY_UP")
end
-- ClassicAPI raw mouse-button events, for [button:N] icon refresh. Ungated,
-- unlike the Nampower features above: these long predate the ClassicAPI v1.15.0
-- floor the addon already refuses to run below.
CleveRoids.Frame:RegisterEvent("GLOBAL_MOUSE_DOWN")
CleveRoids.Frame:RegisterEvent("GLOBAL_MOUSE_UP")
-- ClassicAPI totem-bar tracking, for [totem:X] icon refresh. Ungated for the
-- same reason.
-- ClassicAPI totem-bar tracking, for [totem:X] icon refresh. Ungated, unlike the
-- Nampower features above: it long predates the ClassicAPI floor the addon
-- already refuses to run below.
CleveRoids.Frame:RegisterEvent("PLAYER_TOTEM_UPDATE")
-- NOTE: SuperMacro hook installation is handled by Compatibility/SuperMacro.lua
@@ -5588,26 +5587,10 @@ function CleveRoids.Frame:KEY_UP()
CleveRoids.isActionUpdateQueued = true
end
-- ClassicAPI GLOBAL_MOUSE_DOWN / GLOBAL_MOUSE_UP: arg1 = button name
-- ("LeftButton" .. "Button5"), fired on every raw press/release whether or not
-- the click lands on a frame. [button:N] reads IsMouseButtonDown live, but
-- nothing sampled that state -- the OnUpdate polls modifier keys only -- so a
-- [button:N] macro's icon sat on whatever it resolved to with no button held.
-- Flag set directly rather than through QueueActionUpdate, as with KEY_DOWN:
-- these fire from the message pump, which can beat VARIABLES_LOADED to the
-- CleveRoidMacros table QueueActionUpdate reads.
function CleveRoids.Frame:GLOBAL_MOUSE_DOWN()
CleveRoids.isActionUpdateQueued = true
end
function CleveRoids.Frame:GLOBAL_MOUSE_UP()
CleveRoids.isActionUpdateQueued = true
end
-- ClassicAPI PLAYER_TOTEM_UPDATE: arg1 = the slot (1 Fire .. 4 Air) whose totem
-- was dropped, expired, killed or recalled. The slot goes unread for the same
-- reason the mouse handlers ignore their button -- a [totem:X] macro may name
-- any slot, and [nototem:X] flips on any of them.
-- was dropped, expired, killed or recalled. The slot goes unread and every macro
-- is refreshed: a [totem:X] macro may name any slot, and [nototem:X] flips on any
-- of them.
function CleveRoids.Frame:PLAYER_TOTEM_UPDATE()
CleveRoids.QueueActionUpdate()
end
+3 -2
View File
@@ -45,8 +45,9 @@ CleveRoids.supported = CleveRoids.hasTurtle
-- "may we call C_Macro.SetMacroDisplay" guard, so the two can never disagree.
--
-- Feature-detect rather than version-check: SetMacroDisplay ships in ClassicAPI
-- v1.15.0, this addon's minimum, so a nil here means the client mod is missing
-- outright -- the case Core.lua's requirement check warns about but keeps running.
-- v1.15.0, below this addon's minimum, so a nil here means the client mod is
-- missing outright -- the case Core.lua's requirement check warns about but
-- keeps running.
CleveRoids.ClassicAPIMacroDisplay =
(type(C_Macro) == "table" and C_Macro.SetMacroDisplay ~= nil) and true or false
+1 -1
View File
@@ -10,7 +10,7 @@ Enhanced macro addon for World of Warcraft 1.12.1 (Vanilla/Turtle WoW) with dyna
|-----|:--------:|---------|
| [Nampower](https://github.com/brues-code/nampower/releases) (v3.0.0+) | ✅ | Spell queueing, DBC data, auto-attack events |
| [UnitXP_SP3](https://codeberg.org/konaka/UnitXP_SP3/releases) | ✅ | Distance checks, `[multiscan]` enemy scanning |
| [ClassicAPI](https://github.com/brues-code/ClassicAPI/releases) (v1.15.0+) | ✅ | Modern `C_*` API: dispel-type conditionals (`[magic]`, `[curse]`, …), `[moving]` speed, unit-filtered events |
| [ClassicAPI](https://github.com/brues-code/ClassicAPI/releases) (v1.15.8+) | ✅ | Modern `C_*` API: dispel-type conditionals (`[magic]`, `[curse]`, …), `[moving]` speed, unit-filtered events |
## Installation
+18 -8
View File
@@ -734,14 +734,24 @@ CleveRoids.buttons = {
['5'] = 'Button5',
}
-- True while any mapped mouse button is held. Backs the argument-less [button] /
-- [nobutton], mirroring how a bare [mod] means "any modifier". Bare [nobutton] is
-- the practical "activated by a keybind, not a click" test.
function CleveRoids.AnyMouseButtonDown()
for _, name in pairs(CleveRoids.buttons) do
if IsMouseButtonDown(name) then return true end
end
return false
-- The button that invoked the action now running, as retail's [button:N] means
-- it. ClassicAPI v1.15.8+ scopes GetMouseButtonClicked to the click dispatch, so
-- it reads the button of the handler this macro is running under -- through the
-- helpers the handler calls, not just the handler itself -- and nil at any other
-- time. A held button no longer keeps it set, which is what made mouse-turning
-- poison every keybind press before v1.15.8.
--
-- nil outside a click dispatch, so the tooltip resolves as button 1 and sits
-- still rather than tracking the mouse. Button:Click() with no argument reports
-- LeftButton, which is how a keybind comes out as button 1 on retail.
function CleveRoids.GetActivatingButton()
return GetMouseButtonClicked() or "LeftButton"
end
-- True while a click dispatch is what is running this action. Backs bare
-- [button] / [nobutton], which retail has no equivalent for.
function CleveRoids.WasClickActivated()
return GetMouseButtonClicked() ~= nil
end
CleveRoids.kmods = {