local header = CreateFrame('Button', 'AllInOneActionButton', nil, 'SecureHandlerBaseTemplate, SecureHandlerStateTemplate, SecureActionButtonTemplate')
header:RegisterForClicks('AnyUp', 'AnyDown') -- process both press and release of an arbitrary key
header:SetAttribute('action', 1) -- this attribute will be used in conjunction with 'actionbar' to swap the bar back
do -- generate a state driver condition with generic values to ensure any bar change pushes an update.
-- the actual bar ID check is done in the _onstate-actionpage function body instead.
-- this method seems to work regardless of API changes that cause
-- some of these macro conditions to shift around on certain specs.
local conditionBase, driver = '[%s] %d; ', ''
local conditions = {
----------------------------------
'vehicleui', 'possessbar', 'overridebar', 'shapeshift',
'bar:2', 'bar:3', 'bar:4', 'bar:5', 'bar:6',
'bonusbar:1', 'bonusbar:2', 'bonusbar:3', 'bonusbar:4'
----------------------------------
}
-- concatenate conditions into a long macro condition that can be
-- evaluated by the state driver and send updates to the header.
-- e.g. "[vehicleui] 1; [possessbar] 2; [overridebar] 3;" etc.
for i, macroCondition in ipairs(conditions) do
driver = driver .. conditionBase:format(macroCondition, i)
end
----------------------------------
-- append the list for the default bar (1) when none of the conditions apply.
driver = driver .. (#conditions + 1)
-- register the state driver to handle page updates
RegisterStateDriver(header, 'actionpage', driver)
end
-- set up the _onstate-actionpage function body that sets the new actionpage ID.
-- this is a rewrite of the response in ActionBarController_UpdateAll.
-- state drivers use the _onstate-<name> attribute when looking for the function body to run.
-- 'newstate' is the argument passed to all state drivers expressing the outcome
-- of the registered condition, but in this case it's overwritten by the page calculation.
-- note that all code in a restricted environment is written as strings and compiled in
-- real time using loadstring(), to ensure you don't have access to unrestricted API.
header:SetAttribute('_onstate-actionpage', [[
-- update actionpage
if HasVehicleActionBar() then
newstate = GetVehicleBarIndex()
elseif HasOverrideActionBar() then
newstate = GetOverrideBarIndex()
elseif HasTempShapeshiftActionBar() then
newstate = GetTempShapeshiftBarIndex()
elseif GetBonusBarOffset() > 0 then
newstate = GetBonusBarOffset() + 6
else
newstate = GetActionBarPage()
end
self:SetAttribute('actionpage', newstate)
-- update bindings
self:ClearBindings()
for i=1, 12 do
local key = GetBindingKey('ACTIONBUTTON' .. i)
if key then
self:SetBindingClick(false, key, self, tostring(i))
end
end
]])
-- wrap the preclick script on the header
-- use the inherent 'button' argument to tell the header
-- which action button you want to simulate.
-- since PreClick runs before OnClick, you're able to change
-- what the button should do while you're pressing it.
header:WrapScript(header, 'PreClick', [[
if down then
-- set up the downpress attributes to perform the action first
self:SetID(tonumber(button)) -- see ActionButton_CalculateAction
self:SetAttribute('type', 'action')
else
-- set the type to actionbar on release, which swaps the bar back
self:SetAttribute('type', 'actionbar')
end
self:CallMethod('OnButtonPressed', down, button)
]])
-- finally, execute the actionpage response on load,
-- to set the correct action page and your override bindings.
-- 'Execute' is included in the SecureHandlerBaseTemplate,
-- and allows you to execute code in a restricted environment.
header:Execute(header:GetAttribute('_onstate-actionpage'))
-- this function is called from the preclick script,
-- which visually indicates a button press on your bar.
-- this part isn't functionally necessary,
-- it just makes it look like you're pressing regular buttons,
-- when you're actually pressing your hidden button.
function header:OnButtonPressed(down, id)
local button = GetActionButtonForID(id)
if button then
if down and button:GetButtonState() == 'NORMAL' then
button:SetButtonState('PUSHED')
elseif not down and button:GetButtonState() == 'PUSHED' then
button:SetButtonState('NORMAL')
end
end
end