Thread Tools Display Modes
03-02-08, 07:26 AM   #1
DOAshn
A Defias Bandit
Join Date: Mar 2008
Posts: 3
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?

Last edited by DOAshn : 03-02-08 at 07:37 AM.
  Reply With Quote
03-02-08, 10:59 AM   #2
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
"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)
  Reply With Quote
03-02-08, 05:36 PM   #3
DOAshn
A Defias Bandit
Join Date: Mar 2008
Posts: 3
Originally Posted by Slakah View Post
"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!
  Reply With Quote
03-03-08, 12:22 AM   #4
Shirik
Blasphemer!
Premium Member
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2007
Posts: 818
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.
__________________
たしかにひとつのじだいがおわるのお
ぼくはこのめでみたよ
だけどつぎがじぶんおばんだってことわ
しりたくなかったんだ
It's my turn next.

Shakespeare liked regexes too!
/(bb|[^b]{2})/
  Reply With Quote
03-03-08, 02:12 AM   #5
Layrajha
A Frostmaul Preserver
 
Layrajha's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 275
Originally Posted by Shirik View Post
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?

Last edited by Layrajha : 03-03-08 at 02:15 AM.
  Reply With Quote
03-03-08, 02:58 AM   #6
Shirik
Blasphemer!
Premium Member
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2007
Posts: 818
Originally Posted by Layrajha View Post
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
__________________
たしかにひとつのじだいがおわるのお
ぼくはこのめでみたよ
だけどつぎがじぶんおばんだってことわ
しりたくなかったんだ
It's my turn next.

Shakespeare liked regexes too!
/(bb|[^b]{2})/
  Reply With Quote
03-03-08, 03:04 AM   #7
Tristanian
Andúril
Premium Member
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 279
Originally Posted by DOAshn View Post
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.
  Reply With Quote
03-03-08, 03:13 AM   #8
Shirik
Blasphemer!
Premium Member
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2007
Posts: 818
Originally Posted by Tristanian View Post
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).
__________________
たしかにひとつのじだいがおわるのお
ぼくはこのめでみたよ
だけどつぎがじぶんおばんだってことわ
しりたくなかったんだ
It's my turn next.

Shakespeare liked regexes too!
/(bb|[^b]{2})/
  Reply With Quote
03-03-08, 03:32 AM   #9
Tristanian
Andúril
Premium Member
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 279
Originally Posted by Shirik View Post
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.
  Reply With Quote
03-03-08, 03:44 AM   #10
Shirik
Blasphemer!
Premium Member
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2007
Posts: 818
Originally Posted by Tristanian View Post
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
__________________
たしかにひとつのじだいがおわるのお
ぼくはこのめでみたよ
だけどつぎがじぶんおばんだってことわ
しりたくなかったんだ
It's my turn next.

Shakespeare liked regexes too!
/(bb|[^b]{2})/
  Reply With Quote
03-03-08, 04:02 AM   #11
Tristanian
Andúril
Premium Member
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 279
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 ?
  Reply With Quote
03-03-08, 04:52 AM   #12
Layrajha
A Frostmaul Preserver
 
Layrajha's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 275
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.
  Reply With Quote
03-03-08, 05:04 AM   #13
DOAshn
A Defias Bandit
Join Date: Mar 2008
Posts: 3
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!
  Reply With Quote
03-03-08, 06:49 AM   #14
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
Originally Posted by DOAshn View Post
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.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Create and move frame using LUA only

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