docs improvement
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,7 +6,7 @@ themes/metal/
|
|||||||
themes/space/
|
themes/space/
|
||||||
.DS_STORE
|
.DS_STORE
|
||||||
tasks
|
tasks
|
||||||
testoutput
|
testoutput*
|
||||||
luacov.*
|
luacov.*
|
||||||
docs/doc.json
|
docs/doc.json
|
||||||
docs/doc.md
|
docs/doc.md
|
||||||
|
|||||||
66
FlexLove.lua
66
FlexLove.lua
@@ -147,7 +147,8 @@ flexlove._gcState = {
|
|||||||
-- Deferred callback queue for operations that cannot run while Canvas is active
|
-- Deferred callback queue for operations that cannot run while Canvas is active
|
||||||
flexlove._deferredCallbacks = {}
|
flexlove._deferredCallbacks = {}
|
||||||
|
|
||||||
--- Initialize FlexLove with configuration options, set refence scale for autoscaling on window resize, immediate mode, and error logging / error file path
|
--- Set up FlexLove for your application's specific needs - configure responsive scaling, theming, rendering mode, and debugging tools
|
||||||
|
--- Use this to establish a consistent UI foundation that adapts to different screen sizes and provides performance insights
|
||||||
---@param config {baseScale?: {width?:number, height?:number}, theme?: string|ThemeDefinition, immediateMode?: boolean, stateRetentionFrames?: number, maxStateEntries?: number, autoFrameManagement?: boolean, errorLogFile?: string, enableErrorLogging?: boolean, performanceMonitoring?: boolean, performanceWarnings?: boolean, performanceHudKey?: string, performanceHudPosition?: {x: number, y: number} }
|
---@param config {baseScale?: {width?:number, height?:number}, theme?: string|ThemeDefinition, immediateMode?: boolean, stateRetentionFrames?: number, maxStateEntries?: number, autoFrameManagement?: boolean, errorLogFile?: string, enableErrorLogging?: boolean, performanceMonitoring?: boolean, performanceWarnings?: boolean, performanceHudKey?: string, performanceHudPosition?: {x: number, y: number} }
|
||||||
function flexlove.init(config)
|
function flexlove.init(config)
|
||||||
config = config or {}
|
config = config or {}
|
||||||
@@ -254,8 +255,8 @@ function flexlove.init(config)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Queue a callback to be executed after the current frame's canvas operations complete
|
--- Safely schedule operations that modify LÖVE's rendering state (like window mode changes) to execute after all canvas operations complete
|
||||||
--- This is necessary for operations that cannot run while a Canvas is active (e.g., love.window.setMode)
|
--- Prevents crashes from attempting canvas-incompatible operations during rendering
|
||||||
---@param callback function The callback to execute
|
---@param callback function The callback to execute
|
||||||
function flexlove.deferCallback(callback)
|
function flexlove.deferCallback(callback)
|
||||||
if type(callback) ~= "function" then
|
if type(callback) ~= "function" then
|
||||||
@@ -265,9 +266,8 @@ function flexlove.deferCallback(callback)
|
|||||||
table.insert(flexlove._deferredCallbacks, callback)
|
table.insert(flexlove._deferredCallbacks, callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Execute all deferred callbacks and clear the queue
|
--- Execute deferred operations at the safest point in the render cycle - after all canvas operations are complete
|
||||||
--- IMPORTANT: This MUST be called at the very end of love.draw() after ALL canvases
|
--- Call this at the end of love.draw() to enable window resizing and other state-modifying operations without crashes
|
||||||
--- have been released, including any canvases created by the application (not just FlexLove's canvases)
|
|
||||||
--- @usage
|
--- @usage
|
||||||
--- function love.draw()
|
--- function love.draw()
|
||||||
--- love.graphics.setCanvas(myCanvas)
|
--- love.graphics.setCanvas(myCanvas)
|
||||||
@@ -293,6 +293,8 @@ function flexlove.executeDeferredCallbacks()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Recalculate all UI layouts when the window size changes - ensures your interface adapts seamlessly to new dimensions
|
||||||
|
--- Hook this to love.resize() to maintain proper scaling and positioning across window size changes
|
||||||
function flexlove.resize()
|
function flexlove.resize()
|
||||||
local newWidth, newHeight = love.window.getMode()
|
local newWidth, newHeight = love.window.getMode()
|
||||||
|
|
||||||
@@ -320,7 +322,8 @@ function flexlove.resize()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Can also be set in init()
|
--- Switch between immediate mode (React-like, recreates UI each frame) and retained mode (persistent elements) to match your architectural needs
|
||||||
|
--- Use immediate for simpler state management and declarative UIs, retained for performance-critical applications with complex state
|
||||||
---@param mode "immediate"|"retained"
|
---@param mode "immediate"|"retained"
|
||||||
function flexlove.setMode(mode)
|
function flexlove.setMode(mode)
|
||||||
if mode == "immediate" then
|
if mode == "immediate" then
|
||||||
@@ -340,13 +343,15 @@ function flexlove.setMode(mode)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Check which rendering mode is active to conditionally handle state management logic
|
||||||
|
--- Useful for libraries and reusable components that need to adapt to different rendering strategies
|
||||||
---@return "immediate"|"retained"
|
---@return "immediate"|"retained"
|
||||||
function flexlove.getMode()
|
function flexlove.getMode()
|
||||||
return flexlove._immediateMode and "immediate" or "retained"
|
return flexlove._immediateMode and "immediate" or "retained"
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Begin a new immediate mode frame
|
--- Manually start a new frame in immediate mode for precise control over the UI lifecycle
|
||||||
--- You do NOT need to call this directly, it will autodetect, but you can if you need more granular control - must then paired with endFrame()
|
--- Only needed when you want explicit frame boundaries; otherwise FlexLove auto-manages frames
|
||||||
function flexlove.beginFrame()
|
function flexlove.beginFrame()
|
||||||
if not flexlove._immediateMode then
|
if not flexlove._immediateMode then
|
||||||
return
|
return
|
||||||
@@ -364,9 +369,8 @@ function flexlove.beginFrame()
|
|||||||
Context.clearFrameElements()
|
Context.clearFrameElements()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- End the current immediate mode frame
|
--- Finalize the frame in immediate mode, triggering layout calculations and state persistence
|
||||||
--- You do NOT need to call this directly unless you call beginFrame() manually - it will autodetect, but you can if you need more granular control
|
--- Only needed when manually controlling frames with beginFrame(); otherwise handled automatically
|
||||||
--- MUST BE PAIRED WITH beginFrame()
|
|
||||||
function flexlove.endFrame()
|
function flexlove.endFrame()
|
||||||
if not flexlove._immediateMode then
|
if not flexlove._immediateMode then
|
||||||
return
|
return
|
||||||
@@ -436,6 +440,8 @@ flexlove._gameCanvas = nil
|
|||||||
flexlove._backdropCanvas = nil
|
flexlove._backdropCanvas = nil
|
||||||
flexlove._canvasDimensions = { width = 0, height = 0 }
|
flexlove._canvasDimensions = { width = 0, height = 0 }
|
||||||
|
|
||||||
|
--- Render all UI elements with optional backdrop blur support for glassmorphic effects
|
||||||
|
--- Place your game scene in gameDrawFunc to enable backdrop blur on UI elements; use postDrawFunc for overlays
|
||||||
---@param gameDrawFunc function|nil pass component draws that should be affected by a backdrop blur
|
---@param gameDrawFunc function|nil pass component draws that should be affected by a backdrop blur
|
||||||
---@param postDrawFunc function|nil pass component draws that should NOT be affected by a backdrop blur
|
---@param postDrawFunc function|nil pass component draws that should NOT be affected by a backdrop blur
|
||||||
function flexlove.draw(gameDrawFunc, postDrawFunc)
|
function flexlove.draw(gameDrawFunc, postDrawFunc)
|
||||||
@@ -566,7 +572,8 @@ local function isAncestor(element, target)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Find the topmost element at given coordinates
|
--- Determine which UI element the user is interacting with at a specific screen position
|
||||||
|
--- Essential for custom input handling, tooltips, or debugging click targets in complex layouts
|
||||||
---@param x number
|
---@param x number
|
||||||
---@param y number
|
---@param y number
|
||||||
---@return Element?
|
---@return Element?
|
||||||
@@ -662,6 +669,8 @@ function flexlove.getElementAtPosition(x, y)
|
|||||||
return blockingElements[1]
|
return blockingElements[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Update all UI animations, interactions, and state changes each frame
|
||||||
|
--- Hook this to love.update() to enable hover effects, animations, text cursors, and scrolling
|
||||||
---@param dt number
|
---@param dt number
|
||||||
function flexlove.update(dt)
|
function flexlove.update(dt)
|
||||||
-- Update Performance module with actual delta time for accurate FPS
|
-- Update Performance module with actual delta time for accurate FPS
|
||||||
@@ -742,7 +751,8 @@ function flexlove._manageGC()
|
|||||||
-- "manual" strategy: no automatic GC, user must call flexlove.collectGarbage()
|
-- "manual" strategy: no automatic GC, user must call flexlove.collectGarbage()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Manual garbage collection control
|
--- Manually trigger garbage collection to prevent frame drops during critical gameplay moments
|
||||||
|
--- Use this to control when memory cleanup happens rather than letting it occur unpredictably
|
||||||
---@param mode? string "collect" for full GC, "step" for incremental (default: "collect")
|
---@param mode? string "collect" for full GC, "step" for incremental (default: "collect")
|
||||||
---@param stepSize? number Work units for step mode (default: 200)
|
---@param stepSize? number Work units for step mode (default: 200)
|
||||||
function flexlove.collectGarbage(mode, stepSize)
|
function flexlove.collectGarbage(mode, stepSize)
|
||||||
@@ -760,7 +770,8 @@ function flexlove.collectGarbage(mode, stepSize)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set GC strategy
|
--- Choose how FlexLove manages memory cleanup to balance performance and memory usage for your app's needs
|
||||||
|
--- Use "manual" for tight control in performance-critical sections, "auto" for hands-off operation
|
||||||
---@param strategy string "auto", "periodic", "manual", or "disabled"
|
---@param strategy string "auto", "periodic", "manual", or "disabled"
|
||||||
function flexlove.setGCStrategy(strategy)
|
function flexlove.setGCStrategy(strategy)
|
||||||
if strategy == "auto" or strategy == "periodic" or strategy == "manual" or strategy == "disabled" then
|
if strategy == "auto" or strategy == "periodic" or strategy == "manual" or strategy == "disabled" then
|
||||||
@@ -770,7 +781,8 @@ function flexlove.setGCStrategy(strategy)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get GC statistics
|
--- Monitor memory management behavior to diagnose performance issues and tune GC settings
|
||||||
|
--- Use this to identify memory leaks or optimize garbage collection timing
|
||||||
---@return table stats {gcCount, framesSinceLastGC, currentMemoryMB, strategy}
|
---@return table stats {gcCount, framesSinceLastGC, currentMemoryMB, strategy}
|
||||||
function flexlove.getGCStats()
|
function flexlove.getGCStats()
|
||||||
return {
|
return {
|
||||||
@@ -782,6 +794,8 @@ function flexlove.getGCStats()
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Forward text input to focused editable elements like text fields and text areas
|
||||||
|
--- Hook this to love.textinput() to enable text entry in your UI
|
||||||
---@param text string
|
---@param text string
|
||||||
function flexlove.textinput(text)
|
function flexlove.textinput(text)
|
||||||
if flexlove._focusedElement then
|
if flexlove._focusedElement then
|
||||||
@@ -789,6 +803,8 @@ function flexlove.textinput(text)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Handle keyboard input for text editing, navigation, and performance overlay toggling
|
||||||
|
--- Hook this to love.keypressed() to enable text selection, cursor movement, and the performance HUD
|
||||||
---@param key string
|
---@param key string
|
||||||
---@param scancode string
|
---@param scancode string
|
||||||
---@param isrepeat boolean
|
---@param isrepeat boolean
|
||||||
@@ -800,6 +816,8 @@ function flexlove.keypressed(key, scancode, isrepeat)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Enable mouse wheel scrolling in scrollable containers and lists
|
||||||
|
--- Hook this to love.wheelmoved() to allow users to scroll through content naturally
|
||||||
---@param dx number
|
---@param dx number
|
||||||
---@param dy number
|
---@param dy number
|
||||||
function flexlove.wheelmoved(dx, dy)
|
function flexlove.wheelmoved(dx, dy)
|
||||||
@@ -926,7 +944,8 @@ function flexlove.wheelmoved(dx, dy)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- destroys all top-level elements and resets the framework state
|
--- Clean up all UI elements and reset FlexLove to initial state when changing scenes or shutting down
|
||||||
|
--- Use this to prevent memory leaks when transitioning between game states or menus
|
||||||
function flexlove.destroy()
|
function flexlove.destroy()
|
||||||
for _, win in ipairs(flexlove.topElements) do
|
for _, win in ipairs(flexlove.topElements) do
|
||||||
win:destroy()
|
win:destroy()
|
||||||
@@ -951,6 +970,8 @@ function flexlove.destroy()
|
|||||||
StateManager:reset()
|
StateManager:reset()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Create a new UI element with flexbox layout, styling, and interaction capabilities
|
||||||
|
--- This is your primary API for building interfaces - buttons, panels, text, images, and containers
|
||||||
---@param props ElementProps
|
---@param props ElementProps
|
||||||
---@return Element
|
---@return Element
|
||||||
function flexlove.new(props)
|
function flexlove.new(props)
|
||||||
@@ -1058,6 +1079,8 @@ function flexlove.new(props)
|
|||||||
return element
|
return element
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Check how many UI element states are being tracked in immediate mode to detect memory leaks
|
||||||
|
--- Use this during development to ensure states are properly cleaned up
|
||||||
---@return number
|
---@return number
|
||||||
function flexlove.getStateCount()
|
function flexlove.getStateCount()
|
||||||
if not flexlove._immediateMode then
|
if not flexlove._immediateMode then
|
||||||
@@ -1066,7 +1089,8 @@ function flexlove.getStateCount()
|
|||||||
return StateManager.getStateCount()
|
return StateManager.getStateCount()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Clear state for a specific element ID
|
--- Remove stored state for a specific element when you know it won't be rendered again
|
||||||
|
--- Use this to immediately free memory for elements you've removed from your UI
|
||||||
---@param id string
|
---@param id string
|
||||||
function flexlove.clearState(id)
|
function flexlove.clearState(id)
|
||||||
if not flexlove._immediateMode then
|
if not flexlove._immediateMode then
|
||||||
@@ -1075,7 +1099,8 @@ function flexlove.clearState(id)
|
|||||||
StateManager.clearState(id)
|
StateManager.clearState(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Clear all immediate mode states
|
--- Wipe all element state when transitioning between completely different UI screens
|
||||||
|
--- Use this for scene transitions to start with a clean slate and prevent state pollution
|
||||||
function flexlove.clearAllStates()
|
function flexlove.clearAllStates()
|
||||||
if not flexlove._immediateMode then
|
if not flexlove._immediateMode then
|
||||||
return
|
return
|
||||||
@@ -1083,7 +1108,8 @@ function flexlove.clearAllStates()
|
|||||||
StateManager.clearAllStates()
|
StateManager.clearAllStates()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get state (immediate mode) statistics (for debugging)
|
--- Inspect state management metrics to diagnose performance issues and optimize immediate mode usage
|
||||||
|
--- Use this to understand state lifecycle and identify unexpected state accumulation
|
||||||
---@return { stateCount: number, frameNumber: number, oldestState: number|nil, newestState: number|nil }
|
---@return { stateCount: number, frameNumber: number, oldestState: number|nil, newestState: number|nil }
|
||||||
function flexlove.getStateStats()
|
function flexlove.getStateStats()
|
||||||
if not flexlove._immediateMode then
|
if not flexlove._immediateMode then
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ local Easing = {
|
|||||||
local Animation = {}
|
local Animation = {}
|
||||||
Animation.__index = Animation
|
Animation.__index = Animation
|
||||||
|
|
||||||
---Create a new animation instance
|
--- Build smooth, timed transitions between visual states to create polished, professional UIs
|
||||||
|
--- Use this to animate position, size, opacity, colors, and other properties with customizable easing
|
||||||
---@param props AnimationProps Animation properties
|
---@param props AnimationProps Animation properties
|
||||||
---@return Animation animation The new animation instance
|
---@return Animation animation The new animation instance
|
||||||
function Animation.new(props)
|
function Animation.new(props)
|
||||||
@@ -136,7 +137,8 @@ function Animation.new(props)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
---Update the animation with delta time
|
--- Advance the animation timeline and calculate interpolated values for the current frame
|
||||||
|
--- Call this each frame to progress the animation; returns true when complete for cleanup
|
||||||
---@param dt number Delta time in seconds
|
---@param dt number Delta time in seconds
|
||||||
---@param element table? Optional element reference for callbacks
|
---@param element table? Optional element reference for callbacks
|
||||||
---@return boolean completed True if animation is complete
|
---@return boolean completed True if animation is complete
|
||||||
@@ -302,7 +304,8 @@ local function lerpTable(startTable, finalTable, easedT)
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
---Interpolate animation values at current time
|
--- Calculate the current animated values between start and end states based on elapsed time
|
||||||
|
--- Use this to get the interpolated properties to apply to your element
|
||||||
---@return table result Interpolated values {width?, height?, opacity?, x?, y?, backgroundColor?, ...}
|
---@return table result Interpolated values {width?, height?, opacity?, x?, y?, backgroundColor?, ...}
|
||||||
function Animation:interpolate()
|
function Animation:interpolate()
|
||||||
-- Return cached result if not dirty (avoids recalculation)
|
-- Return cached result if not dirty (avoids recalculation)
|
||||||
@@ -391,7 +394,8 @@ function Animation:interpolate()
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
---Apply this animation to an element
|
--- Attach this animation to an element so it automatically updates and applies changes
|
||||||
|
--- Use this for hands-off animation that integrates with FlexLove's rendering system
|
||||||
---@param element Element The element to apply animation to
|
---@param element Element The element to apply animation to
|
||||||
function Animation:apply(element)
|
function Animation:apply(element)
|
||||||
if not ErrorHandler then
|
if not ErrorHandler then
|
||||||
@@ -417,7 +421,8 @@ function Animation:setTransformModule(TransformModule)
|
|||||||
self._Transform = TransformModule
|
self._Transform = TransformModule
|
||||||
end
|
end
|
||||||
|
|
||||||
---Pause the animation
|
--- Temporarily halt the animation without losing progress
|
||||||
|
--- Use this to freeze animations during pause menus or cutscenes
|
||||||
function Animation:pause()
|
function Animation:pause()
|
||||||
if self._state == "playing" or self._state == "pending" then
|
if self._state == "playing" or self._state == "pending" then
|
||||||
self._paused = true
|
self._paused = true
|
||||||
@@ -425,7 +430,8 @@ function Animation:pause()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Resume the animation
|
--- Continue a paused animation from where it left off
|
||||||
|
--- Use this to unpause animations when returning from pause menus
|
||||||
function Animation:resume()
|
function Animation:resume()
|
||||||
if self._state == "paused" then
|
if self._state == "paused" then
|
||||||
self._paused = false
|
self._paused = false
|
||||||
@@ -433,24 +439,28 @@ function Animation:resume()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Check if animation is paused
|
--- Query pause state to conditionally handle animation logic
|
||||||
|
--- Use this to sync UI behavior with animation state
|
||||||
---@return boolean paused
|
---@return boolean paused
|
||||||
function Animation:isPaused()
|
function Animation:isPaused()
|
||||||
return self._paused
|
return self._paused
|
||||||
end
|
end
|
||||||
|
|
||||||
---Reverse the animation direction
|
--- Flip the animation to play backwards, creating smooth transitions in both directions
|
||||||
|
--- Use this for hover effects that reverse on mouse-out or toggleable UI elements
|
||||||
function Animation:reverse()
|
function Animation:reverse()
|
||||||
self._reversed = not self._reversed
|
self._reversed = not self._reversed
|
||||||
end
|
end
|
||||||
|
|
||||||
---Check if animation is reversed
|
--- Determine current playback direction for conditional animation logic
|
||||||
|
--- Use this to track which direction the animation is playing
|
||||||
---@return boolean reversed
|
---@return boolean reversed
|
||||||
function Animation:isReversed()
|
function Animation:isReversed()
|
||||||
return self._reversed
|
return self._reversed
|
||||||
end
|
end
|
||||||
|
|
||||||
---Set animation playback speed
|
--- Control animation tempo for slow-motion or fast-forward effects
|
||||||
|
--- Use this for bullet-time, game speed multipliers, or debugging
|
||||||
---@param speed number Speed multiplier (1.0 = normal, 2.0 = double speed, 0.5 = half speed)
|
---@param speed number Speed multiplier (1.0 = normal, 2.0 = double speed, 0.5 = half speed)
|
||||||
function Animation:setSpeed(speed)
|
function Animation:setSpeed(speed)
|
||||||
if type(speed) == "number" and speed > 0 then
|
if type(speed) == "number" and speed > 0 then
|
||||||
@@ -458,13 +468,15 @@ function Animation:setSpeed(speed)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get animation playback speed
|
--- Check current playback speed for debugging or UI display
|
||||||
|
--- Use this to show animation speed in dev tools
|
||||||
---@return number speed Current speed multiplier
|
---@return number speed Current speed multiplier
|
||||||
function Animation:getSpeed()
|
function Animation:getSpeed()
|
||||||
return self._speed
|
return self._speed
|
||||||
end
|
end
|
||||||
|
|
||||||
---Seek to a specific time in the animation
|
--- Jump to any point in the animation timeline for previewing or state restoration
|
||||||
|
--- Use this to skip ahead, rewind, or restore saved animation states
|
||||||
---@param time number Time in seconds (clamped to 0-duration)
|
---@param time number Time in seconds (clamped to 0-duration)
|
||||||
function Animation:seek(time)
|
function Animation:seek(time)
|
||||||
if type(time) == "number" then
|
if type(time) == "number" then
|
||||||
@@ -473,13 +485,15 @@ function Animation:seek(time)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get current animation state
|
--- Query animation lifecycle state for conditional logic and debugging
|
||||||
|
--- Use this to determine if cleanup is needed or to prevent duplicate animations
|
||||||
---@return string state Current state: "pending", "playing", "paused", "completed", "cancelled"
|
---@return string state Current state: "pending", "playing", "paused", "completed", "cancelled"
|
||||||
function Animation:getState()
|
function Animation:getState()
|
||||||
return self._state
|
return self._state
|
||||||
end
|
end
|
||||||
|
|
||||||
---Cancel the animation
|
--- Stop the animation immediately without completing, triggering the onCancel callback
|
||||||
|
--- Use this to abort animations when UI elements are removed or user cancels an action
|
||||||
---@param element table? Optional element reference for callback
|
---@param element table? Optional element reference for callback
|
||||||
function Animation:cancel(element)
|
function Animation:cancel(element)
|
||||||
if self._state ~= "cancelled" and self._state ~= "completed" then
|
if self._state ~= "cancelled" and self._state ~= "completed" then
|
||||||
@@ -493,7 +507,8 @@ function Animation:cancel(element)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Reset the animation to its initial state
|
--- Return the animation to the beginning for replay
|
||||||
|
--- Use this to reuse animation instances without recreating them
|
||||||
function Animation:reset()
|
function Animation:reset()
|
||||||
self.elapsed = 0
|
self.elapsed = 0
|
||||||
self._hasStarted = false
|
self._hasStarted = false
|
||||||
@@ -502,13 +517,15 @@ function Animation:reset()
|
|||||||
self._resultDirty = true
|
self._resultDirty = true
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get the current progress of the animation
|
--- Get normalized animation progress for progress bars or synchronized effects
|
||||||
|
--- Use this to drive secondary animations or display completion percentage
|
||||||
---@return number progress Progress from 0 to 1
|
---@return number progress Progress from 0 to 1
|
||||||
function Animation:getProgress()
|
function Animation:getProgress()
|
||||||
return math.min(self.elapsed / self.duration, 1)
|
return math.min(self.elapsed / self.duration, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Chain another animation after this one completes
|
--- Create sequential animation flows that play one after another
|
||||||
|
--- Use this to build complex multi-step animations like slide-in-then-fade
|
||||||
---@param nextAnimation Animation|function Animation instance or factory function that returns an animation
|
---@param nextAnimation Animation|function Animation instance or factory function that returns an animation
|
||||||
---@return Animation nextAnimation The chained animation (for further chaining)
|
---@return Animation nextAnimation The chained animation (for further chaining)
|
||||||
function Animation:chain(nextAnimation)
|
function Animation:chain(nextAnimation)
|
||||||
@@ -528,7 +545,8 @@ function Animation:chain(nextAnimation)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Add delay before animation starts
|
--- Introduce a wait period before animation begins for staggered effects
|
||||||
|
--- Use this to create cascading animations or timed sequences
|
||||||
---@param seconds number Delay duration in seconds
|
---@param seconds number Delay duration in seconds
|
||||||
---@return Animation self For chaining
|
---@return Animation self For chaining
|
||||||
function Animation:delay(seconds)
|
function Animation:delay(seconds)
|
||||||
@@ -545,7 +563,8 @@ function Animation:delay(seconds)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
---Repeat animation multiple times
|
--- Loop the animation for pulsing effects, loading indicators, or continuous motion
|
||||||
|
--- Use this for idle animations and attention-grabbing elements
|
||||||
---@param count number Number of times to repeat (0 = infinite loop)
|
---@param count number Number of times to repeat (0 = infinite loop)
|
||||||
---@return Animation self For chaining
|
---@return Animation self For chaining
|
||||||
function Animation:repeatCount(count)
|
function Animation:repeatCount(count)
|
||||||
@@ -562,7 +581,8 @@ function Animation:repeatCount(count)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
---Enable yoyo mode (animation reverses direction on each repeat)
|
--- Make repeating animations play forwards then backwards for smooth oscillation
|
||||||
|
--- Use this for breathing effects, pulsing highlights, or pendulum motions
|
||||||
---@param enabled boolean? Enable yoyo mode (default: true)
|
---@param enabled boolean? Enable yoyo mode (default: true)
|
||||||
---@return Animation self For chaining
|
---@return Animation self For chaining
|
||||||
function Animation:yoyo(enabled)
|
function Animation:yoyo(enabled)
|
||||||
@@ -573,7 +593,8 @@ function Animation:yoyo(enabled)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Create a simple fade animation
|
--- Quickly create fade in/out effects without manually specifying start/end states
|
||||||
|
--- Use this convenience method for common opacity transitions in tooltips, notifications, and overlays
|
||||||
---@param duration number Duration in seconds
|
---@param duration number Duration in seconds
|
||||||
---@param fromOpacity number Starting opacity (0-1)
|
---@param fromOpacity number Starting opacity (0-1)
|
||||||
---@param toOpacity number Ending opacity (0-1)
|
---@param toOpacity number Ending opacity (0-1)
|
||||||
@@ -601,7 +622,8 @@ function Animation.fade(duration, fromOpacity, toOpacity, easing)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Create a simple scale animation
|
--- Quickly create grow/shrink effects without manually specifying dimensions
|
||||||
|
--- Use this convenience method for bounce effects, pop-ups, and attention animations
|
||||||
---@param duration number Duration in seconds
|
---@param duration number Duration in seconds
|
||||||
---@param fromScale {width:number,height:number} Starting scale
|
---@param fromScale {width:number,height:number} Starting scale
|
||||||
---@param toScale {width:number,height:number} Ending scale
|
---@param toScale {width:number,height:number} Ending scale
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ local ErrorHandler = nil
|
|||||||
---@field onComplete function? Called when all animations complete: (group)
|
---@field onComplete function? Called when all animations complete: (group)
|
||||||
---@field onStart function? Called when group starts: (group)
|
---@field onStart function? Called when group starts: (group)
|
||||||
|
|
||||||
--- Create a new animation group
|
--- Coordinate multiple animations to play together, in sequence, or staggered for complex choreographed effects
|
||||||
|
--- Use this to synchronize related UI changes like simultaneous fades or sequential reveals
|
||||||
---@param props AnimationGroupProps
|
---@param props AnimationGroupProps
|
||||||
---@return AnimationGroup group
|
---@return AnimationGroup group
|
||||||
function AnimationGroup.new(props)
|
function AnimationGroup.new(props)
|
||||||
@@ -142,7 +143,8 @@ function AnimationGroup:_updateStagger(dt, element)
|
|||||||
return allFinished
|
return allFinished
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Update the animation group
|
--- Advance all animations in the group according to their coordination mode
|
||||||
|
--- Call this each frame to progress parallel, sequential, or staggered animations
|
||||||
---@param dt number Delta time
|
---@param dt number Delta time
|
||||||
---@param element table? Optional element reference for callbacks
|
---@param element table? Optional element reference for callbacks
|
||||||
---@return boolean finished True if group is complete
|
---@return boolean finished True if group is complete
|
||||||
@@ -191,7 +193,8 @@ function AnimationGroup:update(dt, element)
|
|||||||
return finished
|
return finished
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Pause all animations in the group
|
--- Freeze the entire animation sequence in unison
|
||||||
|
--- Use this to pause complex multi-part animations during game pauses
|
||||||
function AnimationGroup:pause()
|
function AnimationGroup:pause()
|
||||||
self._paused = true
|
self._paused = true
|
||||||
for _, anim in ipairs(self.animations) do
|
for _, anim in ipairs(self.animations) do
|
||||||
@@ -201,7 +204,8 @@ function AnimationGroup:pause()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Resume all animations in the group
|
--- Continue all paused animations simultaneously from their paused states
|
||||||
|
--- Use this to unpause coordinated animation sequences
|
||||||
function AnimationGroup:resume()
|
function AnimationGroup:resume()
|
||||||
self._paused = false
|
self._paused = false
|
||||||
for _, anim in ipairs(self.animations) do
|
for _, anim in ipairs(self.animations) do
|
||||||
@@ -211,13 +215,15 @@ function AnimationGroup:resume()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if group is paused
|
--- Determine if the entire group is currently paused
|
||||||
|
--- Use this to sync other game logic with animation group state
|
||||||
---@return boolean paused
|
---@return boolean paused
|
||||||
function AnimationGroup:isPaused()
|
function AnimationGroup:isPaused()
|
||||||
return self._paused
|
return self._paused
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Reverse all animations in the group
|
--- Flip all animations to play backwards together
|
||||||
|
--- Use this to reverse complex transitions like panel opens/closes
|
||||||
function AnimationGroup:reverse()
|
function AnimationGroup:reverse()
|
||||||
for _, anim in ipairs(self.animations) do
|
for _, anim in ipairs(self.animations) do
|
||||||
if type(anim.reverse) == "function" then
|
if type(anim.reverse) == "function" then
|
||||||
@@ -226,7 +232,8 @@ function AnimationGroup:reverse()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set speed for all animations in the group
|
--- Control the tempo of all animations simultaneously
|
||||||
|
--- Use this for slow-motion effects or debugging without adjusting individual animations
|
||||||
---@param speed number Speed multiplier
|
---@param speed number Speed multiplier
|
||||||
function AnimationGroup:setSpeed(speed)
|
function AnimationGroup:setSpeed(speed)
|
||||||
for _, anim in ipairs(self.animations) do
|
for _, anim in ipairs(self.animations) do
|
||||||
@@ -236,7 +243,8 @@ function AnimationGroup:setSpeed(speed)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Cancel all animations in the group
|
--- Abort all animations in the group immediately without completion
|
||||||
|
--- Use this when UI is dismissed mid-animation or transitions are interrupted
|
||||||
---@param element table? Optional element reference for callbacks
|
---@param element table? Optional element reference for callbacks
|
||||||
function AnimationGroup:cancel(element)
|
function AnimationGroup:cancel(element)
|
||||||
if self._state ~= "cancelled" and self._state ~= "completed" then
|
if self._state ~= "cancelled" and self._state ~= "completed" then
|
||||||
@@ -249,7 +257,8 @@ function AnimationGroup:cancel(element)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Reset the animation group to initial state
|
--- Restart the entire group from the beginning for reuse
|
||||||
|
--- Use this to replay animation sequences without recreating objects
|
||||||
function AnimationGroup:reset()
|
function AnimationGroup:reset()
|
||||||
self._currentIndex = 1
|
self._currentIndex = 1
|
||||||
self._staggerElapsed = 0
|
self._staggerElapsed = 0
|
||||||
@@ -265,13 +274,15 @@ function AnimationGroup:reset()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the current state of the group
|
--- Check the overall lifecycle state of the animation group
|
||||||
|
--- Use this to conditionally trigger follow-up actions or cleanup
|
||||||
---@return string state "ready", "playing", "completed", "cancelled"
|
---@return string state "ready", "playing", "completed", "cancelled"
|
||||||
function AnimationGroup:getState()
|
function AnimationGroup:getState()
|
||||||
return self._state
|
return self._state
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the overall progress of the group (0-1)
|
--- Calculate completion percentage across all animations in the group
|
||||||
|
--- Use this for progress bars or to synchronize other effects with the group
|
||||||
---@return number progress
|
---@return number progress
|
||||||
function AnimationGroup:getProgress()
|
function AnimationGroup:getProgress()
|
||||||
if #self.animations == 0 then
|
if #self.animations == 0 then
|
||||||
@@ -305,7 +316,8 @@ function AnimationGroup:getProgress()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Apply this animation group to an element
|
--- Attach this group to an element for automatic updates and integration
|
||||||
|
--- Use this for hands-off animation management within FlexLove's system
|
||||||
---@param element Element The element to apply animations to
|
---@param element Element The element to apply animations to
|
||||||
function AnimationGroup:apply(element)
|
function AnimationGroup:apply(element)
|
||||||
if not element or type(element) ~= "table" then
|
if not element or type(element) ~= "table" then
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ local NAMED_COLORS = {
|
|||||||
local Color = {}
|
local Color = {}
|
||||||
Color.__index = Color
|
Color.__index = Color
|
||||||
|
|
||||||
--- Create a new color instance
|
--- Build type-safe color objects with automatic validation and clamping
|
||||||
|
--- Use this to avoid invalid color values and ensure consistent LÖVE-compatible colors (0-1 range)
|
||||||
---@param r number? Red component (0-1), defaults to 0
|
---@param r number? Red component (0-1), defaults to 0
|
||||||
---@param g number? Green component (0-1), defaults to 0
|
---@param g number? Green component (0-1), defaults to 0
|
||||||
---@param b number? Blue component (0-1), defaults to 0
|
---@param b number? Blue component (0-1), defaults to 0
|
||||||
@@ -75,7 +76,8 @@ function Color.new(r, g, b, a)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
---Convert color to RGBA components
|
--- Extract individual color channels for use with love.graphics.setColor()
|
||||||
|
--- Use this to pass colors to LÖVE's rendering functions
|
||||||
---@return number r Red component (0-1)
|
---@return number r Red component (0-1)
|
||||||
---@return number g Green component (0-1)
|
---@return number g Green component (0-1)
|
||||||
---@return number b Blue component (0-1)
|
---@return number b Blue component (0-1)
|
||||||
@@ -84,8 +86,8 @@ function Color:toRGBA()
|
|||||||
return self.r, self.g, self.b, self.a
|
return self.r, self.g, self.b, self.a
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Convert hex string to color
|
--- Parse CSS-style hex colors into Color objects for designer-friendly workflows
|
||||||
--- Supports both 6-digit (#RRGGBB) and 8-digit (#RRGGBBAA) hex formats
|
--- Use this to work with colors from design tools that export hex values
|
||||||
---@param hexWithTag string Hex color string (e.g. "#RRGGBB" or "#RRGGBBAA")
|
---@param hexWithTag string Hex color string (e.g. "#RRGGBB" or "#RRGGBBAA")
|
||||||
---@return Color color The parsed color (returns white on error with warning)
|
---@return Color color The parsed color (returns white on error with warning)
|
||||||
function Color.fromHex(hexWithTag)
|
function Color.fromHex(hexWithTag)
|
||||||
@@ -146,7 +148,8 @@ function Color.fromHex(hexWithTag)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Validate a single color channel value
|
--- Verify and sanitize individual color components to prevent rendering errors
|
||||||
|
--- Use this to safely process user input or external color data
|
||||||
---@param value any Value to validate
|
---@param value any Value to validate
|
||||||
---@param max number? Maximum value (255 for 0-255 range, 1 for 0-1 range), defaults to 1
|
---@param max number? Maximum value (255 for 0-255 range, 1 for 0-1 range), defaults to 1
|
||||||
---@return boolean valid True if valid
|
---@return boolean valid True if valid
|
||||||
@@ -307,7 +310,8 @@ function Color.isValidColorFormat(value)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Validate a color value
|
--- Check if a color value is usable before processing to provide clear error messages
|
||||||
|
--- Use this for config validation and debugging malformed color data
|
||||||
---@param value any Color value to validate
|
---@param value any Color value to validate
|
||||||
---@param options table? Validation options {allowNamed: boolean, requireAlpha: boolean}
|
---@param options table? Validation options {allowNamed: boolean, requireAlpha: boolean}
|
||||||
---@return boolean valid True if valid
|
---@return boolean valid True if valid
|
||||||
@@ -342,7 +346,8 @@ function Color.validateColor(value, options)
|
|||||||
return true, nil
|
return true, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sanitize a color value (always returns a valid Color)
|
--- Convert any color format to a valid Color object with graceful fallbacks
|
||||||
|
--- Use this to robustly handle colors from any source without crashes
|
||||||
---@param value any Color value to sanitize (hex, named, table, or Color instance)
|
---@param value any Color value to sanitize (hex, named, table, or Color instance)
|
||||||
---@param default Color? Default color if invalid (defaults to black)
|
---@param default Color? Default color if invalid (defaults to black)
|
||||||
---@return Color color Sanitized color instance (guaranteed non-nil)
|
---@return Color color Sanitized color instance (guaranteed non-nil)
|
||||||
@@ -418,14 +423,16 @@ function Color.sanitizeColor(value, default)
|
|||||||
return default
|
return default
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Parse a color from various formats (always returns a valid Color)
|
--- Universally convert any color format (hex, named, table) into a Color object
|
||||||
|
--- Use this as your main color input handler to accept flexible color specifications
|
||||||
---@param value any Color value (hex string, named color, table, or Color instance)
|
---@param value any Color value (hex string, named color, table, or Color instance)
|
||||||
---@return Color color Parsed color instance (defaults to black on error)
|
---@return Color color Parsed color instance (defaults to black on error)
|
||||||
function Color.parse(value)
|
function Color.parse(value)
|
||||||
return Color.sanitizeColor(value, Color.new(0, 0, 0, 1))
|
return Color.sanitizeColor(value, Color.new(0, 0, 0, 1))
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Linear interpolation between two colors
|
--- Smoothly transition between two colors for animations and gradients
|
||||||
|
--- Use this to create color-based animations without manual channel calculations
|
||||||
---@param colorA Color Starting color
|
---@param colorA Color Starting color
|
||||||
---@param colorB Color Ending color
|
---@param colorB Color Ending color
|
||||||
---@param t number Interpolation factor (0-1)
|
---@param t number Interpolation factor (0-1)
|
||||||
|
|||||||
@@ -1415,13 +1415,15 @@ function Element.new(props, deps)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get element bounds (content box)
|
--- Retrieve the element's screen-space rectangle for collision detection and positioning calculations
|
||||||
|
--- Use this for custom layout logic, tooltips, or detecting overlaps between elements
|
||||||
---@return { x:number, y:number, width:number, height:number }
|
---@return { x:number, y:number, width:number, height:number }
|
||||||
function Element:getBounds()
|
function Element:getBounds()
|
||||||
return { x = self.x, y = self.y, width = self:getBorderBoxWidth(), height = self:getBorderBoxHeight() }
|
return { x = self.x, y = self.y, width = self:getBorderBoxWidth(), height = self:getBorderBoxHeight() }
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if point is inside element bounds
|
--- Test if a screen coordinate falls within the element's clickable area
|
||||||
|
--- Use this for custom hit detection or determining which element the mouse is over
|
||||||
--- @param x number
|
--- @param x number
|
||||||
--- @param y number
|
--- @param y number
|
||||||
--- @return boolean
|
--- @return boolean
|
||||||
@@ -1430,13 +1432,15 @@ function Element:contains(x, y)
|
|||||||
return bounds.x <= x and bounds.y <= y and bounds.x + bounds.width >= x and bounds.y + bounds.height >= y
|
return bounds.x <= x and bounds.y <= y and bounds.x + bounds.width >= x and bounds.y + bounds.height >= y
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get border-box width (including padding)
|
--- Get the element's total width including padding for layout calculations
|
||||||
|
--- Use this when you need the full visual width rather than just content width
|
||||||
---@return number
|
---@return number
|
||||||
function Element:getBorderBoxWidth()
|
function Element:getBorderBoxWidth()
|
||||||
return self._borderBoxWidth or (self.width + self.padding.left + self.padding.right)
|
return self._borderBoxWidth or (self.width + self.padding.left + self.padding.right)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get border-box height (including padding)
|
--- Get the element's total height including padding for layout calculations
|
||||||
|
--- Use this when you need the full visual height rather than just content height
|
||||||
---@return number
|
---@return number
|
||||||
function Element:getBorderBoxHeight()
|
function Element:getBorderBoxHeight()
|
||||||
return self._borderBoxHeight or (self.height + self.padding.top + self.padding.bottom)
|
return self._borderBoxHeight or (self.height + self.padding.top + self.padding.bottom)
|
||||||
@@ -1473,7 +1477,8 @@ function Element:_detectOverflow()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set scroll position with bounds clamping (delegates to ScrollManager)
|
--- Programmatically scroll content to any position for implementing "scroll to top" buttons or navigation anchors
|
||||||
|
--- Use this to create custom scrolling controls or jump to specific content sections
|
||||||
---@param x number? -- X scroll position (nil to keep current)
|
---@param x number? -- X scroll position (nil to keep current)
|
||||||
---@param y number? -- Y scroll position (nil to keep current)
|
---@param y number? -- Y scroll position (nil to keep current)
|
||||||
function Element:setScrollPosition(x, y)
|
function Element:setScrollPosition(x, y)
|
||||||
@@ -1561,7 +1566,8 @@ function Element:_handleWheelScroll(x, y)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get current scroll position (delegates to ScrollManager)
|
--- Query how far content is scrolled to implement scroll-aware UI like "back to top" buttons
|
||||||
|
--- Use this to create scroll position indicators or trigger lazy-loading
|
||||||
---@return number scrollX, number scrollY
|
---@return number scrollX, number scrollY
|
||||||
function Element:getScrollPosition()
|
function Element:getScrollPosition()
|
||||||
if self._scrollManager then
|
if self._scrollManager then
|
||||||
@@ -1570,7 +1576,8 @@ function Element:getScrollPosition()
|
|||||||
return 0, 0
|
return 0, 0
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get maximum scroll bounds (delegates to ScrollManager)
|
--- Find the scroll limits for validation and scroll position clamping
|
||||||
|
--- Use this to determine if content is fully scrolled or calculate remaining scroll distance
|
||||||
---@return number maxScrollX, number maxScrollY
|
---@return number maxScrollX, number maxScrollY
|
||||||
function Element:getMaxScroll()
|
function Element:getMaxScroll()
|
||||||
if self._scrollManager then
|
if self._scrollManager then
|
||||||
@@ -1579,7 +1586,8 @@ function Element:getMaxScroll()
|
|||||||
return 0, 0
|
return 0, 0
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get scroll percentage (0-1) (delegates to ScrollManager)
|
--- Get normalized scroll progress for scroll-based animations or position indicators
|
||||||
|
--- Use this to drive progress bars or parallax effects based on scroll position
|
||||||
---@return number percentX, number percentY
|
---@return number percentX, number percentY
|
||||||
function Element:getScrollPercentage()
|
function Element:getScrollPercentage()
|
||||||
if self._scrollManager then
|
if self._scrollManager then
|
||||||
@@ -1588,7 +1596,8 @@ function Element:getScrollPercentage()
|
|||||||
return 0, 0
|
return 0, 0
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if element has overflow (delegates to ScrollManager)
|
--- Determine if content extends beyond visible bounds to conditionally show scrollbars or overflow indicators
|
||||||
|
--- Use this to decide whether to display scroll hints or enable scroll interactions
|
||||||
---@return boolean hasOverflowX, boolean hasOverflowY
|
---@return boolean hasOverflowX, boolean hasOverflowY
|
||||||
function Element:hasOverflow()
|
function Element:hasOverflow()
|
||||||
if self._scrollManager then
|
if self._scrollManager then
|
||||||
@@ -1597,7 +1606,8 @@ function Element:hasOverflow()
|
|||||||
return false, false
|
return false, false
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get content dimensions (including overflow) (delegates to ScrollManager)
|
--- Measure total content size including overflowed areas for scroll calculations
|
||||||
|
--- Use this to understand how much content exists beyond the visible viewport
|
||||||
---@return number contentWidth, number contentHeight
|
---@return number contentWidth, number contentHeight
|
||||||
function Element:getContentSize()
|
function Element:getContentSize()
|
||||||
if self._scrollManager then
|
if self._scrollManager then
|
||||||
@@ -1606,7 +1616,8 @@ function Element:getContentSize()
|
|||||||
return 0, 0
|
return 0, 0
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Scroll by delta amount (delegates to ScrollManager)
|
--- Scroll content by a relative amount for smooth scrolling animations or gesture-based scrolling
|
||||||
|
--- Use this to implement custom scroll controls or smooth scroll transitions
|
||||||
---@param dx number? -- X delta (nil for no change)
|
---@param dx number? -- X delta (nil for no change)
|
||||||
---@param dy number? -- Y delta (nil for no change)
|
---@param dy number? -- Y delta (nil for no change)
|
||||||
function Element:scrollBy(dx, dy)
|
function Element:scrollBy(dx, dy)
|
||||||
@@ -1616,7 +1627,8 @@ function Element:scrollBy(dx, dy)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Scroll to top
|
--- Jump to the beginning of scrollable content instantly
|
||||||
|
--- Use this for "back to top" buttons or resetting scroll position
|
||||||
function Element:scrollToTop()
|
function Element:scrollToTop()
|
||||||
self:setScrollPosition(nil, 0)
|
self:setScrollPosition(nil, 0)
|
||||||
end
|
end
|
||||||
@@ -1634,7 +1646,8 @@ function Element:scrollToLeft()
|
|||||||
self:setScrollPosition(0, nil)
|
self:setScrollPosition(0, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Scroll to right
|
--- Jump to the rightmost position of horizontally scrollable content
|
||||||
|
--- Use this to navigate to the end of horizontal lists or carousels
|
||||||
function Element:scrollToRight()
|
function Element:scrollToRight()
|
||||||
if self._scrollManager then
|
if self._scrollManager then
|
||||||
local maxScrollX, _ = self._scrollManager:getMaxScroll()
|
local maxScrollX, _ = self._scrollManager:getMaxScroll()
|
||||||
@@ -1718,7 +1731,8 @@ function Element:getAvailableContentHeight()
|
|||||||
return math.max(0, availableHeight)
|
return math.max(0, availableHeight)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Add child to element
|
--- Dynamically insert a child element into the hierarchy for runtime UI construction
|
||||||
|
--- Use this to build interfaces procedurally or add elements based on application state
|
||||||
---@param child Element
|
---@param child Element
|
||||||
function Element:addChild(child)
|
function Element:addChild(child)
|
||||||
child.parent = self
|
child.parent = self
|
||||||
@@ -1790,7 +1804,8 @@ function Element:addChild(child)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Remove a specific child from this element
|
--- Remove a child element from the hierarchy to dynamically update UIs
|
||||||
|
--- Use this to delete elements when they're no longer needed or respond to user actions
|
||||||
---@param child Element
|
---@param child Element
|
||||||
function Element:removeChild(child)
|
function Element:removeChild(child)
|
||||||
for i, c in ipairs(self.children) do
|
for i, c in ipairs(self.children) do
|
||||||
@@ -1822,7 +1837,8 @@ function Element:removeChild(child)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Remove all children from this element
|
--- Delete all child elements at once for resetting containers or clearing lists
|
||||||
|
--- Use this to efficiently empty containers when rebuilding UI from scratch
|
||||||
function Element:clearChildren()
|
function Element:clearChildren()
|
||||||
-- Clear parent references for all children
|
-- Clear parent references for all children
|
||||||
for _, child in ipairs(self.children) do
|
for _, child in ipairs(self.children) do
|
||||||
@@ -2661,21 +2677,24 @@ end
|
|||||||
-- Input Handling - Focus Management
|
-- Input Handling - Focus Management
|
||||||
-- ====================
|
-- ====================
|
||||||
|
|
||||||
--- Focus this element for keyboard input
|
--- Give this element keyboard focus to enable text input or keyboard navigation
|
||||||
|
--- Use this to automatically focus text fields when showing forms or dialogs
|
||||||
function Element:focus()
|
function Element:focus()
|
||||||
if self._textEditor then
|
if self._textEditor then
|
||||||
self._textEditor:focus()
|
self._textEditor:focus()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Remove focus from this element
|
--- Remove keyboard focus to stop capturing input events
|
||||||
|
--- Use this when closing popups or switching focus to other elements
|
||||||
function Element:blur()
|
function Element:blur()
|
||||||
if self._textEditor then
|
if self._textEditor then
|
||||||
self._textEditor:blur()
|
self._textEditor:blur()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if this element is focused
|
--- Query focus state to conditionally render focus indicators or handle keyboard input
|
||||||
|
--- Use this to style focused elements or determine which element receives keyboard events
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function Element:isFocused()
|
function Element:isFocused()
|
||||||
if self._textEditor then
|
if self._textEditor then
|
||||||
@@ -2688,7 +2707,8 @@ end
|
|||||||
-- Input Handling - Text Buffer Management
|
-- Input Handling - Text Buffer Management
|
||||||
-- ====================
|
-- ====================
|
||||||
|
|
||||||
--- Get current text buffer
|
--- Retrieve the element's current text content for processing or validation
|
||||||
|
--- Use this to read user input from text fields or get display text
|
||||||
---@return string
|
---@return string
|
||||||
function Element:getText()
|
function Element:getText()
|
||||||
if self._textEditor then
|
if self._textEditor then
|
||||||
@@ -2697,7 +2717,8 @@ function Element:getText()
|
|||||||
return self.text or ""
|
return self.text or ""
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set text buffer and mark dirty
|
--- Update the element's text content programmatically for dynamic labels or resetting inputs
|
||||||
|
--- Use this to change text without user input, like clearing fields or updating status messages
|
||||||
---@param text string
|
---@param text string
|
||||||
function Element:setText(text)
|
function Element:setText(text)
|
||||||
if self._textEditor then
|
if self._textEditor then
|
||||||
@@ -2709,7 +2730,8 @@ function Element:setText(text)
|
|||||||
self.text = text
|
self.text = text
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Insert text at position
|
--- Programmatically insert text at any position for autocomplete or text manipulation
|
||||||
|
--- Use this to implement suggestions, templates, or text snippets
|
||||||
---@param text string -- Text to insert
|
---@param text string -- Text to insert
|
||||||
---@param position number? -- Position to insert at (default: cursor position)
|
---@param position number? -- Position to insert at (default: cursor position)
|
||||||
function Element:insertText(text, position)
|
function Element:insertText(text, position)
|
||||||
@@ -2899,7 +2921,8 @@ function Element:_trackActiveAnimations()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set image tint color
|
--- Change the tint color of an image element dynamically for hover effects or state indication
|
||||||
|
--- Use this to recolor images without replacing the asset, like highlighting selected items
|
||||||
---@param color Color Color to tint the image
|
---@param color Color Color to tint the image
|
||||||
function Element:setImageTint(color)
|
function Element:setImageTint(color)
|
||||||
self.imageTint = color
|
self.imageTint = color
|
||||||
@@ -2908,7 +2931,8 @@ function Element:setImageTint(color)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set image opacity
|
--- Adjust image transparency independently from the element for fade effects
|
||||||
|
--- Use this to create image-specific fade animations or disabled states
|
||||||
---@param opacity number Opacity 0-1
|
---@param opacity number Opacity 0-1
|
||||||
function Element:setImageOpacity(opacity)
|
function Element:setImageOpacity(opacity)
|
||||||
if opacity ~= nil then
|
if opacity ~= nil then
|
||||||
@@ -2938,7 +2962,8 @@ function Element:setImageRepeat(repeatMode)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Rotate element by angle
|
--- Apply rotation transform to create spinning animations or rotated layouts
|
||||||
|
--- Use this for loading spinners, compass needles, or angled UI elements
|
||||||
---@param angle number Angle in radians
|
---@param angle number Angle in radians
|
||||||
function Element:rotate(angle)
|
function Element:rotate(angle)
|
||||||
if not self.transform then
|
if not self.transform then
|
||||||
@@ -2947,7 +2972,8 @@ function Element:rotate(angle)
|
|||||||
self.transform.rotate = angle
|
self.transform.rotate = angle
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Scale element
|
--- Resize element visually using scale transforms for zoom effects
|
||||||
|
--- Use this for hover magnification, shrinking animations, or responsive scaling
|
||||||
---@param scaleX number X-axis scale
|
---@param scaleX number X-axis scale
|
||||||
---@param scaleY number? Y-axis scale (defaults to scaleX)
|
---@param scaleY number? Y-axis scale (defaults to scaleX)
|
||||||
function Element:scale(scaleX, scaleY)
|
function Element:scale(scaleX, scaleY)
|
||||||
@@ -2958,7 +2984,8 @@ function Element:scale(scaleX, scaleY)
|
|||||||
self.transform.scaleY = scaleY or scaleX
|
self.transform.scaleY = scaleY or scaleX
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Translate element
|
--- Offset element position using transforms for smooth movement without layout recalculation
|
||||||
|
--- Use this for parallax effects, draggable elements, or position animations
|
||||||
---@param x number X translation
|
---@param x number X translation
|
||||||
---@param y number Y translation
|
---@param y number Y translation
|
||||||
function Element:translate(x, y)
|
function Element:translate(x, y)
|
||||||
@@ -2969,7 +2996,8 @@ function Element:translate(x, y)
|
|||||||
self.transform.translateY = y
|
self.transform.translateY = y
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set transform origin
|
--- Define the pivot point for rotation and scaling transforms
|
||||||
|
--- Use this to rotate around corners, edges, or custom points rather than the center
|
||||||
---@param originX number X origin (0-1, where 0.5 is center)
|
---@param originX number X origin (0-1, where 0.5 is center)
|
||||||
---@param originY number Y origin (0-1, where 0.5 is center)
|
---@param originY number Y origin (0-1, where 0.5 is center)
|
||||||
function Element:setTransformOrigin(originX, originY)
|
function Element:setTransformOrigin(originX, originY)
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ Theme.__index = Theme
|
|||||||
local themes = {}
|
local themes = {}
|
||||||
local activeTheme = nil
|
local activeTheme = nil
|
||||||
|
|
||||||
---Create a new theme instance
|
--- Create reusable design systems with consistent styling, 9-patch assets, and component states
|
||||||
|
--- Use this to build professional-looking UIs with minimal per-element configuration
|
||||||
---@param definition ThemeDefinition Theme definition table
|
---@param definition ThemeDefinition Theme definition table
|
||||||
---@return Theme theme The new theme instance
|
---@return Theme theme The new theme instance
|
||||||
function Theme.new(definition)
|
function Theme.new(definition)
|
||||||
@@ -312,7 +313,8 @@ function Theme.new(definition)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Load a theme from a Lua file
|
--- Import a theme definition from a file to enable hot-reloading and modular design systems
|
||||||
|
--- Use this to load bundled or user-created themes dynamically
|
||||||
---@param path string Path to theme definition file (e.g., "space" or "mytheme")
|
---@param path string Path to theme definition file (e.g., "space" or "mytheme")
|
||||||
---@return Theme? theme The loaded theme, or nil on error
|
---@return Theme? theme The loaded theme, or nil on error
|
||||||
function Theme.load(path)
|
function Theme.load(path)
|
||||||
@@ -348,7 +350,8 @@ function Theme.load(path)
|
|||||||
return theme
|
return theme
|
||||||
end
|
end
|
||||||
|
|
||||||
---Set the active theme
|
--- Switch the global theme to instantly restyle all themed UI elements
|
||||||
|
--- Use this to implement light/dark mode toggles or user-selectable skins
|
||||||
---@param themeOrName Theme|string Theme instance or theme name to activate
|
---@param themeOrName Theme|string Theme instance or theme name to activate
|
||||||
function Theme.setActive(themeOrName)
|
function Theme.setActive(themeOrName)
|
||||||
if type(themeOrName) == "string" then
|
if type(themeOrName) == "string" then
|
||||||
@@ -371,13 +374,15 @@ function Theme.setActive(themeOrName)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the active theme
|
--- Access the current theme to query colors, fonts, or create theme-aware components
|
||||||
|
--- Use this to build UI that adapts to the active design system
|
||||||
---@return Theme? theme The active theme, or nil if none is active
|
---@return Theme? theme The active theme, or nil if none is active
|
||||||
function Theme.getActive()
|
function Theme.getActive()
|
||||||
return activeTheme
|
return activeTheme
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get a component from the active theme
|
--- Retrieve pre-configured visual styles for UI components to maintain consistency
|
||||||
|
--- Use this to apply theme definitions to custom elements
|
||||||
---@param componentName string Name of the component (e.g., "button", "panel")
|
---@param componentName string Name of the component (e.g., "button", "panel")
|
||||||
---@param state string? Optional state (e.g., "hover", "pressed", "disabled")
|
---@param state string? Optional state (e.g., "hover", "pressed", "disabled")
|
||||||
---@return ThemeComponent? component Returns component or nil if not found
|
---@return ThemeComponent? component Returns component or nil if not found
|
||||||
@@ -399,7 +404,8 @@ function Theme.getComponent(componentName, state)
|
|||||||
return component
|
return component
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get a font from the active theme
|
--- Access theme-defined fonts for consistent typography across your UI
|
||||||
|
--- Use this to load fonts specified in your theme definition
|
||||||
---@param fontName string Name of the font family (e.g., "default", "heading")
|
---@param fontName string Name of the font family (e.g., "default", "heading")
|
||||||
---@return string? fontPath Returns font path or nil if not found
|
---@return string? fontPath Returns font path or nil if not found
|
||||||
function Theme.getFont(fontName)
|
function Theme.getFont(fontName)
|
||||||
@@ -410,7 +416,8 @@ function Theme.getFont(fontName)
|
|||||||
return activeTheme.fonts and activeTheme.fonts[fontName]
|
return activeTheme.fonts and activeTheme.fonts[fontName]
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get a color from the active theme
|
--- Retrieve semantic colors from the theme palette for consistent brand identity
|
||||||
|
--- Use this instead of hardcoding colors to support themeing and color scheme switches
|
||||||
---@param colorName string Name of the color (e.g., "primary", "secondary")
|
---@param colorName string Name of the color (e.g., "primary", "secondary")
|
||||||
---@return Color? color Returns Color instance or nil if not found
|
---@return Color? color Returns Color instance or nil if not found
|
||||||
function Theme.getColor(colorName)
|
function Theme.getColor(colorName)
|
||||||
@@ -461,7 +468,8 @@ function Theme.getAllColors()
|
|||||||
return activeTheme.colors
|
return activeTheme.colors
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get a color with a fallback if not found
|
--- Safely get theme colors with guaranteed fallbacks to prevent missing color errors
|
||||||
|
--- Use this when you need a color value no matter what
|
||||||
---@param colorName string Name of the color to retrieve
|
---@param colorName string Name of the color to retrieve
|
||||||
---@param fallback Color? Fallback color if not found (default: white)
|
---@param fallback Color? Fallback color if not found (default: white)
|
||||||
---@return Color color The color or fallback (guaranteed non-nil)
|
---@return Color color The color or fallback (guaranteed non-nil)
|
||||||
@@ -721,7 +729,8 @@ end
|
|||||||
-- Export both Theme and ThemeManager
|
-- Export both Theme and ThemeManager
|
||||||
Theme.Manager = ThemeManager
|
Theme.Manager = ThemeManager
|
||||||
|
|
||||||
---Validate a theme definition for structural correctness (non-aggressive)
|
--- Check theme definitions for correctness before use to catch configuration errors early
|
||||||
|
--- Use this during development to verify custom themes are properly structured
|
||||||
---@param theme table? The theme to validate
|
---@param theme table? The theme to validate
|
||||||
---@param options table? Optional validation options {strict: boolean}
|
---@param options table? Optional validation options {strict: boolean}
|
||||||
---@return boolean valid, table errors List of validation errors
|
---@return boolean valid, table errors List of validation errors
|
||||||
@@ -908,7 +917,8 @@ function Theme.validateTheme(theme, options)
|
|||||||
return #errors == 0, errors
|
return #errors == 0, errors
|
||||||
end
|
end
|
||||||
|
|
||||||
---Sanitize a theme definition by removing invalid values and providing defaults
|
--- Clean up malformed theme data to make it usable without crashing
|
||||||
|
--- Use this to robustly handle user-created or external themes
|
||||||
---@param theme table? The theme to sanitize
|
---@param theme table? The theme to sanitize
|
||||||
---@return table sanitized The sanitized theme
|
---@return table sanitized The sanitized theme
|
||||||
function Theme.sanitizeTheme(theme)
|
function Theme.sanitizeTheme(theme)
|
||||||
|
|||||||
@@ -86,13 +86,20 @@ function Transform.lerp(from, to, t)
|
|||||||
if type(to) ~= "table" then
|
if type(to) ~= "table" then
|
||||||
to = Transform.new()
|
to = Transform.new()
|
||||||
end
|
end
|
||||||
if type(t) ~= "number" or t ~= t or t == math.huge or t == -math.huge then
|
if type(t) ~= "number" or t ~= t then
|
||||||
|
-- NaN or invalid type
|
||||||
t = 0
|
t = 0
|
||||||
|
elseif t == math.huge then
|
||||||
|
-- Positive infinity
|
||||||
|
t = 1
|
||||||
|
elseif t == -math.huge then
|
||||||
|
-- Negative infinity
|
||||||
|
t = 0
|
||||||
|
else
|
||||||
|
-- Clamp t to 0-1 range
|
||||||
|
t = math.max(0, math.min(1, t))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Clamp t to 0-1 range
|
|
||||||
t = math.max(0, math.min(1, t))
|
|
||||||
|
|
||||||
return Transform.new({
|
return Transform.new({
|
||||||
rotate = (from.rotate or 0) * (1 - t) + (to.rotate or 0) * t,
|
rotate = (from.rotate or 0) * (1 - t) + (to.rotate or 0) * t,
|
||||||
scaleX = (from.scaleX or 1) * (1 - t) + (to.scaleX or 1) * t,
|
scaleX = (from.scaleX or 1) * (1 - t) + (to.scaleX or 1) * t,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ function TestAnimationProperties:testColorLerp_MidPoint()
|
|||||||
local colorA = Color.new(0, 0, 0, 1) -- Black
|
local colorA = Color.new(0, 0, 0, 1) -- Black
|
||||||
local colorB = Color.new(1, 1, 1, 1) -- White
|
local colorB = Color.new(1, 1, 1, 1) -- White
|
||||||
local result = Color.lerp(colorA, colorB, 0.5)
|
local result = Color.lerp(colorA, colorB, 0.5)
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.r, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.r, 0.5, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.g, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.g, 0.5, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.b, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.b, 0.5, 0.01)
|
||||||
@@ -38,7 +38,7 @@ function TestAnimationProperties:testColorLerp_StartPoint()
|
|||||||
local colorA = Color.new(1, 0, 0, 1) -- Red
|
local colorA = Color.new(1, 0, 0, 1) -- Red
|
||||||
local colorB = Color.new(0, 0, 1, 1) -- Blue
|
local colorB = Color.new(0, 0, 1, 1) -- Blue
|
||||||
local result = Color.lerp(colorA, colorB, 0)
|
local result = Color.lerp(colorA, colorB, 0)
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.r, 1, 0.01)
|
luaunit.assertAlmostEquals(result.r, 1, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.g, 0, 0.01)
|
luaunit.assertAlmostEquals(result.g, 0, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.b, 0, 0.01)
|
luaunit.assertAlmostEquals(result.b, 0, 0.01)
|
||||||
@@ -48,7 +48,7 @@ function TestAnimationProperties:testColorLerp_EndPoint()
|
|||||||
local colorA = Color.new(1, 0, 0, 1) -- Red
|
local colorA = Color.new(1, 0, 0, 1) -- Red
|
||||||
local colorB = Color.new(0, 0, 1, 1) -- Blue
|
local colorB = Color.new(0, 0, 1, 1) -- Blue
|
||||||
local result = Color.lerp(colorA, colorB, 1)
|
local result = Color.lerp(colorA, colorB, 1)
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.r, 0, 0.01)
|
luaunit.assertAlmostEquals(result.r, 0, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.g, 0, 0.01)
|
luaunit.assertAlmostEquals(result.g, 0, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.b, 1, 0.01)
|
luaunit.assertAlmostEquals(result.b, 1, 0.01)
|
||||||
@@ -58,7 +58,7 @@ function TestAnimationProperties:testColorLerp_Alpha()
|
|||||||
local colorA = Color.new(1, 1, 1, 0) -- Transparent white
|
local colorA = Color.new(1, 1, 1, 0) -- Transparent white
|
||||||
local colorB = Color.new(1, 1, 1, 1) -- Opaque white
|
local colorB = Color.new(1, 1, 1, 1) -- Opaque white
|
||||||
local result = Color.lerp(colorA, colorB, 0.5)
|
local result = Color.lerp(colorA, colorB, 0.5)
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.a, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.a, 0.5, 0.01)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -72,11 +72,11 @@ end
|
|||||||
function TestAnimationProperties:testColorLerp_ClampT()
|
function TestAnimationProperties:testColorLerp_ClampT()
|
||||||
local colorA = Color.new(0, 0, 0, 1)
|
local colorA = Color.new(0, 0, 0, 1)
|
||||||
local colorB = Color.new(1, 1, 1, 1)
|
local colorB = Color.new(1, 1, 1, 1)
|
||||||
|
|
||||||
-- Test t > 1
|
-- Test t > 1
|
||||||
local result1 = Color.lerp(colorA, colorB, 1.5)
|
local result1 = Color.lerp(colorA, colorB, 1.5)
|
||||||
luaunit.assertAlmostEquals(result1.r, 1, 0.01)
|
luaunit.assertAlmostEquals(result1.r, 1, 0.01)
|
||||||
|
|
||||||
-- Test t < 0
|
-- Test t < 0
|
||||||
local result2 = Color.lerp(colorA, colorB, -0.5)
|
local result2 = Color.lerp(colorA, colorB, -0.5)
|
||||||
luaunit.assertAlmostEquals(result2.r, 0, 0.01)
|
luaunit.assertAlmostEquals(result2.r, 0, 0.01)
|
||||||
@@ -90,10 +90,10 @@ function TestAnimationProperties:testPositionAnimation_XProperty()
|
|||||||
start = { x = 0 },
|
start = { x = 0 },
|
||||||
final = { x = 100 },
|
final = { x = 100 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.x, 50, 0.01)
|
luaunit.assertAlmostEquals(result.x, 50, 0.01)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -103,10 +103,10 @@ function TestAnimationProperties:testPositionAnimation_YProperty()
|
|||||||
start = { y = 0 },
|
start = { y = 0 },
|
||||||
final = { y = 200 },
|
final = { y = 200 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.y, 100, 0.01)
|
luaunit.assertAlmostEquals(result.y, 100, 0.01)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -116,10 +116,10 @@ function TestAnimationProperties:testPositionAnimation_XY()
|
|||||||
start = { x = 10, y = 20 },
|
start = { x = 10, y = 20 },
|
||||||
final = { x = 110, y = 220 },
|
final = { x = 110, y = 220 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.x, 60, 0.01)
|
luaunit.assertAlmostEquals(result.x, 60, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.y, 120, 0.01)
|
luaunit.assertAlmostEquals(result.y, 120, 0.01)
|
||||||
end
|
end
|
||||||
@@ -133,10 +133,10 @@ function TestAnimationProperties:testColorAnimation_BackgroundColor()
|
|||||||
final = { backgroundColor = Color.new(0, 0, 1, 1) }, -- Blue
|
final = { backgroundColor = Color.new(0, 0, 1, 1) }, -- Blue
|
||||||
})
|
})
|
||||||
anim:setColorModule(Color)
|
anim:setColorModule(Color)
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertNotNil(result.backgroundColor)
|
luaunit.assertNotNil(result.backgroundColor)
|
||||||
luaunit.assertAlmostEquals(result.backgroundColor.r, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.backgroundColor.r, 0.5, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.backgroundColor.b, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.backgroundColor.b, 0.5, 0.01)
|
||||||
@@ -157,14 +157,14 @@ function TestAnimationProperties:testColorAnimation_MultipleColors()
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
anim:setColorModule(Color)
|
anim:setColorModule(Color)
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertNotNil(result.backgroundColor)
|
luaunit.assertNotNil(result.backgroundColor)
|
||||||
luaunit.assertNotNil(result.borderColor)
|
luaunit.assertNotNil(result.borderColor)
|
||||||
luaunit.assertNotNil(result.textColor)
|
luaunit.assertNotNil(result.textColor)
|
||||||
|
|
||||||
-- Mid-point should be (0.5, 0.5, 0.5) for backgroundColor
|
-- Mid-point should be (0.5, 0.5, 0.5) for backgroundColor
|
||||||
luaunit.assertAlmostEquals(result.backgroundColor.r, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.backgroundColor.r, 0.5, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.backgroundColor.g, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.backgroundColor.g, 0.5, 0.01)
|
||||||
@@ -178,10 +178,10 @@ function TestAnimationProperties:testColorAnimation_WithoutColorModule()
|
|||||||
final = { backgroundColor = Color.new(0, 0, 1, 1) },
|
final = { backgroundColor = Color.new(0, 0, 1, 1) },
|
||||||
})
|
})
|
||||||
-- Don't set Color module
|
-- Don't set Color module
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertNil(result.backgroundColor)
|
luaunit.assertNil(result.backgroundColor)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -192,10 +192,10 @@ function TestAnimationProperties:testColorAnimation_HexColors()
|
|||||||
final = { backgroundColor = "#0000FF" }, -- Blue
|
final = { backgroundColor = "#0000FF" }, -- Blue
|
||||||
})
|
})
|
||||||
anim:setColorModule(Color)
|
anim:setColorModule(Color)
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertNotNil(result.backgroundColor)
|
luaunit.assertNotNil(result.backgroundColor)
|
||||||
luaunit.assertAlmostEquals(result.backgroundColor.r, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.backgroundColor.r, 0.5, 0.01)
|
||||||
end
|
end
|
||||||
@@ -207,10 +207,10 @@ function TestAnimationProperties:testColorAnimation_NamedColors()
|
|||||||
final = { backgroundColor = "blue" },
|
final = { backgroundColor = "blue" },
|
||||||
})
|
})
|
||||||
anim:setColorModule(Color)
|
anim:setColorModule(Color)
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertNotNil(result.backgroundColor)
|
luaunit.assertNotNil(result.backgroundColor)
|
||||||
luaunit.assertAlmostEquals(result.backgroundColor.r, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.backgroundColor.r, 0.5, 0.01)
|
||||||
end
|
end
|
||||||
@@ -223,10 +223,10 @@ function TestAnimationProperties:testNumericAnimation_Gap()
|
|||||||
start = { gap = 0 },
|
start = { gap = 0 },
|
||||||
final = { gap = 20 },
|
final = { gap = 20 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.gap, 10, 0.01)
|
luaunit.assertAlmostEquals(result.gap, 10, 0.01)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -236,10 +236,10 @@ function TestAnimationProperties:testNumericAnimation_ImageOpacity()
|
|||||||
start = { imageOpacity = 0 },
|
start = { imageOpacity = 0 },
|
||||||
final = { imageOpacity = 1 },
|
final = { imageOpacity = 1 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.imageOpacity, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.imageOpacity, 0.5, 0.01)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -249,10 +249,10 @@ function TestAnimationProperties:testNumericAnimation_BorderWidth()
|
|||||||
start = { borderWidth = 1 },
|
start = { borderWidth = 1 },
|
||||||
final = { borderWidth = 10 },
|
final = { borderWidth = 10 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.borderWidth, 5.5, 0.01)
|
luaunit.assertAlmostEquals(result.borderWidth, 5.5, 0.01)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -262,10 +262,10 @@ function TestAnimationProperties:testNumericAnimation_FontSize()
|
|||||||
start = { fontSize = 12 },
|
start = { fontSize = 12 },
|
||||||
final = { fontSize = 24 },
|
final = { fontSize = 24 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.fontSize, 18, 0.01)
|
luaunit.assertAlmostEquals(result.fontSize, 18, 0.01)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -275,10 +275,10 @@ function TestAnimationProperties:testNumericAnimation_MultipleProperties()
|
|||||||
start = { gap = 0, imageOpacity = 0, borderWidth = 1 },
|
start = { gap = 0, imageOpacity = 0, borderWidth = 1 },
|
||||||
final = { gap = 20, imageOpacity = 1, borderWidth = 5 },
|
final = { gap = 20, imageOpacity = 1, borderWidth = 5 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.gap, 10, 0.01)
|
luaunit.assertAlmostEquals(result.gap, 10, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.imageOpacity, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.imageOpacity, 0.5, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.borderWidth, 3, 0.01)
|
luaunit.assertAlmostEquals(result.borderWidth, 3, 0.01)
|
||||||
@@ -292,10 +292,10 @@ function TestAnimationProperties:testTableAnimation_Padding()
|
|||||||
start = { padding = { top = 0, right = 0, bottom = 0, left = 0 } },
|
start = { padding = { top = 0, right = 0, bottom = 0, left = 0 } },
|
||||||
final = { padding = { top = 10, right = 20, bottom = 10, left = 20 } },
|
final = { padding = { top = 10, right = 20, bottom = 10, left = 20 } },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertNotNil(result.padding)
|
luaunit.assertNotNil(result.padding)
|
||||||
luaunit.assertAlmostEquals(result.padding.top, 5, 0.01)
|
luaunit.assertAlmostEquals(result.padding.top, 5, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.padding.right, 10, 0.01)
|
luaunit.assertAlmostEquals(result.padding.right, 10, 0.01)
|
||||||
@@ -309,10 +309,10 @@ function TestAnimationProperties:testTableAnimation_Margin()
|
|||||||
start = { margin = { top = 0, right = 0, bottom = 0, left = 0 } },
|
start = { margin = { top = 0, right = 0, bottom = 0, left = 0 } },
|
||||||
final = { margin = { top = 20, right = 20, bottom = 20, left = 20 } },
|
final = { margin = { top = 20, right = 20, bottom = 20, left = 20 } },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertNotNil(result.margin)
|
luaunit.assertNotNil(result.margin)
|
||||||
luaunit.assertAlmostEquals(result.margin.top, 10, 0.01)
|
luaunit.assertAlmostEquals(result.margin.top, 10, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.margin.right, 10, 0.01)
|
luaunit.assertAlmostEquals(result.margin.right, 10, 0.01)
|
||||||
@@ -324,10 +324,10 @@ function TestAnimationProperties:testTableAnimation_CornerRadius()
|
|||||||
start = { cornerRadius = { topLeft = 0, topRight = 0, bottomLeft = 0, bottomRight = 0 } },
|
start = { cornerRadius = { topLeft = 0, topRight = 0, bottomLeft = 0, bottomRight = 0 } },
|
||||||
final = { cornerRadius = { topLeft = 10, topRight = 10, bottomLeft = 10, bottomRight = 10 } },
|
final = { cornerRadius = { topLeft = 10, topRight = 10, bottomLeft = 10, bottomRight = 10 } },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertNotNil(result.cornerRadius)
|
luaunit.assertNotNil(result.cornerRadius)
|
||||||
luaunit.assertAlmostEquals(result.cornerRadius.topLeft, 5, 0.01)
|
luaunit.assertAlmostEquals(result.cornerRadius.topLeft, 5, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.cornerRadius.topRight, 5, 0.01)
|
luaunit.assertAlmostEquals(result.cornerRadius.topRight, 5, 0.01)
|
||||||
@@ -340,10 +340,10 @@ function TestAnimationProperties:testTableAnimation_PartialKeys()
|
|||||||
start = { padding = { top = 0, left = 0 } },
|
start = { padding = { top = 0, left = 0 } },
|
||||||
final = { padding = { top = 10, right = 20, left = 10 } },
|
final = { padding = { top = 10, right = 20, left = 10 } },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertNotNil(result.padding)
|
luaunit.assertNotNil(result.padding)
|
||||||
luaunit.assertAlmostEquals(result.padding.top, 5, 0.01)
|
luaunit.assertAlmostEquals(result.padding.top, 5, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.padding.left, 5, 0.01)
|
luaunit.assertAlmostEquals(result.padding.left, 5, 0.01)
|
||||||
@@ -357,10 +357,10 @@ function TestAnimationProperties:testTableAnimation_NonNumericValues()
|
|||||||
start = { padding = { top = 0, special = "value" } },
|
start = { padding = { top = 0, special = "value" } },
|
||||||
final = { padding = { top = 10, special = "value" } },
|
final = { padding = { top = 10, special = "value" } },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertNotNil(result.padding)
|
luaunit.assertNotNil(result.padding)
|
||||||
luaunit.assertAlmostEquals(result.padding.top, 5, 0.01)
|
luaunit.assertAlmostEquals(result.padding.top, 5, 0.01)
|
||||||
end
|
end
|
||||||
@@ -392,10 +392,10 @@ function TestAnimationProperties:testCombinedAnimation_AllTypes()
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
anim:setColorModule(Color)
|
anim:setColorModule(Color)
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
-- Check all properties interpolated correctly
|
-- Check all properties interpolated correctly
|
||||||
luaunit.assertAlmostEquals(result.width, 150, 0.01)
|
luaunit.assertAlmostEquals(result.width, 150, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.height, 150, 0.01)
|
luaunit.assertAlmostEquals(result.height, 150, 0.01)
|
||||||
@@ -415,10 +415,10 @@ function TestAnimationProperties:testCombinedAnimation_WithEasing()
|
|||||||
easing = "easeInQuad",
|
easing = "easeInQuad",
|
||||||
})
|
})
|
||||||
anim:setColorModule(Color)
|
anim:setColorModule(Color)
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
-- With easeInQuad, at t=0.5, eased value should be 0.25
|
-- With easeInQuad, at t=0.5, eased value should be 0.25
|
||||||
luaunit.assertAlmostEquals(result.x, 25, 0.01)
|
luaunit.assertAlmostEquals(result.x, 25, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.backgroundColor.r, 0.25, 0.01)
|
luaunit.assertAlmostEquals(result.backgroundColor.r, 0.25, 0.01)
|
||||||
@@ -433,10 +433,10 @@ function TestAnimationProperties:testBackwardCompatibility_WidthHeightOpacity()
|
|||||||
start = { width = 100, height = 100, opacity = 0 },
|
start = { width = 100, height = 100, opacity = 0 },
|
||||||
final = { width = 200, height = 200, opacity = 1 },
|
final = { width = 200, height = 200, opacity = 1 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.width, 150, 0.01)
|
luaunit.assertAlmostEquals(result.width, 150, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.height, 150, 0.01)
|
luaunit.assertAlmostEquals(result.height, 150, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.opacity, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.opacity, 0.5, 0.01)
|
||||||
@@ -444,19 +444,19 @@ end
|
|||||||
|
|
||||||
function TestAnimationProperties:testBackwardCompatibility_FadeHelper()
|
function TestAnimationProperties:testBackwardCompatibility_FadeHelper()
|
||||||
local anim = Animation.fade(1, 0, 1)
|
local anim = Animation.fade(1, 0, 1)
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.opacity, 0.5, 0.01)
|
luaunit.assertAlmostEquals(result.opacity, 0.5, 0.01)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestAnimationProperties:testBackwardCompatibility_ScaleHelper()
|
function TestAnimationProperties:testBackwardCompatibility_ScaleHelper()
|
||||||
local anim = Animation.scale(1, { width = 100, height = 100 }, { width = 200, height = 200 })
|
local anim = Animation.scale(1, { width = 100, height = 100 }, { width = 200, height = 200 })
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.width, 150, 0.01)
|
luaunit.assertAlmostEquals(result.width, 150, 0.01)
|
||||||
luaunit.assertAlmostEquals(result.height, 150, 0.01)
|
luaunit.assertAlmostEquals(result.height, 150, 0.01)
|
||||||
end
|
end
|
||||||
@@ -469,10 +469,10 @@ function TestAnimationProperties:testEdgeCase_MissingStartValue()
|
|||||||
start = { x = 0 },
|
start = { x = 0 },
|
||||||
final = { x = 100, y = 100 },
|
final = { x = 100, y = 100 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.x, 50, 0.01)
|
luaunit.assertAlmostEquals(result.x, 50, 0.01)
|
||||||
luaunit.assertNil(result.y) -- Should be nil since start.y is missing
|
luaunit.assertNil(result.y) -- Should be nil since start.y is missing
|
||||||
end
|
end
|
||||||
@@ -483,10 +483,10 @@ function TestAnimationProperties:testEdgeCase_MissingFinalValue()
|
|||||||
start = { x = 0, y = 0 },
|
start = { x = 0, y = 0 },
|
||||||
final = { x = 100 },
|
final = { x = 100 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
luaunit.assertAlmostEquals(result.x, 50, 0.01)
|
luaunit.assertAlmostEquals(result.x, 50, 0.01)
|
||||||
luaunit.assertNil(result.y) -- Should be nil since final.y is missing
|
luaunit.assertNil(result.y) -- Should be nil since final.y is missing
|
||||||
end
|
end
|
||||||
@@ -497,10 +497,10 @@ function TestAnimationProperties:testEdgeCase_EmptyTables()
|
|||||||
start = {},
|
start = {},
|
||||||
final = {},
|
final = {},
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result = anim:interpolate()
|
local result = anim:interpolate()
|
||||||
|
|
||||||
-- Should not error, just return empty result
|
-- Should not error, just return empty result
|
||||||
luaunit.assertNotNil(result)
|
luaunit.assertNotNil(result)
|
||||||
end
|
end
|
||||||
@@ -512,11 +512,11 @@ function TestAnimationProperties:testEdgeCase_CachedResult()
|
|||||||
start = { x = 0 },
|
start = { x = 0 },
|
||||||
final = { x = 100 },
|
final = { x = 100 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result1 = anim:interpolate()
|
local result1 = anim:interpolate()
|
||||||
local result2 = anim:interpolate() -- Should use cached result
|
local result2 = anim:interpolate() -- Should use cached result
|
||||||
|
|
||||||
luaunit.assertEquals(result1, result2) -- Same table reference
|
luaunit.assertEquals(result1, result2) -- Same table reference
|
||||||
luaunit.assertAlmostEquals(result1.x, 50, 0.01)
|
luaunit.assertAlmostEquals(result1.x, 50, 0.01)
|
||||||
end
|
end
|
||||||
@@ -527,15 +527,15 @@ function TestAnimationProperties:testEdgeCase_ResultInvalidatedOnUpdate()
|
|||||||
start = { x = 0 },
|
start = { x = 0 },
|
||||||
final = { x = 100 },
|
final = { x = 100 },
|
||||||
})
|
})
|
||||||
|
|
||||||
anim:update(0.5)
|
anim:update(0.5)
|
||||||
local result1 = anim:interpolate()
|
local result1 = anim:interpolate()
|
||||||
local x1 = result1.x -- Store value, not reference
|
local x1 = result1.x -- Store value, not reference
|
||||||
|
|
||||||
anim:update(0.25) -- Update again
|
anim:update(0.25) -- Update again
|
||||||
local result2 = anim:interpolate()
|
local result2 = anim:interpolate()
|
||||||
local x2 = result2.x
|
local x2 = result2.x
|
||||||
|
|
||||||
-- Should recalculate
|
-- Should recalculate
|
||||||
-- Note: result1 and result2 are the same cached table, but values should be updated
|
-- Note: result1 and result2 are the same cached table, but values should be updated
|
||||||
luaunit.assertAlmostEquals(x1, 50, 0.01)
|
luaunit.assertAlmostEquals(x1, 50, 0.01)
|
||||||
@@ -544,4 +544,6 @@ function TestAnimationProperties:testEdgeCase_ResultInvalidatedOnUpdate()
|
|||||||
luaunit.assertAlmostEquals(result1.x, 75, 0.01)
|
luaunit.assertAlmostEquals(result1.x, 75, 0.01)
|
||||||
end
|
end
|
||||||
|
|
||||||
os.exit(luaunit.LuaUnit.run())
|
if not _G.RUNNING_ALL_TESTS then
|
||||||
|
os.exit(luaunit.LuaUnit.run())
|
||||||
|
end
|
||||||
|
|||||||
@@ -92,55 +92,60 @@ end
|
|||||||
|
|
||||||
-- Test: warn() prints with correct format (backward compatibility)
|
-- Test: warn() prints with correct format (backward compatibility)
|
||||||
function TestErrorHandler:test_warn_prints_with_format()
|
function TestErrorHandler:test_warn_prints_with_format()
|
||||||
-- Capture print output by mocking print
|
-- Capture io.write output by mocking io.write
|
||||||
local captured = nil
|
local captured = nil
|
||||||
local originalPrint = print
|
local originalWrite = io.write
|
||||||
print = function(msg)
|
io.write = function(msg)
|
||||||
captured = msg
|
captured = msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ErrorHandler.setLogTarget("console")
|
||||||
ErrorHandler.warn("TestModule", "This is a warning")
|
ErrorHandler.warn("TestModule", "This is a warning")
|
||||||
|
ErrorHandler.setLogTarget("none")
|
||||||
|
|
||||||
print = originalPrint
|
io.write = originalWrite
|
||||||
|
|
||||||
luaunit.assertNotNil(captured, "warn() should print")
|
luaunit.assertNotNil(captured, "warn() should print")
|
||||||
luaunit.assertEquals(captured, "[FlexLove - TestModule] Warning: This is a warning")
|
luaunit.assertStrContains(captured, "[WARNING] [TestModule] This is a warning")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Test: warn() with error code
|
-- Test: warn() with error code
|
||||||
function TestErrorHandler:test_warn_with_code()
|
function TestErrorHandler:test_warn_with_code()
|
||||||
local captured = nil
|
local captured = nil
|
||||||
local originalPrint = print
|
local originalWrite = io.write
|
||||||
print = function(msg)
|
io.write = function(msg)
|
||||||
captured = msg
|
captured = msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ErrorHandler.setLogTarget("console")
|
||||||
ErrorHandler.warn("TestModule", "VAL_001", "Potentially invalid property")
|
ErrorHandler.warn("TestModule", "VAL_001", "Potentially invalid property")
|
||||||
|
ErrorHandler.setLogTarget("none")
|
||||||
|
|
||||||
print = originalPrint
|
io.write = originalWrite
|
||||||
|
|
||||||
luaunit.assertNotNil(captured, "warn() should print")
|
luaunit.assertNotNil(captured, "warn() should print")
|
||||||
luaunit.assertStrContains(captured, "[FlexLove - TestModule] Warning [FLEXLOVE_VAL_001]")
|
luaunit.assertStrContains(captured, "[WARNING] [TestModule] [VAL_001]")
|
||||||
luaunit.assertStrContains(captured, "Potentially invalid property")
|
luaunit.assertStrContains(captured, "Potentially invalid property")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Test: warn() with details
|
-- Test: warn() with details
|
||||||
function TestErrorHandler:test_warn_with_details()
|
function TestErrorHandler:test_warn_with_details()
|
||||||
local captured = nil
|
local captured = nil
|
||||||
local originalPrint = print
|
local originalWrite = io.write
|
||||||
print = function(msg)
|
io.write = function(msg)
|
||||||
captured = msg
|
captured = (captured or "") .. msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ErrorHandler.setLogTarget("console")
|
||||||
ErrorHandler.warn("TestModule", "VAL_001", "Check this property", {
|
ErrorHandler.warn("TestModule", "VAL_001", "Check this property", {
|
||||||
property = "height",
|
property = "height",
|
||||||
value = "auto",
|
value = "auto",
|
||||||
})
|
})
|
||||||
|
ErrorHandler.setLogTarget("none")
|
||||||
|
|
||||||
print = originalPrint
|
io.write = originalWrite
|
||||||
|
|
||||||
luaunit.assertNotNil(captured, "warn() should print")
|
luaunit.assertNotNil(captured, "warn() should print")
|
||||||
luaunit.assertStrContains(captured, "Details:")
|
|
||||||
luaunit.assertStrContains(captured, "Property: height")
|
luaunit.assertStrContains(captured, "Property: height")
|
||||||
luaunit.assertStrContains(captured, "Value: auto")
|
luaunit.assertStrContains(captured, "Value: auto")
|
||||||
end
|
end
|
||||||
@@ -225,14 +230,16 @@ end
|
|||||||
-- Test: warnDeprecated prints deprecation warning
|
-- Test: warnDeprecated prints deprecation warning
|
||||||
function TestErrorHandler:test_warnDeprecated_prints_message()
|
function TestErrorHandler:test_warnDeprecated_prints_message()
|
||||||
local captured = nil
|
local captured = nil
|
||||||
local originalPrint = print
|
local originalWrite = io.write
|
||||||
print = function(msg)
|
io.write = function(msg)
|
||||||
captured = msg
|
captured = msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ErrorHandler.setLogTarget("console")
|
||||||
ErrorHandler.warnDeprecated("TestModule", "oldFunction", "newFunction")
|
ErrorHandler.warnDeprecated("TestModule", "oldFunction", "newFunction")
|
||||||
|
ErrorHandler.setLogTarget("none")
|
||||||
|
|
||||||
print = originalPrint
|
io.write = originalWrite
|
||||||
|
|
||||||
luaunit.assertNotNil(captured, "warnDeprecated should print")
|
luaunit.assertNotNil(captured, "warnDeprecated should print")
|
||||||
luaunit.assertStrContains(captured, "'oldFunction' is deprecated. Use 'newFunction' instead")
|
luaunit.assertStrContains(captured, "'oldFunction' is deprecated. Use 'newFunction' instead")
|
||||||
@@ -241,14 +248,16 @@ end
|
|||||||
-- Test: warnCommonMistake prints helpful message
|
-- Test: warnCommonMistake prints helpful message
|
||||||
function TestErrorHandler:test_warnCommonMistake_prints_message()
|
function TestErrorHandler:test_warnCommonMistake_prints_message()
|
||||||
local captured = nil
|
local captured = nil
|
||||||
local originalPrint = print
|
local originalWrite = io.write
|
||||||
print = function(msg)
|
io.write = function(msg)
|
||||||
captured = msg
|
captured = msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ErrorHandler.setLogTarget("console")
|
||||||
ErrorHandler.warnCommonMistake("TestModule", "Width is zero", "Set width to positive value")
|
ErrorHandler.warnCommonMistake("TestModule", "Width is zero", "Set width to positive value")
|
||||||
|
ErrorHandler.setLogTarget("none")
|
||||||
|
|
||||||
print = originalPrint
|
io.write = originalWrite
|
||||||
|
|
||||||
luaunit.assertNotNil(captured, "warnCommonMistake should print")
|
luaunit.assertNotNil(captured, "warnCommonMistake should print")
|
||||||
luaunit.assertStrContains(captured, "Width is zero. Suggestion: Set width to positive value")
|
luaunit.assertStrContains(captured, "Width is zero. Suggestion: Set width to positive value")
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ end
|
|||||||
function TestFlexLove:testModuleLoads()
|
function TestFlexLove:testModuleLoads()
|
||||||
luaunit.assertNotNil(FlexLove)
|
luaunit.assertNotNil(FlexLove)
|
||||||
luaunit.assertNotNil(FlexLove._VERSION)
|
luaunit.assertNotNil(FlexLove._VERSION)
|
||||||
luaunit.assertEquals(FlexLove._VERSION, "0.2.2")
|
luaunit.assertEquals(FlexLove._VERSION, "0.2.3")
|
||||||
luaunit.assertNotNil(FlexLove._DESCRIPTION)
|
luaunit.assertNotNil(FlexLove._DESCRIPTION)
|
||||||
luaunit.assertNotNil(FlexLove._URL)
|
luaunit.assertNotNil(FlexLove._URL)
|
||||||
luaunit.assertNotNil(FlexLove._LICENSE)
|
luaunit.assertNotNil(FlexLove._LICENSE)
|
||||||
|
|||||||
@@ -16,8 +16,12 @@ TestImageTiling = {}
|
|||||||
function TestImageTiling:setUp()
|
function TestImageTiling:setUp()
|
||||||
-- Create a mock image
|
-- Create a mock image
|
||||||
self.mockImage = {
|
self.mockImage = {
|
||||||
getDimensions = function() return 64, 64 end,
|
getDimensions = function()
|
||||||
type = function() return "Image" end,
|
return 64, 64
|
||||||
|
end,
|
||||||
|
type = function()
|
||||||
|
return "Image"
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -30,7 +34,7 @@ function TestImageTiling:testDrawTiledNoRepeat()
|
|||||||
local drawCalls = {}
|
local drawCalls = {}
|
||||||
local originalDraw = love.graphics.draw
|
local originalDraw = love.graphics.draw
|
||||||
love.graphics.draw = function(...)
|
love.graphics.draw = function(...)
|
||||||
table.insert(drawCalls, {...})
|
table.insert(drawCalls, { ... })
|
||||||
end
|
end
|
||||||
|
|
||||||
ImageRenderer.drawTiled(self.mockImage, 100, 100, 200, 200, "no-repeat", 1, nil)
|
ImageRenderer.drawTiled(self.mockImage, 100, 100, 200, 200, "no-repeat", 1, nil)
|
||||||
@@ -49,11 +53,11 @@ function TestImageTiling:testDrawTiledRepeat()
|
|||||||
local drawCalls = {}
|
local drawCalls = {}
|
||||||
local originalDraw = love.graphics.draw
|
local originalDraw = love.graphics.draw
|
||||||
local originalNewQuad = love.graphics.newQuad
|
local originalNewQuad = love.graphics.newQuad
|
||||||
|
|
||||||
love.graphics.draw = function(...)
|
love.graphics.draw = function(...)
|
||||||
table.insert(drawCalls, {...})
|
table.insert(drawCalls, { ... })
|
||||||
end
|
end
|
||||||
|
|
||||||
love.graphics.newQuad = function(...)
|
love.graphics.newQuad = function(...)
|
||||||
return { type = "quad", ... }
|
return { type = "quad", ... }
|
||||||
end
|
end
|
||||||
@@ -75,11 +79,11 @@ function TestImageTiling:testDrawTiledRepeatX()
|
|||||||
local drawCalls = {}
|
local drawCalls = {}
|
||||||
local originalDraw = love.graphics.draw
|
local originalDraw = love.graphics.draw
|
||||||
local originalNewQuad = love.graphics.newQuad
|
local originalNewQuad = love.graphics.newQuad
|
||||||
|
|
||||||
love.graphics.draw = function(...)
|
love.graphics.draw = function(...)
|
||||||
table.insert(drawCalls, {...})
|
table.insert(drawCalls, { ... })
|
||||||
end
|
end
|
||||||
|
|
||||||
love.graphics.newQuad = function(...)
|
love.graphics.newQuad = function(...)
|
||||||
return { type = "quad", ... }
|
return { type = "quad", ... }
|
||||||
end
|
end
|
||||||
@@ -100,11 +104,11 @@ function TestImageTiling:testDrawTiledRepeatY()
|
|||||||
local drawCalls = {}
|
local drawCalls = {}
|
||||||
local originalDraw = love.graphics.draw
|
local originalDraw = love.graphics.draw
|
||||||
local originalNewQuad = love.graphics.newQuad
|
local originalNewQuad = love.graphics.newQuad
|
||||||
|
|
||||||
love.graphics.draw = function(...)
|
love.graphics.draw = function(...)
|
||||||
table.insert(drawCalls, {...})
|
table.insert(drawCalls, { ... })
|
||||||
end
|
end
|
||||||
|
|
||||||
love.graphics.newQuad = function(...)
|
love.graphics.newQuad = function(...)
|
||||||
return { type = "quad", ... }
|
return { type = "quad", ... }
|
||||||
end
|
end
|
||||||
@@ -124,9 +128,9 @@ function TestImageTiling:testDrawTiledSpace()
|
|||||||
-- Test space mode (distributes tiles with even spacing)
|
-- Test space mode (distributes tiles with even spacing)
|
||||||
local drawCalls = {}
|
local drawCalls = {}
|
||||||
local originalDraw = love.graphics.draw
|
local originalDraw = love.graphics.draw
|
||||||
|
|
||||||
love.graphics.draw = function(...)
|
love.graphics.draw = function(...)
|
||||||
table.insert(drawCalls, {...})
|
table.insert(drawCalls, { ... })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Image is 64x64, bounds are 200x200
|
-- Image is 64x64, bounds are 200x200
|
||||||
@@ -142,9 +146,9 @@ function TestImageTiling:testDrawTiledRound()
|
|||||||
-- Test round mode (scales tiles to fit exactly)
|
-- Test round mode (scales tiles to fit exactly)
|
||||||
local drawCalls = {}
|
local drawCalls = {}
|
||||||
local originalDraw = love.graphics.draw
|
local originalDraw = love.graphics.draw
|
||||||
|
|
||||||
love.graphics.draw = function(...)
|
love.graphics.draw = function(...)
|
||||||
table.insert(drawCalls, {...})
|
table.insert(drawCalls, { ... })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Image is 64x64, bounds are 200x200
|
-- Image is 64x64, bounds are 200x200
|
||||||
@@ -160,9 +164,9 @@ function TestImageTiling:testDrawTiledWithOpacity()
|
|||||||
-- Test tiling with opacity
|
-- Test tiling with opacity
|
||||||
local setColorCalls = {}
|
local setColorCalls = {}
|
||||||
local originalSetColor = love.graphics.setColor
|
local originalSetColor = love.graphics.setColor
|
||||||
|
|
||||||
love.graphics.setColor = function(...)
|
love.graphics.setColor = function(...)
|
||||||
table.insert(setColorCalls, {...})
|
table.insert(setColorCalls, { ... })
|
||||||
end
|
end
|
||||||
|
|
||||||
ImageRenderer.drawTiled(self.mockImage, 100, 100, 200, 200, "no-repeat", 0.5, nil)
|
ImageRenderer.drawTiled(self.mockImage, 100, 100, 200, 200, "no-repeat", 0.5, nil)
|
||||||
@@ -186,9 +190,9 @@ function TestImageTiling:testDrawTiledWithTint()
|
|||||||
-- Test tiling with tint color
|
-- Test tiling with tint color
|
||||||
local setColorCalls = {}
|
local setColorCalls = {}
|
||||||
local originalSetColor = love.graphics.setColor
|
local originalSetColor = love.graphics.setColor
|
||||||
|
|
||||||
love.graphics.setColor = function(...)
|
love.graphics.setColor = function(...)
|
||||||
table.insert(setColorCalls, {...})
|
table.insert(setColorCalls, { ... })
|
||||||
end
|
end
|
||||||
|
|
||||||
local redTint = Color.new(1, 0, 0, 1)
|
local redTint = Color.new(1, 0, 0, 1)
|
||||||
@@ -219,7 +223,7 @@ function TestImageTiling:testElementImageRepeatProperty()
|
|||||||
local Renderer = require("modules.Renderer")
|
local Renderer = require("modules.Renderer")
|
||||||
local EventHandler = require("modules.EventHandler")
|
local EventHandler = require("modules.EventHandler")
|
||||||
local ImageCache = require("modules.ImageCache")
|
local ImageCache = require("modules.ImageCache")
|
||||||
|
|
||||||
local deps = {
|
local deps = {
|
||||||
utils = utils,
|
utils = utils,
|
||||||
Color = Color,
|
Color = Color,
|
||||||
@@ -231,7 +235,7 @@ function TestImageTiling:testElementImageRepeatProperty()
|
|||||||
ImageRenderer = ImageRenderer,
|
ImageRenderer = ImageRenderer,
|
||||||
ErrorHandler = ErrorHandler,
|
ErrorHandler = ErrorHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
local element = Element.new({
|
local element = Element.new({
|
||||||
width = 200,
|
width = 200,
|
||||||
height = 200,
|
height = 200,
|
||||||
@@ -251,7 +255,7 @@ function TestImageTiling:testElementImageRepeatDefault()
|
|||||||
local Renderer = require("modules.Renderer")
|
local Renderer = require("modules.Renderer")
|
||||||
local EventHandler = require("modules.EventHandler")
|
local EventHandler = require("modules.EventHandler")
|
||||||
local ImageCache = require("modules.ImageCache")
|
local ImageCache = require("modules.ImageCache")
|
||||||
|
|
||||||
local deps = {
|
local deps = {
|
||||||
utils = utils,
|
utils = utils,
|
||||||
Color = Color,
|
Color = Color,
|
||||||
@@ -263,7 +267,7 @@ function TestImageTiling:testElementImageRepeatDefault()
|
|||||||
ImageRenderer = ImageRenderer,
|
ImageRenderer = ImageRenderer,
|
||||||
ErrorHandler = ErrorHandler,
|
ErrorHandler = ErrorHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
local element = Element.new({
|
local element = Element.new({
|
||||||
width = 200,
|
width = 200,
|
||||||
height = 200,
|
height = 200,
|
||||||
@@ -282,7 +286,7 @@ function TestImageTiling:testElementSetImageRepeat()
|
|||||||
local Renderer = require("modules.Renderer")
|
local Renderer = require("modules.Renderer")
|
||||||
local EventHandler = require("modules.EventHandler")
|
local EventHandler = require("modules.EventHandler")
|
||||||
local ImageCache = require("modules.ImageCache")
|
local ImageCache = require("modules.ImageCache")
|
||||||
|
|
||||||
local deps = {
|
local deps = {
|
||||||
utils = utils,
|
utils = utils,
|
||||||
Color = Color,
|
Color = Color,
|
||||||
@@ -294,7 +298,7 @@ function TestImageTiling:testElementSetImageRepeat()
|
|||||||
ImageRenderer = ImageRenderer,
|
ImageRenderer = ImageRenderer,
|
||||||
ErrorHandler = ErrorHandler,
|
ErrorHandler = ErrorHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
local element = Element.new({
|
local element = Element.new({
|
||||||
width = 200,
|
width = 200,
|
||||||
height = 200,
|
height = 200,
|
||||||
@@ -313,9 +317,9 @@ function TestImageTiling:testElementImageTintProperty()
|
|||||||
local Renderer = require("modules.Renderer")
|
local Renderer = require("modules.Renderer")
|
||||||
local EventHandler = require("modules.EventHandler")
|
local EventHandler = require("modules.EventHandler")
|
||||||
local ImageCache = require("modules.ImageCache")
|
local ImageCache = require("modules.ImageCache")
|
||||||
|
|
||||||
local redTint = Color.new(1, 0, 0, 1)
|
local redTint = Color.new(1, 0, 0, 1)
|
||||||
|
|
||||||
local deps = {
|
local deps = {
|
||||||
utils = utils,
|
utils = utils,
|
||||||
Color = Color,
|
Color = Color,
|
||||||
@@ -327,7 +331,7 @@ function TestImageTiling:testElementImageTintProperty()
|
|||||||
ImageRenderer = ImageRenderer,
|
ImageRenderer = ImageRenderer,
|
||||||
ErrorHandler = ErrorHandler,
|
ErrorHandler = ErrorHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
local element = Element.new({
|
local element = Element.new({
|
||||||
width = 200,
|
width = 200,
|
||||||
height = 200,
|
height = 200,
|
||||||
@@ -346,7 +350,7 @@ function TestImageTiling:testElementSetImageTint()
|
|||||||
local Renderer = require("modules.Renderer")
|
local Renderer = require("modules.Renderer")
|
||||||
local EventHandler = require("modules.EventHandler")
|
local EventHandler = require("modules.EventHandler")
|
||||||
local ImageCache = require("modules.ImageCache")
|
local ImageCache = require("modules.ImageCache")
|
||||||
|
|
||||||
local deps = {
|
local deps = {
|
||||||
utils = utils,
|
utils = utils,
|
||||||
Color = Color,
|
Color = Color,
|
||||||
@@ -358,7 +362,7 @@ function TestImageTiling:testElementSetImageTint()
|
|||||||
ImageRenderer = ImageRenderer,
|
ImageRenderer = ImageRenderer,
|
||||||
ErrorHandler = ErrorHandler,
|
ErrorHandler = ErrorHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
local element = Element.new({
|
local element = Element.new({
|
||||||
width = 200,
|
width = 200,
|
||||||
height = 200,
|
height = 200,
|
||||||
@@ -379,7 +383,7 @@ function TestImageTiling:testElementSetImageOpacity()
|
|||||||
local Renderer = require("modules.Renderer")
|
local Renderer = require("modules.Renderer")
|
||||||
local EventHandler = require("modules.EventHandler")
|
local EventHandler = require("modules.EventHandler")
|
||||||
local ImageCache = require("modules.ImageCache")
|
local ImageCache = require("modules.ImageCache")
|
||||||
|
|
||||||
local deps = {
|
local deps = {
|
||||||
utils = utils,
|
utils = utils,
|
||||||
Color = Color,
|
Color = Color,
|
||||||
@@ -391,7 +395,7 @@ function TestImageTiling:testElementSetImageOpacity()
|
|||||||
ImageRenderer = ImageRenderer,
|
ImageRenderer = ImageRenderer,
|
||||||
ErrorHandler = ErrorHandler,
|
ErrorHandler = ErrorHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
local element = Element.new({
|
local element = Element.new({
|
||||||
width = 200,
|
width = 200,
|
||||||
height = 200,
|
height = 200,
|
||||||
@@ -401,5 +405,6 @@ function TestImageTiling:testElementSetImageOpacity()
|
|||||||
luaunit.assertEquals(element.imageOpacity, 0.7)
|
luaunit.assertEquals(element.imageOpacity, 0.7)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Run the tests
|
if not _G.RUNNING_ALL_TESTS then
|
||||||
os.exit(luaunit.LuaUnit.run())
|
os.exit(luaunit.LuaUnit.run())
|
||||||
|
end
|
||||||
|
|||||||
@@ -23,18 +23,18 @@ end
|
|||||||
|
|
||||||
function TestPerformanceInstrumentation:testTimerStartStop()
|
function TestPerformanceInstrumentation:testTimerStartStop()
|
||||||
Performance.startTimer("test_operation")
|
Performance.startTimer("test_operation")
|
||||||
|
|
||||||
-- Simulate some work
|
-- Simulate some work
|
||||||
local sum = 0
|
local sum = 0
|
||||||
for i = 1, 1000 do
|
for i = 1, 1000 do
|
||||||
sum = sum + i
|
sum = sum + i
|
||||||
end
|
end
|
||||||
|
|
||||||
local elapsed = Performance.stopTimer("test_operation")
|
local elapsed = Performance.stopTimer("test_operation")
|
||||||
|
|
||||||
luaunit.assertNotNil(elapsed)
|
luaunit.assertNotNil(elapsed)
|
||||||
luaunit.assertTrue(elapsed >= 0)
|
luaunit.assertTrue(elapsed >= 0)
|
||||||
|
|
||||||
local metrics = Performance.getMetrics()
|
local metrics = Performance.getMetrics()
|
||||||
luaunit.assertNotNil(metrics.timings["test_operation"])
|
luaunit.assertNotNil(metrics.timings["test_operation"])
|
||||||
luaunit.assertEquals(metrics.timings["test_operation"].count, 1)
|
luaunit.assertEquals(metrics.timings["test_operation"].count, 1)
|
||||||
@@ -44,13 +44,15 @@ function TestPerformanceInstrumentation:testMultipleTimers()
|
|||||||
-- Start multiple timers
|
-- Start multiple timers
|
||||||
Performance.startTimer("layout")
|
Performance.startTimer("layout")
|
||||||
Performance.startTimer("render")
|
Performance.startTimer("render")
|
||||||
|
|
||||||
local sum = 0
|
local sum = 0
|
||||||
for i = 1, 100 do sum = sum + i end
|
for i = 1, 100 do
|
||||||
|
sum = sum + i
|
||||||
|
end
|
||||||
|
|
||||||
Performance.stopTimer("layout")
|
Performance.stopTimer("layout")
|
||||||
Performance.stopTimer("render")
|
Performance.stopTimer("render")
|
||||||
|
|
||||||
local metrics = Performance.getMetrics()
|
local metrics = Performance.getMetrics()
|
||||||
luaunit.assertNotNil(metrics.timings["layout"])
|
luaunit.assertNotNil(metrics.timings["layout"])
|
||||||
luaunit.assertNotNil(metrics.timings["render"])
|
luaunit.assertNotNil(metrics.timings["render"])
|
||||||
@@ -58,15 +60,15 @@ end
|
|||||||
|
|
||||||
function TestPerformanceInstrumentation:testFrameTiming()
|
function TestPerformanceInstrumentation:testFrameTiming()
|
||||||
Performance.startFrame()
|
Performance.startFrame()
|
||||||
|
|
||||||
-- Simulate frame work
|
-- Simulate frame work
|
||||||
local sum = 0
|
local sum = 0
|
||||||
for i = 1, 1000 do
|
for i = 1, 1000 do
|
||||||
sum = sum + i
|
sum = sum + i
|
||||||
end
|
end
|
||||||
|
|
||||||
Performance.endFrame()
|
Performance.endFrame()
|
||||||
|
|
||||||
local frameMetrics = Performance.getFrameMetrics()
|
local frameMetrics = Performance.getFrameMetrics()
|
||||||
luaunit.assertNotNil(frameMetrics)
|
luaunit.assertNotNil(frameMetrics)
|
||||||
luaunit.assertEquals(frameMetrics.frameCount, 1)
|
luaunit.assertEquals(frameMetrics.frameCount, 1)
|
||||||
@@ -77,10 +79,10 @@ function TestPerformanceInstrumentation:testDrawCallCounting()
|
|||||||
Performance.incrementCounter("draw_calls", 1)
|
Performance.incrementCounter("draw_calls", 1)
|
||||||
Performance.incrementCounter("draw_calls", 1)
|
Performance.incrementCounter("draw_calls", 1)
|
||||||
Performance.incrementCounter("draw_calls", 1)
|
Performance.incrementCounter("draw_calls", 1)
|
||||||
|
|
||||||
local counter = Performance.getFrameCounter("draw_calls")
|
local counter = Performance.getFrameCounter("draw_calls")
|
||||||
luaunit.assertEquals(counter, 3)
|
luaunit.assertEquals(counter, 3)
|
||||||
|
|
||||||
-- Reset and check
|
-- Reset and check
|
||||||
Performance.resetFrameCounters()
|
Performance.resetFrameCounters()
|
||||||
counter = Performance.getFrameCounter("draw_calls")
|
counter = Performance.getFrameCounter("draw_calls")
|
||||||
@@ -89,10 +91,10 @@ end
|
|||||||
|
|
||||||
function TestPerformanceInstrumentation:testHUDToggle()
|
function TestPerformanceInstrumentation:testHUDToggle()
|
||||||
luaunit.assertFalse(Performance.getConfig().hudEnabled)
|
luaunit.assertFalse(Performance.getConfig().hudEnabled)
|
||||||
|
|
||||||
Performance.toggleHUD()
|
Performance.toggleHUD()
|
||||||
luaunit.assertTrue(Performance.getConfig().hudEnabled)
|
luaunit.assertTrue(Performance.getConfig().hudEnabled)
|
||||||
|
|
||||||
Performance.toggleHUD()
|
Performance.toggleHUD()
|
||||||
luaunit.assertFalse(Performance.getConfig().hudEnabled)
|
luaunit.assertFalse(Performance.getConfig().hudEnabled)
|
||||||
end
|
end
|
||||||
@@ -100,10 +102,10 @@ end
|
|||||||
function TestPerformanceInstrumentation:testEnableDisable()
|
function TestPerformanceInstrumentation:testEnableDisable()
|
||||||
Performance.enable()
|
Performance.enable()
|
||||||
luaunit.assertTrue(Performance.isEnabled())
|
luaunit.assertTrue(Performance.isEnabled())
|
||||||
|
|
||||||
Performance.disable()
|
Performance.disable()
|
||||||
luaunit.assertFalse(Performance.isEnabled())
|
luaunit.assertFalse(Performance.isEnabled())
|
||||||
|
|
||||||
-- Timers should not record when disabled
|
-- Timers should not record when disabled
|
||||||
Performance.startTimer("disabled_test")
|
Performance.startTimer("disabled_test")
|
||||||
local elapsed = Performance.stopTimer("disabled_test")
|
local elapsed = Performance.stopTimer("disabled_test")
|
||||||
@@ -118,12 +120,12 @@ function TestPerformanceInstrumentation:testMeasureFunction()
|
|||||||
end
|
end
|
||||||
return sum
|
return sum
|
||||||
end
|
end
|
||||||
|
|
||||||
local wrapped = Performance.measure("expensive_op", expensiveOperation)
|
local wrapped = Performance.measure("expensive_op", expensiveOperation)
|
||||||
local result = wrapped(1000)
|
local result = wrapped(1000)
|
||||||
|
|
||||||
luaunit.assertEquals(result, 500500) -- sum of 1 to 1000
|
luaunit.assertEquals(result, 500500) -- sum of 1 to 1000
|
||||||
|
|
||||||
local metrics = Performance.getMetrics()
|
local metrics = Performance.getMetrics()
|
||||||
luaunit.assertNotNil(metrics.timings["expensive_op"])
|
luaunit.assertNotNil(metrics.timings["expensive_op"])
|
||||||
luaunit.assertEquals(metrics.timings["expensive_op"].count, 1)
|
luaunit.assertEquals(metrics.timings["expensive_op"].count, 1)
|
||||||
@@ -131,7 +133,7 @@ end
|
|||||||
|
|
||||||
function TestPerformanceInstrumentation:testMemoryTracking()
|
function TestPerformanceInstrumentation:testMemoryTracking()
|
||||||
Performance.updateMemory()
|
Performance.updateMemory()
|
||||||
|
|
||||||
local memMetrics = Performance.getMemoryMetrics()
|
local memMetrics = Performance.getMemoryMetrics()
|
||||||
luaunit.assertNotNil(memMetrics)
|
luaunit.assertNotNil(memMetrics)
|
||||||
luaunit.assertTrue(memMetrics.currentKb > 0)
|
luaunit.assertTrue(memMetrics.currentKb > 0)
|
||||||
@@ -142,7 +144,7 @@ end
|
|||||||
function TestPerformanceInstrumentation:testExportJSON()
|
function TestPerformanceInstrumentation:testExportJSON()
|
||||||
Performance.startTimer("test_op")
|
Performance.startTimer("test_op")
|
||||||
Performance.stopTimer("test_op")
|
Performance.stopTimer("test_op")
|
||||||
|
|
||||||
local json = Performance.exportJSON()
|
local json = Performance.exportJSON()
|
||||||
luaunit.assertNotNil(json)
|
luaunit.assertNotNil(json)
|
||||||
luaunit.assertTrue(string.find(json, "fps") ~= nil)
|
luaunit.assertTrue(string.find(json, "fps") ~= nil)
|
||||||
@@ -152,16 +154,13 @@ end
|
|||||||
function TestPerformanceInstrumentation:testExportCSV()
|
function TestPerformanceInstrumentation:testExportCSV()
|
||||||
Performance.startTimer("test_op")
|
Performance.startTimer("test_op")
|
||||||
Performance.stopTimer("test_op")
|
Performance.stopTimer("test_op")
|
||||||
|
|
||||||
local csv = Performance.exportCSV()
|
local csv = Performance.exportCSV()
|
||||||
luaunit.assertNotNil(csv)
|
luaunit.assertNotNil(csv)
|
||||||
luaunit.assertTrue(string.find(csv, "Name,Average") ~= nil)
|
luaunit.assertTrue(string.find(csv, "Name,Average") ~= nil)
|
||||||
luaunit.assertTrue(string.find(csv, "test_op") ~= nil)
|
luaunit.assertTrue(string.find(csv, "test_op") ~= nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Run tests if executed directly
|
if not _G.RUNNING_ALL_TESTS then
|
||||||
if arg and arg[0]:find("performance_instrumentation_test%.lua$") then
|
|
||||||
os.exit(luaunit.LuaUnit.run())
|
os.exit(luaunit.LuaUnit.run())
|
||||||
end
|
end
|
||||||
|
|
||||||
return TestPerformanceInstrumentation
|
|
||||||
|
|||||||
@@ -153,4 +153,6 @@ function TestPerformanceWarnings:testLayoutRecalculationTracking()
|
|||||||
luaunit.assertNotNil(root)
|
luaunit.assertNotNil(root)
|
||||||
end
|
end
|
||||||
|
|
||||||
return TestPerformanceWarnings
|
if not _G.RUNNING_ALL_TESTS then
|
||||||
|
os.exit(luaunit.LuaUnit.run())
|
||||||
|
end
|
||||||
|
|||||||
@@ -243,8 +243,8 @@ function TestTransform:testClone_AllProperties()
|
|||||||
luaunit.assertAlmostEquals(clone.originX, 0.25, 0.01)
|
luaunit.assertAlmostEquals(clone.originX, 0.25, 0.01)
|
||||||
luaunit.assertAlmostEquals(clone.originY, 0.75, 0.01)
|
luaunit.assertAlmostEquals(clone.originY, 0.75, 0.01)
|
||||||
|
|
||||||
-- Ensure it's a different object
|
-- Ensure it's a different object (use raw comparison)
|
||||||
luaunit.assertNotEquals(clone, original)
|
luaunit.assertFalse(rawequal(clone, original), "Clone should be a different table instance")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTransform:testClone_Nil()
|
function TestTransform:testClone_Nil()
|
||||||
@@ -289,4 +289,6 @@ function TestTransform:testTransformAnimation()
|
|||||||
luaunit.assertAlmostEquals(result.transform.scaleX, 1.5, 0.01)
|
luaunit.assertAlmostEquals(result.transform.scaleX, 1.5, 0.01)
|
||||||
end
|
end
|
||||||
|
|
||||||
os.exit(luaunit.LuaUnit.run())
|
if not _G.RUNNING_ALL_TESTS then
|
||||||
|
os.exit(luaunit.LuaUnit.run())
|
||||||
|
end
|
||||||
|
|||||||
@@ -105,6 +105,9 @@ function love_helper.graphics.newCanvas(width, height)
|
|||||||
getDimensions = function()
|
getDimensions = function()
|
||||||
return width or mockWindowWidth, height or mockWindowHeight
|
return width or mockWindowWidth, height or mockWindowHeight
|
||||||
end,
|
end,
|
||||||
|
release = function()
|
||||||
|
-- Mock canvas release
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user