diff --git a/FlexLove.lua b/FlexLove.lua index 7d95757..6887a7e 100644 --- a/FlexLove.lua +++ b/FlexLove.lua @@ -329,6 +329,7 @@ end ---@field regions {topLeft:ThemeRegion, topCenter:ThemeRegion, topRight:ThemeRegion, middleLeft:ThemeRegion, middleCenter:ThemeRegion, middleRight:ThemeRegion, bottomLeft:ThemeRegion, bottomCenter:ThemeRegion, bottomRight:ThemeRegion} ---@field stretch {horizontal:table, vertical:table} ---@field states table? +---@field contentAutoSizingMultiplier {width:number?, height:number?}? -- Optional: multiplier for auto-sized content dimensions ---@field _loadedAtlas love.Image? -- Internal: cached loaded atlas image ---@class FontFamily @@ -341,6 +342,7 @@ end ---@field components table ---@field colors table? ---@field fonts table? -- Optional: font family definitions (name -> path) +---@field contentAutoSizingMultiplier {width:number?, height:number?}? -- Optional: default multiplier for auto-sized content dimensions ---@class Theme ---@field name string @@ -348,6 +350,7 @@ end ---@field components table ---@field colors table ---@field fonts table -- Font family definitions +---@field contentAutoSizingMultiplier {width:number?, height:number?}? -- Optional: default multiplier for auto-sized content dimensions local Theme = {} Theme.__index = Theme @@ -483,6 +486,7 @@ function Theme.new(definition) self.components = definition.components or {} self.colors = definition.colors or {} self.fonts = definition.fonts or {} + self.contentAutoSizingMultiplier = definition.contentAutoSizingMultiplier or nil -- Load component-specific atlases for componentName, component in pairs(self.components) do @@ -1865,6 +1869,7 @@ Public API methods to access internal state: ---@field disabled boolean? -- Whether the element is disabled (default: false) ---@field active boolean? -- Whether the element is active/focused (for inputs, default: false) ---@field disableHighlight boolean? -- Whether to disable the pressed state highlight overlay (default: false) +---@field contentAutoSizingMultiplier {width:number?, height:number?}? -- Multiplier for auto-sized content dimensions local Element = {} Element.__index = Element @@ -1915,6 +1920,7 @@ Element.__index = Element ---@field disabled boolean? -- Whether the element is disabled (default: false) ---@field active boolean? -- Whether the element is active/focused (for inputs, default: false) ---@field disableHighlight boolean? -- Whether to disable the pressed state highlight overlay (default: false) +---@field contentAutoSizingMultiplier {width:number?, height:number?}? -- Multiplier for auto-sized content dimensions (default: sourced from theme) local ElementProps = {} ---@param props ElementProps @@ -1954,6 +1960,37 @@ function Element.new(props) self.disableHighlight = self.themeComponent ~= nil end + -- Initialize contentAutoSizingMultiplier after theme is set + -- Priority: element props > theme component > theme default + if props.contentAutoSizingMultiplier then + -- Explicitly set on element + self.contentAutoSizingMultiplier = props.contentAutoSizingMultiplier + else + -- Try to source from theme + local themeToUse = self.theme and themes[self.theme] or Theme.getActive() + if themeToUse then + -- First check if themeComponent has a multiplier + if self.themeComponent then + local component = themeToUse.components[self.themeComponent] + if component and component.contentAutoSizingMultiplier then + self.contentAutoSizingMultiplier = component.contentAutoSizingMultiplier + elseif themeToUse.contentAutoSizingMultiplier then + -- Fall back to theme default + self.contentAutoSizingMultiplier = themeToUse.contentAutoSizingMultiplier + else + self.contentAutoSizingMultiplier = nil + end + elseif themeToUse.contentAutoSizingMultiplier then + -- No themeComponent, use theme default + self.contentAutoSizingMultiplier = themeToUse.contentAutoSizingMultiplier + else + self.contentAutoSizingMultiplier = nil + end + else + self.contentAutoSizingMultiplier = nil + end + end + -- Set parent first so it's available for size calculations self.parent = props.parent @@ -3945,11 +3982,19 @@ function Element:calculateTextWidth() local tempFont = FONT_CACHE.get(self.textSize, fontPath) local width = tempFont:getWidth(self.text) + -- Apply contentAutoSizingMultiplier if set + if self.contentAutoSizingMultiplier and self.contentAutoSizingMultiplier.width then + width = width * self.contentAutoSizingMultiplier.width + end return width end local font = love.graphics.getFont() local width = font:getWidth(self.text) + -- Apply contentAutoSizingMultiplier if set + if self.contentAutoSizingMultiplier and self.contentAutoSizingMultiplier.width then + width = width * self.contentAutoSizingMultiplier.width + end return width end @@ -3978,11 +4023,19 @@ function Element:calculateTextHeight() local tempFont = FONT_CACHE.get(self.textSize, fontPath) local height = tempFont:getHeight() + -- Apply contentAutoSizingMultiplier if set + if self.contentAutoSizingMultiplier and self.contentAutoSizingMultiplier.height then + height = height * self.contentAutoSizingMultiplier.height + end return height end local font = love.graphics.getFont() local height = font:getHeight() + -- Apply contentAutoSizingMultiplier if set + if self.contentAutoSizingMultiplier and self.contentAutoSizingMultiplier.height then + height = height * self.contentAutoSizingMultiplier.height + end return height end diff --git a/examples/ContentAutoSizingMultiplierDemo.lua b/examples/ContentAutoSizingMultiplierDemo.lua new file mode 100644 index 0000000..37c8c0c --- /dev/null +++ b/examples/ContentAutoSizingMultiplierDemo.lua @@ -0,0 +1,115 @@ +-- ContentAutoSizingMultiplier Demo +-- Demonstrates how to use contentAutoSizingMultiplier to add padding/spacing +-- to auto-sized text elements without using explicit padding + +local FlexLove = require("libs.FlexLove") + +function love.load() + -- Initialize with space theme (has contentAutoSizingMultiplier: width=1.05, height=1.1) + FlexLove.Gui.init({ + baseScale = { width = 1920, height = 1080 }, + theme = "space", + }) + + -- Example 1: Text element with theme's default multiplier + -- The space theme has width=1.05 (5% wider) and height=1.1 (10% taller) + FlexLove.Element.new({ + x = "10vw", + y = "10vh", + text = "Theme Default Multiplier", + textSize = "lg", + textColor = FlexLove.Color.new(1, 1, 1, 1), + backgroundColor = FlexLove.Color.new(0.2, 0.2, 0.8, 0.8), + themeComponent = "button", -- Uses theme's contentAutoSizingMultiplier + }) + + -- Example 2: Text element with custom multiplier (override theme) + -- This will be 20% wider and 30% taller than the actual text + FlexLove.Element.new({ + x = "10vw", + y = "25vh", + text = "Custom Multiplier (1.2x, 1.3x)", + textSize = "lg", + textColor = FlexLove.Color.new(1, 1, 1, 1), + backgroundColor = FlexLove.Color.new(0.8, 0.2, 0.2, 0.8), + themeComponent = "button", + contentAutoSizingMultiplier = { width = 1.2, height = 1.3 }, + }) + + -- Example 3: Text element with no multiplier + -- This will be exactly the size of the text (no extra space) + FlexLove.Element.new({ + x = "10vw", + y = "40vh", + text = "No Multiplier (exact fit)", + textSize = "lg", + textColor = FlexLove.Color.new(1, 1, 1, 1), + backgroundColor = FlexLove.Color.new(0.2, 0.8, 0.2, 0.8), + contentAutoSizingMultiplier = { width = 1.0, height = 1.0 }, + }) + + -- Example 4: Container with multiple text elements + -- Shows how multiplier affects layout in flex containers + local container = FlexLove.Element.new({ + x = "10vw", + y = "55vh", + positioning = FlexLove.Positioning.FLEX, + flexDirection = FlexLove.FlexDirection.HORIZONTAL, + gap = 10, + backgroundColor = FlexLove.Color.new(0.1, 0.1, 0.1, 0.8), + padding = { horizontal = 20, vertical = 10 }, + }) + + for i = 1, 3 do + FlexLove.Element.new({ + parent = container, + text = "Item " .. i, + textSize = "md", + textColor = FlexLove.Color.new(1, 1, 1, 1), + backgroundColor = FlexLove.Color.new(0.3, 0.3, 0.6, 0.8), + themeComponent = "button", -- Uses theme's multiplier + }) + end + + -- Example 5: Width-only multiplier + -- Useful for creating horizontal padding without vertical padding + FlexLove.Element.new({ + x = "10vw", + y = "75vh", + text = "Wide Button (1.5x width, 1.0x height)", + textSize = "lg", + textColor = FlexLove.Color.new(1, 1, 1, 1), + backgroundColor = FlexLove.Color.new(0.6, 0.3, 0.6, 0.8), + contentAutoSizingMultiplier = { width = 1.5, height = 1.0 }, + }) + + -- Info text + FlexLove.Element.new({ + x = "50vw", + y = "10vh", + text = "contentAutoSizingMultiplier Demo\n\n" + .. "This feature multiplies auto-sized dimensions:\n" + .. "- Theme default: width=1.05, height=1.1\n" + .. "- Can be overridden per element\n" + .. "- Useful for adding visual breathing room\n" + .. "- Works with text and child-based sizing", + textSize = "sm", + textColor = FlexLove.Color.new(0.9, 0.9, 0.9, 1), + backgroundColor = FlexLove.Color.new(0.15, 0.15, 0.15, 0.9), + padding = { horizontal = 20, vertical = 15 }, + }) +end + +function love.update(dt) + FlexLove.Gui.update(dt) +end + +function love.draw() + -- Dark background + love.graphics.clear(0.05, 0.05, 0.1, 1) + FlexLove.Gui.draw() +end + +function love.resize() + FlexLove.Gui.resize() +end diff --git a/themes/space.lua b/themes/space.lua index 216e8db..fa84926 100644 --- a/themes/space.lua +++ b/themes/space.lua @@ -17,43 +17,110 @@ end return { name = "Space Theme", + contentAutoSizingMultiplier = { height = 1.1, width = 1.05 }, components = { - -- Panel component (882x687 with 110px border) - panel = { - atlas = "themes/space/panel.png", + card = { + atlas = "themes/space/card.png", regions = { - -- 9-slice regions for 882x687 image (110-662-110 split) - -- Top row - topLeft = { x = 0, y = 0, w = 110, h = 110 }, - topCenter = { x = 110, y = 0, w = 662, h = 110 }, - topRight = { x = 772, y = 0, w = 110, h = 110 }, - -- Middle row (stretchable) - middleLeft = { x = 0, y = 110, w = 110, h = 467 }, - middleCenter = { x = 110, y = 110, w = 662, h = 467 }, - middleRight = { x = 772, y = 110, w = 110, h = 467 }, - -- Bottom row - bottomLeft = { x = 0, y = 577, w = 110, h = 110 }, - bottomCenter = { x = 110, y = 577, w = 662, h = 110 }, - bottomRight = { x = 772, y = 577, w = 110, h = 110 }, + topLeft = { x = 0, y = 0, width = 100, height = 100 }, + topCenter = { x = 100, y = 0, width = 205, height = 100 }, + topRight = { x = 305, y = 0, width = 100, height = 100 }, + middleLeft = { x = 0, y = 100, width = 100, height = 178 }, + middleCenter = { x = 100, y = 100, width = 205, height = 178 }, + middleRight = { x = 305, y = 100, width = 100, height = 178 }, + bottomLeft = { x = 0, y = 278, width = 100, height = 100 }, + bottomCenter = { x = 100, y = 278, width = 205, height = 100 }, + bottomRight = { x = 305, y = 278, width = 100, height = 100 }, }, stretch = { horizontal = { "topCenter", "middleCenter", "bottomCenter" }, vertical = { "middleLeft", "middleCenter", "middleRight" }, }, }, - - button = { - atlas = "themes/space/interactive.png", + cardv2 = { + atlas = "themes/space/card-v2.png", regions = { - topLeft = { x = 0, y = 0, w = 31, h = 31 }, - topCenter = { x = 31, y = 0, w = 127, h = 31 }, - topRight = { x = 158, y = 0, w = 31, h = 31 }, - middleLeft = { x = 0, y = 31, w = 31, h = 127 }, - middleCenter = { x = 31, y = 31, w = 127, h = 127 }, - middleRight = { x = 158, y = 31, w = 31, h = 127 }, - bottomLeft = { x = 0, y = 158, w = 31, h = 31 }, - bottomCenter = { x = 31, y = 158, w = 127, h = 31 }, - bottomRight = { x = 158, y = 158, w = 31, h = 31 }, + topLeft = { x = 0, y = 0, width = 100, height = 100 }, + topCenter = { x = 100, y = 0, width = 205, height = 100 }, + topRight = { x = 305, y = 0, width = 100, height = 100 }, + middleLeft = { x = 0, y = 100, width = 100, height = 178 }, + middleCenter = { x = 100, y = 100, width = 205, height = 178 }, + middleRight = { x = 305, y = 100, width = 100, height = 178 }, + bottomLeft = { x = 0, y = 278, width = 100, height = 100 }, + bottomCenter = { x = 100, y = 278, width = 205, height = 100 }, + bottomRight = { x = 305, y = 278, width = 100, height = 100 }, + }, + stretch = { + horizontal = { "topCenter", "middleCenter", "bottomCenter" }, + vertical = { "middleLeft", "middleCenter", "middleRight" }, + }, + }, + panel = { + atlas = "themes/space/panel.png", + regions = { + topLeft = { x = 0, y = 0, width = 38, height = 30 }, + topCenter = { x = 38, y = 0, width = 53, height = 30 }, + topRight = { x = 91, y = 0, width = 22, height = 30 }, + middleLeft = { x = 0, y = 30, width = 38, height = 5 }, + middleCenter = { x = 38, y = 30, width = 53, height = 5 }, + middleRight = { x = 91, y = 30, width = 22, height = 5 }, + bottomLeft = { x = 0, y = 35, width = 38, height = 30 }, + bottomCenter = { x = 38, y = 35, width = 53, height = 30 }, + bottomRight = { x = 91, y = 35, width = 22, height = 30 }, + }, + stretch = { + horizontal = { "topCenter", "middleCenter", "bottomCenter" }, + vertical = { "middleLeft", "middleCenter", "middleRight" }, + }, + }, + panelred = { + atlas = "themes/space/panel-red.png", + regions = { + topLeft = { x = 0, y = 0, width = 38, height = 30 }, + topCenter = { x = 38, y = 0, width = 53, height = 30 }, + topRight = { x = 91, y = 0, width = 22, height = 30 }, + middleLeft = { x = 0, y = 30, width = 38, height = 5 }, + middleCenter = { x = 38, y = 30, width = 53, height = 5 }, + middleRight = { x = 91, y = 30, width = 22, height = 5 }, + bottomLeft = { x = 0, y = 35, width = 38, height = 30 }, + bottomCenter = { x = 38, y = 35, width = 53, height = 30 }, + bottomRight = { x = 91, y = 35, width = 22, height = 30 }, + }, + stretch = { + horizontal = { "topCenter", "middleCenter", "bottomCenter" }, + vertical = { "middleLeft", "middleCenter", "middleRight" }, + }, + }, + panelgreen = { + atlas = "themes/space/panel-green.png", + regions = { + topLeft = { x = 0, y = 0, width = 38, height = 30 }, + topCenter = { x = 38, y = 0, width = 53, height = 30 }, + topRight = { x = 91, y = 0, width = 22, height = 30 }, + middleLeft = { x = 0, y = 30, width = 38, height = 5 }, + middleCenter = { x = 38, y = 30, width = 53, height = 5 }, + middleRight = { x = 91, y = 30, width = 22, height = 5 }, + bottomLeft = { x = 0, y = 35, width = 38, height = 30 }, + bottomCenter = { x = 38, y = 35, width = 53, height = 30 }, + bottomRight = { x = 91, y = 35, width = 22, height = 30 }, + }, + stretch = { + horizontal = { "topCenter", "middleCenter", "bottomCenter" }, + vertical = { "middleLeft", "middleCenter", "middleRight" }, + }, + }, + button = { + atlas = "themes/space/button.png", + regions = { + topLeft = { x = 0, y = 0, width = 14, height = 14 }, + topCenter = { x = 14, y = 0, width = 86, height = 14 }, + topRight = { x = 100, y = 0, width = 14, height = 14 }, + middleLeft = { x = 0, y = 14, width = 14, height = 10 }, + middleCenter = { x = 14, y = 14, width = 86, height = 10 }, + middleRight = { x = 100, y = 14, width = 14, height = 10 }, + bottomLeft = { x = 0, y = 24, width = 14, height = 14 }, + bottomCenter = { x = 14, y = 24, width = 86, height = 14 }, + bottomRight = { x = 100, y = 24, width = 14, height = 14 }, }, stretch = { horizontal = { "topCenter", "middleCenter", "bottomCenter" }, @@ -61,17 +128,17 @@ return { }, states = { hover = { - atlas = "themes/space/interactive-hover.png", + atlas = "themes/space/button-hover.png", regions = { - topLeft = { x = 0, y = 0, w = 31, h = 31 }, - topCenter = { x = 31, y = 0, w = 127, h = 31 }, - topRight = { x = 158, y = 0, w = 31, h = 31 }, - middleLeft = { x = 0, y = 31, w = 31, h = 127 }, - middleCenter = { x = 31, y = 31, w = 127, h = 127 }, - middleRight = { x = 158, y = 31, w = 31, h = 127 }, - bottomLeft = { x = 0, y = 158, w = 31, h = 31 }, - bottomCenter = { x = 31, y = 158, w = 127, h = 31 }, - bottomRight = { x = 158, y = 158, w = 31, h = 31 }, + topLeft = { x = 0, y = 0, width = 14, height = 14 }, + topCenter = { x = 14, y = 0, width = 86, height = 14 }, + topRight = { x = 100, y = 0, width = 14, height = 14 }, + middleLeft = { x = 0, y = 14, width = 14, height = 10 }, + middleCenter = { x = 14, y = 14, width = 86, height = 10 }, + middleRight = { x = 100, y = 14, width = 14, height = 10 }, + bottomLeft = { x = 0, y = 24, width = 14, height = 14 }, + bottomCenter = { x = 14, y = 24, width = 86, height = 14 }, + bottomRight = { x = 100, y = 24, width = 14, height = 14 }, }, stretch = { horizontal = { "topCenter", "middleCenter", "bottomCenter" }, @@ -79,17 +146,17 @@ return { }, }, pressed = { - atlas = "themes/space/interactive-pressed.png", + atlas = "themes/space/button-pressed.png", regions = { - topLeft = { x = 0, y = 0, w = 31, h = 31 }, - topCenter = { x = 31, y = 0, w = 127, h = 31 }, - topRight = { x = 158, y = 0, w = 31, h = 31 }, - middleLeft = { x = 0, y = 31, w = 31, h = 127 }, - middleCenter = { x = 31, y = 31, w = 127, h = 127 }, - middleRight = { x = 158, y = 31, w = 31, h = 127 }, - bottomLeft = { x = 0, y = 158, w = 31, h = 31 }, - bottomCenter = { x = 31, y = 158, w = 127, h = 31 }, - bottomRight = { x = 158, y = 158, w = 31, h = 31 }, + topLeft = { x = 0, y = 0, width = 14, height = 14 }, + topCenter = { x = 14, y = 0, width = 86, height = 14 }, + topRight = { x = 100, y = 0, width = 14, height = 14 }, + middleLeft = { x = 0, y = 14, width = 14, height = 10 }, + middleCenter = { x = 14, y = 14, width = 86, height = 10 }, + middleRight = { x = 100, y = 14, width = 14, height = 10 }, + bottomLeft = { x = 0, y = 24, width = 14, height = 14 }, + bottomCenter = { x = 14, y = 24, width = 86, height = 14 }, + bottomRight = { x = 100, y = 24, width = 14, height = 14 }, }, stretch = { horizontal = { "topCenter", "middleCenter", "bottomCenter" }, @@ -97,75 +164,17 @@ return { }, }, disabled = { - atlas = "themes/space/interactive-disabled.png", + atlas = "themes/space/button-disabled.png", regions = { - topLeft = { x = 0, y = 0, w = 31, h = 31 }, - topCenter = { x = 31, y = 0, w = 127, h = 31 }, - topRight = { x = 158, y = 0, w = 31, h = 31 }, - middleLeft = { x = 0, y = 31, w = 31, h = 127 }, - middleCenter = { x = 31, y = 31, w = 127, h = 127 }, - middleRight = { x = 158, y = 31, w = 31, h = 127 }, - bottomLeft = { x = 0, y = 158, w = 31, h = 31 }, - bottomCenter = { x = 31, y = 158, w = 127, h = 31 }, - bottomRight = { x = 158, y = 158, w = 31, h = 31 }, - }, - stretch = { - horizontal = { "topCenter", "middleCenter", "bottomCenter" }, - vertical = { "middleLeft", "middleCenter", "middleRight" }, - }, - }, - }, - }, - - -- Input component with active and disabled states - input = { - atlas = "themes/space/interactive.png", - regions = { - topLeft = { x = 0, y = 0, w = 31, h = 31 }, - topCenter = { x = 31, y = 0, w = 127, h = 31 }, - topRight = { x = 158, y = 0, w = 31, h = 31 }, - middleLeft = { x = 0, y = 31, w = 31, h = 127 }, - middleCenter = { x = 31, y = 31, w = 127, h = 127 }, - middleRight = { x = 158, y = 31, w = 31, h = 127 }, - bottomLeft = { x = 0, y = 158, w = 31, h = 31 }, - bottomCenter = { x = 31, y = 158, w = 127, h = 31 }, - bottomRight = { x = 158, y = 158, w = 31, h = 31 }, - }, - stretch = { - horizontal = { "topCenter", "middleCenter", "bottomCenter" }, - vertical = { "middleLeft", "middleCenter", "middleRight" }, - }, - states = { - active = { - atlas = "themes/space/interactive-hover.png", - regions = { - topLeft = { x = 0, y = 0, w = 31, h = 31 }, - topCenter = { x = 31, y = 0, w = 127, h = 31 }, - topRight = { x = 158, y = 0, w = 31, h = 31 }, - middleLeft = { x = 0, y = 31, w = 31, h = 127 }, - middleCenter = { x = 31, y = 31, w = 127, h = 127 }, - middleRight = { x = 158, y = 31, w = 31, h = 127 }, - bottomLeft = { x = 0, y = 158, w = 31, h = 31 }, - bottomCenter = { x = 31, y = 158, w = 127, h = 31 }, - bottomRight = { x = 158, y = 158, w = 31, h = 31 }, - }, - stretch = { - horizontal = { "topCenter", "middleCenter", "bottomCenter" }, - vertical = { "middleLeft", "middleCenter", "middleRight" }, - }, - }, - disabled = { - atlas = "themes/space/interactive-disabled.png", - regions = { - topLeft = { x = 0, y = 0, w = 31, h = 31 }, - topCenter = { x = 31, y = 0, w = 127, h = 31 }, - topRight = { x = 158, y = 0, w = 31, h = 31 }, - middleLeft = { x = 0, y = 31, w = 31, h = 127 }, - middleCenter = { x = 31, y = 31, w = 127, h = 127 }, - middleRight = { x = 158, y = 31, w = 31, h = 127 }, - bottomLeft = { x = 0, y = 158, w = 31, h = 31 }, - bottomCenter = { x = 31, y = 158, w = 127, h = 31 }, - bottomRight = { x = 158, y = 158, w = 31, h = 31 }, + topLeft = { x = 0, y = 0, width = 14, height = 14 }, + topCenter = { x = 14, y = 0, width = 86, height = 14 }, + topRight = { x = 100, y = 0, width = 14, height = 14 }, + middleLeft = { x = 0, y = 14, width = 14, height = 10 }, + middleCenter = { x = 14, y = 14, width = 86, height = 10 }, + middleRight = { x = 100, y = 14, width = 14, height = 10 }, + bottomLeft = { x = 0, y = 24, width = 14, height = 14 }, + bottomCenter = { x = 14, y = 24, width = 86, height = 14 }, + bottomRight = { x = 100, y = 24, width = 14, height = 14 }, }, stretch = { horizontal = { "topCenter", "middleCenter", "bottomCenter" },