Thread Tools Display Modes
03-11-15, 06:52 AM   #1
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
SecureActionButtonTemplate - stance/form/vehicle

The documentation on SecureActionButtonTemplate is pretty vague, and thus far I haven't figured out the behaviour completely. If I want to create a secure button that uses the type attribute "action" to cast spells etc, how can I make it so it'll change action depending on which action page is currently occupying the normal action bar?

Since I couldn't figure it out back then, my current approach is using the click type and assigning it to the actual button on the bar. This approach sucks, because it doesn't work with vehicles and pet battles. It only works with stances, forms and paging.

Here's a basic visible button in the center of the screen for testing:
Lua Code:
  1. local btn = CreateFrame("Button", "myButton", UIParent, "SecureActionButtonTemplate, UIMenuButtonStretchTemplate")
  2. btn:SetSize(50,50)
  3. btn:SetPoint("CENTER", 0,0)
  4. btn:SetAttribute("type", "action")
  5. btn:SetAttribute("action", 1)
  6. btn:Show()

Thanks in advance, fellow lua wizards.
__________________

Last edited by MunkDev : 03-11-15 at 07:31 AM.
  Reply With Quote
03-11-15, 08:27 AM   #2
Elkano
A Flamescale Wyrmkin
 
Elkano's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2005
Posts: 131
Judging from this code in ActionButton.lua
Code:
 function ActionButton_CalculateAction (self, button)
 	if ( not button ) then
 		button = SecureButton_GetEffectiveButton(self);
 	end
 	if ( self:GetID() > 0 ) then
 		local page = SecureButton_GetModifiedAttribute(self, "actionpage", button);
 		if ( not page ) then
 			page = GetActionBarPage();
 			if ( self.isExtra ) then
 				page = GetExtraBarIndex();
 			elseif ( self.buttonType == "MULTICASTACTIONBUTTON" ) then
 				page = GetMultiCastBarIndex();
 			end
 		end
 		return (self:GetID() + ((page - 1) * NUM_ACTIONBAR_BUTTONS));
 	else
 		return SecureButton_GetModifiedAttribute(self, "action", button) or 1;
 	end
 end
I'd say instead of setting the action attribute you'll have to assign an ID.
__________________
This posting is made of 100% recycled electrons.
  Reply With Quote
03-11-15, 09:11 AM   #3
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
Originally Posted by Elkano View Post
I'd say instead of setting the action attribute you'll have to assign an ID.
This:
Lua Code:
  1. local btn = CreateFrame("Button", "TestActionButton", UIParent, "SecureActionButtonTemplate, UIMenuButtonStretchTemplate")
  2. btn:SetSize(50,50)
  3. btn:SetPoint("CENTER", 0,0)
  4. btn:SetID(1)
  5. btn:Show()
.. doesn't work. I tried adding the type attribute to this snippet
Lua Code:
  1. btn:SetAttribute("type", "action")
.. and this does exactly the same as specifiying an action as an attribute. But it doesn't work with stances/forms.

Edit: I hooked that function to try and find out what's going on:
Code:
/run hooksecurefunc("ActionButton_CalculateAction", function(...) local s, b = ...; print(s:GetName(), s:GetID(), (s:GetID() + (GetActionBarPage()-1) * NUM_ACTIONBAR_BUTTONS), SecureButton_GetModifiedAttribute(s, "actionpage", b)) end)
And these are the returns I get:
ActionButton1 1 1 1 -- default
ActionButton1 1 1 7 -- cat form
ActionButton1 1 1 9 -- bear form
TestActionButton 1 1 nil -- regardless of form
Granted, my button doesn't have an "actionpage" attribute. If I assign the actionpage attribute, that attribute is of course static and I can't mess with that in combat. Since Bartender and I'm assuming a whole bunch of addons have vehicle and dynamic actionbars, it must be possible to do what I'm trying to do. How can I update the actionpage attribute in combat?
__________________

Last edited by MunkDev : 03-11-15 at 10:05 AM.
  Reply With Quote
03-11-15, 10:37 AM   #4
kurapica.igas
A Chromatic Dragonspawn
Join Date: Aug 2011
Posts: 152
Based on the ActionButton_CalculateAction, if you want it act like what you want, you would also control the button's "actionpage" attribute by yourself.

Here is an example to show how to modify the attribute during the combat.

1. Create one manager secure frame to controls the modification.

Lua Code:
  1. Manager = CreateFrame("Frame", "MyActionButtonManager", UIParent, "SecureHandlerStateTemplate")
  2.  
  3. -- Init the manager's environment
  4. SecureHandlerExecute(Manager, [[
  5.         MyButtons = newtable()   -- Register buttons in here
  6.  
  7.         -- Define the Main action bar updating snippet
  8.     UpdateMainActionBar = [=[
  9.         local page = ...
  10.         if page == "tempshapeshift" then
  11.             if HasTempShapeshiftActionBar() then
  12.                 page = GetTempShapeshiftBarIndex()
  13.             else
  14.                 page = 1
  15.             end
  16.         elseif page == "possess" then
  17.             page = Manager:GetFrameRef("MainMenuBarArtFrame"):GetAttribute("actionpage")
  18.             if page <= 10 then
  19.                 page = Manager:GetFrameRef("OverrideActionBar"):GetAttribute("actionpage")
  20.             end
  21.             if page <= 10 then
  22.                 page = 12
  23.             end
  24.         end
  25.         Manager:SetAttribute("actionpage", page)
  26.  
  27.         for btn in pairs(MyButtons) do
  28.             btn:SetAttribute("actionpage", page)
  29.  
  30.             -- Call btn's Refresh method
  31.             btn:CallMethod("Refresh")
  32.         end
  33.     ]=]
  34. ]])

2. Register the manager to state driver to get the action page based on conditions.

Lua Code:
  1. -- We can get true action page from those frames.
  2. Manager:SetFrameRef("MainMenuBarArtFrame", MainMenuBarArtFrame)
  3. Manager:SetFrameRef("OverrideActionBar", OverrideActionBar)
  4.  
  5. -- ActionBar swap register
  6. local state = {}
  7.  
  8. -- special using
  9. tinsert(state, "[overridebar][possessbar]possess")
  10.  
  11. -- action bar swap
  12. for i = 2, 6 do
  13.     tinsert(state, ("[bar:%d]%d"):format(i, i))
  14. end
  15.  
  16. -- stance
  17. local _, playerclass = UnitClass("player")
  18.  
  19. if playerclass == "DRUID" then
  20.     -- prowl first
  21.     tinsert(state, "[bonusbar:1,stealth]8")
  22. elseif playerclass == "WARRIOR" then
  23.     tinsert(state, "[stance:2]7")
  24.     tinsert(state, "[stance:3]8")
  25. end
  26.  
  27. -- bonusbar map
  28. for i = 1, 4 do
  29.     tinsert(state, ("[bonusbar:%d]%d"):format(i, i+6))
  30. end
  31.  
  32. -- Fix for temp shape shift bar
  33. tinsert(state, "[stance:1]tempshapeshift")
  34.  
  35. tinsert(state, "1")
  36.  
  37. state = table.concat(state, ";")
  38.  
  39. local now = SecureCmdOptionParse(state)
  40.  
  41. -- Register the init value
  42. Manager:SetAttribute("actionpage", now)
  43.  
  44. Manager:RegisterStateDriver("page", state)
  45.  
  46. Manager:SetAttribute("_onstate-page", [=[
  47.     Manager:Run(UpdateMainActionBar, newstate)
  48. ]=])

3. Create those action buttons and register them to the Manager
Lua Code:
  1. for i = 1, 12 do
  2.     local btn = CreateFrame("CheckButton", "MyActionButton" .. i, UIParent, "SecureActionButtonTemplate")
  3.     btn:SetID(i)
  4.     btn:SetAttribute("type", "action")
  5.     btn:SetAttribute("actionpage", Manager:GetAttribute("actionpage"))
  6.  
  7.     -- Other init code
  8.         btn.Refresh = function(self)
  9.                 -- Do some refresh work based on it's action
  10.         end
  11.  
  12.     -- Register it to the Manager
  13.     Manager:SetFrameRef("NewButton", btn)
  14.     SecureHandlerExecute(Manager, [[
  15.         MyButtons[Manager:GetFrameRef("NewButton")] = true
  16.     ]])
  17. end

It's an example code from my lib file ActionHandler.

I don't have much time to test it, but I think it's enough for your questions.
  Reply With Quote
03-11-15, 10:57 AM   #5
Elkano
A Flamescale Wyrmkin
 
Elkano's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2005
Posts: 131
This part of ActionBarControler.lua should be what handles switching the bar in the default code:
Lua Code:
  1. function ActionBarController_UpdateAll(force)
  2.     PossessBar_Update();
  3.     StanceBar_Update();
  4.     CURRENT_ACTION_BAR_STATE = LE_ACTIONBAR_STATE_MAIN;
  5.    
  6.     -- If we have a skinned vehicle bar or skinned override bar, display the OverrideActionBar
  7.     if ((HasVehicleActionBar() and UnitVehicleSkin("player") and UnitVehicleSkin("player") ~= "")
  8.     or (HasOverrideActionBar() and GetOverrideBarSkin() and GetOverrideBarSkin() ~= "")) then
  9.         -- For now, a vehicle has precedence over override bars (hopefully designers make it so these never conflict)
  10.         if (HasVehicleActionBar()) then
  11.             OverrideActionBar_Setup(UnitVehicleSkin("player"), GetVehicleBarIndex());
  12.         else
  13.             OverrideActionBar_Setup(GetOverrideBarSkin(), GetOverrideBarIndex());
  14.         end
  15.        
  16.         CURRENT_ACTION_BAR_STATE = LE_ACTIONBAR_STATE_OVERRIDE;
  17.     -- If we have a non-skinned override bar of some sort, use the MainMenuBarArtFrame
  18.     elseif ( HasBonusActionBar() or HasOverrideActionBar() or HasVehicleActionBar() or HasTempShapeshiftActionBar() or C_PetBattles.IsInBattle() ) then
  19.         if (HasVehicleActionBar()) then
  20.             MainMenuBarArtFrame:SetAttribute("actionpage", GetVehicleBarIndex());
  21.         elseif (HasOverrideActionBar()) then
  22.             MainMenuBarArtFrame:SetAttribute("actionpage", GetOverrideBarIndex());
  23.         elseif (HasTempShapeshiftActionBar()) then
  24.             MainMenuBarArtFrame:SetAttribute("actionpage", GetTempShapeshiftBarIndex());
  25.         elseif (HasBonusActionBar() and GetActionBarPage() == 1) then
  26.             MainMenuBarArtFrame:SetAttribute("actionpage", GetBonusBarIndex());
  27.         else
  28.             MainMenuBarArtFrame:SetAttribute("actionpage", GetActionBarPage());
  29.         end
  30.        
  31.         for k, frame in pairs(ActionBarButtonEventsFrame.frames) do
  32.             ActionButton_UpdateAction(frame);
  33.         end
  34.     else
  35.         -- Otherwise, display the normal action bar
  36.         ActionBarController_ResetToDefault(force);
  37.     end
  38.    
  39.     ValidateActionBarTransition();
  40. end

The individual ActionButtons are set to use their parent's value, but that'll just save some calls:
Lua Code:
  1. self:SetAttribute("useparent-actionpage", true);

Now regarding your problem of updating the attributes on your own frames, using SecureStateDriver and SecureHandlerStateTemplate, like the previous poster also did, is likely the way to go.
However, instead of hardcoding all bar ids like he seems to have done, maybe one can clone Blizzard's code.

Never used the stuff myself though, so the whole things posted were just dug up quickly.
__________________
This posting is made of 100% recycled electrons.
  Reply With Quote
03-11-15, 10:59 AM   #6
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
Certainly more complicated than I thought it would be. This should suffice.
Just a quick question though and I might be an idiot for asking it; why isn't "Manager" localized?
Thank you very much for this!
__________________
  Reply With Quote
03-11-15, 01:10 PM   #7
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
Thank you kurapica.igas. I managed to get it working using a modified version of the code you provided
__________________
  Reply With Quote
03-11-15, 06:15 PM   #8
kurapica.igas
A Chromatic Dragonspawn
Join Date: Aug 2011
Posts: 152
In my action system, I don't really need an existed action bar, so I don't use a use parent attribute.

Localized the Manager is because a blizzard's bug, the "control" may not existed in some condition, but it's a long time ago, I don't know if they fix it.

Since all the snippets are running in the Manager's environment, it's simple to keep a global variable to make sure I can get it everywhere.
  Reply With Quote
03-11-15, 06:18 PM   #9
kurapica.igas
A Chromatic Dragonspawn
Join Date: Aug 2011
Posts: 152
Ah, I forget it in the first part, the first part should be

Lua Code:
  1. Manager = CreateFrame("Frame", "MyActionButtonManager", UIParent, "SecureHandlerStateTemplate")
  2.  
  3. -- Init the manager's environment
  4. SecureHandlerExecute(Manager, [[
  5.      Manager = self    -- I forget this, But I think you already found it by yourself
  6.  
  7.         MyButtons = newtable()   -- Register buttons in here
  8.        -- ...
  9.     ]=]
  10. ]])

I just extract some code from my files, it's defined in several files, sorry, I missed the important line.

Last edited by kurapica.igas : 03-11-15 at 06:22 PM.
  Reply With Quote
03-11-15, 08:21 PM   #10
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
Yes, I figured it all out. This is the code I use for my secure buttons and handler now:
Lua Code:
  1. function ConsolePort:CreateManager()
  2.     if not ConsolePortManager then
  3.         local m = CreateFrame("Frame", "ConsolePortManager", ConsolePort, "SecureHandlerStateTemplate");
  4.         SecureHandlerExecute(m, [[
  5.             CP_BUTTONS = newtable();
  6.             UpdateMainActionBar = [=[
  7.                 local page = ...;
  8.                 if page == "tempshapeshift" then
  9.                     if HasTempShapeshiftActionBar() then
  10.                         page = GetTempShapeshiftBarIndex();
  11.                     else
  12.                         page = 1;
  13.                     end
  14.                 elseif page == "possess" then
  15.                     page = self:GetFrameRef("MainMenuBarArtFrame"):GetAttribute("actionpage");
  16.                     if page <= 10 then
  17.                         page = self:GetFrameRef("OverrideActionBar"):GetAttribute("actionpage");
  18.                     end
  19.                     if page <= 10 then
  20.                         page = 12;
  21.                     end
  22.                 end
  23.                 self:SetAttribute("actionpage", page);
  24.                 for btn in pairs(CP_BUTTONS) do
  25.                     btn:SetAttribute("actionpage", page);
  26.                 end
  27.             ]=]
  28.         ]]);
  29.         m:SetFrameRef("MainMenuBarArtFrame", MainMenuBarArtFrame)
  30.         m:SetFrameRef("OverrideActionBar", OverrideActionBar)
  31.         local state = {};
  32.         table.insert(state, "[overridebar][possessbar]possess");
  33.         for i = 2, 6 do
  34.             table.insert(state, ("[bar:%d]%d"):format(i, i));
  35.         end
  36.         local _, playerClass = UnitClass("player");
  37.         if playerClass == "DRUID" then
  38.             table.insert(state, "[bonusbar:1,stealth]7"); -- edited
  39.         elseif playerClass == "WARRIOR" then
  40.             table.insert(state, "[stance:2]7");
  41.             table.insert(state, "[stance:3]8");
  42.         end
  43.         for i = 1, 4 do
  44.             table.insert(state, ("[bonusbar:%d]%d"):format(i, i+6));
  45.         end
  46.         table.insert(state, "[stance:1]tempshapeshift");
  47.         table.insert(state, "1");
  48.         state = table.concat(state, ";");
  49.         local now = SecureCmdOptionParse(state);
  50.         m:SetAttribute("actionpage", now);
  51.         RegisterStateDriver(m, "page", state);
  52.         m:SetAttribute("_onstate-page", [=[
  53.             self:Run(UpdateMainActionBar, newstate);
  54.         ]=]);
  55.     end
  56. end
  57.  
  58. function ConsolePort:CreateSecureButton(name, modifier, clickbutton, UIcommand)
  59.     local btn   = CreateFrame("Button", name..modifier, UIParent, "SecureActionButtonTemplate");
  60.     local functionRefs = {
  61.         "Taxi", "Gossip", "Quest", "Map", "Book", "Spec", "Glyph", "Menu",
  62.         "Bags", "Gear", "Shop", "Misc", "Popup", "Loot", "Stack", -- "List" (taint issue, left out atm)
  63.     }
  64.     btn.name    = name;
  65.     btn.timer   = 0;
  66.     btn.state   = G.STATE_UP;
  67.     btn.action  = _G[clickbutton];
  68.     btn.command = UIcommand;
  69.     btn.mod     = modifier;
  70.     btn.default = {
  71.         type = "click",
  72.         attr = "clickbutton",
  73.         val  = btn.action
  74.     };
  75.     for i, func in pairs(functionRefs) do
  76.         btn[func] = function(btn) self[func](self, btn.command, btn.state); end;
  77.     end
  78.     btn.rebind  = function(btn) self:ChangeButtonBinding(btn); end;
  79.     btn.revert  = function()
  80.         if  MainBarAction(btn.default.val) then
  81.             btn.default.type = "action";
  82.             btn.default.attr = "action";
  83.             btn.default.val  = MainBarAction(btn.default.val);
  84.         end
  85.         btn:SetAttribute("type", btn.default.type);
  86.         btn:SetAttribute(btn.default.attr, btn.default.val);
  87.     end
  88.     btn:SetID(btn.action:GetID());
  89.     btn.revert();
  90.     btn:SetAttribute("actionpage", ConsolePortManager:GetAttribute("actionpage"));
  91.     btn:RegisterEvent("PLAYER_REGEN_DISABLED");
  92.     btn:SetScript("OnEvent", function(self, event, ...)
  93.         self.revert();
  94.     end);
  95.     btn:HookScript("OnMouseDown", function(self, button)
  96.         local func = self:GetAttribute("type");
  97.         local click = self:GetAttribute("clickbutton");
  98.         self.state = G.STATE_DOWN;
  99.         self.timer = 0;
  100.         if  func == "click" or func == "action" then
  101.             click:SetButtonState("PUSHED");
  102.             return;
  103.         end
  104.         -- Fire function twice where keystate is requested
  105.         if  self[func] then self[func](self); end;
  106.     end);
  107.     btn:HookScript("OnMouseUp", function(self, button)
  108.         local func = self:GetAttribute("type");
  109.         local click = self:GetAttribute("clickbutton");
  110.         self.state = G.STATE_UP;
  111.         if func == "click" or func == "action" then
  112.             click:SetButtonState("NORMAL");
  113.         end
  114.     end);
  115.     if  btn.command == G.UP or
  116.         btn.command == G.DOWN or
  117.         btn.command == G.LEFT or
  118.         btn.command == G.RIGHT then
  119.         btn:SetScript("OnUpdate", function(self, elapsed)
  120.             self.timer = self.timer + elapsed;
  121.             if self.timer >= 0.2 and btn.state == G.STATE_DOWN then
  122.                 local func = self:GetAttribute("type");
  123.                 if self[func] then self[func](self); end;
  124.                 self.timer = 0;
  125.             end
  126.         end);
  127.     end
  128.     ConsolePortManager:SetFrameRef("NewButton", btn);
  129.     SecureHandlerExecute(ConsolePortManager, [[
  130.         CP_BUTTONS[self:GetFrameRef("NewButton")] = true
  131.     ]]);
  132. end

As you can see, I changed all the inbound references to self already.
__________________

Last edited by MunkDev : 03-22-15 at 04:11 PM.
  Reply With Quote
03-22-15, 02:20 PM   #11
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
I've just noticed that this solution doesn't seem to work for prowling druids in cat form.
Any ideas?

Edit: Solved it! The prowled actionpage is 7, not 8.
Changed this bit:
Lua Code:
  1. table.insert(state, "[bonusbar:1,stealth]8");

To this:
Lua Code:
  1. table.insert(state, "[bonusbar:1,stealth]7");
__________________

Last edited by MunkDev : 03-22-15 at 04:11 PM.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » SecureActionButtonTemplate - stance/form/vehicle

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off