project-navigation
Personal tools

Author Topic: lua design thoughts  (Read 47967 times)

Offline geever

  • Project Coder
  • PHALANX Commander
  • ***
  • Posts: 2560
    • View Profile
Re: lua design thoughts
« Reply #30 on: August 16, 2014, 10:51:42 pm »
nah, no autoconversion please. The scripts need some fixes anyway.

-geever

Offline xray

  • Rookie
  • ***
  • Posts: 72
    • View Profile
Re: lua design thoughts
« Reply #31 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

Offline xray

  • Rookie
  • ***
  • Posts: 72
    • View Profile
Re: lua design thoughts
« Reply #32 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

Offline xray

  • Rookie
  • ***
  • Posts: 72
    • View Profile
Re: lua design thoughts
« Reply #33 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

Offline Mattn

  • Administrator
  • PHALANX Commander
  • *****
  • Posts: 4831
  • https://github.com/mgerhardy/vengi
    • View Profile
    • Vengi Voxel Tools
Re: lua design thoughts
« Reply #34 on: September 03, 2014, 08:37:05 pm »
just awesome. nice progress.

Offline xray

  • Rookie
  • ***
  • Posts: 72
    • View Profile
Re: lua design thoughts
« Reply #35 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

Offline Mattn

  • Administrator
  • PHALANX Commander
  • *****
  • Posts: 4831
  • https://github.com/mgerhardy/vengi
    • View Profile
    • Vengi Voxel Tools
Re: lua design thoughts
« Reply #36 on: September 05, 2014, 07:57:52 pm »
hehe - nice one. good job.

Offline xray

  • Rookie
  • ***
  • Posts: 72
    • View Profile
Re: lua design thoughts
« Reply #37 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

Offline Mattn

  • Administrator
  • PHALANX Commander
  • *****
  • Posts: 4831
  • https://github.com/mgerhardy/vengi
    • View Profile
    • Vengi Voxel Tools
Re: lua design thoughts
« Reply #38 on: September 13, 2014, 08:21:28 pm »
awesome - really cool stuff - feel free to prepare a pull request ;)

Offline Internecivus

  • Rookie
  • ***
  • Posts: 64
  • Sometimes I Code
    • View Profile
Re: lua design thoughts
« Reply #39 on: September 16, 2014, 10:38:17 am »
And of course the translating of the .ufo files into lua.

Count me in.

Offline xray

  • Rookie
  • ***
  • Posts: 72
    • View Profile
Re: lua design thoughts
« Reply #40 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

Offline Internecivus

  • Rookie
  • ***
  • Posts: 64
  • Sometimes I Code
    • View Profile
Re: lua design thoughts
« Reply #41 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.

Offline Mattn

  • Administrator
  • PHALANX Commander
  • *****
  • Posts: 4831
  • https://github.com/mgerhardy/vengi
    • View Profile
    • Vengi Voxel Tools
Re: lua design thoughts
« Reply #42 on: September 17, 2014, 09:40:21 pm »
it doesnt matter in his branch as i would squash the merge anyway into one commit

Offline Schilcote

  • Rookie
  • ***
  • Posts: 13
    • View Profile
Re: lua design thoughts
« Reply #43 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.

Offline xray

  • Rookie
  • ***
  • Posts: 72
    • View Profile
Re: lua design thoughts
« Reply #44 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