Thread Tools Display Modes
03-06-13, 02:34 PM   #1
shalom69
A Fallenroot Satyr
Join Date: Feb 2013
Posts: 22
question about key bindings XML LUA

Hey guys I have a question about setting key bindings.

I have a frame in my addon set some key bindings OnLoad with the following lines

in my XML file

Code:
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ .. \FrameXML\UI.xsd">
	<Frame name="BootUpFrame" hidden="true">
		<Scripts>
			<OnLoad	function="Bindings_OnLoad"/>
		</Scripts>
	</Frame>
</Ui>
then the function at the top of my addon

Code:
function Bindings_OnLoad(self)
	SetBindingMacro("k", "Assist Leader")
end
from what I understand this should set my key "k" to macro Assist Leader which does not work in game

I tried this code in my .lua

Code:
function Bindings_OnLoad(self)
	SetBindingMacro("k", "Assist Leader")
	action = GetBindingByKey("k")
	print(action)
end
which outputs

"MACRO Assist Leader"

which indicates at least at the beginning it's bound

also tried

Code:
function Bindings_OnLoad(self)
	success=SetBindingMacro("k", "Assist Leader")
	print(success)
end
which output "1" which means it did the keybind according to API guides

For reference this is basically my multibox addon and it changes a bunch of bindings if I have it activated. If I'm doing arena or whatever I use different keybinds from when I multibox and run PVE.

l must be something simple I'm missing. anyone care to explain?
  Reply With Quote
03-06-13, 04:12 PM   #2
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
1. Your "action" and "success" variables should be local, not global. You should get in the habit of making everything local, and only making things global when it's actually required, which is almost never. Leaked globals cause unnecessary taint, break other addons, or even break the whole UI.

2. I believe key bindings need to be specified in uppercase, so "K" not "k".

3. Make sure you actually have a macro named "Assist Leader" with that exact spelling, capitalization, and spacing.

4. You probably need to wait until PLAYER_LOGIN or at least your addon's own ADDON_LOADED to change bindings, since at the OnLoad point, saved bindings might not be loaded yet, in which case you're setting your binding, and then saved bindings load and overwrite yours.

5. You should probably use override bindings instead of regular, permanent bindings. Override bindings exist only for the session, so they won't mess with the user's regular bindings. They also (as their name might imply) override regular bindings, so you don't need to unset any regular bindings that would otherwise conflict with your addon bindings.

6. Unless you really love XML, don't use it; it's unnecessary, verbose, ugly, annoying to work with directly, and makes it (basically) impossible to keep all of your related code together. Your existing XML and Lua code can be entirely replaced with this Lua code:

Code:
local f = CreateFrame("Frame")
f:RegisterEvent("PLAYER_LOGIN")
f:SetScript("OnEvent", function(self, event, ...)
     SetOverrideBinding(self, nil, "K", "MACRO Assist Leader")
end)
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
03-06-13, 05:11 PM   #3
shalom69
A Fallenroot Satyr
Join Date: Feb 2013
Posts: 22
thanks for the reply dude.

I'm not at home atm so I can't check but at first glance looks like the lowercase "k" may be a culprit. I'll check and let you know when I am at my WOW client.

The syntax on the macro name is for sure good, that's the first thing I checked and I triple checked. The reason I'm using XML is because the dude in my stupid WOW addon book suggested it, although I thought it odd that most of the current addons I looked at omitted the addon.xml file completely.

The weird thing and what spurred me to post here to ask is the fact that

Code:
	action = GetBindingByKey("k")
	print(action)
spit out 2 different responses. When I included it in the OnLoad function the output for the variable action from the WOW client on my chat was "MACRO Assist Leader" as soon as I loaded

5 seconds later when I copy and pasted the exact same syntax manually with /run variable action printed nil.


In both instances it was a lowercase k since I copy and pasted to make sure the syntax was identical. I did find screwing around that the binding names are all in capitals when output from the client so there might be something to the capitalization. Maybe the game does think it successfully binds to the "k" but in fact needs a capital "K." Like I said I'll check and get back to you.

If I can do the entire thing in Lua then that works for me too. One thing that was good about using xml is that the book I am using showed how to bind the keys and get them to show up in the default WOW keybindings menu. Is there a way to do that with lua as well?

Your advice on using local for everything is noted. As I noted in the other thread, it's monkey see monkey do at this point as I'm learning. The examples I saw didn't specify "local action" and "local success" so monkey didn't type that. Semantically, "local" means only applying to that particular addon right? You're right that there's nothing in my addon that needs to be global in that case.
  Reply With Quote
03-06-13, 07:08 PM   #4
shalom69
A Fallenroot Satyr
Join Date: Feb 2013
Posts: 22
OK Phanx

Well the "k" wasn't the issue. Lowercase binds fine. We didn't manage to isolate the problem, it doesn't seem to me that the addon would be able to spit out a variable before it's loaded. I ended up using your workaround of registering the PLAYER_LOGIN and running the entire thing in lua

cheers and thanks for the input and your advice on local variables
  Reply With Quote
03-06-13, 09:28 PM   #5
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by shalom69 View Post
One thing that was good about using xml is that the book I am using showed how to bind the keys and get them to show up in the default WOW keybindings menu. Is there a way to do that with lua as well?
You still need a dedicated Bindings.xml file to add a binding to the default Key Bindings window, but that has to be separate from any other XML content you may have (like creating frames) so you may as well not have any other XML content.

If you use a Bindings.xml file, you do not need to (and should not) call SetBinding manually from your addon. Once the user binds a key in the Key Bindings window, WoW itself will handle assigning that binding at login.

Originally Posted by shalom69 View Post
Your advice on using local for everything is noted. As I noted in the other thread, it's monkey see monkey do at this point as I'm learning. The examples I saw didn't specify "local action" and "local success" so monkey didn't type that. Semantically, "local" means only applying to that particular addon right? You're right that there's nothing in my addon that needs to be global in that case.
That's why I always yell at people for posting code with global variables. Even if they only mean it as a "quick example", other people may not realize it's not code that should be copied and pasted as-is.

Semantically, "local" means the variable only exists in that particular scope. If you write "local var = 2" at the top of your file, then you can access the variable "var" anywhere in that file to get the value 2. If you write the same line inside a function, then the variable only exists inside that function. If you define a variable as local inside a loop, it only exists inside the loop, etc.

A variable defined at the top of a function is available inside a loop that's also inside the same function. You can access variables that are local to higher scopes, but not to lower scopes. Imagine a set of Russian nesting dolls made out of one-way mirror glass. You can see out of them, but not into them. If you're in the 3rd doll from the oustide, you can see your own doll, and the two bigger dolls, but you can't see any dolls that might be inside yours.

Here are some examples:

Code:
local var = 2

local function PrintVar()
    local var = "two"
    print(var)
end

print(PrintVar())
print(var)
... will print "two" and then 2, not 2 and 2, and not "two" and "two".

Code:
local unit = "player"

for i = 1, GetNumSubgroupMembers() do
     print(unit)
     local unit = "party"..i
     print(unit)
end

print(name)
... will (in a full party) print "player", "party1", "party2", "party3", "party4", and then "player" again.

Now, if you remove the "local" declaration inside the loop:

Code:
local unit = "player"

for i = 1, GetNumSubgroupMembers() do
     print(unit)
     unit = "party"..i -- no longer local to this scope
     print(unit)
end

print(name)
... then you're using the "unit" variable from the higher-up scope, so you'll get "player", "party1", "party2", "party3", "party4" -- and then "party4" again instead of "player" like in the previous example.
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.

Last edited by Phanx : 03-06-13 at 09:31 PM.
  Reply With Quote
03-07-13, 05:04 AM   #6
shalom69
A Fallenroot Satyr
Join Date: Feb 2013
Posts: 22
Ok good explanation about local. I knew that declaring local variables at the start of the addon defined them for that addon but your explanation makes it clearer when I declare local variables inside a bunch of if-then statements.

I actually considered in passing that the examples for bindings may have something to do handling functions in different xml and lua files, so I thought to minimize errors the first time I was coding it by following the examples exactly. Obviously knowing the semantics precisely makes that unnecessary for me so I appreciate you typing that all out. To your point I think folks who post examples and do tutorials need to assume that some portion of the readers do not have any formal training and may not be aware of noob things you learn the first week of programming class

I took a look at the bindings.xml

in the xml file

Code:
<Binding header="MYADDON" name="ASSISTLEADERMACRO">?????????()</Binding>
and in the lua

Code:
BINDING_HEADER_MYADDON= "MyAddon"
BINDING_NAME_ASSISTLEADERMACRO= "Assist Leader"
creates the category "MyAddon" and associates a button to a string named "Assist Leader" in WOW default Key Bindings. Using just bindings.xml and .lua how do I assign the actual macro assist actions to the string?
  Reply With Quote
03-07-13, 05:31 AM   #7
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
I am not going to comment or reply about your code because I never found XML useful enough to even bother learning.

What I am adding to the discussion is an example case in support of Phanx's suggestion of using override bindings. In my AddOn SmartRes2, the key binds are overrides. You will not see them in the keybindings window (Esc, Keybindings). Because SmartRes2 deals with all things raising players from the dead after combat, here's what a player can do:
  1. Use the Keybindings screen (Esc, Keybindings) to set a key or combination of keys to perform action X while in combat
  2. Bind the same key or combination of keys in SmartRes2, which will override the bindings when the player is out of combat
This means the same keybinds can be used both in and out of combat for completely different purposes.
  Reply With Quote
03-07-13, 05:47 AM   #8
shalom69
A Fallenroot Satyr
Join Date: Feb 2013
Posts: 22
I gotcha, you are saying that you register PLAYER_REGEN_DISABLED and then SetBindingMacro a bunch of stuff when you flag in and out of combat.

or I guess if SetBinding is protected, you check INCOMBAT FLAGGED when you press a key out of combat you switch it, press then rebind back to combat. something like that.

And then instead of using the default UI Key Binding frame, you make your own frame with the binds or w/e

That's pretty sweet. Your guys' suggestion about using conditional bindings is actually very useful, esp for a multibox with limited keyboard space. I'd still like to know how to work what Phanx was talking about in the bindings.xml and linking the string "Assist Leader" to the actual macro that does the assisting.
  Reply With Quote
03-07-13, 01:46 PM   #9
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
Here's how I did it, using non-Ace3 speak:
Lua Code:
  1. local MyAddOn = ...
  2. local eventFrame = CreateFrame("Frame")
  3. eventFrame:RegisterEvent("PLAYER_LOGIN")
  4. eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
  5. eventFrame:RegiserEvent("PLAYER_REGEN_DISABLED")
  6.  
  7. function MyAddOn:SetBindings()
  8.     -- set your override bindings
  9. end
  10.  
  11. function MyAddOn:ClearBindings()
  12.     -- clear all overrides
  13. end
  14.  
  15. function MyAddOn:PLAYER_LOGIN()
  16.     self:SetBindings()
  17.     eventFrame:UnregisterEvent("PLAYER_LOGIN")
  18. end
  19.  
  20. function MyAddOn:PLAYER_REGEN_ENABLED()
  21.     self:SetBindings()
  22. end
  23.  
  24. function MyAddOn:PLAYER_REGEN_DISABLED()
  25.     self:ClearBindings()
  26. end
  Reply With Quote
03-07-13, 02:13 PM   #10
shalom69
A Fallenroot Satyr
Join Date: Feb 2013
Posts: 22
Cool man, I guess clearing bindings isn't affected by combat so you can simply dump them to get back to combat bindings

Thanks for the example myrroddin can you explain why you need that first line?

Last edited by shalom69 : 03-07-13 at 02:17 PM.
  Reply With Quote
03-07-13, 05:15 PM   #11
Aanson
A Flamescale Wyrmkin
Join Date: Aug 2009
Posts: 124
Originally Posted by myrroddin View Post
function Bindings_OnLoad(self)
SetBindingMacro("k", "Assist Leader")
end
Hey. I could be wrong here, but it's my understanding that once you've set a binding in this way, you immediately need to save the binding before it can be used.

Saving the binding transfers the data from ram to disk (WTF\Account\ACCOUNTNAME\bindings-cache.wtf). I've always looked at it as being the equivalent of pressing confirm in the binding UI frame.

The function you'd be after is:

Lua Code:
  1. SaveBindings(index)

... where 'index' is either integer 1 or 2 (account wide or character wide respectively).



Aanson.
__________________
__________________
  Reply With Quote
03-07-13, 05:16 PM   #12
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
The first line in Myrroddin's example should be:

Code:
local MYADDON, MyAddOn = ...
Every addon gets two variables passed into every one of its Lua files. The first is a string containing the addon's folder/TOC name. The second is a table that's local to the addon, so it's accessible only by the addon's files (unless the addon explicitly adds it to the global namespace) and is handy for sharing stuff between your addon's files that don't need to be shared with the rest of the UI.

However, in Myrroddin's example, the first line isn't actually needed at all, and you can just replace all other instances of "MyAddOn" with "eventFrame" -- if you already have a frame (ie. to listen for events) you don't need to define your functions/methods on another table, you can just define them on the frame.

----------

For bindings, if you are using conditional bindings, you use SetOverrideBinding, not SetBinding. Using SetBinding will permanently overwrite the user's normal bindings; using SetOverrideBinding does nto affect them.

Code:
function MyAddOn:SetBindings()
    -- Make sure you're not in combat:
    if InCombatLockdown() then return end

    -- Temporarily bind "K" to the "Assist Leader" macro:
    SetOverrideBinding(self, nil, "K", "MACRO Assist Leader")
end

function MyAddOn:ClearBindings()
    -- Make sure you're not in combat:
    if InCombatLockdown() then return end

    -- (a) Remove the temporary binding for "K" set by this addon:
    SetOverrideBinding(self, nil, "K", nil)

    -- (b) or, remove *all* temporary bindings set by this addon:
    ClearOverrideBindings(self)

    -- Do either (a) or (b), not both!
end
-----

To bind a key to a macro using Bindings.xml, do this:

Code:
<Bindings>
    <Binding name="MACRO Assist Leader" header="MYADDON">
        -- Nothing goes here.
    </Binding>
</Bindings>
Then define your strings like so:

Code:
BINDING_HEADER_MYADDON = "My AddOn"
_G["BINDING_NAME_MACRO Assist Leader"] = "Assist Leader macro"
Global variables (such as the binding name key) can contain spaces; you just need to quote them as strings and explicitly look them up in the _G table instead of looking them up directly.

----------

Edit:

Aason also has a point. You do need to save permanent bindings. However, you don't need to save override bindings, as they don't actually get "saved" like permanent bindings.

When calling SaveBindings(), always use GetCurrentBindingSet() to get the right "index" value so you don't screw with the user's preference to use account-wide or per-character bindings:

Code:
SaveBindings(GetCurrentBindingSet())
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.

Last edited by Phanx : 03-07-13 at 05:20 PM.
  Reply With Quote
03-08-13, 12:39 AM   #13
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
That's not my quote, Aason! LOL anyway...

Yes, once again, Phanx is correct about the first line in my example code. I left out the table part because it didn't seem required to illustrate. Here is an example of that first line as I use it in FeedTillers:
Lua Code:
  1. -- both of these are variables, and can be named anything
  2. -- I could have written local JoeBob, Clyde = ...
  3. -- but the names I chose are more meaningful
  4. -- ADDON is a string derived from FeedTillers.toc
  5. -- L is a table that I use for localization
  6. local ADDON, L = ...
  7.  
  8. local LOCALE = GetLocale()
  9. if LOCALE == "esES" then
  10. L["CLICK_SORT"] = "Clic para alternar entre ordenar por nombre de PNJ o nombre de comida."
  11. L["SHIFT_DOWN"] = "Mayús-clic para ocultar a los Labradores que ya ha alimentado hoy."
  12. L["ToC/Description"] = "Mostrar cuál comida le gusta cada miembro de los Labradores, y si ha completado su misión diaria \"Un plato para...\""
  13.  
  14. elseif LOCALE == "esMX" then
  15. L["CLICK_SORT"] = "Clic para alternar entre ordenar por nombre de PNJ o nombre de comida."
  16. L["SHIFT_DOWN"] = "Mayús-clic para ocultar a los Labradores que ya ha alimentado hoy."
  17. L["ToC/Description"] = "Mostrar cuál comida le gusta cada miembro de los Labradores, y si ha completado su misión diaria \"Un plato para...\""
  18.  
  19. elseif LOCALE == "itIT" then
  20. L["CLICK_SORT"] = "Click sul plugin per elencare i Coltivatori per il nome o per il loro oggetto"
  21. L["SHIFT_DOWN"] = "Tieni premuto il tasto <Shift> per nascondere i Contivatori giŕ sfamati"
  22. L["ToC/Description"] = "Indica quale Coltivatore, che cibo gli piace, e se lo hai giŕ sfamato oggi con la quest \"Un piatto per...\""
  23.  
  24. elseif LOCALE == "ptBR" then
  25. -- L["CLICK_SORT"] = ""
  26. -- L["SHIFT_DOWN"] = ""
  27. -- L["ToC/Description"] = ""
  28.  
  29. elseif LOCALE == "frFR" then
  30. -- L["CLICK_SORT"] = ""
  31. -- L["SHIFT_DOWN"] = ""
  32. -- L["ToC/Description"] = ""
  33.  
  34. elseif LOCALE == "deDE" then
  35. -- L["CLICK_SORT"] = ""
  36. -- L["SHIFT_DOWN"] = ""
  37. -- L["ToC/Description"] = ""
  38.  
  39. elseif LOCALE == "ruRU" then
  40. -- L["CLICK_SORT"] = ""
  41. -- L["SHIFT_DOWN"] = ""
  42. -- L["ToC/Description"] = ""
  43.  
  44. elseif LOCALE == "zhCN" then
  45. --@localization(locale="zhCN", format="lua_additive_table)@
  46. elseif LOCALE == "zhTW" then
  47. -- L["CLICK_SORT"] = ""
  48. -- L["SHIFT_DOWN"] = ""
  49. -- L["ToC/Description"] = ""
  50.  
  51. else
  52. L["CLICK_SORT"] = "Click the plugin to sort by Tiller name or item name"
  53. L["SHIFT_DOWN"] = "Hold the <Shift> key and click to hide already fed Tillers"
  54. end

Please note that line 45 in the above code refers to Curseforge's and WoWAce's Localization app keyword substitutions.
  Reply With Quote
03-08-13, 07:14 AM   #14
shalom69
A Fallenroot Satyr
Join Date: Feb 2013
Posts: 22
ok thanks for the replies. gj guys

if I have any more questions I'll jump back on this thread after trying out what's been suggested and learning some of the things you brought up
  Reply With Quote
03-10-13, 05:47 PM   #15
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by shalom69
looking at this code (code isn't important except the highlighted part)

Code:
if (msg == "INCOMBAT FLAGGED") then
    if sender == UnitName("player") then
        local clone, target
        for clone, target in pairs(cloneTargetTable) do
            if target == "NONE" or nil then
                RaidNotice_AddMessage(RaidWarningFrame, clone .." HAS NO TARGET", ChatTypeInfo["RAID_WARNING"])
            end
        end
    elseif not UnitAffectingCombat("player") then
        RaidNotice_AddMessage(RaidWarningFrame, sender .." HAS ENTERED COMBAT", ChatTypeInfo["RAID_WARNING"])			
    end    
end
is there a way to declare the variables one further level in?

for example I know you can do

Code:
local _,target = strsplit(" ", msg, 2)
and declare it right in the operation you're doing. just that quick question,

thanks
You don't need to declare those variables as local at all, because they are automatically defined as local to the scope of the loop when used in a "for" statement:

Code:
for k, v in pairs(t) do
   -- k and v exist here
end
-- but not here
Code:
for i = 1, 10 do
   -- i exists here
end
-- but not here
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
03-10-13, 06:49 PM   #16
shalom69
A Fallenroot Satyr
Join Date: Feb 2013
Posts: 22
gotcha. appreciate the information Phanx

I see

Code:
"for i= 1, 10 do local k, v = etc"
all the time. Are only the variables explicitly part of the for = statement declared locally

Last edited by shalom69 : 03-10-13 at 09:34 PM.
  Reply With Quote
03-11-13, 02:40 AM   #17
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Yes. The "=" isn't really important; only the "for ... do" parts. Any variables defined between "for" and "do" are automatically local to the scope that starts with "for" and ends with the corresponding "end".

Variables used in other types of loops are not automatically local, eg:

Code:
local i = 1
while i < 10 do
    -- i exists here
    i = i + 1
end
-- and it still exists here
Code:
local i = 1
repeat
    -- i exists here
    i = i + 1
until i == 10
-- and still here
Also:

Code:
local i = 1
for i = 1, 3 do
    print(i)
end
print(i)
... will print 1 2 3 1, since the "i" inside the loop is the one declared as part of the "for" statement, while the "i" that exists after the for-loop's "end" statement is the one explicitly defined as "local i" before the loop.
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
03-11-13, 03:22 AM   #18
Rilgamon
Premium Member
 
Rilgamon's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 822
I memorized it this way: "do ... end" is a new scope. for, while, etc expand on it.
If your code have variables or functions that run only once during runtime you can put them in a new scope so that they can be removed from memory during the "normal" runtime of your code.

Lua Code:
  1. local setting
  2. do
  3.     local result = false
  4.     local function check()
  5.         return true
  6.     end
  7.     result = check()
  8.     setting = result
  9. end
__________________
The cataclysm broke the world ... and the pandas could not fix it!
  Reply With Quote
03-11-13, 03:24 PM   #19
shalom69
A Fallenroot Satyr
Join Date: Feb 2013
Posts: 22
awesome. thanks for those posts guys
  Reply With Quote

WoWInterface » Developer Discussions » General Authoring Discussion » question about key bindings XML LUA

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