I'm trying to figure out whether the following scenario is possible:
- A key is bound to an in-game button, which inherits the SecureActionButtonTemplate.
- When no extra frames are visible, the button casts an arbitrary spell or performs a protected action.
- When a frame is visible (e.g. WorldMapFrame, QuestFrame) the keybinding is redirected to perform operations on that frame. Think of it as frame hotkeys.
- When the player enters combat, the button is NOT immediately reverted to cast spells, but rather stick to handling whatever frame is overlaying the UI until the player decides to close that frame.
- When in combat, and all frames are hidden, whether they are closed mid combat or not, the secure button goes back to casting spells.
- The definition of an overlaying frame is set by the code author.
My current approach is just reverting instantly upon
PLAYER_REGEN_DISABLED, since this will not taint or cause any issues. The problem is that if I'm trying to manage a frame using keyboard input, it will instantly stop working if I happen to grab aggro from a nearby mob. This is obviously not favorable, since there are more than a few scenarios in which you need to interact with the interface mid combat. Since action bar swapping is something I handle with a SecureStateHandler, maybe it's possible to also make decisions depending on which frames are visible?
Example of what I'm currently using:
Lua Code:
local btn = CreateFrame("Button", name..modifier, UIParent, "SecureActionButtonTemplate");
--
-- Bunch of irrelevant shit missing here
--
btn.reset = function()
btn.default = {
type = "click",
attr = "clickbutton",
val = btn.action
}
end
-- MainBarAction is a function that determines whether the action exists on the standard action bar
btn.revert = function()
if MainBarAction(btn.default.val) then
btn.default.type = "action";
btn.default.attr = "action";
btn.default.val = MainBarAction(btn.default.val);
btn:SetID(btn.default.val);
end
btn:SetAttribute("type", btn.default.type);
btn:SetAttribute(btn.default.attr, btn.default.val);
btn:SetAttribute("clickbutton", btn.action);
end
-- This works because of the grace period between the event and secure state lockdown
btn:RegisterEvent("PLAYER_REGEN_DISABLED");
btn:SetScript("OnEvent", btn.revert);
-- Register to handler
Handler:SetFrameRef("NewButton", btn);
SecureHandlerExecute(Handler, [[
BUTTONS[self:GetFrameRef("NewButton")] = true
]]);
Handler code for bar and stance swapping:
Lua Code:
local m = CreateFrame("Frame", "Handler", UIParent, "SecureHandlerStateTemplate");
SecureHandlerExecute(m, [[
BUTTONS = newtable();
UpdateMainActionBar = [=[
local page = ...;
if page == "tempshapeshift" then
if HasTempShapeshiftActionBar() then
page = GetTempShapeshiftBarIndex();
else
page = 1;
end
elseif page == "possess" then
page = self:GetFrameRef("MainMenuBarArtFrame"):GetAttribute("actionpage");
if page <= 10 then
page = self:GetFrameRef("OverrideActionBar"):GetAttribute("actionpage");
end
if page <= 10 then
page = 12;
end
end
self:SetAttribute("actionpage", page);
for btn in pairs(BUTTONS) do
btn:SetAttribute("actionpage", page);
end
]=]
]]);
m:SetFrameRef("MainMenuBarArtFrame", MainMenuBarArtFrame)
m:SetFrameRef("OverrideActionBar", OverrideActionBar)
local state = {};
tinsert(state, "[overridebar][possessbar]possess");
for i = 2, 6 do
tinsert(state, ("[bar:%d]%d"):format(i, i));
end
local _, playerClass = UnitClass("player");
if playerClass == "DRUID" then
tinsert(state, "[bonusbar:1,stealth]7");
elseif playerClass == "WARRIOR" then
tinsert(state, "[stance:2]7");
tinsert(state, "[stance:3]8");
end
for i = 1, 4 do
tinsert(state, ("[bonusbar:%d]%d"):format(i, i+6));
end
tinsert(state, "[stance:1]tempshapeshift");
tinsert(state, "1");
state = table.concat(state, ";");
local now = SecureCmdOptionParse(state);
m:SetAttribute("actionpage", now);
RegisterStateDriver(m, "page", state);
m:SetAttribute("_onstate-page", [=[
self:Run(UpdateMainActionBar, newstate);
]=]);