Thread Tools Display Modes
08-21-14, 08:55 PM   #1
RyinntheDK
A Murloc Raider
 
RyinntheDK's Avatar
Join Date: Aug 2014
Posts: 6
LUA CreateFrame, then RemoveFrame??

Hello all!

I'm about 3 hours in to my first attempt at creating (err, finding one and building on to it) an addon. I found an addon that I thought was a good start, but lacked a lot of what I desired, so why not learn LUA and write my own eh?

What I've had a tough time doing is creating a toggle effect when using slash commands + Msg.

As you can see in the code, if Msg == "on" then I CreateFrame. If Msg == "off", essentially I would like to just undo the CreateFrame that I just created. I can't figure out how to do this without just applying a ReloadUI (as you see commented out), which, while effective, rather annoying.

Any help would be much appreciated!

Lua Code:
  1. --Tribute to Kip who's code I copied and altered... Consider this Kudos for your 'Bootstrap'
  2.  
  3. --define global vars (saved between sessions)
  4. Enabled = true
  5.  
  6. --define local module vars and defaults
  7. local AddonVersion = GetAddOnMetadata("RyinnsCursorFinder", "Version")
  8.  
  9. local function print(msg)
  10.     DEFAULT_CHAT_FRAME:AddMessage("RyinnsCursorFinder: " .. tostring(msg))
  11. end
  12.  
  13. function Ryinn_Command(Msg,Editbox)
  14.     if Msg then Msg=strlower(Msg); end
  15.     if Msg == "help" then
  16.         print("v"..AddonVersion)
  17.         print("Use /rcf + on/off to Enable/Disable")
  18.         print("Example: /rcf on")
  19.     elseif Msg == "on" then
  20.                 local frame = CreateFrame("Frame", nil, UIParent);
  21.                 frame:SetFrameStrata("TOOLTIP");
  22.  
  23.                 local texture = frame:CreateTexture();
  24.                 texture:SetTexture([[Interface\Cooldown\ping4]]);
  25.                 texture:SetBlendMode("ADD");
  26.                 texture:SetAlpha(0.5);
  27.  
  28.                 local x = 0;
  29.                 local y = 0;
  30.                 local speed = 0;
  31.                 local function OnUpdate(_, elapsed)
  32.                   local dX = x;
  33.                   local dY = y;
  34.                   x, y = GetCursorPosition();
  35.                   dX = x - dX;
  36.                   dY = y - dY;
  37.                   local weight = 2048 ^ -elapsed;
  38.                   speed = math.min(weight * speed + (1 - weight) * math.sqrt(dX * dX + dY * dY) / elapsed, 768);
  39.                   local size = speed / 6 - 16;
  40.                   if (size > 0) then
  41.                     local scale = UIParent:GetEffectiveScale();
  42.                     texture:SetHeight(size);
  43.                     texture:SetWidth(size);
  44.                     texture:SetPoint("CENTER", UIParent, "BOTTOMLEFT", (x + 0.5 * dX) / scale, (y + 0.5 * dY) / scale);
  45.                     texture:Show();
  46.                   else
  47.                     texture:Hide();
  48.                   end
  49.                 end
  50.                 frame:SetScript("OnUpdate", OnUpdate);
  51.                 print("Ryinn's Flash Cursor is Now Enabled. Use /ryn off to disable")
  52.     elseif Msg == "off" then
  53.     Frame1:SetScript("OnShow",nil);
  54.     --StaticPopup_Show("ReloadPop")
  55.     print("test")
  56.     else
  57.         Ryinn_Command("help")
  58.     end
  59. end
  60.  
  61. --Create an anonymous blank frame
  62. local Ryinn = CreateFrame("Frame")
  63. Ryinn:RegisterEvent("PLAYER_REGEN_ENABLED")
  64. Ryinn:RegisterEvent("PLAYER_REGEN_DISABLED")
  65. Ryinn:SetScript("OnEvent", Ryinn_OnEvent)
  66.  
  67. --setup slash command feature
  68. SlashCmdList["Ryinn"]=Ryinn_Command
  69. SLASH_Ryinn1="/rcf",
  70. Ryinn_Command("help")
  71.  
  72. --Setup our Pop Up  Warning for Reload UI
  73. --StaticPopupDialogs["ReloadPop"] = {
  74. --text = "In order to remove the cursor texture, you need to reload your UI.  Is that okay?",
  75. --button1 = "Yes",
  76. --button2 = "No",
  77. --OnAccept = function() ReloadUI() end,
  78. --timeout = 0,
  79. --sound = "RaidWarning",
  80. --whileDead = true,
  81. --hideOnEscape = true,
  82. --preferredIndex = 3,  -- avoid some UI taint, see [url]http://www.wowace.com/announcements/how-to-avoid-some-ui-taint/[/url]
  83. --}
  Reply With Quote
08-21-14, 09:07 PM   #2
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
There is no way to remove a frame from memory once created. What you can do is create the frame when you load the addon and hide it immediately. When you use the slash command, you can toggle the visibility of the frame in order to obtain the effect you want.



Globals are also not saved between sessions. You can however register a specific global to be saved for your addon if defined in its TOC file. For example, the following tag defines RYINNCURSOR_Enabled to be saved between sessions. You can retrieve the data in these variables after ADDON_LOADED fires for your addon.
Code:
## SavedVariables: RYINNCURSOR_Enabled
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 08-21-14 at 09:49 PM.
  Reply With Quote
08-21-14, 09:08 PM   #3
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
There is no RemoveFrame function, nor any other way to destroy a frame once it's been created. Instead, you should create the frame(s) only once, and then just :Hide() them when not in use. You can :Show() them again later. If you need to change what's displayed on the frame, just change things as necessary, using methods like :SetText() on font strings, :SetTexture() and :SetVertexColor() on textures, :SetSize() on frames, etc.

Also, "Enabled" is an absolutely unacceptable name for a global variable. There is an extremely high chance of some other addon (or even the default UI) leaking a variable with that name into the global namespace, overwriting your variable or vice versa. Global variables and object names should always follow two guidelines:

1. The name is unique, and unlikely to collide with the name used by any other addon or the default UI. A simple way to ensure uniqueness is to use your addon name as a prefix, eg. "MyAddon_Enabled".

2. The name clearly identifies the addon that owns it. This way, if it shows up in an error message, the user knows who to tell about it. Using your addon name as a prefix also solves this problem.
__________________
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
08-22-14, 04:23 AM   #4
Duugu
Premium Member
 
Duugu's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 851
An easy way to do what they sayed would be to add a name to you frame and to reference it later on. Like
Lua Code:
  1. local frame = _G["RyinnMainFrame"] or CreateFrame("Frame", "RyinnMainFrame", UIParent);
  Reply With Quote
08-22-14, 07:57 AM   #5
RyinntheDK
A Murloc Raider
 
RyinntheDK's Avatar
Join Date: Aug 2014
Posts: 6
I mentioned that I'm a complete beginner here - and was wondering if someone can help me with the code, perhaps help me re-write it properly for what I'm trying to achieve. Would someone be willing to help me with that?

While I completely understand what you're saying about first Creating the frame, and hiding it immediately, then show or hide works as the toggle, I'm having a difficulty putting it all together.

I really appreciate any help!
  Reply With Quote
08-22-14, 09:10 AM   #6
Choonstertwo
A Chromatic Dragonspawn
 
Choonstertwo's Avatar
AddOn Author - Click to view addons
Join Date: Jan 2011
Posts: 194
I've cleaned up the code a little and added a few comments (all prefixed with my name) explaining the changes and questioning why certain things are the way they are.

Lua Code:
  1. --Tribute to Kip who's code I copied and altered... Consider this Kudos for your 'Bootstrap'
  2.  
  3. --define global vars (saved between sessions)
  4. RyinnsCursorFinder_Enabled = true -- Choonster: I gave this global variable your Addon's name as a prefix so it's unique
  5.  
  6. --define local module vars and defaults
  7. local AddonVersion = GetAddOnMetadata("RyinnsCursorFinder", "Version")
  8.  
  9. local function print(msg)
  10.     DEFAULT_CHAT_FRAME:AddMessage("RyinnsCursorFinder: " .. tostring(msg))
  11. end
  12.  
  13. -- Choonster: I've moved the frame creation into the main body of the script. This is only executed once.
  14. -- I've also given it a name. I'd recommend giving any visible frame a name with your AddOn's name in it so the user knows which AddOn adds it.
  15. local frame = CreateFrame("Frame", "RyinnsCursorFinder_MainFrame", UIParent);
  16. frame:SetFrameStrata("TOOLTIP");
  17. frame:Hide() -- Choonster: Hide the frame initially
  18.  
  19. local texture = frame:CreateTexture();
  20. texture:SetTexture([[Interface\Cooldown\ping4]]);
  21. texture:SetBlendMode("ADD");
  22. texture:SetAlpha(0.5);
  23.  
  24. -- Choonster: Do these variables need to be declared outside of the OnUpdate function? Are they ever used by another function? Do they need to be saved between each call of the function?
  25. local x = 0;
  26. local y = 0;
  27. local speed = 0;
  28.  
  29. -- Choonster: I've passed the function directly to :SetScript here instead of declaring it first and then passing it in.
  30. -- If you're never going to call a function yourself (like most script handler functions), you don't have to give it a name.
  31. frame:SetScript("OnUpdate", function(_, elapsed)
  32.   local dX = x;
  33.   local dY = y;
  34.   x, y = GetCursorPosition();
  35.   dX = x - dX;
  36.   dY = y - dY;
  37.   local weight = 2048 ^ -elapsed;
  38.   speed = math.min(weight * speed + (1 - weight) * math.sqrt(dX * dX + dY * dY) / elapsed, 768);
  39.   local size = speed / 6 - 16;
  40.   if (size > 0) then
  41.     local scale = UIParent:GetEffectiveScale();
  42.     texture:SetHeight(size);
  43.     texture:SetWidth(size);
  44.     texture:SetPoint("CENTER", UIParent, "BOTTOMLEFT", (x + 0.5 * dX) / scale, (y + 0.5 * dY) / scale);
  45.     texture:Show();
  46.   else
  47.     texture:Hide();
  48.   end
  49. end);
  50.  
  51. -- Choonster: I've made this function local, it doesn't need to be global.
  52. local function Ryinn_Command(Msg,Editbox)
  53.     if Msg then Msg=strlower(Msg); end
  54.     if Msg == "help" then
  55.         print("v"..AddonVersion)
  56.         print("Use /rcf + on/off to Enable/Disable")
  57.         print("Example: /rcf on")
  58.     elseif Msg == "on" then
  59.         frame:Show() -- Choonster: Showing the frame makes it visible and allows its OnUpdate handler to fire.
  60.         print("Ryinn's Flash Cursor is Now Enabled. Use /ryn off to disable")
  61.     elseif Msg == "off" then
  62.         frame:Hide() -- Choonster: Hiding the frame makes it invisible and stops its OnUpdate handler from firing.
  63.         --StaticPopup_Show("ReloadPop")
  64.         print("test")
  65.     else
  66.         Ryinn_Command("help")
  67.     end
  68. end
  69.  
  70. --Create an anonymous blank frame
  71. local Ryinn = CreateFrame("Frame")
  72. Ryinn:RegisterEvent("PLAYER_REGEN_ENABLED")
  73. Ryinn:RegisterEvent("PLAYER_REGEN_DISABLED")
  74. Ryinn:SetScript("OnEvent", Ryinn_OnEvent)
  75.  
  76. --setup slash command feature
  77. SlashCmdList["Ryinn"]=Ryinn_Command
  78. SLASH_Ryinn1="/rcf",
  79. Ryinn_Command("help")
  80.  
  81. --Setup our Pop Up  Warning for Reload UI
  82. --StaticPopupDialogs["ReloadPop"] = {
  83. --text = "In order to remove the cursor texture, you need to reload your UI.  Is that okay?",
  84. --button1 = "Yes",
  85. --button2 = "No",
  86. --OnAccept = function() ReloadUI() end,
  87. --timeout = 0,
  88. --sound = "RaidWarning",
  89. --whileDead = true,
  90. --hideOnEscape = true,
  91. --preferredIndex = 3,  -- avoid some UI taint, see [url]http://www.wowace.com/announcements/how-to-avoid-some-ui-taint/[/url]
  92. --}

It's not perfect and I can't guarantee it will work, but it probably will.

If you're going to be saving whether or not the AddOn is enabled, you should use the ADDON_LOADED event to set the default enabled state and then show/hide your frame based on the enabled state.

Last edited by Choonstertwo : 08-22-14 at 09:28 AM.
  Reply With Quote
08-22-14, 09:32 AM   #7
RyinntheDK
A Murloc Raider
 
RyinntheDK's Avatar
Join Date: Aug 2014
Posts: 6
Choonster, thank you! This is great!

And I also very much appreciate the comments, which help me learn.

These aren't going to be used elsewhere so how you have it is fine!

local x = 0;
local y = 0;
local speed = 0;
Now to enhance the experience of the user, how would i pass show/hide on just /rcf, like a toggle?
__________________
There is no try. Only do or don't do. - Yoda
  Reply With Quote
08-22-14, 09:50 AM   #8
Choonstertwo
A Chromatic Dragonspawn
 
Choonstertwo's Avatar
AddOn Author - Click to view addons
Join Date: Jan 2011
Posts: 194
Use this instead of the existing Ryinn_Command function:
Lua Code:
  1. local function Ryinn_Command(msg, editBox)
  2.     -- String library functions can be called as methods on strings.
  3.     msg = msg:trim()
  4.    
  5.     if msg == "" then
  6.         RyinnsCursorFinder_Enabled = not RyinnsCursorFinder_Enabled
  7.         frame:SetShown(RyinnsCursorFinder_Enabled) -- `frame:SetShown(val)` is equivalent to `if val then frame:Show() else frame:Hide() end`
  8.        
  9.         print(("Ryinn's Flash Cursor is now %s. Use /rvm to toggle."):format(RyinnsCursorFinder_Enabled and "Enabled" or "Disabled"))
  10.     else
  11.         print("v" .. AddonVersion)
  12.         print("Use /rcf to enable/disable")
  13.     end
  14. end

After looking at the code more closely, I realised that the x, y and speed variables do need to be declared outside of the OnUpdate function as the values from the previous call are used to calculate the new value of speed each time.

Last edited by Choonstertwo : 08-22-14 at 10:09 AM.
  Reply With Quote
08-22-14, 09:50 AM   #9
Duugu
Premium Member
 
Duugu's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 851
Originally Posted by RyinntheDK View Post
Now to enhance the experience of the user, how would i pass show/hide on just /rcf, like a toggle?
If the user adds no parameters to the slash command the Msg variable should be Nil. So, this should work:

[e] As I read Choonsters reply I'm actually unsure if the value will be Nil or an empty string. *shrug*


Lua Code:
  1. local function Ryinn_Command(Msg) --"Editbox" was unused
  2.     if Msg then Msg=strlower(Msg); end
  3.     if Msg == "help" then
  4.         print("v"..AddonVersion)
  5.         print("Use /rcf + on/off to Enable/Disable")
  6.         print("Example: /rcf on")
  7.     elseif Msg == "on" then
  8.         frame:Show() -- Choonster: Showing the frame makes it visible and allows its OnUpdate handler to fire.
  9.         print("Ryinn's Flash Cursor is Now Enabled. Use /ryn off to disable")
  10.     elseif Msg == "off" then
  11.         frame:Hide() -- Choonster: Hiding the frame makes it invisible and stops its OnUpdate handler from firing.
  12.         --StaticPopup_Show("ReloadPop")
  13.         print("test")
  14.     elseif not Msg
  15.         if frame:IsVisible() then
  16.                 frame:Hide()   
  17.         else
  18.                 frame:Show()
  19.         end
  20.     else
  21.         Ryinn_Command("help")  
  22.     end
  23. end

Last edited by Duugu : 08-22-14 at 09:53 AM.
  Reply With Quote
08-22-14, 10:06 AM   #10
Choonstertwo
A Chromatic Dragonspawn
 
Choonstertwo's Avatar
AddOn Author - Click to view addons
Join Date: Jan 2011
Posts: 194
Originally Posted by Duugu View Post
If the user adds no parameters to the slash command the Msg variable should be Nil. So, this should work:

[e] As I read Choonsters reply I'm actually unsure if the value will be Nil or an empty string. *shrug*
Looking at the UI code, the first argument of the slash command function should always be a string:
https://github.com/tekkub/wow-ui-sou...rame.lua#L4315

Notice that the msg variable passed as the first argument is always assigned to a string value (either a literal or the return value of strsub/strtrim).
  Reply With Quote
08-22-14, 10:19 AM   #11
RyinntheDK
A Murloc Raider
 
RyinntheDK's Avatar
Join Date: Aug 2014
Posts: 6
I tested this in game and so far so good!

I did make one tweak though for functionality. When initially logging in, it would hide the frame, as expected.

On first MSG command, it would skip to disable

On second MSG command, it would enable... so I changed the var to false, and now it works perfectly!

Thanks for everyone's help. Does anyone recommend a good place to learn LUA, like tutorials?


Lua Code:
  1. RyinnsCursorFinder_Enabled = false
__________________
There is no try. Only do or don't do. - Yoda
  Reply With Quote
08-22-14, 10:35 AM   #12
Choonstertwo
A Chromatic Dragonspawn
 
Choonstertwo's Avatar
AddOn Author - Click to view addons
Join Date: Jan 2011
Posts: 194
Originally Posted by RyinntheDK View Post
Does anyone recommend a good place to learn LUA, like tutorials?
For the language itself, you can try some of the stuff on this page, though I'm not sure how up to date it is.

Keep in mind that WoW uses Lua 5.1, the most recent release version of Lua is 5.2. The two are mostly similar, but there are some major differences like the concept of function environments (getfenv/setfenv in 5.1 vs _ENV in 5.2). Function environments are a fairly advanced feature that you're not likely to use very often, though.

The official reference manual is quite useful, but it's not a tutorial.

As for WoW-specific stuff, there's a few tutorials here. There's also this book, but I haven't bought it myself so I can't say how useful it is.

There's also a bunch of handy links in this sticky.

When I was learning Lua, I mostly just learned the various features of the language as I needed them by a combination of trial and error, reading existing code and reading the documentation (the official reference manual or the WoW API Reference as appropriate).
  Reply With Quote
08-22-14, 12:24 PM   #13
RyinntheDK
A Murloc Raider
 
RyinntheDK's Avatar
Join Date: Aug 2014
Posts: 6
Thanks Choon! It'll definitely take some getting used to. First time I've ever really looked at LUA, seems pretty basic for the most part coming off of C++ knowledge, but still rusty.

I'll take a look at the links and play around.

Again, I really appreciate your help!

Cheers~
__________________
There is no try. Only do or don't do. - Yoda
  Reply With Quote
08-22-14, 12:34 PM   #14
Choonstertwo
A Chromatic Dragonspawn
 
Choonstertwo's Avatar
AddOn Author - Click to view addons
Join Date: Jan 2011
Posts: 194
One last thing: The language is actually called Lua, not LUA. It's the Portuguese word for "moon", not an acronym.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » LUA CreateFrame, then RemoveFrame??

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