Removed all goto statements
Fixed the parameter order in observe Used the correct internal property approach Did not use the built-in visibleChildren, as we need to handle elements that are obscured or completely removed
This commit is contained in:
@@ -68,17 +68,15 @@ local lineBreakElement = {
|
|||||||
getVisible = function(self) return true end,
|
getVisible = function(self) return true end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
local function sortElements(self, direction, spacing, wrap)
|
local function sortElements(self, direction, spacing, wrap)
|
||||||
-- Pre-allocate tables to reduce dynamic expansion
|
|
||||||
local elements = self.get("children")
|
|
||||||
local sortedElements = {}
|
local sortedElements = {}
|
||||||
local visibleElements = {}
|
local visibleElements = {}
|
||||||
local childCount = 0
|
local childCount = 0
|
||||||
|
|
||||||
-- First gather all visible elements
|
-- We can't use self.get("visibleChildren") here
|
||||||
for _, elem in pairs(elements) do
|
--because it would exclude elements that are obscured
|
||||||
if elem:getVisible() then
|
for _, elem in pairs(self.get("children")) do
|
||||||
|
if elem.get("visible") then
|
||||||
table.insert(visibleElements, elem)
|
table.insert(visibleElements, elem)
|
||||||
if elem ~= lineBreakElement then
|
if elem ~= lineBreakElement then
|
||||||
childCount = childCount + 1
|
childCount = childCount + 1
|
||||||
@@ -86,19 +84,16 @@ local function sortElements(self, direction, spacing, wrap)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- No visible elements, nothing to layout
|
|
||||||
if childCount == 0 then
|
if childCount == 0 then
|
||||||
return sortedElements
|
return sortedElements
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Use known size to pre-allocate array
|
|
||||||
if not wrap then
|
if not wrap then
|
||||||
-- No-wrap mode, all elements in one row/column
|
|
||||||
sortedElements[1] = {offset=1}
|
sortedElements[1] = {offset=1}
|
||||||
|
|
||||||
for _, elem in ipairs(visibleElements) do
|
for _, elem in ipairs(visibleElements) do
|
||||||
if elem == lineBreakElement then
|
if elem == lineBreakElement then
|
||||||
-- Create new line
|
|
||||||
local nextIndex = #sortedElements + 1
|
local nextIndex = #sortedElements + 1
|
||||||
if sortedElements[nextIndex] == nil then
|
if sortedElements[nextIndex] == nil then
|
||||||
sortedElements[nextIndex] = {offset=1}
|
sortedElements[nextIndex] = {offset=1}
|
||||||
@@ -108,41 +103,32 @@ local function sortElements(self, direction, spacing, wrap)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Wrap mode, need to calculate rows/columns more optimally
|
|
||||||
local containerSize = direction == "row" and self.get("width") or self.get("height")
|
local containerSize = direction == "row" and self.get("width") or self.get("height")
|
||||||
|
|
||||||
-- First split elements by line breaks
|
|
||||||
local segments = {{}}
|
local segments = {{}}
|
||||||
local currentSegment = 1
|
local currentSegment = 1
|
||||||
|
|
||||||
for _, elem in ipairs(visibleElements) do
|
for _, elem in ipairs(visibleElements) do
|
||||||
if elem == lineBreakElement then
|
if elem == lineBreakElement then
|
||||||
-- Start a new segment
|
|
||||||
currentSegment = currentSegment + 1
|
currentSegment = currentSegment + 1
|
||||||
segments[currentSegment] = {}
|
segments[currentSegment] = {}
|
||||||
else
|
else
|
||||||
-- Add to current segment
|
|
||||||
table.insert(segments[currentSegment], elem)
|
table.insert(segments[currentSegment], elem)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Now process each segment optimally
|
|
||||||
for segmentIndex, segment in ipairs(segments) do
|
for segmentIndex, segment in ipairs(segments) do
|
||||||
if #segment == 0 then
|
if #segment == 0 then
|
||||||
-- Empty segment (consecutive line breaks)
|
|
||||||
sortedElements[#sortedElements + 1] = {offset=1}
|
sortedElements[#sortedElements + 1] = {offset=1}
|
||||||
else
|
else
|
||||||
-- Try to pack elements optimally within this segment
|
|
||||||
local rows = {}
|
local rows = {}
|
||||||
local currentRow = {}
|
local currentRow = {}
|
||||||
local currentWidth = 0
|
local currentWidth = 0
|
||||||
|
|
||||||
for _, elem in ipairs(segment) do
|
for _, elem in ipairs(segment) do
|
||||||
-- Get intrinsic size if available, otherwise use current size
|
|
||||||
local intrinsicSize = 0
|
local intrinsicSize = 0
|
||||||
local currentSize = direction == "row" and elem.get("width") or elem.get("height")
|
local currentSize = direction == "row" and elem.get("width") or elem.get("height")
|
||||||
|
|
||||||
-- Try to get intrinsic size, safely
|
|
||||||
local hasIntrinsic = false
|
local hasIntrinsic = false
|
||||||
if direction == "row" then
|
if direction == "row" then
|
||||||
local ok, intrinsicWidth = pcall(function() return elem.get("intrinsicWidth") end)
|
local ok, intrinsicWidth = pcall(function() return elem.get("intrinsicWidth") end)
|
||||||
@@ -158,35 +144,28 @@ local function sortElements(self, direction, spacing, wrap)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Fall back to current size if no intrinsic size
|
|
||||||
local elemSize = hasIntrinsic and intrinsicSize or currentSize
|
local elemSize = hasIntrinsic and intrinsicSize or currentSize
|
||||||
|
|
||||||
local spaceNeeded = elemSize
|
local spaceNeeded = elemSize
|
||||||
|
|
||||||
-- Add spacing if not first element in row
|
|
||||||
if #currentRow > 0 then
|
if #currentRow > 0 then
|
||||||
spaceNeeded = spaceNeeded + spacing
|
spaceNeeded = spaceNeeded + spacing
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if element fits in current row
|
|
||||||
if currentWidth + spaceNeeded <= containerSize or #currentRow == 0 then
|
if currentWidth + spaceNeeded <= containerSize or #currentRow == 0 then
|
||||||
-- Element fits or it's first element (must place even if too large)
|
|
||||||
table.insert(currentRow, elem)
|
table.insert(currentRow, elem)
|
||||||
currentWidth = currentWidth + spaceNeeded
|
currentWidth = currentWidth + spaceNeeded
|
||||||
else
|
else
|
||||||
-- Element doesn't fit, start new row
|
|
||||||
table.insert(rows, currentRow)
|
table.insert(rows, currentRow)
|
||||||
currentRow = {elem}
|
currentRow = {elem}
|
||||||
currentWidth = elemSize
|
currentWidth = elemSize
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Don't forget the last row
|
|
||||||
if #currentRow > 0 then
|
if #currentRow > 0 then
|
||||||
table.insert(rows, currentRow)
|
table.insert(rows, currentRow)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add rows to sorted elements
|
|
||||||
for _, row in ipairs(rows) do
|
for _, row in ipairs(rows) do
|
||||||
sortedElements[#sortedElements + 1] = {offset=1}
|
sortedElements[#sortedElements + 1] = {offset=1}
|
||||||
for _, elem in ipairs(row) do
|
for _, elem in ipairs(row) do
|
||||||
@@ -197,7 +176,6 @@ local function sortElements(self, direction, spacing, wrap)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Filter out empty rows/columns
|
|
||||||
local filteredElements = {}
|
local filteredElements = {}
|
||||||
for i, rowOrColumn in ipairs(sortedElements) do
|
for i, rowOrColumn in ipairs(sortedElements) do
|
||||||
if #rowOrColumn > 0 then
|
if #rowOrColumn > 0 then
|
||||||
@@ -640,44 +618,27 @@ end
|
|||||||
|
|
||||||
-- Optimize updateLayout function
|
-- Optimize updateLayout function
|
||||||
local function updateLayout(self, direction, spacing, justifyContent, wrap)
|
local function updateLayout(self, direction, spacing, justifyContent, wrap)
|
||||||
-- Check essential properties for layout
|
|
||||||
if self.get("width") <= 0 or self.get("height") <= 0 then
|
if self.get("width") <= 0 or self.get("height") <= 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Force direction to be valid
|
|
||||||
direction = (direction == "row" or direction == "column") and direction or "row"
|
direction = (direction == "row" or direction == "column") and direction or "row"
|
||||||
|
|
||||||
-- Check if container size has changed since last layout
|
|
||||||
local currentWidth, currentHeight = self.get("width"), self.get("height")
|
local currentWidth, currentHeight = self.get("width"), self.get("height")
|
||||||
local lastWidth = self.get("_lastLayoutWidth") or 0
|
local sizeChanged = currentWidth ~= self._lastLayoutWidth or currentHeight ~= self._lastLayoutHeight
|
||||||
local lastHeight = self.get("_lastLayoutHeight") or 0
|
|
||||||
local sizeChanged = currentWidth ~= lastWidth or currentHeight ~= lastHeight
|
|
||||||
|
|
||||||
-- Store current size for next comparison
|
self._lastLayoutWidth = currentWidth
|
||||||
self.set("_lastLayoutWidth", currentWidth)
|
self._lastLayoutHeight = currentHeight
|
||||||
self.set("_lastLayoutHeight", currentHeight)
|
|
||||||
|
|
||||||
-- If container size increased, we might need to reset flexGrow items to recalculate
|
if wrap and sizeChanged and (currentWidth > self._lastLayoutWidth or currentHeight > self._lastLayoutHeight) then
|
||||||
if wrap and sizeChanged and (currentWidth > lastWidth or currentHeight > lastHeight) then
|
for _, child in pairs(self.get("children")) do
|
||||||
-- Get reference to all children
|
|
||||||
local allChildren = self.get("children")
|
|
||||||
|
|
||||||
-- Reset flex items to intrinsic size temporarily to allow reflow
|
|
||||||
for _, child in pairs(allChildren) do
|
|
||||||
if child ~= lineBreakElement and child:getVisible() and child.get("flexGrow") and child.get("flexGrow") > 0 then
|
if child ~= lineBreakElement and child:getVisible() and child.get("flexGrow") and child.get("flexGrow") > 0 then
|
||||||
if direction == "row" then
|
if direction == "row" then
|
||||||
-- Store the actual width temporarily
|
|
||||||
local actualWidth = child.get("width")
|
|
||||||
-- Reset to intrinsic width for layout calculation
|
|
||||||
local ok, value = pcall(function() return child.get("intrinsicWidth") end)
|
local ok, value = pcall(function() return child.get("intrinsicWidth") end)
|
||||||
if ok and value then
|
if ok and value then
|
||||||
child.set("width", value)
|
child.set("width", value)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Store the actual height temporarily
|
|
||||||
local actualHeight = child.get("height")
|
|
||||||
-- Reset to intrinsic height for layout calculation
|
|
||||||
local ok, value = pcall(function() return child.get("intrinsicHeight") end)
|
local ok, value = pcall(function() return child.get("intrinsicHeight") end)
|
||||||
if ok and value then
|
if ok and value then
|
||||||
child.set("height", value)
|
child.set("height", value)
|
||||||
@@ -687,94 +648,70 @@ local function updateLayout(self, direction, spacing, justifyContent, wrap)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get all elements that need layout
|
|
||||||
local elements = sortElements(self, direction, spacing, wrap)
|
local elements = sortElements(self, direction, spacing, wrap)
|
||||||
|
if #elements == 0 then return end
|
||||||
|
|
||||||
-- Debug: Check what elements were found
|
|
||||||
if #elements == 0 then
|
|
||||||
return -- No elements to layout
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Based on direction, select layout function, avoid checking every iteration
|
|
||||||
local layoutFunction = direction == "row" and calculateRow or calculateColumn
|
local layoutFunction = direction == "row" and calculateRow or calculateColumn
|
||||||
|
|
||||||
-- Apply layout calculation with vertical offset
|
|
||||||
if direction == "row" and wrap then
|
if direction == "row" and wrap then
|
||||||
-- In row direction with wrap, we need to offset each row vertically
|
|
||||||
local currentY = 1
|
local currentY = 1
|
||||||
for i, rowOrColumn in ipairs(elements) do
|
for i, rowOrColumn in ipairs(elements) do
|
||||||
-- Skip empty rows
|
if #rowOrColumn > 0 then
|
||||||
if #rowOrColumn == 0 then goto continue end
|
for _, element in ipairs(rowOrColumn) do
|
||||||
|
if element ~= lineBreakElement then
|
||||||
-- First, set the vertical offset for this row
|
element.set("y", currentY)
|
||||||
for _, element in ipairs(rowOrColumn) do
|
end
|
||||||
if element ~= lineBreakElement then
|
end
|
||||||
element.set("y", currentY)
|
|
||||||
|
layoutFunction(self, rowOrColumn, spacing, justifyContent)
|
||||||
|
|
||||||
|
local rowHeight = 0
|
||||||
|
for _, element in ipairs(rowOrColumn) do
|
||||||
|
if element ~= lineBreakElement then
|
||||||
|
rowHeight = math.max(rowHeight, element.get("height"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if i < #elements then
|
||||||
|
currentY = currentY + rowHeight + spacing
|
||||||
|
else
|
||||||
|
currentY = currentY + rowHeight
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Apply the row layout
|
|
||||||
layoutFunction(self, rowOrColumn, spacing, justifyContent)
|
|
||||||
|
|
||||||
-- Calculate height for this row (maximum element height)
|
|
||||||
local rowHeight = 0
|
|
||||||
for _, element in ipairs(rowOrColumn) do
|
|
||||||
if element ~= lineBreakElement then
|
|
||||||
rowHeight = math.max(rowHeight, element.get("height"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Move to next row (add spacing only if not the last row)
|
|
||||||
if i < #elements then
|
|
||||||
currentY = currentY + rowHeight + spacing
|
|
||||||
else
|
|
||||||
currentY = currentY + rowHeight
|
|
||||||
end
|
|
||||||
|
|
||||||
::continue::
|
|
||||||
end
|
end
|
||||||
elseif direction == "column" and wrap then
|
elseif direction == "column" and wrap then
|
||||||
-- In column direction with wrap, we need to offset each column horizontally
|
|
||||||
local currentX = 1
|
local currentX = 1
|
||||||
for i, rowOrColumn in ipairs(elements) do
|
for i, rowOrColumn in ipairs(elements) do
|
||||||
-- Skip empty columns
|
if #rowOrColumn > 0 then
|
||||||
if #rowOrColumn == 0 then goto continue end
|
for _, element in ipairs(rowOrColumn) do
|
||||||
|
if element ~= lineBreakElement then
|
||||||
-- First, set the horizontal offset for this column
|
element.set("x", currentX)
|
||||||
for _, element in ipairs(rowOrColumn) do
|
end
|
||||||
if element ~= lineBreakElement then
|
end
|
||||||
element.set("x", currentX)
|
|
||||||
|
layoutFunction(self, rowOrColumn, spacing, justifyContent)
|
||||||
|
|
||||||
|
local columnWidth = 0
|
||||||
|
for _, element in ipairs(rowOrColumn) do
|
||||||
|
if element ~= lineBreakElement then
|
||||||
|
columnWidth = math.max(columnWidth, element.get("width"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if i < #elements then
|
||||||
|
currentX = currentX + columnWidth + spacing
|
||||||
|
else
|
||||||
|
currentX = currentX + columnWidth
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Apply the column layout
|
|
||||||
layoutFunction(self, rowOrColumn, spacing, justifyContent)
|
|
||||||
|
|
||||||
-- Calculate width for this column (maximum element width)
|
|
||||||
local columnWidth = 0
|
|
||||||
for _, element in ipairs(rowOrColumn) do
|
|
||||||
if element ~= lineBreakElement then
|
|
||||||
columnWidth = math.max(columnWidth, element.get("width"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Move to next column (add spacing only if not the last column)
|
|
||||||
if i < #elements then
|
|
||||||
currentX = currentX + columnWidth + spacing
|
|
||||||
else
|
|
||||||
currentX = currentX + columnWidth
|
|
||||||
end
|
|
||||||
|
|
||||||
::continue::
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Simple case: no wrapping
|
for _, rowOrColumn in ipairs(elements) do
|
||||||
for i, rowOrColumn in ipairs(elements) do
|
|
||||||
layoutFunction(self, rowOrColumn, spacing, justifyContent)
|
layoutFunction(self, rowOrColumn, spacing, justifyContent)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
self:sortChildren()
|
||||||
-- Reset layout update flag
|
self.set("childrenEventsSorted", false)
|
||||||
self.set("flexUpdateLayout", false)
|
self.set("flexUpdateLayout", false)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -789,11 +726,9 @@ function Flexbox.new()
|
|||||||
self.set("background", colors.blue)
|
self.set("background", colors.blue)
|
||||||
self.set("z", 10)
|
self.set("z", 10)
|
||||||
|
|
||||||
-- Add instance properties for layout tracking
|
self._lastLayoutWidth = 0
|
||||||
self:instanceProperty("_lastLayoutWidth", {default = 0, type = "number"})
|
self._lastLayoutHeight = 0
|
||||||
self:instanceProperty("_lastLayoutHeight", {default = 0, type = "number"})
|
|
||||||
|
|
||||||
-- Add observers for properties that affect layout
|
|
||||||
self:observe("width", function() self.set("flexUpdateLayout", true) end)
|
self:observe("width", function() self.set("flexUpdateLayout", true) end)
|
||||||
self:observe("height", function() self.set("flexUpdateLayout", true) end)
|
self:observe("height", function() self.set("flexUpdateLayout", true) end)
|
||||||
self:observe("flexDirection", function() self.set("flexUpdateLayout", true) end)
|
self:observe("flexDirection", function() self.set("flexUpdateLayout", true) end)
|
||||||
@@ -831,20 +766,18 @@ function Flexbox:addChild(element)
|
|||||||
element:instanceProperty("intrinsicWidth", {default = element.get("width"), type = "number"})
|
element:instanceProperty("intrinsicWidth", {default = element.get("width"), type = "number"})
|
||||||
element:instanceProperty("intrinsicHeight", {default = element.get("height"), type = "number"})
|
element:instanceProperty("intrinsicHeight", {default = element.get("height"), type = "number"})
|
||||||
|
|
||||||
-- Add observer to child element's flexGrow and flexShrink properties
|
|
||||||
element:observe("flexGrow", function() self.set("flexUpdateLayout", true) end)
|
element:observe("flexGrow", function() self.set("flexUpdateLayout", true) end)
|
||||||
element:observe("flexShrink", function() self.set("flexUpdateLayout", true) end)
|
element:observe("flexShrink", function() self.set("flexUpdateLayout", true) end)
|
||||||
|
|
||||||
-- Add observer for size changes to track intrinsic size
|
element:observe("width", function(_, newValue, oldValue)
|
||||||
element:observe("width", function(_, oldW, newW)
|
|
||||||
if element.get("flexGrow") == 0 then
|
if element.get("flexGrow") == 0 then
|
||||||
element.set("intrinsicWidth", newW)
|
element.set("intrinsicWidth", newValue)
|
||||||
end
|
end
|
||||||
self.set("flexUpdateLayout", true)
|
self.set("flexUpdateLayout", true)
|
||||||
end)
|
end)
|
||||||
element:observe("height", function(_, oldH, newH)
|
element:observe("height", function(_, newValue, oldValue)
|
||||||
if element.get("flexGrow") == 0 then
|
if element.get("flexGrow") == 0 then
|
||||||
element.set("intrinsicHeight", newH)
|
element.set("intrinsicHeight", newValue)
|
||||||
end
|
end
|
||||||
self.set("flexUpdateLayout", true)
|
self.set("flexUpdateLayout", true)
|
||||||
end)
|
end)
|
||||||
|
|||||||
Reference in New Issue
Block a user