hover&unhover events

This commit is contained in:
Michael Freno
2025-12-11 13:06:50 -05:00
parent 0bceade7d5
commit 56c8e744d5
7 changed files with 742 additions and 230 deletions

View File

@@ -1,24 +1,19 @@
-- Profiling test comparing retained mode flag vs. default behavior in complex UI
-- This simulates creating a settings menu multiple times per frame to stress test
-- the performance difference between explicit mode="retained" and implicit retained mode
package.path = package.path .. ";../../?.lua;../../modules/?.lua"
-- Settings Menu Mode Comparison Profile
-- Compares performance between explicit mode="retained" flags vs implicit retained mode
local FlexLove = require("FlexLove")
local Color = require("modules.Color")
-- Mock resolution sets (simplified)
local resolution_sets = {
["16:9"] = {
{ 1920, 1080 },
{ 1600, 900 },
{ 1280, 720 },
},
["16:10"] = {
{ 1920, 1200 },
{ 1680, 1050 },
{ 1280, 800 },
local profile = {
name = "Settings Menu Mode Comparison",
description = "Tests whether explicit mode='retained' has performance overhead",
testPhase = "warmup", -- warmup, implicit, explicit, complete
frameCount = 0,
framesPerPhase = 300, -- 5 seconds at 60 FPS
results = {
implicit = { startMem = 0, endMem = 0, avgFrameTime = 0, frameTimes = {} },
explicit = { startMem = 0, endMem = 0, avgFrameTime = 0, frameTimes = {} },
},
currentFrameStart = nil,
}
-- Mock Settings object
@@ -28,48 +23,32 @@ local Settings = {
fullscreen = false,
vsync = true,
msaa = 4,
resizable = true,
borderless = false,
masterVolume = 0.8,
musicVolume = 0.7,
sfxVolume = 0.9,
crtEffectStrength = 0.3,
},
get = function(self, key)
return self.values[key]
end,
set = function(self, key, value)
self.values[key] = value
end,
reset_to_defaults = function(self) end,
apply = function(self) end,
}
-- Helper function to round numbers
local function round(num, decimals)
local mult = 10 ^ (decimals or 0)
return math.floor(num * mult + 0.5) / mult
end
-- Simplified SettingsMenu implementation
local function create_settings_menu_with_mode_flag(use_mode_flag)
local function createSettingsMenu(useExplicitMode)
local GuiZIndexing = { MainMenuOverlay = 100 }
-- Backdrop
local backdrop_props = {
local backdropProps = {
z = GuiZIndexing.MainMenuOverlay - 1,
width = "100%",
height = "100%",
backdropBlur = { radius = 10 },
backgroundColor = Color.new(1, 1, 1, 0.1),
backgroundColor = FlexLove.Color.new(1, 1, 1, 0.1),
}
if use_mode_flag then
backdrop_props.mode = "retained"
if useExplicitMode then
backdropProps.mode = "retained"
end
local backdrop = FlexLove.new(backdrop_props)
local backdrop = FlexLove.new(backdropProps)
-- Main window
local window_props = {
local windowProps = {
z = GuiZIndexing.MainMenuOverlay,
x = "5%",
y = "5%",
@@ -85,10 +64,10 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
padding = { horizontal = "5%", vertical = "3%" },
gap = 10,
}
if use_mode_flag then
window_props.mode = "retained"
if useExplicitMode then
windowProps.mode = "retained"
end
local window = FlexLove.new(window_props)
local window = FlexLove.new(windowProps)
-- Close button
FlexLove.new({
@@ -131,7 +110,7 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
textAlign = "start",
textSize = "xl",
width = "100%",
textColor = Color.new(0.8, 0.9, 1, 1),
textColor = FlexLove.Color.new(0.8, 0.9, 1, 1),
})
-- Resolution control
@@ -246,7 +225,7 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
width = "30%",
})
local button_container = FlexLove.new({
local buttonContainer = FlexLove.new({
parent = row4,
width = "60%",
height = "100%",
@@ -255,19 +234,19 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
gap = 5,
})
local msaa_values = { 0, 1, 2, 4, 8, 16 }
for _, msaa_val in ipairs(msaa_values) do
local is_selected = Settings:get("msaa") == msaa_val
local msaaValues = { 0, 1, 2, 4, 8, 16 }
for _, msaaVal in ipairs(msaaValues) do
local isSelected = Settings:get("msaa") == msaaVal
FlexLove.new({
parent = button_container,
themeComponent = is_selected and "buttonv1" or "buttonv2",
text = tostring(msaa_val),
parent = buttonContainer,
themeComponent = isSelected and "buttonv1" or "buttonv2",
text = tostring(msaaVal),
textAlign = "center",
width = "8vw",
height = "100%",
textSize = "sm",
disabled = is_selected,
opacity = is_selected and 0.7 or 1.0,
disabled = isSelected,
opacity = isSelected and 0.7 or 1.0,
})
end
@@ -278,7 +257,7 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
textAlign = "start",
textSize = "xl",
width = "100%",
textColor = Color.new(0.8, 0.9, 1, 1),
textColor = FlexLove.Color.new(0.8, 0.9, 1, 1),
})
-- Master volume slider
@@ -301,7 +280,7 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
width = "30%",
})
local slider_container = FlexLove.new({
local sliderContainer = FlexLove.new({
parent = row5,
width = "50%",
height = "100%",
@@ -314,8 +293,8 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
local value = Settings:get("masterVolume")
local normalized = value
local slider_track = FlexLove.new({
parent = slider_container,
local sliderTrack = FlexLove.new({
parent = sliderContainer,
width = "80%",
height = "75%",
positioning = "flex",
@@ -324,7 +303,7 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
})
FlexLove.new({
parent = slider_track,
parent = sliderTrack,
width = (normalized * 100) .. "%",
height = "100%",
themeComponent = "buttonv1",
@@ -332,7 +311,7 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
})
FlexLove.new({
parent = slider_container,
parent = sliderContainer,
text = string.format("%d", value * 100),
textAlign = "center",
textSize = "md",
@@ -340,7 +319,7 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
})
-- Meta controls (bottom buttons)
local meta_container = FlexLove.new({
local metaContainer = FlexLove.new({
parent = window,
positioning = "absolute",
width = "100%",
@@ -349,8 +328,8 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
x = "0%",
})
local button_bar = FlexLove.new({
parent = meta_container,
local buttonBar = FlexLove.new({
parent = metaContainer,
width = "100%",
positioning = "flex",
flexDirection = "horizontal",
@@ -360,7 +339,7 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
})
FlexLove.new({
parent = button_bar,
parent = buttonBar,
themeComponent = "buttonv2",
text = "Reset",
textAlign = "center",
@@ -372,136 +351,365 @@ local function create_settings_menu_with_mode_flag(use_mode_flag)
return { backdrop = backdrop, window = window }
end
-- Profile configuration
local PROFILE_NAME = "Settings Menu Mode Comparison"
local ITERATIONS_PER_TEST = 100 -- Create the menu 100 times to measure difference
print("=" .. string.rep("=", 78))
print(string.format(" %s", PROFILE_NAME))
print("=" .. string.rep("=", 78))
print()
print("This profile compares performance when creating a complex settings menu")
print("with explicit mode='retained' flags vs. implicit retained mode (global).")
print()
print(string.format("Test configuration:"))
print(string.format(" - Iterations: %d menu creations per test", ITERATIONS_PER_TEST))
print(string.format(" - Elements per menu: ~45 (backdrop, window, buttons, sliders, etc.)"))
print(string.format(" - Total elements created: ~%d per test", ITERATIONS_PER_TEST * 45))
print()
-- Warm up
print("Warming up...")
FlexLove.init({ immediateMode = false, theme = "space" })
for i = 1, 10 do
create_settings_menu_with_mode_flag(false)
end
collectgarbage("collect")
-- Test 1: Without explicit mode flags (implicit retained via global setting)
print("Running Test 1: Without explicit mode='retained' flags...")
FlexLove.init({ immediateMode = false, theme = "space" })
collectgarbage("collect")
local mem_before_implicit = collectgarbage("count")
local time_before_implicit = os.clock()
for i = 1, ITERATIONS_PER_TEST do
local menu = create_settings_menu_with_mode_flag(false)
function profile.init()
print("\n=== Settings Menu Mode Comparison Profile ===\n")
print("Testing whether explicit mode='retained' has performance overhead")
print("compared to implicit retained mode (global setting).\n")
FlexLove.init({
width = love.graphics.getWidth(),
height = love.graphics.getHeight(),
immediateMode = false, -- Global retained mode
theme = "space",
})
profile.testPhase = "warmup"
profile.frameCount = 0
print("Phase 1: Warmup (30 frames)...")
end
local time_after_implicit = os.clock()
collectgarbage("collect")
local mem_after_implicit = collectgarbage("count")
local time_implicit = time_after_implicit - time_before_implicit
local mem_implicit = mem_after_implicit - mem_before_implicit
print(string.format(" Time: %.4f seconds", time_implicit))
print(string.format(" Memory: %.2f KB", mem_implicit))
print(string.format(" Avg time per menu: %.4f ms", (time_implicit / ITERATIONS_PER_TEST) * 1000))
print()
-- Test 2: With explicit mode="retained" flags
print("Running Test 2: With explicit mode='retained' flags...")
FlexLove.init({ immediateMode = false, theme = "space" })
collectgarbage("collect")
local mem_before_explicit = collectgarbage("count")
local time_before_explicit = os.clock()
for i = 1, ITERATIONS_PER_TEST do
local menu = create_settings_menu_with_mode_flag(true)
function profile.update(dt)
if profile.testPhase == "complete" then
return
end
-- Track frame time
local frameStart = love.timer.getTime()
if profile.testPhase == "warmup" then
-- Warmup phase - create menu a few times
createSettingsMenu(false)
profile.frameCount = profile.frameCount + 1
if profile.frameCount >= 30 then
print(" Warmup complete.\n")
print("Phase 2: Testing WITHOUT explicit mode flags (" .. profile.framesPerPhase .. " frames)...")
profile.testPhase = "implicit"
profile.frameCount = 0
collectgarbage("collect")
collectgarbage("collect")
profile.results.implicit.startMem = collectgarbage("count")
end
elseif profile.testPhase == "implicit" then
-- Test implicit mode (no explicit mode flags)
createSettingsMenu(false)
local frameTime = (love.timer.getTime() - frameStart) * 1000
table.insert(profile.results.implicit.frameTimes, frameTime)
profile.frameCount = profile.frameCount + 1
if profile.frameCount >= profile.framesPerPhase then
collectgarbage("collect")
collectgarbage("collect")
profile.results.implicit.endMem = collectgarbage("count")
-- Calculate average
local sum = 0
for _, ft in ipairs(profile.results.implicit.frameTimes) do
sum = sum + ft
end
profile.results.implicit.avgFrameTime = sum / #profile.results.implicit.frameTimes
print(" Complete. Avg frame time: " .. string.format("%.4f", profile.results.implicit.avgFrameTime) .. "ms\n")
print("Phase 3: Testing WITH explicit mode='retained' flags (" .. profile.framesPerPhase .. " frames)...")
profile.testPhase = "explicit"
profile.frameCount = 0
collectgarbage("collect")
collectgarbage("collect")
profile.results.explicit.startMem = collectgarbage("count")
end
elseif profile.testPhase == "explicit" then
-- Test explicit mode (with mode="retained" flags)
createSettingsMenu(true)
local frameTime = (love.timer.getTime() - frameStart) * 1000
table.insert(profile.results.explicit.frameTimes, frameTime)
profile.frameCount = profile.frameCount + 1
if profile.frameCount >= profile.framesPerPhase then
collectgarbage("collect")
collectgarbage("collect")
profile.results.explicit.endMem = collectgarbage("count")
-- Calculate average
local sum = 0
for _, ft in ipairs(profile.results.explicit.frameTimes) do
sum = sum + ft
end
profile.results.explicit.avgFrameTime = sum / #profile.results.explicit.frameTimes
print(" Complete. Avg frame time: " .. string.format("%.4f", profile.results.explicit.avgFrameTime) .. "ms\n")
profile.testPhase = "complete"
profile.generateReport()
end
end
end
local time_after_explicit = os.clock()
collectgarbage("collect")
local mem_after_explicit = collectgarbage("count")
local time_explicit = time_after_explicit - time_before_explicit
local mem_explicit = mem_after_explicit - mem_before_explicit
print(string.format(" Time: %.4f seconds", time_explicit))
print(string.format(" Memory: %.2f KB", mem_explicit))
print(string.format(" Avg time per menu: %.4f ms", (time_explicit / ITERATIONS_PER_TEST) * 1000))
print()
-- Calculate differences
print("=" .. string.rep("=", 78))
print("RESULTS COMPARISON")
print("=" .. string.rep("=", 78))
print()
local time_diff = time_explicit - time_implicit
local time_percent = (time_diff / time_implicit) * 100
local mem_diff = mem_explicit - mem_implicit
print(string.format("Time Difference:"))
print(string.format(" Without mode flag: %.4f seconds", time_implicit))
print(string.format(" With mode flag: %.4f seconds", time_explicit))
print(string.format(" Difference: %.4f seconds (%+.2f%%)", time_diff, time_percent))
print()
print(string.format("Memory Difference:"))
print(string.format(" Without mode flag: %.2f KB", mem_implicit))
print(string.format(" With mode flag: %.2f KB", mem_explicit))
print(string.format(" Difference: %+.2f KB", mem_diff))
print()
-- Interpretation
print("INTERPRETATION:")
print()
if math.abs(time_percent) < 5 then
print(" ✓ Performance is essentially identical (< 5% difference)")
print(" The explicit mode flag has negligible impact on performance.")
elseif time_percent > 0 then
print(string.format(" ⚠ Explicit mode flag is %.2f%% SLOWER", time_percent))
print(" This indicates overhead from mode checking/resolution.")
else
print(string.format(" ✓ Explicit mode flag is %.2f%% FASTER", -time_percent))
print(" This indicates potential optimization benefits.")
function profile.draw()
-- Draw the current menu
if profile.testPhase ~= "complete" then
FlexLove.draw()
end
-- Draw status overlay
love.graphics.setColor(0, 0, 0, 0.85)
love.graphics.rectangle("fill", 10, 10, 400, 120)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.print("Settings Menu Mode Comparison", 20, 20)
if profile.testPhase == "warmup" then
love.graphics.print("Phase: Warmup (" .. profile.frameCount .. "/30)", 20, 45)
elseif profile.testPhase == "implicit" then
love.graphics.print("Phase: Without mode flags", 20, 45)
love.graphics.print("Progress: " .. profile.frameCount .. "/" .. profile.framesPerPhase, 20, 65)
elseif profile.testPhase == "explicit" then
love.graphics.print("Phase: With mode='retained' flags", 20, 45)
love.graphics.print("Progress: " .. profile.frameCount .. "/" .. profile.framesPerPhase, 20, 65)
elseif profile.testPhase == "complete" then
love.graphics.print("Phase: COMPLETE", 20, 45)
love.graphics.print("Report saved! Press S to save again, ESC to exit.", 20, 65)
end
love.graphics.print("Memory: " .. string.format("%.2f", collectgarbage("count") / 1024) .. " MB", 20, 85)
love.graphics.print("Press ESC to return to menu", 20, 105)
end
print()
if math.abs(mem_diff) < 50 then
print(" ✓ Memory usage is essentially identical (< 50 KB difference)")
elseif mem_diff > 0 then
print(string.format(" ⚠ Explicit mode flag uses %.2f KB MORE memory", mem_diff))
else
print(string.format(" ✓ Explicit mode flag uses %.2f KB LESS memory", -mem_diff))
function profile.generateReport()
print("\n" .. string.rep("=", 80))
print("RESULTS COMPARISON")
print(string.rep("=", 80) .. "\n")
local timeDiff = profile.results.explicit.avgFrameTime - profile.results.implicit.avgFrameTime
local timePercent = (timeDiff / profile.results.implicit.avgFrameTime) * 100
local memDiff = (profile.results.explicit.endMem - profile.results.explicit.startMem) -
(profile.results.implicit.endMem - profile.results.implicit.startMem)
print("Time Comparison:")
print(string.format(" Without mode flag: %.4f ms", profile.results.implicit.avgFrameTime))
print(string.format(" With mode flag: %.4f ms", profile.results.explicit.avgFrameTime))
print(string.format(" Difference: %.4f ms (%+.2f%%)", timeDiff, timePercent))
print()
print("Memory Comparison:")
print(string.format(" Without mode flag: %.2f KB", profile.results.implicit.endMem - profile.results.implicit.startMem))
print(string.format(" With mode flag: %.2f KB", profile.results.explicit.endMem - profile.results.explicit.startMem))
print(string.format(" Difference: %+.2f KB", memDiff))
print()
print("INTERPRETATION:")
print()
if math.abs(timePercent) < 5 then
print(" ✓ Performance is essentially identical (< 5% difference)")
print(" The explicit mode flag has negligible impact on performance.")
elseif timePercent > 0 then
print(string.format(" ⚠ Explicit mode flag is %.2f%% SLOWER", timePercent))
print(" This indicates overhead from mode checking/resolution.")
else
print(string.format(" ✓ Explicit mode flag is %.2f%% FASTER", -timePercent))
print(" This indicates potential optimization benefits.")
end
print()
if math.abs(memDiff) < 50 then
print(" ✓ Memory usage is essentially identical (< 50 KB difference)")
elseif memDiff > 0 then
print(string.format(" ⚠ Explicit mode flag uses %.2f KB MORE memory", memDiff))
else
print(string.format(" ✓ Explicit mode flag uses %.2f KB LESS memory", -memDiff))
end
print()
print("RECOMMENDATION:")
print()
if math.abs(timePercent) < 5 and math.abs(memDiff) < 50 then
print(" The explicit mode='retained' flag provides clarity and explicitness")
print(" without any meaningful performance cost. It's recommended for:")
print(" - Code readability (makes intent explicit)")
print(" - Future-proofing (if global mode changes)")
print(" - Mixed-mode UIs (where some elements are immediate)")
else
print(" Consider the trade-offs based on your specific use case.")
end
print()
print(string.rep("=", 80))
print("Profile complete! Press S to save report.")
print(string.rep("=", 80) .. "\n")
-- Save report automatically
profile.saveReportToFile()
end
print()
print("RECOMMENDATION:")
print()
if math.abs(time_percent) < 5 and math.abs(mem_diff) < 50 then
print(" The explicit mode='retained' flag provides clarity and explicitness")
print(" without any meaningful performance cost. It's recommended for:")
print(" - Code readability (makes intent explicit)")
print(" - Future-proofing (if global mode changes)")
print(" - Mixed-mode UIs (where some elements are immediate)")
else
print(" Consider the trade-offs based on your specific use case.")
function profile.saveReportToFile()
local timestamp = os.date("%Y-%m-%d_%H-%M-%S")
local filename = string.format("reports/settings_menu_mode_profile/%s.md", timestamp)
-- Get the actual project directory
local sourceDir = love.filesystem.getSource()
local filepath
if sourceDir:match("%.love$") then
-- Running from .love file, use save directory
love.filesystem.createDirectory("reports/settings_menu_mode_profile")
-- Use love.filesystem for sandboxed writes
local content = profile.formatReportMarkdown()
local success = love.filesystem.write(filename, content)
if success then
print("\n✓ Report saved to: " .. love.filesystem.getSaveDirectory() .. "/" .. filename)
else
print("\n✗ Failed to save report")
end
else
-- Running from source, use io module
filepath = sourceDir .. "/" .. filename
os.execute('mkdir -p "' .. sourceDir .. '/reports/settings_menu_mode_profile"')
local file, err = io.open(filepath, "w")
if file then
file:write(profile.formatReportMarkdown())
file:close()
print("\n✓ Report saved to: " .. filepath)
-- Also save as latest.md
local latestPath = sourceDir .. "/reports/settings_menu_mode_profile/latest.md"
local latestFile = io.open(latestPath, "w")
if latestFile then
latestFile:write(profile.formatReportMarkdown())
latestFile:close()
end
else
print("\n✗ Failed to save report: " .. tostring(err))
end
end
end
print()
print("=" .. string.rep("=", 78))
print("Profile complete!")
print("=" .. string.rep("=", 78))
function profile.formatReportMarkdown()
local lines = {}
table.insert(lines, "# Settings Menu Mode Comparison Report")
table.insert(lines, "")
table.insert(lines, "**Generated:** " .. os.date("%Y-%m-%d %H:%M:%S"))
table.insert(lines, "")
table.insert(lines, "This profile compares performance when creating a complex settings menu")
table.insert(lines, "with explicit `mode='retained'` flags vs. implicit retained mode (global setting).")
table.insert(lines, "")
table.insert(lines, "---")
table.insert(lines, "")
table.insert(lines, "## Test Configuration")
table.insert(lines, "")
table.insert(lines, "- **Frames per test:** " .. profile.framesPerPhase)
table.insert(lines, "- **Elements per menu:** ~45 (backdrop, window, buttons, sliders, etc.)")
table.insert(lines, "- **Global mode:** `immediateMode = false` (retained)")
table.insert(lines, "")
table.insert(lines, "## Results")
table.insert(lines, "")
local timeDiff = profile.results.explicit.avgFrameTime - profile.results.implicit.avgFrameTime
local timePercent = (timeDiff / profile.results.implicit.avgFrameTime) * 100
local memDiff = (profile.results.explicit.endMem - profile.results.explicit.startMem) -
(profile.results.implicit.endMem - profile.results.implicit.startMem)
table.insert(lines, "### Frame Time Comparison")
table.insert(lines, "")
table.insert(lines, "| Metric | Without `mode` flag | With `mode='retained'` flag | Difference |")
table.insert(lines, "|--------|--------------------:|----------------------------:|-----------:|")
table.insert(lines, string.format("| Average Frame Time | %.4f ms | %.4f ms | %+.4f ms (%+.2f%%) |",
profile.results.implicit.avgFrameTime,
profile.results.explicit.avgFrameTime,
timeDiff,
timePercent))
table.insert(lines, "")
table.insert(lines, "### Memory Comparison")
table.insert(lines, "")
table.insert(lines, "| Metric | Without `mode` flag | With `mode='retained'` flag | Difference |")
table.insert(lines, "|--------|--------------------:|----------------------------:|-----------:|")
table.insert(lines, string.format("| Memory Used | %.2f KB | %.2f KB | %+.2f KB |",
profile.results.implicit.endMem - profile.results.implicit.startMem,
profile.results.explicit.endMem - profile.results.explicit.startMem,
memDiff))
table.insert(lines, "")
table.insert(lines, "## Interpretation")
table.insert(lines, "")
if math.abs(timePercent) < 5 then
table.insert(lines, "✓ **Performance is essentially identical** (< 5% difference)")
table.insert(lines, "")
table.insert(lines, "The explicit `mode='retained'` flag has negligible impact on performance.")
elseif timePercent > 0 then
table.insert(lines, string.format("⚠ **Explicit mode flag is %.2f%% SLOWER**", timePercent))
table.insert(lines, "")
table.insert(lines, "This indicates overhead from mode checking/resolution.")
else
table.insert(lines, string.format("✓ **Explicit mode flag is %.2f%% FASTER**", -timePercent))
table.insert(lines, "")
table.insert(lines, "This indicates potential optimization benefits.")
end
table.insert(lines, "")
if math.abs(memDiff) < 50 then
table.insert(lines, "✓ **Memory usage is essentially identical** (< 50 KB difference)")
elseif memDiff > 0 then
table.insert(lines, string.format("⚠ **Explicit mode flag uses %.2f KB MORE memory**", memDiff))
else
table.insert(lines, string.format("✓ **Explicit mode flag uses %.2f KB LESS memory**", -memDiff))
end
table.insert(lines, "")
table.insert(lines, "## Recommendation")
table.insert(lines, "")
if math.abs(timePercent) < 5 and math.abs(memDiff) < 50 then
table.insert(lines, "The explicit `mode='retained'` flag provides clarity and explicitness")
table.insert(lines, "without any meaningful performance cost. It's recommended for:")
table.insert(lines, "")
table.insert(lines, "- **Code readability** - Makes intent explicit")
table.insert(lines, "- **Future-proofing** - If global mode changes")
table.insert(lines, "- **Mixed-mode UIs** - Where some elements are immediate")
else
table.insert(lines, "Consider the trade-offs based on your specific use case.")
end
table.insert(lines, "")
table.insert(lines, "---")
table.insert(lines, "")
table.insert(lines, "*Report generated by FlexLöve Performance Profiler*")
return table.concat(lines, "\n")
end
function profile.keypressed(key, profiler)
if key == "s" and profile.testPhase == "complete" then
profile.saveReportToFile()
end
end
function profile.resize(w, h)
FlexLove.resize(w, h)
end
function profile.reset()
profile.testPhase = "warmup"
profile.frameCount = 0
profile.results = {
implicit = { startMem = 0, endMem = 0, avgFrameTime = 0, frameTimes = {} },
explicit = { startMem = 0, endMem = 0, avgFrameTime = 0, frameTimes = {} },
}
print("\nProfile reset. Starting over...\n")
end
function profile.cleanup()
print("\nCleaning up settings menu mode profile...\n")
end
return profile

View File

@@ -1,8 +1,26 @@
-- FlexLöve Profiler - Main Entry Point
-- Load FlexLöve from parent directory
package.path = package.path .. ";../?.lua;../?/init.lua"
local FlexLove = require("libs.FlexLove")
-- Override require to handle FlexLove.modules.X properly
-- When FlexLove.lua requires "FlexLove.modules.ErrorHandler",
-- we redirect it to "../modules/ErrorHandler"
local originalRequire = require
local function customRequire(modname)
-- Check if this is a FlexLove.modules.X require
local moduleName = modname:match("^FlexLove%.modules%.(.+)$")
if moduleName then
-- Redirect to ../modules/X
return originalRequire("modules." .. moduleName)
end
-- Otherwise use original require
return originalRequire(modname)
end
_G.require = customRequire
-- Set up package.path for normal requires
package.path = package.path .. ";../?.lua;../?/init.lua;../modules/?.lua"
local FlexLove = originalRequire("FlexLove")
local PerformanceProfiler = require("profiling.utils.PerformanceProfiler")
local lv = love
@@ -118,17 +136,9 @@ local function buildMenu()
padding = { horizontal = 40, vertical = 40 },
})
local container = FlexLove.new({
parent = root,
positioning = "flex",
flexDirection = "vertical",
alignItems = "center",
gap = 30,
})
-- Title
FlexLove.new({
parent = container,
parent = root,
width = 600,
height = 80,
backgroundColor = FlexLove.Color.new(0.15, 0.15, 0.25, 1),
@@ -141,6 +151,16 @@ local function buildMenu()
textColor = FlexLove.Color.new(0.3, 0.8, 1, 1),
})
local container = FlexLove.new({
parent = root,
positioning = "flex",
flexDirection = "vertical",
alignItems = "center",
height = "100%",
width = "100%",
gap = 30,
})
-- Subtitle
FlexLove.new({
parent = container,
@@ -152,32 +172,43 @@ local function buildMenu()
-- Profile list
local profileList = FlexLove.new({
parent = container,
width = 600,
width = "80%",
height = "80%",
positioning = "flex",
flexDirection = "vertical",
gap = 10,
padding = { vertical = 20, horizontal = 20 },
overflowY = "scroll",
--mode = "retained",
})
for i, profile in ipairs(state.profiles) do
local isSelected = i == state.selectedIndex
local isHovered = i == state.hoveredIndex
local button = FlexLove.new({
parent = profileList,
width = "100%",
width = "50%",
height = 50,
backgroundColor = isSelected and FlexLove.Color.new(0.2, 0.4, 0.8, 1) or FlexLove.Color.new(0.15, 0.15, 0.25, 1),
backgroundColor = isSelected and FlexLove.Color.new(0.2, 0.4, 0.8, 1)
or isHovered and FlexLove.Color.new(0.2, 0.2, 0.35, 1)
or FlexLove.Color.new(0.15, 0.15, 0.25, 1),
borderRadius = 8,
positioning = "flex",
justifyContent = "flex-start",
justifyContent = "center",
alignItems = "center",
alignSelf = "center",
--mode = "retained",
padding = { horizontal = 15, vertical = 15 },
onEvent = function(element, event)
onEvent = function(_, event)
if event.type == "release" then
state.selectedIndex = i
loadProfile(profile)
elseif event.type == "hover" and not isSelected then
element.backgroundColor = FlexLove.Color.new(0.2, 0.2, 0.35, 1)
state.hoveredIndex = i
--element.backgroundColor = FlexLove.Color.new(0.2, 0.2, 0.35, 1)
elseif event.type == "unhover" and not isSelected then
element.backgroundColor = FlexLove.Color.new(0.15, 0.15, 0.25, 1)
state.hoveredIndex = 0
--element.backgroundColor = FlexLove.Color.new(0.15, 0.15, 0.25, 1)
end
end,
})
@@ -223,8 +254,6 @@ end
function lv.load(args)
FlexLove.init({
width = lv.graphics.getWidth(),
height = lv.graphics.getHeight(),
immediateMode = true,
})
@@ -266,6 +295,10 @@ function lv.update(dt)
end
end
function lv.wheelmoved(x, y)
FlexLove.wheelmoved(x, y)
end
function lv.draw()
if state.mode == "menu" then
buildMenu()