text scaling basics
This commit is contained in:
94
FlexLove.lua
94
FlexLove.lua
@@ -132,7 +132,7 @@ function Units.parse(value)
|
|||||||
unit = "px"
|
unit = "px"
|
||||||
end
|
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
|
if not validUnits[unit] then
|
||||||
return num, "px"
|
return num, "px"
|
||||||
end
|
end
|
||||||
@@ -462,6 +462,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? -- Font size for text content
|
---@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 transform TransformProps -- Transform properties for animations and styling
|
||||||
---@field transition TransitionProps -- Transition settings for animations
|
---@field transition TransitionProps -- Transition settings for animations
|
||||||
---@field callback function? -- Callback function for click events
|
---@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 titleColor Color? -- Color of the text content (default: black)
|
||||||
---@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 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 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)
|
||||||
---@field justifyContent JustifyContent? -- Alignment of items along main axis (default: FLEX_START)
|
---@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.padding = Units.resolveSpacing(props.padding, self.width, self.height)
|
||||||
self.margin = Units.resolveSpacing(props.margin, 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 props.textSize then
|
||||||
if type(props.textSize) == "string" then
|
if type(props.textSize) == "string" then
|
||||||
local value, unit = Units.parse(props.textSize)
|
local value, unit = Units.parse(props.textSize)
|
||||||
self.units.textSize = { value = value, unit = unit }
|
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
|
else
|
||||||
|
self.textSize = Units.resolve(value, unit, viewportWidth, viewportHeight, nil)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.textSize = props.textSize
|
||||||
self.units.textSize = { value = props.textSize, unit = "px" }
|
self.units.textSize = { value = props.textSize, unit = "px" }
|
||||||
end
|
end
|
||||||
else
|
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
|
end
|
||||||
|
|
||||||
-- Store original spacing values for proper resize handling
|
-- Store original spacing values for proper resize handling
|
||||||
@@ -1369,10 +1415,10 @@ function Element:draw()
|
|||||||
love.graphics.setColor(textColorWithOpacity:toRGBA())
|
love.graphics.setColor(textColorWithOpacity:toRGBA())
|
||||||
|
|
||||||
local origFont = love.graphics.getFont()
|
local origFont = love.graphics.getFont()
|
||||||
local tempFont
|
|
||||||
if self.textSize then
|
if self.textSize then
|
||||||
tempFont = love.graphics.newFont(self.textSize)
|
-- Use cached font instead of creating new one every frame
|
||||||
love.graphics.setFont(tempFont)
|
local font = FONT_CACHE.get(self.textSize)
|
||||||
|
love.graphics.setFont(font)
|
||||||
end
|
end
|
||||||
local font = love.graphics.getFont()
|
local font = love.graphics.getFont()
|
||||||
local textWidth = font:getWidth(self.text)
|
local textWidth = font:getWidth(self.text)
|
||||||
@@ -1520,10 +1566,34 @@ function Element:recalculateUnits(newViewportWidth, newViewportHeight)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Recalculate textSize if using viewport units
|
-- Recalculate textSize if auto-scaling is enabled or using viewport/element-relative units
|
||||||
if self.units.textSize.unit ~= "px" then
|
if self.autoScaleText and self.units.textSize.value and self.units.textSize.unit ~= "px" then
|
||||||
self.textSize =
|
local unit = self.units.textSize.unit
|
||||||
Units.resolve(self.units.textSize.value, self.units.textSize.unit, newViewportWidth, newViewportHeight, nil)
|
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
|
end
|
||||||
|
|
||||||
-- Recalculate gap if using viewport or percentage units
|
-- 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
|
end
|
||||||
|
|
||||||
function TestTextScaling.testNoTextSize()
|
function TestTextScaling.testNoTextSize()
|
||||||
-- Create an element without textSize specified
|
-- Create an element without textSize specified (auto-scaling enabled by default)
|
||||||
local element = Gui.new({
|
local element = Gui.new({
|
||||||
id = "testElement",
|
id = "testElement",
|
||||||
width = 100,
|
width = 100,
|
||||||
@@ -115,13 +115,32 @@ function TestTextScaling.testNoTextSize()
|
|||||||
text = "Hello World",
|
text = "Hello World",
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Check initial state - should default to some value
|
-- Check initial state - should auto-scale by default (1.5vh)
|
||||||
luaunit.assertEquals(element.units.textSize.value, nil)
|
luaunit.assertEquals(element.autoScaleText, true)
|
||||||
luaunit.assertEquals(element.textSize, 12) -- Default fallback
|
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)
|
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
|
end
|
||||||
|
|
||||||
-- Edge case tests
|
-- Edge case tests
|
||||||
|
|||||||
@@ -17,20 +17,22 @@ function love_helper.graphics.getDimensions()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function love_helper.graphics.newFont(size)
|
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 a mock font object with basic methods
|
||||||
return {
|
return {
|
||||||
getWidth = function(self, text)
|
getWidth = function(self, text)
|
||||||
-- Handle both colon and dot syntax
|
-- Handle both colon and dot syntax
|
||||||
if type(self) == "string" then
|
if type(self) == "string" then
|
||||||
-- Called with dot syntax: font.getWidth(text)
|
-- Called with dot syntax: font.getWidth(text)
|
||||||
return #self * size / 2
|
return #self * fontSize / 2
|
||||||
else
|
else
|
||||||
-- Called with colon syntax: font:getWidth(text)
|
-- Called with colon syntax: font:getWidth(text)
|
||||||
return #text * size / 2
|
return #text * fontSize / 2
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
getHeight = function()
|
getHeight = function()
|
||||||
return size
|
return fontSize
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user