WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   Lua/XML Help (https://www.wowinterface.com/forums/forumdisplay.php?f=16)
-   -   Stack overflow (https://www.wowinterface.com/forums/showthread.php?t=59756)

Benalish 01-09-24 05:35 PM

Stack overflow
 
May I ask why this code goes into stack overflow

Lua Code:
  1. local function SetWidgetScript(frame,method)
  2.     if frame:IsShown() then
  3.         for i = 1, #frame.buttons do
  4.             frame.buttons[i]:HookScript("OnMouseDown", method)
  5.         end
  6.     else
  7.         frame:SetScript(
  8.             "OnShow", --when "OnShow" is fired, IsShown() become true
  9.                 SetWidgetScript(frame,method)
  10.         )
  11.     end
  12. end

While it works fine by simply wrapping myfunc() inside the function environment after "OnShow"?

Lua Code:
  1. local function SetWidgetScript(frame,method)
  2.     if frame:IsShown() then
  3.         for i = 1, #frame.buttons do
  4.             frame.buttons[i]:HookScript("OnMouseDown", method)
  5.         end
  6.     else
  7.         frame:SetScript(
  8.             "OnShow", --when "OnShow" is fired, IsShown() become true
  9.             function()
  10.                 SetWidgetScript(frame,method)
  11.             end
  12.         )
  13.     end
  14. end

SDPhantom 01-09-24 07:34 PM

The first example, you're calling yourself instead of registering a function to run as a script. What happens when you make a function call as an argument for another function is it runs the function call immediately and uses the return value(s) as the argument(s) for the second function. In this case, you're calling yourself (SetWidgetScript()) and :SetScript() is expecting to take any returns to use for the callback script. SetWidgetScript() doesn't actually return anything, so if it wasn't repeatedly calling itself, causing the stack overflow, it would end up sending nil to :SetScript().

The second example creates a dynamic function to call SetWidgetScript() with the arguments supplied as upvalues. Unlike the first example, you aren't calling it immediately as you are just giving it a function.



It's not exactly the way I'd design this function either way as trying to call it to register multiple callbacks on the same frame will cause the previous OnShow registrations to be lost. Also the OnShow script never unregisters itself, so it'll just keep calling itself whenever it gets fired, which will keep piling :HookScript() calls on top of each other.

Mouse scripts require a "visible" frame to fire, so there's no need to delay hooking until it's shown anyway.

Benalish 01-10-24 07:29 AM

The original code was

Lua Code:
  1. local function SetWidgetScript(frame,method)
  2.     if frame:IsShown() then
  3.         for i = 1, #frame.buttons do
  4.             frame.buttons[i]:HookScript("OnMouseDown", method)
  5.         end
  6.     else
  7.         frame:SetScript(
  8.             "OnShow", function()
  9.                  for i = 1, #frame.buttons do
  10.                      frame.buttons[i]:HookScript("OnMouseDown", method)
  11.                  end
  12.             end
  13.         )
  14.     end
  15. end

I changed the method in the "OnShow" script just to not repeat the for i=1...end part twice.
There is probably a better way to write a DRY script.

SDPhantom 01-11-24 12:51 AM

In all honestly, I rather use buttons than generic frames to receive clicks. As I said earlier, mouse events require the frame to be "visible" already to fire, they shouldn't if they're hidden. This is just over-engineered.

It should be this simple.
Lua Code:
  1. local function SetWidgetScript(frame,method)
  2.     for _,button in ipairs(frame.buttons) do
  3.         button:HookScript("OnMouseDown",method);
  4.     end
  5. end


All times are GMT -6. The time now is 05:07 AM.

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