WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   Lua/XML Help (https://www.wowinterface.com/forums/forumdisplay.php?f=16)
-   -   dynamic key bindings with SecureHandlers (https://www.wowinterface.com/forums/showthread.php?t=57596)

illej 10-11-19 08:24 PM

dynamic key bindings with SecureHandlers
 
Hey all,

I'm new here, and new to writing WoW addons and a bit at a loss as to how to progress with my mod.

To start with, the functionality I am trying to achieve is as follows:

The idea is that I want to be able to easily cast any Shaman totem with 3 key strokes.

1) Starting state: keys 1 through to 9 are bound as normally to actionbar 1
2) clicking '/click ElementBinder' in a macro rebinds keys 1 to 4 to represent the 4 totem elements (1: Earth, 2: Fire, 3: Water, 4: Air)
3) Now, pressing any of these overridden keys further overrides them to represent individual Totems to cast,(1: cast Earthbind Totem, 2: cast Strength of Earth, etc).
4) After any Totem is cast, clear all existing bindings: keys 1 through to 9 are bound as normally to actionbar 1

So for Example:
Pressing F > 1 > 2 casts Strength of Earth totem
Pressing F > 1 > 1 casts Earthbind Totem
Pressing F > 2 > 1 casts Searing Totem
Pressing F > 3 > 1 casts Healing Stream Totem
etc ..

So far I have been using SecureHandlers to enable this to work in combat - and it does! However, I have no way to clear the overridden bindings once a Totem is cast and I am looking for any help/suggestions to achieve this!

Here is some code snippets.

Macro to initiate the functionality:
Code:

/click ElementBinder
Lua code to handle the click:
Code:

local element_binder = CreateFrame("BUTTON", "ElementBinder", UIParent, "SecureHandlerClickTemplate");
element_binder:SetAttribute("_onclick", [=[
    if button == "LeftButton" then   
        self:SetBindingClick(true, "1", "EarthBtn")
        self:SetBindingClick(true, "2", "FireBtn")
        -- etc for water and air
    end
]=]);

local earth_btn = CreateFrame("BUTTON", "EarthBtn", element_binder, "SecureHandlerClickTemplate")
earth_btn:SetAttribute("_onclick", [=[
    if button == "LeftButton" then
        self:SetBindingSpell(true, "1", "Earthbind Totem")
        self:SetBindingSpell(true, "2", "Strength of Earth Totem")
        -- etc for the rest of the earth totems
    end
]=])

So some solutions I have (unsuccessfully) explored are:
  • trying to use :WrapScript on the 'EarthBtn' frame and call :ClearBindings() in the 'PostClick', but I have no idea really of what frames should be attached to or inherited from, and this never works
  • RegisterEvent('COMBAT_LOG_EVENT'), and parsing the combat log for 'SPELL_SUMMON' and then trying to call :ClearBindings() on one of the SecureHandler frames, but again I can't figure out the inheritance chain to enable this in the protected path.

Also, is there another way of initiating the original 'click' on the first handler other than through a macro?

Any ideas?

Thanks in advance!

Kanegasi 10-11-19 10:21 PM

There's a distinction with the terminology you're using. There's no such thing as "clearing" a binding you have set back to what it was. When you set a key to something, the previous setting is lost. Clearing a binding is to remove all actions from it. There is also no inherent default to any key, you either need to remember the default before changing a single binding or use LoadBindings(0) to reset ALL keys.

If your intention is to "reset" 1-4 back to the action buttons, just set them to the action buttons. SetBinding and BindingID are what you're looking for.

If I recall correctly, frame:ClearBindings() only removes the frame from any registered bindings, the actual binding is not changed.

kurapica.igas 10-11-19 10:31 PM

It seems like a feature in my IGAS_UI/ActionBar, you can find an example at the end of the page. Well, maybe not, you require two level key binding.

Back to the question, I have an example for you

Lua Code:
  1. -- The button configs
  2. local Config = {
  3.     {
  4.         {
  5.             type = "macro",
  6.             macrotext = "/run print('1-1')"
  7.         },
  8.         {
  9.             type = "macro",
  10.             macrotext = "/run print('1-2')"
  11.         },
  12.     },
  13.     {
  14.         {
  15.             type = "macro",
  16.             macrotext = "/run print('2-1')"
  17.         },
  18.     },
  19.     {
  20.         {
  21.             type = "macro",
  22.             macrotext = "/run print('3-1')"
  23.         },
  24.     },
  25. }
  26.  
  27. -- A mananger frame to control all secure behaviors
  28. local _ManagerFrame = CreateFrame("Frame", "TwoLevelKeyManager", UIParent, "SecureHandlerStateTemplate")
  29.  
  30. _ManagerFrame.Execute = SecureHandlerExecute
  31. _ManagerFrame.WrapScript = function(self, frame, script, preBody, postBody) return SecureHandlerWrapScript(frame, script, self, preBody, postBody) end
  32. _ManagerFrame.SetFrameRef = SecureHandlerSetFrameRef
  33.  
  34. _ManagerFrame:Execute[[
  35.     Manager = self
  36.  
  37.     ToggleButtons = newtable()
  38.     ToggleState = newtable()
  39.  
  40.     -- secure code snippets to be run by manager
  41.     CloseFinalLevel = [==[
  42.         local index = ...
  43.         print("CloseFinalLevel", index)
  44.         if ToggleState[index] then
  45.             ToggleState[index] = false
  46.  
  47.             for i, btn in ipairs(ToggleButtons[index]) do
  48.                 print("Clear Bind", btn:GetName())
  49.                 btn:ClearBinding("" .. i)
  50.             end
  51.         end
  52.         print("Clear Bind", ToggleButtons[index][0]:GetName())
  53.         ToggleButtons[index][0]:ClearBinding("" .. index)
  54.     ]==]
  55.  
  56.     OpenFinalLevel = [==[
  57.         local index = ...
  58.         print("OpenFinalLevel", index)
  59.         if not ToggleState[index] then
  60.             ToggleState[index] = true
  61.  
  62.             for i, btn in ipairs(ToggleButtons[index]) do
  63.                 print("Bind", btn:GetName())
  64.                 btn:SetBindingClick(true, "" .. i, btn:GetName(), "LeftButton")
  65.             end
  66.         end
  67.     ]==]
  68.  
  69.     ToggleRoot = [==[
  70.         print("ToggleRoot", not ToggleState[0])
  71.         if ToggleState[0] then
  72.             -- Clear binding for all
  73.             ToggleState[0] = false
  74.  
  75.             for i, buttons in ipairs(ToggleButtons) do
  76.                 Manager:Run(CloseFinalLevel, i)
  77.             end
  78.         else
  79.             -- Bind key to first level
  80.             ToggleState[0] = true
  81.  
  82.             for i, buttons in ipairs(ToggleButtons) do
  83.                 print("Bind", buttons[0]:GetName())
  84.                 buttons[0]:SetBindingClick(true, "" .. i, buttons[0]:GetName(), "LeftButton")
  85.             end
  86.         end
  87.     ]==]
  88. ]]
  89.  
  90. -- Create buttons
  91. local rootbtn = CreateFrame("CheckButton", "RootToggle", UIParent, "SecureActionButtonTemplate")
  92.  
  93. -- Bind the root key
  94. SetOverrideBindingClick(rootbtn, true, "F", "RootToggle", "LeftButton")
  95.  
  96. -- Toggle the first level button's key binding
  97. _ManagerFrame:WrapScript(rootbtn, "OnClick", [[Manager:Run(ToggleRoot)]])
  98.  
  99. -- Generate the first and second level buttons and register them to the mananger
  100. for i, firstlvl in ipairs(Config) do
  101.     local btn = CreateFrame("CheckButton", "FirstLvlToggle" .. i, UIParent, "SecureActionButtonTemplate")
  102.     _ManagerFrame:SetFrameRef("FirstLvlToggle", btn)
  103.  
  104.     for j, nxtlvl in ipairs(firstlvl) do
  105.         local fbtn = CreateFrame("CheckButton", "NxtLvlToggle" .. i .. "_" .. j, UIParent, "SecureActionButtonTemplate")
  106.         _ManagerFrame:SetFrameRef("NxtLvlToggle" .. j, fbtn)
  107.  
  108.         for k, v in pairs(nxtlvl) do
  109.             fbtn:SetAttribute(k, v) -- Bind marco
  110.         end
  111.  
  112.         -- Clear all key bindings after click the second level button
  113.         _ManagerFrame:WrapScript(fbtn, "OnClick", [[return button, true]], [[Manager:Run(ToggleRoot)]])
  114.     end
  115.  
  116.     _ManagerFrame:Execute(string.format([[
  117.         local index, count = %d, %d
  118.         local buttons = newtable()
  119.         buttons[0] = Manager:GetFrameRef("FirstLvlToggle")
  120.         print("Register", index, buttons[0]:GetName())
  121.         for i = 1, count do
  122.             buttons[i] = Manager:GetFrameRef("NxtLvlToggle" .. i)
  123.             print("Register", buttons[i]:GetName())
  124.         end
  125.  
  126.         ToggleButtons[index] = buttons
  127.     ]], i, #firstlvl))
  128.  
  129.     -- Bind the key to the second level
  130.     _ManagerFrame:WrapScript(btn, "OnClick", string.format([[Manager:Run(OpenFinalLevel, %d)]], i))
  131. end

Run it with WowLua or my Cube or whatever in the game, then press F-1-1, F-1-2, F-2-1 to see the result, the log should show how the code works.

I have no high level shaman, so I use other macro for examples.

MooreaTv 10-14-19 04:55 PM

if you use SetOverrideBindingClick and ClearOverrideBindings you'll get your temporary bindings

example of use:

https://github.com/mooreatv/AuctionD....lua#L145-L151

illej 10-20-19 03:15 AM

Quote:

Originally Posted by kurapica.igas (Post 334222)
It seems like a feature in my IGAS_UI/ActionBar, you can find an example at the end of the page. Well, maybe not, you require two level key binding.

Back to the question, I have an example for you

Lua Code:
  1. -- The button configs
  2. local Config = {
  3.     {
  4.         {
  5.             type = "macro",
  6.             macrotext = "/run print('1-1')"
  7.         },
  8.         {
  9.             type = "macro",
  10.             macrotext = "/run print('1-2')"
  11.         },
  12.     },
  13.     {
  14.         {
  15.             type = "macro",
  16.             macrotext = "/run print('2-1')"
  17.         },
  18.     },
  19.     {
  20.         {
  21.             type = "macro",
  22.             macrotext = "/run print('3-1')"
  23.         },
  24.     },
  25. }
  26.  
  27. -- A mananger frame to control all secure behaviors
  28. local _ManagerFrame = CreateFrame("Frame", "TwoLevelKeyManager", UIParent, "SecureHandlerStateTemplate")
  29.  
  30. _ManagerFrame.Execute = SecureHandlerExecute
  31. _ManagerFrame.WrapScript = function(self, frame, script, preBody, postBody) return SecureHandlerWrapScript(frame, script, self, preBody, postBody) end
  32. _ManagerFrame.SetFrameRef = SecureHandlerSetFrameRef
  33.  
  34. _ManagerFrame:Execute[[
  35.     Manager = self
  36.  
  37.     ToggleButtons = newtable()
  38.     ToggleState = newtable()
  39.  
  40.     -- secure code snippets to be run by manager
  41.     CloseFinalLevel = [==[
  42.         local index = ...
  43.         print("CloseFinalLevel", index)
  44.         if ToggleState[index] then
  45.             ToggleState[index] = false
  46.  
  47.             for i, btn in ipairs(ToggleButtons[index]) do
  48.                 print("Clear Bind", btn:GetName())
  49.                 btn:ClearBinding("" .. i)
  50.             end
  51.         end
  52.         print("Clear Bind", ToggleButtons[index][0]:GetName())
  53.         ToggleButtons[index][0]:ClearBinding("" .. index)
  54.     ]==]
  55.  
  56.     OpenFinalLevel = [==[
  57.         local index = ...
  58.         print("OpenFinalLevel", index)
  59.         if not ToggleState[index] then
  60.             ToggleState[index] = true
  61.  
  62.             for i, btn in ipairs(ToggleButtons[index]) do
  63.                 print("Bind", btn:GetName())
  64.                 btn:SetBindingClick(true, "" .. i, btn:GetName(), "LeftButton")
  65.             end
  66.         end
  67.     ]==]
  68.  
  69.     ToggleRoot = [==[
  70.         print("ToggleRoot", not ToggleState[0])
  71.         if ToggleState[0] then
  72.             -- Clear binding for all
  73.             ToggleState[0] = false
  74.  
  75.             for i, buttons in ipairs(ToggleButtons) do
  76.                 Manager:Run(CloseFinalLevel, i)
  77.             end
  78.         else
  79.             -- Bind key to first level
  80.             ToggleState[0] = true
  81.  
  82.             for i, buttons in ipairs(ToggleButtons) do
  83.                 print("Bind", buttons[0]:GetName())
  84.                 buttons[0]:SetBindingClick(true, "" .. i, buttons[0]:GetName(), "LeftButton")
  85.             end
  86.         end
  87.     ]==]
  88. ]]
  89.  
  90. -- Create buttons
  91. local rootbtn = CreateFrame("CheckButton", "RootToggle", UIParent, "SecureActionButtonTemplate")
  92.  
  93. -- Bind the root key
  94. SetOverrideBindingClick(rootbtn, true, "F", "RootToggle", "LeftButton")
  95.  
  96. -- Toggle the first level button's key binding
  97. _ManagerFrame:WrapScript(rootbtn, "OnClick", [[Manager:Run(ToggleRoot)]])
  98.  
  99. -- Generate the first and second level buttons and register them to the mananger
  100. for i, firstlvl in ipairs(Config) do
  101.     local btn = CreateFrame("CheckButton", "FirstLvlToggle" .. i, UIParent, "SecureActionButtonTemplate")
  102.     _ManagerFrame:SetFrameRef("FirstLvlToggle", btn)
  103.  
  104.     for j, nxtlvl in ipairs(firstlvl) do
  105.         local fbtn = CreateFrame("CheckButton", "NxtLvlToggle" .. i .. "_" .. j, UIParent, "SecureActionButtonTemplate")
  106.         _ManagerFrame:SetFrameRef("NxtLvlToggle" .. j, fbtn)
  107.  
  108.         for k, v in pairs(nxtlvl) do
  109.             fbtn:SetAttribute(k, v) -- Bind marco
  110.         end
  111.  
  112.         -- Clear all key bindings after click the second level button
  113.         _ManagerFrame:WrapScript(fbtn, "OnClick", [[return button, true]], [[Manager:Run(ToggleRoot)]])
  114.     end
  115.  
  116.     _ManagerFrame:Execute(string.format([[
  117.         local index, count = %d, %d
  118.         local buttons = newtable()
  119.         buttons[0] = Manager:GetFrameRef("FirstLvlToggle")
  120.         print("Register", index, buttons[0]:GetName())
  121.         for i = 1, count do
  122.             buttons[i] = Manager:GetFrameRef("NxtLvlToggle" .. i)
  123.             print("Register", buttons[i]:GetName())
  124.         end
  125.  
  126.         ToggleButtons[index] = buttons
  127.     ]], i, #firstlvl))
  128.  
  129.     -- Bind the key to the second level
  130.     _ManagerFrame:WrapScript(btn, "OnClick", string.format([[Manager:Run(OpenFinalLevel, %d)]], i))
  131. end

Run it with WowLua or my Cube or whatever in the game, then press F-1-1, F-1-2, F-2-1 to see the result, the log should show how the code works.

I have no high level shaman, so I use other macro for examples.

Oh wow that is exactly what I was looking for! Thank you so much! There is no way I would have figured that out myself! How did you learn about this stuff? Any particular resources? Or did you just work through the Blizz UI source?

illej 10-20-19 03:16 AM

Quote:

Originally Posted by MooreaTv (Post 334255)
if you use SetOverrideBindingClick and ClearOverrideBindings you'll get your temporary bindings

example of use:

https://github.com/mooreatv/AuctionD....lua#L145-L151

Thanks! That was what I was using initially but it wouldn't work in combat :( Hence delving into SecureHandler-Hell :)

illej 10-20-19 03:20 AM

Quote:

Originally Posted by Kanegasi (Post 334221)
There's a distinction with the terminology you're using. There's no such thing as "clearing" a binding you have set back to what it was. When you set a key to something, the previous setting is lost. Clearing a binding is to remove all actions from it. There is also no inherent default to any key, you either need to remember the default before changing a single binding or use LoadBindings(0) to reset ALL keys.

If your intention is to "reset" 1-4 back to the action buttons, just set them to the action buttons. SetBinding and BindingID are what you're looking for.

If I recall correctly, frame:ClearBindings() only removes the frame from any registered bindings, the actual binding is not changed.

Sorry I mustn't have been clear in my initial post, I was referring to SetOverrideBinding and ClearOverrideBinding. From what I have read the protected path uses this functionality but it is called SetBinding / ClearBinding even though under the hood it is calling SetOverrideBinding / ClearOverrideBinding :)

Thanks for your reply anyway!

kurapica.igas 10-20-19 06:03 AM

Quote:

Originally Posted by illej (Post 334322)
Oh wow that is exactly what I was looking for! Thank you so much! There is no way I would have figured that out myself! How did you learn about this stuff? Any particular resources? Or did you just work through the Blizz UI source?

Several years ago, I study the whole restrict code for three times to figure out all usages, here is the list and read order:

1. RestrictedInfrastructure.lua: Wrap the normal frame to secure handler, and some api for secure using.

2. RestrictedEnvironment.lua: the restricted environment to execute the secure snippets and some api.

3. RestrictedFrames.lua: the secure handler methods that can be used in secure snippets.

4. RestrictedExecution.lua: the execution of the secure snippets, the core part is every secure handler(the wrapper of the frame) has a standalone environment, that's why I always create a manager to process the secure snippets, so all processed in one environment.

5. SecureHandlers.lua: All for secure handler's events, the best in it is the SecureHandlerWrapScript, so I can use manager to wrap all secure frame's actions.

6. SecureTemplates.lua: the template for secure frames, the common is the SecureActionButtonTemplate, used for action button.

7. SecureGroupHeaders.lua: for the raid panel, it's a hard topic, I use it for my secure panel lib, so they can refresh well in the combat no matter player join or leave.

8. SecureStateDriver.lua: It's the best system event trigger(although only support marco conditions) to force the secure frames refreshing during the combat.

I have practiced those features in my own raid panel, container, action bar addon(no dependencies), some features are never used in other addons, the secure system is a gold mine but hard for digging.

If your are interesting, I have some post in this forum talked about the usages of the secure frames, you may learn some tricks from them.


All times are GMT -6. The time now is 05:20 PM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI