diff --git a/Modules/Skinning.lua b/Modules/Skinning.lua index a31f04a..9888971 100644 --- a/Modules/Skinning.lua +++ b/Modules/Skinning.lua @@ -138,6 +138,74 @@ function Skin:HideArt(frame) frame:Hide() end +-- Buttons --------------------------------------------------------------------- +-- +-- IsEnabled hands back 0 and 1 on this client rather than false and true +-- (probed), and 0 is truthy in Lua, so every enabled check has to normalise +-- what it gets back before comparing it. +function Skin:IsEnabled(button) + local state = button.IsEnabled and button:IsEnabled() + + return state ~= nil and state ~= false and state ~= 0 +end + +-- The target is resolved the way ElvUI's own SetModifiedBackdrop and +-- SetOriginalBackdrop resolve it -- the backdrop when the button has one, the +-- button itself otherwise -- or the colour lands on something that isn't the +-- object drawing the border. +local function SetButtonBorder(button, lit) + local backdrop = button.backdrop or button + if not backdrop.SetBackdropBorderColor then return end + + backdrop:SetBackdropBorderColor(unpack(lit and E.media.rgbvaluecolor or E.media.bordercolor)) +end + +-- S:HandleButton ends with an unconditional pair of OnEnter/OnLeave hooks that +-- swap the border to the value colour and back, and neither of them looks at +-- IsEnabled. A disabled button still fires both scripts on this client, so a +-- dead button lights up under the cursor and reads as clickable -- the greyed +-- Activate button, Save changes with nothing pending, Purchase with nothing +-- selected. HookScript can't be undone, but handlers fire in registration +-- order, so a hook registered after HandleButton runs last and has the final +-- say on the colour. +-- +-- This one deliberately doesn't consult IsMouseOver: OnEnter can fire a pixel +-- before IsMouseOver turns true (ElvUI's own aura code pads around the same +-- quirk), and reading it here would drop the highlight off a live button. +local function OnButtonEnter(button) + if Skin:IsEnabled(button) then return end + + SetButtonBorder(button, false) +end + +-- For a button that can be disabled while the cursor is already on it: OnEnter +-- has been and gone by then, so nothing corrects the border until the pointer +-- leaves. Only worth wiring up where something already watches the button's +-- state -- it does not justify an OnUpdate of its own. +function Skin:RefreshButtonBorder(button) + SetButtonBorder(button, self:IsEnabled(button) and button:IsMouseOver()) +end + +-- Every S:HandleButton call site in the plugin goes through here, so the +-- disabled-hover correction is inherited rather than repeated per module. The +-- extra arguments are HandleButton's own (strip, isDeclineButton, +-- useCreateBackdrop, noSetTemplate) and are passed straight through. +function Skin:Button(button, ...) + if not button then return end + + S:HandleButton(button, ...) + + -- Guarded separately from HandleButton's own isSkinned flag: these frames + -- re-run their skin pass on every show, and HookScript stacks handlers + -- rather than replacing them, so an unguarded re-hook adds another copy + -- per show for the life of the session. + if not button.CoAButtonSkinned then + button.CoAButtonSkinned = true + + button:HookScript("OnEnter", OnButtonEnter) + end +end + -- Several of these controls draw their art as anonymous regions of one atlas -- file rather than through the named Left/Middle/Right fields or the Normal/ -- Pushed/Disabled set S:HandleButton knows how to clear, so its own clearing @@ -205,7 +273,7 @@ function Skin:Dropdown(dropdown, menu) dropdown.CoASkinned = true self:StripArtByFile(dropdown, DROPDOWN_ART) - S:HandleButton(dropdown) + self:Button(dropdown) SkinDropdownCaret(dropdown) self:Panel(menu) diff --git a/Modules/TalentFrame.lua b/Modules/TalentFrame.lua index 6553674..c43ed70 100644 --- a/Modules/TalentFrame.lua +++ b/Modules/TalentFrame.lua @@ -56,7 +56,7 @@ function SkinChildren(frame, depth) -- "...MenuClose", and it was coming out of the walk as an -- ordinary templated square with the X stripped off it. if not (name and name:find("Close")) then - S:HandleButton(child) + Skin:Button(child) end elseif objType == "Frame" then if IsDropDown(child, name) then @@ -353,17 +353,21 @@ local ACTIVATE_DISABLED_COLOR = {0.55, 0.55, 0.55} -- says so -- the ElvUI panel is drawn the same either way. The label is greyed -- instead, which is how ElvUI marks a dead button everywhere else. -- --- IsEnabled hands back 0 and 1 on this client rather than false and true --- (probed), and 0 is truthy in Lua, so it's normalised before it's compared --- against the cached value. Compared rather than written blind because this --- runs from the button's update: there's no event for a spec becoming active. +-- The enabled state is compared against the cached one rather than written +-- blind because this runs from the button's update: there's no event for a spec +-- becoming active. Skin:IsEnabled does the 0/1-to-boolean normalisation this +-- client needs (see Skinning.lua). local function UpdateActivateState(button) - local state = button:IsEnabled() - local enabled = state ~= nil and state ~= false and state ~= 0 + local enabled = Skin:IsEnabled(button) if enabled == button.CoAActivateEnabled then return end button.CoAActivateEnabled = enabled + -- Activating a spec disables this button under the very cursor that just + -- clicked it, so its hover border would stay lit until the pointer moved + -- off. This update already watches the state, so it carries the border too. + Skin:RefreshButtonBorder(button) + local text = ActivateLabel(button) if not text then return end @@ -380,7 +384,7 @@ local function SkinActivateButton(button) button.CoASkinned = true Skin:StripArtByFile(button, Skin.RedButtonArt) - S:HandleButton(button) + Skin:Button(button) button:HookScript("OnUpdate", UpdateActivateState) end @@ -601,7 +605,7 @@ local function SkinBottomBar() -- stopped at the arrow, and the dead space to its right is taken out -- of the hit rect so it can't swallow clicks meant for those icons. if dropdown and arrow then - S:HandleButton(dropdown, nil, nil, true) + Skin:Button(dropdown, nil, nil, true) if dropdown.backdrop then dropdown.backdrop:ClearAllPoints() @@ -719,7 +723,7 @@ local function SkinSpecChoices() local cardName = SPEC_CHOICE:format(i) if not _G[cardName] then break end - S:HandleButton(_G[cardName.."SelectButton"], true) + Skin:Button(_G[cardName.."SelectButton"], true) end end diff --git a/Modules/VanityFrame.lua b/Modules/VanityFrame.lua index 21c9fa0..5226a64 100644 --- a/Modules/VanityFrame.lua +++ b/Modules/VanityFrame.lua @@ -17,8 +17,8 @@ local FRAME_NAME = "StoreCollectionFrame" -- Highlight textures), confirmed by probe -- S:HandleButton's own texture -- clearing handles these directly, no manual stripping needed. local function SkinActionButtons() - S:HandleButton(_G[FRAME_NAME.."ActivateStoreButton"]) - S:HandleButton(_G[FRAME_NAME.."BuyStoreButton"]) + Skin:Button(_G[FRAME_NAME.."ActivateStoreButton"]) + Skin:Button(_G[FRAME_NAME.."BuyStoreButton"]) end local function SkinSearchBox() diff --git a/Modules/WardrobeFrame.lua b/Modules/WardrobeFrame.lua index f70fd10..3e7a513 100644 --- a/Modules/WardrobeFrame.lua +++ b/Modules/WardrobeFrame.lua @@ -8,9 +8,9 @@ local FRAME_NAME = "AppearanceWardrobeFrame" -- Standard UIPanelButtonTemplate art, same as Vanity's action buttons -- -- S:HandleButton's own texture clearing handles these directly. local function SkinActionButtons() - S:HandleButton(_G[FRAME_NAME.."PlayerModelSaveOutfitButton"]) - S:HandleButton(_G[FRAME_NAME.."DisableTransmogButton"]) - S:HandleButton(_G[FRAME_NAME.."DisableSpellVisualsButton"]) + Skin:Button(_G[FRAME_NAME.."PlayerModelSaveOutfitButton"]) + Skin:Button(_G[FRAME_NAME.."DisableTransmogButton"]) + Skin:Button(_G[FRAME_NAME.."DisableSpellVisualsButton"]) end -- Apply and Cancel only exist while a transmog change is pending, which is why @@ -46,7 +46,7 @@ local function SkinPendingButton(name) -- Stripped before templating: HandleButton adds its backdrop as regions of -- this same button, so a strip afterwards takes the backdrop with the pill. - S:HandleButton(button) + Skin:Button(button) local text = button.GetFontString and button:GetFontString() if text then text:SetTextColor(unpack(LABEL_COLOR)) end