text scaling basics
This commit is contained in:
94
FlexLove.lua
94
FlexLove.lua
@@ -132,7 +132,7 @@ function Units.parse(value)
|
||||
unit = "px"
|
||||
end
|
||||
|
||||
local validUnits = { px = true, ["%"] = true, vw = true, vh = true }
|
||||
local validUnits = { px = true, ["%"] = true, vw = true, vh = true, ew = true, eh = true }
|
||||
if not validUnits[unit] then
|
||||
return num, "px"
|
||||
end
|
||||
@@ -462,6 +462,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? -- Font size for text content
|
||||
---@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
|
||||
---@field callback function? -- Callback function for click events
|
||||
@@ -492,7 +493,8 @@ Element.__index = Element
|
||||
---@field titleColor Color? -- Color of the text content (default: black)
|
||||
---@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 for text content (default: nil)
|
||||
---@field textSize number|string? -- Font size for text content (default: auto-scaled)
|
||||
---@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)
|
||||
---@field justifyContent JustifyContent? -- Alignment of items along main axis (default: FLEX_START)
|
||||
@@ -629,17 +631,61 @@ function Element.new(props)
|
||||
self.padding = Units.resolveSpacing(props.padding, self.width, self.height)
|
||||
self.margin = Units.resolveSpacing(props.margin, self.width, self.height)
|
||||
|
||||
-- Store original textSize units
|
||||
-- Store original textSize units and constraints
|
||||
self.minTextSize = props.minTextSize
|
||||
self.maxTextSize = props.maxTextSize
|
||||
|
||||
-- Auto-scale text by default (can be disabled with autoScaleText = false)
|
||||
if props.autoScaleText == nil then
|
||||
self.autoScaleText = true
|
||||
else
|
||||
self.autoScaleText = props.autoScaleText
|
||||
end
|
||||
|
||||
if props.textSize then
|
||||
if type(props.textSize) == "string" then
|
||||
local value, unit = Units.parse(props.textSize)
|
||||
self.units.textSize = { value = value, unit = unit }
|
||||
self.textSize = Units.resolve(value, unit, viewportWidth, viewportHeight, nil)
|
||||
|
||||
-- Resolve textSize based on unit type
|
||||
if unit == "%" or unit == "vh" then
|
||||
-- Percentage and vh are relative to viewport height
|
||||
self.textSize = Units.resolve(value, unit, viewportWidth, viewportHeight, viewportHeight)
|
||||
elseif unit == "vw" then
|
||||
-- vw is relative to viewport width
|
||||
self.textSize = Units.resolve(value, unit, viewportWidth, viewportHeight, viewportWidth)
|
||||
elseif unit == "ew" then
|
||||
-- Element width relative (will be resolved after width is set)
|
||||
self.textSize = (value / 100) * self.width
|
||||
elseif unit == "eh" then
|
||||
-- Element height relative (will be resolved after height is set)
|
||||
self.textSize = (value / 100) * self.height
|
||||
else
|
||||
self.textSize = Units.resolve(value, unit, viewportWidth, viewportHeight, nil)
|
||||
end
|
||||
else
|
||||
self.textSize = props.textSize
|
||||
self.units.textSize = { value = props.textSize, unit = "px" }
|
||||
end
|
||||
else
|
||||
self.units.textSize = { value = 12, unit = "px" }
|
||||
-- No textSize specified - use auto-scaling default
|
||||
if self.autoScaleText then
|
||||
-- Default to 1.5vh (1.5% of viewport height) for auto-scaling
|
||||
self.textSize = (1.5 / 100) * viewportHeight
|
||||
self.units.textSize = { value = 1.5, unit = "vh" }
|
||||
else
|
||||
-- Fixed 12px when auto-scaling is disabled
|
||||
self.textSize = 12
|
||||
self.units.textSize = { value = nil, unit = "px" }
|
||||
end
|
||||
end
|
||||
|
||||
-- Apply min/max constraints
|
||||
if self.minTextSize and self.textSize < self.minTextSize then
|
||||
self.textSize = self.minTextSize
|
||||
end
|
||||
if self.maxTextSize and self.textSize > self.maxTextSize then
|
||||
self.textSize = self.maxTextSize
|
||||
end
|
||||
|
||||
-- Store original spacing values for proper resize handling
|
||||
@@ -1369,10 +1415,10 @@ function Element:draw()
|
||||
love.graphics.setColor(textColorWithOpacity:toRGBA())
|
||||
|
||||
local origFont = love.graphics.getFont()
|
||||
local tempFont
|
||||
if self.textSize then
|
||||
tempFont = love.graphics.newFont(self.textSize)
|
||||
love.graphics.setFont(tempFont)
|
||||
-- Use cached font instead of creating new one every frame
|
||||
local font = FONT_CACHE.get(self.textSize)
|
||||
love.graphics.setFont(font)
|
||||
end
|
||||
local font = love.graphics.getFont()
|
||||
local textWidth = font:getWidth(self.text)
|
||||
@@ -1520,10 +1566,34 @@ function Element:recalculateUnits(newViewportWidth, newViewportHeight)
|
||||
end
|
||||
end
|
||||
|
||||
-- Recalculate textSize if using viewport units
|
||||
if self.units.textSize.unit ~= "px" then
|
||||
self.textSize =
|
||||
Units.resolve(self.units.textSize.value, self.units.textSize.unit, newViewportWidth, newViewportHeight, nil)
|
||||
-- Recalculate textSize if auto-scaling is enabled or using viewport/element-relative units
|
||||
if self.autoScaleText and self.units.textSize.value and self.units.textSize.unit ~= "px" then
|
||||
local unit = self.units.textSize.unit
|
||||
local value = self.units.textSize.value
|
||||
|
||||
if unit == "%" or unit == "vh" then
|
||||
-- Percentage and vh are relative to viewport height
|
||||
self.textSize = Units.resolve(value, unit, newViewportWidth, newViewportHeight, newViewportHeight)
|
||||
elseif unit == "vw" then
|
||||
-- vw is relative to viewport width
|
||||
self.textSize = Units.resolve(value, unit, newViewportWidth, newViewportHeight, newViewportWidth)
|
||||
elseif unit == "ew" then
|
||||
-- Element width relative
|
||||
self.textSize = (value / 100) * self.width
|
||||
elseif unit == "eh" then
|
||||
-- Element height relative
|
||||
self.textSize = (value / 100) * self.height
|
||||
else
|
||||
self.textSize = Units.resolve(value, unit, newViewportWidth, newViewportHeight, nil)
|
||||
end
|
||||
|
||||
-- Apply min/max constraints
|
||||
if self.minTextSize and self.textSize < self.minTextSize then
|
||||
self.textSize = self.minTextSize
|
||||
end
|
||||
if self.maxTextSize and self.textSize > self.maxTextSize then
|
||||
self.textSize = self.maxTextSize
|
||||
end
|
||||
end
|
||||
|
||||
-- Recalculate gap if using viewport or percentage units
|
||||
|
||||
131
examples/TextAutoScaling.lua
Normal file
131
examples/TextAutoScaling.lua
Normal file
@@ -0,0 +1,131 @@
|
||||
-- Example demonstrating text auto-scaling feature
|
||||
-- Text automatically scales proportionally with window size by default
|
||||
|
||||
package.path = package.path .. ";?.lua"
|
||||
require("testing/loveStub")
|
||||
local FlexLove = require("FlexLove")
|
||||
local Gui = FlexLove.GUI
|
||||
local Color = FlexLove.Color
|
||||
|
||||
print("=== Text Auto-Scaling Examples ===\n")
|
||||
|
||||
-- Example 1: Default auto-scaling (enabled by default)
|
||||
print("1. Default Auto-Scaling (no textSize specified)")
|
||||
print(" Text will scale proportionally with window size")
|
||||
local button1 = Gui.new({
|
||||
x = 10,
|
||||
y = 10,
|
||||
padding = { horizontal = 16, vertical = 8 },
|
||||
text = "Auto-Scaled Button",
|
||||
textAlign = "center",
|
||||
border = { top = true, right = true, bottom = true, left = true },
|
||||
borderColor = Color.new(1, 1, 1),
|
||||
textColor = Color.new(1, 1, 1),
|
||||
})
|
||||
print(" Initial size (800x600): textSize = " .. button1.textSize .. "px")
|
||||
button1:resize(1600, 1200)
|
||||
print(" After resize (1600x1200): textSize = " .. button1.textSize .. "px")
|
||||
print(" Scaling factor: " .. (button1.textSize / 9.0) .. "x\n")
|
||||
|
||||
-- Example 2: Disable auto-scaling for fixed text size
|
||||
print("2. Auto-Scaling Disabled (autoScaleText = false)")
|
||||
print(" Text remains fixed at 12px regardless of window size")
|
||||
Gui.destroy()
|
||||
local button2 = Gui.new({
|
||||
x = 10,
|
||||
y = 60,
|
||||
padding = { horizontal = 16, vertical = 8 },
|
||||
text = "Fixed Size Button",
|
||||
textAlign = "center",
|
||||
autoScaleText = false,
|
||||
border = { top = true, right = true, bottom = true, left = true },
|
||||
borderColor = Color.new(1, 1, 1),
|
||||
textColor = Color.new(1, 1, 1),
|
||||
})
|
||||
print(" Initial size (800x600): textSize = " .. button2.textSize .. "px")
|
||||
button2:resize(1600, 1200)
|
||||
print(" After resize (1600x1200): textSize = " .. button2.textSize .. "px")
|
||||
print(" No scaling applied\n")
|
||||
|
||||
-- Example 3: Custom auto-scaling with viewport units
|
||||
print("3. Custom Auto-Scaling (textSize = '2vh')")
|
||||
print(" Text scales at 2% of viewport height")
|
||||
Gui.destroy()
|
||||
local title = Gui.new({
|
||||
x = 10,
|
||||
y = 110,
|
||||
text = "Large Title",
|
||||
textSize = "2vh",
|
||||
textColor = Color.new(1, 1, 1),
|
||||
})
|
||||
print(" Initial size (800x600): textSize = " .. title.textSize .. "px")
|
||||
title:resize(1600, 1200)
|
||||
print(" After resize (1600x1200): textSize = " .. title.textSize .. "px")
|
||||
print(" Scaling factor: " .. (title.textSize / 12.0) .. "x\n")
|
||||
|
||||
-- Example 4: Fixed pixel size (still auto-scales if using viewport units)
|
||||
print("4. Fixed Pixel Size (textSize = 20)")
|
||||
print(" Explicit pixel values don't scale")
|
||||
Gui.destroy()
|
||||
local button3 = Gui.new({
|
||||
x = 10,
|
||||
y = 160,
|
||||
padding = { horizontal = 16, vertical = 8 },
|
||||
text = "20px Button",
|
||||
textSize = 20,
|
||||
textAlign = "center",
|
||||
border = { top = true, right = true, bottom = true, left = true },
|
||||
borderColor = Color.new(1, 1, 1),
|
||||
textColor = Color.new(1, 1, 1),
|
||||
})
|
||||
print(" Initial size (800x600): textSize = " .. button3.textSize .. "px")
|
||||
button3:resize(1600, 1200)
|
||||
print(" After resize (1600x1200): textSize = " .. button3.textSize .. "px")
|
||||
print(" Fixed at 20px\n")
|
||||
|
||||
-- Example 5: Element-relative scaling
|
||||
print("5. Element-Relative Scaling (textSize = '10ew')")
|
||||
print(" Text scales at 10% of element width")
|
||||
Gui.destroy()
|
||||
local box = Gui.new({
|
||||
x = 10,
|
||||
y = 210,
|
||||
width = 200,
|
||||
height = 100,
|
||||
text = "Responsive Box",
|
||||
textSize = "10ew",
|
||||
textAlign = "center",
|
||||
background = Color.new(0.2, 0.2, 0.2),
|
||||
textColor = Color.new(1, 1, 1),
|
||||
})
|
||||
print(" Initial (width=200): textSize = " .. box.textSize .. "px")
|
||||
box.width = 400
|
||||
box:resize(800, 600)
|
||||
print(" After width change (width=400): textSize = " .. box.textSize .. "px")
|
||||
print(" Scales with element size\n")
|
||||
|
||||
-- Example 6: Combining auto-scaling with min/max constraints
|
||||
print("6. Auto-Scaling with Constraints")
|
||||
print(" Text scales between 10px and 24px")
|
||||
Gui.destroy()
|
||||
local constrained = Gui.new({
|
||||
x = 10,
|
||||
y = 260,
|
||||
text = "Constrained Text",
|
||||
textSize = "3vh",
|
||||
minTextSize = 10,
|
||||
maxTextSize = 24,
|
||||
textColor = Color.new(1, 1, 1),
|
||||
})
|
||||
print(" Initial (3vh of 600): textSize = " .. constrained.textSize .. "px")
|
||||
constrained:resize(1600, 1200)
|
||||
print(" After resize (3vh of 1200 = 36px, clamped): textSize = " .. constrained.textSize .. "px")
|
||||
print(" Clamped to maxTextSize = 24px\n")
|
||||
|
||||
print("=== Summary ===")
|
||||
print("• Auto-scaling is ENABLED by default")
|
||||
print("• Default scaling: 1.5vh (1.5% of viewport height)")
|
||||
print("• Disable with: autoScaleText = false")
|
||||
print("• Custom scaling: use vh, vw, %, ew, or eh units")
|
||||
print("• Fixed sizes: use pixel values (e.g., textSize = 16)")
|
||||
print("• Constraints: use minTextSize and maxTextSize")
|
||||
@@ -107,7 +107,7 @@ function TestTextScaling.testVhTextSize()
|
||||
end
|
||||
|
||||
function TestTextScaling.testNoTextSize()
|
||||
-- Create an element without textSize specified
|
||||
-- Create an element without textSize specified (auto-scaling enabled by default)
|
||||
local element = Gui.new({
|
||||
id = "testElement",
|
||||
width = 100,
|
||||
@@ -115,13 +115,32 @@ function TestTextScaling.testNoTextSize()
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
-- Check initial state - should default to some value
|
||||
luaunit.assertEquals(element.units.textSize.value, nil)
|
||||
luaunit.assertEquals(element.textSize, 12) -- Default fallback
|
||||
-- Check initial state - should auto-scale by default (1.5vh)
|
||||
luaunit.assertEquals(element.autoScaleText, true)
|
||||
luaunit.assertEquals(element.units.textSize.value, 1.5)
|
||||
luaunit.assertEquals(element.units.textSize.unit, "vh")
|
||||
luaunit.assertEquals(element.textSize, 9.0) -- 1.5% of 600px
|
||||
|
||||
-- Resize should not affect default textSize
|
||||
-- Resize should scale the text
|
||||
element:resize(1600, 1200)
|
||||
luaunit.assertEquals(element.textSize, 12)
|
||||
luaunit.assertEquals(element.textSize, 18.0) -- 1.5% of 1200px
|
||||
|
||||
-- Test with auto-scaling disabled
|
||||
local elementNoScale = Gui.new({
|
||||
id = "testElementNoScale",
|
||||
width = 100,
|
||||
height = 50,
|
||||
text = "Hello World",
|
||||
autoScaleText = false,
|
||||
})
|
||||
|
||||
luaunit.assertEquals(elementNoScale.autoScaleText, false)
|
||||
luaunit.assertEquals(elementNoScale.units.textSize.value, nil)
|
||||
luaunit.assertEquals(elementNoScale.textSize, 12) -- Fixed 12px
|
||||
|
||||
-- Resize should not affect textSize when auto-scaling is disabled
|
||||
elementNoScale:resize(1600, 1200)
|
||||
luaunit.assertEquals(elementNoScale.textSize, 12)
|
||||
end
|
||||
|
||||
-- Edge case tests
|
||||
|
||||
@@ -17,20 +17,22 @@ function love_helper.graphics.getDimensions()
|
||||
end
|
||||
|
||||
function love_helper.graphics.newFont(size)
|
||||
-- Ensure size is a number
|
||||
local fontSize = tonumber(size) or 12
|
||||
-- Return a mock font object with basic methods
|
||||
return {
|
||||
getWidth = function(self, text)
|
||||
-- Handle both colon and dot syntax
|
||||
if type(self) == "string" then
|
||||
-- Called with dot syntax: font.getWidth(text)
|
||||
return #self * size / 2
|
||||
return #self * fontSize / 2
|
||||
else
|
||||
-- Called with colon syntax: font:getWidth(text)
|
||||
return #text * size / 2
|
||||
return #text * fontSize / 2
|
||||
end
|
||||
end,
|
||||
getHeight = function()
|
||||
return size
|
||||
return fontSize
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user