need to remove guiding bars

This commit is contained in:
Michael Freno
2025-10-15 20:11:27 -04:00
parent a971cabafa
commit ba550a60d0
10 changed files with 942 additions and 236 deletions

View File

@@ -352,37 +352,121 @@ function ImageScaler.scaleNearest(sourceImageData, srcX, srcY, srcW, srcH, destW
if not sourceImageData then if not sourceImageData then
error(formatError("ImageScaler", "Source ImageData cannot be nil")) error(formatError("ImageScaler", "Source ImageData cannot be nil"))
end end
if srcW <= 0 or srcH <= 0 or destW <= 0 or destH <= 0 then if srcW <= 0 or srcH <= 0 or destW <= 0 or destH <= 0 then
error(formatError("ImageScaler", "Dimensions must be positive")) error(formatError("ImageScaler", "Dimensions must be positive"))
end end
-- Create destination ImageData -- Create destination ImageData
local destImageData = love.image.newImageData(destW, destH) local destImageData = love.image.newImageData(destW, destH)
-- Calculate scale ratios (cached outside loops for performance) -- Calculate scale ratios (cached outside loops for performance)
local scaleX = srcW / destW local scaleX = srcW / destW
local scaleY = srcH / destH local scaleY = srcH / destH
-- Nearest-neighbor sampling -- Nearest-neighbor sampling
for destY = 0, destH - 1 do for destY = 0, destH - 1 do
for destX = 0, destW - 1 do for destX = 0, destW - 1 do
-- Calculate source pixel coordinates using floor (nearest-neighbor) -- Calculate source pixel coordinates using floor (nearest-neighbor)
local srcPixelX = math.floor(destX * scaleX) + srcX local srcPixelX = math.floor(destX * scaleX) + srcX
local srcPixelY = math.floor(destY * scaleY) + srcY local srcPixelY = math.floor(destY * scaleY) + srcY
-- Clamp to source bounds (safety check) -- Clamp to source bounds (safety check)
srcPixelX = math.min(srcPixelX, srcX + srcW - 1) srcPixelX = math.min(srcPixelX, srcX + srcW - 1)
srcPixelY = math.min(srcPixelY, srcY + srcH - 1) srcPixelY = math.min(srcPixelY, srcY + srcH - 1)
-- Sample source pixel -- Sample source pixel
local r, g, b, a = sourceImageData:getPixel(srcPixelX, srcPixelY) local r, g, b, a = sourceImageData:getPixel(srcPixelX, srcPixelY)
-- Write to destination -- Write to destination
destImageData:setPixel(destX, destY, r, g, b, a) destImageData:setPixel(destX, destY, r, g, b, a)
end end
end end
return destImageData
end
--- Linear interpolation helper
--- Blends between two values based on interpolation factor
---@param a number -- Start value
---@param b number -- End value
---@param t number -- Interpolation factor [0, 1]
---@return number -- Interpolated value
local function lerp(a, b, t)
return a + (b - a) * t
end
--- Scale an ImageData region using bilinear interpolation
--- Produces smooth, filtered scaling - ideal for high-quality upscaling
---@param sourceImageData love.ImageData -- Source image data
---@param srcX number -- Source region X (0-based)
---@param srcY number -- Source region Y (0-based)
---@param srcW number -- Source region width
---@param srcH number -- Source region height
---@param destW number -- Destination width
---@param destH number -- Destination height
---@return love.ImageData -- Scaled image data
function ImageScaler.scaleBilinear(sourceImageData, srcX, srcY, srcW, srcH, destW, destH)
if not sourceImageData then
error(formatError("ImageScaler", "Source ImageData cannot be nil"))
end
if srcW <= 0 or srcH <= 0 or destW <= 0 or destH <= 0 then
error(formatError("ImageScaler", "Dimensions must be positive"))
end
-- Create destination ImageData
local destImageData = love.image.newImageData(destW, destH)
-- Calculate scale ratios
local scaleX = srcW / destW
local scaleY = srcH / destH
-- Bilinear interpolation
for destY = 0, destH - 1 do
for destX = 0, destW - 1 do
-- Calculate fractional source position
local srcXf = destX * scaleX
local srcYf = destY * scaleY
-- Get integer coordinates for 2x2 sampling grid
local x0 = math.floor(srcXf)
local y0 = math.floor(srcYf)
local x1 = math.min(x0 + 1, srcW - 1)
local y1 = math.min(y0 + 1, srcH - 1)
-- Get fractional parts for interpolation
local fx = srcXf - x0
local fy = srcYf - y0
-- Sample 4 neighboring pixels (with source offset)
local r00, g00, b00, a00 = sourceImageData:getPixel(srcX + x0, srcY + y0)
local r10, g10, b10, a10 = sourceImageData:getPixel(srcX + x1, srcY + y0)
local r01, g01, b01, a01 = sourceImageData:getPixel(srcX + x0, srcY + y1)
local r11, g11, b11, a11 = sourceImageData:getPixel(srcX + x1, srcY + y1)
-- Interpolate horizontally (top and bottom rows)
local rTop = lerp(r00, r10, fx)
local gTop = lerp(g00, g10, fx)
local bTop = lerp(b00, b10, fx)
local aTop = lerp(a00, a10, fx)
local rBottom = lerp(r01, r11, fx)
local gBottom = lerp(g01, g11, fx)
local bBottom = lerp(b01, b11, fx)
local aBottom = lerp(a01, a11, fx)
-- Interpolate vertically (final result)
local r = lerp(rTop, rBottom, fy)
local g = lerp(gTop, gBottom, fy)
local b = lerp(bTop, bBottom, fy)
local a = lerp(aTop, aBottom, fy)
-- Write to destination
destImageData:setPixel(destX, destY, r, g, b, a)
end
end
return destImageData return destImageData
end end
@@ -996,27 +1080,119 @@ function NineSlice.draw(component, atlas, x, y, width, height, opacity)
return love.graphics.newQuad(region.x, region.y, region.w, region.h, atlasWidth, atlasHeight) return love.graphics.newQuad(region.x, region.y, region.w, region.h, atlasWidth, atlasHeight)
end end
-- CORNERS (no scaling - 1:1 pixel perfect) -- Check if corner scaling is enabled
love.graphics.draw(atlas, makeQuad(regions.topLeft), x, y) local scaleCorners = component.scaleCorners or false
love.graphics.draw(atlas, makeQuad(regions.topRight), x + left + contentWidth, y) local scalingAlgorithm = component.scalingAlgorithm or "bilinear"
love.graphics.draw(atlas, makeQuad(regions.bottomLeft), x, y + top + contentHeight)
love.graphics.draw(atlas, makeQuad(regions.bottomRight), x + left + contentWidth, y + top + contentHeight)
-- TOP/BOTTOM EDGES (stretch horizontally only) if scaleCorners and Gui and Gui.scaleFactors then
if contentWidth > 0 then -- Initialize cache if needed
love.graphics.draw(atlas, makeQuad(regions.topCenter), x + left, y, 0, scaleX, 1) if not component._scaledRegionCache then
love.graphics.draw(atlas, makeQuad(regions.bottomCenter), x + left, y + top + contentHeight, 0, scaleX, 1) component._scaledRegionCache = {}
end end
-- LEFT/RIGHT EDGES (stretch vertically only) -- Get current scale factors
if contentHeight > 0 then local scaleFactorX = Gui.scaleFactors.x or 1
love.graphics.draw(atlas, makeQuad(regions.middleLeft), x, y + top, 0, 1, scaleY) local scaleFactorY = Gui.scaleFactors.y or 1
love.graphics.draw(atlas, makeQuad(regions.middleRight), x + left + contentWidth, y + top, 0, 1, scaleY) local scaleFactor = math.max(scaleFactorX, scaleFactorY)
end
-- CENTER (stretch both dimensions) -- Helper to get or create scaled region
if contentWidth > 0 and contentHeight > 0 then local function getScaledRegion(regionName, region, targetWidth, targetHeight)
love.graphics.draw(atlas, makeQuad(regions.middleCenter), x + left, y + top, 0, scaleX, scaleY) local cacheKey = string.format("%s_%.2f_%s", regionName, scaleFactor, scalingAlgorithm)
if component._scaledRegionCache[cacheKey] then
return component._scaledRegionCache[cacheKey]
end
-- Extract region from atlas
local atlasData = atlas:getData()
local scaledData
if scalingAlgorithm == "nearest" then
scaledData = ImageScaler.scaleNearest(atlasData, region.x, region.y, region.w, region.h, targetWidth, targetHeight)
else
scaledData = ImageScaler.scaleBilinear(atlasData, region.x, region.y, region.w, region.h, targetWidth, targetHeight)
end
-- Convert to image and cache
local scaledImage = love.graphics.newImage(scaledData)
component._scaledRegionCache[cacheKey] = scaledImage
return scaledImage
end
-- Calculate scaled dimensions for corners
local scaledLeft = math.floor(left * scaleFactor + 0.5)
local scaledRight = math.floor(right * scaleFactor + 0.5)
local scaledTop = math.floor(top * scaleFactor + 0.5)
local scaledBottom = math.floor(bottom * scaleFactor + 0.5)
-- CORNERS (scaled using algorithm)
local topLeftScaled = getScaledRegion("topLeft", regions.topLeft, scaledLeft, scaledTop)
local topRightScaled = getScaledRegion("topRight", regions.topRight, scaledRight, scaledTop)
local bottomLeftScaled = getScaledRegion("bottomLeft", regions.bottomLeft, scaledLeft, scaledBottom)
local bottomRightScaled = getScaledRegion("bottomRight", regions.bottomRight, scaledRight, scaledBottom)
love.graphics.draw(topLeftScaled, x, y)
love.graphics.draw(topRightScaled, x + scaledLeft + contentWidth, y)
love.graphics.draw(bottomLeftScaled, x, y + scaledTop + contentHeight)
love.graphics.draw(bottomRightScaled, x + scaledLeft + contentWidth, y + scaledTop + contentHeight)
-- Update content dimensions to account for scaled borders
local adjustedContentWidth = width - scaledLeft - scaledRight
local adjustedContentHeight = height - scaledTop - scaledBottom
adjustedContentWidth = math.max(0, adjustedContentWidth)
adjustedContentHeight = math.max(0, adjustedContentHeight)
-- Recalculate stretch scales
local adjustedScaleX = adjustedContentWidth / centerW
local adjustedScaleY = adjustedContentHeight / centerH
-- TOP/BOTTOM EDGES (stretch horizontally, scale vertically)
if adjustedContentWidth > 0 then
local topCenterScaled = getScaledRegion("topCenter", regions.topCenter, regions.topCenter.w, scaledTop)
local bottomCenterScaled = getScaledRegion("bottomCenter", regions.bottomCenter, regions.bottomCenter.w, scaledBottom)
love.graphics.draw(topCenterScaled, x + scaledLeft, y, 0, adjustedScaleX, 1)
love.graphics.draw(bottomCenterScaled, x + scaledLeft, y + scaledTop + adjustedContentHeight, 0, adjustedScaleX, 1)
end
-- LEFT/RIGHT EDGES (stretch vertically, scale horizontally)
if adjustedContentHeight > 0 then
local middleLeftScaled = getScaledRegion("middleLeft", regions.middleLeft, scaledLeft, regions.middleLeft.h)
local middleRightScaled = getScaledRegion("middleRight", regions.middleRight, scaledRight, regions.middleRight.h)
love.graphics.draw(middleLeftScaled, x, y + scaledTop, 0, 1, adjustedScaleY)
love.graphics.draw(middleRightScaled, x + scaledLeft + adjustedContentWidth, y + scaledTop, 0, 1, adjustedScaleY)
end
-- CENTER (stretch both dimensions, no scaling)
if adjustedContentWidth > 0 and adjustedContentHeight > 0 then
love.graphics.draw(atlas, makeQuad(regions.middleCenter), x + scaledLeft, y + scaledTop, 0, adjustedScaleX, adjustedScaleY)
end
else
-- Original rendering logic (no scaling)
-- CORNERS (no scaling - 1:1 pixel perfect)
love.graphics.draw(atlas, makeQuad(regions.topLeft), x, y)
love.graphics.draw(atlas, makeQuad(regions.topRight), x + left + contentWidth, y)
love.graphics.draw(atlas, makeQuad(regions.bottomLeft), x, y + top + contentHeight)
love.graphics.draw(atlas, makeQuad(regions.bottomRight), x + left + contentWidth, y + top + contentHeight)
-- TOP/BOTTOM EDGES (stretch horizontally only)
if contentWidth > 0 then
love.graphics.draw(atlas, makeQuad(regions.topCenter), x + left, y, 0, scaleX, 1)
love.graphics.draw(atlas, makeQuad(regions.bottomCenter), x + left, y + top + contentHeight, 0, scaleX, 1)
end
-- LEFT/RIGHT EDGES (stretch vertically only)
if contentHeight > 0 then
love.graphics.draw(atlas, makeQuad(regions.middleLeft), x, y + top, 0, 1, scaleY)
love.graphics.draw(atlas, makeQuad(regions.middleRight), x + left + contentWidth, y + top, 0, 1, scaleY)
end
-- CENTER (stretch both dimensions)
if contentWidth > 0 and contentHeight > 0 then
love.graphics.draw(atlas, makeQuad(regions.middleCenter), x + left, y + top, 0, scaleX, scaleY)
end
end end
-- Reset color -- Reset color
@@ -1500,6 +1676,17 @@ function Gui.resize()
Gui.scaleFactors.y = newHeight / Gui.baseScale.height Gui.scaleFactors.y = newHeight / Gui.baseScale.height
end end
-- Clear scaled region caches for all themes
for _, theme in pairs(themes) do
if theme.components then
for _, component in pairs(theme.components) do
if component._scaledRegionCache then
component._scaledRegionCache = {}
end
end
end
end
for _, win in ipairs(Gui.topElements) do for _, win in ipairs(Gui.topElements) do
win:resize(newWidth, newHeight) win:resize(newWidth, newHeight)
end end
@@ -1584,6 +1771,8 @@ function Gui.destroy()
-- Reset base scale and scale factors -- Reset base scale and scale factors
Gui.baseScale = nil Gui.baseScale = nil
Gui.scaleFactors = { x = 1.0, y = 1.0 } Gui.scaleFactors = { x = 1.0, y = 1.0 }
-- Reset cached viewport
Gui._cachedViewport = { width = 0, height = 0 }
end end
-- Simple GUI library for LOVE2D -- Simple GUI library for LOVE2D
@@ -4030,25 +4219,28 @@ function Element:recalculateUnits(newViewportWidth, newViewportHeight)
-- BORDER-BOX MODEL: Calculate content dimensions from border-box dimensions -- BORDER-BOX MODEL: Calculate content dimensions from border-box dimensions
-- For explicitly-sized elements (non-auto), _borderBoxWidth/_borderBoxHeight were set earlier -- For explicitly-sized elements (non-auto), _borderBoxWidth/_borderBoxHeight were set earlier
-- Now we calculate content width/height by subtracting padding -- Now we calculate content width/height by subtracting padding
if self.units.width.unit ~= "auto" then -- Only recalculate if using viewport/percentage units (where _borderBoxWidth actually changed)
-- _borderBoxWidth was already set during width recalculation if self.units.width.unit ~= "auto" and self.units.width.unit ~= "px" then
-- _borderBoxWidth was recalculated for viewport/percentage units
-- Calculate content width by subtracting padding -- Calculate content width by subtracting padding
self.width = math.max(0, self._borderBoxWidth - self.padding.left - self.padding.right) self.width = math.max(0, self._borderBoxWidth - self.padding.left - self.padding.right)
else elseif self.units.width.unit == "auto" then
-- For auto-sized elements, width is content width (calculated in resize method) -- For auto-sized elements, width is content width (calculated in resize method)
-- Update border-box to include padding -- Update border-box to include padding
self._borderBoxWidth = self.width + self.padding.left + self.padding.right self._borderBoxWidth = self.width + self.padding.left + self.padding.right
end end
-- For pixel units, width stays as-is (may have been manually modified)
if self.units.height.unit ~= "auto" then if self.units.height.unit ~= "auto" and self.units.height.unit ~= "px" then
-- _borderBoxHeight was already set during height recalculation -- _borderBoxHeight was recalculated for viewport/percentage units
-- Calculate content height by subtracting padding -- Calculate content height by subtracting padding
self.height = math.max(0, self._borderBoxHeight - self.padding.top - self.padding.bottom) self.height = math.max(0, self._borderBoxHeight - self.padding.top - self.padding.bottom)
else elseif self.units.height.unit == "auto" then
-- For auto-sized elements, height is content height (calculated in resize method) -- For auto-sized elements, height is content height (calculated in resize method)
-- Update border-box to include padding -- Update border-box to include padding
self._borderBoxHeight = self.height + self.padding.top + self.padding.bottom self._borderBoxHeight = self.height + self.padding.top + self.padding.bottom
end end
-- For pixel units, height stays as-is (may have been manually modified)
end end
--- Resize element and its children based on game window size change --- Resize element and its children based on game window size change
@@ -4057,6 +4249,14 @@ end
function Element:resize(newGameWidth, newGameHeight) function Element:resize(newGameWidth, newGameHeight)
self:recalculateUnits(newGameWidth, newGameHeight) self:recalculateUnits(newGameWidth, newGameHeight)
-- For non-auto-sized elements with viewport/percentage units, update content dimensions from border-box
if not self.autosizing.width and self._borderBoxWidth and self.units.width.unit ~= "px" then
self.width = math.max(0, self._borderBoxWidth - self.padding.left - self.padding.right)
end
if not self.autosizing.height and self._borderBoxHeight and self.units.height.unit ~= "px" then
self.height = math.max(0, self._borderBoxHeight - self.padding.top - self.padding.bottom)
end
-- Update children -- Update children
for _, child in ipairs(self.children) do for _, child in ipairs(self.children) do
child:resize(newGameWidth, newGameHeight) child:resize(newGameWidth, newGameHeight)
@@ -4076,6 +4276,48 @@ function Element:resize(newGameWidth, newGameHeight)
self.height = contentHeight self.height = contentHeight
end end
-- Re-resolve ew/eh textSize units after all dimensions are finalized
-- This ensures textSize updates based on current width/height (whether calculated or manually set)
if self.units.textSize.value then
local unit = self.units.textSize.unit
local value = self.units.textSize.value
local scaleX, scaleY = Gui.getScaleFactors()
if unit == "ew" then
-- Element width relative (use current width)
self.textSize = (value / 100) * self.width
-- Apply min/max constraints
local minSize = self.minTextSize and (Gui.baseScale and (self.minTextSize * scaleY) or self.minTextSize)
local maxSize = self.maxTextSize and (Gui.baseScale and (self.maxTextSize * scaleY) or self.maxTextSize)
if minSize and self.textSize < minSize then
self.textSize = minSize
end
if maxSize and self.textSize > maxSize then
self.textSize = maxSize
end
if self.textSize < 1 then
self.textSize = 1
end
elseif unit == "eh" then
-- Element height relative (use current height)
self.textSize = (value / 100) * self.height
-- Apply min/max constraints
local minSize = self.minTextSize and (Gui.baseScale and (self.minTextSize * scaleY) or self.minTextSize)
local maxSize = self.maxTextSize and (Gui.baseScale and (self.maxTextSize * scaleY) or self.maxTextSize)
if minSize and self.textSize < minSize then
self.textSize = minSize
end
if maxSize and self.textSize > maxSize then
self.textSize = maxSize
end
if self.textSize < 1 then
self.textSize = 1
end
end
end
self:layoutChildren() self:layoutChildren()
self.prevGameSize.width = newGameWidth self.prevGameSize.width = newGameWidth
self.prevGameSize.height = newGameHeight self.prevGameSize.height = newGameHeight

View File

@@ -0,0 +1,243 @@
local FlexLove = require("FlexLove")
local Gui = FlexLove.GUI
local Theme = FlexLove.Theme
local Color = FlexLove.Color
---@class CornerScalingDemo
---@field window Element
---@field currentMode string
---@field modeButtons table
local CornerScalingDemo = {}
CornerScalingDemo.__index = CornerScalingDemo
function CornerScalingDemo.init()
local self = setmetatable({}, CornerScalingDemo)
self.currentMode = "none"
self.modeButtons = {}
-- Try to load theme
local themeLoaded = pcall(function()
Theme.load("space")
Theme.setActive("space")
end)
-- Create main window
self.window = Gui.new({
x = 50,
y = 50,
width = 900,
height = 650,
backgroundColor = Color.new(0.1, 0.1, 0.15, 0.95),
border = { top = true, bottom = true, left = true, right = true },
borderColor = Color.new(0.6, 0.6, 0.7, 1),
positioning = "flex",
flexDirection = "vertical",
gap = 20,
padding = { top = 20, right = 20, bottom = 20, left = 20 },
})
-- Title
Gui.new({
parent = self.window,
height = 40,
text = "NineSlice Corner Scaling Demo",
textSize = 24,
textAlign = "center",
textColor = Color.new(1, 1, 1, 1),
backgroundColor = Color.new(0.15, 0.15, 0.25, 1),
})
-- Status
Gui.new({
parent = self.window,
height = 30,
text = themeLoaded and "✓ Theme loaded - Scaling demonstration active"
or "⚠ Theme not loaded - Please ensure theme assets exist",
textSize = 14,
textAlign = "center",
textColor = themeLoaded and Color.new(0.3, 0.9, 0.3, 1) or Color.new(0.9, 0.6, 0.3, 1),
backgroundColor = Color.new(0.08, 0.08, 0.12, 0.8),
})
-- Mode selector section
local modeSection = Gui.new({
parent = self.window,
height = 80,
backgroundColor = Color.new(0.12, 0.12, 0.18, 1),
padding = { top = 15, right = 15, bottom = 15, left = 15 },
positioning = "flex",
flexDirection = "vertical",
gap = 10,
})
Gui.new({
parent = modeSection,
height = 20,
text = "Select Scaling Mode:",
textSize = 14,
textColor = Color.new(0.8, 0.9, 1, 1),
backgroundColor = Color.new(0, 0, 0, 0),
})
-- Button container
local buttonContainer = Gui.new({
parent = modeSection,
height = 40,
positioning = "flex",
flexDirection = "horizontal",
gap = 15,
backgroundColor = Color.new(0, 0, 0, 0),
})
-- Helper to create mode button
local function createModeButton(mode, label)
local button = Gui.new({
parent = buttonContainer,
width = 180,
height = 40,
text = label,
textAlign = "center",
textSize = 14,
textColor = Color.new(1, 1, 1, 1),
backgroundColor = self.currentMode == mode and Color.new(0.3, 0.6, 0.9, 1) or Color.new(0.25, 0.25, 0.35, 1),
callback = function(element, event)
if event.type == "click" then
self:setMode(mode)
end
end,
})
self.modeButtons[mode] = button
return button
end
createModeButton("none", "No Scaling (Default)")
createModeButton("nearest", "Nearest Neighbor")
createModeButton("bilinear", "Bilinear Interpolation")
-- Comparison section
local comparisonSection = Gui.new({
parent = self.window,
height = 420,
backgroundColor = Color.new(0.08, 0.08, 0.12, 1),
padding = { top = 20, right = 20, bottom = 20, left = 20 },
positioning = "flex",
flexDirection = "vertical",
gap = 15,
})
-- Description
Gui.new({
parent = comparisonSection,
height = 60,
text = "The panels below demonstrate different scaling modes.\n" ..
"• No Scaling: Corners remain at original size (may appear small at high DPI)\n" ..
"• Nearest Neighbor: Sharp, pixelated scaling (ideal for pixel art)\n" ..
"• Bilinear: Smooth, filtered scaling (ideal for high-quality graphics)",
textSize = 12,
textColor = Color.new(0.7, 0.8, 0.9, 1),
backgroundColor = Color.new(0, 0, 0, 0),
})
-- Demo panels container
local panelsContainer = Gui.new({
parent = comparisonSection,
positioning = "flex",
flexDirection = "horizontal",
gap = 20,
backgroundColor = Color.new(0, 0, 0, 0),
})
-- Helper to create demo panel
local function createDemoPanel(size, label)
local container = Gui.new({
parent = panelsContainer,
width = (900 - 80 - 40) / 3, -- Divide available space
positioning = "flex",
flexDirection = "vertical",
gap = 10,
backgroundColor = Color.new(0, 0, 0, 0),
})
Gui.new({
parent = container,
height = 20,
text = label,
textSize = 12,
textAlign = "center",
textColor = Color.new(0.8, 0.9, 1, 1),
backgroundColor = Color.new(0, 0, 0, 0),
})
local panel = Gui.new({
parent = container,
width = size,
height = size,
backgroundColor = Color.new(0.2, 0.3, 0.4, 0.5),
theme = themeLoaded and "panel" or nil,
padding = { top = 15, right = 15, bottom = 15, left = 15 },
})
Gui.new({
parent = panel,
text = "Themed\nPanel",
textSize = 14,
textAlign = "center",
textColor = Color.new(1, 1, 1, 1),
backgroundColor = Color.new(0, 0, 0, 0),
})
return panel
end
createDemoPanel(120, "Small (120x120)")
createDemoPanel(160, "Medium (160x160)")
createDemoPanel(200, "Large (200x200)")
-- Info footer
Gui.new({
parent = self.window,
height = 30,
text = "Resize the window to see how scaling adapts to different DPI settings",
textSize = 11,
textAlign = "center",
textColor = Color.new(0.5, 0.6, 0.7, 1),
backgroundColor = Color.new(0.08, 0.08, 0.12, 1),
})
return self
end
function CornerScalingDemo:setMode(mode)
self.currentMode = mode
-- Update button colors
for modeName, button in pairs(self.modeButtons) do
button.backgroundColor = modeName == mode and Color.new(0.3, 0.6, 0.9, 1) or Color.new(0.25, 0.25, 0.35, 1)
end
-- Update theme components based on mode
local activeTheme = Theme.getActive()
if activeTheme and activeTheme.components then
for componentName, component in pairs(activeTheme.components) do
if mode == "none" then
component.scaleCorners = false
elseif mode == "nearest" then
component.scaleCorners = true
component.scalingAlgorithm = "nearest"
elseif mode == "bilinear" then
component.scaleCorners = true
component.scalingAlgorithm = "bilinear"
end
-- Clear cache to force re-rendering
if component._scaledRegionCache then
component._scaledRegionCache = {}
end
end
end
print("Scaling mode changed to: " .. mode)
end
return CornerScalingDemo.init()

View File

@@ -1,181 +0,0 @@
-- 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 ===")

View File

@@ -15,17 +15,13 @@ function TestUnitsSystem:setUp()
-- Clear any existing GUI elements and reset viewport -- Clear any existing GUI elements and reset viewport
Gui.destroy() Gui.destroy()
-- Set a consistent viewport size for testing -- Set a consistent viewport size for testing
love.graphics.getDimensions = function() love.window.setMode(1200, 800)
return 1200, 800
end
end end
function TestUnitsSystem:tearDown() function TestUnitsSystem:tearDown()
Gui.destroy() Gui.destroy()
-- Restore original viewport size -- Restore original viewport size
love.graphics.getDimensions = function() love.window.setMode(800, 600)
return 800, 600
end
end end
-- ============================================ -- ============================================
@@ -150,14 +146,15 @@ function TestUnitsSystem:testResizeViewportUnits()
luaunit.assertEquals(container.width, 600) -- 50% of 1200 luaunit.assertEquals(container.width, 600) -- 50% of 1200
luaunit.assertEquals(container.height, 200) -- 25% of 800 luaunit.assertEquals(container.height, 200) -- 25% of 800
-- Simulate viewport resize -- Simulate viewport resize using setMode
love.graphics.getDimensions = function() love.window.setMode(1600, 1000)
return 1600, 1000
end
container:resize(1600, 1000) container:resize(1600, 1000)
luaunit.assertEquals(container.width, 800) -- 50% of 1600 luaunit.assertEquals(container.width, 800) -- 50% of 1600
luaunit.assertEquals(container.height, 250) -- 25% of 1000 luaunit.assertEquals(container.height, 250) -- 25% of 1000
-- Restore viewport
love.window.setMode(1200, 800)
end end
function TestUnitsSystem:testResizePercentageUnits() function TestUnitsSystem:testResizePercentageUnits()

View File

@@ -11,6 +11,16 @@ local Gui = FlexLove.GUI
-- Test suite for comprehensive text scaling -- Test suite for comprehensive text scaling
TestTextScaling = {} TestTextScaling = {}
function TestTextScaling:setUp()
-- Reset viewport to default before each test
love.window.setMode(800, 600)
Gui.destroy()
end
function TestTextScaling:tearDown()
Gui.destroy()
end
-- Basic functionality tests -- Basic functionality tests
function TestTextScaling.testFixedTextSize() function TestTextScaling.testFixedTextSize()
-- Create an element with fixed textSize in pixels (auto-scaling disabled) -- Create an element with fixed textSize in pixels (auto-scaling disabled)

View File

@@ -1,6 +1,8 @@
-- Test padding resize behavior with percentage units -- Test padding resize behavior with percentage units
package.path = package.path .. ";?.lua" package.path = package.path .. ";?.lua"
local luaunit = require("testing.luaunit") local luaunit = require("testing.luaunit")
local loveStub = require("testing.loveStub")
_G.love = loveStub
local FlexLove = require("FlexLove") local FlexLove = require("FlexLove")
TestPaddingResize = {} TestPaddingResize = {}

View File

@@ -0,0 +1,288 @@
package.path = package.path .. ";?.lua"
local luaunit = require("testing.luaunit")
local loveStub = require("testing.loveStub")
_G.love = loveStub
local FlexLove = require("FlexLove")
TestImageScalerBilinear = {}
function TestImageScalerBilinear:setUp()
-- Create a simple test image (2x2 with distinct colors)
self.testImage2x2 = love.image.newImageData(2, 2)
-- Top-left: red
self.testImage2x2:setPixel(0, 0, 1, 0, 0, 1)
-- Top-right: green
self.testImage2x2:setPixel(1, 0, 0, 1, 0, 1)
-- Bottom-left: blue
self.testImage2x2:setPixel(0, 1, 0, 0, 1, 1)
-- Bottom-right: white
self.testImage2x2:setPixel(1, 1, 1, 1, 1, 1)
end
function TestImageScalerBilinear:test2xScaling()
-- Scale 2x2 to 4x4 (2x factor)
local scaled = FlexLove.ImageScaler.scaleBilinear(self.testImage2x2, 0, 0, 2, 2, 4, 4)
luaunit.assertEquals(scaled:getWidth(), 4)
luaunit.assertEquals(scaled:getHeight(), 4)
-- Corner pixels should match original (no interpolation at exact positions)
local r, g, b, a = scaled:getPixel(0, 0)
luaunit.assertAlmostEquals(r, 1, 0.01) -- Red
luaunit.assertAlmostEquals(g, 0, 0.01)
luaunit.assertAlmostEquals(b, 0, 0.01)
-- Center pixel at (1,1) should be blend of all 4 corners
-- At (0.5, 0.5) in source space -> blend of all 4 colors
r, g, b, a = scaled:getPixel(1, 1)
-- Should be approximately (0.5, 0.5, 0.5) - average of red, green, blue, white
luaunit.assertTrue(r > 0.3 and r < 0.7, "Center pixel should be blended")
luaunit.assertTrue(g > 0.3 and g < 0.7, "Center pixel should be blended")
luaunit.assertTrue(b > 0.3 and b < 0.7, "Center pixel should be blended")
end
function TestImageScalerBilinear:testGradientSmoothing()
-- Create a simple gradient: black to white horizontally
local gradient = love.image.newImageData(2, 1)
gradient:setPixel(0, 0, 0, 0, 0, 1) -- Black
gradient:setPixel(1, 0, 1, 1, 1, 1) -- White
-- Scale to 4 pixels wide
local scaled = FlexLove.ImageScaler.scaleBilinear(gradient, 0, 0, 2, 1, 4, 1)
luaunit.assertEquals(scaled:getWidth(), 4)
luaunit.assertEquals(scaled:getHeight(), 1)
-- Check smooth gradient progression
local r0 = scaled:getPixel(0, 0)
local r1 = scaled:getPixel(1, 0)
local r2 = scaled:getPixel(2, 0)
local r3 = scaled:getPixel(3, 0)
-- Should be monotonically increasing (or equal at end due to clamping)
luaunit.assertTrue(r0 < r1, "Gradient should increase")
luaunit.assertTrue(r1 < r2, "Gradient should increase")
luaunit.assertTrue(r2 <= r3, "Gradient should increase or stay same")
-- First should be close to black, last close to white
luaunit.assertAlmostEquals(r0, 0, 0.15)
luaunit.assertAlmostEquals(r3, 1, 0.15)
end
function TestImageScalerBilinear:testSameSizeScaling()
-- Scale 2x2 to 2x2 (should be identical)
local scaled = FlexLove.ImageScaler.scaleBilinear(self.testImage2x2, 0, 0, 2, 2, 2, 2)
luaunit.assertEquals(scaled:getWidth(), 2)
luaunit.assertEquals(scaled:getHeight(), 2)
-- Verify all pixels match original
for y = 0, 1 do
for x = 0, 1 do
local r1, g1, b1, a1 = self.testImage2x2:getPixel(x, y)
local r2, g2, b2, a2 = scaled:getPixel(x, y)
luaunit.assertAlmostEquals(r1, r2, 0.01)
luaunit.assertAlmostEquals(g1, g2, 0.01)
luaunit.assertAlmostEquals(b1, b2, 0.01)
luaunit.assertAlmostEquals(a1, a2, 0.01)
end
end
end
function TestImageScalerBilinear:test1x1Scaling()
-- Create 1x1 image
local img1x1 = love.image.newImageData(1, 1)
img1x1:setPixel(0, 0, 0.5, 0.5, 0.5, 1)
-- Scale to 4x4
local scaled = FlexLove.ImageScaler.scaleBilinear(img1x1, 0, 0, 1, 1, 4, 4)
luaunit.assertEquals(scaled:getWidth(), 4)
luaunit.assertEquals(scaled:getHeight(), 4)
-- All pixels should be the same color (no neighbors to interpolate with)
for y = 0, 3 do
for x = 0, 3 do
local r, g, b = scaled:getPixel(x, y)
luaunit.assertAlmostEquals(r, 0.5, 0.01)
luaunit.assertAlmostEquals(g, 0.5, 0.01)
luaunit.assertAlmostEquals(b, 0.5, 0.01)
end
end
end
function TestImageScalerBilinear:testPureColorMaintenance()
-- Create pure white image
local whiteImg = love.image.newImageData(2, 2)
for y = 0, 1 do
for x = 0, 1 do
whiteImg:setPixel(x, y, 1, 1, 1, 1)
end
end
local scaled = FlexLove.ImageScaler.scaleBilinear(whiteImg, 0, 0, 2, 2, 4, 4)
-- All pixels should remain pure white
for y = 0, 3 do
for x = 0, 3 do
local r, g, b = scaled:getPixel(x, y)
luaunit.assertAlmostEquals(r, 1, 0.01)
luaunit.assertAlmostEquals(g, 1, 0.01)
luaunit.assertAlmostEquals(b, 1, 0.01)
end
end
-- Test pure black
local blackImg = love.image.newImageData(2, 2)
for y = 0, 1 do
for x = 0, 1 do
blackImg:setPixel(x, y, 0, 0, 0, 1)
end
end
scaled = FlexLove.ImageScaler.scaleBilinear(blackImg, 0, 0, 2, 2, 4, 4)
for y = 0, 3 do
for x = 0, 3 do
local r, g, b = scaled:getPixel(x, y)
luaunit.assertAlmostEquals(r, 0, 0.01)
luaunit.assertAlmostEquals(g, 0, 0.01)
luaunit.assertAlmostEquals(b, 0, 0.01)
end
end
end
function TestImageScalerBilinear:testAlphaInterpolation()
-- Create image with varying alpha
local img = love.image.newImageData(2, 2)
img:setPixel(0, 0, 1, 0, 0, 1.0) -- Opaque red
img:setPixel(1, 0, 1, 0, 0, 0.0) -- Transparent red
img:setPixel(0, 1, 1, 0, 0, 1.0) -- Opaque red
img:setPixel(1, 1, 1, 0, 0, 0.0) -- Transparent red
local scaled = FlexLove.ImageScaler.scaleBilinear(img, 0, 0, 2, 2, 4, 2)
-- Check that alpha is interpolated smoothly
local r, g, b, a0 = scaled:getPixel(0, 0)
luaunit.assertAlmostEquals(a0, 1.0, 0.01)
local r, g, b, a1 = scaled:getPixel(1, 0)
-- Should be between 1.0 and 0.0
luaunit.assertTrue(a1 > 0.3 and a1 < 0.7, "Alpha should be interpolated")
local r, g, b, a3 = scaled:getPixel(3, 0)
luaunit.assertAlmostEquals(a3, 0.0, 0.15)
end
function TestImageScalerBilinear:testSubregionScaling()
-- Create 4x4 image with different quadrants
local img4x4 = love.image.newImageData(4, 4)
-- Fill with pattern: top-left red, rest black
for y = 0, 3 do
for x = 0, 3 do
if x < 2 and y < 2 then
img4x4:setPixel(x, y, 1, 0, 0, 1) -- red
else
img4x4:setPixel(x, y, 0, 0, 0, 1) -- black
end
end
end
-- Scale only the top-left 2x2 red quadrant to 4x4
local scaled = FlexLove.ImageScaler.scaleBilinear(img4x4, 0, 0, 2, 2, 4, 4)
luaunit.assertEquals(scaled:getWidth(), 4)
luaunit.assertEquals(scaled:getHeight(), 4)
-- All pixels should be red (from source quadrant)
for y = 0, 3 do
for x = 0, 3 do
local r, g, b = scaled:getPixel(x, y)
luaunit.assertAlmostEquals(r, 1, 0.01)
luaunit.assertAlmostEquals(g, 0, 0.01)
luaunit.assertAlmostEquals(b, 0, 0.01)
end
end
end
function TestImageScalerBilinear:testEdgePixelHandling()
-- Create 3x3 checkerboard
local checkerboard = love.image.newImageData(3, 3)
for y = 0, 2 do
for x = 0, 2 do
if (x + y) % 2 == 0 then
checkerboard:setPixel(x, y, 1, 1, 1, 1) -- white
else
checkerboard:setPixel(x, y, 0, 0, 0, 1) -- black
end
end
end
-- Scale to 9x9
local scaled = FlexLove.ImageScaler.scaleBilinear(checkerboard, 0, 0, 3, 3, 9, 9)
luaunit.assertEquals(scaled:getWidth(), 9)
luaunit.assertEquals(scaled:getHeight(), 9)
-- Verify corners are correct (no out-of-bounds access)
local r, g, b = scaled:getPixel(0, 0)
luaunit.assertAlmostEquals(r, 1, 0.01) -- Top-left should be white
r, g, b = scaled:getPixel(8, 8)
luaunit.assertAlmostEquals(r, 1, 0.01) -- Bottom-right should be white
end
function TestImageScalerBilinear:testNonUniformScaling()
-- Scale 2x2 to 6x4 (3x horizontal, 2x vertical)
local scaled = FlexLove.ImageScaler.scaleBilinear(self.testImage2x2, 0, 0, 2, 2, 6, 4)
luaunit.assertEquals(scaled:getWidth(), 6)
luaunit.assertEquals(scaled:getHeight(), 4)
-- Top-left corner should be red
local r, g, b = scaled:getPixel(0, 0)
luaunit.assertAlmostEquals(r, 1, 0.01)
luaunit.assertAlmostEquals(g, 0, 0.01)
-- Should have smooth interpolation in between
r, g, b = scaled:getPixel(2, 1)
-- Middle area should have blended colors
luaunit.assertTrue(r > 0.1, "Should have some red component")
luaunit.assertTrue(g > 0.1, "Should have some green component")
luaunit.assertTrue(b > 0.1, "Should have some blue component")
end
function TestImageScalerBilinear:testComparison_SmootherThanNearest()
-- Create gradient
local gradient = love.image.newImageData(2, 1)
gradient:setPixel(0, 0, 0, 0, 0, 1)
gradient:setPixel(1, 0, 1, 1, 1, 1)
local bilinear = FlexLove.ImageScaler.scaleBilinear(gradient, 0, 0, 2, 1, 8, 1)
local nearest = FlexLove.ImageScaler.scaleNearest(gradient, 0, 0, 2, 1, 8, 1)
-- Count unique values (nearest should have fewer due to blocky nature)
local bilinearValues = {}
local nearestValues = {}
for x = 0, 7 do
local rb = bilinear:getPixel(x, 0)
local rn = nearest:getPixel(x, 0)
bilinearValues[string.format("%.2f", rb)] = true
nearestValues[string.format("%.2f", rn)] = true
end
local bilinearCount = 0
for _ in pairs(bilinearValues) do bilinearCount = bilinearCount + 1 end
local nearestCount = 0
for _ in pairs(nearestValues) do nearestCount = nearestCount + 1 end
-- Bilinear should have more unique values (smoother gradient)
luaunit.assertTrue(bilinearCount >= nearestCount,
"Bilinear should produce smoother gradient with more unique values")
end
luaunit.LuaUnit.run()

View File

@@ -153,5 +153,53 @@ function love_helper.touch.getPosition(id)
return 0, 0 -- Default touch position return 0, 0 -- Default touch position
end end
-- Mock image functions
love_helper.image = {}
-- Mock ImageData object
local ImageData = {}
ImageData.__index = ImageData
function ImageData.new(width, height)
local self = setmetatable({}, ImageData)
self.width = width
self.height = height
-- Store pixel data as a 2D array [y][x] = {r, g, b, a}
self.pixels = {}
for y = 0, height - 1 do
self.pixels[y] = {}
for x = 0, width - 1 do
self.pixels[y][x] = {0, 0, 0, 0} -- Default to transparent black
end
end
return self
end
function ImageData:getWidth()
return self.width
end
function ImageData:getHeight()
return self.height
end
function ImageData:setPixel(x, y, r, g, b, a)
if x >= 0 and x < self.width and y >= 0 and y < self.height then
self.pixels[y][x] = {r, g, b, a or 1}
end
end
function ImageData:getPixel(x, y)
if x >= 0 and x < self.width and y >= 0 and y < self.height then
local pixel = self.pixels[y][x]
return pixel[1], pixel[2], pixel[3], pixel[4]
end
return 0, 0, 0, 0
end
function love_helper.image.newImageData(width, height)
return ImageData.new(width, height)
end
_G.love = love_helper _G.love = love_helper
return love_helper return love_helper

View File

@@ -0,0 +1,69 @@
local Color = require("libs.FlexLove").Color
return {
name = "Metal Theme",
contentAutoSizingMultiplier = { width = 1.05, height = 1.1 },
components = {
framev1 = {
atlas = "themes/metal/Frame/Frame01a.9.png",
},
framev2 = {
atlas = "themes/metal/Frame/Frame01b.9.png",
},
framev3 = {
atlas = "themes/metal/Frame/Frame02a.9.png",
},
framev4 = {
atlas = "themes/metal/Frame/Frame02b.9.png",
},
framev5 = {
atlas = "themes/metal/Frame/Frame03a.9.png",
},
framev6 = {
atlas = "themes/metal/Frame/Frame03b.9.png",
},
buttonv1 = {
atlas = "themes/metal/Button/Button01a_1.9.png",
states = {
hover = {
atlas = "themes/metal/Button/Button01a_4.9.png",
},
pressed = {
atlas = "themes/metal/Button/Button01a_2.9.png",
},
disabled = {
atlas = "themes/metal/Button/Button01a_4.9.png",
},
},
},
buttonv2 = {
atlas = "themes/metal/Button/Button02a_1.9.png",
states = {
hover = {
atlas = "themes/metal/Button/Button02a_4.9.png",
},
pressed = {
atlas = "themes/metal/Button/Button02a_2.9.png",
},
disabled = {
atlas = "themes/metal/Button/Button02a_4.9.png",
},
},
},
},
-- Optional: Theme colors
colors = {
primary = Color.new(),
secondary = Color.new(),
text = Color.new(),
textDark = Color.new(),
},
-- Optional: Theme fonts
-- Define font families that can be referenced by name
-- Paths are relative to FlexLove location or absolute
fonts = {
default = "themes/space/VT323-Regular.ttf",
},
}

View File

@@ -1,16 +1,4 @@
-- Space Theme local Color = require("libs.FlexLove").Color
local Color = {}
Color.__index = Color
function Color.new(r, g, b, a)
local self = setmetatable({}, Color)
self.r = r or 0
self.g = g or 0
self.b = b or 0
self.a = a or 1
return self
end
return { return {
name = "Space Theme", name = "Space Theme",