UFO:Alien Invasion

Development => Coding => Topic started by: xray on June 11, 2014, 09:25:51 pm

Title: lua design thoughts
Post by: xray on June 11, 2014, 09:25:51 pm
Hi,

I've created a fork of the master branch to begin replacing the current ui script language with lua.

Before getting into the coding, it's always good to have some thoughts about how the implementation should look like. That's what this post is for and I invite everyone to post ideas.

The goal is to bring lua in as a scripting language, replacing the current native script implementation found in the ui. I'll probably start with the ui-part since that's the area that'll benefit most.

One question I'd like to hear some views on is wether the implementation should be allowed to break the current scripts or that it should be additive. The first will lead to a full rewrite of the .ufo files, the second will allow a more evolutionary rewrite of .ufo files.

Another question is wether the lua language should be used for all the .ufo files, including those not dealing with the ui (e.g. campaigns.ufo, installations.ufo).

I'll be looking forward to any thoughts.

xray
Title: Re: lua design thoughts
Post by: Duke on June 11, 2014, 11:31:07 pm
Some devs (including me) + potential contributors are not familiar with LUA. So a good start would be to take some existing GUI elements from the scripts and show them here in comparison to what they could look like in LUA.

Considering the huge amount of scripted data we have, I vote for a migration. Probably on a per file basis.

Unlike the UI, the other scripts are mostly plain data definitions afaik. What would be the benefits of using LUA ? Again, comparing old and new examples would help me understand it better.

If we choose the 'additive' solution, it should be possible to automagically transform most of the .ufo files with some 'converter-tool'. That should speed up the migration process and avoid the errors inevitebly resulting from manual conversion.
Title: Re: lua design thoughts
Post by: Mattn on June 12, 2014, 04:07:26 pm
* we could get syntax checks in an editor
* we could do more things in the ui scripts that we can do now
* we don't have to maintain an own language
* lua is well known to the world (our script not)

but i would also vote for a small example window (maybe the main window) and just put the code in place. keep in mind that we are heavily replacing/reworking the ui in 2.6 - so just stick to one window until the code is in place, then we can merge the code (side by side with the old ui scripts) - then start the conversion window by window as aduke suggested.

I would first vote only for the ui.
Title: Re: lua design thoughts
Post by: xray on June 17, 2014, 03:39:02 pm
I've recreated a small part of the file main.ufo into lua to show what it could look like in lua (see code snippet below). Added some comments to explain a bit what is happening. Feel free to post any comments on it.

As for the actual implementation. I'm planning to use SWIG to create the binding between lua and the ufo main C/C++ code. The advantage of this is, that should lua evolve (chances are it will), the SWIG tool will handle this. Also, SWIG provides binding implementation for multiple dynamic languages, so should the need arise to actually switch to another script language, it will be a lot easier to do.

xray

Code: [Select]
--[[
MAIN MENU
--]]

-- definitions of named colors (not present yet, names are illustrative)
-- contains entry: COLOR_DARK_YELLOW = [0.0, 0.5, 0.0, 1.0]
-- contains entry: COLOR_WHITE = [1.0, 1.0, 1.0, 1.0]
require ("_colors.ufo")

-- create title control
local lb_title = Label ("_UFO: ALIEN INVASION", 500, 50, 262, 50, "f_title", [0.59, 0.78, 0.56, 1.0], ALIGN_CC)

-- create facebook button
local btn_facebook = Button ("_Facebook", 10, 720, 80, 20, COLOR_DARK_YELLOW)
btn_facebook.tooltip = "_Like us on facebook"
btn_facebook.padding = 0
btn_facebook.selectcolor = [1, 1, 1, 1]

function btn_facebook:onClick ()
-- todo
end

-- create twitter button
local btn_twitter = Button ("_Twitter", 100, 720, 70, 20, COLOR_DARK_YELLOW)
btn_twitter.tooltip = "_Follow us on twitter"
btn_twitter.padding = 0
btn_twitter.selectcolor = COLOR_WHITE

function btn_twitter:onClick ()
-- todo
end

-- import a button defined in _assets.ufo.lua (the .lua is added automatically)
require ("_assets.ufo")

-- create button CAMPAIGN
local btn_campain = MainMenuBtn ("_CAMPAIGN")

function btn_campaign:onClick ()
-- todo
end

-- create buttons panel
local pnl_buttons = Panel ()
pnl_buttons.pos = [312, 250]
pnl_buttons.size = [400, 400]
pnl_buttons.layout = LAYOUT_TOP_DOWN_FLOW
pnl_buttons.layoutMargin = 15

-- create the main window
local main = Window ()
main.background = "ui/main_bg"
main:add (title)
main:add (btn_facebook)
main:add (btn_twitter)
main:add (pnl_buttons)
Title: Re: lua design thoughts
Post by: Duke on June 17, 2014, 10:52:41 pm
I found one of the most painful tasks in UI developement is to count pixels to align UI elements properly.
What would it look like if I want to place the twitter and facebook buttons from your example
- at the same y
- give them the same height
- and place the 2nd +60x from the first
without magic numbers ?
Title: Re: lua design thoughts
Post by: Mattn on June 18, 2014, 10:19:15 am
well, that is something that could be scripted in lua with some own functions. we don't need c++ code for that.

anyway - great stuff, i like it.
Title: Re: lua design thoughts
Post by: xray on June 18, 2014, 01:02:00 pm
I found one of the most painful tasks in UI developement is to count pixels to align UI elements properly.
What would it look like if I want to place the twitter and facebook buttons from your example
- at the same y
- give them the same height
- and place the 2nd +60x from the first
without magic numbers ?

Actually, what you are requesting here is something called a layout manager. It is probably a good idea to implement such feature and it is possible to do this in lua, though I'm not really sure what the performance impact will be here. Anyway basically it comes down to exposing some metrics from the environment to the lua script (e.g. screen widht/height) as constants and writing functions that position window/panel/buttons. These functions can be written in a generic way so that they can be reused. Also, apart from positioning controls, it might also be interesting to look to seperating the styling of the gui from the structure.
Title: Re: lua design thoughts
Post by: shevegen on June 22, 2014, 09:02:04 pm
Don't forget the docs please!

Docs help other people understand.
Title: Re: lua design thoughts
Post by: xray on June 27, 2014, 10:31:24 pm
Work is progressing slowly, but at least it is moving forward. On the way I had to figure out a way to identify lua script from other (old style ;D) scripts. I guess the easiest solution would be to use the .lua file extension, however, this might bring in some unwanted side effects in external tools (e.g. makefiles, packaging tools). So I followed the linux method: adding a topline command identifying the file as a lua script. It looks like this.

Code: [Select]
--!usr/bin/lua

.. the rest of the lua code goes here ..

where the "--" being lua's line comment marker and "!usr/bin/lua" of course being an utterly arbitrary string.

xray
Title: Re: lua design thoughts
Post by: Mattn on June 28, 2014, 12:50:26 pm
imo we should just use a custom extension for this. lus for example - "l"ua-"u"fo-"s"cript or lua-ui-script. something like that.

This is due to the fact that we have directory listing functions that can filter by extension. And this is (in particular if you are using pk3) a lot faster than first open a file, check the "magic" and then discard or not discard it.

what do you think?
Title: Re: lua design thoughts
Post by: Mattn on June 28, 2014, 12:50:58 pm
oh... btw. did you fork ufoai on github? and if so, did you already push some stuff that i could check out?
Title: Re: lua design thoughts
Post by: xray on June 28, 2014, 06:44:28 pm
oh... btw. did you fork ufoai on github? and if so, did you already push some stuff that i could check out?

Yes, did forked the main branch. Visit on http://github.com/rxadmin/ufoai.

I did pushed a few changes to git however nothing substantional yet. I'll make a push when I can actually run some lua code.
imo we should just use a custom extension for this. lus for example - "l"ua-"u"fo-"s"cript or lua-ui-script. something like that.

This is due to the fact that we have directory listing functions that can filter by extension. And this is (in particular if you are using pk3) a lot faster than first open a file, check the "magic" and then discard or not discard it.

what do you think?

Actually, I'm not sure if the opening of the file is such a big issue. I'm investigating a way to precompile the lua script files before using them. So the actual work of opening the file is only at startup. Also, currently the opening of the file is actually embedded with the old script engine reading the headers of the .ufo files (see: FS_NextFileHeader).

xray
Title: Re: lua design thoughts
Post by: Mattn on June 28, 2014, 07:14:38 pm
yes, but only *.ufo files, not any other file with a different extension in the same dir.

doing it by file extension would also not lead to the need to add the magic header to each file - but ok, first let's see it in action, then we can continue to discuss this minor thing.
Title: Re: lua design thoughts
Post by: Mattn on June 29, 2014, 10:12:21 am
i've checked your branch a little bit - can you explain how this swig stuff will work for us? what is needed in order to generate the bindings and stuff like that?
Title: Re: lua design thoughts
Post by: xray on July 01, 2014, 10:51:19 am
i've checked your branch a little bit - can you explain how this swig stuff will work for us? what is needed in order to generate the bindings and stuff like that?

For a complete overview I suggest you check http://www.swig.org/. In brief it translates a interface (.i) file to a c code module that can be linked into the application and that contains the boilerplate code needed to link lua and C++. In the .i you specify what types, constants, functions, structures etc. are exposed to lua. The code module will then hold wrapper functions for all this stuff as well as some initializing functions.

So, as an example, suppose you want to expose the Com_Printf(..) function to lua. The interface file looks like this.
Code: [Select]
%module common

%{
/* the header file including the Com_Printf(..) function goes here */
#include "shared.h"
%}

/* expose common print function */
void Com_Printf(const char* fmt, ...);

The result is a c module containing the wrapper code. A small part is shown below. The advantage of using SWIG is that the writing the boilerplate code is rather tedious and error prone. Even though lua has a simple binding API, the actual process of binding functions, variables, structs etc. is a lot of work. SWIG takes care of this so we can concentrate on the fun thing: deciding what to expose.

Code: [Select]
#ifdef __cplusplus
extern "C" {
#endif
static int _wrap_Com_Printf(lua_State* L) {
  int SWIG_arg = 0;
  char *arg1 = (char *) 0 ;
  void *arg2 = 0 ;
 
  SWIG_check_num_args("Com_Printf",1,1)
  if(!SWIG_lua_isnilstring(L,1)) SWIG_fail_arg("Com_Printf",1,"char const *");
  arg1 = (char *)lua_tostring(L, 1);
  Com_Printf((char const *)arg1,arg2);
 
  return SWIG_arg;
 
  if(0) SWIG_fail;
 
fail:
  lua_error(L);
  return SWIG_arg;
}
Title: Re: lua design thoughts
Post by: Mattn on July 01, 2014, 08:13:47 pm
cool - awesome. I only wonder what the binary is we need for that and whether this binary works everywhere (tm) - or whether we have to compile it from somewhere on our own. (because we of course would not push the generated boilerplate code to the git repo but generate it as a pre-build step).
Title: Re: lua design thoughts
Post by: xray on July 02, 2014, 08:05:11 am
SWIG is open source and binaries are available for various platforms, among these various linux distro's and windows. Also the SWIG website makes mention that it is part of several linux distro's so if you're using linux as development platform it is probably already available.
Title: Re: lua design thoughts
Post by: Mattn on July 02, 2014, 10:13:19 am
so we should add that as pre-build-step to the codeblocks projects and put the binary into https://github.com/ufoai/ufoai.codeblocks

I will take care of the Makefile based-build system once we merge the pull request.
Title: Re: lua design thoughts
Post by: xray on July 02, 2014, 10:57:29 am
so we should add that as pre-build-step to the codeblocks projects and put the binary into https://github.com/ufoai/ufoai.codeblocks

I will take care of the Makefile based-build system once we merge the pull request.

Actually, I was quite pleased to see that the GNU compiler definition in CodeBlocks already recognizes the SWIG .i file and correctly launches the corresponding rule. Check out the compiler settings, then go to the advanced settings and in the file extension drop down, select the .i extension.
Title: Re: lua design thoughts
Post by: xray on July 04, 2014, 08:54:10 am
A new design thought. About renaming. The process of exposing ufo's C functions to lua will force you to use the C naming scheme inside lua. So typically you'll end up writing lua code like this:

Code: [Select]
function myLuaCode ()
    local msg = "hello, I'm a lua string"
    ufoai.Com_Printf("lua: %s\n", msg)
    ...
end

Now for the given example, the result might be okay. However this will also apply to structures (e.g. uiNode_t), enums, constants. Everything will have the original C naming. Now SWIG has the option to rename an identifier in the process. So there is an option to rewrite the above sample to:

Code: [Select]
function myLuaCode ()
    local msg = "hello, I'm a lua string"
    ufoai.print("lua: %s\n", msg)
    ...
end

This will make the code more in line with the lua syntax. As a side benefit, the code gets another layer isolating it from changes in the C API which is always a good idea. Of course the price to pay is having a more complex SWIG interface file, since each identifier being exposed has to be renamed.
Title: Re: lua design thoughts
Post by: xray on July 05, 2014, 09:14:07 am
Following up on the question:

i've checked your branch a little bit - can you explain how this swig stuff will work for us? what is needed in order to generate the bindings and stuff like that?

As it turns out, the default usage of SWIG is to provide a lua script access to a C program. This is not what I had in mind. Like the lua integration for the AI, I wanted to call from the C program into a specific lua function. More to the point: I want to call a lua function in a script that, using C functions, creates uiNode_t nodes and then returns this structure back to the C program. Technically this is called embedding and the hard part is because the returned value isn't a primitive value.

SWIG has some utility functions, however they where located in the generated .cpp file. That is, until I finally figured out how to get to these functions without actually copying the signature into my own header. Luckily the guys building SWIG have added an option to expose the utility functions using a generated header file. To do this, call once:

Code: [Select]
swig -c++ -lua -external-runtime swig_lua_runtime.h

The generated header file can now be used to convert values, wrapped by SWIG and passed on into lua, back to the original C values. I've attached a zip-file with a test project showing how this is done. The line above is attached as a prebuild step.

I'm posting the description to make sure it is documented for the future, since it took me really some time to figure this out.

xray
Title: Re: lua design thoughts
Post by: Mattn on July 05, 2014, 01:05:27 pm
great. i will keep that in mind when doing the makefile based buildsystem for that stuff.

about the renaming: yes, please hide the c api
Title: Re: lua design thoughts
Post by: Mattn on July 07, 2014, 06:33:53 pm
I've researched a little bit on swig and co - https://github.com/vinniefalco/LuaBridge looks cool, too.

Quote
Headers-only: No Makefile, no .cpp files, just one #include!
Title: Re: lua design thoughts
Post by: xray on July 07, 2014, 08:30:19 pm
I've researched a little bit on swig and co - https://github.com/vinniefalco/LuaBridge looks cool, too.

Yes, I've looked at that one too. Also checked out ooLua and toLua++. All tools have their pro's/con's. However in my opinion SWIG has two major advantages over the other tools:
1. Multiple targets. So if, in the future, we decide to skip with lua and replace it by something else we can reuse the interface files to generate the new binding.
2. Again multiple targets. This makes the tool more attractive to a broader user group. So the tool will be better tested, has more support etc.

Anyway, these were my thoughts.

xray
Title: Re: lua design thoughts
Post by: Mattn on July 07, 2014, 09:00:32 pm
awesome. it wasnt meant as a replacement, not even a suggestion for ufoai. i have no clue about swig or any other binding lib because i havent done anything with them. so dont give anything on my oppinion about this topic ;)
Title: Re: lua design thoughts
Post by: xray on July 18, 2014, 12:38:48 pm
Quote
One small step...

I've managed to set the basic framework for lua embedding. It is now possible to create a .ufo script containing lua code. The lua code is then loaded. In the code it is possible to register a function with ufo that is called after the direct loading by lua. The intended use is that this function will be responsible for creating a ui-node tree. So in future, a typically lua ui script will look like this:

Code: [Select]
--!usr/bin/lua

.. code you write to support creating the ui node tree ..
 
function my_awesome_ui_create_function ()
  -- ufo.print is actually calling Com_Printf(..)
  ufo.print("inside onload callback: this function should return a ui node tree to ufo\n")
end

-- here we register the function "my_awesome_ui_create_function" as a callback
ufoui.register_onload (my_awesome_ui_create_function)


Up to now, creating nodes is not yet possible, only writing to the log window is possible. But it's a start. You can check out the progress on http://github.com/rxadmin/ufoai.

xray
Title: Re: lua design thoughts
Post by: Mattn on July 18, 2014, 09:21:18 pm
awesome. i will check it out. thanks a lot.
Title: Re: lua design thoughts
Post by: xray on August 12, 2014, 08:38:43 pm
Just back from a vacation, added a small but significant step to the code. I've actually managed to create a window in lua and open it from the main ui (using old style scripting).

In lua the window was created using the following code (see: test_lua.ufo)
Code: [Select]
function load ()
ufo.print("inside test_lua.ufo: calling load\n")
-- create a window node with name 'lua_test'
local w = ufoui.create_window ("window", "lua_test", nil)
-- return the window node to the game
return w
end

In the main.ufo (old style) a test button was added that popped the lua-created window.
Code: [Select]
button test
{
pos "190 720"
string "_Testing"
tooltip "_Testing ui lua integration"
size "70 20"
color "0 0.8 0 1"
selectcolor "1 1 1 1"
padding 0
onClick { cmd "ui_push lua_test;" }
}

Even though the window was empty, it is quite satisfying to see the old-style/new-style work together like this.
If you like details, check out http://github.com/rxadmin/ufoai.

xray
Title: Re: lua design thoughts
Post by: Mattn on August 15, 2014, 08:19:06 pm
awesome. thats cool. i will check it out
Title: Re: lua design thoughts
Post by: Internecivus on August 16, 2014, 03:46:51 pm
Looks promising.
Hope we'd have automatic convertor from old-style to new style :)
Title: Re: lua design thoughts
Post by: geever on August 16, 2014, 10:51:42 pm
nah, no autoconversion please. The scripts need some fixes anyway.

-geever
Title: Re: lua design thoughts
Post by: xray on August 17, 2014, 08:45:32 am
I haven't yet thought about conversion tooling. It makes sense but there are some things to consider. Among one of them is the number of files to convert. Currently I count 55 .ufo files for the gui system. Wich isn't actually a large number. So the time spent developing a conversion tool might just be better spent by manually converting them. If scripts need fixes to, then manually converting them seems a better choice.

Anyway, this issue is still some time in the future. First I have to expand the functionality of the embedded lua system and attach the event mechanism (so we can write a "on click"..).

xray
Title: Re: lua design thoughts
Post by: xray on August 29, 2014, 04:28:20 pm
This one was a bitch: the event mechanism. It took me more time than expected, however I managed to get a basic event mechanism in place. It is now possible to respond to say a mouse click or a key press.

Anyway, in the process I noticed a strange behaviour. Located in the ui_input.cpp, function UI_KeyPressedInWindow. The code is below. The original code just tested the "binding" and if no binding was found, returned "false". I've added a few lines in order to allow a window node to respond to key events.

Code: [Select]
/* search requested key binding */
binding = UI_WindowNodeGetKeyBinding(window, key);
if (!binding) {
/* no binding found, perhaps a lua event handler is attached to this window */
UI_Node_KeyPressed (window, key, unicode);
return false;
}

Anyway, the strange thing is that with this implementation, each time a mouse click is given in an empty window, it triggers first the lua on_keypressed event handler (with key value = 200) then followed the lua on_click event handler. In other words a mouse click somehow triggers a key press. I wonder why.

xray
Title: Re: lua design thoughts
Post by: xray on September 03, 2014, 12:10:18 pm
Moving on, I've setup the mechanism to access the properties/methods in the ui nodes. The trick here is to mimic the class structure (as defined by the behaviour types) in lua. Again, I was impressed by SWIG's power.

And finally, I've managed to add buttons to a window. As a showcase, I've uploaded an image of a numeric keyboard. The lua code creating it is added below. Try to do this old style :)

Code: [Select]
local key_x = 20
local key_y = 80
local key_w = 40
local key_h = 40
--[[
first create the node structure
--]]
local Window = ufoui.create_window ("lua_test", nil)
local BtnTest = ufoui.create_button (Window, "test", nil)
-- create numeric pad
local x=key_x
local y=key_y
for i = 1, 9 do
-- create a button named btn<digit>
local digit = ufoui.create_button (Window, "btn" .. tostring(i), nil)
-- set properties
digit:set_text(tostring(i))
digit:set_pos(x, y)
digit:set_size(key_w, key_h)
digit:set_color(0.0, 0.1, 0.0, 1.0)
digit:set_selectcolor(1.0, 1.0, 1.0, 1.0)
digit:set_disabledcolor(1.0, 0.0, 0.0, 1.0)
digit:set_borderthickness(1)
digit:set_bordercolor(0.0, 0.5, 0.0, 1.0)
-- move to next position
x =  x + (key_w + 2)
if (i == 3) or (i == 6) then
-- start new row
y = y + (key_h + 2)
x = key_x
end
end

-- setup the window node
Window:set_background ("ui/main_bg")
Window.on_click = function (sender, x, y)
ufo.print("window on_click event handler\n")
ufo.print(string.format("sender = %s, position = (%s,%s)\n", tostring(sender.name), tostring(x), tostring(y)))
end
Window.on_rightclick = function (sender, x, y)
ufo.print("window on_rightclick event handler\n")
if (BtnTest:is_disabled()) then
ufo.print("BtnTest is disabled\n")
else
ufo.print("BtnTest is NOT disabled\n")
end
BtnTest:set_disabled(not BtnTest:is_disabled())
end
Window.on_middleclick = function (sender, x, y)
ufo.print("window on_middleclick event handler\n")
end
Window.on_keypressed = function (sender, key, unicode)
ufo.print("window on_keypressed event handler\n")
ufo.print(string.format("key = %s\n", tostring(key)))
end

--[[
button facebook
{
pos "10 720"
string "_Facebook"
tooltip "_Like us on facebook"
size "80 20"
color "0 1 0 1"
selectcolor "1 1 1 1"
padding 0
onClick { cmd "cl_openurl http://www.facebook.com/ufoai/;" }
}
--]]
-- setup the button node
BtnTest:set_text("hello world!")
BtnTest:set_tooltip("hello from the lua engine to ufo AI")
BtnTest:set_pos(20.0, 20.0)
BtnTest:set_size(200.0, 20.0)
BtnTest:set_color(0.0, 0.1, 0.0, 1.0)
BtnTest:set_selectcolor(1.0, 1.0, 1.0, 1.0)
BtnTest:set_disabledcolor(1.0, 0.0, 0.0, 1.0)
BtnTest:set_borderthickness(1)
BtnTest:set_bordercolor(0.0, 0.5, 0.0, 1.0)


function load ()
ufo.print("inside test_lua.ufo: calling load\n")
ufo.print(string.format("created a ui node: name = %s\n", Window.name))
return Window
end

-- register callbacks
ufoui.register_onload (load)

xray
Title: Re: lua design thoughts
Post by: Mattn on September 03, 2014, 08:37:05 pm
just awesome. nice progress.
Title: Re: lua design thoughts
Post by: xray on September 05, 2014, 03:19:46 pm
To demonstrate the power of having lua on the lua side, I've upgraded the example of the previous post (demonstrating the building of a numeric keypad) to a working calculator.

Code: [Select]
--!usr/bin/lua

--[[
TEST WINDOW
--]]

-- start of calculator
local key_x = 20
local key_y = 80
local key_w = 60
local key_h = 60

-- holds the calculation
buffer_command = ""
-- holds the current display value
buffer = ""
buffer_clear = true
-- holds the command
command = ""

--[[
first create the node structure
--]]
Window = ufoui.create_window ("lua_test", nil)

-- create result field
display = ufoui.create_button (Window, "test", nil)
display:set_text("_")
display:set_tooltip("calculator display")
display:set_pos(20.0, 20.0)
display:set_size(200.0, 20.0)
display:set_color(1.0, 1.0, 0.0, 1.0)
display:set_selectcolor(1.0, 1.0, 1.0, 1.0)
display:set_disabledcolor(1.0, 0.0, 0.0, 1.0)
display:set_borderthickness(1)
display:set_bordercolor(0.0, 0.5, 0.0, 1.0)

-- create numeric pad
local x=key_x
local y=key_y
for i = 1, 9 do
-- create a button named btn<digit>
local digit = ufoui.create_button (Window, "btn" .. tostring(i), nil)
-- set properties
digit:set_text(tostring(i))
digit:set_pos(x, y)
digit:set_size(key_w, key_h)
digit:set_color(0.0, 1.0, 0.0, 1.0)
digit:set_selectcolor(1.0, 1.0, 1.0, 1.0)
digit:set_disabledcolor(1.0, 0.0, 0.0, 1.0)
digit:set_borderthickness(1)
digit:set_bordercolor(0.0, 0.5, 0.0, 1.0)
digit.on_click = function (sender)
ufo.print(string.format("processing [%d]\n", tostring(i)))
-- clear buffer on request
if (buffer_clear) then
buffer = ""
buffer_clear = false
end
-- add digit to buffer
buffer = buffer .. tostring(i)
display:set_text(buffer)
end
-- move to next position
x =  x + (key_w + 2)
if (i == 3) or (i == 6) then
-- start new row
y = y + (key_h + 2)
x = key_x
end
end
-- add 0 button
zero = ufoui.create_button (Window, "btn0", nil)
zero:set_text("0")
zero:set_pos(key_x, y + key_h + 2)
zero:set_size(3 * key_w, key_h)
zero:set_color(0.0, 1.0, 0.0, 1.0)
zero:set_selectcolor(1.0, 1.0, 1.0, 1.0)
zero:set_disabledcolor(1.0, 0.0, 0.0, 1.0)
zero:set_borderthickness(1)
zero:set_bordercolor(0.0, 0.5, 0.0, 1.0)
zero.on_click = function (sender)
ufo.print("processing [0]\n")
-- clear buffer on request
if (buffer_clear) then
buffer = ""
buffer_clear = false
end
-- add zero to buffer
buffer = buffer .. "0"
display:set_text(buffer)
end
-- add + button
plus = ufoui.create_button (Window, "btnAdd", nil)
plus:set_text("+")
plus:set_pos(x + 2, key_y)
plus:set_size (key_w, key_h)
plus:set_color(0.0, 1.0, 0.0, 1.0)
plus:set_selectcolor(1.0, 1.0, 1.0, 1.0)
plus:set_disabledcolor(1.0, 0.0, 0.0, 1.0)
plus:set_borderthickness(1)
plus:set_bordercolor(0.0, 0.5, 0.0, 1.0)
plus.on_click = function (sender)
ufo.print("processing [+]\n")
-- add command to buffer_command
command = "+"
buffer_clear = true
buffer_command = buffer_command .. buffer .. command
ufo.print(string.format("command: %s\n", buffer_command))
end
-- add - button
minus = ufoui.create_button (Window, "btnSub", nil)
minus:set_text("-")
minus:set_pos(x + 2, key_y + (key_h + 2))
minus:set_size (key_w, key_h)
minus:set_color(0.0, 1.0, 0.0, 1.0)
minus:set_selectcolor(1.0, 1.0, 1.0, 1.0)
minus:set_disabledcolor(1.0, 0.0, 0.0, 1.0)
minus:set_borderthickness(1)
minus:set_bordercolor(0.0, 0.5, 0.0, 1.0)
minus.on_click = function (sender)
ufo.print("processing [-]\n")
-- add command to buffer_command
command = "-"
buffer_clear = true
buffer_command = buffer_command .. buffer .. command
ufo.print(string.format("command: %s\n", buffer_command))
end
-- add return button
enter = ufoui.create_button (Window, "btnGo", nil)
enter:set_text("GO!")
enter:set_pos(x + 2, key_y + 2 * (key_h + 2))
enter:set_size (key_w, key_h)
enter:set_color(0.0, 1.0, 0.0, 1.0)
enter:set_selectcolor(1.0, 1.0, 1.0, 1.0)
enter:set_disabledcolor(1.0, 0.0, 0.0, 1.0)
enter:set_borderthickness(1)
enter:set_bordercolor(0.0, 0.5, 0.0, 1.0)
enter.on_click = function (sender)
ufo.print("processing [GO!]\n")
buffer_command = buffer_command .. buffer
-- execute as lua command to perform the math
ufo.print(string.format("execute: %s\n", buffer_command))
local fn = loadstring("return ".. buffer_command)
buffer = tostring(fn())
display:set_text(buffer)
-- reset but keep result in buffer for next operation
buffer_command = ""
buffer_clear = true
end
-- add clear button
clear = ufoui.create_button (Window, "btnClear", nil)
clear:set_text("C")
clear:set_pos(x + 2, key_y + 3 * (key_h + 2))
clear:set_size (key_w, key_h)
clear:set_color(0.0, 1.0, 0.0, 1.0)
clear:set_selectcolor(1.0, 1.0, 1.0, 1.0)
clear:set_disabledcolor(1.0, 0.0, 0.0, 1.0)
clear:set_borderthickness(1)
clear:set_bordercolor(0.0, 0.5, 0.0, 1.0)
clear.on_click = function (sender)
ufo.print("processing [C]\n")
buffer_command = ""
buffer_clear = true
buffer = ""
display:set_text(buffer)
end
-- setup the window node
Window:set_background ("ui/main_bg")
Window.on_click = function (sender, x, y)
ufo.print("window on_click event handler\n")
ufo.print(string.format("sender = %s, position = (%s,%s)\n", tostring(sender.name), tostring(x), tostring(y)))
end
Window.on_rightclick = function (sender, x, y)
ufo.print("window on_rightclick event handler\n")
end
Window.on_middleclick = function (sender, x, y)
ufo.print("window on_middleclick event handler\n")
end
Window.on_keypressed = function (sender, key, unicode)
ufo.print("window on_keypressed event handler\n")
ufo.print(string.format("key = %s\n", tostring(key)))
end
Window.on_windowopened = function (sender)
ufo.print("window is being opened\n")
end
Window.on_windowclosed = function (sender)
ufo.print("window is being closed\n")
end

function load ()
ufo.print("inside test_lua.ufo: calling load\n")
ufo.print(string.format("created a ui node: name = %s\n", Window.name))
return Window
end

-- register callbacks
ufoui.register_onload (load)


xray
Title: Re: lua design thoughts
Post by: Mattn on September 05, 2014, 07:57:52 pm
hehe - nice one. good job.
Title: Re: lua design thoughts
Post by: xray on September 13, 2014, 10:37:03 am
Recreating the main window.

A milestone has been reached. The code below (lua) recreates a fully functional main window. The result is displayed in the image below.

For comparison, I've added the related parts of the main.ufo script between --[[ and --]] comments. If you want to get into details, see my branch at http://github.com/rxadmin/ufoai.

The status is that most of the groundwork is done, meaning the technical stuff of loading lua, calling events, getting the right
table in lua with derived ui nodes, etc.

What remains is bringing more of the behaviour classes to lua (currently button, string, checkbox, panel are supported). And of course the translating of the .ufo files into lua.


Code: [Select]
--!usr/bin/lua

--[[
first create the node structure
--]]
wndMain = ufo.create_window ("lua_main", nil)
wndMain:set_background ("ui/main_bg")

--[[
string title {
string "_UFO: ALIEN INVASION"
size "500 50"
pos "262 50"
font "f_title"
color "0.59 0.78 0.56 1"
contentalign ALIGN_CC
}
--]]
local lbTitle = ufo.create_string (wndMain, "lbTitle", nil)
lbTitle:set_text("_UFO: ALIEN INVASION")
lbTitle:set_pos(262, 50)
lbTitle:set_size(500, 50)
lbTitle:set_font("f_title")
lbTitle:set_color(0.59, 0.78, 0.56, 1.0)
lbTitle:set_contentalign(ufo.ALIGN_CC)

--[[
button facebook
{
pos "10 720"
string "_Facebook"
tooltip "_Like us on facebook"
size "80 20"
color "0 0.5 0 1"
selectcolor "1 1 1 1"
padding 0
onClick { cmd "cl_openurl http://www.facebook.com/ufoai/;" }
}
--]]
local btnFacebook = ufo.create_button(wndMain, "btnFacebook", nil)
btnFacebook:set_text("_Facebook")
btnFacebook:set_tooltip("_Like us on facebook")
btnFacebook:set_pos(10, 720)
btnFacebook:set_size(80, 20)
btnFacebook:set_color(0.0, 0.5, 0.0, 1.0)
btnFacebook:set_selectcolor(1.0, 1.0, 1.0, 1.0)
btnFacebook:set_padding (0)
btnFacebook.on_click = function (sender)
ufo.cmd ("cl_openurl http://www.facebook.com/ufoai/;")
end

--[[
button twitter
{
pos "100 720"
string "_Twitter"
tooltip "_Follow us on twitter"
size "70 20"
color "0 0.5 0 1"
selectcolor "1 1 1 1"
padding 0
onClick { cmd "cl_openurl http://www.twitter.com/ufoai/;" }
}
--]]
local btnTwitter = ufo.create_button(wndMain, "btnTwitter", nil)
btnTwitter:set_text("_Twitter")
btnTwitter:set_tooltip("_Follow u on twitter")
btnTwitter:set_pos(100, 720)
btnTwitter:set_size(70, 20)
btnTwitter:set_color(0.0, 0.5, 0.0, 1.0)
btnTwitter:set_selectcolor(1.0, 1.0, 1.0, 1.0)
btnTwitter:set_padding (0)
btnTwitter.on_click = function (sender)
ufo.cmd ("cl_openurl http://www.twitter.com/ufoai/;")
end

--[[
panel buttons {
{
pos "312 250"
size "400 400"
layout LAYOUT_TOP_DOWN_FLOW
layoutMargin 15
}
MainMenuBtn btn_campaign {
string "_CAMPAIGN"
onClick {
cmd "ui_push campaign;"
}
}
MainMenuBtn btn_skirmish {
string "_SKIRMISH"
onClick {
cmd "ui_push skirmish;"
}
}
MainMenuBtn btn_multiplayer {
string "_MULTIPLAYER"
onClick {
cmd "ui_push multiplayer;"
}
}
MainMenu2Btn btn_options {
string "_OPTIONS"
onClick {
cmd "ui_push options;"
}
}
MainMenu2Btn btn_tutorials {
string "_TUTORIALS"
onClick {
cmd "ui_push tutorials;"
}
}
MainMenu2Btn btn_credits {
string "_DEVELOPERS"
onClick {
cmd "seq_start developers;"
}
}
MainMenuExitBtn btn_exit {
string "_EXIT"
onClick {
cmd "quit;"
}
}
}
--]]
local pnlButtons = ufo.create_panel (wndMain, "pnlButtons", nil)
pnlButtons:set_pos(312, 250)
pnlButtons:set_size (400, 400)
pnlButtons:set_layout (ufo.LAYOUT_TOP_DOWN_FLOW)
pnlButtons:set_layoutmargin (15)

local btnCampaign = ufo.create_button (pnlButtons, "btnCampaign", "MainMenuBtn")
btnCampaign:set_text ("_CAMPAIGN")
btnCampaign.on_click = function (sender)
ufo.cmd "ui_push campaign;"
end

local btnSkirmish = ufo.create_button (pnlButtons, "btnSkirmish", "MainMenuBtn")
btnSkirmish:set_text ("_SKIRMISH")
btnSkirmish.on_click = function (sender)
cmd "ui_push skirmish;"
end

local btnMultiplayer = ufo.create_button (pnlButtons, "btnMultiplayer", "MainMenuBtn")
btnMultiplayer:set_text ("_MULTIPLAYER")
btnMultiplayer.on_click = function (sender)
cmd "ui_push multiplayer;"
end

local btnOptions = ufo.create_button (pnlButtons, "btnOptions", "MainMenu2Btn")
btnOptions:set_text ("_OPTIONS")
btnOptions.on_click = function (sender)
ufo.cmd ("ui_push options;")
end

local btnTutorials = ufo.create_button (pnlButtons, "btnTutorials", "MainMenu2Btn")
btnTutorials:set_text ("_TUTORIALS")
btnTutorials.on_click = function (sender)
ufo.cmd ("ui_push tutorials;")
end

local btnCredits = ufo.create_button (pnlButtons, "btnCredits", "MainMenu2Btn")
btnCredits:set_text ("_DEVELOPERS")
btnCredits.on_click = function (sender)
ufo.cmd ("seq_start developers;")
end

local btnExit = ufo.create_button (pnlButtons, "btnExit", "MainMenuExitBtn")
btnExit:set_text ("_EXIT")
btnExit.on_click = function (sender)
ufo.cmd ("quit;")
end

--[[
string version {
string *cvar:version
pos "10 744"
size "1004 20"
}
--]]
local lbVersion = ufo.create_string (wndMain, "lbVersion", nil)
lbVersion:set_text (ufo.findvar("version"):as_string())
lbVersion:set_pos (10, 744)
lbVersion:set_size (1004, 20)

function load ()
return wndMain
end

-- register callbacks
ufo.register_onload (load)

xray
Title: Re: lua design thoughts
Post by: Mattn on September 13, 2014, 08:21:28 pm
awesome - really cool stuff - feel free to prepare a pull request ;)
Title: Re: lua design thoughts
Post by: Internecivus on September 16, 2014, 10:38:17 am
And of course the translating of the .ufo files into lua.

Count me in.
Title: Re: lua design thoughts
Post by: xray on September 16, 2014, 02:18:26 pm
At the moment I think it is too early to pull it into the main branch. For one thing, only few ui-node types are supported and another issue is that at the moment I'm looking for a way to implement function nodes. The solution for the last problem is already in my mind, just needs to be implemented.

The implementation follows roughly the translation of the _assets.ufo file. I've split the file into _assets.0.ufo (the lua version) and _assets.1.ufo (the remainder that hasn't been translated). Basically I translate top down moving components from the 1 to 0 and then translate them. This way the lua version is loaded before the old script version.

In the future, if all .ufo scripts are lua based, it would be nice to have load script that loads all the other scripts in a predefined order. Currently the order is defined by the name. I'm not sure if this is really portable.

Just keep following this post to be informed on the progress.

xray
Title: Re: lua design thoughts
Post by: Internecivus on September 16, 2014, 07:37:33 pm
btw, I've found that you are using merge-based workflow. When I started to contribute, I was told that it is preferable to use rebase-based workflow as it is a team policy. Anyway, let ask Mattn to clarify.
Title: Re: lua design thoughts
Post by: Mattn on September 17, 2014, 09:40:21 pm
it doesnt matter in his branch as i would squash the merge anyway into one commit
Title: Re: lua design thoughts
Post by: Schilcote on September 18, 2014, 01:31:59 am
I guess the easiest solution would be to use the .lua file extension, however, this might bring in some unwanted side effects in external tools (e.g. makefiles, packaging tools) [...so we're using a different extension instead.]

Did we actually verify that this would be a problem? It seems a bit silly to call things something other than what they are, especially if it turns out we don't actually need to.
Title: Re: lua design thoughts
Post by: xray on September 26, 2014, 09:01:49 pm
Though speed is slowing down a bit, the implementation of the function nodes is now in place. In fact, instead of using function nodes, I'm using simply plain lua functions. From ufo it is possible to callback into lua, so that mechanism is reused here. So instead of defining a function node, you simply attach a function in lua to a node. The example below shows how this is done.

Code: [Select]
--[[
component MainMenuTab extends button
{
{
size "125 30"
font "f_small_bold"
bgcolor "0.4 0.515 0.5 0.25"
color "1 1 1 0.5"
}
func enable {
*node:parent@bgcolor = "0.4 0.515 0.5 1"
*node:parent@color = "0 0 0 0.9"
}
func disable {
*node:parent@bgcolor = "0.4 0.515 0.5 0.25"
*node:parent@color = "1 1 1 0.5"
}
}
--]]
local MainMenuTab = ufo.create_component ("button", "MainMenuTab", nil)
MainMenuTab:set_size(1215, 30)
MainMenuTab:set_font("f_small_bold")
MainMenuTab:set_backgroundcolor(0.4, 0.515, 0.5, 0.25)
MainMenuTab:set_color(1, 1, 1, 0.5)
function MainMenuTab.enable (sender)
local parent = sender:parent()
parent:set_backgroundcolor(0.4, 0.515, 0.5, 1)
parent:set_color(0, 0, 0, 0.9)
end
function MainMenuTab.disable (sender)
local parent=sender:parent()
parent:set_backgroundcolor(0.4, 0.515, 0.5, 0.25)
parent:set_color(1, 1, 1, 0.5)
end

I guess the same will aply for the confunc. No need to make this distinction anymore. As always, code is found on http://github.com/rxadmin/ufoai.

xray
Title: Re: lua design thoughts
Post by: Mattn on October 02, 2014, 02:41:32 pm
but keep in mind that confunc is also callable from the in game-console where as func is not.
Title: Re: lua design thoughts
Post by: Mattn on October 02, 2014, 02:46:11 pm
e.g. the component ObjectInfo is in both lua and ufo script - which one is taken into account here?
Title: Re: lua design thoughts
Post by: xray on October 02, 2014, 08:15:46 pm
e.g. the component ObjectInfo is in both lua and ufo script - which one is taken into account here?

When two definitions are available, the first one processed is taken into account. In the case here, the lua version, however this depends on the actual order of file processing. Also keep in mind that my branch has an unfinished translation of _assets.ufo (they're named _assets.0.ufo and _assets.1.ufo). So ObjectInfo is now in both however once the translation is finished in lua, the ufo script version is removed.

xray

Title: Re: lua design thoughts
Post by: xray on October 04, 2014, 10:17:33 am
Added another nice feature: events returning a boolean value. In the _assets.ufo script I came across the following code (component: healthbar)

Code: [Select]
visiblewhen "*cvar:mn_show_employee <= 2"

Using the event mechanism in lua this now becomes.

Code: [Select]
healthbar.on_visiblewhen = function (sender)
return (ufo.findvar("mn_show_employee"):as_integer() <= 2)
end

Even though the result currently is the same, the latter implementation allows for much more flexibility in creating complex expressions.

xray
Title: Re: lua design thoughts
Post by: xray on October 04, 2014, 04:07:40 pm
Next milestone reached: the file _assets.ufo is now translated in lua.

xray
Title: Re: lua design thoughts
Post by: xray on October 23, 2014, 08:51:53 pm
It has been a while since I put a post here. Mostly caused by stress at work and stress at home (my master thesis   :) ).

Anyway: I recently finished the binding for all classes. Means that the basic interface work is more or less finished.

I've also developed a way to generate some rudimentary documentation from the SWIG interface file. Maintaining API documentation by hand is simply not possible with the extend the API now has. The method uses SWIGs feature to dump the parsing stages in an intermediate xml file wich is then formatted using a xsl stylesheet. The result is attached below.

xray


Title: Re: lua design thoughts
Post by: Mattn on October 24, 2014, 08:28:22 pm
just awesome. great stuff. that sounds like it is mergeable?
Title: Re: lua design thoughts
Post by: xray on October 24, 2014, 08:51:32 pm
Ehmm... well... yes, I guess it is. That leaves me with two questions:
1. how to merge the branche into the main trunc
2. I have some documentation to be added to the wiki (explaining a bit how stuff works), how do I get it into the wiki?

xray
Title: Re: lua design thoughts
Post by: Mattn on October 31, 2014, 09:36:40 pm
do a pull request on github.

Signup in the wiki (http://ufoai.org/w/index.php?title=Special:UserLogin&type=signup) and add your documentation into http://ufoai.org/wiki/Technical_Documentation
Title: Re: lua design thoughts
Post by: Mattn on October 31, 2014, 09:37:27 pm
you can also post the docs here and we will create the first version in the wiki if you are not familiar with mediawiki - then you can edit them later.
Title: Re: lua design thoughts
Post by: Internecivus on November 15, 2014, 10:28:33 am
I've got the latest snapshot of rxadmin/ufoai, made some changes to Makefile to be able to build project on Linux and despite I was able to build project, it segfaults during reading from "_assets.ufo". Is it still unfinished or am I missing something?

P.S. Segfaults are occuring if component has StatBarWide in it. When I commented these components, it continued further.
Title: Re: lua design thoughts
Post by: xray on November 15, 2014, 11:03:17 am
It is finished, however, my build is on windows so I'm not sure why on linux is segfaults. Please provide me with some more info, e.g. the location of the segfault and the modifications you made to the build.
Title: Re: lua design thoughts
Post by: Internecivus on November 15, 2014, 11:51:20 am
http://pastebin.com/Cw3De8sg (http://pastebin.com/Cw3De8sg) console output.
http://pastebin.com/e5t0rfv0 (http://pastebin.com/e5t0rfv0) Changes I've made
http://pastebin.com/HFqbfApC (http://pastebin.com/HFqbfApC)  Backtrace
http://pastebin.com/dpxnRZe6 (http://pastebin.com/dpxnRZe6) Full backtrace

Segfaults are occuring if component has StatBarWide in it. When I commented these components, it continued further crashing somewhere else because of absence of these components.
Title: Re: lua design thoughts
Post by: xray on November 15, 2014, 01:52:20 pm
The code is not wrong here. Runs fine on windows, however, I'm building with C::B. My guess, somewhere there is a wrong file compiled & added in the build.

Please check: do you have a file ui_lua_shared.cxx? If so, please remove it.
Note: you should also have a file ui_lua_shared.cpp. This file must be kept.

Just updated the repository rxadmin/ufoai, so if you pull, it will be removed automatically.

Title: Re: lua design thoughts
Post by: Internecivus on November 16, 2014, 10:07:37 am
I commented out this string and now I'm able to play, but I am not sure if it wasn't a bad idea.

http://pastebin.com/t54JC339 (http://pastebin.com/t54JC339)
Title: Re: lua design thoughts
Post by: xray on November 16, 2014, 10:49:38 am
I commented out this string and now I'm able to play, but I am not sure if it wasn't a bad idea.

http://pastebin.com/t54JC339 (http://pastebin.com/t54JC339)

The line you commented out should free up allocated memory. So you get a memory leak. In general, this is not a good idea. The segfault occurs due to an invalid pointer being freed. I'll look into this problem, though it may take some time since I have to port the build to my Ubuntu machine.

If I look at the backtrace I get the feeling youre running 64-bit. Correct?

Title: Re: lua design thoughts
Post by: Internecivus on November 16, 2014, 11:22:30 am
Yeah, exactly.

Btw, I am almost done with porting codeblocks project for *nix target.
Title: Re: lua design thoughts
Post by: xray on November 16, 2014, 12:39:40 pm
I've reviewed the code and updated the repository at rxadmin. Looks like you found a true platform bug.

The code runs fine on Win32 due to the fact that floats are 32-bit. So initializing a float value to 0, and then use it as a pointer (as the code did) creates in fact a nullptr value wich is nicely ignored by Mem_Free.

On 64-bit platforms however, setting the 32-bit float to a 64-bit pointer field does not fully set the value to 0x0. So Mem_Free here tried to release the code. Thus the segfault.

The real cause here is of course the implementation of uiAbstractValueNode_t. It tries to mix cvar and floats in a single field without a flag that controls what is actually there. The code should be rewritten in the future.

Please do a pull from rxadmin/ufoai and try if the code runs. I look forward to youre comment.

xray
Title: Re: lua design thoughts
Post by: Internecivus on November 16, 2014, 01:13:54 pm
Everything looks fine so far.

I'm going to help with converting ufo files to new format.
Title: Re: lua design thoughts
Post by: xray on November 16, 2014, 01:54:24 pm
I'm going to help with converting ufo files to new format.

Great. In that case you'll be needing a listing of the api in lua. See attacheded file for this.
Title: Re: lua design thoughts
Post by: Internecivus on November 17, 2014, 07:57:22 am
I can't figure out how to derive from 'ipopup' window, can you help me?
Title: Re: lua design thoughts
Post by: xray on November 17, 2014, 08:46:07 am
Code: [Select]
local foo = ufo.create_window("foo", "ipopup")
Title: Re: lua design thoughts
Post by: Internecivus on November 18, 2014, 09:10:43 am
Another thing is causing segfaults: when I click on my base, it will eventually segfault during deleting nodes from geoscape.
I'll provide more details as soon as I encounter it once again.
Title: Re: lua design thoughts
Post by: xray on November 18, 2014, 01:07:42 pm
Just looked into this. Happend on my machine also.  I've already added the fix to the repository rxadmin/ufoai. So with a pull it should be gone.

Thanx for your effort in testing.
Title: Re: lua design thoughts
Post by: Internecivus on November 18, 2014, 05:24:27 pm
Another struggling point.
How to create confuncs?
Title: Re: lua design thoughts
Post by: xray on November 18, 2014, 05:53:27 pm
Confunc's are nodes. Create them using ufo.create_confunc. Then add an on_click eventhandler.
Title: Re: lua design thoughts
Post by: Internecivus on November 18, 2014, 06:43:37 pm
Okay, seems that I still can't figure it out.

This what was before:
Code: [Select]
confunc add_mail
    {
        call *node:root.maillist.mainBody.mails@createChild("mail<1>", "mailheader")
        *node:root.maillist.mainBody.mails.mail<1>.header.index@integer = <1>
        *node:root.maillist.mainBody.mails.mail<1>.header.headline@string = <2>
        *node:root.maillist.mainBody.mails.mail<1>.icon@src = <3>
        if ( <4> eq 0 ) {
            *node:root.maillist.mainBody.mails.mail<1>.header@bgcolor   = "0.56 0.81 0.76 0.6"
        }
        *node:root.maillist.mainBody.mails.mail<1>.header.date@string = <5>
    }
and my interpretation:
Code: [Select]
add_mail = ufo.create_confunc(mailclient, "add_mail", nil)
add_mail.on_click = function(sender, index, headline, icon, state, date)
        local mail = ufo.create_control(mails, "mailheader", "mail"..index, nil)

        mail:child("header"):child("index"):set_value(index)
        mail:child("header"):child("headline"):set_text(headline)
        mail:child("header"):child("icon"):set_src(icon)
        mail:child("header"):child("date"):set_text(date)
        if (state == 0) then
            mail:child("header"):set_backgroundcolor(0.56, 0.81, 0.76, 0.6)
        end
    end
Title: Re: lua design thoughts
Post by: xray on November 18, 2014, 08:26:55 pm
First, can you specify the precise error you encounter?

As for the code, most of it looks okay, with exception of "set_src" that doesn't exist in lua, it was renamed to "set_source".

Also, keep in mind that if you write:
Code: [Select]
add_mail = ufo.create_confunc(mailclient, "add_mail", nil)

you create a global variable add_mail, so this identifier is shared in all modules. If in another module an identical variable "add_mail" is created the last one is preserved. So in general it is better to create module local variables using the local keyword.

Code: [Select]
local add_mail = ufo.create_confunc(mailclient, "add_mail", nil)

Title: Re: lua design thoughts
Post by: Internecivus on November 18, 2014, 08:39:51 pm
The problem is that it does nothing and I don't know how to do it propery.

Maybe it is something with arguments that I should pass to this confunc.
Title: Re: lua design thoughts
Post by: Internecivus on November 18, 2014, 08:49:25 pm
Another thing.

Window 'mail' is inherited from ipopup.
It has
Code: [Select]
mail:set_closebutton(true)But I don't see any close button somehow.
And another thing is that this window takes up all place.
http://i.imgur.com/R2OKnpK.jpg (http://i.imgur.com/R2OKnpK.jpg) - expected behavior
http://i.imgur.com/P5R8Lwm.jpg (http://i.imgur.com/P5R8Lwm.jpg) - what it really does
Title: Re: lua design thoughts
Post by: xray on November 18, 2014, 09:20:17 pm
Can you post the full translation?
Title: Re: lua design thoughts
Post by: Internecivus on November 18, 2014, 09:34:27 pm
https://github.com/drone-pl/ufoai/blob/df015731694cb77c069f19724c3046e75f2d67b6/base/ufos/ui/mailclient.ufo (https://github.com/drone-pl/ufoai/blob/df015731694cb77c069f19724c3046e75f2d67b6/base/ufos/ui/mailclient.ufo)
Title: Re: lua design thoughts
Post by: Internecivus on November 19, 2014, 07:32:37 pm
Yet another thing - how to apply string value to cvar?
Title: Re: lua design thoughts
Post by: xray on November 19, 2014, 08:20:09 pm
Fixed a bug in the confunc execution. It should work properly now. Added some improvements to the confunc code also.
As for the call the 'removeAllChild', it wasn't implemented yet. I've added it, however it is renamed to "remove_childs".
Please pull the latest fixes from the repository at rxadmin/ufoai.

I also checked youre translation at:
https://github.com/drone-pl/ufoai/blob/df015731694cb77c069f19724c3046e75f2d67b6/base/ufos/ui/mailclient.ufo (https://github.com/drone-pl/ufoai/blob/df015731694cb77c069f19724c3046e75f2d67b6/base/ufos/ui/mailclient.ufo)

There are some places where you query for a child at the wrong level. E.g. in add_mail, you were referencing "mail:child("header"):child("icon").." however "icon" is positioned as a direct child node of "mail", not "header".
Also index can be directly accessed as it is a parameter given to the call.

I've added the corrected version to the rxadmin/ufoai repository as well.

xray.


Title: Re: lua design thoughts
Post by: xray on November 19, 2014, 08:27:39 pm
Yet another thing - how to apply string value to cvar?

It is not yet implemented. I've postponed this since I wanted to first find a way to create and reuse variables inside node using lua only. This involves creating lua based table properties that are linked to values stored inside a node. A bit like a callback mechanism only for variables. If I can find a way I can bring lua dynamic variables into the UI.
Title: Re: lua design thoughts
Post by: Internecivus on November 22, 2014, 09:31:51 am
Hmm, seems like nodes created in confuncs are treated as statically created and cannot be removed via remove_childs (which removes only dynamically nodes).

Another feature request: implement starlayout and preventtypingescape for uiWindow.

P.S. btw, just as a matter of fact, 'childs' may be not a proper word, because plural word for 'child' is 'children' :)
Title: Re: lua design thoughts
Post by: xray on November 22, 2014, 04:51:59 pm
Hmm, seems like nodes created in confuncs are treated as statically created and cannot be removed via remove_childs (which removes only dynamically nodes).

True. I copied the creation code directly from ui_parse.cpp, the module parsing the ufo scripts. So any node created using lua is basically a static node. However, I think it should be easy to extend the current mechanism with a flag indicating that a dynamic node should be created. I'll look into this.

Quote
Another feature request: implement starlayout and preventtypingescape for uiWindow.

I'll put it on the 'todo' list.

Quote
P.S. btw, just as a matter of fact, 'childs' may be not a proper word, because plural word for 'child' is 'children' :)

I'll rename it. We don't want to have any typos here.
Title: Re: lua design thoughts
Post by: xray on November 23, 2014, 11:16:51 am
Hmm, seems like nodes created in confuncs are treated as statically created and cannot be removed via remove_childs (which removes only dynamically nodes).

I've added an extra parameter to the node creation functions allowing the creation of dynamic nodes. Usage:
Code: [Select]
local mail = ufo.create_control(mails, "mailheader", "mail" .. index, nil, ufo.DYNAMIC_CONTROL)

This solves the problem, however, I'm not happy with this solution. The solution forces the lua side to have knowledge about static/dynamic nodes and when to use them.

If I look at the code, I have the strong feeling that static nodes are only created during the initial script loading stage and are never created by events and confuncs. If that is the case, the switch between static/dynamic can be made after the parsing stage and it should not become an extra parameter on the lua side. I would like someone to confirm this so I can refactor the implementation.

Title: Re: lua design thoughts
Post by: Internecivus on November 23, 2014, 12:13:06 pm
If I look at the code, I have the strong feeling that static nodes are only created during the initial script loading stage and are never created by events and confuncs.

AFAIK you are right.

P.S. Can you check if on_viewchange is working properly?
In 'mail' window, for example, it has to update scrollbar fullsize and viewsize property and if fullsize is greater than viewsize, scrollbar should appear (autoshowscroll(true)). I don't see scrollbar regardless of the size of the mail text.
Title: Re: lua design thoughts
Post by: geever on November 23, 2014, 01:14:08 pm
xray, I think Bayo's intention was converting every node to dynamic ones, but he did not have time to finish it. So having them all dynamic is good.

-geever
Title: Re: lua design thoughts
Post by: xray on November 23, 2014, 03:47:37 pm
OK. I'm going to make all lua created nodes dynamic. This will remove the previous implemented "flags" paramater in the node creation functions.
Title: Re: lua design thoughts
Post by: xray on November 23, 2014, 04:40:02 pm
P.S. Can you check if on_viewchange is working properly?
In 'mail' window, for example, it has to update scrollbar fullsize and viewsize property and if fullsize is greater than viewsize, scrollbar should appear (autoshowscroll(true)). I don't see scrollbar regardless of the size of the mail text.

You were right. There was a rather annoying bug here. It also prevented the trigger on various other event handlers. I've fixed it and on_viewchange is now properly called. However, this did not solved the viewsize issue. I'll have to look into this a bit further.

I've also changed the creation of nodes. They now are allways dynamic nodes. This triggered an assertion in the execution of func/confunc nodes. It didn't liked dynamic nodes. Possibly to prevent the execution of nodes being deleted. I've removed this assertion but we do need a safeguard against this situation.
Title: Re: lua design thoughts
Post by: Sandro on November 23, 2014, 05:14:26 pm
Just keep the list of deleted nodes? It could not be so big if you code it seperately, in a map for example. If memory serves, LUA provides the hashmap class?
Title: Re: lua design thoughts
Post by: geever on November 23, 2014, 07:14:21 pm
newer UI scripts use "delete all & create nodes" approach quite heavily..
Title: Re: lua design thoughts
Post by: xray on November 26, 2014, 09:36:07 pm
Another thing.

Window 'mail' is inherited from ipopup.
It has
Code: [Select]
mail:set_closebutton(true)But I don't see any close button somehow.
And another thing is that this window takes up all place.

I've fixed the issues with the close button and the sizing. All these issues were actually related to a fundamental difference in node creating using ufo script and using lua. In ufo script, the node is created, then the properties are read and set and only after that the node is inserted and onLoaded is called. In lua, the node is created, inserted and onLoaded is called. Then the remainder of the lua code sets various properties. This resulted in the isFullscreen flag being incorrect, resulting in the wrong sizing and the closebutton/dragbutton not being created.
Title: Re: lua design thoughts
Post by: xray on November 29, 2014, 08:24:14 pm
Added a fix for the order in wich the close/drag buttons were created.
Title: Re: lua design thoughts
Post by: Internecivus on December 07, 2014, 07:07:21 am
Optionlist (or some of its ancestor) lacks cvar attribute.
Quote
UI_AbstractOptionGetCurrentValue: node 'checkcvars.select_language' doesn't have a valid cvar assigned
Title: Re: lua design thoughts
Post by: xray on December 07, 2014, 05:54:51 pm
I've added the possibility to set a cvar value to an option list.
Title: Re: lua design thoughts
Post by: Internecivus on December 08, 2014, 08:10:12 pm
Few more problems I've experienced.
I created a pull request and commented on each issue I stated above.
Title: Re: lua design thoughts
Post by: xray on December 08, 2014, 10:03:44 pm
Few more problems I've experienced.
  • requested set_cvar segfaults
  • cvar-associated checkbox can't be checked back if unchecked
  • nor intro window nor model_test windows work (in case of intro you have to find intro.ogm, because it was removed a while ago)
  • and some others
I created a pull request and commented on each issue I stated above.

Thx. for the pull request, it clarified a lot. Problem was I did not have a lua ufo file that actually triggered this code. Found the segfault. It should work now. Also added the option to set cvar with a string value. Can you check if it's working now?

As for the other issues, I'm going to look into this by the end of this week. The checkbox issue might be related to the mixing of int/float values in the cvar.
Title: Re: lua design thoughts
Post by: Internecivus on December 09, 2014, 08:56:12 am
Methods 'viewpos', 'fullsize', 'viewsize' and setters for them are missing from optionlist node.

Quote
lua error(0) [node=select_language]: [string "main.ufo"]:292: attempt to call method 'fullsize' (a nil value)
lua error(0) [node=select_language_scroll]: [string "main.ufo"]:303: attempt to call method 'set_viewpos' (a nil value)
Title: Re: lua design thoughts
Post by: xray on December 09, 2014, 08:38:44 pm
  • nor intro window
Merged your pull request. The segfaults are gone. At least as far as I can see. Anyway, the intro seems to be running on my system without troubles. At least.. I guess with "intro" you mean the sequence played when you start a new campaign.
Title: Re: lua design thoughts
Post by: Internecivus on December 10, 2014, 03:46:11 pm
A little feature request - passing parameters to on_windowopened so 'ui_push windowname param' will work.
Title: Re: lua design thoughts
Post by: xray on December 10, 2014, 05:28:14 pm
Fixed the intro problem. Now seeing a nice blue earth in the window...
Title: update on project (was: Re: lua design thoughts)
Post by: xray on May 31, 2015, 07:18:37 pm
Hi everybody,

I guess you were wondering wether this project was actually abandoned. Well, it's not. However, it's been a long time, I know.

Since  my last post in january I got all tied up in both work and my master thesis (should you wonder: yes it is finished and yes I got my master title  ;D ;D ;D). I was planning on bringing this project into the main branch until I realised that I couldn't spent any time on issues should they arise. So I postponed the pull request.
 
Now I'm picking up were I left. Last week I merged with the main branch and fixed some issues. This week I'm going to setup a linux build environment. After that, it will be time to merge with the main branch.

Regards,
xray
Title: Re: lua design thoughts
Post by: Internecivus on May 31, 2015, 07:26:25 pm
My laptop was fried few months ago and I can't afford a replacement yet. Hope I'll get something until the end of the summer.
Title: Re: lua design thoughts
Post by: ShipIt on June 01, 2015, 07:24:24 am
Good to hear this is still alive.

Also congratulations on your master title!
Title: Re: lua design thoughts
Post by: geever on August 23, 2015, 06:52:33 pm
I just found out that this is in master already... Hmm, I like it and don't, at the same time. It has great potential for moving out a lot of UI related code from C, but it also moves away from regular people: more programming and what is the worst: lots of of extra (useless) typing.

I'm still looking for the change, hoping that the current state is just the beginning of something really good.

@xray: Would you be interested in my thoughts and improving the LUA UI engine?


-geever
Title: Re: lua design thoughts
Post by: xray on August 23, 2015, 09:03:58 pm
I just found out that this is in master already... Hmm, I like it and don't, at the same time. It has great potential for moving out a lot of UI related code from C, but it also moves away from regular people: more programming and what is the worst: lots of of extra (useless) typing.[/quoute]

I had it moved in the main branch so it gets more feedback. Located in an isolated branch the development was more or less stalling. As for the design: it's lua, yes you need to program. But that's precisely why it was implemented firsthand since ufo script is not a good programming language with major drawback in writing the functions. As for the extra typing: one solution is to write boilerplate lua code that accepts some hierarchic structure defining the look of the gui and then translates it into appropriate api calls.

As it is, current implementation works side-by-side with the ufo script implementation, so at the moment anyone can pick the language he/she is most comfortable with.

I'm still looking for the change, hoping that the current state is just the beginning of something really good.
[/qoute]

I'm sure in the end it will become just that :-)

@xray: Would you be interested in my thoughts and improving the LUA UI engine?
-geever

@geever: Of course I'm open for thoughts on improving the LUA UI engine. You can PM me or post in this thread. Looking forward to your ideas!

xray
Title: Re: lua design thoughts
Post by: geever on August 24, 2015, 12:42:09 am
Cool! I started to create a "Base capacities" widget that is used in transfer and market screens, to play with the LUA scripts. It is in very early stage, doesn't worth showing the current state but found some "could be improved" parts with it already. (Disclaimer: I'm no LUA expert.)

1. Simplify initialization
    As I mentioned creating nodes is boring. Let's see, if I need 3 labels as a table header:

Code: [Select]
    local building_head = ufo.create_string(box, "building_head", nil)
    building_head:set_box(0, 20, 150, 20)
    building_head:set_font("f_very_small_bold")
    building_head:set_contentalign(ufo.ALIGN_CC)
    building_head:set_longlines(ufo.LONGLINES_PRETTYCHOP)
    building_head:set_text("_Building")

This is just one. I need 3 copies. To save my fingers I'll copy&paste&modify it. I'll have to replace building_head 7 times.

What if instead we could write:
Code: [Select]
    local building_head = ufo.create_string(box, "building_head", {
        ["super"]        = nil,
        ["box"]          = { 0, 20, 150, 20 },
        ["font"]         = "f_very_small_bold",
        ["contentalign"] = ufo.ALIGN_CC,
        ["longlines"]    = ufo.LONGLINES_PRETTYCHOP,
        ["text"]         = "_Building"
    })

Less risk in mistyping or forget to replace building_head.


Also if I have a panel and would like to create nodes inside it, it would be so natural using the panel's method to create subnodes:

Code: [Select]
    local row = ufo.create_component("panel", "cmp_basecap_line", nil)

    local building = row:create_string("building", nil)
    local freespace = row:create_string("freespace", nil)
    local allspace = row:create_string("allspace", nil)

But currently we need the long form.

-geever
Title: Re: lua design thoughts
Post by: geever on August 24, 2015, 01:05:32 am
2. Scrollbars
    Assigning scrollbars is annoying in the ufoscript too. Here is it in LUA:

Code: [Select]
    local ObjectInfo_info_description = ufo.create_text(ObjectInfo_info, "description", nil)
    ...
    ObjectInfo_info_description.on_viewchange = function (sender)
        ObjectInfo_info_descriptionscroll:set_fullsize(sender:fullsize())
        ObjectInfo_info_descriptionscroll:set_current(sender:viewpos())
    end
    ObjectInfo_info_description.on_wheel = function (sender)
        ObjectInfo_info_descriptionscroll:set_current(sender:viewpos())
    end

    local ObjectInfo_info_descriptionscroll = ufo.create_vscrollbar(ObjectInfo_info, "description_scroll", nil)
    ...
    ObjectInfo_info_descriptionscroll.on_change = function (sender)
        ObjectInfo_info_description:set_viewpos(sender:current())
    end

What if instead we added uiAbstractScrollableNode and uiAbstractScrollbarNode methods for assignment:
Code: [Select]
    local text1 = ufo.create_text(root, "text1", nil)
    local scroll1 = ufo.create_vscrollbar(root, "scroll1", nil)
    text1:assign_scrollbar(scroll1)

    local text2 = ufo.create_text(root, "text2", nil)
    local scroll2 = ufo.create_vscrollbar(root, "scroll2", nil)
    scroll2:assign_to(text2)

And it would automatically make the event handshake?

Scrollbar could be generated automatically: (using Suggestion#1)
Code: [Select]
    local text1 = ufo.create_text(root, "text1", {
        ...
        ["add_vscrollbar"] = auto    -- true | false | auto, where auto is the "hide if unused" option. It should be false by default.
    })

-geever
Title: Re: lua design thoughts
Post by: xray on August 25, 2015, 03:36:41 pm
What if instead we could write:
Code: [Select]
    local building_head = ufo.create_string(box, "building_head", {
        ["super"]        = nil,
        ["box"]          = { 0, 20, 150, 20 },
        ["font"]         = "f_very_small_bold",
        ["contentalign"] = ufo.ALIGN_CC,
        ["longlines"]    = ufo.LONGLINES_PRETTYCHOP,
        ["text"]         = "_Building"
    })

This is much in line with an idea I had about setting a group of properties using lua's native table structure. I'm going to look into this a bit more. It would look a bit like this:

Code: [Select]
local fooNode = ufo.create_button(...)
fooNode.set_properties({
  box = {0, 0, 100, 100}
  text = "_Foo button"
  ...
})

Typically it can be solved the lua way, using boilerplate code (e.g. a "set_properties" method) taking a lua table and then calling the api. Drawback is that you have to maintain two sets of interfaces. The lua-C interface and the lua-lua interace. An alternative is to add the method to the basic node that accepts a lua table, scans it and looks for properties in on the C-side. Drawback here is that the lua api has renamed some of the properties to more logical names.

xray
Title: Re: lua design thoughts
Post by: xray on August 25, 2015, 03:47:55 pm
2. Scrollbars
    Assigning scrollbars is annoying in the ufoscript too.
    ...

Problem here is that this is a flaw in the existing c-implementation of the ui, rather than a lua issue. If you look at similar implementations the actual creation of the scrollbar is delegated to the owning control. So I guess an "auto_scroll" property will need to be implemented on the c-side first. After that we have to evaluate if there is actually a need to implement a custom "on_scroll" event. However this event should not be used to scroll the parent view, since this aspect should be handled by the parent control creating the scrollbars.

Anyway, this takes some redesigning of the ui. I'll put it on the todo-list.

xray



xray
Title: Re: lua design thoughts
Post by: geever on August 26, 2015, 06:21:39 pm
Anyway, this takes some redesigning of the ui. I'll put it on the todo-list.

Thanks!

What about using our bug/feature tracker as a todo-list?

-geever
Title: Re: lua design thoughts
Post by: xray on August 27, 2015, 07:48:43 am
Thanks!

What about using our bug/feature tracker as a todo-list?

-geever

Added them as 2 improvements: 5495 and 5496.

xray
Title: Re: lua design thoughts
Post by: xray on August 30, 2015, 07:27:17 pm
Hi,

A small update. I managed to write some lua code allowing the following construct (using lua tables). It allows for a hierarchy of (nested) tables defining ui nodes to be created in a single statement. This more or less mimics the old ufo script behaviour, only then in lua. Currently the code to create a set of nodes looks like this:

Code: [Select]
node = {
_type = "panel",
_name = "pnlTest",

pos = { 10, 250 },
size = { 200, 200 },
layout = ufo.LAYOUT_TOP_DOWN_FLOW,
layoutmargin = 15,
backgroundcolor = { 0.50, 0.00, 0.00, 1.00 },

{
_type = "MainMenuBtn",
_name = "btnTest1",
text = "_Test 1"
},

{
_type = "MainMenuBtn",
_name = "btnTest2",
text = "_Test 2"
},

{
_type = "MainMenuBtn",
_name = "btnTest3",
text = "_Test 3"
}
}

...
do
wndMain = ufo.create_window ("main", nil)
wndMain:set_background ("ui/main_bg")

local pnlTest = build(node, wndMain)

        ...


The above results in the red panel on the left (see attached image). I'm going to work on this further but for now, it looks promising.

xray
Title: Re: lua design thoughts
Post by: xray on September 02, 2015, 01:54:03 pm
Hi,

I've created a pull request with an update on the lua integration of the ui. It is now possible to create windows/compones/nodes from a data structure that looks more-or-less like ufo script. Note that there are some small changes to the situation described in the previous post, they're described in the wiki.

@geever: hope this is what youre looking for  :)

xray
Title: Re: lua design thoughts
Post by: Mattn on September 17, 2015, 10:05:38 pm
Cool stuff. Would merge the pull request - but there is a merge commit in it. If you could get rid of that one, that would be nice. Otherwise I could also cherry-pick them manually.
Title: Re: lua design thoughts
Post by: xray on September 18, 2015, 08:34:53 am
... Would merge the pull request - but there is a merge commit in it. ...

How do I get rid of the merge commit?

xray
Title: Re: lua design thoughts
Post by: DarkRain on September 18, 2015, 04:56:45 pm
Doing
Code: [Select]
git rebase <ufoai master branch> <your branch>should be enough to get rid of them, but to push a rebased branch you'll need to use the --force option, and everybody who already pulled it will need to reset/rebase their local version of your branch, and so and so on
Title: Re: lua design thoughts
Post by: xray on September 19, 2015, 04:28:16 pm
Rebased the PR. Hope it is alright now.

xray