- 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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user