From 8030a1f13a5d24891056cfa42dd6cf023929c159 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Tue, 14 Oct 2025 11:07:34 -0400 Subject: [PATCH] removing stale tasks --- FlexLove.lua | 173 ++++++++++++- testing/__tests__/20_padding_resize_tests.lua | 240 ++++++++++++++++++ 2 files changed, 399 insertions(+), 14 deletions(-) create mode 100644 testing/__tests__/20_padding_resize_tests.lua diff --git a/FlexLove.lua b/FlexLove.lua index 6170f0e..cca0c67 100644 --- a/FlexLove.lua +++ b/FlexLove.lua @@ -2026,12 +2026,16 @@ function Element.new(props) right = { value = nil, unit = "px" }, bottom = { value = nil, unit = "px" }, left = { value = nil, unit = "px" }, + horizontal = { value = nil, unit = "px" }, -- Shorthand for left/right + vertical = { value = nil, unit = "px" }, -- Shorthand for top/bottom }, margin = { top = { value = nil, unit = "px" }, right = { value = nil, unit = "px" }, bottom = { value = nil, unit = "px" }, left = { value = nil, unit = "px" }, + horizontal = { value = nil, unit = "px" }, -- Shorthand for left/right + vertical = { value = nil, unit = "px" }, -- Shorthand for top/bottom }, } @@ -2275,18 +2279,58 @@ function Element.new(props) end -- Store original spacing values for proper resize handling + -- Store shorthand properties first (horizontal/vertical) + if props.padding then + if props.padding.horizontal then + if type(props.padding.horizontal) == "string" then + local value, unit = Units.parse(props.padding.horizontal) + self.units.padding.horizontal = { value = value, unit = unit } + else + self.units.padding.horizontal = { value = props.padding.horizontal, unit = "px" } + end + end + if props.padding.vertical then + if type(props.padding.vertical) == "string" then + local value, unit = Units.parse(props.padding.vertical) + self.units.padding.vertical = { value = value, unit = unit } + else + self.units.padding.vertical = { value = props.padding.vertical, unit = "px" } + end + end + end + -- Initialize all padding sides for _, side in ipairs({ "top", "right", "bottom", "left" }) do if props.padding and props.padding[side] then if type(props.padding[side]) == "string" then local value, unit = Units.parse(props.padding[side]) - self.units.padding[side] = { value = value, unit = unit } + self.units.padding[side] = { value = value, unit = unit, explicit = true } else - self.units.padding[side] = { value = props.padding[side], unit = "px" } + self.units.padding[side] = { value = props.padding[side], unit = "px", explicit = true } end else - -- Use resolved padding values from Units.resolveSpacing - self.units.padding[side] = { value = self.padding[side], unit = "px" } + -- Mark as derived from shorthand (will use shorthand during resize if available) + self.units.padding[side] = { value = self.padding[side], unit = "px", explicit = false } + end + end + + -- Store margin shorthand properties + if props.margin then + if props.margin.horizontal then + if type(props.margin.horizontal) == "string" then + local value, unit = Units.parse(props.margin.horizontal) + self.units.margin.horizontal = { value = value, unit = unit } + else + self.units.margin.horizontal = { value = props.margin.horizontal, unit = "px" } + end + end + if props.margin.vertical then + if type(props.margin.vertical) == "string" then + local value, unit = Units.parse(props.margin.vertical) + self.units.margin.vertical = { value = value, unit = unit } + else + self.units.margin.vertical = { value = props.margin.vertical, unit = "px" } + end end end @@ -2295,13 +2339,13 @@ function Element.new(props) if props.margin and props.margin[side] then if type(props.margin[side]) == "string" then local value, unit = Units.parse(props.margin[side]) - self.units.margin[side] = { value = value, unit = unit } + self.units.margin[side] = { value = value, unit = unit, explicit = true } else - self.units.margin[side] = { value = props.margin[side], unit = "px" } + self.units.margin[side] = { value = props.margin[side], unit = "px", explicit = true } end else - -- Use resolved margin values from Units.resolveSpacing - self.units.margin[side] = { value = self.margin[side], unit = "px" } + -- Mark as derived from shorthand (will use shorthand during resize if available) + self.units.margin[side] = { value = self.margin[side], unit = "px", explicit = false } end end @@ -3688,12 +3732,61 @@ function Element:recalculateUnits(newViewportWidth, newViewportHeight) end -- Recalculate spacing (padding/margin) if using viewport or percentage units - local containerWidth = self.parent and self.parent.width or newViewportWidth - local containerHeight = self.parent and self.parent.height or newViewportHeight + -- For percentage-based padding, we need to use the parent's border-box dimensions + local parentBorderBoxWidth = self.parent and self.parent._borderBoxWidth or newViewportWidth + local parentBorderBoxHeight = self.parent and self.parent._borderBoxHeight or newViewportHeight + -- Handle shorthand properties first (horizontal/vertical) + local resolvedHorizontalPadding = nil + local resolvedVerticalPadding = nil + + if self.units.padding.horizontal and self.units.padding.horizontal.unit ~= "px" then + resolvedHorizontalPadding = Units.resolve( + self.units.padding.horizontal.value, + self.units.padding.horizontal.unit, + newViewportWidth, + newViewportHeight, + parentBorderBoxWidth + ) + elseif self.units.padding.horizontal and self.units.padding.horizontal.value then + resolvedHorizontalPadding = self.units.padding.horizontal.value + end + + if self.units.padding.vertical and self.units.padding.vertical.unit ~= "px" then + resolvedVerticalPadding = Units.resolve( + self.units.padding.vertical.value, + self.units.padding.vertical.unit, + newViewportWidth, + newViewportHeight, + parentBorderBoxHeight + ) + elseif self.units.padding.vertical and self.units.padding.vertical.value then + resolvedVerticalPadding = self.units.padding.vertical.value + end + + -- Resolve individual padding sides (with fallback to shorthand) for _, side in ipairs({ "top", "right", "bottom", "left" }) do - if self.units.padding[side].unit ~= "px" then - local parentSize = (side == "top" or side == "bottom") and containerHeight or containerWidth + -- Check if this side was explicitly set or if we should use shorthand + local useShorthand = false + if not self.units.padding[side].explicit then + -- Not explicitly set, check if we have shorthand + if side == "left" or side == "right" then + useShorthand = resolvedHorizontalPadding ~= nil + elseif side == "top" or side == "bottom" then + useShorthand = resolvedVerticalPadding ~= nil + end + end + + if useShorthand then + -- Use shorthand value + if side == "left" or side == "right" then + self.padding[side] = resolvedHorizontalPadding + else + self.padding[side] = resolvedVerticalPadding + end + elseif self.units.padding[side].unit ~= "px" then + -- Recalculate non-pixel units + local parentSize = (side == "top" or side == "bottom") and parentBorderBoxHeight or parentBorderBoxWidth self.padding[side] = Units.resolve( self.units.padding[side].value, self.units.padding[side].unit, @@ -3702,9 +3795,60 @@ function Element:recalculateUnits(newViewportWidth, newViewportHeight) parentSize ) end + -- If unit is "px" and not using shorthand, value stays the same + end - if self.units.margin[side].unit ~= "px" then - local parentSize = (side == "top" or side == "bottom") and containerHeight or containerWidth + -- Handle margin shorthand properties + local resolvedHorizontalMargin = nil + local resolvedVerticalMargin = nil + + if self.units.margin.horizontal and self.units.margin.horizontal.unit ~= "px" then + resolvedHorizontalMargin = Units.resolve( + self.units.margin.horizontal.value, + self.units.margin.horizontal.unit, + newViewportWidth, + newViewportHeight, + parentBorderBoxWidth + ) + elseif self.units.margin.horizontal and self.units.margin.horizontal.value then + resolvedHorizontalMargin = self.units.margin.horizontal.value + end + + if self.units.margin.vertical and self.units.margin.vertical.unit ~= "px" then + resolvedVerticalMargin = Units.resolve( + self.units.margin.vertical.value, + self.units.margin.vertical.unit, + newViewportWidth, + newViewportHeight, + parentBorderBoxHeight + ) + elseif self.units.margin.vertical and self.units.margin.vertical.value then + resolvedVerticalMargin = self.units.margin.vertical.value + end + + -- Resolve individual margin sides (with fallback to shorthand) + for _, side in ipairs({ "top", "right", "bottom", "left" }) do + -- Check if this side was explicitly set or if we should use shorthand + local useShorthand = false + if not self.units.margin[side].explicit then + -- Not explicitly set, check if we have shorthand + if side == "left" or side == "right" then + useShorthand = resolvedHorizontalMargin ~= nil + elseif side == "top" or side == "bottom" then + useShorthand = resolvedVerticalMargin ~= nil + end + end + + if useShorthand then + -- Use shorthand value + if side == "left" or side == "right" then + self.margin[side] = resolvedHorizontalMargin + else + self.margin[side] = resolvedVerticalMargin + end + elseif self.units.margin[side].unit ~= "px" then + -- Recalculate non-pixel units + local parentSize = (side == "top" or side == "bottom") and parentBorderBoxHeight or parentBorderBoxWidth self.margin[side] = Units.resolve( self.units.margin[side].value, self.units.margin[side].unit, @@ -3713,6 +3857,7 @@ function Element:recalculateUnits(newViewportWidth, newViewportHeight) parentSize ) end + -- If unit is "px" and not using shorthand, value stays the same end -- BORDER-BOX MODEL: After recalculating width/height/padding, update border-box dimensions diff --git a/testing/__tests__/20_padding_resize_tests.lua b/testing/__tests__/20_padding_resize_tests.lua new file mode 100644 index 0000000..56d18c6 --- /dev/null +++ b/testing/__tests__/20_padding_resize_tests.lua @@ -0,0 +1,240 @@ +-- Test padding resize behavior with percentage units +local luaunit = require("testing.luaunit") +local FlexLove = require("FlexLove") + +TestPaddingResize = {} + +function TestPaddingResize:setUp() + -- Reset GUI state before each test + FlexLove.Gui.destroy() + + -- Set up a consistent viewport size + love.window.setMode(1920, 1080) + + -- Initialize with base scaling + FlexLove.Gui.init({ + baseScale = { width = 1920, height = 1080 } + }) +end + +function TestPaddingResize:tearDown() + FlexLove.Gui.destroy() +end + +-- Test that percentage padding recalculates on resize +function TestPaddingResize:testPercentagePaddingHorizontalResize() + -- Create parent with percentage padding + local parent = FlexLove.Element.new({ + x = 0, + y = 0, + width = "100%", + height = "100%", + padding = { horizontal = "10%", vertical = "5%" }, + }) + + -- Initial padding should be 10% of 1920 = 192px (horizontal), 5% of 1080 = 54px (vertical) + luaunit.assertAlmostEquals(parent.padding.left, 192, 1, "Initial left padding should be 10% of 1920") + luaunit.assertAlmostEquals(parent.padding.right, 192, 1, "Initial right padding should be 10% of 1920") + luaunit.assertAlmostEquals(parent.padding.top, 54, 1, "Initial top padding should be 5% of 1080") + luaunit.assertAlmostEquals(parent.padding.bottom, 54, 1, "Initial bottom padding should be 5% of 1080") + + -- Resize to larger viewport + parent:resize(2560, 1440) + + -- Padding should recalculate: 10% of 2560 = 256px (horizontal), 5% of 1440 = 72px (vertical) + luaunit.assertAlmostEquals(parent.padding.left, 256, 1, "After resize, left padding should be 10% of 2560") + luaunit.assertAlmostEquals(parent.padding.right, 256, 1, "After resize, right padding should be 10% of 2560") + luaunit.assertAlmostEquals(parent.padding.top, 72, 1, "After resize, top padding should be 5% of 1440") + luaunit.assertAlmostEquals(parent.padding.bottom, 72, 1, "After resize, bottom padding should be 5% of 1440") + + -- Resize to smaller viewport + parent:resize(1280, 720) + + -- Padding should recalculate: 10% of 1280 = 128px (horizontal), 5% of 720 = 36px (vertical) + luaunit.assertAlmostEquals(parent.padding.left, 128, 1, "After second resize, left padding should be 10% of 1280") + luaunit.assertAlmostEquals(parent.padding.right, 128, 1, "After second resize, right padding should be 10% of 1280") + luaunit.assertAlmostEquals(parent.padding.top, 36, 1, "After second resize, top padding should be 5% of 720") + luaunit.assertAlmostEquals(parent.padding.bottom, 36, 1, "After second resize, bottom padding should be 5% of 720") +end + +-- Test that pixel padding with fixed dimensions doesn't shrink on resize +function TestPaddingResize:testPixelPaddingFixedDimensions() + -- Create element with pixel padding and fixed dimensions + local element = FlexLove.Element.new({ + x = 0, + y = 0, + width = 160, + height = 40, + padding = { horizontal = 12, vertical = 8 }, + }) + + -- Store initial dimensions + local initialWidth = element.width + local initialHeight = element.height + local initialPaddingLeft = element.padding.left + local initialPaddingTop = element.padding.top + + -- Resize multiple times + for i = 1, 5 do + element:resize(1920 + i * 100, 1080 + i * 50) + end + + -- Dimensions should scale with base scaling but not shrink progressively + luaunit.assertTrue( + element.width >= initialWidth * 0.9, + string.format("Width should not shrink significantly. Initial: %f, Current: %f", initialWidth, element.width) + ) + luaunit.assertTrue( + element.height >= initialHeight * 0.9, + string.format("Height should not shrink significantly. Initial: %f, Current: %f", initialHeight, element.height) + ) +end + +-- Test nested elements with percentage padding +function TestPaddingResize:testNestedPercentagePadding() + -- Create parent with percentage padding + local parent = FlexLove.Element.new({ + x = 0, + y = 0, + width = "100%", + height = "100%", + padding = { horizontal = "10%", vertical = "10%" }, + positioning = FlexLove.Positioning.FLEX, + flexDirection = FlexLove.FlexDirection.VERTICAL, + }) + + -- Create child with percentage padding (relative to parent's content area) + local child = FlexLove.Element.new({ + parent = parent, + width = "80%", + height = "50%", + padding = { horizontal = "5%", vertical = "5%" }, + }) + + -- Store initial child padding + local initialChildPaddingLeft = child.padding.left + local initialChildPaddingTop = child.padding.top + + -- Resize + parent:resize(2560, 1440) + + -- Child padding should recalculate based on parent's new content area + -- Parent content width after padding: 2560 - 2*(10% of 2560) = 2560 - 512 = 2048 + -- Child width: 80% of 2048 = 1638.4 + -- Child horizontal padding: 5% of 1638.4 = 81.92 + luaunit.assertTrue( + child.padding.left > initialChildPaddingLeft, + string.format( + "Child left padding should increase after resize. Initial: %f, Current: %f", + initialChildPaddingLeft, + child.padding.left + ) + ) + luaunit.assertTrue( + child.padding.top > initialChildPaddingTop, + string.format( + "Child top padding should increase after resize. Initial: %f, Current: %f", + initialChildPaddingTop, + child.padding.top + ) + ) +end + +-- Test that percentage padding doesn't cause progressive shrinkage +function TestPaddingResize:testNoProgressiveShrinkage() + -- Create element with percentage padding + local element = FlexLove.Element.new({ + x = 0, + y = 0, + width = "100%", + height = "100%", + padding = { horizontal = "10%", vertical = "10%" }, + }) + + -- Store initial content dimensions + local initialContentWidth = element.width + local initialContentHeight = element.height + + -- Resize back to original size multiple times + for i = 1, 10 do + element:resize(2560, 1440) -- Larger + element:resize(1920, 1080) -- Back to original + end + + -- Content dimensions should return to original (no progressive shrinkage) + luaunit.assertAlmostEquals( + element.width, + initialContentWidth, + 5, + string.format( + "Content width should return to original after multiple resizes. Initial: %f, Current: %f", + initialContentWidth, + element.width + ) + ) + luaunit.assertAlmostEquals( + element.height, + initialContentHeight, + 5, + string.format( + "Content height should return to original after multiple resizes. Initial: %f, Current: %f", + initialContentHeight, + element.height + ) + ) +end + +-- Test viewport-relative padding (vw/vh) +function TestPaddingResize:testViewportRelativePadding() + -- Create element with viewport-relative padding + local element = FlexLove.Element.new({ + x = 0, + y = 0, + width = "50%", + height = "50%", + padding = { horizontal = "2vw", vertical = "3vh" }, + }) + + -- Initial padding: 2vw of 1920 = 38.4px, 3vh of 1080 = 32.4px + luaunit.assertAlmostEquals(element.padding.left, 38.4, 1, "Initial left padding should be 2vw of 1920") + luaunit.assertAlmostEquals(element.padding.top, 32.4, 1, "Initial top padding should be 3vh of 1080") + + -- Resize + element:resize(2560, 1440) + + -- Padding should recalculate: 2vw of 2560 = 51.2px, 3vh of 1440 = 43.2px + luaunit.assertAlmostEquals(element.padding.left, 51.2, 1, "After resize, left padding should be 2vw of 2560") + luaunit.assertAlmostEquals(element.padding.top, 43.2, 1, "After resize, top padding should be 3vh of 1440") +end + +-- Test individual side padding with different units +function TestPaddingResize:testMixedPaddingUnits() + -- Create element with mixed padding units + local element = FlexLove.Element.new({ + x = 0, + y = 0, + width = "100%", + height = "100%", + padding = { top = "5%", right = "2vw", bottom = 20, left = "3vh" }, + }) + + -- Store initial padding + local initialTop = element.padding.top + local initialRight = element.padding.right + local initialBottom = element.padding.bottom + local initialLeft = element.padding.left + + -- Resize + element:resize(2560, 1440) + + -- Check that each side recalculates according to its unit + luaunit.assertTrue(initialTop < element.padding.top, "Top padding (%) should increase") + luaunit.assertTrue(initialRight < element.padding.right, "Right padding (vw) should increase") + luaunit.assertTrue( + math.abs(initialBottom - element.padding.bottom) < 1, + "Bottom padding (px) should remain roughly the same with base scaling" + ) + luaunit.assertTrue(initialLeft < element.padding.left, "Left padding (vh) should increase") +end + +return TestPaddingResize