fonts
This commit is contained in:
74
FlexLove.lua
74
FlexLove.lua
@@ -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
|
||||
return FONT_CACHE[size]
|
||||
else
|
||||
-- Load default font
|
||||
FONT_CACHE[cacheKey] = love.graphics.newFont(size)
|
||||
end
|
||||
end
|
||||
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
214
examples/FontFamilyDemo.lua
Normal 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
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user