WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   Lua/XML Help (https://www.wowinterface.com/forums/forumdisplay.php?f=16)
-   -   Create and move frame using LUA only (https://www.wowinterface.com/forums/showthread.php?t=15125)

DOAshn 03-02-08 07:26 AM

Create and move frame using LUA only
 
Oops, put this in the wrong section. A mod please move it.

Im trying to make a frame using lua only but my SetScript functions isn't working.

I've tried numerous ways found on wowwiki, these forums and even googleing. I must have missed something important that you have to do in order to make it work.

This is my current test code:

Code:

function Benque_OnEvent(self, event)
        if event == "OnMouseUp" then
                self:StartMoving();
                message("OnMouseUp Event");
        else
                self:StopMovingOrSizing();
                message("Other event");
        end
end

local frame = CreateFrame("Frame", "MyFrame123", UIParent);
frame:SetBackdrop({bgFile="Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile="Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true,
        tileSize = 16,
        edgeSize = 16,
        insets = {left = 4, right = 4, top = 4, bottom = 4}});

frame:SetBackdropColor(0,0,0,1);

--frame:SetFrameStrata(0);
frame:SetFrameLevel(0);
frame:SetWidth(250);
frame:SetHeight(250);
frame:SetPoint("TOPLEFT", "UIParent", "TOPLEFT");

frame:RegisterForDrag("LeftButton");
--frame:SetUserPlaced( true );
frame:SetMovable(true);
--frame:EnableMouse(true);
--frame:RegisterEvent("ADDON_LOADED");

frame:SetScript("OnEvent", Benque_OnEvent(frame, ...) );
frame:RegisterEvent("OnMouseDown");
frame:RegisterEvent("OnMouseUp");
--frame:SetScript("OnClick", Benque_OnLoad(frame) );
--frame:SetScript("OnDragStart", function(self) self:StartMoving() end);
--frame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end);

frame:Show();

The only thing that happens is that the OnEvent script is only triggered once at startup. No other events is passed.

What have i missed?

Slakah 03-02-08 10:59 AM

"OnMouseUp" and "OnMouseDown" arn't events their handlers and should be used as follows.


Code:

local function onmousedownfunc(self, button)
    --do stuff here
end
<frame>:SetScript("OnMouseDown", onmousedownfunc)


DOAshn 03-02-08 05:36 PM

Quote:

Originally Posted by Slakah (Post 84414)
"OnMouseUp" and "OnMouseDown" arn't events their handlers and should be used as follows.


Code:

local function onmousedownfunc(self, button)
    --do stuff here
end
<frame>:SetScript("OnMouseDown", onmousedownfunc)


K, i tested that like this.

Code:

local function Benque_OnMouseDown(self, button)
    self:StartMoving();
    message("test");
end

...
frame:SetScript("OnMouseDown", Benque_OnMouseDown);

But nothing happens, not even the message is displayed.
gah!

Shirik 03-03-08 12:22 AM

This is wrong:

Code:

frame:SetScript("OnEvent", Benque_OnEvent(frame, ...) );
And should be
Code:

frame:SetScript("OnEvent", Benque_OnEvent);
The same applies to your commented-out on load handler.

Layrajha 03-03-08 02:12 AM

Quote:

Originally Posted by Shirik (Post 84460)
This is wrong:

Code:

frame:SetScript("OnEvent", Benque_OnEvent(frame, ...) );
And should be
Code:

frame:SetScript("OnEvent", Benque_OnEvent);
The same applies to your commented-out on load handler.

I still have a question about the handlers (mostly the OnUpdate and OnEvent ones) that have global variables as parameters (for instance, the "arg1" one). The question is: should I use function() myhandler(arg1) end instead of myhandler, in the SetScript, in order to copy arg1 in a function-scoped variable to prevent concurrent threads from modifying what I'm working on, or will the handler be executed before any modification is applied to my arg1 variable (basically, are there concurrency problems ^^)? And even if no change is applied, is there another reason, or basically, what are the pros and cons of both codes?

Shirik 03-03-08 02:58 AM

Quote:

Originally Posted by Layrajha (Post 84463)
I still have a question about the handlers (mostly the OnUpdate and OnEvent ones) that have global variables as parameters (for instance, the "arg1" one). The question is: should I use function() myhandler(arg1) end instead of myhandler, in the SetScript, in order to copy arg1 in a function-scoped variable to prevent concurrent threads from modifying what I'm working on, or will the handler be executed before any modification is applied to my arg1 variable (basically, are there concurrency problems ^^)? And even if no change is applied, is there another reason, or basically, what are the pros and cons of both codes?

Your handler is run atomically - there is no chance anything can modify it between the time it starts and the time it finishes -- however you could modify it (but should not!).

Note that no two Lua scripts are ever running at the same time. While other threads might be running, the entire UI runs in one thread. This is why performance is so critical to framerate.

Similarly, you should not have had a problem even concurrency existed in the UI, because you should NOT be using the global variables. You should be using locals:

Code:

-- Don't do this:
frame:SetScript("OnEvent", function()
  -- Do something with arg1 and event
  -- NOTE: These are GLOBALS and cause performance issues
end);


-----------------------------------------
-- This is better:
frame:SetScript("OnEvent", function(self, event, ...)
  local arg1 = select(1, ...)  -- (Note this select is unnecessary, but for any arg that is not arg1, this would be required)
  -- Note: Since these are function parameters, these are LOCALS and are much better to use
end);


------------------------------------------
-- This is equivalently good:
local function myEventHandler(self, event, ...)
  local arg1 = select(1, ...)
  -- Do something with event and arg1
end;
frame:SetScript("OnEvent", myEventHandler);
-- Note: The script will be passed the parameters as in the above example as is to be expected (and will be local)

Hope this helps,
-- Shirik

Tristanian 03-03-08 03:04 AM

Quote:

Originally Posted by DOAshn (Post 84438)
K, i tested that like this.

Code:

local function Benque_OnMouseDown(self, button)
    self:StartMoving();
    message("test");
end

...
frame:SetScript("OnMouseDown", Benque_OnMouseDown);

But nothing happens, not even the message is displayed.
gah!

Obviously, since no mouse button argument is passed or checked against. This should work however.

Code:

local function Benque_OnMouseDown(button)
  if button == "LeftButton" then
    MyFrame123:StartMoving();
    message("test");
  end
end

...
frame:SetScript("OnMouseDown", Benque_OnMouseDown(arg1));

You should be able to pass the frame in the function as well, as a secondary argument, if you really want to. You may also need to RegisterForClicks in your frame.

Shirik 03-03-08 03:13 AM

Quote:

Originally Posted by Tristanian (Post 84469)
You should be able to pass the frame in the function as well, as a secondary argument, if you really want to. You may also need to RegisterForClicks in your frame.

No, you cannot, as I have already stated. You can't pass anything at the time of registering the script. You are not passing a closure if you call the function (as is being done there), you are passing nil (which, in turn, sets the script to nil, and won't run).

Tristanian 03-03-08 03:32 AM

Quote:

Originally Posted by Shirik (Post 84472)
No, you cannot, as I have already stated. You can't pass anything at the time of registering the script. You are not passing a closure if you call the function (as is being done there), you are passing nil (which, in turn, sets the script to nil, and won't run).

Quite right, I stand corrected.

Shirik 03-03-08 03:44 AM

Quote:

Originally Posted by Tristanian (Post 84475)
Quite right, I stand corrected.

Actually I apologize as I didn't really explain my comment earlier so it may have lead to confusion:

This is wrong:
Code:

function myHandler(arg1)
  -- Do stuff
end

frame:SetScript("OnEvent", myHandler(arg1));

Problem is that you are calling myHandler with the parameter arg1 when you set up the script... NOT during the event. The return value from myHandler(arg1) is what ends up going into the script (which, in this case, is nil). You don't intend to do this. What you intended to do was this:

Code:

function myHandler(arg1)
  -- Do stuff
end

frame:SetScript("OnEvent", myHandler);

This passes the function itself to the script, which is then called whenever the event occurs. However this has one flaw -- arg1 is not the first parameter. So you have two options... the first, obvious one, is to make myHandler accept all the parameters:

Code:

function myHandler(self, event, arg1, arg2, arg3)
Or alternatively, as many like to do, including myself,
Code:

function myHandler(self, event, ...)
The second, more subtle, more memory consuming, but all-around easier-in-the-end option is to create a closure. This can have several benefits and uses, but in general should only be used if you're going to call SetScript once and only one (because each time it is called, you create a new closure, resulting in more garbage being generated):

Code:

function myHandler(arg1)
  -- Do stuff with arg1
end

frame:SetScript("OnEvent", function(self, event, arg1) myHandler(arg1); end);

"... But wait Shirik! You just said we can't call myHandler() there!" Yes, this is true, but we're not actually calling it there. If you look closer, we're making a new function (which has no name, and if seen in a stack traceback will just appear as a '?' where you would normally see the function name), and THAT function calls myHandler() when IT ITSELF is called. However, we are then taking that function and passing it to SetScript as before. It may become more clear what this means if it is expanded slightly (though this gives the function a name:)

Code:

function myHandler(arg1)
  -- do stuff
end

local function someUnnamedFunction(self, event, arg1)
  myHandler(arg1);
end

frame:SetScript("OnEvent", someUnnamedFunction);

Again, this may seem like more work, but can have its uses. If it's a bit confusing, I suggest you take a look at Programming in Lua on the section regarding closures.

Regards,
-- Shirik

Tristanian 03-03-08 04:02 AM

A very good explanation Shirik, I don't use Setscript that much so I was under the impression that args are actually passed when the event occurs.

I very much rather handle script methods in xml files. I was always wondering if there is any significant difference when you pass arguments there. I'm guessing that there is since method registration is using a different mechanism perhaps ?

Layrajha 03-03-08 04:52 AM

Thanks for your answer to my question, Shirik, it was very clear. I had guesses about the fact that the UI was run in a single thread, but I had difficulties thinking of a test that would make it totally sure :) And yes, obviously, using several calls to the same global variable is bad: when I talked about using a global variable as a parameter, I just meant using its value, but store it wherever you want (the stack being the valid answer, I guess).
Thank you again.

DOAshn 03-03-08 05:04 AM

Yay, i got it to work now!

In order for it to work i had to create a Button, not a frame. I guess frames aren't clickable? Because Frames don't have the function RegisterForClicks().

Code:

local function Benque_OnMouseDown(self, button)
  self:StartMoving();
end

...

myFrame = CreateFrame("Button", "MyFrame" ...);
...
myFrame:SetScript("OnMouseDown", Benque_OnMouseDown);
...

Thanks for the help!

Slakah 03-03-08 06:49 AM

Quote:

Originally Posted by DOAshn (Post 84481)
Yay, i got it to work now!

In order for it to work i had to create a Button, not a frame. I guess frames aren't clickable? Because Frames don't have the function RegisterForClicks().

Code:

local function Benque_OnMouseDown(self, button)
  self:StartMoving();
end

...

myFrame = CreateFrame("Button", "MyFrame" ...);
...
myFrame:SetScript("OnMouseDown", Benque_OnMouseDown);
...

Thanks for the help!

To make a frame move use the handlers "OnDragStart" and "OnDragStop", I think you might have to use
<frame>:SetMovable(true)
<frame>:EnableMouse(true)
<frame>:RegisterForDrag("LeftButton")

as well.


All times are GMT -6. The time now is 06:56 PM.

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