1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00
luanti/builtin/ui/elem.lua

158 lines
3.8 KiB
Lua
Raw Normal View History

2024-01-23 22:54:45 -08:00
-- Luanti
-- SPDX-License-Identifier: LGPL-2.1-or-later
-- Copyright (C) 2023 v-rob, Vincent Robinson <robinsonvincent89@gmail.com>
ui._elem_types = {}
function ui._new_type(base, type, type_id, id_required)
local class = core.class(base)
class._type = type
class._type_id = type_id
class._id_required = id_required
class._handlers = setmetatable({}, {__index = base and base._handlers})
2024-01-23 22:54:45 -08:00
ui._elem_types[type] = class
return class
end
function ui.derive_elem(base, type)
assert(core.is_subclass(base, ui.Elem))
ui._req(type, "id")
assert(not ui._elem_types[type],
"Derived element name already used: '" .. type .. "'")
return ui._new_type(base, type, base._type_id, base._id_required)
end
ui.Elem = ui._new_type(nil, "elem", 0x00, false)
function ui.Elem:new(param)
local function make_elem(props)
self:_init(ui._req(props, "table"))
return self
end
if type(param) == "string" then
self._id = ui._req(param, "id")
return make_elem
end
self._id = ui.new_id()
return make_elem(param)
end
function ui.Elem:_init(props)
self._groups = {}
self._children = {}
self._props = ui._cascade_props(props.props or props, {})
self._styles = {}
-- Set by parent ui.Elem
self._parent = nil
-- Set by ui.Window
self._boxes = {main = true}
self._window = nil
if self._id_required then
assert(not ui._is_reserved_id(self._id),
"Element ID is required for '" .. self._type .. "'")
end
for _, group in ipairs(ui._opt_array(props.groups, "id", {})) do
self._groups[group] = true
end
for _, child in ipairs(ui._opt_array(props.children, ui.Elem, props)) do
if core.is_instance(child, ui.Elem) then
assert(child._parent == nil,
"Element '" .. child._id .. "' already has a parent")
assert(not core.is_instance(child, ui.Root),
"ui.Root may not be a child element")
child._parent = self
table.insert(self._children, child)
end
end
for _, style in ipairs(ui._opt_array(props.styles, ui.Style, props)) do
if core.is_instance(style, ui.Style) then
table.insert(self._styles, style)
end
end
for _, item in ipairs(props) do
assert(core.is_instance(item, ui.Elem) or core.is_instance(item, ui.Style))
end
end
function ui.Elem:_get_flat()
local elems = {self}
for _, child in ipairs(self._children) do
table.insert_all(elems, child:_get_flat())
end
return elems
end
function ui.Elem:_encode()
return ui._encode("Bz S", self._type_id, self._id, self:_encode_fields())
end
function ui.Elem:_encode_fields()
local fl = ui._make_flags()
if ui._shift_flag(fl, #self._children > 0) then
local child_ids = {}
for i, child in ipairs(self._children) do
child_ids[i] = child._id
end
ui._encode_flag(fl, "Z", ui._encode_array("z", child_ids))
end
self:_encode_box(fl, self._boxes.main)
return ui._encode_flags(fl)
end
function ui.Elem:_encode_box(fl, box)
-- Element encoding always happens after styles are computed and boxes are
-- populated with style indices. So, if this box has any styles applied to
-- it, encode the relevant states.
if not ui._shift_flag(fl, box.n > 0) then
return
end
local box_fl = ui._make_flags()
-- For each state, check if there is any styling. If there is, add it
-- to the box's flags.
for i = ui._STATE_NONE, ui._NUM_STATES - 1 do
if ui._shift_flag(box_fl, box[i] ~= ui._NO_STYLE) then
ui._encode_flag(box_fl, "I", box[i])
end
end
ui._encode_flag(fl, "s", ui._encode_flags(box_fl))
end
function ui.Elem:_on_event(code, ev, data)
-- Get the handler function for this event if we recognize it.
local handler = self._handlers[code]
if not handler then
core.log("info", "Invalid event for " .. self._type_id .. ": " .. code)
return
end
-- If the event handler returned a callback function for the user, call it
-- with the event table.
local callback = handler(self, ev, data)
if callback then
callback(ev)
end
end