Skip to content

Commit 6304bd6

Browse files
committed
Create Lua frontend GUI code
1 parent 451127f commit 6304bd6

10 files changed

+1403
-1
lines changed

.luacheckrc

+8-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,14 @@ read_globals = {
1919
"Settings",
2020

2121
string = {fields = {"split", "trim"}},
22-
table = {fields = {"copy", "getn", "indexof", "insert_all"}},
22+
table = {fields = {
23+
"copy",
24+
"shallow_copy",
25+
"getn",
26+
"indexof",
27+
"insert_all",
28+
"merge"
29+
}},
2330
math = {fields = {"hypot", "round"}},
2431
}
2532

builtin/common/misc_helpers.lua

+43
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,15 @@ function table.copy(t, seen)
489489
end
490490

491491

492+
function table.shallow_copy(t)
493+
local new = {}
494+
for k, v in pairs(t) do
495+
new[k] = v
496+
end
497+
return new
498+
end
499+
500+
492501
function table.insert_all(t, other)
493502
for i=1, #other do
494503
t[#t + 1] = other[i]
@@ -497,6 +506,15 @@ function table.insert_all(t, other)
497506
end
498507

499508

509+
function table.merge(...)
510+
local new = {}
511+
for _, t in ipairs{...} do
512+
table.insert_all(new, t)
513+
end
514+
return new
515+
end
516+
517+
500518
function table.key_value_swap(t)
501519
local ti = {}
502520
for k,v in pairs(t) do
@@ -760,3 +778,28 @@ function core.parse_coordinates(x, y, z, relative_to)
760778
local rz = core.parse_relative_number(z, relative_to.z)
761779
return rx and ry and rz and { x = rx, y = ry, z = rz }
762780
end
781+
782+
local function call(class, ...)
783+
local obj = core.class(class)
784+
if obj.new then
785+
obj:new(...)
786+
end
787+
return obj
788+
end
789+
790+
function core.class(super)
791+
super = super or {}
792+
super.__index = super
793+
super.__call = call
794+
795+
return setmetatable({}, super)
796+
end
797+
798+
function core.is_instance(obj, class)
799+
if type(obj) ~= "table" then
800+
return false
801+
end
802+
803+
local meta = getmetatable(obj)
804+
return meta == class or core.is_instance(meta, class)
805+
end

builtin/game/init.lua

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
local scriptpath = core.get_builtin_path()
33
local commonpath = scriptpath .. "common" .. DIR_DELIM
44
local gamepath = scriptpath .. "game".. DIR_DELIM
5+
local uipath = scriptpath .. "ui" .. DIR_DELIM
56

67
-- Shared between builtin files, but
78
-- not exposed to outer context
@@ -37,6 +38,7 @@ dofile(gamepath .. "forceloading.lua")
3738
dofile(gamepath .. "statbars.lua")
3839
dofile(gamepath .. "knockback.lua")
3940
dofile(gamepath .. "async.lua")
41+
dofile(uipath .. "init.lua")
4042

4143
core.after(0, builtin_shared.cache_content_ids)
4244

builtin/ui/elem.lua

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
--[[
2+
Minetest
3+
Copyright (C) 2023 v-rob, Vincent Robinson <[email protected]>
4+
5+
This program is free software; you can redistribute it and/or modify
6+
it under the terms of the GNU Lesser General Public License as published by
7+
the Free Software Foundation; either version 2.1 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public License along
16+
with this program; if not, write to the Free Software Foundation, Inc.,
17+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+
--]]
19+
20+
ui._elem_types = {}
21+
22+
function ui._new_type(base, type, type_id)
23+
local class = core.class(base)
24+
25+
class._type = type
26+
class._type_id = type_id
27+
28+
ui._elem_types[type] = class
29+
30+
return class
31+
end
32+
33+
ui.Elem = ui._new_type(nil, "elem", 0)
34+
35+
function ui.Elem:new(props)
36+
self._id = props.id or ui.new_id()
37+
self._groups = {}
38+
self._boxes = {main = true}
39+
self._style = props.style or ui.Style{props = props}
40+
self._children = table.merge(props.children or props)
41+
42+
-- Set by parent ui.Elem
43+
self._parent = nil
44+
self._index = nil
45+
self._rindex = nil
46+
47+
-- Set by ui.Window
48+
self._window = nil
49+
50+
assert(ui.is_id(self._id), "Element ID must be an ID string")
51+
52+
for _, group in ipairs(props.groups or {}) do
53+
assert(ui.is_id(group), "Element group must be an ID string")
54+
self._groups[group] = true
55+
end
56+
57+
for i, child in ipairs(self._children) do
58+
assert(child._parent == nil, "Element already has a parent")
59+
assert(not core.is_instance(child, ui.Root),
60+
"ui.Root can only be a root element")
61+
62+
child._parent = self
63+
child._index = i
64+
child._rindex = #self._children - i + 1
65+
end
66+
end
67+
68+
function ui.Elem:_get_flat()
69+
local elems = {self}
70+
for _, child in ipairs(self._children) do
71+
table.insert_all(elems, child:_get_flat())
72+
end
73+
return elems
74+
end
75+
76+
function ui.Elem:_encode()
77+
return ui._encode("Bz S", self._type_id, self._id, self:_encode_fields())
78+
end
79+
80+
function ui.Elem:_encode_fields()
81+
local fl = ui._make_flags()
82+
83+
if ui._shift_flag(fl, #self._children > 0) then
84+
local child_ids = {}
85+
for i, child in ipairs(self._children) do
86+
child_ids[i] = child._id
87+
end
88+
89+
ui._encode_flag(fl, "Z", ui._encode_array("z", child_ids))
90+
end
91+
92+
self:_encode_box(fl, self._boxes.main)
93+
94+
return ui._encode("S", ui._encode_flags(fl))
95+
end
96+
97+
function ui.Elem:_encode_box(fl, box)
98+
-- Element encoding always happens after styles are computed and boxes are
99+
-- populated with style indices. So, if this box has any styles applied to
100+
-- it, encode the relevant states.
101+
if not ui._shift_flag(fl, box.n > 0) then
102+
return
103+
end
104+
105+
local box_fl = ui._make_flags()
106+
107+
-- For each state, check if there is any styling. If there is, add it
108+
-- to the box's flags.
109+
for i = ui._STATE_NONE, ui._NUM_STATES - 1 do
110+
if ui._shift_flag(box_fl, box[i] ~= ui._NO_STYLE) then
111+
ui._encode_flag(box_fl, "I", box[i])
112+
end
113+
end
114+
115+
ui._encode_flag(fl, "S", ui._encode_flags(box_fl))
116+
end

builtin/ui/elem_defs.lua

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
--[[
2+
Minetest
3+
Copyright (C) 2024 v-rob, Vincent Robinson <[email protected]>
4+
5+
This program is free software; you can redistribute it and/or modify
6+
it under the terms of the GNU Lesser General Public License as published by
7+
the Free Software Foundation; either version 2.1 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public License along
16+
with this program; if not, write to the Free Software Foundation, Inc.,
17+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+
--]]
19+
20+
ui.Root = ui._new_type(ui.Elem, "root", 1)
21+
22+
function ui.Root:new(props)
23+
ui.Elem.new(self, props)
24+
25+
self._boxes.backdrop = true
26+
end
27+
28+
function ui.Root:_encode_fields()
29+
local fl = ui._make_flags()
30+
31+
self:_encode_box(fl, self._boxes.backdrop)
32+
33+
return ui._encode("ZS", ui.Elem._encode_fields(self), ui._encode_flags(fl))
34+
end
35+
36+
ui.Flex = ui._new_type(ui.Elem, "flex", 2)
37+
38+
function ui.Flex:new(props)
39+
ui.Elem.new(self, props)
40+
41+
self._dir = props.dir
42+
self._wrap = props.wrap
43+
end
44+
45+
local dir_map = {left = 0, up = 1, right = 2, down = 3};
46+
local wrap_map = {none = 0, forward = 1, backward = 2}
47+
48+
function ui.Flex:_encode_fields()
49+
local fl = ui._make_flags()
50+
51+
if ui._shift_flag(fl, self._dir) then
52+
ui._encode_flag(fl, "B", dir_map[self._dir])
53+
end
54+
if ui._shift_flag(fl, self._wrap) then
55+
ui._encode_flag(fl, "B", wrap_map[self._wrap])
56+
end
57+
58+
return ui._encode("ZS", ui.Elem._encode_fields(self), ui._encode_flags(fl))
59+
end
60+
61+
ui.Grid = ui._new_type(ui.Elem, "grid", 3)
62+
63+
function ui.Grid:new(props)
64+
ui.Elem.new(self, props)
65+
66+
self._hsizes = table.merge(props.hsizes or {})
67+
self._vsizes = table.merge(props.vsizes or {})
68+
69+
self._hweights = table.merge(props.hweights or {})
70+
self._vweights = table.merge(props.vweights or {})
71+
end
72+
73+
function ui.Grid:_encode_fields()
74+
local fl = ui._make_flags()
75+
76+
if ui._shift_flag(fl, #self._hsizes > 0) then
77+
ui._encode_flag(fl, "Z", ui._encode_array("f", self._hsizes))
78+
end
79+
if ui._shift_flag(fl, #self._vsizes > 0) then
80+
ui._encode_flag(fl, "Z", ui._encode_array("f", self._vsizes))
81+
end
82+
83+
if ui._shift_flag(fl, #self._hweights > 0) then
84+
ui._encode_flag(fl, "Z", ui._encode_array("f", self._hweights))
85+
end
86+
if ui._shift_flag(fl, #self._vweights > 0) then
87+
ui._encode_flag(fl, "Z", ui._encode_array("f", self._vweights))
88+
end
89+
90+
return ui._encode("ZS", ui.Elem._encode_fields(self), ui._encode_flags(fl))
91+
end

builtin/ui/init.lua

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--[[
2+
Minetest
3+
Copyright (C) 2023 v-rob, Vincent Robinson <[email protected]>
4+
5+
This program is free software; you can redistribute it and/or modify
6+
it under the terms of the GNU Lesser General Public License as published by
7+
the Free Software Foundation; either version 2.1 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public License along
16+
with this program; if not, write to the Free Software Foundation, Inc.,
17+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+
--]]
19+
20+
ui = {}
21+
22+
local UI_PATH = core.get_builtin_path() .. "ui" .. DIR_DELIM
23+
24+
dofile(UI_PATH .. "util.lua")
25+
dofile(UI_PATH .. "selector.lua")
26+
dofile(UI_PATH .. "style.lua")
27+
dofile(UI_PATH .. "elem.lua")
28+
dofile(UI_PATH .. "window.lua")
29+
dofile(UI_PATH .. "elem_defs.lua")

0 commit comments

Comments
 (0)