- Created Plugin loading system

- Added lazy loading system for elements (optional feature)
- Improved rendering performance
- Added ID system which is separated from Eement Names
- Added Focussystem for container
- Improved container performance by only rendering and handling events from visible childrens instead of all
- Added label and input
- Added animation and xml
This commit is contained in:
Robert Jelic
2025-02-13 10:51:12 +01:00
parent bca8889fbd
commit 6dfa554523
23 changed files with 1833 additions and 494 deletions

View File

@@ -1,6 +1,7 @@
local VisualElement = require("elements/VisualElement")
local elementManager = require("elementManager")
local VisualElement = elementManager.getElement("VisualElement")
local expect = require("libraries/expect")
local split = require("libraries/utils").split
local max = math.max
@@ -9,65 +10,129 @@ local Container = setmetatable({}, VisualElement)
Container.__index = Container
Container.defineProperty(Container, "children", {default = {}, type = "table"})
Container.defineProperty(Container, "childrenSorted", {default = true, type = "boolean"})
Container.defineProperty(Container, "childrenEventsSorted", {default = true, type = "boolean"})
Container.defineProperty(Container, "childrenEvents", {default = {}, type = "table"})
Container.defineProperty(Container, "eventListenerCount", {default = {}, type = "table"})
Container.defineProperty(Container, "focusedChild", {default = nil, type = "table", setter = function(self, value, internal)
local oldChild = self._values.focusedChild
if value == oldChild then return value end
if oldChild then
if oldChild:isType("Container") then
oldChild.set("focusedChild", nil, true)
end
oldChild.set("focused", false, true)
end
if value and not internal then
value.set("focused", true, true)
if self.parent then
self.parent:setFocusedChild(self)
end
end
return value
end})
Container.defineProperty(Container, "visibleChildren", {default = {}, type = "table"})
Container.defineProperty(Container, "visibleChildrenEvents", {default = {}, type = "table"})
function Container:isChildVisible(child)
local childX, childY = child.get("x"), child.get("y")
local childW, childH = child.get("width"), child.get("height")
local containerW, containerH = self.get("width"), self.get("height")
return childX <= containerW and
childY <= containerH and
childX + childW > 0 and
childY + childH > 0
end
for k, _ in pairs(elementManager:getElementList()) do
local capitalizedName = k:sub(1,1):upper() .. k:sub(2)
--if not capitalizedName == "BaseFrame" then
if capitalizedName ~= "BaseFrame" then
Container["add"..capitalizedName] = function(self, ...)
expect(1, self, "table")
local element = self.basalt.create(k, ...)
self.basalt.LOGGER.debug(capitalizedName.." created with ID: " .. element.id)
self:addChild(element)
return element
end
--end
Container["addDelayed"..capitalizedName] = function(self, prop)
expect(1, self, "table")
local element = self.basalt.create(k, prop, true, self)
return element
end
end
end
---@diagnostic disable-next-line: duplicate-set-field
function Container.new(id, basalt)
function Container.new(props, basalt)
local self = setmetatable({}, Container):__init()
self:init(id, basalt)
self.set("type", "Container")
self:init(props, basalt)
return self
end
function Container:init(props, basalt)
VisualElement.init(self, props, basalt)
self.set("type", "Container")
end
function Container:addChild(child)
if child == self then
error("Cannot add container to itself")
end
local childZ = child.get("z")
local pos = 1
for i, existing in ipairs(self._values.children) do
if existing.get("z") > childZ then
break
end
pos = i + 1
end
table.insert(self._values.children, pos, child)
table.insert(self._values.children, child)
child.parent = self
self.set("childrenSorted", false)
self:registerChildrenEvents(child)
return self
end
local function sortAndFilterChildren(self, children)
local visibleChildren = {}
for _, child in ipairs(children) do
if self:isChildVisible(child) then
table.insert(visibleChildren, child)
end
end
for i = 2, #visibleChildren do
local current = visibleChildren[i]
local currentZ = current.get("z")
local j = i - 1
while j > 0 do
local compare = visibleChildren[j].get("z")
if compare > currentZ then
visibleChildren[j + 1] = visibleChildren[j]
j = j - 1
else
break
end
end
visibleChildren[j + 1] = current
end
return visibleChildren
end
function Container:sortChildren()
table.sort(self._values.children, function(a, b)
return a.get("z") < b.get("z")
end)
self.set("visibleChildren", sortAndFilterChildren(self, self._values.children))
self.set("childrenSorted", true)
end
function Container:sortChildrenEvents(eventName)
if self._values.childrenEvents[eventName] then
table.sort(self._values.childrenEvents[eventName], function(a, b)
return a.get("z") > b.get("z")
end)
self._values.visibleChildrenEvents[eventName] = sortAndFilterChildren(self, self._values.childrenEvents[eventName])
end
self.set("childrenEventsSorted", true)
end
function Container:registerChildrenEvents(child)
if(child._registeredEvents == nil)then return end
for event in pairs(child._registeredEvents) do
self:registerChildEvent(child, event)
end
@@ -89,20 +154,13 @@ function Container:registerChildEvent(child, eventName)
end
end
local childZ = child.get("z")
local pos = 1
for i, existing in ipairs(self._values.childrenEvents[eventName]) do
if existing.get("z") < childZ then
break
end
pos = i + 1
end
table.insert(self._values.childrenEvents[eventName], pos, child)
self.set("childrenEventsSorted", false)
table.insert(self._values.childrenEvents[eventName], child)
self._values.eventListenerCount[eventName] = self._values.eventListenerCount[eventName] + 1
end
function Container:removeChildrenEvents(child)
if(child._registeredEvents == nil)then return end
for event in pairs(child._registeredEvents) do
self:unregisterChildEvent(child, event)
end
@@ -130,45 +188,98 @@ function Container:unregisterChildEvent(child, eventName)
end
function Container:removeChild(child)
for i,v in ipairs(self.children) do
for i,v in ipairs(self._values.children) do
if v == child then
table.remove(self._values.children, i)
child.parent = nil
break
end
end
self:removeChildrenEvents(child)
return self
end
function Container:getChild(path)
if type(path) == "string" then
local parts = split(path, "/")
for _,v in pairs(self._values.children) do
if v.get("name") == parts[1] then
if #parts == 1 then
return v
else
if(v:isType("Container"))then
return v:find(table.concat(parts, "/", 2))
end
end
end
end
end
return nil
end
local function convertMousePosition(self, event, ...)
local args = {...}
if event:find("mouse_") then
local button, absX, absY = ...
local relX, relY = self:getRelativePosition(absX, absY)
args = {button, relX, relY}
end
return args
end
local function callChildrenEvents(self, visibleOnly, event, ...)
local children = visibleOnly and self.get("visibleChildrenEvents") or self.get("childrenEvents")
if children[event] then
local events = children[event]
for i = #events, 1, -1 do
local child = events[i]
if(child:dispatchEvent(event, ...))then
return true, child
end
end
end
return false
end
function Container:handleEvent(event, ...)
if(VisualElement.handleEvent(self, event, ...))then
local args = {...}
if event:find("mouse_") then
local button, absX, absY = ...
local relX, relY = self:getRelativePosition(absX, absY)
args = {button, relX, relY}
end
if self._values.childrenEvents[event] then
for _, child in ipairs(self._values.childrenEvents[event]) do
if(child:handleEvent(event, table.unpack(args)))then
return true
end
end
end
local args = convertMousePosition(self, event, ...)
return callChildrenEvents(self, false, event, table.unpack(args))
end
end
--[[function Container:mouse_click(button, x, y)
function Container:mouse_click(button, x, y)
if VisualElement.mouse_click(self, button, x, y) then
if self._values.childrenEvents.mouse_click then
for _, child in ipairs(self._values.childrenEvents.mouse_click) do
if child:mouse_click(button, x, y) then
return true
end
end
local args = convertMousePosition(self, "mouse_click", button, x, y)
local success, child = callChildrenEvents(self, true, "mouse_click", table.unpack(args))
if(success)then
self.set("focusedChild", child)
return true
end
self.set("focusedChild", nil)
end
end]]
end
function Container:key(key)
if self.get("focusedChild") then
return self.get("focusedChild"):dispatchEvent("key", key)
end
return true
end
function Container:char(char)
if self.get("focusedChild") then
return self.get("focusedChild"):dispatchEvent("char", char)
end
return true
end
function Container:key_up(key)
if self.get("focusedChild") then
return self.get("focusedChild"):dispatchEvent("key_up", key)
end
return true
end
function Container:multiBlit(x, y, width, height, text, fg, bg)
local w, h = self.get("width"), self.get("height")
@@ -194,10 +305,17 @@ function Container:textFg(x, y, text, fg)
VisualElement.textFg(self, math.max(1, x), math.max(1, y), text:sub(textStart, textStart + textLen - 1), fg)
end
---@diagnostic disable-next-line: duplicate-set-field
function Container:render()
VisualElement.render(self)
for _, child in ipairs(self._values.children) do
if not self.get("childrenSorted")then
self:sortChildren()
end
if not self.get("childrenEventsSorted")then
for event in pairs(self._values.childrenEvents) do
self:sortChildrenEvents(event)
end
end
for _, child in ipairs(self.get("visibleChildren")) do
if child == self then
self.basalt.LOGGER.error("CIRCULAR REFERENCE DETECTED!")
return