stopping for the night

This commit is contained in:
Michael Freno
2025-10-14 01:58:03 -04:00
parent 3cc416dcff
commit 23ccabcbbd
4 changed files with 171 additions and 101 deletions

View File

@@ -644,7 +644,7 @@ function Theme.getColorNames()
if not activeTheme or not activeTheme.colors then if not activeTheme or not activeTheme.colors then
return nil return nil
end end
local colorNames = {} local colorNames = {}
for name, _ in pairs(activeTheme.colors) do for name, _ in pairs(activeTheme.colors) do
table.insert(colorNames, name) table.insert(colorNames, name)
@@ -658,7 +658,7 @@ function Theme.getAllColors()
if not activeTheme then if not activeTheme then
return nil return nil
end end
return activeTheme.colors return activeTheme.colors
end end
@@ -671,7 +671,7 @@ function Theme.getColorOrDefault(colorName, fallback)
if color then if color then
return color return color
end end
return fallback or Color.new(1, 1, 1, 1) return fallback or Color.new(1, 1, 1, 1)
end end
@@ -2649,28 +2649,37 @@ function Element:applyPositioningOffsets(element)
return return
end end
-- Apply top offset (distance from parent's content box top edge) -- Only apply offsets to explicitly absolute children or children in relative/absolute containers
if element.top then -- Flex/grid children ignore positioning offsets as they participate in layout
element.y = parent.y + parent.padding.top + element.top local isFlexChild = element.positioning == Positioning.FLEX
end or element.positioning == Positioning.GRID
or (element.positioning == Positioning.ABSOLUTE and not element._explicitlyAbsolute)
-- Apply bottom offset (distance from parent's content box bottom edge) if not isFlexChild then
-- BORDER-BOX MODEL: Use border-box dimensions for positioning -- Apply absolute positioning for explicitly absolute children
if element.bottom then -- Apply top offset (distance from parent's content box top edge)
local elementBorderBoxHeight = element:getBorderBoxHeight() if element.top then
element.y = parent.y + parent.padding.top + parent.height - element.bottom - elementBorderBoxHeight element.y = parent.y + parent.padding.top + element.top
end end
-- Apply left offset (distance from parent's content box left edge) -- Apply bottom offset (distance from parent's content box bottom edge)
if element.left then -- BORDER-BOX MODEL: Use border-box dimensions for positioning
element.x = parent.x + parent.padding.left + element.left if element.bottom then
end local elementBorderBoxHeight = element:getBorderBoxHeight()
element.y = parent.y + parent.padding.top + parent.height - element.bottom - elementBorderBoxHeight
end
-- Apply right offset (distance from parent's content box right edge) -- Apply left offset (distance from parent's content box left edge)
-- BORDER-BOX MODEL: Use border-box dimensions for positioning if element.left then
if element.right then element.x = parent.x + parent.padding.left + element.left
local elementBorderBoxWidth = element:getBorderBoxWidth() end
element.x = parent.x + parent.padding.left + parent.width - element.right - elementBorderBoxWidth
-- Apply right offset (distance from parent's content box right edge)
-- BORDER-BOX MODEL: Use border-box dimensions for positioning
if element.right then
local elementBorderBoxWidth = element:getBorderBoxWidth()
element.x = parent.x + parent.padding.left + parent.width - element.right - elementBorderBoxWidth
end
end end
end end
@@ -2797,26 +2806,31 @@ function Element:layoutChildren()
for _, child in ipairs(flexChildren) do for _, child in ipairs(flexChildren) do
-- BORDER-BOX MODEL: Use border-box dimensions for layout calculations -- BORDER-BOX MODEL: Use border-box dimensions for layout calculations
-- Include margins in size calculations
local childMainSize = 0 local childMainSize = 0
local childMainMargin = 0
if self.flexDirection == FlexDirection.HORIZONTAL then if self.flexDirection == FlexDirection.HORIZONTAL then
childMainSize = child:getBorderBoxWidth() childMainSize = child:getBorderBoxWidth()
childMainMargin = child.margin.left + child.margin.right
else else
childMainSize = child:getBorderBoxHeight() childMainSize = child:getBorderBoxHeight()
childMainMargin = child.margin.top + child.margin.bottom
end end
local childTotalMainSize = childMainSize + childMainMargin
-- Check if adding this child would exceed the available space -- Check if adding this child would exceed the available space
local lineSpacing = #currentLine > 0 and self.gap or 0 local lineSpacing = #currentLine > 0 and self.gap or 0
if #currentLine > 0 and currentLineSize + lineSpacing + childMainSize > availableMainSize then if #currentLine > 0 and currentLineSize + lineSpacing + childTotalMainSize > availableMainSize then
-- Start a new line -- Start a new line
if #currentLine > 0 then if #currentLine > 0 then
table.insert(lines, currentLine) table.insert(lines, currentLine)
end end
currentLine = { child } currentLine = { child }
currentLineSize = childMainSize currentLineSize = childTotalMainSize
else else
-- Add to current line -- Add to current line
table.insert(currentLine, child) table.insert(currentLine, child)
currentLineSize = currentLineSize + lineSpacing + childMainSize currentLineSize = currentLineSize + lineSpacing + childTotalMainSize
end end
end end
@@ -2843,13 +2857,18 @@ function Element:layoutChildren()
local maxCrossSize = 0 local maxCrossSize = 0
for _, child in ipairs(line) do for _, child in ipairs(line) do
-- BORDER-BOX MODEL: Use border-box dimensions for layout calculations -- BORDER-BOX MODEL: Use border-box dimensions for layout calculations
-- Include margins in cross-axis size calculations
local childCrossSize = 0 local childCrossSize = 0
local childCrossMargin = 0
if self.flexDirection == FlexDirection.HORIZONTAL then if self.flexDirection == FlexDirection.HORIZONTAL then
childCrossSize = child:getBorderBoxHeight() childCrossSize = child:getBorderBoxHeight()
childCrossMargin = child.margin.top + child.margin.bottom
else else
childCrossSize = child:getBorderBoxWidth() childCrossSize = child:getBorderBoxWidth()
childCrossMargin = child.margin.left + child.margin.right
end end
maxCrossSize = math.max(maxCrossSize, childCrossSize) local childTotalCrossSize = childCrossSize + childCrossMargin
maxCrossSize = math.max(maxCrossSize, childTotalCrossSize)
end end
lineHeights[lineIndex] = maxCrossSize lineHeights[lineIndex] = maxCrossSize
totalLinesHeight = totalLinesHeight + maxCrossSize totalLinesHeight = totalLinesHeight + maxCrossSize
@@ -2913,14 +2932,14 @@ function Element:layoutChildren()
for lineIndex, line in ipairs(lines) do for lineIndex, line in ipairs(lines) do
local lineHeight = lineHeights[lineIndex] local lineHeight = lineHeights[lineIndex]
-- Calculate total size of children in this line (including padding) -- Calculate total size of children in this line (including padding and margins)
-- BORDER-BOX MODEL: Use border-box dimensions for layout calculations -- BORDER-BOX MODEL: Use border-box dimensions for layout calculations
local totalChildrenSize = 0 local totalChildrenSize = 0
for _, child in ipairs(line) do for _, child in ipairs(line) do
if self.flexDirection == FlexDirection.HORIZONTAL then if self.flexDirection == FlexDirection.HORIZONTAL then
totalChildrenSize = totalChildrenSize + child:getBorderBoxWidth() totalChildrenSize = totalChildrenSize + child:getBorderBoxWidth() + child.margin.left + child.margin.right
else else
totalChildrenSize = totalChildrenSize + child:getBorderBoxHeight() totalChildrenSize = totalChildrenSize + child:getBorderBoxHeight() + child.margin.top + child.margin.bottom
end end
end end
@@ -2966,30 +2985,39 @@ 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)
-- Add reservedMainStart to account for absolutely positioned siblings -- Add reservedMainStart and left margin to account for absolutely positioned siblings and margins
child.x = self.x + self.padding.left + reservedMainStart + currentMainPos child.x = self.x + self.padding.left + reservedMainStart + currentMainPos + child.margin.left
-- BORDER-BOX MODEL: Use border-box dimensions for alignment calculations -- BORDER-BOX MODEL: Use border-box dimensions for alignment calculations
local childBorderBoxHeight = child:getBorderBoxHeight() local childBorderBoxHeight = child:getBorderBoxHeight()
local childTotalCrossSize = childBorderBoxHeight + child.margin.top + child.margin.bottom
if effectiveAlign == AlignItems.FLEX_START then if effectiveAlign == AlignItems.FLEX_START then
child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos + child.margin.top
elseif effectiveAlign == AlignItems.CENTER then elseif effectiveAlign == AlignItems.CENTER then
child.y = self.y child.y = self.y
+ self.padding.top + self.padding.top
+ reservedCrossStart + reservedCrossStart
+ currentCrossPos + currentCrossPos
+ ((lineHeight - childBorderBoxHeight) / 2) + ((lineHeight - childTotalCrossSize) / 2)
+ child.margin.top
elseif effectiveAlign == AlignItems.FLEX_END then elseif effectiveAlign == AlignItems.FLEX_END then
child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos + lineHeight - childBorderBoxHeight child.y = self.y
+ self.padding.top
+ reservedCrossStart
+ currentCrossPos
+ lineHeight
- childTotalCrossSize
+ child.margin.top
elseif effectiveAlign == AlignItems.STRETCH then elseif effectiveAlign == AlignItems.STRETCH then
-- STRETCH: Only apply if height was not explicitly set -- STRETCH: Only apply if height was not explicitly set
if child.autosizing and child.autosizing.height then if child.autosizing and child.autosizing.height then
-- STRETCH: Set border-box height to lineHeight, content area shrinks to fit -- STRETCH: Set border-box height to lineHeight minus margins, content area shrinks to fit
child._borderBoxHeight = lineHeight local availableHeight = lineHeight - child.margin.top - child.margin.bottom
child.height = math.max(0, lineHeight - child.padding.top - child.padding.bottom) child._borderBoxHeight = availableHeight
child.height = math.max(0, availableHeight - child.padding.top - child.padding.bottom)
end end
child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos + child.margin.top
end end
-- Apply positioning offsets (top, right, bottom, left) -- Apply positioning offsets (top, right, bottom, left)
@@ -3005,35 +3033,48 @@ function Element:layoutChildren()
child:layoutChildren() child:layoutChildren()
end end
-- Advance position by child's border-box width -- Advance position by child's border-box width plus margins
currentMainPos = currentMainPos + child:getBorderBoxWidth() + itemSpacing currentMainPos = currentMainPos
+ child:getBorderBoxWidth()
+ child.margin.left
+ child.margin.right
+ itemSpacing
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)
-- Add reservedMainStart to account for absolutely positioned siblings -- Add reservedMainStart and top margin to account for absolutely positioned siblings and margins
child.y = self.y + self.padding.top + reservedMainStart + currentMainPos child.y = self.y + self.padding.top + reservedMainStart + currentMainPos + child.margin.top
-- BORDER-BOX MODEL: Use border-box dimensions for alignment calculations -- BORDER-BOX MODEL: Use border-box dimensions for alignment calculations
local childBorderBoxWidth = child:getBorderBoxWidth() local childBorderBoxWidth = child:getBorderBoxWidth()
local childTotalCrossSize = childBorderBoxWidth + child.margin.left + child.margin.right
if effectiveAlign == AlignItems.FLEX_START then if effectiveAlign == AlignItems.FLEX_START then
child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos + child.margin.left
elseif effectiveAlign == AlignItems.CENTER then elseif effectiveAlign == AlignItems.CENTER then
child.x = self.x child.x = self.x
+ self.padding.left + self.padding.left
+ reservedCrossStart + reservedCrossStart
+ currentCrossPos + currentCrossPos
+ ((lineHeight - childBorderBoxWidth) / 2) + ((lineHeight - childTotalCrossSize) / 2)
+ child.margin.left
elseif effectiveAlign == AlignItems.FLEX_END then elseif effectiveAlign == AlignItems.FLEX_END then
child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos + lineHeight - childBorderBoxWidth child.x = self.x
+ self.padding.left
+ reservedCrossStart
+ currentCrossPos
+ lineHeight
- childTotalCrossSize
+ child.margin.left
elseif effectiveAlign == AlignItems.STRETCH then elseif effectiveAlign == AlignItems.STRETCH then
-- STRETCH: Only apply if width was not explicitly set -- STRETCH: Only apply if width was not explicitly set
if child.autosizing and child.autosizing.width then if child.autosizing and child.autosizing.width then
-- STRETCH: Set border-box width to lineHeight, content area shrinks to fit -- STRETCH: Set border-box width to lineHeight minus margins, content area shrinks to fit
child._borderBoxWidth = lineHeight local availableWidth = lineHeight - child.margin.left - child.margin.right
child.width = math.max(0, lineHeight - child.padding.left - child.padding.right) child._borderBoxWidth = availableWidth
child.width = math.max(0, availableWidth - child.padding.left - child.padding.right)
end end
child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos + child.margin.left
end end
-- Apply positioning offsets (top, right, bottom, left) -- Apply positioning offsets (top, right, bottom, left)
@@ -3044,14 +3085,31 @@ function Element:layoutChildren()
child:layoutChildren() child:layoutChildren()
end end
-- Advance position by child's border-box height -- Advance position by child's border-box height plus margins
currentMainPos = currentMainPos + child:getBorderBoxHeight() + itemSpacing currentMainPos = currentMainPos
+ child:getBorderBoxHeight()
+ child.margin.top
+ child.margin.bottom
+ itemSpacing
end end
end end
-- Move to next line position -- Move to next line position
currentCrossPos = currentCrossPos + lineHeight + lineSpacing currentCrossPos = currentCrossPos + lineHeight + lineSpacing
end end
-- Position explicitly absolute children after flex layout
for _, child in ipairs(self.children) do
if child.positioning == Positioning.ABSOLUTE and child._explicitlyAbsolute then
-- Apply positioning offsets (top, right, bottom, left)
self:applyPositioningOffsets(child)
-- If child has children, layout them after position change
if #child.children > 0 then
child:layoutChildren()
end
end
end
end end
--- Destroy element and its children --- Destroy element and its children

View File

@@ -757,9 +757,9 @@ function TestAlignItems:testComplexCardLayoutMixedAlignItems()
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
-- footer.y = card.y (10) + header.height (50) + gap (10) + content.height (120) + gap (10) = 200 -- footer.y = card.y (10) + header.height (50) + content.height (120) = 180 (no gap specified)
luaunit.assertEquals(timestamp.y, 207) -- Footer center: (30 - 16) / 2 = 7, plus footer.y = 200 + 7 = 207 luaunit.assertEquals(timestamp.y, 187) -- Footer center: (30 - 16) / 2 = 7, plus footer.y = 180 + 7 = 187
luaunit.assertEquals(status.y, 205) -- Footer center: (30 - 20) / 2 = 5, plus footer.y = 200 + 5 = 205 luaunit.assertEquals(status.y, 185) -- Footer center: (30 - 20) / 2 = 5, plus footer.y = 180 + 5 = 185
end end
-- Test 17: Complex Media Object Pattern with Nested Alignments -- Test 17: Complex Media Object Pattern with Nested Alignments
@@ -961,11 +961,11 @@ function TestAlignItems:testComplexMediaObjectNestedAlignments()
luaunit.assertEquals(attachments.x, 120) -- 80 + (280 - 200) / 2 = 80 + 40 = 120 luaunit.assertEquals(attachments.x, 120) -- 80 + (280 - 200) / 2 = 80 + 40 = 120
-- Verify attachment items center alignment -- Verify attachment items center alignment
luaunit.assertEquals(attach1.y, 41) -- attachments.y + (15 - 12) / 2 = 40 + 1.5 ≈ 41 luaunit.assertEquals(attach1.y, 101.5) -- attachments.y (100) + (15 - 12) / 2 = 100 + 1.5 = 101.5
luaunit.assertEquals(attach2.y, 43) -- attachments.y + (15 - 8) / 2 = 40 + 3.5 ≈ 43 luaunit.assertEquals(attach2.y, 103.5) -- attachments.y (100) + (15 - 8) / 2 = 100 + 3.5 = 103.5
-- Verify actions footer center alignment -- Verify actions footer center alignment
luaunit.assertEquals(like.y, 125) -- actionsFooter.y + (30 - 16) / 2 = 120 + 7 = 127, but reactions also center luaunit.assertEquals(like.y, 127) -- actionsFooter.y (120) + reactions centered (5) + like centered (2) = 127
luaunit.assertEquals(moreActions.y, 123) -- actionsFooter.y + (30 - 24) / 2 = 120 + 3 = 123 luaunit.assertEquals(moreActions.y, 123) -- actionsFooter.y + (30 - 24) / 2 = 120 + 3 = 123
end end
@@ -1144,8 +1144,8 @@ function TestAlignItems:testComplexToolbarVariedAlignments()
-- Verify left section flex-start alignment -- Verify left section flex-start alignment
luaunit.assertEquals(logo.y, 0) -- Aligned to top luaunit.assertEquals(logo.y, 0) -- Aligned to top
luaunit.assertEquals(navItem1.y, 5) -- navigation.y + (50 - 30) / 2 = 5 + 10 = 15, but nav is at y=10 luaunit.assertEquals(navItem1.y, 10) -- navigation.y (0) + (50 - 30) / 2 = 0 + 10 = 10
luaunit.assertEquals(navItem2.y, 2) -- navigation.y + (50 - 35) / 2 = 5 + 7.5 ≈ 12 luaunit.assertEquals(navItem2.y, 7.5) -- navigation.y (0) + (50 - 35) / 2 = 0 + 7.5 = 7.5
-- Verify center section stretch alignment -- Verify center section stretch alignment
luaunit.assertEquals(searchInput.y, 4) -- searchContainer.y + (40 - 32) / 2 = 10 + 4 = 14 luaunit.assertEquals(searchInput.y, 4) -- searchContainer.y + (40 - 32) / 2 = 10 + 4 = 14
@@ -1157,11 +1157,11 @@ function TestAlignItems:testComplexToolbarVariedAlignments()
luaunit.assertEquals(userMenu.y, 15) -- (60 - 45) = 15 from bottom luaunit.assertEquals(userMenu.y, 15) -- (60 - 45) = 15 from bottom
-- Verify notification items center alignment -- Verify notification items center alignment
luaunit.assertEquals(notifIcon.x, 5) -- (30 - 20) / 2 = 5 luaunit.assertEquals(notifIcon.x, 455) -- rightSection.x (450) + notifications.x (0) + center offset (5) = 455
luaunit.assertEquals(notifBadge.x, 9) -- (30 - 12) / 2 = 9 luaunit.assertEquals(notifBadge.x, 459) -- rightSection.x (450) + notifications.x (0) + center offset (9) = 459
-- Verify user menu center alignment -- Verify user menu center alignment
luaunit.assertEquals(userAvatar.y, 21) -- userMenu.y + (45 - 32) / 2 = 15 + 6.5 21 luaunit.assertEquals(userAvatar.y, 21.5) -- userMenu.y + (45 - 32) / 2 = 15 + 6.5 = 21.5
luaunit.assertEquals(dropdown.y, 25) -- userMenu.y + (45 - 25) / 2 = 15 + 10 = 25 luaunit.assertEquals(dropdown.y, 25) -- userMenu.y + (45 - 25) / 2 = 15 + 10 = 25
end end
@@ -1510,8 +1510,8 @@ function TestAlignItems:testComplexDashboardWidgetLayout()
luaunit.assertEquals(statCard2.x, 15) -- Same center alignment luaunit.assertEquals(statCard2.x, 15) -- Same center alignment
-- Verify stat card alignments -- Verify stat card alignments
luaunit.assertEquals(stat1Value.x, 60) -- statCard1.x + (220 - 100) / 2 = 15 + 60 = 75 luaunit.assertEquals(stat1Value.x, 75) -- statCard1.x + (220 - 100) / 2 = 15 + 60 = 75
luaunit.assertEquals(stat1Label.x, 35) -- statCard1.x + (220 - 150) / 2 = 15 + 35 = 50 luaunit.assertEquals(stat1Label.x, 50) -- statCard1.x + (220 - 150) / 2 = 15 + 35 = 50
luaunit.assertEquals(stat2Value.x, 115) -- statCard2.x + (220 - 120) = 15 + 100 = 115 luaunit.assertEquals(stat2Value.x, 115) -- statCard2.x + (220 - 120) = 15 + 100 = 115
luaunit.assertEquals(stat2Trend.x, 155) -- statCard2.x + (220 - 80) = 15 + 140 = 155 luaunit.assertEquals(stat2Trend.x, 155) -- statCard2.x + (220 - 80) = 15 + 140 = 155
@@ -1528,8 +1528,8 @@ function TestAlignItems:testComplexDashboardWidgetLayout()
luaunit.assertEquals(task2.x, 670) -- tasksList.x + (130 - 110) = 650 + 20 = 670 luaunit.assertEquals(task2.x, 670) -- tasksList.x + (130 - 110) = 650 + 20 = 670
-- Verify footer center alignment -- Verify footer center alignment
luaunit.assertEquals(status.y, 10) -- (40 - 20) / 2 = 10 luaunit.assertEquals(status.y, 570) -- footer.y (560) + (40 - 20) / 2 = 560 + 10 = 570
luaunit.assertEquals(timestamp.y, 12) -- (40 - 16) / 2 = 12 luaunit.assertEquals(timestamp.y, 572) -- footer.y (560) + (40 - 16) / 2 = 560 + 12 = 572
end end
-- Test 20: Complex Form Layout with Multi-Level Alignments -- Test 20: Complex Form Layout with Multi-Level Alignments
@@ -1859,29 +1859,29 @@ function TestAlignItems:testComplexFormMultiLevelAlignments()
-- Verify personal section flex-start alignment -- Verify personal section flex-start alignment
luaunit.assertEquals(sectionTitle1.x, 50) -- Aligned to start luaunit.assertEquals(sectionTitle1.x, 50) -- Aligned to start
luaunit.assertEquals(nameRow.x, 60) -- Form.x + 10 margin = 50 + 10 = 60 luaunit.assertEquals(nameRow.x, 50) -- Same as personalSection.x (no margin)
-- Verify name field alignments -- Verify name field alignments
luaunit.assertEquals(firstNameLabel.x, 60) -- firstNameField starts at nameRow.x luaunit.assertEquals(firstNameLabel.x, 50) -- firstNameField starts at nameRow.x (50)
luaunit.assertEquals(lastNameLabel.x, 180) -- lastNameField.x + (220 - 120) = 60 + 220 + 100 = 380, but flex-end within field luaunit.assertEquals(lastNameLabel.x, 370) -- lastNameField.x (270) + (220 - 120) = 270 + 100 = 370 (flex-end within field)
luaunit.assertEquals(lastNameInput.x, 100) -- lastNameField.x + (220 - 200) = 280 + 20 = 300 luaunit.assertEquals(lastNameInput.x, 290) -- lastNameField.x (270) + (220 - 200) = 270 + 20 = 290
-- Verify preferences section center alignment -- Verify preferences section center alignment
luaunit.assertEquals(sectionTitle2.x, 175) -- 50 + (500 - 250) / 2 = 50 + 125 = 175 luaunit.assertEquals(sectionTitle2.x, 175) -- 50 + (500 - 250) / 2 = 50 + 125 = 175
luaunit.assertEquals(optionsContainer.x, 100) -- 50 + (500 - 400) / 2 = 50 + 50 = 100 luaunit.assertEquals(optionsContainer.x, 100) -- 50 + (500 - 400) / 2 = 50 + 50 = 100
-- Verify option alignments -- Verify option alignments
luaunit.assertEquals(checkbox1.y, 332) -- option1.y + (25 - 20) / 2, where option1.y ≈ 330 luaunit.assertEquals(checkbox1.y, 362.5) -- option1.y (360) + (25 - 20) / 2 = 360 + 2.5 = 362.5
luaunit.assertEquals(label1.y, 333) -- option1.y + (25 - 18) / 2 333 luaunit.assertEquals(label1.y, 363.5) -- option1.y (360) + (25 - 18) / 2 = 360 + 3.5 = 363.5
-- Verify right options flex-end alignment -- Verify right options flex-end alignment
luaunit.assertEquals(dropdown.x, 310) -- rightOptions.x + (180 - 150) = 280 + 30 = 310 luaunit.assertEquals(dropdown.x, 310) -- rightOptions.x + (180 - 150) = 280 + 30 = 310
luaunit.assertEquals(slider.x, 320) -- rightOptions.x + (180 - 140) = 280 + 40 = 320 luaunit.assertEquals(slider.x, 320) -- rightOptions.x + (180 - 140) = 280 + 40 = 320
-- Verify actions section alignments -- Verify actions section alignments
luaunit.assertEquals(cancelBtn.y, 10) -- (60 - 40) / 2 = 10 luaunit.assertEquals(cancelBtn.y, 520) -- actionsSection.y (510) + (60 - 40) / 2 = 510 + 10 = 520
luaunit.assertEquals(saveBtn.y, 12) -- submitGroup.y + (50 - 40) / 2 = 5 + 5 = 10, but relative positioning luaunit.assertEquals(saveBtn.y, 520) -- submitGroup.y (515) + (50 - 40) / 2 = 515 + 5 = 520
luaunit.assertEquals(submitBtn.y, 10) -- submitGroup.y + (50 - 45) / 2 = 5 + 2.5 ≈ 7 luaunit.assertEquals(submitBtn.y, 517.5) -- submitGroup.y (515) + (50 - 45) / 2 = 515 + 2.5 = 517.5
end end
-- Test 21: Complex Modal Dialog with Nested Alignments -- Test 21: Complex Modal Dialog with Nested Alignments
@@ -2242,20 +2242,20 @@ function TestAlignItems:testComplexModalDialogNestedAlignments()
-- Verify content header flex-end alignment -- Verify content header flex-end alignment
luaunit.assertEquals(contentTitle.y, 209) -- contentHeader.y + (50 - 35) = 194 + 15 = 209 luaunit.assertEquals(contentTitle.y, 209) -- contentHeader.y + (50 - 35) = 194 + 15 = 209
luaunit.assertEquals(editBtn.y, 214) -- contentActions center: contentHeader.y + (50 - 40)/2 + (40-30)/2 luaunit.assertEquals(editBtn.y, 209) -- contentActions center: contentHeader.y + (50 - 40)/2 + (40 - 30)/2 = 194 + 5 + 10 = 209
-- Verify content body center alignment -- Verify content body center alignment
luaunit.assertEquals(contentText.x, 237) -- contentArea.x + (450 - 400) / 2 = 212 + 25 = 237 luaunit.assertEquals(contentText.x, 387) -- contentArea.x (362) + (450 - 400) / 2 = 362 + 25 = 387
luaunit.assertEquals(contentImage.x, 337) -- contentArea.x + (450 - 200) / 2 = 212 + 125 = 337 luaunit.assertEquals(contentImage.x, 487) -- contentArea.x (362) + (450 - 200) / 2 = 362 + 125 = 487
-- Verify content meta flex-end alignment -- Verify content meta flex-end alignment
luaunit.assertEquals(lastModified.x, 542) -- contentArea.x + contentMeta.x + (350 - 120 - 100) = 362 + 130 = 492 luaunit.assertEquals(lastModified.x, 492) -- contentArea.x (362) + (350 - 220) = 362 + 130 = 492
luaunit.assertEquals(author.x, 662) -- After lastModified position luaunit.assertEquals(author.x, 612) -- lastModified.x (492) + lastModified.width (120) = 612
-- Verify footer center alignment -- Verify footer center alignment
luaunit.assertEquals(footerActions.x, 362) -- modal.x + (600 - 300) / 2 = 212 + 150 = 362 luaunit.assertEquals(footerActions.x, 362) -- modal.x + (600 - 300) / 2 = 212 + 150 = 362
luaunit.assertEquals(cancelModalBtn.y, 639) -- modalFooter.y + (60 - 50)/2 + (50-40)/2 = 634 + 5 + 5 = 644 luaunit.assertEquals(cancelModalBtn.y, 584) -- modalFooter.y (574) + (60-50)/2 + (50-40)/2 = 574 + 5 + 5 = 584
luaunit.assertEquals(okBtn.y, 636) -- footerActions center: (50 - 45) / 2 = 2.5, plus modalFooter offset luaunit.assertEquals(okBtn.y, 581.5) -- footerActions.y (579) + (50 - 45) / 2 = 579 + 2.5 = 581.5
end end
luaunit.LuaUnit.run() luaunit.LuaUnit.run()

View File

@@ -188,8 +188,8 @@ function TestLayoutValidation:testNegativeDimensionsAndPositions()
luaunit.assertTrue(success) -- Should not crash luaunit.assertTrue(success) -- Should not crash
luaunit.assertEquals(element.x, -10) -- Negative positions should work luaunit.assertEquals(element.x, -10) -- Negative positions should work
luaunit.assertEquals(element.y, -20) luaunit.assertEquals(element.y, -20)
luaunit.assertEquals(element.width, -50) -- Negative dimensions should work (though unusual) luaunit.assertEquals(element.width, 0) -- Negative dimensions are clamped to 0
luaunit.assertEquals(element.height, -30) luaunit.assertEquals(element.height, 0) -- Negative dimensions are clamped to 0
end end
-- Test 6: Extremely Large Values -- Test 6: Extremely Large Values
@@ -929,6 +929,18 @@ function TestLayoutValidation:testCircularReferenceValidation()
container1:layoutChildren() container1:layoutChildren()
end) end)
-- Clean up the circular reference to restore valid structure
-- Remove container2 from container5's children
if container5.children and #container5.children > 0 then
for i = #container5.children, 1, -1 do
if container5.children[i] == container2 then
table.remove(container5.children, i)
end
end
end
-- Restore container2's original parent
container2.parent = container1
return { return {
container1 = container1, container1 = container1,
container2 = container2, container2 = container2,
@@ -948,7 +960,7 @@ function TestLayoutValidation:testCircularReferenceValidation()
luaunit.assertIsTable(result.container2) luaunit.assertIsTable(result.container2)
luaunit.assertIsTable(result.container3) luaunit.assertIsTable(result.container3)
-- Test that final layout still works -- Test that final layout still works after cleanup
local finalLayoutSuccess = captureError(function() local finalLayoutSuccess = captureError(function()
result.container1:layoutChildren() result.container1:layoutChildren()
end) end)

View File

@@ -111,9 +111,9 @@ function TestPerformance:testBasicLayoutPerformanceBenchmark()
print(string.format(" 100 children: %.6f seconds", time_100)) print(string.format(" 100 children: %.6f seconds", time_100))
-- Assert reasonable performance (should complete within 1 second) -- Assert reasonable performance (should complete within 1 second)
luaunit.assertTrue(time_10 < 1.0, "10 children layout should complete within 1 second") luaunit.assertTrue(time_10 < 0.05, "10 children layout should complete within 0.05 seconds")
luaunit.assertTrue(time_50 < 1.0, "50 children layout should complete within 1 second") luaunit.assertTrue(time_50 < 0.05, "50 children layout should complete within 0.05 seconds")
luaunit.assertTrue(time_100 < 1.0, "100 children layout should complete within 1 second") luaunit.assertTrue(time_100 < 0.05, "100 children layout should complete within 0.05 seconds")
-- Performance should scale reasonably (not exponentially) -- Performance should scale reasonably (not exponentially)
-- Allow some overhead but ensure it's not exponential growth -- Allow some overhead but ensure it's not exponential growth
@@ -140,7 +140,7 @@ function TestPerformance:testScalabilityWithLargeNumbers()
print(string.format("Scalability Test - %d children: %.6f seconds", size, time)) print(string.format("Scalability Test - %d children: %.6f seconds", size, time))
-- Each test should complete within reasonable time -- Each test should complete within reasonable time
luaunit.assertTrue(time < 2.0, string.format("%d children should layout within 2 seconds", size)) luaunit.assertTrue(time < 0.05, string.format("%d children should layout within 0.05 seconds", size))
end end
-- Check that performance scales linearly or sub-linearly -- Check that performance scales linearly or sub-linearly
@@ -201,7 +201,7 @@ function TestPerformance:testComplexNestedLayoutPerformance()
print(string.format("Complex Nested Layout (5x4x3 = 60 total elements): %.6f seconds", time)) print(string.format("Complex Nested Layout (5x4x3 = 60 total elements): %.6f seconds", time))
-- Complex nested layout should complete within reasonable time -- Complex nested layout should complete within reasonable time
luaunit.assertTrue(time < 3.0, "Complex nested layout should complete within 3 seconds") luaunit.assertTrue(time < 0.05, "Complex nested layout should complete within 0.05 seconds")
-- Verify structure was created correctly -- Verify structure was created correctly
luaunit.assertEquals(#root.children, 5) -- 5 sections luaunit.assertEquals(#root.children, 5) -- 5 sections
@@ -233,7 +233,7 @@ function TestPerformance:testFlexWrapPerformanceWithManyElements()
print(string.format("Flex Wrap Performance (50 wrapping elements): %.6f seconds", time)) print(string.format("Flex Wrap Performance (50 wrapping elements): %.6f seconds", time))
-- Flex wrap with many elements should complete within reasonable time -- Flex wrap with many elements should complete within reasonable time
luaunit.assertTrue(time < 2.0, "Flex wrap layout should complete within 2 seconds") luaunit.assertTrue(time < 0.05, "Flex wrap layout should complete within 0.05 seconds")
-- Verify that elements are positioned (wrapped layout worked) -- Verify that elements are positioned (wrapped layout worked)
luaunit.assertTrue(children[1].x >= 0 and children[1].y >= 0, "First child should be positioned") luaunit.assertTrue(children[1].x >= 0 and children[1].y >= 0, "First child should be positioned")
@@ -268,7 +268,7 @@ function TestPerformance:testDynamicLayoutChangePerformance()
print(string.format("Dynamic Layout Changes (30 relayouts): %.6f seconds", time)) print(string.format("Dynamic Layout Changes (30 relayouts): %.6f seconds", time))
-- Dynamic layout changes should complete within reasonable time -- Dynamic layout changes should complete within reasonable time
luaunit.assertTrue(time < 2.0, "Dynamic layout changes should complete within 2 seconds") luaunit.assertTrue(time < 0.05, "Dynamic layout changes should complete within 0.05 seconds")
-- Verify final layout is valid -- Verify final layout is valid
luaunit.assertTrue(children[1].x >= 0 and children[1].y >= 0, "Children should be positioned after changes") luaunit.assertTrue(children[1].x >= 0 and children[1].y >= 0, "Children should be positioned after changes")
@@ -297,7 +297,7 @@ function TestPerformance:testMemoryUsagePattern()
print(string.format("Memory Usage Pattern Test (5 cycles, 100 elements each): %.6f seconds", time)) print(string.format("Memory Usage Pattern Test (5 cycles, 100 elements each): %.6f seconds", time))
-- Memory pattern test should complete within reasonable time -- Memory pattern test should complete within reasonable time
luaunit.assertTrue(time < 3.0, "Memory usage pattern test should complete within 3 seconds") luaunit.assertTrue(time < 0.05, "Memory usage pattern test should complete within 0.05 seconds")
-- Verify container is clean after cycles -- Verify container is clean after cycles
luaunit.assertEquals(#container.children, 0, "Container should be clean after memory test") luaunit.assertEquals(#container.children, 0, "Container should be clean after memory test")
@@ -347,7 +347,7 @@ function TestPerformance:testPerformanceWithDifferentLayoutStrategies()
print(string.format("Layout Strategy '%s': %.6f seconds", strategy.name, time)) print(string.format("Layout Strategy '%s': %.6f seconds", strategy.name, time))
-- Each strategy should complete within reasonable time -- Each strategy should complete within reasonable time
luaunit.assertTrue(time < 1.0, string.format("'%s' layout should complete within 1 second", strategy.name)) luaunit.assertTrue(time < 0.05, string.format("'%s' layout should complete within 0.05 second", strategy.name))
end end
-- All strategies should perform reasonably similarly -- All strategies should perform reasonably similarly
@@ -387,8 +387,8 @@ function TestPerformance:testStressTestWithMaximumElements()
-- Stress test should complete within reasonable time even with many elements -- Stress test should complete within reasonable time even with many elements
luaunit.assertTrue( luaunit.assertTrue(
time < 5.0, time < 0.05,
string.format("Stress test with %d elements should complete within 5 seconds", stress_count) string.format("Stress test with %d elements should complete within 0.05 seconds", stress_count)
) )
-- Verify that all children are positioned -- Verify that all children are positioned
@@ -679,7 +679,7 @@ function TestPerformance:testComplexEnterpriseApplicationPerformance()
print(string.format(" Elements/Second: %.0f", structure_info.total_elements / time)) print(string.format(" Elements/Second: %.0f", structure_info.total_elements / time))
-- Performance assertions for enterprise-grade application -- Performance assertions for enterprise-grade application
luaunit.assertTrue(time < 8.0, "Enterprise dashboard should layout within 8 seconds") luaunit.assertTrue(time < 0.05, "Enterprise dashboard should layout within 0.05 seconds")
luaunit.assertTrue(structure_info.total_elements > 200, "Should have created substantial element count") luaunit.assertTrue(structure_info.total_elements > 200, "Should have created substantial element count")
luaunit.assertTrue(structure_info.max_depth >= 5, "Should have deep nesting structure") luaunit.assertTrue(structure_info.max_depth >= 5, "Should have deep nesting structure")
@@ -994,9 +994,9 @@ function TestPerformance:testComplexAnimationReadyLayoutPerformance()
print(string.format(" 60fps Target: %.6f seconds/frame", target_frame_time)) print(string.format(" 60fps Target: %.6f seconds/frame", target_frame_time))
-- Performance assertions for animation-ready layouts -- Performance assertions for animation-ready layouts
luaunit.assertTrue(time < 12.0, "Animation setup should complete within 12 seconds") luaunit.assertTrue(time < 0.05, "Animation setup should complete within 0.05 seconds")
luaunit.assertTrue(avg_frame_time < target_frame_time * 2, "Average frame time should be reasonable for 30fps+") luaunit.assertTrue(avg_frame_time < target_frame_time * 2, "Average frame time should be reasonable for 30fps+")
luaunit.assertTrue(max_frame_time < 0.1, "No single frame should take more than 100ms") luaunit.assertTrue(max_frame_time < 0.05, "No single frame should take more than 50ms")
luaunit.assertTrue(metrics.total_elements > 100, "Should have substantial number of animated elements") luaunit.assertTrue(metrics.total_elements > 100, "Should have substantial number of animated elements")
-- Verify structure integrity after animations -- Verify structure integrity after animations
@@ -1387,9 +1387,9 @@ function TestPerformance:testExtremeScalePerformanceBenchmark()
) )
-- Individual test assertions -- Individual test assertions
luaunit.assertTrue(test_time < 20.0, string.format("%s should complete within 20 seconds", test_config.name)) luaunit.assertTrue(test_time < 1.0, string.format("%s should complete within 1 seconds", test_config.name))
luaunit.assertTrue( luaunit.assertTrue(
test_metrics.created_elements > 100, test_metrics.created_elements > 50,
string.format("%s should create substantial elements", test_config.name) string.format("%s should create substantial elements", test_config.name)
) )
end end