cleannup
This commit is contained in:
100
FlexLove.lua
100
FlexLove.lua
@@ -17,6 +17,20 @@ local function formatError(module, message)
|
|||||||
return string.format("[FlexLove.%s] %s", module, message)
|
return string.format("[FlexLove.%s] %s", module, message)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Top level GUI manager
|
||||||
|
---@class Gui
|
||||||
|
---@field topElements table<integer, Element>
|
||||||
|
---@field baseScale {width:number, height:number}?
|
||||||
|
---@field scaleFactors {x:number, y:number}
|
||||||
|
---@field defaultTheme string? -- Default theme name to use for elements
|
||||||
|
local Gui = {
|
||||||
|
topElements = {},
|
||||||
|
baseScale = nil,
|
||||||
|
scaleFactors = { x = 1.0, y = 1.0 },
|
||||||
|
defaultTheme = nil,
|
||||||
|
_cachedViewport = { width = 0, height = 0 }, -- Cached viewport dimensions
|
||||||
|
}
|
||||||
|
|
||||||
-- ====================
|
-- ====================
|
||||||
-- Color System
|
-- Color System
|
||||||
-- ====================
|
-- ====================
|
||||||
@@ -478,6 +492,7 @@ end
|
|||||||
---@field scaleCorners boolean? -- Optional: scale non-stretched regions (corners/edges) with window size. Default: false
|
---@field scaleCorners boolean? -- Optional: scale non-stretched regions (corners/edges) with window size. Default: false
|
||||||
---@field scalingAlgorithm "nearest"|"bilinear"? -- Optional: scaling algorithm for non-stretched regions. Default: "bilinear"
|
---@field scalingAlgorithm "nearest"|"bilinear"? -- Optional: scaling algorithm for non-stretched regions. Default: "bilinear"
|
||||||
---@field _loadedAtlas string|love.Image? -- Internal: cached loaded atlas image
|
---@field _loadedAtlas string|love.Image? -- Internal: cached loaded atlas image
|
||||||
|
---@field _loadedAtlasData love.ImageData? -- Internal: cached loaded atlas ImageData for pixel access
|
||||||
---@field _ninePatchData {insets:table, stretchX:table, stretchY:table}? -- Internal: parsed 9-patch data with multiple stretch regions
|
---@field _ninePatchData {insets:table, stretchX:table, stretchY:table}? -- Internal: parsed 9-patch data with multiple stretch regions
|
||||||
---@field _scaledRegionCache table<string, love.Image>? -- Internal: cache for scaled corner/edge images
|
---@field _scaledRegionCache table<string, love.Image>? -- Internal: cache for scaled corner/edge images
|
||||||
|
|
||||||
@@ -496,6 +511,7 @@ end
|
|||||||
---@class Theme
|
---@class Theme
|
||||||
---@field name string
|
---@field name string
|
||||||
---@field atlas love.Image? -- Optional: global atlas
|
---@field atlas love.Image? -- Optional: global atlas
|
||||||
|
---@field atlasData love.ImageData?
|
||||||
---@field components table<string, ThemeComponent>
|
---@field components table<string, ThemeComponent>
|
||||||
---@field colors table<string, Color>
|
---@field colors table<string, Color>
|
||||||
---@field fonts table<string, string> -- Font family definitions
|
---@field fonts table<string, string> -- Font family definitions
|
||||||
@@ -557,19 +573,30 @@ local function resolveImagePath(imagePath)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Safely load an image with error handling
|
--- Safely load an image with error handling
|
||||||
|
--- Returns both Image and ImageData to avoid deprecated getData() API
|
||||||
---@param imagePath string
|
---@param imagePath string
|
||||||
---@return love.Image?, string? -- Returns image or nil, error message
|
---@return love.Image?, love.ImageData?, string? -- Returns image, imageData, or nil with error message
|
||||||
local function safeLoadImage(imagePath)
|
local function safeLoadImage(imagePath)
|
||||||
local success, result = pcall(function()
|
local success, imageData = pcall(function()
|
||||||
return love.graphics.newImage(imagePath)
|
return love.image.newImageData(imagePath)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if success then
|
if not success then
|
||||||
return result, nil
|
local errorMsg = string.format("[FlexLove] Failed to load image data: %s - %s", imagePath, tostring(imageData))
|
||||||
else
|
|
||||||
local errorMsg = string.format("[FlexLove] Failed to load image: %s - %s", imagePath, tostring(result))
|
|
||||||
print(errorMsg)
|
print(errorMsg)
|
||||||
return nil, errorMsg
|
return nil, nil, errorMsg
|
||||||
|
end
|
||||||
|
|
||||||
|
local imageSuccess, image = pcall(function()
|
||||||
|
return love.graphics.newImage(imageData)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if imageSuccess then
|
||||||
|
return image, imageData, nil
|
||||||
|
else
|
||||||
|
local errorMsg = string.format("[FlexLove] Failed to create image: %s - %s", imagePath, tostring(image))
|
||||||
|
print(errorMsg)
|
||||||
|
return nil, nil, errorMsg
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -618,9 +645,10 @@ function Theme.new(definition)
|
|||||||
if definition.atlas then
|
if definition.atlas then
|
||||||
if type(definition.atlas) == "string" then
|
if type(definition.atlas) == "string" then
|
||||||
local resolvedPath = resolveImagePath(definition.atlas)
|
local resolvedPath = resolveImagePath(definition.atlas)
|
||||||
local image, loaderr = safeLoadImage(resolvedPath)
|
local image, imageData, loaderr = safeLoadImage(resolvedPath)
|
||||||
if image then
|
if image then
|
||||||
self.atlas = image
|
self.atlas = image
|
||||||
|
self.atlasData = imageData
|
||||||
else
|
else
|
||||||
print(
|
print(
|
||||||
"[FlexLove] Warning: Failed to load global atlas for theme '"
|
"[FlexLove] Warning: Failed to load global atlas for theme '"
|
||||||
@@ -658,9 +686,10 @@ function Theme.new(definition)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local image, loaderr = safeLoadImage(resolvedPath)
|
local image, imageData, loaderr = safeLoadImage(resolvedPath)
|
||||||
if image then
|
if image then
|
||||||
comp._loadedAtlas = image
|
comp._loadedAtlas = image
|
||||||
|
comp._loadedAtlasData = imageData
|
||||||
else
|
else
|
||||||
print("[FlexLove] Warning: Failed to load atlas " .. errorContext .. ": " .. tostring(loaderr))
|
print("[FlexLove] Warning: Failed to load atlas " .. errorContext .. ": " .. tostring(loaderr))
|
||||||
end
|
end
|
||||||
@@ -706,6 +735,7 @@ function Theme.new(definition)
|
|||||||
if type(component.atlas) == "string" then
|
if type(component.atlas) == "string" then
|
||||||
loadAtlasWithNinePatch(component, component.atlas, "for component '" .. componentName .. "'")
|
loadAtlasWithNinePatch(component, component.atlas, "for component '" .. componentName .. "'")
|
||||||
else
|
else
|
||||||
|
-- Direct Image object (no ImageData available - scaleCorners won't work)
|
||||||
component._loadedAtlas = component.atlas
|
component._loadedAtlas = component.atlas
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -720,6 +750,7 @@ function Theme.new(definition)
|
|||||||
if type(stateComponent.atlas) == "string" then
|
if type(stateComponent.atlas) == "string" then
|
||||||
loadAtlasWithNinePatch(stateComponent, stateComponent.atlas, "for state '" .. stateName .. "'")
|
loadAtlasWithNinePatch(stateComponent, stateComponent.atlas, "for state '" .. stateName .. "'")
|
||||||
else
|
else
|
||||||
|
-- Direct Image object (no ImageData available - scaleCorners won't work)
|
||||||
stateComponent._loadedAtlas = stateComponent.atlas
|
stateComponent._loadedAtlas = stateComponent.atlas
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1062,8 +1093,12 @@ function NineSlice.draw(component, atlas, x, y, width, height, opacity)
|
|||||||
return component._scaledRegionCache[cacheKey]
|
return component._scaledRegionCache[cacheKey]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Extract region from atlas (regions already account for 9-patch borders)
|
-- Get ImageData from component (stored during theme loading)
|
||||||
local atlasData = atlas:getData()
|
local atlasData = component._loadedAtlasData
|
||||||
|
if not atlasData then
|
||||||
|
error(formatError("NineSlice", "No ImageData available for atlas. Image must be loaded with safeLoadImage."))
|
||||||
|
end
|
||||||
|
|
||||||
local scaledData
|
local scaledData
|
||||||
|
|
||||||
if scalingAlgorithm == "nearest" then
|
if scalingAlgorithm == "nearest" then
|
||||||
@@ -1259,7 +1294,7 @@ local TEXT_SIZE_PRESETS = {
|
|||||||
["4xl"] = 7.0, -- 7vh
|
["4xl"] = 7.0, -- 7vh
|
||||||
}
|
}
|
||||||
|
|
||||||
local Positioning, FlexDirection, JustifyContent, AlignContent, AlignItems, TextAlign, AlignSelf, JustifySelf, FlexWrap, TextSize =
|
local Positioning, FlexDirection, JustifyContent, AlignContent, AlignItems, TextAlign, AlignSelf, JustifySelf, FlexWrap =
|
||||||
enums.Positioning,
|
enums.Positioning,
|
||||||
enums.FlexDirection,
|
enums.FlexDirection,
|
||||||
enums.JustifyContent,
|
enums.JustifyContent,
|
||||||
@@ -1268,8 +1303,7 @@ local Positioning, FlexDirection, JustifyContent, AlignContent, AlignItems, Text
|
|||||||
enums.TextAlign,
|
enums.TextAlign,
|
||||||
enums.AlignSelf,
|
enums.AlignSelf,
|
||||||
enums.JustifySelf,
|
enums.JustifySelf,
|
||||||
enums.FlexWrap,
|
enums.FlexWrap
|
||||||
enums.TextSize
|
|
||||||
|
|
||||||
-- ====================
|
-- ====================
|
||||||
-- Units System
|
-- Units System
|
||||||
@@ -1353,7 +1387,8 @@ function Units.getViewport()
|
|||||||
if love.graphics and love.graphics.getDimensions then
|
if love.graphics and love.graphics.getDimensions then
|
||||||
return love.graphics.getDimensions()
|
return love.graphics.getDimensions()
|
||||||
else
|
else
|
||||||
return love.window.getMode()
|
local w, h = love.window.getMode()
|
||||||
|
return w, h
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1433,7 +1468,7 @@ function Units.isValid(unitStr)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local value, unit = Units.parse(unitStr)
|
local _, unit = Units.parse(unitStr)
|
||||||
local validUnits = { px = true, ["%"] = true, vw = true, vh = true, ew = true, eh = true }
|
local validUnits = { px = true, ["%"] = true, vw = true, vh = true, ew = true, eh = true }
|
||||||
return validUnits[unit] == true
|
return validUnits[unit] == true
|
||||||
end
|
end
|
||||||
@@ -1585,20 +1620,6 @@ function Grid.layoutGridItems(element)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Top level GUI manager
|
|
||||||
---@class Gui
|
|
||||||
---@field topElements table<integer, Element>
|
|
||||||
---@field baseScale {width:number, height:number}?
|
|
||||||
---@field scaleFactors {x:number, y:number}
|
|
||||||
---@field defaultTheme string? -- Default theme name to use for elements
|
|
||||||
local Gui = {
|
|
||||||
topElements = {},
|
|
||||||
baseScale = nil,
|
|
||||||
scaleFactors = { x = 1.0, y = 1.0 },
|
|
||||||
defaultTheme = nil,
|
|
||||||
_cachedViewport = { width = 0, height = 0 }, -- Cached viewport dimensions
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Initialize FlexLove with configuration
|
--- Initialize FlexLove with configuration
|
||||||
---@param config {baseScale?: {width?:number, height?:number}, theme?: string|ThemeDefinition} --Default: {width: 1920, height: 1080}
|
---@param config {baseScale?: {width?:number, height?:number}, theme?: string|ThemeDefinition} --Default: {width: 1920, height: 1080}
|
||||||
function Gui.init(config)
|
function Gui.init(config)
|
||||||
@@ -1799,7 +1820,8 @@ local function getModifiers()
|
|||||||
shift = love.keyboard.isDown("lshift", "rshift"),
|
shift = love.keyboard.isDown("lshift", "rshift"),
|
||||||
ctrl = love.keyboard.isDown("lctrl", "rctrl"),
|
ctrl = love.keyboard.isDown("lctrl", "rctrl"),
|
||||||
alt = love.keyboard.isDown("lalt", "ralt"),
|
alt = love.keyboard.isDown("lalt", "ralt"),
|
||||||
cmd = love.keyboard.isDown("lgui", "rgui"), -- Mac Command key
|
---@diagnostic disable-next-line
|
||||||
|
cmd = love.keyboard.isDown("lgui", "rgui"), -- Super key
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2050,7 +2072,7 @@ end
|
|||||||
|
|
||||||
--- Resolve text size preset to viewport units
|
--- Resolve text size preset to viewport units
|
||||||
---@param sizeValue string|number
|
---@param sizeValue string|number
|
||||||
---@return number, string -- Returns value and unit ("vh" for presets, original unit otherwise)
|
---@return number?, string? -- Returns value and unit ("vh" for presets, original unit otherwise)
|
||||||
local function resolveTextSizePreset(sizeValue)
|
local function resolveTextSizePreset(sizeValue)
|
||||||
if type(sizeValue) == "string" then
|
if type(sizeValue) == "string" then
|
||||||
-- Check if it's a preset
|
-- Check if it's a preset
|
||||||
@@ -2134,6 +2156,8 @@ Public API methods to access internal state:
|
|||||||
---@field justifySelf JustifySelf -- Alignment of the item itself along main axis (default: AUTO)
|
---@field justifySelf JustifySelf -- Alignment of the item itself along main axis (default: AUTO)
|
||||||
---@field alignSelf AlignSelf -- Alignment of the item itself along cross axis (default: AUTO)
|
---@field alignSelf AlignSelf -- Alignment of the item itself along cross axis (default: AUTO)
|
||||||
---@field textSize number? -- Resolved font size for text content in pixels
|
---@field textSize number? -- Resolved font size for text content in pixels
|
||||||
|
---@field minTextSize number
|
||||||
|
---@field maxTextSize number
|
||||||
---@field fontFamily string? -- Font family name from theme or path to font file
|
---@field fontFamily string? -- Font family name from theme or path to font file
|
||||||
---@field autoScaleText boolean -- Whether text should auto-scale with window size (default: true)
|
---@field autoScaleText boolean -- Whether text should auto-scale with window size (default: true)
|
||||||
---@field transform TransformProps -- Transform properties for animations and styling
|
---@field transform TransformProps -- Transform properties for animations and styling
|
||||||
@@ -2145,11 +2169,13 @@ Public API methods to access internal state:
|
|||||||
---@field _lastClickButton number? -- Button of last click
|
---@field _lastClickButton number? -- Button of last click
|
||||||
---@field _clickCount number -- Current click count for multi-click detection
|
---@field _clickCount number -- Current click count for multi-click detection
|
||||||
---@field _touchPressed table<any, boolean> -- Track touch pressed state
|
---@field _touchPressed table<any, boolean> -- Track touch pressed state
|
||||||
|
---@field _explicitlyAbsolute boolean?
|
||||||
---@field gridRows number? -- Number of rows in the grid
|
---@field gridRows number? -- Number of rows in the grid
|
||||||
---@field gridColumns number? -- Number of columns in the grid
|
---@field gridColumns number? -- Number of columns in the grid
|
||||||
---@field columnGap number|string? -- Gap between grid columns
|
---@field columnGap number|string? -- Gap between grid columns
|
||||||
---@field rowGap number|string? -- Gap between grid rows
|
---@field rowGap number|string? -- Gap between grid rows
|
||||||
---@field theme string|{component:string, state:string?}? -- Theme component to use for rendering
|
---@field theme string -- Theme component to use for rendering
|
||||||
|
---@field themeComponent string?
|
||||||
---@field _themeState string? -- Current theme state (normal, hover, pressed, active, disabled)
|
---@field _themeState string? -- Current theme state (normal, hover, pressed, active, disabled)
|
||||||
---@field disabled boolean? -- Whether the element is disabled (default: false)
|
---@field disabled boolean? -- Whether the element is disabled (default: false)
|
||||||
---@field active boolean? -- Whether the element is active/focused (for inputs, default: false)
|
---@field active boolean? -- Whether the element is active/focused (for inputs, default: false)
|
||||||
@@ -2183,6 +2209,8 @@ Element.__index = Element
|
|||||||
---@field textAlign TextAlign? -- Alignment of the text content (default: START)
|
---@field textAlign TextAlign? -- Alignment of the text content (default: START)
|
||||||
---@field textColor Color? -- Color of the text content (default: black)
|
---@field textColor Color? -- Color of the text content (default: black)
|
||||||
---@field textSize number|string? -- Font size: number (px), string with units ("2vh", "10%"), or preset ("xxs"|"xs"|"sm"|"md"|"lg"|"xl"|"xxl"|"3xl"|"4xl") (default: "md")
|
---@field textSize number|string? -- Font size: number (px), string with units ("2vh", "10%"), or preset ("xxs"|"xs"|"sm"|"md"|"lg"|"xl"|"xxl"|"3xl"|"4xl") (default: "md")
|
||||||
|
---@field minTextSize number
|
||||||
|
---@field maxTextSize number
|
||||||
---@field fontFamily string? -- Font family name from theme or path to font file (default: theme default or system default)
|
---@field fontFamily string? -- Font family name from theme or path to font file (default: theme default or system default)
|
||||||
---@field autoScaleText boolean? -- Whether text should auto-scale with window size (default: true)
|
---@field autoScaleText boolean? -- Whether text should auto-scale with window size (default: true)
|
||||||
---@field positioning Positioning? -- Layout positioning mode (default: RELATIVE)
|
---@field positioning Positioning? -- Layout positioning mode (default: RELATIVE)
|
||||||
@@ -2230,7 +2258,7 @@ function Element.new(props)
|
|||||||
-- - theme: which theme to use (defaults to Gui.defaultTheme if not specified)
|
-- - theme: which theme to use (defaults to Gui.defaultTheme if not specified)
|
||||||
-- - themeComponent: which component from the theme (e.g., "panel", "button", "input")
|
-- - themeComponent: which component from the theme (e.g., "panel", "button", "input")
|
||||||
-- If themeComponent is nil, no theme is applied (manual styling)
|
-- If themeComponent is nil, no theme is applied (manual styling)
|
||||||
self.theme = props.theme or Gui.defaultTheme
|
self.theme = props.theme or Gui.defaultTheme or ""
|
||||||
self.themeComponent = props.themeComponent or nil
|
self.themeComponent = props.themeComponent or nil
|
||||||
|
|
||||||
-- Initialize state properties
|
-- Initialize state properties
|
||||||
@@ -4251,7 +4279,7 @@ function Element:resize(newGameWidth, newGameHeight)
|
|||||||
if self.units.textSize.value then
|
if self.units.textSize.value then
|
||||||
local unit = self.units.textSize.unit
|
local unit = self.units.textSize.unit
|
||||||
local value = self.units.textSize.value
|
local value = self.units.textSize.value
|
||||||
local scaleX, scaleY = Gui.getScaleFactors()
|
local _, scaleY = Gui.getScaleFactors()
|
||||||
|
|
||||||
if unit == "ew" then
|
if unit == "ew" then
|
||||||
-- Element width relative (use current width)
|
-- Element width relative (use current width)
|
||||||
|
|||||||
Reference in New Issue
Block a user