Add core library and element classes for Basalt framework

This commit is contained in:
Robert Jelic
2025-02-09 13:40:17 +01:00
parent acc5949085
commit 1d31fb8d0c
16 changed files with 1329 additions and 0 deletions

141
src/propertySystem.lua Normal file
View File

@@ -0,0 +1,141 @@
local deepCopy = require("libraries/utils").deepCopy
local expect = require("libraries/expect")
local PropertySystem = {}
PropertySystem.__index = PropertySystem
PropertySystem._properties = {}
function PropertySystem.defineProperty(class, name, config)
if not rawget(class, '_properties') then
class._properties = {}
end
class._properties[name] = {
type = config.type,
default = config.default,
canTriggerRender = config.canTriggerRender,
getter = config.getter,
setter = config.setter,
}
local capitalizedName = name:sub(1,1):upper() .. name:sub(2)
class["get" .. capitalizedName] = function(self)
expect(1, self, "element")
local value = self._values[name]
return config.getter and config.getter(value) or value
end
class["set" .. capitalizedName] = function(self, value)
expect(1, self, "element")
expect(2, value, config.type)
if config.setter then
value = config.setter(self, value)
end
self:_updateProperty(name, value)
return self
end
end
function PropertySystem:__init()
self._values = {}
self._observers = {}
self.set = function(name, value)
local oldValue = self._values[name]
self._values[name] = value
if(self._properties[name].setter) then
value = self._properties[name].setter(self, value)
end
if oldValue ~= value and self._observers[name] then
for _, callback in ipairs(self._observers[name]) do
callback(self, value, oldValue)
end
end
end
self.get = function(name)
return self._values[name]
end
local properties = {}
local currentClass = getmetatable(self).__index
while currentClass do
if rawget(currentClass, '_properties') then
for name, config in pairs(currentClass._properties) do
if not properties[name] then
properties[name] = config
end
end
end
currentClass = getmetatable(currentClass) and rawget(getmetatable(currentClass), '__index')
end
self._properties = properties
local originalMT = getmetatable(self)
local originalIndex = originalMT.__index
setmetatable(self, {
__index = function(t, k)
if self._properties[k] then
return self._values[k]
end
if type(originalIndex) == "function" then
return originalIndex(t, k)
else
return originalIndex[k]
end
end,
__newindex = function(t, k, v)
if self._properties[k] then
if self._properties[k].setter then
v = self._properties[k].setter(self, v)
end
self:_updateProperty(k, v)
else
rawset(t, k, v)
end
end,
__tostring = function(self)
return string.format("Object: %s (id: %s)", self._values.type, self.id)
end
})
for name, config in pairs(properties) do
if self._values[name] == nil then
if type(config.default) == "table" then
self._values[name] = deepCopy(config.default)
else
self._values[name] = config.default
end
end
end
return self
end
function PropertySystem:_updateProperty(name, value)
local oldValue = self._values[name]
if oldValue ~= value then
self._values[name] = value
if self._properties[name].canTriggerRender then
self:updateRender()
end
if self._observers[name] then
for _, callback in ipairs(self._observers[name]) do
callback(self, value, oldValue)
end
end
end
end
function PropertySystem:observe(name, callback)
self._observers[name] = self._observers[name] or {}
table.insert(self._observers[name], callback)
return self
end
return PropertySystem