cleaned up rendering mode swapping

This commit is contained in:
Michael Freno
2025-12-12 00:08:25 -05:00
parent 9d8f6aa60d
commit ec73d8c7c8
11 changed files with 1270 additions and 46 deletions

View File

@@ -188,6 +188,24 @@ end
---@param props ElementProps
---@return Element
function Element.new(props)
-- Early check: If this is a retained-mode element in an immediate-mode context,
-- check if it already exists (was restored to parent) to prevent duplicates
local elementMode = props.mode
if elementMode == nil then
elementMode = Element._Context._immediateMode and "immediate" or "retained"
end
-- If retained mode and has an ID, check if element already exists in parent's children
if elementMode == "retained" and props.id and props.id ~= "" and props.parent then
-- Check if this element already exists in parent's restored children
for _, child in ipairs(props.parent.children) do
if child.id == props.id and child._elementMode == "retained" then
-- Element already exists (was restored), return existing instance
return child
end
end
end
local self = setmetatable({}, Element)
-- Create dependency subsets for sub-modules (defined once, used throughout)
@@ -272,11 +290,56 @@ function Element.new(props)
self.children = {}
self.onEvent = props.onEvent
-- Auto-generate ID in immediate mode if not provided
if self._elementMode == "immediate" and (not props.id or props.id == "") then
-- Track whether ID was auto-generated (before ID assignment)
local idWasAutoGenerated = not props.id or props.id == ""
-- Auto-generate ID if not provided (for all elements)
if idWasAutoGenerated then
self.id = Element._StateManager.generateID(props, props.parent)
else
self.id = props.id or ""
self.id = props.id
end
-- AFTER ID is determined, check for duplicate top-level OR child retained elements
-- ONLY for auto-generated IDs (same call site recreating same element)
-- If user provides explicit ID, they control uniqueness
if self._elementMode == "retained" and idWasAutoGenerated and self.id and self.id ~= "" then
if not props.parent then
-- Top-level element: check in topElements
for _, existingElement in ipairs(Element._Context.topElements) do
if existingElement.id == self.id and existingElement._elementMode == "retained" then
-- Element already exists (was preserved from previous frame), return existing instance
-- CRITICAL: Clear children array to prevent accumulation
-- Children will be re-declared this frame (retained children will be found via duplicate check)
existingElement.children = {}
return existingElement
end
end
else
-- Child element: check in parent's children
for _, existingChild in ipairs(props.parent.children) do
if existingChild.id == self.id and existingChild._elementMode == "retained" then
-- Element already exists (was restored to parent), return existing instance
-- CRITICAL: Clear children array to prevent accumulation
-- Children will be re-declared this frame (retained children will be found via duplicate check)
existingChild.children = {}
return existingChild
end
end
end
end
-- In immediate mode, restore retained children from StateManager
-- This allows retained-mode children to persist when immediate-mode parents recreate
if self._elementMode == "immediate" and self.id and self.id ~= "" then
local retainedChildren = Element._StateManager.getRetainedChildren(self.id)
if retainedChildren and #retainedChildren > 0 then
-- Restore retained children and update their parent references
for _, child in ipairs(retainedChildren) do
child.parent = self
table.insert(self.children, child)
end
end
end
self.userdata = props.userdata
@@ -2237,6 +2300,11 @@ function Element:destroy()
-- Clear children table
self.children = {}
-- Clear retained children from StateManager (if this is an immediate-mode element)
if self._elementMode == "immediate" and self.id and self.id ~= "" then
Element._StateManager.clearRetainedChildren(self.id)
end
-- Clear parent reference
if self.parent then
self.parent = nil
@@ -3503,6 +3571,12 @@ function Element:saveState()
state._textDragOccurred = self._textDragOccurred
end
-- Save retained children references (for mixed-mode trees)
-- Only save if this is an immediate-mode element with retained children
if self._elementMode == "immediate" and #self.children > 0 then
Element._StateManager.saveRetainedChildren(self.id, self.children)
end
return state
end