want things simpler

This commit is contained in:
Michael Freno
2025-12-12 19:15:27 -05:00
parent b714b6204c
commit 1d6ad6018f
10 changed files with 139 additions and 824 deletions

View File

@@ -1,186 +0,0 @@
-- Example demonstrating hover and unhover events in FlexLöve
-- This shows how to use the new hover/unhover events for interactive UI elements
local FlexLove = require("FlexLove")
function love.load()
FlexLove.init({
baseScale = { width = 1920, height = 1080 },
immediateMode = true,
autoFrameManagement = false,
})
end
-- State to track hover status
local hoverStatus = "Not hovering"
local hoverCount = 0
local unhoverCount = 0
local lastEventTime = 0
function love.update(dt)
FlexLove.beginFrame()
-- Create a container
FlexLove.new({
width = "100vw",
height = "100vh",
backgroundColor = FlexLove.Color.fromHex("#1a1a2e"),
positioning = "flex",
flexDirection = "vertical",
justifyContent = "center",
alignItems = "center",
gap = 30,
})
-- Title
FlexLove.new({
text = "Hover Event Demo",
textSize = "4xl",
textColor = FlexLove.Color.fromHex("#ffffff"),
})
-- Instructions
FlexLove.new({
text = "Move your mouse over the boxes below to see hover events",
textSize = "lg",
textColor = FlexLove.Color.fromHex("#a0a0a0"),
})
-- Status display
FlexLove.new({
text = hoverStatus,
textSize = "xl",
textColor = FlexLove.Color.fromHex("#4ecca3"),
padding = 20,
})
-- Event counters
FlexLove.new({
text = string.format("Hover events: %d | Unhover events: %d", hoverCount, unhoverCount),
textSize = "md",
textColor = FlexLove.Color.fromHex("#ffffff"),
})
-- Container for hover boxes
local boxContainer = FlexLove.new({
positioning = "flex",
flexDirection = "horizontal",
gap = 30,
})
-- Hover Box 1
FlexLove.new({
parent = boxContainer,
width = 200,
height = 200,
backgroundColor = FlexLove.Color.fromHex("#e94560"),
cornerRadius = 15,
positioning = "flex",
justifyContent = "center",
alignItems = "center",
text = "Hover Me!",
textSize = "xl",
textColor = FlexLove.Color.fromHex("#ffffff"),
onEvent = function(element, event)
if event.type == "hover" then
hoverStatus = "Hovering over RED box!"
hoverCount = hoverCount + 1
lastEventTime = love.timer.getTime()
elseif event.type == "unhover" then
hoverStatus = "Left RED box"
unhoverCount = unhoverCount + 1
lastEventTime = love.timer.getTime()
elseif event.type == "click" then
print("Clicked RED box!")
end
end,
})
-- Hover Box 2
FlexLove.new({
parent = boxContainer,
width = 200,
height = 200,
backgroundColor = FlexLove.Color.fromHex("#4ecca3"),
cornerRadius = 15,
positioning = "flex",
justifyContent = "center",
alignItems = "center",
text = "Hover Me!",
textSize = "xl",
textColor = FlexLove.Color.fromHex("#1a1a2e"),
onEvent = function(element, event)
if event.type == "hover" then
hoverStatus = "Hovering over GREEN box!"
hoverCount = hoverCount + 1
lastEventTime = love.timer.getTime()
elseif event.type == "unhover" then
hoverStatus = "Left GREEN box"
unhoverCount = unhoverCount + 1
lastEventTime = love.timer.getTime()
elseif event.type == "click" then
print("Clicked GREEN box!")
end
end,
})
-- Hover Box 3
FlexLove.new({
parent = boxContainer,
width = 200,
height = 200,
backgroundColor = FlexLove.Color.fromHex("#0f3460"),
cornerRadius = 15,
positioning = "flex",
justifyContent = "center",
alignItems = "center",
text = "Hover Me!",
textSize = "xl",
textColor = FlexLove.Color.fromHex("#ffffff"),
onEvent = function(element, event)
if event.type == "hover" then
hoverStatus = "Hovering over BLUE box!"
hoverCount = hoverCount + 1
lastEventTime = love.timer.getTime()
elseif event.type == "unhover" then
hoverStatus = "Left BLUE box"
unhoverCount = unhoverCount + 1
lastEventTime = love.timer.getTime()
elseif event.type == "click" then
print("Clicked BLUE box!")
end
end,
})
-- Reset button
FlexLove.new({
width = 200,
height = 50,
backgroundColor = FlexLove.Color.fromHex("#e94560"),
cornerRadius = 25,
positioning = "flex",
justifyContent = "center",
alignItems = "center",
text = "Reset Counters",
textSize = "md",
textColor = FlexLove.Color.fromHex("#ffffff"),
margin = { top = 30 },
onEvent = function(element, event)
if event.type == "click" then
hoverCount = 0
unhoverCount = 0
hoverStatus = "Counters reset!"
end
end,
})
FlexLove.endFrame()
end
function love.draw()
FlexLove.draw()
end
function love.resize(w, h)
FlexLove.resize(w, h)
end

View File

@@ -1,219 +0,0 @@
-- Demo: Retained children with immediate parents
-- Shows how retained-mode children persist when immediate-mode parents recreate each frame
local FlexLove = require("FlexLove")
-- Track frame count for demo
local frameCount = 0
-- Retained button state (will persist across frames)
local buttonClicks = 0
local topLevelButtonClicks = 0
function love.load()
love.window.setTitle("Mixed-Mode Demo: Retained Children + Immediate Parents")
love.window.setMode(800, 600)
FlexLove.init({
immediateMode = true,
performanceMonitoring = true,
})
end
function love.update(dt)
FlexLove.update(dt)
end
function love.draw()
FlexLove.beginFrame()
-- Frame counter (immediate mode - recreates each frame)
local header = FlexLove.new({
width = 800,
height = 60,
backgroundColor = { 0.1, 0.1, 0.15, 1 },
padding = 20,
flexDirection = "horizontal",
justifyContent = "space-between",
alignItems = "center",
})
FlexLove.new({
parent = header,
text = "Frame: " .. frameCount,
textColor = { 1, 1, 1, 1 },
textSize = 20,
})
FlexLove.new({
parent = header,
text = "Mixed-Mode Element Tree Demo",
textColor = { 0.8, 0.9, 1, 1 },
textSize = 24,
})
-- Main content area (immediate parent)
local container = FlexLove.new({
id = "main_container",
y = 60,
width = 800,
height = 540,
padding = 30,
gap = 20,
backgroundColor = { 0.05, 0.05, 0.08, 1 },
flexDirection = "vertical",
})
-- Section 1: Retained button in immediate parent
FlexLove.new({
parent = container,
text = "1. Retained Button (persists across frames)",
textColor = { 0.9, 0.9, 0.9, 1 },
textSize = 18,
})
local retainedButton = FlexLove.new({
id = "retained_button",
mode = "retained", -- This button will persist!
parent = container,
width = 300,
height = 50,
backgroundColor = { 0.2, 0.6, 0.9, 1 },
cornerRadius = 8,
text = "Clicks: " .. buttonClicks,
textColor = { 1, 1, 1, 1 },
textSize = 16,
textAlign = "center",
onEvent = function(element, event)
if event.type == "click" then
buttonClicks = buttonClicks + 1
-- Update button text
element.text = "Clicks: " .. buttonClicks
end
end,
})
FlexLove.new({
parent = container,
text = "Note: Button state persists even though parent recreates every frame",
textColor = { 0.6, 0.6, 0.6, 1 },
textSize = 12,
})
-- Section 2: Top-level retained element
FlexLove.new({
parent = container,
text = "2. Top-Level Retained Element (also persists)",
textColor = { 0.9, 0.9, 0.9, 1 },
textSize = 18,
margin = { top = 20 },
})
FlexLove.new({
parent = container,
text = "Look at the bottom-left corner for a persistent panel",
textColor = { 0.6, 0.6, 0.6, 1 },
textSize = 12,
})
-- Section 3: Comparison with immediate button
FlexLove.new({
parent = container,
text = "3. Immediate Button (recreates every frame)",
textColor = { 0.9, 0.9, 0.9, 1 },
textSize = 18,
margin = { top = 20 },
})
FlexLove.new({
parent = container,
width = 300,
height = 50,
backgroundColor = { 0.9, 0.3, 0.3, 1 },
cornerRadius = 8,
text = "Can't track clicks (recreated)",
textColor = { 1, 1, 1, 1 },
textSize = 16,
textAlign = "center",
onEvent = function(element, event)
if event.type == "click" then
print("Immediate button clicked (but counter can't persist)")
end
end,
})
FlexLove.new({
parent = container,
text = "Note: This button is recreated every frame, so it can't maintain state",
textColor = { 0.6, 0.6, 0.6, 1 },
textSize = 12,
})
-- Top-level retained element (persists in immediate mode!)
local topLevelPanel = FlexLove.new({
id = "top_level_panel",
mode = "retained",
x = 10,
y = 500,
width = 250,
height = 90,
backgroundColor = { 0.15, 0.5, 0.3, 1 },
cornerRadius = 10,
padding = 15,
gap = 10,
flexDirection = "vertical",
})
FlexLove.new({
id = "panel_title",
mode = "retained",
parent = topLevelPanel,
text = "Persistent Panel",
textColor = { 1, 1, 1, 1 },
textSize = 16,
})
FlexLove.new({
id = "panel_button",
mode = "retained",
parent = topLevelPanel,
width = 220,
height = 35,
backgroundColor = { 1, 1, 1, 0.9 },
cornerRadius = 5,
text = "Panel Clicks: " .. topLevelButtonClicks,
textColor = { 0.15, 0.5, 0.3, 1 },
textSize = 14,
textAlign = "center",
onEvent = function(element, event)
if event.type == "click" then
topLevelButtonClicks = topLevelButtonClicks + 1
element.text = "Panel Clicks: " .. topLevelButtonClicks
end
end,
})
FlexLove.endFrame()
-- Increment frame counter AFTER drawing
frameCount = frameCount + 1
-- Draw all UI elements
FlexLove.draw()
end
function love.mousepressed(x, y, button)
FlexLove.mousepressed(x, y, button)
end
function love.mousereleased(x, y, button)
FlexLove.mousereleased(x, y, button)
end
function love.mousemoved(x, y, dx, dy)
FlexLove.mousemoved(x, y, dx, dy)
end
function love.wheelmoved(x, y)
FlexLove.wheelmoved(x, y)
end

View File

@@ -1,275 +0,0 @@
-- Mode Override Demo
-- Demonstrates per-element mode override in FlexLöve
-- Shows how to mix immediate and retained mode elements in the same application
package.path = package.path .. ";../?.lua;../modules/?.lua"
local FlexLove = require("FlexLove")
local Color = require("modules.Color")
-- Global state
local frameCount = 0
local clickCount = 0
local retainedPanelCreated = false
local retainedPanel = nil
function love.load()
-- Initialize FlexLove in immediate mode globally
FlexLove.init({
immediateMode = true,
theme = "space",
})
love.window.setTitle("Mode Override Demo - FlexLöve")
love.window.setMode(1200, 800)
end
function love.update(dt)
FlexLove.update(dt)
frameCount = frameCount + 1
end
function love.draw()
love.graphics.clear(0.1, 0.1, 0.15, 1)
FlexLove.beginFrame()
-- Title - Immediate mode (default, recreated every frame)
FlexLove.new({
text = "Mode Override Demo",
textSize = 32,
textColor = Color.new(1, 1, 1, 1),
width = "100vw",
height = 60,
textAlign = "center",
backgroundColor = Color.new(0.2, 0.2, 0.3, 1),
padding = { top = 15, bottom = 15 },
})
-- Container for demo panels
local container = FlexLove.new({
positioning = "flex",
flexDirection = "horizontal",
justifyContent = "center",
alignItems = "flex-start",
gap = 20,
width = "100vw",
height = "calc(100vh - 60px)",
padding = { top = 20, left = 20, right = 20, bottom = 20 },
})
-- LEFT PANEL: Immediate Mode (Dynamic, recreated every frame)
local leftPanel = FlexLove.new({
mode = "immediate", -- Explicit immediate mode (would be default anyway)
parent = container,
width = "45vw",
height = "calc(100vh - 100px)",
backgroundColor = Color.new(0.15, 0.15, 0.2, 0.95),
cornerRadius = 8,
padding = { top = 20, left = 20, right = 20, bottom = 20 },
positioning = "flex",
flexDirection = "vertical",
gap = 15,
})
-- Panel title
FlexLove.new({
parent = leftPanel,
text = "Immediate Mode Panel",
textSize = 24,
textColor = Color.new(0.4, 0.8, 1, 1),
width = "100%",
height = 40,
})
-- Description
FlexLove.new({
parent = leftPanel,
text = "Elements in this panel are recreated every frame.\nState is preserved by StateManager.",
textSize = 14,
textColor = Color.new(0.8, 0.8, 0.8, 1),
width = "100%",
height = 60,
})
-- Live frame counter (updates automatically)
FlexLove.new({
parent = leftPanel,
text = string.format("Frame: %d", frameCount),
textSize = 18,
textColor = Color.new(1, 1, 0.5, 1),
width = "100%",
height = 30,
backgroundColor = Color.new(0.2, 0.2, 0.3, 1),
padding = { top = 5, left = 10, right = 10, bottom = 5 },
cornerRadius = 4,
})
-- Click counter (updates automatically)
FlexLove.new({
parent = leftPanel,
text = string.format("Clicks: %d", clickCount),
textSize = 18,
textColor = Color.new(0.5, 1, 0.5, 1),
width = "100%",
height = 30,
backgroundColor = Color.new(0.2, 0.2, 0.3, 1),
padding = { top = 5, left = 10, right = 10, bottom = 5 },
cornerRadius = 4,
})
-- Interactive button (state preserved across frames)
FlexLove.new({
parent = leftPanel,
text = "Click Me! (Immediate Mode)",
textSize = 16,
textColor = Color.new(1, 1, 1, 1),
width = "100%",
height = 50,
themeComponent = "button",
onEvent = function(element, event)
if event.type == "release" then
clickCount = clickCount + 1
end
end,
})
-- Info box
FlexLove.new({
parent = leftPanel,
text = "Notice how the frame counter updates\nautomatically without any manual updates.\n\nThis is the power of immediate mode:\nUI reflects application state automatically.",
textSize = 13,
textColor = Color.new(0.7, 0.7, 0.7, 1),
width = "100%",
height = "auto",
backgroundColor = Color.new(0.1, 0.1, 0.15, 1),
padding = { top = 10, left = 10, right = 10, bottom = 10 },
cornerRadius = 4,
})
-- RIGHT PANEL: Retained Mode (Static, created once)
-- Only create on first frame
if not retainedPanelCreated then
retainedPanel = FlexLove.new({
mode = "retained", -- Explicit retained mode override
parent = container,
width = "45vw",
height = "calc(100vh - 100px)",
backgroundColor = Color.new(0.2, 0.15, 0.15, 0.95),
cornerRadius = 8,
padding = { top = 20, left = 20, right = 20, bottom = 20 },
positioning = "flex",
flexDirection = "vertical",
gap = 15,
})
-- Panel title (retained)
FlexLove.new({
mode = "retained",
parent = retainedPanel,
text = "Retained Mode Panel",
textSize = 24,
textColor = Color.new(1, 0.6, 0.4, 1),
width = "100%",
height = 40,
})
-- Description (retained)
FlexLove.new({
mode = "retained",
parent = retainedPanel,
text = "Elements in this panel are created once\nand persist across frames.",
textSize = 14,
textColor = Color.new(0.8, 0.8, 0.8, 1),
width = "100%",
height = 60,
})
-- Static frame counter (won't update)
local staticCounter = FlexLove.new({
mode = "retained",
parent = retainedPanel,
text = string.format("Created at frame: %d", frameCount),
textSize = 18,
textColor = Color.new(1, 1, 0.5, 1),
width = "100%",
height = 30,
backgroundColor = Color.new(0.3, 0.2, 0.2, 1),
padding = { top = 5, left = 10, right = 10, bottom = 5 },
cornerRadius = 4,
})
-- Click counter placeholder (must be manually updated)
local retainedClickCounter = FlexLove.new({
mode = "retained",
parent = retainedPanel,
text = string.format("Clicks: %d (manual update needed)", clickCount),
textSize = 18,
textColor = Color.new(0.5, 1, 0.5, 1),
width = "100%",
height = 30,
backgroundColor = Color.new(0.3, 0.2, 0.2, 1),
padding = { top = 5, left = 10, right = 10, bottom = 5 },
cornerRadius = 4,
})
-- Interactive button with manual update
FlexLove.new({
mode = "retained",
parent = retainedPanel,
text = "Click Me! (Retained Mode)",
textSize = 16,
textColor = Color.new(1, 1, 1, 1),
width = "100%",
height = 50,
themeComponent = "button",
onEvent = function(element, event)
if event.type == "release" then
clickCount = clickCount + 1
-- In retained mode, we must manually update the UI
retainedClickCounter.text = string.format("Clicks: %d (manual update needed)", clickCount)
end
end,
})
-- Info box (retained)
FlexLove.new({
mode = "retained",
parent = retainedPanel,
text = "Notice how this panel's elements\ndon't update automatically.\n\nIn retained mode, you must manually\nupdate element properties when state changes.\n\nThis gives better performance for\nstatic UI elements.",
textSize = 13,
textColor = Color.new(0.7, 0.7, 0.7, 1),
width = "100%",
height = "auto",
backgroundColor = Color.new(0.15, 0.1, 0.1, 1),
padding = { top = 10, left = 10, right = 10, bottom = 10 },
cornerRadius = 4,
})
retainedPanelCreated = true
end
FlexLove.endFrame()
-- Bottom instructions
love.graphics.setColor(0.5, 0.5, 0.5, 1)
love.graphics.print("Global mode: Immediate | Left panel: Immediate (explicit) | Right panel: Retained (override)", 10, love.graphics.getHeight() - 30)
love.graphics.print("Press ESC to quit", 10, love.graphics.getHeight() - 15)
end
function love.keypressed(key)
if key == "escape" then
love.event.quit()
end
end
function love.mousepressed(x, y, button)
FlexLove.mousepressed(x, y, button)
end
function love.mousereleased(x, y, button)
FlexLove.mousereleased(x, y, button)
end
function love.mousemoved(x, y, dx, dy)
FlexLove.mousemoved(x, y, dx, dy)
end

View File

@@ -16,7 +16,9 @@ local originalSearchers = package.searchers or package.loaders
table.insert(originalSearchers, 2, function(modname)
if modname:match("^FlexLove%.modules%.") then
local moduleName = modname:gsub("^FlexLove%.modules%.", "")
return function() return require("modules." .. moduleName) end
return function()
return require("modules." .. moduleName)
end
end
end)
@@ -46,7 +48,7 @@ function TestElementModeOverride:test_modeResolution_explicitImmediate()
mode = "immediate",
text = "Test",
})
luaunit.assertEquals(element._elementMode, "immediate")
end
@@ -56,7 +58,7 @@ function TestElementModeOverride:test_modeResolution_explicitRetained()
mode = "retained",
text = "Test",
})
luaunit.assertEquals(element._elementMode, "retained")
end
@@ -64,22 +66,22 @@ end
function TestElementModeOverride:test_modeResolution_nilUsesGlobalImmediate()
FlexLove.setMode("immediate")
FlexLove.beginFrame()
local element = FlexLove.new({
text = "Test",
})
luaunit.assertEquals(element._elementMode, "immediate")
end
-- Test 04: Mode resolution - nil uses global (retained)
function TestElementModeOverride:test_modeResolution_nilUsesGlobalRetained()
FlexLove.setMode("retained")
local element = FlexLove.new({
text = "Test",
})
luaunit.assertEquals(element._elementMode, "retained")
end
@@ -92,7 +94,7 @@ function TestElementModeOverride:test_idGeneration_onlyForImmediate()
})
luaunit.assertNotNil(immediateEl.id)
luaunit.assertNotEquals(immediateEl.id, "")
-- Retained element without ID should have empty ID
local retainedEl = FlexLove.new({
mode = "retained",
@@ -104,13 +106,13 @@ end
-- Test 06: Immediate override in retained context
function TestElementModeOverride:test_immediateOverrideInRetainedContext()
FlexLove.setMode("retained")
local element = FlexLove.new({
mode = "immediate",
id = "test-immediate",
text = "Immediate in retained context",
})
luaunit.assertEquals(element._elementMode, "immediate")
luaunit.assertEquals(element.id, "test-immediate")
end
@@ -119,12 +121,12 @@ end
function TestElementModeOverride:test_retainedOverrideInImmediateContext()
FlexLove.setMode("immediate")
FlexLove.beginFrame()
local element = FlexLove.new({
mode = "retained",
text = "Retained in immediate context",
})
luaunit.assertEquals(element._elementMode, "retained")
luaunit.assertEquals(element.id, "") -- Should not auto-generate ID
end
@@ -133,19 +135,19 @@ end
function TestElementModeOverride:test_mixedMode_immediateParent_retainedChild()
FlexLove.setMode("immediate")
FlexLove.beginFrame()
local parent = FlexLove.new({
mode = "immediate",
id = "parent",
text = "Parent",
})
local child = FlexLove.new({
mode = "retained",
parent = parent,
text = "Child",
})
luaunit.assertEquals(parent._elementMode, "immediate")
luaunit.assertEquals(child._elementMode, "retained")
-- Child should not inherit parent mode
@@ -155,19 +157,19 @@ end
-- Test 09: Mixed-mode parent-child (retained parent, immediate child)
function TestElementModeOverride:test_mixedMode_retainedParent_immediateChild()
FlexLove.setMode("retained")
local parent = FlexLove.new({
mode = "retained",
text = "Parent",
})
local child = FlexLove.new({
mode = "immediate",
id = "child",
parent = parent,
text = "Child",
})
luaunit.assertEquals(parent._elementMode, "retained")
luaunit.assertEquals(child._elementMode, "immediate")
luaunit.assertEquals(child.id, "child")
@@ -177,24 +179,24 @@ end
function TestElementModeOverride:test_frameRegistration_onlyImmediate()
FlexLove.setMode("immediate")
FlexLove.beginFrame()
local immediate1 = FlexLove.new({
mode = "immediate",
id = "imm1",
text = "Immediate 1",
})
local retained1 = FlexLove.new({
mode = "retained",
text = "Retained 1",
})
local immediate2 = FlexLove.new({
mode = "immediate",
id = "imm2",
text = "Immediate 2",
})
-- Count immediate elements in _currentFrameElements
local immediateCount = 0
for _, element in ipairs(FlexLove._currentFrameElements) do
@@ -202,14 +204,14 @@ function TestElementModeOverride:test_frameRegistration_onlyImmediate()
immediateCount = immediateCount + 1
end
end
luaunit.assertEquals(immediateCount, 2)
end
-- Test 11: Layout calculation for retained parent with immediate children
function TestElementModeOverride:test_layoutRetainedParentWithImmediateChildren()
FlexLove.setMode("retained")
-- Create retained parent with flex layout
local parent = FlexLove.new({
mode = "retained",
@@ -219,11 +221,11 @@ function TestElementModeOverride:test_layoutRetainedParentWithImmediateChildren(
flexDirection = "horizontal",
gap = 10,
})
-- Switch to immediate mode and add children
FlexLove.setMode("immediate")
FlexLove.beginFrame()
local child1 = FlexLove.new({
mode = "immediate",
id = "child1",
@@ -231,7 +233,7 @@ function TestElementModeOverride:test_layoutRetainedParentWithImmediateChildren(
width = 100,
height = 50,
})
local child2 = FlexLove.new({
mode = "immediate",
id = "child2",
@@ -239,9 +241,9 @@ function TestElementModeOverride:test_layoutRetainedParentWithImmediateChildren(
width = 100,
height = 50,
})
FlexLove.endFrame()
-- Verify children are positioned correctly by flex layout
luaunit.assertEquals(child1.x, 0)
luaunit.assertEquals(child1.y, 0)
@@ -253,7 +255,7 @@ end
function TestElementModeOverride:test_deeplyNestedMixedModes()
FlexLove.setMode("immediate")
FlexLove.beginFrame()
-- Level 1: Retained root
local root = FlexLove.new({
mode = "retained",
@@ -263,7 +265,7 @@ function TestElementModeOverride:test_deeplyNestedMixedModes()
flexDirection = "vertical",
gap = 5,
})
-- Level 2: Immediate child of retained parent
local middle = FlexLove.new({
mode = "immediate",
@@ -275,7 +277,7 @@ function TestElementModeOverride:test_deeplyNestedMixedModes()
flexDirection = "horizontal",
gap = 10,
})
-- Level 3: Retained grandchildren
local leaf1 = FlexLove.new({
mode = "retained",
@@ -283,16 +285,16 @@ function TestElementModeOverride:test_deeplyNestedMixedModes()
width = 100,
height = 50,
})
local leaf2 = FlexLove.new({
mode = "retained",
parent = middle,
width = 100,
height = 50,
})
FlexLove.endFrame()
-- Verify all levels are positioned correctly
luaunit.assertEquals(root.x, 0)
luaunit.assertEquals(root.y, 0)
@@ -307,20 +309,20 @@ end
-- Test 13: Immediate children of retained parents receive updates
function TestElementModeOverride:test_immediateChildrenOfRetainedParentsGetUpdated()
FlexLove.setMode("retained")
local updateCount = 0
-- Create retained parent
local parent = FlexLove.new({
mode = "retained",
width = 800,
height = 600,
})
-- Switch to immediate mode for child
FlexLove.setMode("immediate")
FlexLove.beginFrame()
-- Create immediate child that tracks updates
local child = FlexLove.new({
mode = "immediate",
@@ -329,15 +331,17 @@ function TestElementModeOverride:test_immediateChildrenOfRetainedParentsGetUpdat
width = 100,
height = 50,
})
-- Manually call update on the child to simulate what endFrame should do
-- In the real implementation, endFrame calls update on retained parents,
-- which cascades to immediate children
FlexLove.endFrame()
-- The child should be in the state manager
local state = StateManager.getState("updateTest")
luaunit.assertNotNil(state)
end
os.exit(luaunit.LuaUnit.run())
if not _G.RUNNING_ALL_TESTS then
os.exit(luaunit.LuaUnit.run())
end

View File

@@ -54,12 +54,9 @@ end
function TestModuleLoader:test_safeRequire_throws_error_for_missing_required_module()
-- Test loading a non-existent required module should throw error
lu.assertErrorMsgContains(
"Required module",
function()
ModuleLoader.safeRequire(modulePath .. "modules.NonExistentModule", false)
end
)
lu.assertErrorMsgContains("Required module", function()
ModuleLoader.safeRequire(modulePath .. "modules.NonExistentModule", false)
end)
end
function TestModuleLoader:test_stub_has_safe_init_method()
@@ -116,7 +113,7 @@ function TestModuleLoader:test_stub_returns_function_for_unknown_properties()
-- Unknown properties should return no-op functions for safe method calls
lu.assertIsFunction(stub.unknownProperty)
lu.assertIsFunction(stub.anotherUnknownProperty)
-- Calling unknown methods should not error
stub:unknownMethod()
stub:anotherUnknownMethod("arg1", "arg2")
@@ -197,5 +194,3 @@ end
if not _G.RUNNING_ALL_TESTS then
os.exit(lu.LuaUnit.run())
end
return TestModuleLoader

View File

@@ -1,6 +1,6 @@
--[[
Test: Retained Elements in Immediate Mode (No Duplication)
This test verifies that retained-mode elements don't get recreated
when the overall application is in immediate mode.
]]
@@ -39,34 +39,34 @@ function TestRetainedInImmediateMode:test_topLevelRetainedElementPersists()
})
return backdrop
end
FlexLove.beginFrame()
-- Frame 1: Create a retained element (no explicit ID)
local backdrop = createUI()
local backdropId = backdrop.id
luaunit.assertNotNil(backdropId, "Backdrop should have auto-generated ID")
luaunit.assertEquals(backdrop._elementMode, "retained")
FlexLove.endFrame()
-- Frame 2: Call createUI() again (same function, same line numbers)
FlexLove.beginFrame()
local backdrop2 = createUI()
-- Should return the SAME element, not create a new one
luaunit.assertEquals(backdrop2.id, backdropId, "Should return existing element with same ID")
luaunit.assertEquals(backdrop2, backdrop, "Should return exact same element instance")
FlexLove.endFrame()
end
-- Test that retained elements with explicit IDs can be recreated
function TestRetainedInImmediateMode:test_explicitIdAllowsNewElements()
FlexLove.beginFrame()
-- Create element with explicit ID
local element1 = FlexLove.new({
id = "my_custom_id",
@@ -75,26 +75,26 @@ function TestRetainedInImmediateMode:test_explicitIdAllowsNewElements()
height = 100,
backgroundColor = Color.new(1, 0, 0, 1),
})
FlexLove.endFrame()
FlexLove.beginFrame()
-- Create another element with SAME explicit ID but different properties
-- This should create a NEW element (user controls uniqueness)
local element2 = FlexLove.new({
id = "my_custom_id",
mode = "retained",
width = 200, -- Different properties
width = 200, -- Different properties
height = 200,
backgroundColor = Color.new(0, 1, 0, 1),
})
-- With explicit IDs, we allow duplicates (user responsibility)
luaunit.assertEquals(element2.id, "my_custom_id")
-- Properties should match NEW element, not old
luaunit.assertEquals(element2.width, 200)
FlexLove.endFrame()
end
@@ -106,38 +106,38 @@ function TestRetainedInImmediateMode:test_multipleRetainedElementsPersist()
width = "100%",
height = "100%",
})
local window = FlexLove.new({
mode = "retained",
width = "90%",
height = "90%",
})
return backdrop, window
end
FlexLove.beginFrame()
local backdrop, window = createUI()
local backdropId = backdrop.id
local windowId = window.id
luaunit.assertNotEquals(backdropId, windowId, "Different elements should have different IDs")
FlexLove.endFrame()
-- Frame 2
FlexLove.beginFrame()
local backdrop2, window2 = createUI()
-- Both should return existing elements
luaunit.assertEquals(backdrop2.id, backdropId)
luaunit.assertEquals(window2.id, windowId)
luaunit.assertEquals(backdrop2, backdrop)
luaunit.assertEquals(window2, window)
FlexLove.endFrame()
end
@@ -149,45 +149,46 @@ function TestRetainedInImmediateMode:test_retainedChildOfRetainedParentPersists(
width = 400,
height = 400,
})
local child = FlexLove.new({
mode = "retained",
parent = parent,
width = 100,
height = 100,
})
return parent, child
end
FlexLove.beginFrame()
local parent, child = createUI()
local parentId = parent.id
local childId = child.id
FlexLove.endFrame()
-- Frame 2
FlexLove.beginFrame()
local parent2, child2 = createUI()
-- Parent should be the same
luaunit.assertEquals(parent2.id, parentId)
luaunit.assertEquals(parent2, parent)
-- Child should also be the same instance
luaunit.assertEquals(child2.id, childId, "Child ID should match")
luaunit.assertEquals(child2, child, "Child should be same instance")
-- Child should still exist in parent's children
luaunit.assertEquals(#parent2.children, 1, "Parent should have exactly 1 child")
luaunit.assertEquals(parent2.children[1].id, childId)
FlexLove.endFrame()
end
-- Run tests
os.exit(luaunit.LuaUnit.run())
if not _G.RUNNING_ALL_TESTS then
os.exit(luaunit.LuaUnit.run())
end

View File

@@ -1,6 +1,6 @@
--[[
Test: Retained Elements with Varying Props (ID Stability)
This test verifies that retained-mode elements return the same instance
across frames even when props vary slightly (e.g., different Color instances).
]]
@@ -30,33 +30,33 @@ end
-- Test that retained elements persist despite creating new Color instances
function TestRetainedPropStability:test_retainedElementIgnoresColorInstanceChanges()
FlexLove.beginFrame()
-- Frame 1: Create retained element with Color instance
local backdrop1 = FlexLove.new({
mode = "retained",
width = "100%",
height = "100%",
backgroundColor = Color.new(1, 1, 1, 0.1), -- NEW Color instance
backgroundColor = Color.new(1, 1, 1, 0.1), -- NEW Color instance
})
local id1 = backdrop1.id
FlexLove.endFrame()
-- Frame 2: Same props but NEW Color instance (common pattern in user code)
FlexLove.beginFrame()
local backdrop2 = FlexLove.new({
mode = "retained",
width = "100%",
height = "100%",
backgroundColor = Color.new(1, 1, 1, 0.1), -- NEW Color instance (different table)
backgroundColor = Color.new(1, 1, 1, 0.1), -- NEW Color instance (different table)
})
-- Should return SAME element despite different Color instance
luaunit.assertEquals(backdrop2.id, id1, "ID should be stable across frames")
luaunit.assertEquals(backdrop2, backdrop1, "Should return same element instance")
FlexLove.endFrame()
end
@@ -81,23 +81,23 @@ function TestRetainedPropStability:test_retainedElementWithComplexProps()
gap = 10,
})
end
FlexLove.beginFrame()
local window1 = createWindow()
local id1 = window1.id
FlexLove.endFrame()
-- Frame 2: Same function, same props
FlexLove.beginFrame()
local window2 = createWindow()
-- Should return same element
luaunit.assertEquals(window2.id, id1)
luaunit.assertEquals(window2, window1)
FlexLove.endFrame()
end
@@ -108,27 +108,27 @@ function TestRetainedPropStability:test_retainedElementWithBackdropBlur()
mode = "retained",
width = "100%",
height = "100%",
backdropBlur = { radius = 10 }, -- Table prop
backdropBlur = { radius = 10 }, -- Table prop
backgroundColor = Color.new(1, 1, 1, 0.1),
})
end
FlexLove.beginFrame()
local backdrop1 = createBackdrop()
local id1 = backdrop1.id
FlexLove.endFrame()
-- Frame 2
FlexLove.beginFrame()
local backdrop2 = createBackdrop()
-- Should return same element
luaunit.assertEquals(backdrop2.id, id1)
luaunit.assertEquals(backdrop2, backdrop1)
FlexLove.endFrame()
end
@@ -143,7 +143,7 @@ function TestRetainedPropStability:test_multipleRetainedElementsWithVaryingProps
backdropBlur = { radius = 10 },
backgroundColor = Color.new(1, 1, 1, 0.1),
})
local window = FlexLove.new({
mode = "retained",
z = 100,
@@ -154,31 +154,32 @@ function TestRetainedPropStability:test_multipleRetainedElementsWithVaryingProps
themeComponent = "framev3",
padding = { horizontal = "5%", vertical = "3%" },
})
return backdrop, window
end
FlexLove.beginFrame()
local backdrop1, window1 = createUI()
local backdropId = backdrop1.id
local windowId = window1.id
FlexLove.endFrame()
-- Frame 2: New Color instances, new table instances for props
FlexLove.beginFrame()
local backdrop2, window2 = createUI()
-- Both should return existing elements
luaunit.assertEquals(backdrop2.id, backdropId)
luaunit.assertEquals(window2.id, windowId)
luaunit.assertEquals(backdrop2, backdrop1)
luaunit.assertEquals(window2, window1)
FlexLove.endFrame()
end
-- Run tests
os.exit(luaunit.LuaUnit.run())
if not _G.RUNNING_ALL_TESTS then
os.exit(luaunit.LuaUnit.run())
end

View File

@@ -1059,7 +1059,7 @@ function TestScrollManagerEdgeCases:testScrollbarKnobOffsetDefault()
-- When not provided, scrollbarKnobOffset should be nil (use theme default)
local sm = createScrollManager({})
luaunit.assertNil(sm.scrollbarKnobOffset)
-- When explicitly set to 0, it should be normalized
local sm2 = createScrollManager({ scrollbarKnobOffset = 0 })
luaunit.assertNotNil(sm2.scrollbarKnobOffset)
@@ -1073,7 +1073,7 @@ function TestScrollManagerEdgeCases:testScrollbarKnobOffsetStatePersistence()
local sm = createScrollManager({ scrollbarKnobOffset = { x = 5, y = 10 } })
local state = sm:getState()
luaunit.assertNotNil(state.scrollbarKnobOffset)
local sm2 = createScrollManager({})
sm2:setState(state)
luaunit.assertEquals(sm2.scrollbarKnobOffset.x, 5)

View File

@@ -373,9 +373,6 @@ function TestThemeValidation:test_validate_valid_colors()
luaunit.assertEquals(#errors, 0)
end
function TestThemeValidation:test_validate_colors_non_table()
local theme = {
name = "Test Theme",
@@ -726,7 +723,6 @@ function TestThemeValidation:test_sanitize_nil_theme()
luaunit.assertEquals(sanitized.name, "Invalid Theme")
end
function TestThemeValidation:test_sanitize_theme_with_non_string_name()
local theme = {
name = 123,
@@ -735,7 +731,6 @@ function TestThemeValidation:test_sanitize_theme_with_non_string_name()
luaunit.assertEquals(type(sanitized.name), "string")
end
function TestThemeValidation:test_sanitize_removes_non_string_color_names()
local theme = {
name = "Test",
@@ -774,8 +769,6 @@ end
-- === Complex Theme Validation ===
-- Run tests if this file is executed directly
if not _G.RUNNING_ALL_TESTS then
os.exit(luaunit.LuaUnit.run())

View File

@@ -38,32 +38,33 @@ local luaunit = require("testing.luaunit")
local testFiles = {
"testing/__tests__/animation_test.lua",
"testing/__tests__/blur_test.lua",
"testing/__tests__/calc_test.lua",
"testing/__tests__/critical_failures_test.lua",
"testing/__tests__/element_test.lua",
"testing/__tests__/element_mode_override_test.lua",
"testing/__tests__/event_handler_test.lua",
"testing/__tests__/flexlove_test.lua",
"testing/__tests__/grid_test.lua",
"testing/__tests__/image_cache_test.lua",
"testing/__tests__/image_renderer_test.lua",
"testing/__tests__/image_scaler_test.lua",
"testing/__tests__/input_event_test.lua",
"testing/__tests__/layout_engine_test.lua",
"testing/__tests__/mixed_mode_events_test.lua",
"testing/__tests__/mixed_mode_children_test.lua",
"testing/__tests__/retained_in_immediate_test.lua",
"testing/__tests__/mixed_mode_events_test.lua",
"testing/__tests__/module_loader_test.lua",
"testing/__tests__/ninepatch_test.lua",
"testing/__tests__/performance_test.lua",
"testing/__tests__/renderer_test.lua",
"testing/__tests__/retained_in_immediate_test.lua",
"testing/__tests__/retained_prop_stability_test.lua",
"testing/__tests__/roundedrect_test.lua",
"testing/__tests__/scroll_manager_test.lua",
"testing/__tests__/text_editor_test.lua",
"testing/__tests__/theme_test.lua",
"testing/__tests__/touch_events_test.lua",
"testing/__tests__/units_test.lua",
"testing/__tests__/utils_test.lua",
"testing/__tests__/calc_test.lua",
-- Feature/Integration tests
"testing/__tests__/critical_failures_test.lua",
"testing/__tests__/flexlove_test.lua",
"testing/__tests__/touch_events_test.lua",
}
local success = true