text size presets
This commit is contained in:
268
FlexLove.lua
268
FlexLove.lua
@@ -102,9 +102,34 @@ local enums = {
|
|||||||
},
|
},
|
||||||
---@enum FlexWrap
|
---@enum FlexWrap
|
||||||
FlexWrap = { NOWRAP = "nowrap", WRAP = "wrap", WRAP_REVERSE = "wrap-reverse" },
|
FlexWrap = { NOWRAP = "nowrap", WRAP = "wrap", WRAP_REVERSE = "wrap-reverse" },
|
||||||
|
---@enum TextSize
|
||||||
|
TextSize = {
|
||||||
|
XXS = "xxs",
|
||||||
|
XS = "xs",
|
||||||
|
SM = "sm",
|
||||||
|
MD = "md",
|
||||||
|
LG = "lg",
|
||||||
|
XL = "xl",
|
||||||
|
XXL = "xxl",
|
||||||
|
XL3 = "3xl",
|
||||||
|
XL4 = "4xl",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local Positioning, FlexDirection, JustifyContent, AlignContent, AlignItems, TextAlign, AlignSelf, JustifySelf, FlexWrap =
|
-- Text size preset mappings (in vh units for auto-scaling)
|
||||||
|
local TEXT_SIZE_PRESETS = {
|
||||||
|
xxs = 0.75, -- 0.75vh
|
||||||
|
xs = 1.25, -- 1.25vh
|
||||||
|
sm = 1.75, -- 1.75vh
|
||||||
|
md = 2.25, -- 2.25vh (default)
|
||||||
|
lg = 2.75, -- 2.75vh
|
||||||
|
xl = 3.5, -- 3.5vh
|
||||||
|
xxl = 4.5, -- 4.5vh
|
||||||
|
["3xl"] = 5.0, -- 5vh
|
||||||
|
["4xl"] = 7.0, -- 7vh
|
||||||
|
}
|
||||||
|
|
||||||
|
local Positioning, FlexDirection, JustifyContent, AlignContent, AlignItems, TextAlign, AlignSelf, JustifySelf, FlexWrap, TextSize =
|
||||||
enums.Positioning,
|
enums.Positioning,
|
||||||
enums.FlexDirection,
|
enums.FlexDirection,
|
||||||
enums.JustifyContent,
|
enums.JustifyContent,
|
||||||
@@ -113,7 +138,8 @@ local Positioning, FlexDirection, JustifyContent, AlignContent, AlignItems, Text
|
|||||||
enums.TextAlign,
|
enums.TextAlign,
|
||||||
enums.AlignSelf,
|
enums.AlignSelf,
|
||||||
enums.JustifySelf,
|
enums.JustifySelf,
|
||||||
enums.FlexWrap
|
enums.FlexWrap,
|
||||||
|
enums.TextSize
|
||||||
|
|
||||||
-- ====================
|
-- ====================
|
||||||
-- Units System
|
-- Units System
|
||||||
@@ -332,10 +358,18 @@ function Grid.layoutGridItems(element)
|
|||||||
local childTotalHeight = child.height + child.padding.top + child.padding.bottom
|
local childTotalHeight = child.height + child.padding.top + child.padding.bottom
|
||||||
child.x = cellX + (cellWidth - childTotalWidth) / 2
|
child.x = cellX + (cellWidth - childTotalWidth) / 2
|
||||||
child.y = cellY + (cellHeight - childTotalHeight) / 2
|
child.y = cellY + (cellHeight - childTotalHeight) / 2
|
||||||
elseif effectiveAlignItems == AlignItems.FLEX_START or effectiveAlignItems == "flex-start" or effectiveAlignItems == "start" then
|
elseif
|
||||||
|
effectiveAlignItems == AlignItems.FLEX_START
|
||||||
|
or effectiveAlignItems == "flex-start"
|
||||||
|
or effectiveAlignItems == "start"
|
||||||
|
then
|
||||||
child.x = cellX
|
child.x = cellX
|
||||||
child.y = cellY
|
child.y = cellY
|
||||||
elseif effectiveAlignItems == AlignItems.FLEX_END or effectiveAlignItems == "flex-end" or effectiveAlignItems == "end" then
|
elseif
|
||||||
|
effectiveAlignItems == AlignItems.FLEX_END
|
||||||
|
or effectiveAlignItems == "flex-end"
|
||||||
|
or effectiveAlignItems == "end"
|
||||||
|
then
|
||||||
local childTotalWidth = child.width + child.padding.left + child.padding.right
|
local childTotalWidth = child.width + child.padding.left + child.padding.right
|
||||||
local childTotalHeight = child.height + child.padding.top + child.padding.bottom
|
local childTotalHeight = child.height + child.padding.top + child.padding.bottom
|
||||||
child.x = cellX + cellWidth - childTotalWidth
|
child.x = cellX + cellWidth - childTotalWidth
|
||||||
@@ -433,6 +467,9 @@ function Gui.destroy()
|
|||||||
win:destroy()
|
win:destroy()
|
||||||
end
|
end
|
||||||
Gui.topElements = {}
|
Gui.topElements = {}
|
||||||
|
-- Reset base scale and scale factors
|
||||||
|
Gui.baseScale = nil
|
||||||
|
Gui.scaleFactors = { x = 1.0, y = 1.0 }
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Simple GUI library for LOVE2D
|
-- Simple GUI library for LOVE2D
|
||||||
@@ -583,6 +620,25 @@ function FONT_CACHE.getFont(textSize)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Text Size Utilities
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
--- Resolve text size preset to viewport units
|
||||||
|
---@param sizeValue string|number
|
||||||
|
---@return number, string -- Returns value and unit ("vh" for presets, original unit otherwise)
|
||||||
|
local function resolveTextSizePreset(sizeValue)
|
||||||
|
if type(sizeValue) == "string" then
|
||||||
|
-- Check if it's a preset
|
||||||
|
local preset = TEXT_SIZE_PRESETS[sizeValue]
|
||||||
|
if preset then
|
||||||
|
return preset, "vh"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Not a preset, return nil to indicate normal parsing should occur
|
||||||
|
return nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
---@class Border
|
---@class Border
|
||||||
---@field top boolean?
|
---@field top boolean?
|
||||||
---@field right boolean?
|
---@field right boolean?
|
||||||
@@ -625,7 +681,7 @@ end
|
|||||||
---@field flexWrap FlexWrap -- Whether children wrap to multiple lines (default: NOWRAP)
|
---@field flexWrap FlexWrap -- Whether children wrap to multiple lines (default: NOWRAP)
|
||||||
---@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? -- Resolved font size for text content in pixels
|
||||||
---@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
|
||||||
@@ -661,7 +717,7 @@ 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: auto-scaled)
|
---@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 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)
|
||||||
@@ -711,7 +767,6 @@ function Element.new(props)
|
|||||||
self.opacity = props.opacity or 1
|
self.opacity = props.opacity or 1
|
||||||
|
|
||||||
self.text = props.text
|
self.text = props.text
|
||||||
self.textSize = props.textSize or 12
|
|
||||||
self.textAlign = props.textAlign or TextAlign.START
|
self.textAlign = props.textAlign or TextAlign.START
|
||||||
|
|
||||||
--- self positioning ---
|
--- self positioning ---
|
||||||
@@ -747,6 +802,95 @@ function Element.new(props)
|
|||||||
-- Get scale factors from Gui (will be used later)
|
-- Get scale factors from Gui (will be used later)
|
||||||
local scaleX, scaleY = Gui.getScaleFactors()
|
local scaleX, scaleY = Gui.getScaleFactors()
|
||||||
|
|
||||||
|
-- Store original textSize units and constraints
|
||||||
|
self.minTextSize = props.minTextSize
|
||||||
|
self.maxTextSize = props.maxTextSize
|
||||||
|
|
||||||
|
-- Set autoScaleText BEFORE textSize processing (needed for correct initialization)
|
||||||
|
if props.autoScaleText == nil then
|
||||||
|
self.autoScaleText = true
|
||||||
|
else
|
||||||
|
self.autoScaleText = props.autoScaleText
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle textSize BEFORE width/height calculation (needed for auto-sizing)
|
||||||
|
if props.textSize then
|
||||||
|
if type(props.textSize) == "string" then
|
||||||
|
-- Check if it's a preset first
|
||||||
|
local presetValue, presetUnit = resolveTextSizePreset(props.textSize)
|
||||||
|
local value, unit
|
||||||
|
|
||||||
|
if presetValue then
|
||||||
|
-- It's a preset, use the preset value and unit
|
||||||
|
value, unit = presetValue, presetUnit
|
||||||
|
self.units.textSize = { value = value, unit = unit }
|
||||||
|
else
|
||||||
|
-- Not a preset, parse normally
|
||||||
|
value, unit = Units.parse(props.textSize)
|
||||||
|
self.units.textSize = { value = value, unit = unit }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
-- ew is relative to element width (use viewport width as fallback during initialization)
|
||||||
|
-- Will be re-resolved after width is set
|
||||||
|
self.textSize = (value / 100) * viewportWidth
|
||||||
|
elseif unit == "eh" then
|
||||||
|
-- eh is relative to element height (use viewport height as fallback during initialization)
|
||||||
|
-- Will be re-resolved after height is set
|
||||||
|
self.textSize = (value / 100) * viewportHeight
|
||||||
|
elseif unit == "px" then
|
||||||
|
-- Pixel units
|
||||||
|
self.textSize = value
|
||||||
|
else
|
||||||
|
error("Unknown textSize unit: " .. unit)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Validate pixel textSize value
|
||||||
|
if props.textSize <= 0 then
|
||||||
|
error("textSize must be greater than 0, got: " .. tostring(props.textSize))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pixel textSize value
|
||||||
|
if self.autoScaleText and Gui.baseScale then
|
||||||
|
-- With base scaling: store original pixel value and scale relative to base resolution
|
||||||
|
self.units.textSize = { value = props.textSize, unit = "px" }
|
||||||
|
self.textSize = props.textSize * scaleY
|
||||||
|
elseif self.autoScaleText then
|
||||||
|
-- Without base scaling: convert to viewport units for auto-scaling
|
||||||
|
-- Calculate what percentage of viewport height this represents
|
||||||
|
local vhValue = (props.textSize / viewportHeight) * 100
|
||||||
|
self.units.textSize = { value = vhValue, unit = "vh" }
|
||||||
|
self.textSize = props.textSize -- Initial size is the specified pixel value
|
||||||
|
else
|
||||||
|
-- No auto-scaling: apply base scaling if set, otherwise use raw value
|
||||||
|
self.textSize = Gui.baseScale and (props.textSize * scaleY) or props.textSize
|
||||||
|
self.units.textSize = { value = props.textSize, unit = "px" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- No textSize specified - use auto-scaling default
|
||||||
|
if self.autoScaleText and Gui.baseScale then
|
||||||
|
-- With base scaling: use 12px as default and scale
|
||||||
|
self.units.textSize = { value = 12, unit = "px" }
|
||||||
|
self.textSize = 12 * scaleY
|
||||||
|
elseif self.autoScaleText then
|
||||||
|
-- Without base scaling: default to 1.5vh (1.5% of viewport height)
|
||||||
|
self.units.textSize = { value = 1.5, unit = "vh" }
|
||||||
|
self.textSize = (1.5 / 100) * viewportHeight
|
||||||
|
else
|
||||||
|
-- No auto-scaling: use 12px with optional base scaling
|
||||||
|
self.textSize = Gui.baseScale and (12 * scaleY) or 12
|
||||||
|
self.units.textSize = { value = nil, unit = "px" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Handle width (both w and width properties, prefer w if both exist)
|
-- Handle width (both w and width properties, prefer w if both exist)
|
||||||
local widthProp = props.width
|
local widthProp = props.width
|
||||||
if widthProp then
|
if widthProp then
|
||||||
@@ -808,67 +952,15 @@ 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 and constraints
|
-- Re-resolve ew/eh textSize units now that width/height are set
|
||||||
self.minTextSize = props.minTextSize
|
if props.textSize and type(props.textSize) == "string" then
|
||||||
self.maxTextSize = props.maxTextSize
|
local value, unit = Units.parse(props.textSize)
|
||||||
|
if unit == "ew" then
|
||||||
-- Auto-scale text by default (can be disabled with autoScaleText = false)
|
-- Element width relative (now that width is set)
|
||||||
if props.autoScaleText == nil then
|
self.textSize = (value / 100) * self.width
|
||||||
self.autoScaleText = true
|
elseif unit == "eh" then
|
||||||
else
|
-- Element height relative (now that height is set)
|
||||||
self.autoScaleText = props.autoScaleText
|
self.textSize = (value / 100) * self.height
|
||||||
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 }
|
|
||||||
|
|
||||||
-- 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
|
|
||||||
-- Validate pixel textSize value
|
|
||||||
if props.textSize <= 0 then
|
|
||||||
error("textSize must be greater than 0, got: " .. tostring(props.textSize))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Pixel textSize value
|
|
||||||
if self.autoScaleText then
|
|
||||||
-- Convert pixel value to viewport units for auto-scaling
|
|
||||||
-- Calculate what percentage of viewport height this represents
|
|
||||||
local vhValue = (props.textSize / viewportHeight) * 100
|
|
||||||
self.units.textSize = { value = vhValue, unit = "vh" }
|
|
||||||
self.textSize = props.textSize -- Initial size is the specified pixel value
|
|
||||||
else
|
|
||||||
-- Apply base scaling to pixel text sizes (no auto-scaling)
|
|
||||||
self.textSize = Gui.baseScale and (props.textSize * scaleY) or props.textSize
|
|
||||||
self.units.textSize = { value = props.textSize, unit = "px" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- 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 (with base scaling if set)
|
|
||||||
self.textSize = Gui.baseScale and (12 * scaleY) or 12
|
|
||||||
self.units.textSize = { value = nil, unit = "px" }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1505,10 +1597,7 @@ function Element:layoutChildren()
|
|||||||
child.y = self.y + self.padding.top + currentCrossPos
|
child.y = self.y + self.padding.top + currentCrossPos
|
||||||
elseif effectiveAlign == AlignItems.CENTER then
|
elseif effectiveAlign == AlignItems.CENTER then
|
||||||
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
||||||
child.y = self.y
|
child.y = self.y + self.padding.top + currentCrossPos + ((lineHeight - childTotalHeight) / 2)
|
||||||
+ self.padding.top
|
|
||||||
+ currentCrossPos
|
|
||||||
+ ((lineHeight - childTotalHeight) / 2)
|
|
||||||
elseif effectiveAlign == AlignItems.FLEX_END then
|
elseif effectiveAlign == AlignItems.FLEX_END then
|
||||||
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
|
||||||
child.y = self.y + self.padding.top + currentCrossPos + lineHeight - childTotalHeight
|
child.y = self.y + self.padding.top + currentCrossPos + lineHeight - childTotalHeight
|
||||||
@@ -1543,10 +1632,7 @@ function Element:layoutChildren()
|
|||||||
child.x = self.x + self.padding.left + currentCrossPos
|
child.x = self.x + self.padding.left + currentCrossPos
|
||||||
elseif effectiveAlign == AlignItems.CENTER then
|
elseif effectiveAlign == AlignItems.CENTER then
|
||||||
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
||||||
child.x = self.x
|
child.x = self.x + self.padding.left + currentCrossPos + ((lineHeight - childTotalWidth) / 2)
|
||||||
+ self.padding.left
|
|
||||||
+ currentCrossPos
|
|
||||||
+ ((lineHeight - childTotalWidth) / 2)
|
|
||||||
elseif effectiveAlign == AlignItems.FLEX_END then
|
elseif effectiveAlign == AlignItems.FLEX_END then
|
||||||
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
|
||||||
child.x = self.x + self.padding.left + currentCrossPos + lineHeight - childTotalWidth
|
child.x = self.x + self.padding.left + currentCrossPos + lineHeight - childTotalWidth
|
||||||
@@ -1640,12 +1726,7 @@ function Element:draw()
|
|||||||
Color.new(self.borderColor.r, self.borderColor.g, self.borderColor.b, self.borderColor.a * self.opacity)
|
Color.new(self.borderColor.r, self.borderColor.g, self.borderColor.b, self.borderColor.a * self.opacity)
|
||||||
love.graphics.setColor(borderColorWithOpacity:toRGBA())
|
love.graphics.setColor(borderColorWithOpacity:toRGBA())
|
||||||
if self.border.top then
|
if self.border.top then
|
||||||
love.graphics.line(
|
love.graphics.line(self.x, self.y, self.x + self.width + self.padding.left + self.padding.right, self.y)
|
||||||
self.x,
|
|
||||||
self.y,
|
|
||||||
self.x + self.width + self.padding.left + self.padding.right,
|
|
||||||
self.y
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
if self.border.bottom then
|
if self.border.bottom then
|
||||||
love.graphics.line(
|
love.graphics.line(
|
||||||
@@ -1656,12 +1737,7 @@ function Element:draw()
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
if self.border.left then
|
if self.border.left then
|
||||||
love.graphics.line(
|
love.graphics.line(self.x, self.y, self.x, self.y + self.height + self.padding.top + self.padding.bottom)
|
||||||
self.x,
|
|
||||||
self.y,
|
|
||||||
self.x,
|
|
||||||
self.y + self.height + self.padding.top + self.padding.bottom
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
if self.border.right then
|
if self.border.right then
|
||||||
love.graphics.line(
|
love.graphics.line(
|
||||||
@@ -1851,11 +1927,17 @@ function Element:recalculateUnits(newViewportWidth, newViewportHeight)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Recalculate textSize if auto-scaling is enabled or using viewport/element-relative units
|
-- 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
|
if self.autoScaleText and self.units.textSize.value then
|
||||||
local unit = self.units.textSize.unit
|
local unit = self.units.textSize.unit
|
||||||
local value = self.units.textSize.value
|
local value = self.units.textSize.value
|
||||||
|
|
||||||
if unit == "%" or unit == "vh" then
|
if unit == "px" and Gui.baseScale then
|
||||||
|
-- With base scaling: scale pixel values relative to base resolution
|
||||||
|
self.textSize = value * scaleY
|
||||||
|
elseif unit == "px" then
|
||||||
|
-- Without base scaling but auto-scaling enabled: text doesn't scale
|
||||||
|
self.textSize = value
|
||||||
|
elseif unit == "%" or unit == "vh" then
|
||||||
-- Percentage and vh are relative to viewport height
|
-- Percentage and vh are relative to viewport height
|
||||||
self.textSize = Units.resolve(value, unit, newViewportWidth, newViewportHeight, newViewportHeight)
|
self.textSize = Units.resolve(value, unit, newViewportWidth, newViewportHeight, newViewportHeight)
|
||||||
elseif unit == "vw" then
|
elseif unit == "vw" then
|
||||||
@@ -1887,7 +1969,7 @@ function Element:recalculateUnits(newViewportWidth, newViewportHeight)
|
|||||||
self.textSize = 1 -- Minimum 1px
|
self.textSize = 1 -- Minimum 1px
|
||||||
end
|
end
|
||||||
elseif self.units.textSize.unit == "px" and self.units.textSize.value and Gui.baseScale then
|
elseif self.units.textSize.unit == "px" and self.units.textSize.value and Gui.baseScale then
|
||||||
-- Reapply base scaling to pixel text sizes
|
-- No auto-scaling but base scaling is set: reapply base scaling to pixel text sizes
|
||||||
self.textSize = self.units.textSize.value * scaleY
|
self.textSize = self.units.textSize.value * scaleY
|
||||||
|
|
||||||
-- Protect against too-small text sizes (minimum 1px)
|
-- Protect against too-small text sizes (minimum 1px)
|
||||||
@@ -1983,6 +2065,10 @@ end
|
|||||||
|
|
||||||
---@return number
|
---@return number
|
||||||
function Element:calculateTextHeight()
|
function Element:calculateTextHeight()
|
||||||
|
if self.text == nil then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
if self.textSize then
|
if self.textSize then
|
||||||
local tempFont = FONT_CACHE.get(self.textSize)
|
local tempFont = FONT_CACHE.get(self.textSize)
|
||||||
local height = tempFont:getHeight()
|
local height = tempFont:getHeight()
|
||||||
|
|||||||
167
examples/TextSizePresets.lua
Normal file
167
examples/TextSizePresets.lua
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
-- Example demonstrating text size presets
|
||||||
|
-- FlexLove provides convenient size presets that automatically scale with viewport
|
||||||
|
|
||||||
|
package.path = package.path .. ";?.lua"
|
||||||
|
require("testing/loveStub")
|
||||||
|
local FlexLove = require("FlexLove")
|
||||||
|
local Gui = FlexLove.GUI
|
||||||
|
local Color = FlexLove.Color
|
||||||
|
|
||||||
|
print("=== Text Size Presets Examples ===\n")
|
||||||
|
|
||||||
|
-- Example 1: All size presets
|
||||||
|
print("1. All Text Size Presets")
|
||||||
|
print(" Demonstrating all available size presets\n")
|
||||||
|
|
||||||
|
local presets = {
|
||||||
|
{ name = "xxs", vh = 0.75 },
|
||||||
|
{ name = "xs", vh = 1.0 },
|
||||||
|
{ name = "sm", vh = 1.25 },
|
||||||
|
{ name = "md", vh = 1.5 },
|
||||||
|
{ name = "lg", vh = 2.0 },
|
||||||
|
{ name = "xl", vh = 2.5 },
|
||||||
|
{ name = "xxl", vh = 3.0 },
|
||||||
|
{ name = "3xl", vh = 4.0 },
|
||||||
|
{ name = "4xl", vh = 5.0 },
|
||||||
|
}
|
||||||
|
|
||||||
|
print("At viewport height 600px:")
|
||||||
|
for _, preset in ipairs(presets) do
|
||||||
|
local element = Gui.new({
|
||||||
|
text = "Sample Text (" .. preset.name .. ")",
|
||||||
|
textSize = preset.name,
|
||||||
|
textColor = Color.new(1, 1, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
local expectedSize = (preset.vh / 100) * 600
|
||||||
|
print(string.format(" %4s: textSize = %.2fpx (expected: %.2fpx = %.2fvh)",
|
||||||
|
preset.name, element.textSize, expectedSize, preset.vh))
|
||||||
|
|
||||||
|
-- Verify it matches expected size
|
||||||
|
assert(math.abs(element.textSize - expectedSize) < 0.01,
|
||||||
|
string.format("Size mismatch for %s: got %.2f, expected %.2f", preset.name, element.textSize, expectedSize))
|
||||||
|
|
||||||
|
element:destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
print("\n2. Auto-Scaling Behavior")
|
||||||
|
print(" Text size presets automatically scale with viewport\n")
|
||||||
|
|
||||||
|
Gui.destroy()
|
||||||
|
local mdElement = Gui.new({
|
||||||
|
text = "Medium Text",
|
||||||
|
textSize = "md",
|
||||||
|
textColor = Color.new(1, 1, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
print(" 'md' preset at 600px viewport: " .. mdElement.textSize .. "px")
|
||||||
|
mdElement:resize(1200, 1200)
|
||||||
|
print(" 'md' preset at 1200px viewport: " .. mdElement.textSize .. "px")
|
||||||
|
print(" Scaling factor: " .. (mdElement.textSize / 9.0) .. "x\n")
|
||||||
|
|
||||||
|
-- Example 3: Combining presets with other properties
|
||||||
|
print("3. Presets with Flex Layout")
|
||||||
|
print(" Using presets in a practical layout\n")
|
||||||
|
|
||||||
|
Gui.destroy()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 10,
|
||||||
|
y = 10,
|
||||||
|
width = 400,
|
||||||
|
height = 300,
|
||||||
|
positioning = "flex",
|
||||||
|
flexDirection = "vertical",
|
||||||
|
gap = 10,
|
||||||
|
padding = { horizontal = 20, vertical = 20 },
|
||||||
|
background = Color.new(0.1, 0.1, 0.1),
|
||||||
|
})
|
||||||
|
|
||||||
|
local title = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
text = "Title (xl)",
|
||||||
|
textSize = "xl",
|
||||||
|
textColor = Color.new(1, 1, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
local subtitle = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
text = "Subtitle (lg)",
|
||||||
|
textSize = "lg",
|
||||||
|
textColor = Color.new(0.8, 0.8, 0.8),
|
||||||
|
})
|
||||||
|
|
||||||
|
local body = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
text = "Body text (md)",
|
||||||
|
textSize = "md",
|
||||||
|
textColor = Color.new(0.7, 0.7, 0.7),
|
||||||
|
})
|
||||||
|
|
||||||
|
local caption = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
text = "Caption (sm)",
|
||||||
|
textSize = "sm",
|
||||||
|
textColor = Color.new(0.5, 0.5, 0.5),
|
||||||
|
})
|
||||||
|
|
||||||
|
print(" Title: " .. title.textSize .. "px")
|
||||||
|
print(" Subtitle: " .. subtitle.textSize .. "px")
|
||||||
|
print(" Body: " .. body.textSize .. "px")
|
||||||
|
print(" Caption: " .. caption.textSize .. "px\n")
|
||||||
|
|
||||||
|
-- Example 4: Presets vs Custom Units
|
||||||
|
print("4. Presets vs Custom Units")
|
||||||
|
print(" Comparing preset convenience with custom units\n")
|
||||||
|
|
||||||
|
Gui.destroy()
|
||||||
|
local preset = Gui.new({
|
||||||
|
text = "Using preset 'lg'",
|
||||||
|
textSize = "lg",
|
||||||
|
textColor = Color.new(1, 1, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
local custom = Gui.new({
|
||||||
|
text = "Using custom '2vh'",
|
||||||
|
textSize = "2vh",
|
||||||
|
textColor = Color.new(1, 1, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
print(" Preset 'lg': " .. preset.textSize .. "px (2vh)")
|
||||||
|
print(" Custom '2vh': " .. custom.textSize .. "px")
|
||||||
|
print(" Both are equivalent!\n")
|
||||||
|
|
||||||
|
-- Example 5: Responsive Typography
|
||||||
|
print("5. Responsive Typography Scale")
|
||||||
|
print(" Building a complete type scale with presets\n")
|
||||||
|
|
||||||
|
Gui.destroy()
|
||||||
|
local typeScale = {
|
||||||
|
{ label = "Display", preset = "4xl" },
|
||||||
|
{ label = "Heading 1", preset = "3xl" },
|
||||||
|
{ label = "Heading 2", preset = "xxl" },
|
||||||
|
{ label = "Heading 3", preset = "xl" },
|
||||||
|
{ label = "Heading 4", preset = "lg" },
|
||||||
|
{ label = "Body Large", preset = "md" },
|
||||||
|
{ label = "Body", preset = "sm" },
|
||||||
|
{ label = "Caption", preset = "xs" },
|
||||||
|
{ label = "Fine Print", preset = "xxs" },
|
||||||
|
}
|
||||||
|
|
||||||
|
print(" Typography Scale at 600px viewport:")
|
||||||
|
for _, item in ipairs(typeScale) do
|
||||||
|
local element = Gui.new({
|
||||||
|
text = item.label,
|
||||||
|
textSize = item.preset,
|
||||||
|
textColor = Color.new(1, 1, 1),
|
||||||
|
})
|
||||||
|
print(string.format(" %-15s (%4s): %.2fpx", item.label, item.preset, element.textSize))
|
||||||
|
element:destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
print("\n=== Summary ===")
|
||||||
|
print("• Text size presets: xxs, xs, sm, md, lg, xl, xxl, 3xl, 4xl")
|
||||||
|
print("• All presets use viewport-relative units (vh)")
|
||||||
|
print("• Automatically scale with window size")
|
||||||
|
print("• Provide consistent typography scales")
|
||||||
|
print("• Can be mixed with custom units (px, vh, vw, %, ew, eh)")
|
||||||
|
print("• Default preset when no textSize specified: md (1.5vh)")
|
||||||
Reference in New Issue
Block a user