disambiguate, reintroduce removed method
This commit is contained in:
83
FlexLove.lua
83
FlexLove.lua
@@ -27,6 +27,27 @@ function Color:toRGBA()
|
|||||||
return self.r, self.g, self.b, self.a
|
return self.r, self.g, self.b, self.a
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Convert hex string to color
|
||||||
|
---@param hexWithTag string -- e.g. "#RRGGBB" or "#RRGGBBAA"
|
||||||
|
---@return Color
|
||||||
|
function Color.fromHex(hexWithTag)
|
||||||
|
local hex = hexWithTag:gsub("#", "")
|
||||||
|
if #hex == 6 then
|
||||||
|
local r = tonumber("0x" .. hex:sub(1, 2)) or 0
|
||||||
|
local g = tonumber("0x" .. hex:sub(3, 4)) or 0
|
||||||
|
local b = tonumber("0x" .. hex:sub(5, 6)) or 0
|
||||||
|
return Color.new(r, g, b, 1)
|
||||||
|
elseif #hex == 8 then
|
||||||
|
local r = tonumber("0x" .. hex:sub(1, 2)) or 0
|
||||||
|
local g = tonumber("0x" .. hex:sub(3, 4)) or 0
|
||||||
|
local b = tonumber("0x" .. hex:sub(5, 6)) or 0
|
||||||
|
local a = tonumber("0x" .. hex:sub(7, 8)) / 255
|
||||||
|
return Color.new(r, g, b, a)
|
||||||
|
else
|
||||||
|
error("Invalid hex string")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local enums = {
|
local enums = {
|
||||||
---@enum TextAlign
|
---@enum TextAlign
|
||||||
TextAlign = { START = "start", CENTER = "center", END = "end", JUSTIFY = "justify" },
|
TextAlign = { START = "start", CENTER = "center", END = "end", JUSTIFY = "justify" },
|
||||||
@@ -83,14 +104,14 @@ local enums = {
|
|||||||
FlexWrap = { NOWRAP = "nowrap", WRAP = "wrap", WRAP_REVERSE = "wrap-reverse" },
|
FlexWrap = { NOWRAP = "nowrap", WRAP = "wrap", WRAP_REVERSE = "wrap-reverse" },
|
||||||
---@enum GridAutoFlow
|
---@enum GridAutoFlow
|
||||||
GridAutoFlow = { ROW = "row", COLUMN = "column", ROW_DENSE = "row dense", COLUMN_DENSE = "column dense" },
|
GridAutoFlow = { ROW = "row", COLUMN = "column", ROW_DENSE = "row dense", COLUMN_DENSE = "column dense" },
|
||||||
---@enum JustifyItems
|
---@enum GridJustifyItems
|
||||||
JustifyItems = {
|
GridJustifyItems = {
|
||||||
STRETCH = "stretch",
|
STRETCH = "stretch",
|
||||||
START = "start",
|
START = "start",
|
||||||
END = "end",
|
END = "end",
|
||||||
CENTER = "center",
|
CENTER = "center",
|
||||||
},
|
},
|
||||||
---@enum AlignContent (Grid)
|
---@enum GridAlignContent
|
||||||
GridAlignContent = {
|
GridAlignContent = {
|
||||||
STRETCH = "stretch",
|
STRETCH = "stretch",
|
||||||
START = "start",
|
START = "start",
|
||||||
@@ -100,7 +121,7 @@ local enums = {
|
|||||||
SPACE_AROUND = "space-around",
|
SPACE_AROUND = "space-around",
|
||||||
SPACE_EVENLY = "space-evenly",
|
SPACE_EVENLY = "space-evenly",
|
||||||
},
|
},
|
||||||
---@enum JustifyContent (Grid)
|
---@enum GridJustifyContent
|
||||||
GridJustifyContent = {
|
GridJustifyContent = {
|
||||||
STRETCH = "stretch",
|
STRETCH = "stretch",
|
||||||
START = "start",
|
START = "start",
|
||||||
@@ -112,7 +133,7 @@ local enums = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local Positioning, FlexDirection, JustifyContent, AlignContent, AlignItems, TextAlign, AlignSelf, JustifySelf, FlexWrap, GridAutoFlow, JustifyItems, GridAlignContent, GridJustifyContent =
|
local Positioning, FlexDirection, JustifyContent, AlignContent, AlignItems, TextAlign, AlignSelf, JustifySelf, FlexWrap, GridAutoFlow, GridJustifyItems, GridAlignContent, GridJustifyContent =
|
||||||
enums.Positioning,
|
enums.Positioning,
|
||||||
enums.FlexDirection,
|
enums.FlexDirection,
|
||||||
enums.JustifyContent,
|
enums.JustifyContent,
|
||||||
@@ -123,7 +144,7 @@ local Positioning, FlexDirection, JustifyContent, AlignContent, AlignItems, Text
|
|||||||
enums.JustifySelf,
|
enums.JustifySelf,
|
||||||
enums.FlexWrap,
|
enums.FlexWrap,
|
||||||
enums.GridAutoFlow,
|
enums.GridAutoFlow,
|
||||||
enums.JustifyItems,
|
enums.GridJustifyItems,
|
||||||
enums.GridAlignContent,
|
enums.GridAlignContent,
|
||||||
enums.GridJustifyContent
|
enums.GridJustifyContent
|
||||||
|
|
||||||
@@ -618,13 +639,7 @@ function Grid.layoutGridItems(element)
|
|||||||
for _, child in ipairs(element.children) do
|
for _, child in ipairs(element.children) do
|
||||||
-- Skip explicitly absolute positioned children
|
-- Skip explicitly absolute positioned children
|
||||||
if not (child.positioning == Positioning.ABSOLUTE and child._explicitlyAbsolute) then
|
if not (child.positioning == Positioning.ABSOLUTE and child._explicitlyAbsolute) then
|
||||||
local placement = Grid.calculateItemPlacement(
|
local placement = Grid.calculateItemPlacement(child, #columnSizes, #rowSizes, autoPlacementCursor, gridAutoFlow)
|
||||||
child,
|
|
||||||
#columnSizes,
|
|
||||||
#rowSizes,
|
|
||||||
autoPlacementCursor,
|
|
||||||
gridAutoFlow
|
|
||||||
)
|
|
||||||
|
|
||||||
-- Ensure placement is within bounds, expand grid if necessary
|
-- Ensure placement is within bounds, expand grid if necessary
|
||||||
local columnStart = math.max(1, math.min(placement.columnStart, #columnSizes + 1))
|
local columnStart = math.max(1, math.min(placement.columnStart, #columnSizes + 1))
|
||||||
@@ -639,19 +654,19 @@ function Grid.layoutGridItems(element)
|
|||||||
local itemHeight = (rowPositions[rowEnd] or (element.y + element.height)) - itemY - rowGap
|
local itemHeight = (rowPositions[rowEnd] or (element.y + element.height)) - itemY - rowGap
|
||||||
|
|
||||||
-- Apply alignment within grid cell
|
-- Apply alignment within grid cell
|
||||||
local effectiveJustifySelf = child.justifySelf or element.justifyItems or JustifyItems.STRETCH
|
local effectiveJustifySelf = child.justifySelf or element.justifyItems or GridJustifyItems.STRETCH
|
||||||
local effectiveAlignSelf = child.alignSelf or element.alignItems or AlignItems.STRETCH
|
local effectiveAlignSelf = child.alignSelf or element.alignItems or AlignItems.STRETCH
|
||||||
|
|
||||||
-- Handle justifySelf (horizontal alignment)
|
-- Handle justifySelf (horizontal alignment)
|
||||||
if effectiveJustifySelf == JustifyItems.STRETCH or effectiveJustifySelf == "stretch" then
|
if effectiveJustifySelf == GridJustifyItems.STRETCH or effectiveJustifySelf == "stretch" then
|
||||||
child.x = itemX + child.padding.left
|
child.x = itemX + child.padding.left
|
||||||
child.width = itemWidth - child.padding.left - child.padding.right
|
child.width = itemWidth - child.padding.left - child.padding.right
|
||||||
elseif effectiveJustifySelf == JustifyItems.START or effectiveJustifySelf == "start" or effectiveJustifySelf == "flex-start" then
|
elseif effectiveJustifySelf == GridJustifyItems.START or effectiveJustifySelf == "start" or effectiveJustifySelf == "flex-start" then
|
||||||
child.x = itemX + child.padding.left
|
child.x = itemX + child.padding.left
|
||||||
-- Keep child's natural width
|
-- Keep child's natural width
|
||||||
elseif effectiveJustifySelf == JustifyItems.END or effectiveJustifySelf == "end" or effectiveJustifySelf == "flex-end" then
|
elseif effectiveJustifySelf == GridJustifyItems.END or effectiveJustifySelf == "end" or effectiveJustifySelf == "flex-end" then
|
||||||
child.x = itemX + itemWidth - child.width - child.padding.right
|
child.x = itemX + itemWidth - child.width - child.padding.right
|
||||||
elseif effectiveJustifySelf == JustifyItems.CENTER or effectiveJustifySelf == "center" then
|
elseif effectiveJustifySelf == GridJustifyItems.CENTER or effectiveJustifySelf == "center" then
|
||||||
child.x = itemX + (itemWidth - child.width) / 2
|
child.x = itemX + (itemWidth - child.width) / 2
|
||||||
else
|
else
|
||||||
-- Default to stretch
|
-- Default to stretch
|
||||||
@@ -663,10 +678,18 @@ function Grid.layoutGridItems(element)
|
|||||||
if effectiveAlignSelf == AlignItems.STRETCH or effectiveAlignSelf == "stretch" then
|
if effectiveAlignSelf == AlignItems.STRETCH or effectiveAlignSelf == "stretch" then
|
||||||
child.y = itemY + child.padding.top
|
child.y = itemY + child.padding.top
|
||||||
child.height = itemHeight - child.padding.top - child.padding.bottom
|
child.height = itemHeight - child.padding.top - child.padding.bottom
|
||||||
elseif effectiveAlignSelf == AlignItems.FLEX_START or effectiveAlignSelf == "flex-start" or effectiveAlignSelf == "start" then
|
elseif
|
||||||
|
effectiveAlignSelf == AlignItems.FLEX_START
|
||||||
|
or effectiveAlignSelf == "flex-start"
|
||||||
|
or effectiveAlignSelf == "start"
|
||||||
|
then
|
||||||
child.y = itemY + child.padding.top
|
child.y = itemY + child.padding.top
|
||||||
-- Keep child's natural height
|
-- Keep child's natural height
|
||||||
elseif effectiveAlignSelf == AlignItems.FLEX_END or effectiveAlignSelf == "flex-end" or effectiveAlignSelf == "end" then
|
elseif
|
||||||
|
effectiveAlignSelf == AlignItems.FLEX_END
|
||||||
|
or effectiveAlignSelf == "flex-end"
|
||||||
|
or effectiveAlignSelf == "end"
|
||||||
|
then
|
||||||
child.y = itemY + itemHeight - child.height - child.padding.bottom
|
child.y = itemY + itemHeight - child.height - child.padding.bottom
|
||||||
elseif effectiveAlignSelf == AlignItems.CENTER or effectiveAlignSelf == "center" then
|
elseif effectiveAlignSelf == AlignItems.CENTER or effectiveAlignSelf == "center" then
|
||||||
child.y = itemY + (itemHeight - child.height) / 2
|
child.y = itemY + (itemHeight - child.height) / 2
|
||||||
@@ -982,7 +1005,7 @@ end
|
|||||||
---@field gridColumn string|number? -- Grid item column placement
|
---@field gridColumn string|number? -- Grid item column placement
|
||||||
---@field gridRow string|number? -- Grid item row placement
|
---@field gridRow string|number? -- Grid item row placement
|
||||||
---@field gridArea string? -- Grid item named area placement
|
---@field gridArea string? -- Grid item named area placement
|
||||||
---@field justifyItems JustifyItems? -- Default horizontal alignment for grid items
|
---@field justifyItems GridJustifyItems? -- Default horizontal alignment for grid items
|
||||||
---@field alignItems AlignItems? -- Default vertical alignment for grid items
|
---@field alignItems AlignItems? -- Default vertical alignment for grid items
|
||||||
local Element = {}
|
local Element = {}
|
||||||
Element.__index = Element
|
Element.__index = Element
|
||||||
@@ -1033,7 +1056,7 @@ Element.__index = Element
|
|||||||
---@field gridColumn string|number? -- Grid item column placement (e.g., "1", "2 / 4", "span 2")
|
---@field gridColumn string|number? -- Grid item column placement (e.g., "1", "2 / 4", "span 2")
|
||||||
---@field gridRow string|number? -- Grid item row placement
|
---@field gridRow string|number? -- Grid item row placement
|
||||||
---@field gridArea string? -- Grid item named area placement
|
---@field gridArea string? -- Grid item named area placement
|
||||||
---@field justifyItems JustifyItems? -- Default horizontal alignment for grid items
|
---@field justifyItems GridJustifyItems? -- Default horizontal alignment for grid items
|
||||||
local ElementProps = {}
|
local ElementProps = {}
|
||||||
|
|
||||||
---@param props ElementProps
|
---@param props ElementProps
|
||||||
@@ -1516,7 +1539,7 @@ function Element.new(props)
|
|||||||
self.gridAutoRows = props.gridAutoRows or "auto"
|
self.gridAutoRows = props.gridAutoRows or "auto"
|
||||||
self.justifyContent = props.justifyContent or GridJustifyContent.START
|
self.justifyContent = props.justifyContent or GridJustifyContent.START
|
||||||
self.alignContent = props.alignContent or GridAlignContent.START
|
self.alignContent = props.alignContent or GridAlignContent.START
|
||||||
self.justifyItems = props.justifyItems or JustifyItems.STRETCH
|
self.justifyItems = props.justifyItems or GridJustifyItems.STRETCH
|
||||||
self.alignItems = props.alignItems or AlignItems.STRETCH
|
self.alignItems = props.alignItems or AlignItems.STRETCH
|
||||||
|
|
||||||
-- Handle columnGap and rowGap
|
-- Handle columnGap and rowGap
|
||||||
@@ -1580,11 +1603,15 @@ function Element:addChild(child)
|
|||||||
|
|
||||||
table.insert(self.children, child)
|
table.insert(self.children, child)
|
||||||
|
|
||||||
if self.autosizing.height then
|
-- Only recalculate auto-sizing if the child participates in layout
|
||||||
self.height = self:calculateAutoHeight()
|
-- (CSS: absolutely positioned children don't affect parent auto-sizing)
|
||||||
end
|
if not child._explicitlyAbsolute then
|
||||||
if self.autosizing.width then
|
if self.autosizing.height then
|
||||||
self.width = self:calculateAutoWidth()
|
self.height = self:calculateAutoHeight()
|
||||||
|
end
|
||||||
|
if self.autosizing.width then
|
||||||
|
self.width = self:calculateAutoWidth()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self:layoutChildren()
|
self:layoutChildren()
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ function TestAbsolutePositioningBasic:testDefaultAbsolutePositioning()
|
|||||||
height = 100,
|
height = 100,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Default should be relative positioning
|
-- Default should be absolute positioning (RELATIVE not yet implemented)
|
||||||
luaunit.assertEquals(elem.positioning, Positioning.RELATIVE)
|
luaunit.assertEquals(elem.positioning, Positioning.ABSOLUTE)
|
||||||
luaunit.assertEquals(elem.x, 50)
|
luaunit.assertEquals(elem.x, 50)
|
||||||
luaunit.assertEquals(elem.y, 75)
|
luaunit.assertEquals(elem.y, 75)
|
||||||
end
|
end
|
||||||
@@ -288,7 +288,7 @@ function TestAbsolutePositioningBasic:testAbsoluteChildNoParentAutoSizeAffect()
|
|||||||
positioning = Positioning.ABSOLUTE,
|
positioning = Positioning.ABSOLUTE,
|
||||||
})
|
})
|
||||||
|
|
||||||
local originalParentWidtheight = parent.width
|
local originalParentWidth = parent.width
|
||||||
local originalParentHeight = parent.height
|
local originalParentHeight = parent.height
|
||||||
|
|
||||||
local child = Gui.new({
|
local child = Gui.new({
|
||||||
@@ -747,7 +747,7 @@ function TestAbsolutePositioningBasic:testAsymmetricAbsoluteTree()
|
|||||||
})
|
})
|
||||||
|
|
||||||
-- Left branch: deep nesting
|
-- Left branch: deep nesting
|
||||||
local leftBrancheight = Gui.new({
|
local leftBranch = Gui.new({
|
||||||
parent = root,
|
parent = root,
|
||||||
id = "leftBranch",
|
id = "leftBranch",
|
||||||
x = 100,
|
x = 100,
|
||||||
@@ -792,7 +792,7 @@ function TestAbsolutePositioningBasic:testAsymmetricAbsoluteTree()
|
|||||||
})
|
})
|
||||||
|
|
||||||
-- Right branch: wide shallow
|
-- Right branch: wide shallow
|
||||||
local rightBrancheight = Gui.new({
|
local rightBranch = Gui.new({
|
||||||
parent = root,
|
parent = root,
|
||||||
id = "rightBranch",
|
id = "rightBranch",
|
||||||
x = 800,
|
x = 800,
|
||||||
|
|||||||
@@ -868,7 +868,7 @@ function TestAbsolutePositioningChildLayout:testGridStructureAbsolutePositioning
|
|||||||
|
|
||||||
-- Create grid cells
|
-- Create grid cells
|
||||||
local cells = {}
|
local cells = {}
|
||||||
for rowidth = 1, rows do
|
for row = 1, rows do
|
||||||
cells[row] = {}
|
cells[row] = {}
|
||||||
for col = 1, cols do
|
for col = 1, cols do
|
||||||
local x = (col - 1) * (cellWidth + gap)
|
local x = (col - 1) * (cellWidth + gap)
|
||||||
@@ -927,7 +927,7 @@ function TestAbsolutePositioningChildLayout:testGridStructureAbsolutePositioning
|
|||||||
-- Verify grid structure
|
-- Verify grid structure
|
||||||
luaunit.assertEquals(#grid.children, rows * cols)
|
luaunit.assertEquals(#grid.children, rows * cols)
|
||||||
|
|
||||||
for rowidth = 1, rows do
|
for row = 1, rows do
|
||||||
for col = 1, cols do
|
for col = 1, cols do
|
||||||
local cell = cells[row][col]
|
local cell = cells[row][col]
|
||||||
local expectedX = (col - 1) * (cellWidth + gap)
|
local expectedX = (col - 1) * (cellWidth + gap)
|
||||||
|
|||||||
@@ -748,16 +748,18 @@ function TestAlignItems:testComplexCardLayoutMixedAlignItems()
|
|||||||
luaunit.assertEquals(title.y, 23) -- Same center alignment
|
luaunit.assertEquals(title.y, 23) -- Same center alignment
|
||||||
|
|
||||||
-- Verify actions buttons have FLEX_START alignment
|
-- Verify actions buttons have FLEX_START alignment
|
||||||
luaunit.assertEquals(btn1.y, 10) -- Start of actions container
|
-- actions is centered in header: header.y (10) + (header.height (50) - actions.height (30)) / 2 = 20
|
||||||
luaunit.assertEquals(btn2.y, 10) -- Same start position
|
luaunit.assertEquals(btn1.y, 20) -- Start of actions container
|
||||||
|
luaunit.assertEquals(btn2.y, 20) -- Same start position
|
||||||
|
|
||||||
-- Verify content alignment (FLEX_END)
|
-- Verify content alignment (FLEX_END)
|
||||||
luaunit.assertEquals(contentText.x, 60) -- 300 - 250 = 50, plus card.x = 10 + 50 = 60
|
luaunit.assertEquals(contentText.x, 60) -- 300 - 250 = 50, plus card.x = 10 + 50 = 60
|
||||||
luaunit.assertEquals(metadata.x, 130) -- 300 - 180 = 120, plus card.x = 10 + 120 = 130
|
luaunit.assertEquals(metadata.x, 130) -- 300 - 180 = 120, plus card.x = 10 + 120 = 130
|
||||||
|
|
||||||
-- Verify footer center alignment
|
-- Verify footer center alignment
|
||||||
luaunit.assertEquals(timestamp.y, 175) -- Footer center: (30 - 16) / 2 = 7, plus footer.y = 168 + 7 = 175
|
-- footer.y = card.y (10) + header.height (50) + gap (10) + content.height (120) + gap (10) = 200
|
||||||
luaunit.assertEquals(status.y, 173) -- Footer center: (30 - 20) / 2 = 5, plus footer.y = 168 + 5 = 173
|
luaunit.assertEquals(timestamp.y, 207) -- Footer center: (30 - 16) / 2 = 7, plus footer.y = 200 + 7 = 207
|
||||||
|
luaunit.assertEquals(status.y, 205) -- Footer center: (30 - 20) / 2 = 5, plus footer.y = 200 + 5 = 205
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Test 17: Complex Media Object Pattern with Nested Alignments
|
-- Test 17: Complex Media Object Pattern with Nested Alignments
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ function TestGridLayout:test_justify_items_stretch()
|
|||||||
positioning = enums.Positioning.GRID,
|
positioning = enums.Positioning.GRID,
|
||||||
gridTemplateColumns = "100px 100px 100px",
|
gridTemplateColumns = "100px 100px 100px",
|
||||||
gridTemplateRows = "100px",
|
gridTemplateRows = "100px",
|
||||||
justifyItems = enums.JustifyItems.STRETCH,
|
justifyItems = enums.GridJustifyItems.STRETCH,
|
||||||
columnGap = 0,
|
columnGap = 0,
|
||||||
rowGap = 0,
|
rowGap = 0,
|
||||||
padding = { horizontal = 0, vertical = 0 },
|
padding = { horizontal = 0, vertical = 0 },
|
||||||
|
|||||||
Reference in New Issue
Block a user