- added List

- added checkbox
- added program
- added slider
- added progressbar
- added reactive (dynamicValues)
smaller bug fixxes
This commit is contained in:
Robert Jelic
2025-02-14 14:40:20 +01:00
parent 6dfa554523
commit b7f22bf63f
22 changed files with 1021 additions and 76 deletions

View File

@@ -312,6 +312,7 @@ function VisualElement.hooks.dispatchEvent(self, event, ...)
end
function VisualElement.setup(element)
VisualElementBaseDispatchEvent = element.dispatchEvent
element.defineProperty(element, "animation", {default = nil, type = "table"})
element.listenTo(element, "timer")
end

View File

@@ -1,32 +1,173 @@
local function setupReactiveProperty(element, propertyName, expression)
local errorManager = require("errorManager")
local PropertySystem = require("propertySystem")
local log = require("log")
end
local protectedNames = {
colors = true,
math = true,
clamp = true,
round = true
}
local function createReactiveFunction(expression, scope)
local code = expression:gsub(
"(%w+)%s*%?%s*([^:]+)%s*:%s*([^}]+)",
"%1 and %2 or %3"
)
local mathEnv = {
clamp = function(val, min, max)
return math.min(math.max(val, min), max)
end,
round = function(val)
return math.floor(val + 0.5)
end
}
return load(string.format([[
return function(self)
return %s
local function parseExpression(expr, element)
expr = expr:gsub("^{(.+)}$", "%1")
for k,v in pairs(colors) do
if type(k) == "string" then
expr = expr:gsub("%f[%w]"..k.."%f[%W]", "colors."..k)
end
]], code), "reactive", "t", scope)()
end
expr = expr:gsub("([%w_]+)%.([%w_]+)", function(obj, prop)
if protectedNames[obj] then
return obj.."."..prop
end
return string.format('__getProperty("%s", "%s")', obj, prop)
end)
local env = setmetatable({
colors = colors,
math = math,
__getProperty = function(objName, propName)
if objName == "self" then
return element.get(propName)
elseif objName == "parent" then
return element.parent.get(propName)
else
local target = element:getBaseFrame():getChild(objName)
if not target then
errorManager.header = "Reactive evaluation error"
errorManager.error("Could not find element: " .. objName)
return nil
end
return target.get(propName)
end
end
}, { __index = mathEnv })
local func, err = load("return "..expr, "reactive", "t", env)
if not func then
errorManager.header = "Reactive evaluation error"
errorManager.error("Invalid expression: " .. err)
return function() return nil end
end
return func
end
local function validateReferences(expr, element)
for ref in expr:gmatch("([%w_]+)%.") do
if not protectedNames[ref] then
if ref == "parent" then
if not element.parent then
errorManager.header = "Reactive evaluation error"
errorManager.error("No parent element available")
return false
end
else
local target = element:getBaseFrame():getChild(ref)
if not target then
errorManager.header = "Reactive evaluation error"
errorManager.error("Referenced element not found: " .. ref)
return false
end
end
end
end
return true
end
local functionCache = {}
local observerCache = setmetatable({}, {__mode = "k"})
local function setupObservers(element, expr)
if observerCache[element] then
for _, observer in ipairs(observerCache[element]) do
observer.target:removeObserver(observer.property, observer.callback)
end
end
local observers = {}
for ref, prop in expr:gmatch("([%w_]+)%.([%w_]+)") do
if not protectedNames[ref] then
local target
if ref == "self" then
target = element
elseif ref == "parent" then
target = element.parent
else
target = element:getBaseFrame():getChild(ref)
end
if target then
local observer = {
target = target,
property = prop,
callback = function()
element:updateRender()
end
}
target:observe(prop, observer.callback)
table.insert(observers, observer)
end
end
end
observerCache[element] = observers
end
PropertySystem.addSetterHook(function(element, propertyName, value, config)
if type(value) == "string" and value:match("^{.+}$") then
local expr = value:gsub("^{(.+)}$", "%1")
if not validateReferences(expr, element) then
return config.default
end
setupObservers(element, expr)
if not functionCache[value] then
local parsedFunc = parseExpression(value, element)
functionCache[value] = parsedFunc
end
return function(self)
local success, result = pcall(functionCache[value])
if not success then
errorManager.header = "Reactive evaluation error"
if type(result) == "string" then
errorManager.error("Error evaluating expression: " .. result)
else
errorManager.error("Error evaluating expression")
end
return config.default
end
return result
end
end
end)
local BaseElement = {}
function BaseElement:setReactiveProperty(propertyName, expression)
setupReactiveProperty(self, propertyName, expression)
return self
end
function BaseElement:setReactive(propertyName, expression)
local reactiveFunc = createReactiveFunction(expression, self)
self.set(propertyName, reactiveFunc)
return self
end
BaseElement.hooks = {
destroy = function(self)
if observerCache[self] then
for _, observer in ipairs(observerCache[self]) do
observer.target:observe(observer.property, observer.callback)
end
observerCache[self] = nil
end
end
}
return {
BaseElement = BaseElement

99
src/plugins/theme.lua Normal file
View File

@@ -0,0 +1,99 @@
-- Has to be reworked
local Theme = {}
local defaultTheme = {
colors = {
primary = colors.blue,
secondary = colors.cyan,
background = colors.black,
text = colors.white,
borders = colors.gray,
error = colors.red,
success = colors.green,
},
elementStyles = {
Button = {
background = "background",
foreground = "text",
activeBackground = "primary",
activeForeground = "text",
},
Input = {
background = "background",
foreground = "text",
focusBackground = "primary",
focusForeground = "text",
},
Frame = {
background = "background",
foreground = "text"
}
}
}
local themes = {
default = defaultTheme
}
local currentTheme = "default"
function Theme.registerTheme(name, theme)
themes[name] = theme
end
local function resolveThemeValue(value, theme)
if type(value) == "string" and theme.colors[value] then
return theme.colors[value]
end
return value
end
local BaseElement = {
hooks = {
init = function(self)
-- Theme Properties für das Element registrieren
self.defineProperty(self, "theme", {
default = currentTheme,
type = "string",
setter = function(self, value)
self:applyTheme(value)
return value
end
})
end
}
}
function BaseElement:applyTheme(themeName)
local theme = themes[themeName] or themes.default
local elementType = self.get("type")
if theme.elementStyles[elementType] then
local styles = theme.elementStyles[elementType]
for prop, value in pairs(styles) do
if self:getPropertyConfig(prop) then
self.set(prop, resolveThemeValue(value, theme))
end
end
end
end
local Container = {
hooks = {
addChild = function(self, child)
if self.get("themeInherit") then
child.set("theme", self.get("theme"))
end
end
}
}
function Container.setup(element)
element.defineProperty(element, "themeInherit", {default = true, type = "boolean"})
end
return {
BaseElement = BaseElement,
Container = Container,
}