9.9 KiB
local elementManager = require("elementManager") local Container = elementManager.getElement("Container")
---@class Flexbox : Container local Flexbox = setmetatable({}, Container) Flexbox.__index = Flexbox
Flexbox.defineProperty(Flexbox, "flexDirection", {default = "row", type = "string"}) Flexbox.defineProperty(Flexbox, "flexSpacing", {default = 1, type = "number"}) Flexbox.defineProperty(Flexbox, "flexJustifyContent", { default = "flex-start", type = "string", setter = function(self, value) if not value:match("^flex%-") then value = "flex-" .. value end return value end }) Flexbox.defineProperty(Flexbox, "flexWrap", {default = false, type = "boolean"}) Flexbox.defineProperty(Flexbox, "flexUpdateLayout", {default = false, type = "boolean"})
local lineBreakElement = { getHeight = function(self) return 0 end, getWidth = function(self) return 0 end, getZ = function(self) return 1 end, getPosition = function(self) return 0, 0 end, getSize = function(self) return 0, 0 end, isType = function(self) return false end, getType = function(self) return "lineBreak" end, getName = function(self) return "lineBreak" end, setPosition = function(self) end, setParent = function(self) end, setSize = function(self) end, getFlexGrow = function(self) return 0 end, getFlexShrink = function(self) return 0 end, getFlexBasis = function(self) return 0 end, init = function(self) end, getVisible = function(self) return true end, }
local function sortElements(self, direction, spacing, wrap) local elements = self.get("children") local sortedElements = {} if not(wrap)then local index = 1 local lineSize = 1 local lineOffset = 1 for _,v in pairs(elements)do if(sortedElements[index]==nil)then sortedElements[index]={offset=1} end
local childHeight = direction == "row" and v.get("height") or v.get("width")
if childHeight > lineSize then
lineSize = childHeight
end
if(v == lineBreakElement)then
lineOffset = lineOffset + lineSize + spacing
lineSize = 1
index = index + 1
sortedElements[index] = {offset=lineOffset}
else
table.insert(sortedElements[index], v)
end
end
elseif(wrap)then
local lineSize = 1
local lineOffset = 1
local maxSize = direction == "row" and self.get("width") or self.get("height")
local usedSize = 0
local index = 1
for _,v in pairs(elements) do
if(sortedElements[index]==nil) then sortedElements[index]={offset=1} end
if v:getType() == "lineBreak" then
lineOffset = lineOffset + lineSize + spacing
usedSize = 0
lineSize = 1
index = index + 1
sortedElements[index] = {offset=lineOffset}
else
local objSize = direction == "row" and v.get("width") or v.get("height")
if(objSize+usedSize<=maxSize) then
table.insert(sortedElements[index], v)
usedSize = usedSize + objSize + spacing
else
lineOffset = lineOffset + lineSize + spacing
lineSize = direction == "row" and v.get("height") or v.get("width")
index = index + 1
usedSize = objSize + spacing
sortedElements[index] = {offset=lineOffset, v}
end
local childHeight = direction == "row" and v.get("height") or v.get("width")
if childHeight > lineSize then
lineSize = childHeight
end
end
end
end return sortedElements end
local function calculateRow(self, children, spacing, justifyContent) local containerWidth = self.get("width")
local usedSpace = spacing * (#children - 1)
local totalFlexGrow = 0
for _, child in ipairs(children) do
if child ~= lineBreakElement then
usedSpace = usedSpace + child.get("width")
totalFlexGrow = totalFlexGrow + child.get("flexGrow")
end
end
local remainingSpace = containerWidth - usedSpace
local extraSpacePerUnit = totalFlexGrow > 0 and (remainingSpace / totalFlexGrow) or 0
local distributedSpace = 0
local currentX = 1
for i, child in ipairs(children) do
if child ~= lineBreakElement then
local childWidth = child.get("width")
if child.get("flexGrow") > 0 then
if i == #children then
local extraSpace = remainingSpace - distributedSpace
childWidth = childWidth + extraSpace
else
local extraSpace = math.floor(extraSpacePerUnit * child.get("flexGrow"))
childWidth = childWidth + extraSpace
distributedSpace = distributedSpace + extraSpace
end
end
child.set("x", currentX)
child.set("y", children.offset or 1)
child.set("width", childWidth)
currentX = currentX + childWidth + spacing
end
end
if justifyContent == "flex-end" then
local offset = containerWidth - (currentX - spacing - 1)
for _, child in ipairs(children) do
child.set("x", child.get("x") + offset)
end
elseif justifyContent == "flex-center" or justifyContent == "center" then -- Akzeptiere beide Formate
local offset = math.floor((containerWidth - (currentX - spacing - 1)) / 2)
for _, child in ipairs(children) do
child.set("x", child.get("x") + offset)
end
end
end
local function calculateColumn(self, children, spacing, justifyContent) local containerHeight = self.get("height")
local usedSpace = spacing * (#children - 1)
local totalFlexGrow = 0
for _, child in ipairs(children) do
if child ~= lineBreakElement then
usedSpace = usedSpace + child.get("height")
totalFlexGrow = totalFlexGrow + child.get("flexGrow")
end
end
local remainingSpace = containerHeight - usedSpace
local extraSpacePerUnit = totalFlexGrow > 0 and (remainingSpace / totalFlexGrow) or 0
local distributedSpace = 0
local currentY = 1
for i, child in ipairs(children) do
if child ~= lineBreakElement then
local childHeight = child.get("height")
if child.get("flexGrow") > 0 then
if i == #children then
local extraSpace = remainingSpace - distributedSpace
childHeight = childHeight + extraSpace
else
local extraSpace = math.floor(extraSpacePerUnit * child.get("flexGrow"))
childHeight = childHeight + extraSpace
distributedSpace = distributedSpace + extraSpace
end
end
child.set("x", children.offset or 1)
child.set("y", currentY)
child.set("height", childHeight)
currentY = currentY + childHeight + spacing
end
end
if justifyContent == "flex-end" then
local offset = containerHeight - (currentY - spacing - 1)
for _, child in ipairs(children) do
child.set("y", child.get("y") + offset)
end
elseif justifyContent == "flex-center" or justifyContent == "center" then -- Akzeptiere beide Formate
local offset = math.floor((containerHeight - (currentY - spacing - 1)) / 2)
for _, child in ipairs(children) do
child.set("y", child.get("y") + offset)
end
end
end
local function updateLayout(self, direction, spacing, justifyContent, wrap) local elements = sortElements(self, direction, spacing, wrap) if direction == "row" then for _,v in pairs(elements)do calculateRow(self, v, spacing, justifyContent) end else for _,v in pairs(elements)do calculateColumn(self, v, spacing, justifyContent) end end self.set("flexUpdateLayout", false) end
--- Creates a new Flexbox instance --- @return Flexbox object The newly created Flexbox instance --- @usage local element = Flexbox.new("myId", basalt) function Flexbox.new() local self = setmetatable({}, Flexbox):__init() self.set("width", 12) self.set("height", 6) self.set("background", colors.blue) self.set("z", 10) self:observe("width", function() self.set("flexUpdateLayout", true) end) self:observe("height", function() self.set("flexUpdateLayout", true) end) return self end
function Flexbox:init(props, basalt) Container.init(self, props, basalt) self.set("type", "Flexbox") end
function Flexbox:addChild(element) Container.addChild(self, element)
if(element~=lineBreakElement)then
element:instanceProperty("flexGrow", {default = 0, type = "number"})
element:instanceProperty("flexShrink", {default = 0, type = "number"})
element:instanceProperty("flexBasis", {default = 0, type = "number"})
end
self.set("flexUpdateLayout", true)
return self
end
function Flexbox:removeChild(element) Container.removeChild(self, element)
if(element~=lineBreakElement)then element.setFlexGrow = nil element.setFlexShrink = nil element.setFlexBasis = nil element.getFlexGrow = nil element.getFlexShrink = nil element.getFlexBasis = nil element.set("flexGrow", nil) element.set("flexShrink", nil) element.set("flexBasis", nil) end
self.set("flexUpdateLayout", true) return self end
--- Adds a new line break to the flexbox. ---@param self Flexbox The element itself ---@return Flexbox function Flexbox:addLineBreak() self:addChild(lineBreakElement) return self end
function Flexbox:render() if(self.get("flexUpdateLayout"))then updateLayout(self, self.get("flexDirection"), self.get("flexSpacing"), self.get("flexJustifyContent"), self.get("flexWrap")) end Container.render(self) end
return Flexbox