WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   Lua/XML Help (https://www.wowinterface.com/forums/forumdisplay.php?f=16)
-   -   LUA CreateFrame, then RemoveFrame?? (https://www.wowinterface.com/forums/showthread.php?t=49772)

RyinntheDK 08-21-14 08:55 PM

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. --}

SDPhantom 08-21-14 09:07 PM

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

Phanx 08-21-14 09:08 PM

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.

Duugu 08-22-14 04:23 AM

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);

RyinntheDK 08-22-14 07:57 AM

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!

Choonstertwo 08-22-14 09:10 AM

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.

RyinntheDK 08-22-14 09:32 AM

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!

Quote:

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?

Choonstertwo 08-22-14 09:50 AM

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.

Duugu 08-22-14 09:50 AM

Quote:

Originally Posted by RyinntheDK (Post 295817)
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

Choonstertwo 08-22-14 10:06 AM

Quote:

Originally Posted by Duugu (Post 295820)
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).

RyinntheDK 08-22-14 10:19 AM

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

Choonstertwo 08-22-14 10:35 AM

Quote:

Originally Posted by RyinntheDK (Post 295822)
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).

RyinntheDK 08-22-14 12:24 PM

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~

Choonstertwo 08-22-14 12:34 PM

One last thing: The language is actually called Lua, not LUA. It's the Portuguese word for "moon", not an acronym.


All times are GMT -6. The time now is 03:49 AM.

vBulletin © 2020, Jelsoft Enterprises Ltd
© 2004 - 2020 MMOUI