Skip to content

Commit 5ec5d9d

Browse files
committed
Create Lua frontend GUI code
1 parent 7f7b80e commit 5ec5d9d

11 files changed

+1387
-2
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+
"getn",
25+
"indexof",
26+
"insert_all",
27+
"merge",
28+
"shallow_copy",
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

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
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, id_required)
23+
local class = core.class(base)
24+
25+
class._type = type
26+
class._type_id = type_id
27+
class._id_required = id_required
28+
29+
ui._elem_types[type] = class
30+
31+
return class
32+
end
33+
34+
function ui.derive_elem(base, type)
35+
assert(not ui._elem_types[type], "Derived element name already used")
36+
return ui._new_type(base, type, base._type_id, base._id_required)
37+
end
38+
39+
ui.Elem = ui._new_type(nil, "elem", 0x00, false)
40+
41+
ui.Label = ui.derive_elem(ui.Elem, "label")
42+
ui.Image = ui.derive_elem(ui.Elem, "image")
43+
44+
function ui.Elem:new(props)
45+
if self._id_required then
46+
assert(ui.is_id(props.id), "Element type requires ID")
47+
end
48+
49+
self._id = props.id or ui.new_id()
50+
self._groups = {}
51+
self._boxes = {main = true}
52+
self._style = props.style or ui.Style{props = props}
53+
self._children = table.merge(props.children or props)
54+
55+
-- Set by parent ui.Elem
56+
self._parent = nil
57+
self._index = nil
58+
self._rindex = nil
59+
60+
-- Set by ui.Window
61+
self._window = nil
62+
63+
assert(ui.is_id(self._id), "Element ID must be an ID string")
64+
65+
for _, group in ipairs(props.groups or {}) do
66+
assert(ui.is_id(group), "Element group must be an ID string")
67+
self._groups[group] = true
68+
end
69+
70+
for i, child in ipairs(self._children) do
71+
assert(child._parent == nil, "Element already has a parent")
72+
assert(not core.is_instance(child, ui.Root),
73+
"ui.Root can only be a root element")
74+
75+
child._parent = self
76+
child._index = i
77+
child._rindex = #self._children - i + 1
78+
end
79+
end
80+
81+
function ui.Elem:_get_flat()
82+
local elems = {self}
83+
for _, child in ipairs(self._children) do
84+
table.insert_all(elems, child:_get_flat())
85+
end
86+
return elems
87+
end
88+
89+
function ui.Elem:_encode()
90+
return ui._encode("Bz S", self._type_id, self._id, self:_encode_fields())
91+
end
92+
93+
function ui.Elem:_encode_fields()
94+
local fl = ui._make_flags()
95+
96+
if ui._shift_flag(fl, #self._children > 0) then
97+
local child_ids = {}
98+
for i, child in ipairs(self._children) do
99+
child_ids[i] = child._id
100+
end
101+
102+
ui._encode_flag(fl, "Z", ui._encode_array("z", child_ids))
103+
end
104+
105+
self:_encode_box(fl, self._boxes.main)
106+
107+
return ui._encode("S", ui._encode_flags(fl))
108+
end
109+
110+
function ui.Elem:_encode_box(fl, box)
111+
-- Element encoding always happens after styles are computed and boxes are
112+
-- populated with style indices. So, if this box has any styles applied to
113+
-- it, encode the relevant states.
114+
if not ui._shift_flag(fl, box.n > 0) then
115+
return
116+
end
117+
118+
local box_fl = ui._make_flags()
119+
120+
-- For each state, check if there is any styling. If there is, add it
121+
-- to the box's flags.
122+
for i = ui._STATE_NONE, ui._NUM_STATES - 1 do
123+
if ui._shift_flag(box_fl, box[i] ~= ui._NO_STYLE) then
124+
ui._encode_flag(box_fl, "I", box[i])
125+
end
126+
end
127+
128+
ui._encode_flag(fl, "s", ui._encode_flags(box_fl))
129+
end

builtin/ui/elem_defs.lua

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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", 0x01, false)
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

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)