fixed scrollframe not sending scroll events to its children fixed scrollframe scrolling even if mouse is not hovering over the element improved the behaviour of the flow layout
295 lines
10 KiB
Lua
295 lines
10 KiB
Lua
local flow = {}
|
|
|
|
--- Calculates positions for all children in a flow layout
|
|
--- @param instance table The layout instance
|
|
--- - container: the container to layout
|
|
--- - options: layout options
|
|
--- - direction: "horizontal" or "vertical" (default: "horizontal")
|
|
--- - spacing: gap between elements (default: 0)
|
|
--- - padding: padding around the flow (default: 0)
|
|
--- - align: "start", "center", or "end" (default: "start")
|
|
function flow.calculate(instance)
|
|
local container = instance.container
|
|
local options = instance.options or {}
|
|
|
|
local children = container.get("children")
|
|
local containerWidth = container.get("width")
|
|
local containerHeight = container.get("height")
|
|
|
|
local direction = options.direction or "horizontal"
|
|
local spacing = options.spacing or 0
|
|
local padding = options.padding or 0
|
|
local align = options.align or "start"
|
|
|
|
local childCount = #children
|
|
if childCount == 0 then
|
|
instance._positions = {}
|
|
return
|
|
end
|
|
|
|
local positions = {}
|
|
|
|
if direction == "horizontal" then
|
|
local rows = {}
|
|
local currentRow = {}
|
|
local currentX = padding + 1
|
|
local currentY = padding + 1
|
|
local maxHeightInRow = 0
|
|
|
|
for i, child in ipairs(children) do
|
|
local childWidth = child.get("width")
|
|
local childHeight = child.get("height")
|
|
local layoutConfig = child.get("layoutConfig") or {}
|
|
|
|
if child._userModified then
|
|
child._originalWidth = childWidth
|
|
child._originalHeight = childHeight
|
|
end
|
|
|
|
local basis = layoutConfig.basis
|
|
if not basis then
|
|
if not child._originalWidth then
|
|
child._originalWidth = childWidth
|
|
end
|
|
basis = child._originalWidth
|
|
end
|
|
|
|
local hasFlexConfig = layoutConfig.grow or layoutConfig.shrink or layoutConfig.basis
|
|
|
|
if currentX + basis - 1 > containerWidth - padding and currentX > padding + 1 then
|
|
|
|
table.insert(rows, {
|
|
children = currentRow,
|
|
y = currentY,
|
|
height = maxHeightInRow
|
|
})
|
|
|
|
currentRow = {}
|
|
currentX = padding + 1
|
|
currentY = currentY + maxHeightInRow + spacing
|
|
maxHeightInRow = 0
|
|
end
|
|
|
|
table.insert(currentRow, {
|
|
child = child,
|
|
width = childWidth,
|
|
height = childHeight,
|
|
basis = basis,
|
|
grow = layoutConfig.grow or 0,
|
|
shrink = layoutConfig.shrink or 1,
|
|
hasFlexConfig = hasFlexConfig
|
|
})
|
|
|
|
currentX = currentX + basis + spacing
|
|
maxHeightInRow = math.max(maxHeightInRow, childHeight)
|
|
end
|
|
|
|
if #currentRow > 0 then
|
|
table.insert(rows, {
|
|
children = currentRow,
|
|
y = currentY,
|
|
height = maxHeightInRow
|
|
})
|
|
end
|
|
|
|
for _, row in ipairs(rows) do
|
|
local totalBasis = 0
|
|
local totalSpacing = (#row.children - 1) * spacing
|
|
local totalGrow = 0
|
|
local totalShrink = 0
|
|
|
|
for _, item in ipairs(row.children) do
|
|
totalBasis = totalBasis + item.basis
|
|
totalGrow = totalGrow + item.grow
|
|
totalShrink = totalShrink + item.shrink
|
|
end
|
|
|
|
local availableWidth = containerWidth - 2 * padding
|
|
local remainingSpace = availableWidth - totalBasis - totalSpacing
|
|
|
|
for _, item in ipairs(row.children) do
|
|
if remainingSpace > 0 and totalGrow > 0 then
|
|
local extraSpace = (remainingSpace * item.grow) / totalGrow
|
|
item.finalWidth = item.basis + extraSpace
|
|
elseif remainingSpace < 0 and totalShrink > 0 then
|
|
local reduceSpace = (-remainingSpace * item.shrink) / totalShrink
|
|
item.finalWidth = math.max(1, item.basis - reduceSpace)
|
|
else
|
|
item.finalWidth = item.basis
|
|
end
|
|
|
|
if not item.hasFlexConfig then
|
|
item.finalWidth = item.basis
|
|
end
|
|
end
|
|
|
|
local rowWidth = 0
|
|
for j, item in ipairs(row.children) do
|
|
rowWidth = rowWidth + item.finalWidth
|
|
if j < #row.children then
|
|
rowWidth = rowWidth + spacing
|
|
end
|
|
end
|
|
|
|
local startX = padding + 1
|
|
if align == "center" then
|
|
startX = padding + 1 + math.floor((containerWidth - 2 * padding - rowWidth) / 2)
|
|
elseif align == "end" then
|
|
startX = containerWidth - padding - rowWidth + 1
|
|
end
|
|
|
|
local x = startX
|
|
for _, item in ipairs(row.children) do
|
|
local y = row.y
|
|
if align == "center" then
|
|
y = row.y + math.floor((row.height - item.height) / 2)
|
|
elseif align == "end" then
|
|
y = row.y + row.height - item.height
|
|
end
|
|
|
|
positions[item.child] = {
|
|
x = x,
|
|
y = y,
|
|
width = math.floor(item.finalWidth),
|
|
height = item.height
|
|
}
|
|
|
|
x = x + math.floor(item.finalWidth) + spacing
|
|
end
|
|
end
|
|
|
|
else
|
|
local columns = {}
|
|
local currentColumn = {}
|
|
local currentX = padding + 1
|
|
local currentY = padding + 1
|
|
local maxWidthInColumn = 0
|
|
|
|
for i, child in ipairs(children) do
|
|
local childWidth = child.get("width")
|
|
local childHeight = child.get("height")
|
|
local layoutConfig = child.get("layoutConfig") or {}
|
|
|
|
-- If user modified the element, update the original size
|
|
if child._userModified then
|
|
child._originalWidth = childWidth
|
|
child._originalHeight = childHeight
|
|
end
|
|
|
|
local basis = layoutConfig.basis
|
|
if not basis then
|
|
if not child._originalHeight then
|
|
child._originalHeight = childHeight
|
|
end
|
|
basis = child._originalHeight
|
|
end
|
|
|
|
local hasFlexConfig = layoutConfig.grow or layoutConfig.shrink or layoutConfig.basis
|
|
|
|
if currentY + basis - 1 > containerHeight - padding and currentY > padding + 1 then
|
|
table.insert(columns, {
|
|
children = currentColumn,
|
|
x = currentX,
|
|
width = maxWidthInColumn
|
|
})
|
|
|
|
currentColumn = {}
|
|
currentY = padding + 1
|
|
currentX = currentX + maxWidthInColumn + spacing
|
|
maxWidthInColumn = 0
|
|
end
|
|
|
|
table.insert(currentColumn, {
|
|
child = child,
|
|
width = childWidth,
|
|
height = childHeight,
|
|
basis = basis,
|
|
grow = layoutConfig.grow or 0,
|
|
shrink = layoutConfig.shrink or 1,
|
|
hasFlexConfig = hasFlexConfig
|
|
})
|
|
|
|
currentY = currentY + basis + spacing
|
|
maxWidthInColumn = math.max(maxWidthInColumn, childWidth)
|
|
end
|
|
|
|
if #currentColumn > 0 then
|
|
table.insert(columns, {
|
|
children = currentColumn,
|
|
x = currentX,
|
|
width = maxWidthInColumn
|
|
})
|
|
end
|
|
|
|
for _, column in ipairs(columns) do
|
|
local totalBasis = 0
|
|
local totalSpacing = (#column.children - 1) * spacing
|
|
local totalGrow = 0
|
|
local totalShrink = 0
|
|
|
|
for _, item in ipairs(column.children) do
|
|
totalBasis = totalBasis + item.basis
|
|
totalGrow = totalGrow + item.grow
|
|
totalShrink = totalShrink + item.shrink
|
|
end
|
|
|
|
local availableHeight = containerHeight - 2 * padding
|
|
local remainingSpace = availableHeight - totalBasis - totalSpacing
|
|
|
|
for _, item in ipairs(column.children) do
|
|
if remainingSpace > 0 and totalGrow > 0 then
|
|
local extraSpace = (remainingSpace * item.grow) / totalGrow
|
|
item.finalHeight = item.basis + extraSpace
|
|
elseif remainingSpace < 0 and totalShrink > 0 then
|
|
local reduceSpace = (-remainingSpace * item.shrink) / totalShrink
|
|
item.finalHeight = math.max(1, item.basis - reduceSpace)
|
|
else
|
|
item.finalHeight = item.basis
|
|
end
|
|
|
|
if not item.hasFlexConfig then
|
|
item.finalHeight = item.basis
|
|
end
|
|
end
|
|
|
|
local columnHeight = 0
|
|
for j, item in ipairs(column.children) do
|
|
columnHeight = columnHeight + item.finalHeight
|
|
if j < #column.children then
|
|
columnHeight = columnHeight + spacing
|
|
end
|
|
end
|
|
|
|
local startY = padding + 1
|
|
if align == "center" then
|
|
startY = padding + 1 + math.floor((containerHeight - 2 * padding - columnHeight) / 2)
|
|
elseif align == "end" then
|
|
startY = containerHeight - padding - columnHeight + 1
|
|
end
|
|
|
|
local y = startY
|
|
for _, item in ipairs(column.children) do
|
|
local x = column.x
|
|
if align == "center" then
|
|
x = column.x + math.floor((column.width - item.width) / 2)
|
|
elseif align == "end" then
|
|
x = column.x + column.width - item.width
|
|
end
|
|
|
|
positions[item.child] = {
|
|
x = x,
|
|
y = y,
|
|
width = item.width,
|
|
height = math.floor(item.finalHeight)
|
|
}
|
|
|
|
y = y + math.floor(item.finalHeight) + spacing
|
|
end
|
|
end
|
|
end
|
|
|
|
instance._positions = positions
|
|
end
|
|
|
|
return flow
|