test alignment
This commit is contained in:
210
FlexLove.lua
210
FlexLove.lua
@@ -287,18 +287,30 @@ end
|
||||
function Color.fromHex(hexWithTag)
|
||||
local hex = hexWithTag:gsub("#", "")
|
||||
if #hex == 6 then
|
||||
local r = tonumber("0x" .. hex:sub(1, 2)) or 0
|
||||
local g = tonumber("0x" .. hex:sub(3, 4)) or 0
|
||||
local b = tonumber("0x" .. hex:sub(5, 6)) or 0
|
||||
local r = tonumber("0x" .. hex:sub(1, 2))
|
||||
local g = tonumber("0x" .. hex:sub(3, 4))
|
||||
local b = tonumber("0x" .. hex:sub(5, 6))
|
||||
if not r or not g or not b then
|
||||
error(
|
||||
formatError("Color", string.format("Invalid hex string format: '%s'. Contains invalid hex digits", hexWithTag))
|
||||
)
|
||||
end
|
||||
return Color.new(r, g, b, 1)
|
||||
elseif #hex == 8 then
|
||||
local r = tonumber("0x" .. hex:sub(1, 2)) or 0
|
||||
local g = tonumber("0x" .. hex:sub(3, 4)) or 0
|
||||
local b = tonumber("0x" .. hex:sub(5, 6)) or 0
|
||||
local a = tonumber("0x" .. hex:sub(7, 8)) / 255
|
||||
return Color.new(r, g, b, a)
|
||||
local r = tonumber("0x" .. hex:sub(1, 2))
|
||||
local g = tonumber("0x" .. hex:sub(3, 4))
|
||||
local b = tonumber("0x" .. hex:sub(5, 6))
|
||||
local a = tonumber("0x" .. hex:sub(7, 8))
|
||||
if not r or not g or not b or not a then
|
||||
error(
|
||||
formatError("Color", string.format("Invalid hex string format: '%s'. Contains invalid hex digits", hexWithTag))
|
||||
)
|
||||
end
|
||||
return Color.new(r, g, b, a / 255)
|
||||
else
|
||||
error(formatError("Color", string.format("Invalid hex string format: '%s'. Expected #RRGGBB or #RRGGBBAA", hexWithTag)))
|
||||
error(
|
||||
formatError("Color", string.format("Invalid hex string format: '%s'. Expected #RRGGBB or #RRGGBBAA", hexWithTag))
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -626,6 +638,43 @@ function Theme.getRegisteredThemes()
|
||||
return themeNames
|
||||
end
|
||||
|
||||
--- Get all available color names from the active theme
|
||||
---@return table<string>|nil -- Array of color names, or nil if no theme active
|
||||
function Theme.getColorNames()
|
||||
if not activeTheme or not activeTheme.colors then
|
||||
return nil
|
||||
end
|
||||
|
||||
local colorNames = {}
|
||||
for name, _ in pairs(activeTheme.colors) do
|
||||
table.insert(colorNames, name)
|
||||
end
|
||||
return colorNames
|
||||
end
|
||||
|
||||
--- Get all colors from the active theme
|
||||
---@return table<string, Color>|nil -- Table of all colors, or nil if no theme active
|
||||
function Theme.getAllColors()
|
||||
if not activeTheme then
|
||||
return nil
|
||||
end
|
||||
|
||||
return activeTheme.colors
|
||||
end
|
||||
|
||||
--- Get a color with a fallback if not found
|
||||
---@param colorName string -- Name of the color to retrieve
|
||||
---@param fallback Color|nil -- Fallback color if not found (default: white)
|
||||
---@return Color -- The color or fallback
|
||||
function Theme.getColorOrDefault(colorName, fallback)
|
||||
local color = Theme.getColor(colorName)
|
||||
if color then
|
||||
return color
|
||||
end
|
||||
|
||||
return fallback or Color.new(1, 1, 1, 1)
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Rounded Rectangle Helper
|
||||
-- ====================
|
||||
@@ -1269,7 +1318,7 @@ local Gui = {
|
||||
baseScale = nil,
|
||||
scaleFactors = { x = 1.0, y = 1.0 },
|
||||
defaultTheme = nil,
|
||||
_cachedViewport = { width = 0, height = 0 }, -- Cached viewport dimensions
|
||||
_cachedViewport = { width = 0, height = 0 }, -- Cached viewport dimensions
|
||||
}
|
||||
|
||||
--- Initialize FlexLove with configuration
|
||||
@@ -1474,15 +1523,23 @@ end
|
||||
---@field transition table?
|
||||
--- Easing functions for animations
|
||||
local Easing = {
|
||||
linear = function(t) return t end,
|
||||
linear = function(t)
|
||||
return t
|
||||
end,
|
||||
|
||||
easeInQuad = function(t) return t * t end,
|
||||
easeOutQuad = function(t) return t * (2 - t) end,
|
||||
easeInQuad = function(t)
|
||||
return t * t
|
||||
end,
|
||||
easeOutQuad = function(t)
|
||||
return t * (2 - t)
|
||||
end,
|
||||
easeInOutQuad = function(t)
|
||||
return t < 0.5 and 2 * t * t or -1 + (4 - 2 * t) * t
|
||||
end,
|
||||
|
||||
easeInCubic = function(t) return t * t * t end,
|
||||
easeInCubic = function(t)
|
||||
return t * t * t
|
||||
end,
|
||||
easeOutCubic = function(t)
|
||||
local t1 = t - 1
|
||||
return t1 * t1 * t1 + 1
|
||||
@@ -1491,7 +1548,9 @@ local Easing = {
|
||||
return t < 0.5 and 4 * t * t * t or (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
|
||||
end,
|
||||
|
||||
easeInQuart = function(t) return t * t * t * t end,
|
||||
easeInQuart = function(t)
|
||||
return t * t * t * t
|
||||
end,
|
||||
easeOutQuart = function(t)
|
||||
local t1 = t - 1
|
||||
return 1 - t1 * t1 * t1 * t1
|
||||
@@ -1552,7 +1611,7 @@ end
|
||||
---@return boolean
|
||||
function Animation:update(dt)
|
||||
self.elapsed = self.elapsed + dt
|
||||
self._resultDirty = true -- Mark cached result as dirty
|
||||
self._resultDirty = true -- Mark cached result as dirty
|
||||
if self.elapsed >= self.duration then
|
||||
return true -- finished
|
||||
else
|
||||
@@ -1568,8 +1627,8 @@ function Animation:interpolate()
|
||||
end
|
||||
|
||||
local t = math.min(self.elapsed / self.duration, 1)
|
||||
t = self.easing(t) -- Apply easing function
|
||||
local result = self._cachedResult -- Reuse existing table
|
||||
t = self.easing(t) -- Apply easing function
|
||||
local result = self._cachedResult -- Reuse existing table
|
||||
|
||||
-- Clear previous values
|
||||
result.width = nil
|
||||
@@ -1597,7 +1656,7 @@ function Animation:interpolate()
|
||||
end
|
||||
end
|
||||
|
||||
self._resultDirty = false -- Mark as clean
|
||||
self._resultDirty = false -- Mark as clean
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -1643,8 +1702,8 @@ function Animation.scale(duration, fromScale, toScale)
|
||||
end
|
||||
|
||||
local FONT_CACHE = {}
|
||||
local FONT_CACHE_MAX_SIZE = 50 -- Limit cache size to prevent unbounded growth
|
||||
local FONT_CACHE_ORDER = {} -- Track access order for LRU eviction
|
||||
local FONT_CACHE_MAX_SIZE = 50 -- Limit cache size to prevent unbounded growth
|
||||
local FONT_CACHE_ORDER = {} -- Track access order for LRU eviction
|
||||
|
||||
--- Create or get a font from cache
|
||||
---@param size number
|
||||
@@ -2159,7 +2218,11 @@ function Element.new(props)
|
||||
-- First, resolve padding using temporary dimensions
|
||||
-- For auto-sized elements, this is content width; for explicit sizing, this is border-box width
|
||||
local tempPadding = Units.resolveSpacing(props.padding, self.width, self.height)
|
||||
self.margin = Units.resolveSpacing(props.margin, self.width, self.height)
|
||||
|
||||
-- Margin percentages are relative to parent's dimensions (CSS spec)
|
||||
local parentWidth = self.parent and self.parent.width or viewportWidth
|
||||
local parentHeight = self.parent and self.parent.height or viewportHeight
|
||||
self.margin = Units.resolveSpacing(props.margin, parentWidth, parentHeight)
|
||||
|
||||
-- For auto-sized elements, add padding to get border-box dimensions
|
||||
if self.autosizing.width then
|
||||
@@ -2878,16 +2941,13 @@ function Element:layoutChildren()
|
||||
elseif self.justifyContent == JustifyContent.SPACE_BETWEEN then
|
||||
startPos = 0
|
||||
if #line > 1 then
|
||||
-- Gap already accounted for in freeSpace calculation
|
||||
itemSpacing = self.gap + (freeSpace / (#line - 1))
|
||||
end
|
||||
elseif self.justifyContent == JustifyContent.SPACE_AROUND then
|
||||
-- Gap already accounted for in freeSpace calculation
|
||||
local spaceAroundEach = freeSpace / #line
|
||||
startPos = spaceAroundEach / 2
|
||||
itemSpacing = self.gap + spaceAroundEach
|
||||
elseif self.justifyContent == JustifyContent.SPACE_EVENLY then
|
||||
-- Gap already accounted for in freeSpace calculation
|
||||
local spaceBetween = freeSpace / (#line + 1)
|
||||
startPos = spaceBetween
|
||||
itemSpacing = self.gap + spaceBetween
|
||||
@@ -2923,9 +2983,12 @@ function Element:layoutChildren()
|
||||
elseif effectiveAlign == AlignItems.FLEX_END then
|
||||
child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos + lineHeight - childBorderBoxHeight
|
||||
elseif effectiveAlign == AlignItems.STRETCH then
|
||||
-- STRETCH: Set border-box height to lineHeight, content area shrinks to fit
|
||||
child._borderBoxHeight = lineHeight
|
||||
child.height = math.max(0, lineHeight - child.padding.top - child.padding.bottom)
|
||||
-- STRETCH: Only apply if height was not explicitly set
|
||||
if child.autosizing and child.autosizing.height then
|
||||
-- STRETCH: Set border-box height to lineHeight, content area shrinks to fit
|
||||
child._borderBoxHeight = lineHeight
|
||||
child.height = math.max(0, lineHeight - child.padding.top - child.padding.bottom)
|
||||
end
|
||||
child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos
|
||||
end
|
||||
|
||||
@@ -2964,9 +3027,12 @@ function Element:layoutChildren()
|
||||
elseif effectiveAlign == AlignItems.FLEX_END then
|
||||
child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos + lineHeight - childBorderBoxWidth
|
||||
elseif effectiveAlign == AlignItems.STRETCH then
|
||||
-- STRETCH: Set border-box width to lineHeight, content area shrinks to fit
|
||||
child._borderBoxWidth = lineHeight
|
||||
child.width = math.max(0, lineHeight - child.padding.left - child.padding.right)
|
||||
-- STRETCH: Only apply if width was not explicitly set
|
||||
if child.autosizing and child.autosizing.width then
|
||||
-- STRETCH: Set border-box width to lineHeight, content area shrinks to fit
|
||||
child._borderBoxWidth = lineHeight
|
||||
child.width = math.max(0, lineHeight - child.padding.left - child.padding.right)
|
||||
end
|
||||
child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos
|
||||
end
|
||||
|
||||
@@ -3056,14 +3122,7 @@ function Element:draw()
|
||||
local backgroundWithOpacity =
|
||||
Color.new(drawBackgroundColor.r, drawBackgroundColor.g, drawBackgroundColor.b, drawBackgroundColor.a * self.opacity)
|
||||
love.graphics.setColor(backgroundWithOpacity:toRGBA())
|
||||
RoundedRect.draw(
|
||||
"fill",
|
||||
self.x,
|
||||
self.y,
|
||||
borderBoxWidth,
|
||||
borderBoxHeight,
|
||||
self.cornerRadius
|
||||
)
|
||||
RoundedRect.draw("fill", self.x, self.y, borderBoxWidth, borderBoxHeight, self.cornerRadius)
|
||||
|
||||
-- LAYER 2: Draw theme on top of backgroundColor (if theme exists)
|
||||
if self.themeComponent then
|
||||
@@ -3100,11 +3159,15 @@ function Element:draw()
|
||||
|
||||
if atlasToUse and component.regions then
|
||||
-- Validate component has required structure
|
||||
local hasAllRegions = component.regions.topLeft and component.regions.topCenter and
|
||||
component.regions.topRight and component.regions.middleLeft and
|
||||
component.regions.middleCenter and component.regions.middleRight and
|
||||
component.regions.bottomLeft and component.regions.bottomCenter and
|
||||
component.regions.bottomRight
|
||||
local hasAllRegions = component.regions.topLeft
|
||||
and component.regions.topCenter
|
||||
and component.regions.topRight
|
||||
and component.regions.middleLeft
|
||||
and component.regions.middleCenter
|
||||
and component.regions.middleRight
|
||||
and component.regions.bottomLeft
|
||||
and component.regions.bottomCenter
|
||||
and component.regions.bottomRight
|
||||
if hasAllRegions then
|
||||
NineSlice.draw(component, atlasToUse, self.x, self.y, self.width, self.height, self.padding, self.opacity)
|
||||
else
|
||||
@@ -3710,19 +3773,34 @@ function Element:calculateAutoWidth()
|
||||
return contentWidth
|
||||
end
|
||||
|
||||
-- For HORIZONTAL flex: sum children widths + gaps
|
||||
-- For VERTICAL flex: max of children widths
|
||||
local isHorizontal = self.flexDirection == "horizontal"
|
||||
local totalWidth = contentWidth
|
||||
local maxWidth = contentWidth
|
||||
local participatingChildren = 0
|
||||
|
||||
for _, child in ipairs(self.children) do
|
||||
-- Skip explicitly absolute positioned children as they don't affect parent auto-sizing
|
||||
if not child._explicitlyAbsolute then
|
||||
-- BORDER-BOX MODEL: Use border-box width for auto-sizing calculations
|
||||
local childBorderBoxWidth = child:getBorderBoxWidth()
|
||||
totalWidth = totalWidth + childBorderBoxWidth
|
||||
if isHorizontal then
|
||||
totalWidth = totalWidth + childBorderBoxWidth
|
||||
else
|
||||
maxWidth = math.max(maxWidth, childBorderBoxWidth)
|
||||
end
|
||||
participatingChildren = participatingChildren + 1
|
||||
end
|
||||
end
|
||||
|
||||
return totalWidth + (self.gap * participatingChildren)
|
||||
if isHorizontal then
|
||||
-- Add gaps between children (n-1 gaps for n children)
|
||||
local gapCount = math.max(0, participatingChildren - 1)
|
||||
return totalWidth + (self.gap * gapCount)
|
||||
else
|
||||
return maxWidth
|
||||
end
|
||||
end
|
||||
|
||||
--- Calculate auto height based on children
|
||||
@@ -3732,19 +3810,34 @@ function Element:calculateAutoHeight()
|
||||
return height
|
||||
end
|
||||
|
||||
-- For VERTICAL flex: sum children heights + gaps
|
||||
-- For HORIZONTAL flex: max of children heights
|
||||
local isVertical = self.flexDirection == "vertical"
|
||||
local totalHeight = height
|
||||
local maxHeight = height
|
||||
local participatingChildren = 0
|
||||
|
||||
for _, child in ipairs(self.children) do
|
||||
-- Skip explicitly absolute positioned children as they don't affect parent auto-sizing
|
||||
if not child._explicitlyAbsolute then
|
||||
-- BORDER-BOX MODEL: Use border-box height for auto-sizing calculations
|
||||
local childBorderBoxHeight = child:getBorderBoxHeight()
|
||||
totalHeight = totalHeight + childBorderBoxHeight
|
||||
if isVertical then
|
||||
totalHeight = totalHeight + childBorderBoxHeight
|
||||
else
|
||||
maxHeight = math.max(maxHeight, childBorderBoxHeight)
|
||||
end
|
||||
participatingChildren = participatingChildren + 1
|
||||
end
|
||||
end
|
||||
|
||||
return totalHeight + (self.gap * participatingChildren)
|
||||
if isVertical then
|
||||
-- Add gaps between children (n-1 gaps for n children)
|
||||
local gapCount = math.max(0, participatingChildren - 1)
|
||||
return totalHeight + (self.gap * gapCount)
|
||||
else
|
||||
return maxHeight
|
||||
end
|
||||
end
|
||||
|
||||
---@param newText string
|
||||
@@ -3769,4 +3862,23 @@ Gui.new = Element.new
|
||||
Gui.Element = Element
|
||||
Gui.Animation = Animation
|
||||
Gui.Theme = Theme
|
||||
return { GUI = Gui, Gui = Gui, Element = Element, Color = Color, Theme = Theme, Animation = Animation, enums = enums }
|
||||
|
||||
-- Export individual enums for convenience
|
||||
return {
|
||||
GUI = Gui,
|
||||
Gui = Gui,
|
||||
Element = Element,
|
||||
Color = Color,
|
||||
Theme = Theme,
|
||||
Animation = Animation,
|
||||
enums = enums,
|
||||
-- Export individual enums at top level
|
||||
Positioning = Positioning,
|
||||
FlexDirection = FlexDirection,
|
||||
JustifyContent = JustifyContent,
|
||||
AlignItems = AlignItems,
|
||||
AlignSelf = AlignSelf,
|
||||
AlignContent = AlignContent,
|
||||
FlexWrap = FlexWrap,
|
||||
TextAlign = TextAlign,
|
||||
}
|
||||
|
||||
170
examples/ThemeColorAccessDemo.lua
Normal file
170
examples/ThemeColorAccessDemo.lua
Normal file
@@ -0,0 +1,170 @@
|
||||
-- Theme Color Access Demo
|
||||
-- Demonstrates various ways to access and use theme colors
|
||||
|
||||
package.path = package.path .. ";./?.lua;../?.lua"
|
||||
|
||||
local FlexLove = require("FlexLove")
|
||||
local Theme = FlexLove.Theme
|
||||
local Gui = FlexLove.Gui
|
||||
local Color = FlexLove.Color
|
||||
|
||||
-- Initialize love stubs for testing
|
||||
love = {
|
||||
graphics = {
|
||||
newFont = function(size) return { getHeight = function() return size end } end,
|
||||
getFont = function() return { getHeight = function() return 12 end } end,
|
||||
getWidth = function() return 1920 end,
|
||||
getHeight = function() return 1080 end,
|
||||
newImage = function() return {} end,
|
||||
newQuad = function() return {} end,
|
||||
},
|
||||
}
|
||||
|
||||
print("=== Theme Color Access Demo ===\n")
|
||||
|
||||
-- Load and activate the space theme
|
||||
Theme.load("space")
|
||||
Theme.setActive("space")
|
||||
|
||||
print("1. Basic Color Access")
|
||||
print("---------------------")
|
||||
|
||||
-- Method 1: Using Theme.getColor() (Recommended)
|
||||
local primaryColor = Theme.getColor("primary")
|
||||
local secondaryColor = Theme.getColor("secondary")
|
||||
local textColor = Theme.getColor("text")
|
||||
local textDarkColor = Theme.getColor("textDark")
|
||||
|
||||
print(string.format("Primary: r=%.2f, g=%.2f, b=%.2f", primaryColor.r, primaryColor.g, primaryColor.b))
|
||||
print(string.format("Secondary: r=%.2f, g=%.2f, b=%.2f", secondaryColor.r, secondaryColor.g, secondaryColor.b))
|
||||
print(string.format("Text: r=%.2f, g=%.2f, b=%.2f", textColor.r, textColor.g, textColor.b))
|
||||
print(string.format("Text Dark: r=%.2f, g=%.2f, b=%.2f", textDarkColor.r, textDarkColor.g, textDarkColor.b))
|
||||
|
||||
print("\n2. Get All Available Colors")
|
||||
print("----------------------------")
|
||||
|
||||
-- Method 2: Get all color names
|
||||
local colorNames = Theme.getColorNames()
|
||||
if colorNames then
|
||||
print("Available colors in theme:")
|
||||
for _, name in ipairs(colorNames) do
|
||||
print(" - " .. name)
|
||||
end
|
||||
end
|
||||
|
||||
print("\n3. Get All Colors at Once")
|
||||
print("-------------------------")
|
||||
|
||||
-- Method 3: Get all colors as a table
|
||||
local allColors = Theme.getAllColors()
|
||||
if allColors then
|
||||
print("All colors:")
|
||||
for name, color in pairs(allColors) do
|
||||
print(string.format(" %s: r=%.2f, g=%.2f, b=%.2f, a=%.2f", name, color.r, color.g, color.b, color.a))
|
||||
end
|
||||
end
|
||||
|
||||
print("\n4. Safe Color Access with Fallback")
|
||||
print("-----------------------------------")
|
||||
|
||||
-- Method 4: Get color with fallback
|
||||
local accentColor = Theme.getColorOrDefault("accent", Color.new(1, 0, 0, 1)) -- Falls back to red
|
||||
local primaryColor2 = Theme.getColorOrDefault("primary", Color.new(1, 0, 0, 1)) -- Uses theme color
|
||||
|
||||
print(string.format("Accent (fallback): r=%.2f, g=%.2f, b=%.2f", accentColor.r, accentColor.g, accentColor.b))
|
||||
print(string.format("Primary (theme): r=%.2f, g=%.2f, b=%.2f", primaryColor2.r, primaryColor2.g, primaryColor2.b))
|
||||
|
||||
print("\n5. Using Colors in GUI Elements")
|
||||
print("--------------------------------")
|
||||
|
||||
-- Create a container with theme colors
|
||||
local container = Gui.new({
|
||||
width = 400,
|
||||
height = 300,
|
||||
backgroundColor = Theme.getColor("secondary"),
|
||||
positioning = FlexLove.enums.Positioning.FLEX,
|
||||
flexDirection = FlexLove.enums.FlexDirection.VERTICAL,
|
||||
gap = 10,
|
||||
padding = { top = 20, right = 20, bottom = 20, left = 20 },
|
||||
})
|
||||
|
||||
-- Create a button with primary color
|
||||
local button = Gui.new({
|
||||
parent = container,
|
||||
width = 360,
|
||||
height = 50,
|
||||
backgroundColor = Theme.getColor("primary"),
|
||||
textColor = Theme.getColor("text"),
|
||||
text = "Click Me!",
|
||||
textSize = 18,
|
||||
})
|
||||
|
||||
-- Create a text label with dark text
|
||||
local label = Gui.new({
|
||||
parent = container,
|
||||
width = 360,
|
||||
height = 30,
|
||||
backgroundColor = Theme.getColorOrDefault("background", Color.new(0.2, 0.2, 0.2, 1)),
|
||||
textColor = Theme.getColor("textDark"),
|
||||
text = "This is a label with dark text",
|
||||
textSize = 14,
|
||||
})
|
||||
|
||||
print("Created GUI elements with theme colors:")
|
||||
print(string.format(" Container: %d children", #container.children))
|
||||
print(string.format(" Button background: r=%.2f, g=%.2f, b=%.2f", button.backgroundColor.r, button.backgroundColor.g, button.backgroundColor.b))
|
||||
print(string.format(" Label text color: r=%.2f, g=%.2f, b=%.2f", label.textColor.r, label.textColor.g, label.textColor.b))
|
||||
|
||||
print("\n6. Creating Color Variations")
|
||||
print("-----------------------------")
|
||||
|
||||
-- Create variations of theme colors
|
||||
local primaryDark = Color.new(
|
||||
primaryColor.r * 0.7,
|
||||
primaryColor.g * 0.7,
|
||||
primaryColor.b * 0.7,
|
||||
primaryColor.a
|
||||
)
|
||||
|
||||
local primaryLight = Color.new(
|
||||
math.min(1, primaryColor.r * 1.3),
|
||||
math.min(1, primaryColor.g * 1.3),
|
||||
math.min(1, primaryColor.b * 1.3),
|
||||
primaryColor.a
|
||||
)
|
||||
|
||||
local primaryTransparent = Color.new(
|
||||
primaryColor.r,
|
||||
primaryColor.g,
|
||||
primaryColor.b,
|
||||
0.5
|
||||
)
|
||||
|
||||
print(string.format("Primary (original): r=%.2f, g=%.2f, b=%.2f, a=%.2f", primaryColor.r, primaryColor.g, primaryColor.b, primaryColor.a))
|
||||
print(string.format("Primary (dark): r=%.2f, g=%.2f, b=%.2f, a=%.2f", primaryDark.r, primaryDark.g, primaryDark.b, primaryDark.a))
|
||||
print(string.format("Primary (light): r=%.2f, g=%.2f, b=%.2f, a=%.2f", primaryLight.r, primaryLight.g, primaryLight.b, primaryLight.a))
|
||||
print(string.format("Primary (50%% alpha): r=%.2f, g=%.2f, b=%.2f, a=%.2f", primaryTransparent.r, primaryTransparent.g, primaryTransparent.b, primaryTransparent.a))
|
||||
|
||||
print("\n7. Quick Reference")
|
||||
print("------------------")
|
||||
print([[
|
||||
// Basic usage:
|
||||
local color = Theme.getColor("primary")
|
||||
|
||||
// With fallback:
|
||||
local color = Theme.getColorOrDefault("accent", Color.new(1, 0, 0, 1))
|
||||
|
||||
// Get all colors:
|
||||
local colors = Theme.getAllColors()
|
||||
|
||||
// Get color names:
|
||||
local names = Theme.getColorNames()
|
||||
|
||||
// Use in elements:
|
||||
local button = Gui.new({
|
||||
backgroundColor = Theme.getColor("primary"),
|
||||
textColor = Theme.getColor("text"),
|
||||
})
|
||||
]])
|
||||
|
||||
print("\n=== Demo Complete ===")
|
||||
181
examples/ThemeColorAccessSimple.lua
Normal file
181
examples/ThemeColorAccessSimple.lua
Normal file
@@ -0,0 +1,181 @@
|
||||
-- Simple Theme Color Access Demo
|
||||
-- Shows how to access theme colors without creating GUI elements
|
||||
|
||||
package.path = package.path .. ";./?.lua;../?.lua"
|
||||
|
||||
local FlexLove = require("FlexLove")
|
||||
local Theme = FlexLove.Theme
|
||||
local Color = FlexLove.Color
|
||||
|
||||
-- Initialize minimal love stubs
|
||||
love = {
|
||||
graphics = {
|
||||
newFont = function(size) return { getHeight = function() return size end } end,
|
||||
newImage = function() return {} end,
|
||||
newQuad = function() return {} end,
|
||||
},
|
||||
}
|
||||
|
||||
print("=== Theme Color Access - Simple Demo ===\n")
|
||||
|
||||
-- Load and activate the space theme
|
||||
Theme.load("space")
|
||||
Theme.setActive("space")
|
||||
|
||||
print("✓ Theme 'space' loaded and activated\n")
|
||||
|
||||
-- ============================================
|
||||
-- METHOD 1: Basic Color Access (Recommended)
|
||||
-- ============================================
|
||||
print("METHOD 1: Theme.getColor(colorName)")
|
||||
print("------------------------------------")
|
||||
|
||||
local primaryColor = Theme.getColor("primary")
|
||||
local secondaryColor = Theme.getColor("secondary")
|
||||
local textColor = Theme.getColor("text")
|
||||
local textDarkColor = Theme.getColor("textDark")
|
||||
|
||||
print(string.format("primary = Color(r=%.2f, g=%.2f, b=%.2f, a=%.2f)",
|
||||
primaryColor.r, primaryColor.g, primaryColor.b, primaryColor.a))
|
||||
print(string.format("secondary = Color(r=%.2f, g=%.2f, b=%.2f, a=%.2f)",
|
||||
secondaryColor.r, secondaryColor.g, secondaryColor.b, secondaryColor.a))
|
||||
print(string.format("text = Color(r=%.2f, g=%.2f, b=%.2f, a=%.2f)",
|
||||
textColor.r, textColor.g, textColor.b, textColor.a))
|
||||
print(string.format("textDark = Color(r=%.2f, g=%.2f, b=%.2f, a=%.2f)",
|
||||
textDarkColor.r, textDarkColor.g, textDarkColor.b, textDarkColor.a))
|
||||
|
||||
-- ============================================
|
||||
-- METHOD 2: Get All Color Names
|
||||
-- ============================================
|
||||
print("\nMETHOD 2: Theme.getColorNames()")
|
||||
print("--------------------------------")
|
||||
|
||||
local colorNames = Theme.getColorNames()
|
||||
print("Available colors:")
|
||||
for i, name in ipairs(colorNames) do
|
||||
print(string.format(" %d. %s", i, name))
|
||||
end
|
||||
|
||||
-- ============================================
|
||||
-- METHOD 3: Get All Colors at Once
|
||||
-- ============================================
|
||||
print("\nMETHOD 3: Theme.getAllColors()")
|
||||
print("-------------------------------")
|
||||
|
||||
local allColors = Theme.getAllColors()
|
||||
print("All colors with values:")
|
||||
for name, color in pairs(allColors) do
|
||||
print(string.format(" %-10s = (%.2f, %.2f, %.2f, %.2f)",
|
||||
name, color.r, color.g, color.b, color.a))
|
||||
end
|
||||
|
||||
-- ============================================
|
||||
-- METHOD 4: Safe Access with Fallback
|
||||
-- ============================================
|
||||
print("\nMETHOD 4: Theme.getColorOrDefault(colorName, fallback)")
|
||||
print("-------------------------------------------------------")
|
||||
|
||||
-- Try to get a color that exists
|
||||
local existingColor = Theme.getColorOrDefault("primary", Color.new(1, 0, 0, 1))
|
||||
print(string.format("Existing color 'primary': (%.2f, %.2f, %.2f) ✓",
|
||||
existingColor.r, existingColor.g, existingColor.b))
|
||||
|
||||
-- Try to get a color that doesn't exist (will use fallback)
|
||||
local missingColor = Theme.getColorOrDefault("accent", Color.new(1, 0, 0, 1))
|
||||
print(string.format("Missing color 'accent' (fallback): (%.2f, %.2f, %.2f) ✓",
|
||||
missingColor.r, missingColor.g, missingColor.b))
|
||||
|
||||
-- ============================================
|
||||
-- PRACTICAL EXAMPLES
|
||||
-- ============================================
|
||||
print("\n=== Practical Usage Examples ===\n")
|
||||
|
||||
print("Example 1: Using colors in element creation")
|
||||
print("--------------------------------------------")
|
||||
print([[
|
||||
local button = Gui.new({
|
||||
width = 200,
|
||||
height = 50,
|
||||
backgroundColor = Theme.getColor("primary"),
|
||||
textColor = Theme.getColor("text"),
|
||||
text = "Click Me!"
|
||||
})
|
||||
]])
|
||||
|
||||
print("\nExample 2: Creating color variations")
|
||||
print("-------------------------------------")
|
||||
print([[
|
||||
local primary = Theme.getColor("primary")
|
||||
|
||||
-- Darker version (70% brightness)
|
||||
local primaryDark = Color.new(
|
||||
primary.r * 0.7,
|
||||
primary.g * 0.7,
|
||||
primary.b * 0.7,
|
||||
primary.a
|
||||
)
|
||||
|
||||
-- Lighter version (130% brightness)
|
||||
local primaryLight = Color.new(
|
||||
math.min(1, primary.r * 1.3),
|
||||
math.min(1, primary.g * 1.3),
|
||||
math.min(1, primary.b * 1.3),
|
||||
primary.a
|
||||
)
|
||||
|
||||
-- Semi-transparent version
|
||||
local primaryTransparent = Color.new(
|
||||
primary.r,
|
||||
primary.g,
|
||||
primary.b,
|
||||
0.5 -- 50% opacity
|
||||
)
|
||||
]])
|
||||
|
||||
print("\nExample 3: Safe color access")
|
||||
print("-----------------------------")
|
||||
print([[
|
||||
-- With fallback to white if color doesn't exist
|
||||
local bgColor = Theme.getColorOrDefault("background", Color.new(1, 1, 1, 1))
|
||||
|
||||
-- With fallback to theme's secondary color
|
||||
local borderColor = Theme.getColorOrDefault(
|
||||
"border",
|
||||
Theme.getColor("secondary")
|
||||
)
|
||||
]])
|
||||
|
||||
print("\nExample 4: Dynamic color selection")
|
||||
print("-----------------------------------")
|
||||
print([[
|
||||
-- Get all available colors
|
||||
local colors = Theme.getAllColors()
|
||||
|
||||
-- Pick a random color
|
||||
local colorNames = {}
|
||||
for name in pairs(colors) do
|
||||
table.insert(colorNames, name)
|
||||
end
|
||||
local randomColorName = colorNames[math.random(#colorNames)]
|
||||
local randomColor = colors[randomColorName]
|
||||
]])
|
||||
|
||||
print("\n=== Quick Reference ===\n")
|
||||
print("Theme.getColor(name) -- Get a specific color")
|
||||
print("Theme.getColorOrDefault(n, fb) -- Get color with fallback")
|
||||
print("Theme.getAllColors() -- Get all colors as table")
|
||||
print("Theme.getColorNames() -- Get array of color names")
|
||||
print("Theme.hasActive() -- Check if theme is active")
|
||||
print("Theme.getActive() -- Get active theme object")
|
||||
|
||||
print("\n=== Available Colors in 'space' Theme ===\n")
|
||||
for i, name in ipairs(colorNames) do
|
||||
local color = allColors[name]
|
||||
print(string.format("%-10s RGB(%.0f, %.0f, %.0f)",
|
||||
name,
|
||||
color.r * 255,
|
||||
color.g * 255,
|
||||
color.b * 255))
|
||||
end
|
||||
|
||||
print("\n=== Demo Complete ===")
|
||||
@@ -442,9 +442,9 @@ function TestHorizontalFlexDirection:testHorizontalLayoutAlignItemsStretch()
|
||||
parent:addChild(child1)
|
||||
parent:addChild(child2)
|
||||
|
||||
-- With align-items stretch in horizontal layout, children should stretch to parent height
|
||||
luaunit.assertEquals(child1.height, parent.height)
|
||||
luaunit.assertEquals(child2.height, parent.height)
|
||||
-- With align-items stretch, children with explicit heights should keep them (CSS flexbox behavior)
|
||||
luaunit.assertEquals(child1.height, 30)
|
||||
luaunit.assertEquals(child2.height, 40)
|
||||
end
|
||||
|
||||
-- Test 14: Horizontal layout with align-items center
|
||||
|
||||
@@ -402,9 +402,9 @@ function TestVerticalFlexDirection:testVerticalLayoutAlignItemsStretch()
|
||||
parent:addChild(child1)
|
||||
parent:addChild(child2)
|
||||
|
||||
-- Children should be stretched to fill parent width
|
||||
luaunit.assertEquals(child1.width, parent.width)
|
||||
luaunit.assertEquals(child2.width, parent.width)
|
||||
-- Children with explicit widths should keep them (CSS flexbox behavior)
|
||||
luaunit.assertEquals(child1.width, 80)
|
||||
luaunit.assertEquals(child2.width, 60)
|
||||
end
|
||||
|
||||
-- Test 13: Vertical layout with space-between
|
||||
|
||||
@@ -189,11 +189,11 @@ function TestAlignItems:testHorizontalFlexAlignItemsStretch()
|
||||
container:addChild(child1)
|
||||
container:addChild(child2)
|
||||
|
||||
-- Children should be stretched to fill container height
|
||||
-- Children with explicit heights should NOT be stretched (CSS flexbox behavior)
|
||||
luaunit.assertEquals(child1.y, 0)
|
||||
luaunit.assertEquals(child2.y, 0)
|
||||
luaunit.assertEquals(child1.height, 100)
|
||||
luaunit.assertEquals(child2.height, 100)
|
||||
luaunit.assertEquals(child1.height, 30) -- Keeps explicit height
|
||||
luaunit.assertEquals(child2.height, 40) -- Keeps explicit height
|
||||
end
|
||||
|
||||
-- Test 5: Vertical Flex with AlignItems.FLEX_START
|
||||
@@ -357,11 +357,11 @@ function TestAlignItems:testVerticalFlexAlignItemsStretch()
|
||||
container:addChild(child1)
|
||||
container:addChild(child2)
|
||||
|
||||
-- Children should be stretched to fill container width
|
||||
-- Children with explicit widths should NOT be stretched (CSS flexbox behavior)
|
||||
luaunit.assertEquals(child1.x, 0)
|
||||
luaunit.assertEquals(child2.x, 0)
|
||||
luaunit.assertEquals(child1.width, 200)
|
||||
luaunit.assertEquals(child2.width, 200)
|
||||
luaunit.assertEquals(child1.width, 50) -- Keeps explicit width
|
||||
luaunit.assertEquals(child2.width, 80) -- Keeps explicit width
|
||||
end
|
||||
|
||||
-- Test 9: Default AlignItems value (should be STRETCH)
|
||||
@@ -386,9 +386,9 @@ function TestAlignItems:testDefaultAlignItems()
|
||||
|
||||
container:addChild(child)
|
||||
|
||||
-- Default should be STRETCH
|
||||
-- Default should be STRETCH, but explicit heights are respected
|
||||
luaunit.assertEquals(container.alignItems, AlignItems.STRETCH)
|
||||
luaunit.assertEquals(child.height, 100) -- Should be stretched
|
||||
luaunit.assertEquals(child.height, 30) -- Keeps explicit height (CSS flexbox behavior)
|
||||
end
|
||||
|
||||
-- Test 10: AlignItems with mixed child sizes
|
||||
|
||||
@@ -280,12 +280,12 @@ function TestFlexWrap07_WrapWithStretchAlignItems()
|
||||
|
||||
local positions = layoutAndGetPositions(container)
|
||||
|
||||
-- All children in first line should stretch to tallest (35)
|
||||
luaunit.assertEquals(positions[1].height, 35) -- child1 stretched
|
||||
luaunit.assertEquals(positions[2].height, 35) -- child2 keeps height
|
||||
-- Children with explicit heights should keep them (CSS flexbox behavior)
|
||||
luaunit.assertEquals(positions[1].height, 20) -- child1 keeps explicit height
|
||||
luaunit.assertEquals(positions[2].height, 35) -- child2 keeps explicit height
|
||||
|
||||
-- Child in second line should keep its height (no other children to stretch to)
|
||||
luaunit.assertEquals(positions[3].height, 25) -- child3 original height
|
||||
-- Child in second line should keep its height
|
||||
luaunit.assertEquals(positions[3].height, 25) -- child3 keeps explicit height
|
||||
|
||||
-- Verify positions
|
||||
luaunit.assertEquals(positions[1].y, 0) -- First line
|
||||
|
||||
@@ -186,11 +186,11 @@ function TestComprehensiveFlex:testNestedFlexContainersComplexLayout()
|
||||
-- Positions are absolute including parent container position
|
||||
luaunit.assertEquals(inner2Positions[1].x, 20) -- parent x + 0
|
||||
luaunit.assertEquals(inner2Positions[1].y, 95) -- parent y + 0
|
||||
luaunit.assertEquals(inner2Positions[1].height, 50) -- stretched to full container height
|
||||
luaunit.assertEquals(inner2Positions[1].height, 25) -- explicit height, not stretched (CSS spec compliance)
|
||||
|
||||
luaunit.assertEquals(inner2Positions[2].x, 60) -- parent x + 40
|
||||
luaunit.assertEquals(inner2Positions[2].y, 95) -- parent y + 0
|
||||
luaunit.assertEquals(inner2Positions[2].height, 50) -- stretched to full container height
|
||||
luaunit.assertEquals(inner2Positions[2].height, 25) -- explicit height, not stretched (CSS spec compliance)
|
||||
end
|
||||
|
||||
-- Test 3: All flex properties combined with absolute positioning
|
||||
@@ -390,19 +390,19 @@ function TestComprehensiveFlex:testDeeplyNestedFlexContainers()
|
||||
-- These positions are relative to level 1 container position
|
||||
luaunit.assertEquals(level2Positions[1].x, 20) -- positioned by level 1
|
||||
luaunit.assertEquals(level2Positions[1].y, 25) -- positioned by level 1
|
||||
luaunit.assertEquals(level2Positions[1].height, 100) -- stretched to full cross-axis height
|
||||
luaunit.assertEquals(level2Positions[1].height, 80) -- explicit height, not stretched (CSS spec compliance)
|
||||
|
||||
luaunit.assertEquals(level2Positions[2].x, 110) -- positioned by level 1 + space-between
|
||||
luaunit.assertEquals(level2Positions[2].y, 25) -- positioned by level 1
|
||||
luaunit.assertEquals(level2Positions[2].height, 100) -- stretched to full cross-axis height
|
||||
luaunit.assertEquals(level2Positions[2].height, 80) -- explicit height, not stretched (CSS spec compliance)
|
||||
|
||||
-- Level 3a: flex-end justification, center alignment
|
||||
-- Positions are absolute including parent positions
|
||||
luaunit.assertEquals(level3aPositions[1].x, 40) -- absolute position
|
||||
luaunit.assertEquals(level3aPositions[1].y, 90) -- flex-end: positioned at bottom of stretched container
|
||||
luaunit.assertEquals(level3aPositions[1].y, 70) -- flex-end: 25 (level2.y) + 45 (80 - 35 total children)
|
||||
|
||||
luaunit.assertEquals(level3aPositions[2].x, 42.5) -- absolute position
|
||||
luaunit.assertEquals(level3aPositions[2].y, 110) -- second item: 90 + 20 = 110
|
||||
luaunit.assertEquals(level3aPositions[2].y, 90) -- second item: 70 + 20 = 90
|
||||
|
||||
-- Level 3b: flex-start justification, flex-end alignment
|
||||
-- Positions are absolute including parent positions
|
||||
@@ -1656,8 +1656,8 @@ function TestComprehensiveFlex:testComplexDashboardLayout()
|
||||
middlePositions[i] = { x = child.x, y = child.y, width = child.width, height = child.height }
|
||||
end
|
||||
|
||||
luaunit.assertEquals(middlePositions[1].x, 300) -- chart panel (300 + 20 padding)
|
||||
luaunit.assertEquals(middlePositions[2].x, 1000) -- stats panel (300 + 680 + 20)
|
||||
luaunit.assertEquals(middlePositions[1].x, 300) -- chart panel (280 sidebar + 20 padding)
|
||||
luaunit.assertEquals(middlePositions[2].x, 1020) -- stats panel (280 + 20 + 680 + 40 gap with SPACE_BETWEEN)
|
||||
|
||||
-- Test chart legend wrapping
|
||||
local chartPanel = middleContent.children[1]
|
||||
@@ -1699,7 +1699,7 @@ function TestComprehensiveFlex:testComplexDashboardLayout()
|
||||
end
|
||||
|
||||
luaunit.assertEquals(bottomPositions[1].x, 300) -- table panel
|
||||
luaunit.assertEquals(bottomPositions[2].x, 860) -- right panels (300 + 540 + 20)
|
||||
luaunit.assertEquals(bottomPositions[2].x, 880) -- right panels (280 + 20 + 540 + 40 gap with SPACE_BETWEEN)
|
||||
|
||||
-- Test right panels layout
|
||||
local rightPanels = bottomContent.children[2]
|
||||
@@ -1710,8 +1710,8 @@ function TestComprehensiveFlex:testComplexDashboardLayout()
|
||||
rightPositions[i] = { x = child.x, y = child.y, width = child.width, height = child.height }
|
||||
end
|
||||
|
||||
luaunit.assertEquals(rightPositions[1].x, 860) -- alerts panel
|
||||
luaunit.assertEquals(rightPositions[2].x, 1120) -- progress panel (860 + 240 + 20)
|
||||
luaunit.assertEquals(rightPositions[1].x, 880) -- alerts panel (same as parent due to SPACE_BETWEEN)
|
||||
luaunit.assertEquals(rightPositions[2].x, 1140) -- progress panel (880 + 240 + 20)
|
||||
end
|
||||
|
||||
luaunit.LuaUnit.run()
|
||||
|
||||
@@ -140,6 +140,7 @@ function TestAuxiliaryFunctions:testCalculateAutoWidthWithChildren()
|
||||
local parent = Gui.new({
|
||||
positioning = enums.Positioning.FLEX,
|
||||
flexDirection = enums.FlexDirection.HORIZONTAL,
|
||||
gap = 5, -- Add gap to test gap calculation
|
||||
})
|
||||
|
||||
local child1 = Gui.new({
|
||||
@@ -172,6 +173,7 @@ function TestAuxiliaryFunctions:testCalculateAutoHeightWithChildren()
|
||||
local parent = Gui.new({
|
||||
positioning = enums.Positioning.FLEX,
|
||||
flexDirection = enums.FlexDirection.VERTICAL,
|
||||
gap = 5, -- Add gap to test gap calculation
|
||||
})
|
||||
|
||||
local child1 = Gui.new({
|
||||
@@ -491,18 +493,21 @@ function TestAuxiliaryFunctions:testAnimationInterpolationAtBoundaries()
|
||||
|
||||
-- At start (elapsed = 0)
|
||||
scaleAnim.elapsed = 0
|
||||
scaleAnim._resultDirty = true -- Mark dirty after changing elapsed
|
||||
local result = scaleAnim:interpolate()
|
||||
luaunit.assertEquals(result.width, 100)
|
||||
luaunit.assertEquals(result.height, 50)
|
||||
|
||||
-- At end (elapsed = duration)
|
||||
scaleAnim.elapsed = 1.0
|
||||
scaleAnim._resultDirty = true -- Mark dirty after changing elapsed
|
||||
result = scaleAnim:interpolate()
|
||||
luaunit.assertEquals(result.width, 200)
|
||||
luaunit.assertEquals(result.height, 100)
|
||||
|
||||
-- Beyond end (elapsed > duration) - should clamp to end values
|
||||
scaleAnim.elapsed = 1.5
|
||||
scaleAnim._resultDirty = true -- Mark dirty after changing elapsed
|
||||
result = scaleAnim:interpolate()
|
||||
luaunit.assertEquals(result.width, 200)
|
||||
luaunit.assertEquals(result.height, 100)
|
||||
@@ -594,11 +599,11 @@ function TestAuxiliaryFunctions:testComplexColorManagementSystem()
|
||||
end
|
||||
|
||||
-- Test color variations (opacity, brightness adjustments)
|
||||
local opacities = { 0.1, 0.25, 0.5, 0.75, 0.9 }
|
||||
for color_name, color_set in pairs(theme_colors) do
|
||||
color_variations[color_name] = {}
|
||||
|
||||
-- Create opacity variations
|
||||
local opacities = { 0.1, 0.25, 0.5, 0.75, 0.9 }
|
||||
for _, opacity in ipairs(opacities) do
|
||||
local variant_color = Color.new(color_set.manual.r, color_set.manual.g, color_set.manual.b, opacity)
|
||||
color_variations[color_name]["alpha_" .. tostring(opacity)] = variant_color
|
||||
@@ -678,7 +683,13 @@ function TestAuxiliaryFunctions:testComplexColorManagementSystem()
|
||||
ui_container:layoutChildren()
|
||||
|
||||
luaunit.assertEquals(#ui_container.children, 5, "Should have 5 themed components")
|
||||
luaunit.assertEquals(#theme_colors, 5, "Should have 5 base theme colors")
|
||||
|
||||
-- Count theme_colors (it's a table with string keys, not an array)
|
||||
local theme_color_count = 0
|
||||
for _ in pairs(theme_colors) do
|
||||
theme_color_count = theme_color_count + 1
|
||||
end
|
||||
luaunit.assertEquals(theme_color_count, 5, "Should have 5 base theme colors")
|
||||
|
||||
local total_variations = 0
|
||||
for _, variations in pairs(color_variations) do
|
||||
@@ -902,6 +913,7 @@ function TestAuxiliaryFunctions:testAdvancedTextAndAutoSizingSystem()
|
||||
table.insert(main_container.children, nested_container)
|
||||
|
||||
-- Create nested structure with auto-sizing children
|
||||
local prev_container = nested_container
|
||||
for level = 1, 3 do
|
||||
local level_container = Gui.new({
|
||||
width = 750 - (level * 50),
|
||||
@@ -911,12 +923,9 @@ function TestAuxiliaryFunctions:testAdvancedTextAndAutoSizingSystem()
|
||||
justifyContent = enums.JustifyContent.SPACE_AROUND,
|
||||
gap = 5,
|
||||
})
|
||||
level_container.parent = level == 1 and nested_container
|
||||
or main_container.children[#main_container.children].children[level - 1]
|
||||
table.insert(
|
||||
(level == 1 and nested_container or main_container.children[#main_container.children].children[level - 1]).children,
|
||||
level_container
|
||||
)
|
||||
level_container.parent = prev_container
|
||||
table.insert(prev_container.children, level_container)
|
||||
prev_container = level_container
|
||||
|
||||
for item = 1, 4 do
|
||||
local item_text = string.format("L%d-Item%d: %s", level, item, string.rep("Text ", level))
|
||||
@@ -943,7 +952,13 @@ function TestAuxiliaryFunctions:testAdvancedTextAndAutoSizingSystem()
|
||||
#text_scenarios + 1,
|
||||
"Should have scenario containers plus nested container"
|
||||
)
|
||||
luaunit.assertTrue(#content_manager.text_metrics >= #text_scenarios, "Should have metrics for all scenarios")
|
||||
|
||||
-- Count text_metrics (it's a table with string keys, not an array)
|
||||
local metrics_count = 0
|
||||
for _ in pairs(content_manager.text_metrics) do
|
||||
metrics_count = metrics_count + 1
|
||||
end
|
||||
luaunit.assertTrue(metrics_count >= #text_scenarios, "Should have metrics for all scenarios")
|
||||
|
||||
print(
|
||||
string.format(
|
||||
@@ -1607,8 +1622,10 @@ function TestAuxiliaryFunctions:testAdvancedGUIManagementAndCleanup()
|
||||
end
|
||||
|
||||
-- Test opacity management across hierarchy
|
||||
for i, element_pair in pairs(managed_elements) do
|
||||
if i % 2 == 0 then
|
||||
for element_id, element_pair in pairs(managed_elements) do
|
||||
-- Extract number from "element_N" key
|
||||
local num = tonumber(element_id:match("%d+"))
|
||||
if num and num % 2 == 0 then
|
||||
element_pair:updateOpacity(0.5)
|
||||
luaunit.assertEquals(element_pair.opacity, 0.5, "Even elements should have 0.5 opacity")
|
||||
end
|
||||
|
||||
@@ -22,6 +22,10 @@ end
|
||||
|
||||
function TestUnitsSystem:tearDown()
|
||||
Gui.destroy()
|
||||
-- Restore original viewport size
|
||||
love.graphics.getDimensions = function()
|
||||
return 800, 600
|
||||
end
|
||||
end
|
||||
|
||||
-- ============================================
|
||||
@@ -245,7 +249,7 @@ function TestUnitsSystem:testMarginUnits()
|
||||
})
|
||||
|
||||
luaunit.assertEquals(container.margin.top, 8) -- 8px
|
||||
luaunit.assertEquals(container.margin.right, 12) -- 3% of 400
|
||||
luaunit.assertEquals(container.margin.right, 36) -- 3% of viewport width (1200) - CSS spec: % margins relative to containing block
|
||||
luaunit.assertEquals(container.margin.bottom, 8) -- 1% of 800
|
||||
luaunit.assertEquals(container.margin.left, 24) -- 2% of 1200
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ function TestEventSystem:setUp()
|
||||
-- Initialize GUI before each test
|
||||
Gui.init({ baseScale = { width = 1920, height = 1080 } })
|
||||
love.window.setMode(1920, 1080)
|
||||
Gui.resize(1920, 1080) -- Recalculate scale factors after setMode
|
||||
end
|
||||
|
||||
function TestEventSystem:tearDown()
|
||||
|
||||
@@ -8,7 +8,7 @@ TestNegativeMargin = {}
|
||||
|
||||
function TestNegativeMargin:setUp()
|
||||
FlexLove.Gui.destroy()
|
||||
FlexLove.Gui.init({ baseScale = { width = 1920, height = 1080 } })
|
||||
-- Don't call init to use 1:1 scaling (like other tests)
|
||||
end
|
||||
|
||||
function TestNegativeMargin:tearDown()
|
||||
|
||||
@@ -88,12 +88,59 @@ end
|
||||
|
||||
-- Mock mouse functions
|
||||
love_helper.mouse = {}
|
||||
|
||||
-- Mock mouse state
|
||||
local mockMouseX = 0
|
||||
local mockMouseY = 0
|
||||
local mockMouseButtons = {} -- Table to track button states
|
||||
|
||||
function love_helper.mouse.getPosition()
|
||||
return 0, 0 -- Default position
|
||||
return mockMouseX, mockMouseY
|
||||
end
|
||||
|
||||
function love_helper.mouse.setPosition(x, y)
|
||||
mockMouseX = x
|
||||
mockMouseY = y
|
||||
end
|
||||
|
||||
function love_helper.mouse.isDown(button)
|
||||
return false -- Default not pressed
|
||||
return mockMouseButtons[button] or false
|
||||
end
|
||||
|
||||
function love_helper.mouse.setDown(button, isDown)
|
||||
mockMouseButtons[button] = isDown
|
||||
end
|
||||
|
||||
-- Mock timer functions
|
||||
love_helper.timer = {}
|
||||
|
||||
-- Mock time state
|
||||
local mockTime = 0
|
||||
|
||||
function love_helper.timer.getTime()
|
||||
return mockTime
|
||||
end
|
||||
|
||||
function love_helper.timer.setTime(time)
|
||||
mockTime = time
|
||||
end
|
||||
|
||||
function love_helper.timer.step(dt)
|
||||
mockTime = mockTime + dt
|
||||
end
|
||||
|
||||
-- Mock keyboard functions
|
||||
love_helper.keyboard = {}
|
||||
|
||||
-- Mock keyboard state
|
||||
local mockKeyboardKeys = {} -- Table to track key states
|
||||
|
||||
function love_helper.keyboard.isDown(key)
|
||||
return mockKeyboardKeys[key] or false
|
||||
end
|
||||
|
||||
function love_helper.keyboard.setDown(key, isDown)
|
||||
mockKeyboardKeys[key] = isDown
|
||||
end
|
||||
|
||||
-- Mock touch functions
|
||||
|
||||
Reference in New Issue
Block a user