This commit is contained in:
Michael Freno
2025-10-13 11:31:24 -04:00
parent ae2e08ca1b
commit f6ef2dc82e
3 changed files with 288 additions and 8 deletions

View File

@@ -65,17 +65,23 @@ end
---@field states table<string, ThemeComponent>?
---@field _loadedAtlas love.Image? -- Internal: cached loaded atlas image
---@class FontFamily
---@field path string -- Path to the font file (relative to FlexLove or absolute)
---@field _loadedFont love.Font? -- Internal: cached loaded font
---@class ThemeDefinition
---@field name string
---@field atlas string|love.Image? -- Optional: global atlas (can be overridden per component)
---@field components table<string, ThemeComponent>
---@field colors table<string, Color>?
---@field fonts table<string, string>? -- Optional: font family definitions (name -> path)
---@class Theme
---@field name string
---@field atlas love.Image? -- Optional: global atlas
---@field components table<string, ThemeComponent>
---@field colors table<string, Color>
---@field fonts table<string, string> -- Font family definitions
local Theme = {}
Theme.__index = Theme
@@ -151,6 +157,7 @@ function Theme.new(definition)
self.components = definition.components or {}
self.colors = definition.colors or {}
self.fonts = definition.fonts or {}
-- Load component-specific atlases
for componentName, component in pairs(self.components) do
@@ -1174,20 +1181,40 @@ local FONT_CACHE = {}
--- Create or get a font from cache
---@param size number
---@param fontPath string? -- Optional: path to font file
---@return love.Font
function FONT_CACHE.get(size)
if not FONT_CACHE[size] then
FONT_CACHE[size] = love.graphics.newFont(size)
function FONT_CACHE.get(size, fontPath)
-- Create cache key from size and font path
local cacheKey = fontPath and (fontPath .. "_" .. tostring(size)) or tostring(size)
if not FONT_CACHE[cacheKey] then
if fontPath then
-- Load custom font
local resolvedPath = resolveImagePath(fontPath)
-- Note: love.graphics.newFont signature is (path, size) for custom fonts
local success, font = pcall(love.graphics.newFont, resolvedPath, size)
if success then
FONT_CACHE[cacheKey] = font
else
-- Fallback to default font if custom font fails to load
print("[FlexLove] Failed to load font: " .. fontPath .. " - using default font")
FONT_CACHE[cacheKey] = love.graphics.newFont(size)
end
else
-- Load default font
FONT_CACHE[cacheKey] = love.graphics.newFont(size)
end
end
return FONT_CACHE[size]
return FONT_CACHE[cacheKey]
end
--- Get font for text size (cached)
---@param textSize number?
---@param fontPath string? -- Optional: path to font file
---@return love.Font
function FONT_CACHE.getFont(textSize)
function FONT_CACHE.getFont(textSize, fontPath)
if textSize then
return FONT_CACHE.get(textSize)
return FONT_CACHE.get(textSize, fontPath)
else
return love.graphics.getFont()
end
@@ -1256,6 +1283,7 @@ end
---@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 textSize number? -- Resolved font size for text content in pixels
---@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 transform TransformProps -- Transform properties for animations and styling
---@field transition TransitionProps -- Transition settings for animations
@@ -1303,6 +1331,7 @@ Element.__index = Element
---@field textAlign TextAlign? -- Alignment of the text content (default: START)
---@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 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 positioning Positioning? -- Layout positioning mode (default: ABSOLUTE)
---@field flexDirection FlexDirection? -- Direction of flex layout (default: HORIZONTAL)
@@ -1458,6 +1487,18 @@ function Element.new(props)
self.autoScaleText = props.autoScaleText
end
-- Handle fontFamily (can be font name from theme or direct path to font file)
self.fontFamily = props.fontFamily
-- If using themeComponent but no fontFamily specified, apply default font
if props.themeComponent and not props.fontFamily then
local themeToUse = self.theme and themes[self.theme] or Theme.getActive()
if themeToUse and themeToUse.fonts then
-- Use default font from theme if available
self.fontFamily = "default"
end
end
-- Handle textSize BEFORE width/height calculation (needed for auto-sizing)
if props.textSize then
if type(props.textSize) == "string" then
@@ -2543,8 +2584,27 @@ function Element:draw()
local origFont = love.graphics.getFont()
if self.textSize then
-- Resolve font path from font family
local fontPath = nil
if self.fontFamily then
-- Check if fontFamily is a theme font name
local themeToUse = self.theme and themes[self.theme] or Theme.getActive()
if themeToUse and themeToUse.fonts and themeToUse.fonts[self.fontFamily] then
fontPath = themeToUse.fonts[self.fontFamily]
else
-- Treat as direct path to font file
fontPath = self.fontFamily
end
elseif self.themeComponent then
-- If using themeComponent but no fontFamily specified, check for default font in theme
local themeToUse = self.theme and themes[self.theme] or Theme.getActive()
if themeToUse and themeToUse.fonts and themeToUse.fonts.default then
fontPath = themeToUse.fonts.default
end
end
-- Use cached font instead of creating new one every frame
local font = FONT_CACHE.get(self.textSize)
local font = FONT_CACHE.get(self.textSize, fontPath)
love.graphics.setFont(font)
end
local font = love.graphics.getFont()

214
examples/FontFamilyDemo.lua Normal file
View File

@@ -0,0 +1,214 @@
-- Font Family Demo
-- Demonstrates how to use custom fonts with FlexLove theme system
local FlexLove = require("libs.FlexLove")
local Gui = FlexLove.GUI
local Color = FlexLove.Color
local Theme = FlexLove.Theme
-- Initialize FlexLove with base scaling and theme
Gui.init({
baseScale = { width = 1920, height = 1080 },
theme = "space",
})
-- Create a simple theme with custom fonts
local customTheme = Theme.new({
name = "Custom Font Theme",
-- Define font families
-- Note: These paths are examples - replace with your actual font files
fonts = {
-- You can reference fonts by name in your elements
-- default = "path/to/your/font.ttf",
-- heading = "path/to/your/heading-font.ttf",
-- mono = "path/to/your/monospace-font.ttf",
},
colors = {
background = Color.new(0.1, 0.1, 0.15, 1),
text = Color.new(0.9, 0.9, 0.95, 1),
},
})
-- Set the custom theme as active
-- Theme.setActive(customTheme)
-- Create main container
local container = Gui.new({
x = 100,
y = 100,
width = 1720,
height = 880,
backgroundColor = Color.new(0.1, 0.1, 0.15, 1),
cornerRadius = 10,
positioning = "flex",
flexDirection = "vertical",
padding = { top = 40, horizontal = 40, bottom = 40 },
gap = 30,
})
-- Title
Gui.new({
parent = container,
text = "Font Family Demo",
textSize = "3xl",
textColor = Color.new(1, 1, 1, 1),
textAlign = "center",
-- fontFamily = "heading", -- Uncomment to use custom heading font from theme
})
-- Description
Gui.new({
parent = container,
text = "FlexLove supports custom font families through the theme system",
textSize = "md",
textColor = Color.new(0.8, 0.8, 0.9, 1),
textAlign = "center",
-- fontFamily = "default", -- Uncomment to use custom default font from theme
})
-- Example 1: Default System Font
local example1 = Gui.new({
parent = container,
width = "100%",
backgroundColor = Color.new(0.15, 0.15, 0.2, 1),
cornerRadius = 8,
padding = { top = 20, horizontal = 20, bottom = 20 },
positioning = "flex",
flexDirection = "vertical",
gap = 10,
})
Gui.new({
parent = example1,
text = "1. Default System Font",
textSize = "lg",
textColor = Color.new(0.3, 0.7, 1, 1),
})
Gui.new({
parent = example1,
text = "This text uses the default system font (no fontFamily specified)",
textSize = "md",
textColor = Color.new(0.9, 0.9, 0.95, 1),
})
-- Example 2: Font from Theme
local example2 = Gui.new({
parent = container,
width = "100%",
backgroundColor = Color.new(0.15, 0.15, 0.2, 1),
cornerRadius = 8,
padding = { top = 20, horizontal = 20, bottom = 20 },
positioning = "flex",
flexDirection = "vertical",
gap = 10,
})
Gui.new({
parent = example2,
text = "2. Font from Theme",
textSize = "lg",
textColor = Color.new(0.3, 0.7, 1, 1),
})
Gui.new({
parent = example2,
text = "Use fontFamily='default' to reference fonts defined in your theme",
textSize = "md",
textColor = Color.new(0.9, 0.9, 0.95, 1),
-- fontFamily = "default", -- Uncomment when you have fonts defined in theme
})
-- Example 3: Direct Font Path
local example3 = Gui.new({
parent = container,
width = "100%",
backgroundColor = Color.new(0.15, 0.15, 0.2, 1),
cornerRadius = 8,
padding = { top = 20, horizontal = 20, bottom = 20 },
positioning = "flex",
flexDirection = "vertical",
gap = 10,
})
Gui.new({
parent = example3,
text = "3. Direct Font Path",
textSize = "lg",
textColor = Color.new(0.3, 0.7, 1, 1),
})
Gui.new({
parent = example3,
text = "You can also specify a direct path: fontFamily='path/to/font.ttf'",
textSize = "md",
textColor = Color.new(0.9, 0.9, 0.95, 1),
-- fontFamily = "path/to/your/font.ttf", -- Uncomment with actual font path
})
-- Example 4: Different Sizes with Same Font
local example4 = Gui.new({
parent = container,
width = "100%",
backgroundColor = Color.new(0.15, 0.15, 0.2, 1),
cornerRadius = 8,
padding = { top = 20, horizontal = 20, bottom = 20 },
positioning = "flex",
flexDirection = "vertical",
gap = 10,
})
Gui.new({
parent = example4,
text = "4. Multiple Sizes",
textSize = "lg",
textColor = Color.new(0.3, 0.7, 1, 1),
})
local sizeContainer = Gui.new({
parent = example4,
positioning = "flex",
flexDirection = "vertical",
gap = 5,
})
local sizes = { "xs", "sm", "md", "lg", "xl", "xxl" }
for _, size in ipairs(sizes) do
Gui.new({
parent = sizeContainer,
text = "Text size: " .. size,
textSize = size,
textColor = Color.new(0.9, 0.9, 0.95, 1),
-- fontFamily = "default", -- Same font, different sizes
})
end
-- Instructions
Gui.new({
parent = container,
text = "To use custom fonts: 1) Add font files to your project, 2) Define them in theme.fonts, 3) Reference by name in elements",
textSize = "sm",
textColor = Color.new(0.6, 0.6, 0.7, 1),
textAlign = "center",
})
-- LÖVE callbacks
function love.load()
print("Font Family Demo loaded")
print("Add your custom font files and update the theme definition to see custom fonts in action")
end
function love.update(dt)
Gui.update(dt)
end
function love.draw()
love.graphics.clear(0.05, 0.05, 0.08, 1)
Gui.draw()
end
function love.resize(w, h)
Gui.resize()
end

View File

@@ -16,7 +16,6 @@ end
return {
name = "Space Theme",
components = {
-- Panel component
panel = {
@@ -184,4 +183,11 @@ return {
text = Color.new(0.80, 0.90, 1.00), -- soft cool-white for general text
textDark = Color.new(0.35, 0.40, 0.45), -- dimmed gray-blue for secondary text
},
-- 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",
},
}