change spacing logic
This commit is contained in:
137
FlexLove.lua
137
FlexLove.lua
@@ -778,9 +778,37 @@ function Grid.layoutGridItems(element)
|
|||||||
local rows = element.gridRows or 1
|
local rows = element.gridRows or 1
|
||||||
local columns = element.gridColumns or 1
|
local columns = element.gridColumns or 1
|
||||||
|
|
||||||
-- Calculate available space
|
-- Calculate space reserved by absolutely positioned siblings
|
||||||
local availableWidth = element.width - element.padding.left - element.padding.right
|
local reservedLeft = 0
|
||||||
local availableHeight = element.height - element.padding.top - element.padding.bottom
|
local reservedRight = 0
|
||||||
|
local reservedTop = 0
|
||||||
|
local reservedBottom = 0
|
||||||
|
|
||||||
|
for _, child in ipairs(element.children) do
|
||||||
|
-- Only consider absolutely positioned children with explicit positioning
|
||||||
|
if child.positioning == Positioning.ABSOLUTE and child._explicitlyAbsolute then
|
||||||
|
if child.left then
|
||||||
|
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
||||||
|
reservedLeft = math.max(reservedLeft, child.left + childTotalWidth)
|
||||||
|
end
|
||||||
|
if child.right then
|
||||||
|
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
||||||
|
reservedRight = math.max(reservedRight, child.right + childTotalWidth)
|
||||||
|
end
|
||||||
|
if child.top then
|
||||||
|
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
||||||
|
reservedTop = math.max(reservedTop, child.top + childTotalHeight)
|
||||||
|
end
|
||||||
|
if child.bottom then
|
||||||
|
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
||||||
|
reservedBottom = math.max(reservedBottom, child.bottom + childTotalHeight)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Calculate available space (accounting for padding and reserved space)
|
||||||
|
local availableWidth = element.width - element.padding.left - element.padding.right - reservedLeft - reservedRight
|
||||||
|
local availableHeight = element.height - element.padding.top - element.padding.bottom - reservedTop - reservedBottom
|
||||||
|
|
||||||
-- Get gaps
|
-- Get gaps
|
||||||
local columnGap = element.columnGap or 0
|
local columnGap = element.columnGap or 0
|
||||||
@@ -812,9 +840,9 @@ function Grid.layoutGridItems(element)
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Calculate cell position
|
-- Calculate cell position (accounting for reserved space)
|
||||||
local cellX = element.x + element.padding.left + (col * (cellWidth + columnGap))
|
local cellX = element.x + element.padding.left + reservedLeft + (col * (cellWidth + columnGap))
|
||||||
local cellY = element.y + element.padding.top + (row * (cellHeight + rowGap))
|
local cellY = element.y + element.padding.top + reservedTop + (row * (cellHeight + rowGap))
|
||||||
|
|
||||||
-- Apply alignment within grid cell (default to stretch)
|
-- Apply alignment within grid cell (default to stretch)
|
||||||
local effectiveAlignItems = element.alignItems or AlignItems.STRETCH
|
local effectiveAlignItems = element.alignItems or AlignItems.STRETCH
|
||||||
@@ -2016,15 +2044,80 @@ function Element:layoutChildren()
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Calculate available space (accounting for padding)
|
-- Calculate space reserved by absolutely positioned siblings with explicit positioning
|
||||||
|
local reservedMainStart = 0 -- Space reserved at the start of main axis (left for horizontal, top for vertical)
|
||||||
|
local reservedMainEnd = 0 -- Space reserved at the end of main axis (right for horizontal, bottom for vertical)
|
||||||
|
local reservedCrossStart = 0 -- Space reserved at the start of cross axis (top for horizontal, left for vertical)
|
||||||
|
local reservedCrossEnd = 0 -- Space reserved at the end of cross axis (bottom for horizontal, right for vertical)
|
||||||
|
|
||||||
|
for _, child in ipairs(self.children) do
|
||||||
|
-- Only consider absolutely positioned children with explicit positioning
|
||||||
|
if child.positioning == Positioning.ABSOLUTE and child._explicitlyAbsolute then
|
||||||
|
if self.flexDirection == FlexDirection.HORIZONTAL then
|
||||||
|
-- Horizontal layout: main axis is X, cross axis is Y
|
||||||
|
-- Check for left positioning (reserves space at main axis start)
|
||||||
|
if child.left then
|
||||||
|
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
||||||
|
local spaceNeeded = child.left + childTotalWidth
|
||||||
|
reservedMainStart = math.max(reservedMainStart, spaceNeeded)
|
||||||
|
end
|
||||||
|
-- Check for right positioning (reserves space at main axis end)
|
||||||
|
if child.right then
|
||||||
|
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
||||||
|
local spaceNeeded = child.right + childTotalWidth
|
||||||
|
reservedMainEnd = math.max(reservedMainEnd, spaceNeeded)
|
||||||
|
end
|
||||||
|
-- Check for top positioning (reserves space at cross axis start)
|
||||||
|
if child.top then
|
||||||
|
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
||||||
|
local spaceNeeded = child.top + childTotalHeight
|
||||||
|
reservedCrossStart = math.max(reservedCrossStart, spaceNeeded)
|
||||||
|
end
|
||||||
|
-- Check for bottom positioning (reserves space at cross axis end)
|
||||||
|
if child.bottom then
|
||||||
|
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
||||||
|
local spaceNeeded = child.bottom + childTotalHeight
|
||||||
|
reservedCrossEnd = math.max(reservedCrossEnd, spaceNeeded)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Vertical layout: main axis is Y, cross axis is X
|
||||||
|
-- Check for top positioning (reserves space at main axis start)
|
||||||
|
if child.top then
|
||||||
|
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
||||||
|
local spaceNeeded = child.top + childTotalHeight
|
||||||
|
reservedMainStart = math.max(reservedMainStart, spaceNeeded)
|
||||||
|
end
|
||||||
|
-- Check for bottom positioning (reserves space at main axis end)
|
||||||
|
if child.bottom then
|
||||||
|
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
||||||
|
local spaceNeeded = child.bottom + childTotalHeight
|
||||||
|
reservedMainEnd = math.max(reservedMainEnd, spaceNeeded)
|
||||||
|
end
|
||||||
|
-- Check for left positioning (reserves space at cross axis start)
|
||||||
|
if child.left then
|
||||||
|
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
||||||
|
local spaceNeeded = child.left + childTotalWidth
|
||||||
|
reservedCrossStart = math.max(reservedCrossStart, spaceNeeded)
|
||||||
|
end
|
||||||
|
-- Check for right positioning (reserves space at cross axis end)
|
||||||
|
if child.right then
|
||||||
|
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
||||||
|
local spaceNeeded = child.right + childTotalWidth
|
||||||
|
reservedCrossEnd = math.max(reservedCrossEnd, spaceNeeded)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Calculate available space (accounting for padding and reserved space)
|
||||||
local availableMainSize = 0
|
local availableMainSize = 0
|
||||||
local availableCrossSize = 0
|
local availableCrossSize = 0
|
||||||
if self.flexDirection == FlexDirection.HORIZONTAL then
|
if self.flexDirection == FlexDirection.HORIZONTAL then
|
||||||
availableMainSize = self.width - self.padding.left - self.padding.right
|
availableMainSize = self.width - self.padding.left - self.padding.right - reservedMainStart - reservedMainEnd
|
||||||
availableCrossSize = self.height - self.padding.top - self.padding.bottom
|
availableCrossSize = self.height - self.padding.top - self.padding.bottom - reservedCrossStart - reservedCrossEnd
|
||||||
else
|
else
|
||||||
availableMainSize = self.height - self.padding.top - self.padding.bottom
|
availableMainSize = self.height - self.padding.top - self.padding.bottom - reservedMainStart - reservedMainEnd
|
||||||
availableCrossSize = self.width - self.padding.left - self.padding.right
|
availableCrossSize = self.width - self.padding.left - self.padding.right - reservedCrossStart - reservedCrossEnd
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Handle flex wrap: create lines of children
|
-- Handle flex wrap: create lines of children
|
||||||
@@ -2208,20 +2301,21 @@ function Element:layoutChildren()
|
|||||||
if self.flexDirection == FlexDirection.HORIZONTAL then
|
if self.flexDirection == FlexDirection.HORIZONTAL then
|
||||||
-- Horizontal layout: main axis is X, cross axis is Y
|
-- Horizontal layout: main axis is X, cross axis is Y
|
||||||
-- Position child at border box (x, y represents top-left including padding)
|
-- Position child at border box (x, y represents top-left including padding)
|
||||||
child.x = self.x + self.padding.left + currentMainPos
|
-- Add reservedMainStart to account for absolutely positioned siblings
|
||||||
|
child.x = self.x + self.padding.left + reservedMainStart + currentMainPos
|
||||||
|
|
||||||
if effectiveAlign == AlignItems.FLEX_START then
|
if effectiveAlign == AlignItems.FLEX_START then
|
||||||
child.y = self.y + self.padding.top + currentCrossPos
|
child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos
|
||||||
elseif effectiveAlign == AlignItems.CENTER then
|
elseif effectiveAlign == AlignItems.CENTER then
|
||||||
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
||||||
child.y = self.y + self.padding.top + currentCrossPos + ((lineHeight - childTotalHeight) / 2)
|
child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos + ((lineHeight - childTotalHeight) / 2)
|
||||||
elseif effectiveAlign == AlignItems.FLEX_END then
|
elseif effectiveAlign == AlignItems.FLEX_END then
|
||||||
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
||||||
child.y = self.y + self.padding.top + currentCrossPos + lineHeight - childTotalHeight
|
child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos + lineHeight - childTotalHeight
|
||||||
elseif effectiveAlign == AlignItems.STRETCH then
|
elseif effectiveAlign == AlignItems.STRETCH then
|
||||||
-- STRETCH always stretches children in cross-axis direction
|
-- STRETCH always stretches children in cross-axis direction
|
||||||
child.height = lineHeight - child.padding.top - child.padding.bottom
|
child.height = lineHeight - child.padding.top - child.padding.bottom
|
||||||
child.y = self.y + self.padding.top + currentCrossPos
|
child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Apply positioning offsets (top, right, bottom, left)
|
-- Apply positioning offsets (top, right, bottom, left)
|
||||||
@@ -2243,20 +2337,21 @@ function Element:layoutChildren()
|
|||||||
else
|
else
|
||||||
-- Vertical layout: main axis is Y, cross axis is X
|
-- Vertical layout: main axis is Y, cross axis is X
|
||||||
-- Position child at border box (x, y represents top-left including padding)
|
-- Position child at border box (x, y represents top-left including padding)
|
||||||
child.y = self.y + self.padding.top + currentMainPos
|
-- Add reservedMainStart to account for absolutely positioned siblings
|
||||||
|
child.y = self.y + self.padding.top + reservedMainStart + currentMainPos
|
||||||
|
|
||||||
if effectiveAlign == AlignItems.FLEX_START then
|
if effectiveAlign == AlignItems.FLEX_START then
|
||||||
child.x = self.x + self.padding.left + currentCrossPos
|
child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos
|
||||||
elseif effectiveAlign == AlignItems.CENTER then
|
elseif effectiveAlign == AlignItems.CENTER then
|
||||||
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
||||||
child.x = self.x + self.padding.left + currentCrossPos + ((lineHeight - childTotalWidth) / 2)
|
child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos + ((lineHeight - childTotalWidth) / 2)
|
||||||
elseif effectiveAlign == AlignItems.FLEX_END then
|
elseif effectiveAlign == AlignItems.FLEX_END then
|
||||||
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
||||||
child.x = self.x + self.padding.left + currentCrossPos + lineHeight - childTotalWidth
|
child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos + lineHeight - childTotalWidth
|
||||||
elseif effectiveAlign == AlignItems.STRETCH then
|
elseif effectiveAlign == AlignItems.STRETCH then
|
||||||
-- STRETCH always stretches children in cross-axis direction
|
-- STRETCH always stretches children in cross-axis direction
|
||||||
child.width = lineHeight - child.padding.left - child.padding.right
|
child.width = lineHeight - child.padding.left - child.padding.right
|
||||||
child.x = self.x + self.padding.left + currentCrossPos
|
child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Apply positioning offsets (top, right, bottom, left)
|
-- Apply positioning offsets (top, right, bottom, left)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ FlexLöve is a lightweight, flexible GUI library for Löve2D that implements a f
|
|||||||
|
|
||||||
## ⚠️ Development Status
|
## ⚠️ Development Status
|
||||||
|
|
||||||
This library is under active development. While many features are functional, some aspects may change or have incomplete implementations.
|
This library is under active development. While many features are functional, some aspects may change or have incomplete/broken implementations.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|||||||
437
testing/__tests__/17_sibling_space_reservation_tests.lua
Normal file
437
testing/__tests__/17_sibling_space_reservation_tests.lua
Normal file
@@ -0,0 +1,437 @@
|
|||||||
|
-- Test: Sibling Space Reservation in Flex and Grid Layouts
|
||||||
|
-- Purpose: Verify that absolutely positioned siblings with explicit positioning
|
||||||
|
-- properly reserve space in flex and grid containers
|
||||||
|
|
||||||
|
local lu = require("testing.luaunit")
|
||||||
|
local FlexLove = require("libs.FlexLove")
|
||||||
|
local Gui = FlexLove.GUI
|
||||||
|
local Color = FlexLove.Color
|
||||||
|
|
||||||
|
-- Mock love.graphics and love.window
|
||||||
|
_G.love = require("testing.loveStub")
|
||||||
|
|
||||||
|
TestSiblingSpaceReservation = {}
|
||||||
|
|
||||||
|
function TestSiblingSpaceReservation:setUp()
|
||||||
|
-- Reset GUI state before each test
|
||||||
|
Gui.destroy()
|
||||||
|
-- Set up a standard viewport
|
||||||
|
love.window.setMode(1920, 1080)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestSiblingSpaceReservation:tearDown()
|
||||||
|
Gui.destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Flex Layout Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestSiblingSpaceReservation:test_flex_horizontal_left_positioned_sibling_reserves_space()
|
||||||
|
-- Create a flex container
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 1000,
|
||||||
|
height = 200,
|
||||||
|
positioning = "flex",
|
||||||
|
flexDirection = "horizontal",
|
||||||
|
justifyContent = "flex-start",
|
||||||
|
padding = { top = 0, right = 0, bottom = 0, left = 0 },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add an absolutely positioned sibling with left positioning
|
||||||
|
local absoluteSibling = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
positioning = "absolute",
|
||||||
|
left = 10, -- 10px from left edge
|
||||||
|
width = 50,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(1, 0, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add a flex child that should start after the absolutely positioned sibling
|
||||||
|
local flexChild = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 100,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(0, 1, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Layout children
|
||||||
|
container:layoutChildren()
|
||||||
|
|
||||||
|
-- The absolutely positioned sibling reserves: left (10) + width (50) + padding (0) = 60px
|
||||||
|
-- The flex child should start at x = container.x + padding.left + reservedLeft
|
||||||
|
-- = 0 + 0 + 60 = 60
|
||||||
|
lu.assertEquals(flexChild.x, 60, "Flex child should start after absolutely positioned sibling")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestSiblingSpaceReservation:test_flex_horizontal_right_positioned_sibling_reserves_space()
|
||||||
|
-- Create a flex container
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 1000,
|
||||||
|
height = 200,
|
||||||
|
positioning = "flex",
|
||||||
|
flexDirection = "horizontal",
|
||||||
|
justifyContent = "flex-start",
|
||||||
|
padding = { top = 0, right = 0, bottom = 0, left = 0 },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add an absolutely positioned sibling with right positioning
|
||||||
|
local absoluteSibling = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
positioning = "absolute",
|
||||||
|
right = 10, -- 10px from right edge
|
||||||
|
width = 50,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(1, 0, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add a flex child
|
||||||
|
local flexChild = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 100,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(0, 1, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Layout children
|
||||||
|
container:layoutChildren()
|
||||||
|
|
||||||
|
-- The absolutely positioned sibling reserves: right (10) + width (50) + padding (0) = 60px
|
||||||
|
-- Available space = 1000 - 0 (padding) - 0 (reservedLeft) - 60 (reservedRight) = 940px
|
||||||
|
-- The flex child (width 100) should fit within this space
|
||||||
|
-- Child should start at x = 0
|
||||||
|
lu.assertEquals(flexChild.x, 0, "Flex child should start at container left edge")
|
||||||
|
|
||||||
|
-- The absolutely positioned sibling should be at the right edge
|
||||||
|
-- x = container.x + container.width + padding.left - right - (width + padding)
|
||||||
|
-- = 0 + 1000 + 0 - 10 - 50 = 940
|
||||||
|
lu.assertEquals(absoluteSibling.x, 940, "Absolutely positioned sibling should be at right edge")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestSiblingSpaceReservation:test_flex_vertical_top_positioned_sibling_reserves_space()
|
||||||
|
-- Create a vertical flex container
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 1000,
|
||||||
|
positioning = "flex",
|
||||||
|
flexDirection = "vertical",
|
||||||
|
justifyContent = "flex-start",
|
||||||
|
padding = { top = 0, right = 0, bottom = 0, left = 0 },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add an absolutely positioned sibling with top positioning
|
||||||
|
local absoluteSibling = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
positioning = "absolute",
|
||||||
|
top = 10, -- 10px from top edge
|
||||||
|
width = 50,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(1, 0, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add a flex child that should start after the absolutely positioned sibling
|
||||||
|
local flexChild = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 50,
|
||||||
|
height = 100,
|
||||||
|
backgroundColor = Color.new(0, 1, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Layout children
|
||||||
|
container:layoutChildren()
|
||||||
|
|
||||||
|
-- The absolutely positioned sibling reserves: top (10) + height (50) + padding (0) = 60px
|
||||||
|
-- The flex child should start at y = container.y + padding.top + reservedTop
|
||||||
|
-- = 0 + 0 + 60 = 60
|
||||||
|
lu.assertEquals(flexChild.y, 60, "Flex child should start after absolutely positioned sibling")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestSiblingSpaceReservation:test_flex_horizontal_multiple_positioned_siblings()
|
||||||
|
-- Create a flex container
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 1000,
|
||||||
|
height = 200,
|
||||||
|
positioning = "flex",
|
||||||
|
flexDirection = "horizontal",
|
||||||
|
justifyContent = "flex-start",
|
||||||
|
padding = { top = 0, right = 0, bottom = 0, left = 0 },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add two absolutely positioned siblings (left and right)
|
||||||
|
local leftSibling = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
positioning = "absolute",
|
||||||
|
left = 5,
|
||||||
|
width = 40,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(1, 0, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
local rightSibling = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
positioning = "absolute",
|
||||||
|
right = 5,
|
||||||
|
width = 40,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(0, 0, 1, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add flex children
|
||||||
|
local flexChild1 = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 100,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(0, 1, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
local flexChild2 = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 100,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(0, 1, 1, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Layout children
|
||||||
|
container:layoutChildren()
|
||||||
|
|
||||||
|
-- Reserved left: 5 + 40 = 45px
|
||||||
|
-- Reserved right: 5 + 40 = 45px
|
||||||
|
-- Available space: 1000 - 45 - 45 = 910px
|
||||||
|
-- First flex child should start at x = 0 + 0 + 45 = 45
|
||||||
|
lu.assertEquals(flexChild1.x, 45, "First flex child should start after left sibling")
|
||||||
|
|
||||||
|
-- Second flex child should start at x = 45 + 100 + gap = 145 (assuming gap=10)
|
||||||
|
lu.assertIsTrue(flexChild2.x >= 145, "Second flex child should be positioned after first")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Grid Layout Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestSiblingSpaceReservation:test_grid_left_positioned_sibling_reserves_space()
|
||||||
|
-- Create a grid container
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 1000,
|
||||||
|
height = 500,
|
||||||
|
positioning = "grid",
|
||||||
|
gridRows = 2,
|
||||||
|
gridColumns = 3,
|
||||||
|
columnGap = 10,
|
||||||
|
rowGap = 10,
|
||||||
|
padding = { top = 0, right = 0, bottom = 0, left = 0 },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add an absolutely positioned sibling with left positioning
|
||||||
|
local absoluteSibling = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
positioning = "absolute",
|
||||||
|
left = 10,
|
||||||
|
width = 50,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(1, 0, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add grid children
|
||||||
|
local gridChild1 = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
backgroundColor = Color.new(0, 1, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Layout children
|
||||||
|
container:layoutChildren()
|
||||||
|
|
||||||
|
-- Reserved left: 10 + 50 = 60px
|
||||||
|
-- Available width: 1000 - 60 = 940px
|
||||||
|
-- Column gaps: 2 * 10 = 20px
|
||||||
|
-- Cell width: (940 - 20) / 3 = 306.67px
|
||||||
|
-- First grid child should start at x = 0 + 0 + 60 = 60
|
||||||
|
lu.assertEquals(gridChild1.x, 60, "Grid child should start after absolutely positioned sibling")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestSiblingSpaceReservation:test_grid_top_positioned_sibling_reserves_space()
|
||||||
|
-- Create a grid container
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 1000,
|
||||||
|
height = 500,
|
||||||
|
positioning = "grid",
|
||||||
|
gridRows = 2,
|
||||||
|
gridColumns = 3,
|
||||||
|
columnGap = 10,
|
||||||
|
rowGap = 10,
|
||||||
|
padding = { top = 0, right = 0, bottom = 0, left = 0 },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add an absolutely positioned sibling with top positioning
|
||||||
|
local absoluteSibling = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
positioning = "absolute",
|
||||||
|
top = 10,
|
||||||
|
width = 50,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(1, 0, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add grid children
|
||||||
|
local gridChild1 = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
backgroundColor = Color.new(0, 1, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Layout children
|
||||||
|
container:layoutChildren()
|
||||||
|
|
||||||
|
-- Reserved top: 10 + 50 = 60px
|
||||||
|
-- Available height: 500 - 60 = 440px
|
||||||
|
-- Row gaps: 1 * 10 = 10px
|
||||||
|
-- Cell height: (440 - 10) / 2 = 215px
|
||||||
|
-- First grid child should start at y = 0 + 0 + 60 = 60
|
||||||
|
lu.assertEquals(gridChild1.y, 60, "Grid child should start after absolutely positioned sibling")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestSiblingSpaceReservation:test_grid_multiple_positioned_siblings()
|
||||||
|
-- Create a grid container
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 1000,
|
||||||
|
height = 500,
|
||||||
|
positioning = "grid",
|
||||||
|
gridRows = 2,
|
||||||
|
gridColumns = 2,
|
||||||
|
columnGap = 0,
|
||||||
|
rowGap = 0,
|
||||||
|
padding = { top = 0, right = 0, bottom = 0, left = 0 },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add absolutely positioned siblings at all corners
|
||||||
|
local topLeftSibling = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
positioning = "absolute",
|
||||||
|
left = 10,
|
||||||
|
top = 10,
|
||||||
|
width = 40,
|
||||||
|
height = 40,
|
||||||
|
backgroundColor = Color.new(1, 0, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
local bottomRightSibling = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
positioning = "absolute",
|
||||||
|
right = 10,
|
||||||
|
bottom = 10,
|
||||||
|
width = 40,
|
||||||
|
height = 40,
|
||||||
|
backgroundColor = Color.new(0, 0, 1, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add grid children
|
||||||
|
local gridChild1 = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
backgroundColor = Color.new(0, 1, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Layout children
|
||||||
|
container:layoutChildren()
|
||||||
|
|
||||||
|
-- Reserved left: 10 + 40 = 50px
|
||||||
|
-- Reserved right: 10 + 40 = 50px
|
||||||
|
-- Reserved top: 10 + 40 = 50px
|
||||||
|
-- Reserved bottom: 10 + 40 = 50px
|
||||||
|
-- Available width: 1000 - 50 - 50 = 900px
|
||||||
|
-- Available height: 500 - 50 - 50 = 400px
|
||||||
|
-- Cell width: 900 / 2 = 450px
|
||||||
|
-- Cell height: 400 / 2 = 200px
|
||||||
|
-- First grid child should start at (50, 50)
|
||||||
|
lu.assertEquals(gridChild1.x, 50, "Grid child X should account for left sibling")
|
||||||
|
lu.assertEquals(gridChild1.y, 50, "Grid child Y should account for top sibling")
|
||||||
|
lu.assertEquals(gridChild1.width, 450, "Grid cell width should account for reserved space")
|
||||||
|
lu.assertEquals(gridChild1.height, 200, "Grid cell height should account for reserved space")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Edge Cases
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestSiblingSpaceReservation:test_non_explicitly_absolute_children_dont_reserve_space()
|
||||||
|
-- Children that default to absolute positioning (not explicitly set)
|
||||||
|
-- should NOT reserve space in flex layouts
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 1000,
|
||||||
|
height = 200,
|
||||||
|
positioning = "flex",
|
||||||
|
flexDirection = "horizontal",
|
||||||
|
padding = { top = 0, right = 0, bottom = 0, left = 0 },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- This child has positioning="flex" so it participates in layout
|
||||||
|
local flexChild = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
positioning = "flex",
|
||||||
|
left = 10, -- This should be ignored since it's a flex child
|
||||||
|
width = 100,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(0, 1, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Layout children
|
||||||
|
container:layoutChildren()
|
||||||
|
|
||||||
|
-- Flex child should start at x = 0 (no reserved space)
|
||||||
|
lu.assertEquals(flexChild.x, 0, "Flex children with positioning offsets should not reserve space")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestSiblingSpaceReservation:test_absolute_without_positioning_offsets_doesnt_reserve_space()
|
||||||
|
-- Absolutely positioned children without left/right/top/bottom
|
||||||
|
-- should NOT reserve space
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 1000,
|
||||||
|
height = 200,
|
||||||
|
positioning = "flex",
|
||||||
|
flexDirection = "horizontal",
|
||||||
|
padding = { top = 0, right = 0, bottom = 0, left = 0 },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Absolutely positioned but no positioning offsets
|
||||||
|
local absoluteChild = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
positioning = "absolute",
|
||||||
|
x = 50,
|
||||||
|
y = 50,
|
||||||
|
width = 50,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(1, 0, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Flex child
|
||||||
|
local flexChild = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 100,
|
||||||
|
height = 50,
|
||||||
|
backgroundColor = Color.new(0, 1, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Layout children
|
||||||
|
container:layoutChildren()
|
||||||
|
|
||||||
|
-- Flex child should start at x = 0 (no reserved space)
|
||||||
|
lu.assertEquals(flexChild.x, 0, "Absolute children without positioning offsets should not reserve space")
|
||||||
|
end
|
||||||
|
|
||||||
|
return TestSiblingSpaceReservation
|
||||||
@@ -20,6 +20,7 @@ local testFiles = {
|
|||||||
"testing/__tests__/14_text_scaling_basic_tests.lua",
|
"testing/__tests__/14_text_scaling_basic_tests.lua",
|
||||||
"testing/__tests__/15_grid_layout_tests.lua",
|
"testing/__tests__/15_grid_layout_tests.lua",
|
||||||
"testing/__tests__/16_event_system_tests.lua",
|
"testing/__tests__/16_event_system_tests.lua",
|
||||||
|
"testing/__tests__/17_sibling_space_reservation_tests.lua",
|
||||||
}
|
}
|
||||||
|
|
||||||
-- testingun all tests, but don't exit on error
|
-- testingun all tests, but don't exit on error
|
||||||
|
|||||||
Reference in New Issue
Block a user