fonts
This commit is contained in:
74
FlexLove.lua
74
FlexLove.lua
@@ -65,17 +65,23 @@ end
|
|||||||
---@field states table<string, ThemeComponent>?
|
---@field states table<string, ThemeComponent>?
|
||||||
---@field _loadedAtlas love.Image? -- Internal: cached loaded atlas image
|
---@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
|
---@class ThemeDefinition
|
||||||
---@field name string
|
---@field name string
|
||||||
---@field atlas string|love.Image? -- Optional: global atlas (can be overridden per component)
|
---@field atlas string|love.Image? -- Optional: global atlas (can be overridden per component)
|
||||||
---@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>? -- Optional: font family definitions (name -> path)
|
||||||
|
|
||||||
---@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 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
|
||||||
local Theme = {}
|
local Theme = {}
|
||||||
Theme.__index = Theme
|
Theme.__index = Theme
|
||||||
|
|
||||||
@@ -151,6 +157,7 @@ function Theme.new(definition)
|
|||||||
|
|
||||||
self.components = definition.components or {}
|
self.components = definition.components or {}
|
||||||
self.colors = definition.colors or {}
|
self.colors = definition.colors or {}
|
||||||
|
self.fonts = definition.fonts or {}
|
||||||
|
|
||||||
-- Load component-specific atlases
|
-- Load component-specific atlases
|
||||||
for componentName, component in pairs(self.components) do
|
for componentName, component in pairs(self.components) do
|
||||||
@@ -1174,20 +1181,40 @@ local FONT_CACHE = {}
|
|||||||
|
|
||||||
--- Create or get a font from cache
|
--- Create or get a font from cache
|
||||||
---@param size number
|
---@param size number
|
||||||
|
---@param fontPath string? -- Optional: path to font file
|
||||||
---@return love.Font
|
---@return love.Font
|
||||||
function FONT_CACHE.get(size)
|
function FONT_CACHE.get(size, fontPath)
|
||||||
if not FONT_CACHE[size] then
|
-- Create cache key from size and font path
|
||||||
FONT_CACHE[size] = love.graphics.newFont(size)
|
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
|
end
|
||||||
return FONT_CACHE[size]
|
else
|
||||||
|
-- Load default font
|
||||||
|
FONT_CACHE[cacheKey] = love.graphics.newFont(size)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return FONT_CACHE[cacheKey]
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get font for text size (cached)
|
--- Get font for text size (cached)
|
||||||
---@param textSize number?
|
---@param textSize number?
|
||||||
|
---@param fontPath string? -- Optional: path to font file
|
||||||
---@return love.Font
|
---@return love.Font
|
||||||
function FONT_CACHE.getFont(textSize)
|
function FONT_CACHE.getFont(textSize, fontPath)
|
||||||
if textSize then
|
if textSize then
|
||||||
return FONT_CACHE.get(textSize)
|
return FONT_CACHE.get(textSize, fontPath)
|
||||||
else
|
else
|
||||||
return love.graphics.getFont()
|
return love.graphics.getFont()
|
||||||
end
|
end
|
||||||
@@ -1256,6 +1283,7 @@ end
|
|||||||
---@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 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
|
||||||
---@field transition TransitionProps -- Transition settings for animations
|
---@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 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 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: ABSOLUTE)
|
---@field positioning Positioning? -- Layout positioning mode (default: ABSOLUTE)
|
||||||
---@field flexDirection FlexDirection? -- Direction of flex layout (default: HORIZONTAL)
|
---@field flexDirection FlexDirection? -- Direction of flex layout (default: HORIZONTAL)
|
||||||
@@ -1458,6 +1487,18 @@ function Element.new(props)
|
|||||||
self.autoScaleText = props.autoScaleText
|
self.autoScaleText = props.autoScaleText
|
||||||
end
|
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)
|
-- Handle textSize BEFORE width/height calculation (needed for auto-sizing)
|
||||||
if props.textSize then
|
if props.textSize then
|
||||||
if type(props.textSize) == "string" then
|
if type(props.textSize) == "string" then
|
||||||
@@ -2543,8 +2584,27 @@ function Element:draw()
|
|||||||
|
|
||||||
local origFont = love.graphics.getFont()
|
local origFont = love.graphics.getFont()
|
||||||
if self.textSize then
|
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
|
-- 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)
|
love.graphics.setFont(font)
|
||||||
end
|
end
|
||||||
local font = love.graphics.getFont()
|
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 {
|
return {
|
||||||
name = "Space Theme",
|
name = "Space Theme",
|
||||||
|
|
||||||
components = {
|
components = {
|
||||||
-- Panel component
|
-- Panel component
|
||||||
panel = {
|
panel = {
|
||||||
@@ -184,4 +183,11 @@ return {
|
|||||||
text = Color.new(0.80, 0.90, 1.00), -- soft cool-white for general text
|
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
|
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