diff --git a/FlexLove.lua b/FlexLove.lua index dd9c50e..0161893 100644 --- a/FlexLove.lua +++ b/FlexLove.lua @@ -104,10 +104,10 @@ local function getFlexLoveBasePath() fsPath = fsPath:gsub("^%./", "") -- Remove trailing / fsPath = fsPath:gsub("/$", "") - + -- Convert filesystem path to Lua module path local modulePath = fsPath:gsub("/", ".") - + return modulePath, fsPath end end @@ -127,7 +127,7 @@ local function resolveImagePath(imagePath) if imagePath:match("^/") or imagePath:match("^[A-Z]:") then return imagePath end - + -- Otherwise, make it relative to FlexLove's location return FLEXLOVE_FILESYSTEM_PATH .. "/" .. imagePath end @@ -263,6 +263,102 @@ function Theme.getComponent(componentName, state) return component end +-- ==================== +-- Rounded Rectangle Helper +-- ==================== + +local RoundedRect = {} + +--- Generate points for a rounded rectangle +---@param x number +---@param y number +---@param width number +---@param height number +---@param cornerRadius {topLeft:number, topRight:number, bottomLeft:number, bottomRight:number} +---@param segments number? -- Number of segments per corner arc (default: 10) +---@return table -- Array of vertices for love.graphics.polygon +function RoundedRect.getPoints(x, y, width, height, cornerRadius, segments) + segments = segments or 10 + local points = {} + + -- Helper to add arc points + local function addArc(cx, cy, radius, startAngle, endAngle) + if radius <= 0 then + table.insert(points, cx) + table.insert(points, cy) + return + end + + for i = 0, segments do + local angle = startAngle + (endAngle - startAngle) * (i / segments) + table.insert(points, cx + math.cos(angle) * radius) + table.insert(points, cy + math.sin(angle) * radius) + end + end + + local r1 = math.min(cornerRadius.topLeft, width / 2, height / 2) + local r2 = math.min(cornerRadius.topRight, width / 2, height / 2) + local r3 = math.min(cornerRadius.bottomRight, width / 2, height / 2) + local r4 = math.min(cornerRadius.bottomLeft, width / 2, height / 2) + + -- Top-right corner + addArc(x + width - r2, y + r2, r2, -math.pi / 2, 0) + + -- Bottom-right corner + addArc(x + width - r3, y + height - r3, r3, 0, math.pi / 2) + + -- Bottom-left corner + addArc(x + r4, y + height - r4, r4, math.pi / 2, math.pi) + + -- Top-left corner + addArc(x + r1, y + r1, r1, math.pi, math.pi * 1.5) + + return points +end + +--- Draw a filled rounded rectangle +---@param mode string -- "fill" or "line" +---@param x number +---@param y number +---@param width number +---@param height number +---@param cornerRadius {topLeft:number, topRight:number, bottomLeft:number, bottomRight:number} +function RoundedRect.draw(mode, x, y, width, height, cornerRadius) + -- Check if any corners are rounded + local hasRoundedCorners = cornerRadius.topLeft > 0 + or cornerRadius.topRight > 0 + or cornerRadius.bottomLeft > 0 + or cornerRadius.bottomRight > 0 + + if not hasRoundedCorners then + -- No rounded corners, use regular rectangle + love.graphics.rectangle(mode, x, y, width, height) + return + end + + local points = RoundedRect.getPoints(x, y, width, height, cornerRadius) + + if mode == "fill" then + love.graphics.polygon("fill", points) + else + -- For line mode, draw the outline + love.graphics.polygon("line", points) + end +end + +--- Create a stencil function for rounded rectangle clipping +---@param x number +---@param y number +---@param width number +---@param height number +---@param cornerRadius {topLeft:number, topRight:number, bottomLeft:number, bottomRight:number} +---@return function +function RoundedRect.stencilFunction(x, y, width, height, cornerRadius) + return function() + RoundedRect.draw("fill", x, y, width, height, cornerRadius) + end +end + -- ==================== -- NineSlice Renderer -- ==================== @@ -312,11 +408,11 @@ function NineSlice.draw(component, atlas, x, y, width, height, opacity) -- Check if element is too small and needs proportional scaling local scaleDownX = 1 local scaleDownY = 1 - + if width < minWidth then scaleDownX = width / minWidth end - + if height < minHeight then scaleDownY = height / minHeight end @@ -336,13 +432,37 @@ function NineSlice.draw(component, atlas, x, y, width, height, opacity) love.graphics.draw(atlas, makeQuad(regions.topLeft), x, y, 0, scaleDownX, scaleDownY) -- Top-right corner - love.graphics.draw(atlas, makeQuad(regions.topRight), x + width - scaledRightCornerWidth, y, 0, scaleDownX, scaleDownY) + love.graphics.draw( + atlas, + makeQuad(regions.topRight), + x + width - scaledRightCornerWidth, + y, + 0, + scaleDownX, + scaleDownY + ) -- Bottom-left corner - love.graphics.draw(atlas, makeQuad(regions.bottomLeft), x, y + height - scaledBottomLeftHeight, 0, scaleDownX, scaleDownY) + love.graphics.draw( + atlas, + makeQuad(regions.bottomLeft), + x, + y + height - scaledBottomLeftHeight, + 0, + scaleDownX, + scaleDownY + ) -- Bottom-right corner - love.graphics.draw(atlas, makeQuad(regions.bottomRight), x + width - scaledRightCornerWidth, y + height - scaledBottomRightHeight, 0, scaleDownX, scaleDownY) + love.graphics.draw( + atlas, + makeQuad(regions.bottomRight), + x + width - scaledRightCornerWidth, + y + height - scaledBottomRightHeight, + 0, + scaleDownX, + scaleDownY + ) -- Top edge (stretched) if centerWidth > 0 then @@ -388,7 +508,15 @@ function NineSlice.draw(component, atlas, x, y, width, height, opacity) if centerWidth > 0 and centerHeight > 0 then local scaleX = centerWidth / regions.middleCenter.w local scaleY = centerHeight / regions.middleCenter.h - love.graphics.draw(atlas, makeQuad(regions.middleCenter), x + scaledCornerWidth, y + scaledCornerHeight, 0, scaleX, scaleY) + love.graphics.draw( + atlas, + makeQuad(regions.middleCenter), + x + scaledCornerWidth, + y + scaledCornerHeight, + 0, + scaleX, + scaleY + ) end -- Reset color @@ -1083,6 +1211,7 @@ end ---@field opacity number ---@field borderColor Color -- Color of the border ---@field backgroundColor Color -- Background color of the element +---@field cornerRadius number|{topLeft:number?, topRight:number?, bottomLeft:number?, bottomRight:number?}? -- Corner radius for rounded corners (default: 0) ---@field prevGameSize {width:number, height:number} -- Previous game size for resize calculations ---@field text string? -- Text content to display in the element ---@field textColor Color -- Color of the text content @@ -1117,6 +1246,7 @@ end ---@field _themeState string? -- Current theme state (normal, hover, pressed, active, disabled) ---@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) local Element = {} Element.__index = Element @@ -1136,6 +1266,7 @@ Element.__index = Element ---@field borderColor Color? -- Color of the border (default: black) ---@field opacity number? ---@field backgroundColor Color? -- Background color (default: transparent) +---@field cornerRadius number|{topLeft:number?, topRight:number?, bottomLeft:number?, bottomRight:number?}? -- Corner radius: number (all corners) or table for individual corners (default: 0) ---@field gap number|string? -- Space between children elements (default: 10) ---@field padding {top:number|string?, right:number|string?, bottom:number|string?, left:number|string?, horizontal: number|string?, vertical:number|string?}? -- Padding around children (default: {top=0, right=0, bottom=0, left=0}) ---@field margin {top:number|string?, right:number|string?, bottom:number|string?, left:number|string?, horizontal: number|string?, vertical:number|string?}? -- Margin around children (default: {top=0, right=0, bottom=0, left=0}) @@ -1164,6 +1295,7 @@ Element.__index = Element ---@field themeComponent string? -- Theme component to use (e.g., "panel", "button", "input"). If nil, no theme is applied ---@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) local ElementProps = {} ---@param props ElementProps @@ -1195,6 +1327,14 @@ function Element.new(props) self.disabled = props.disabled or false self.active = props.active or false + -- disableHighlight defaults to true when using themeComponent (themes handle their own visual feedback) + -- Can be explicitly overridden by setting props.disableHighlight + if props.disableHighlight ~= nil then + self.disableHighlight = props.disableHighlight + else + self.disableHighlight = self.themeComponent ~= nil + end + -- Set parent first so it's available for size calculations self.parent = props.parent @@ -1217,6 +1357,32 @@ function Element.new(props) self.backgroundColor = props.backgroundColor or Color.new(0, 0, 0, 0) self.opacity = props.opacity or 1 + -- Handle cornerRadius (can be number or table) + if props.cornerRadius then + if type(props.cornerRadius) == "number" then + self.cornerRadius = { + topLeft = props.cornerRadius, + topRight = props.cornerRadius, + bottomLeft = props.cornerRadius, + bottomRight = props.cornerRadius, + } + else + self.cornerRadius = { + topLeft = props.cornerRadius.topLeft or 0, + topRight = props.cornerRadius.topRight or 0, + bottomLeft = props.cornerRadius.bottomLeft or 0, + bottomRight = props.cornerRadius.bottomRight or 0, + } + end + else + self.cornerRadius = { + topLeft = 0, + topRight = 0, + bottomLeft = 0, + bottomRight = 0, + } + end + self.text = props.text self.textAlign = props.textAlign or TextAlign.START @@ -2156,7 +2322,8 @@ function Element:draw() if self.animation then local anim = self.animation:interpolate() if anim.opacity then - drawBackgroundColor = Color.new(self.backgroundColor.r, self.backgroundColor.g, self.backgroundColor.b, anim.opacity) + drawBackgroundColor = + Color.new(self.backgroundColor.r, self.backgroundColor.g, self.backgroundColor.b, anim.opacity) end end @@ -2166,12 +2333,13 @@ function Element:draw() local backgroundWithOpacity = Color.new(drawBackgroundColor.r, drawBackgroundColor.g, drawBackgroundColor.b, drawBackgroundColor.a * self.opacity) love.graphics.setColor(backgroundWithOpacity:toRGBA()) - love.graphics.rectangle( + RoundedRect.draw( "fill", self.x, self.y, self.width + self.padding.left + self.padding.right, - self.height + self.padding.top + self.padding.bottom + self.height + self.padding.top + self.padding.bottom, + self.cornerRadius ) -- LAYER 2: Draw theme on top of backgroundColor (if theme exists) @@ -2232,27 +2400,44 @@ function Element:draw() local borderColorWithOpacity = Color.new(self.borderColor.r, self.borderColor.g, self.borderColor.b, self.borderColor.a * self.opacity) love.graphics.setColor(borderColorWithOpacity:toRGBA()) - if self.border.top then - love.graphics.line(self.x, self.y, self.x + self.width + self.padding.left + self.padding.right, self.y) - end - if self.border.bottom then - love.graphics.line( + + -- Check if all borders are enabled + local allBorders = self.border.top and self.border.bottom and self.border.left and self.border.right + + if allBorders then + -- Draw complete rounded rectangle border + RoundedRect.draw( + "line", self.x, - self.y + self.height + self.padding.top + self.padding.bottom, - self.x + self.width + self.padding.left + self.padding.right, - self.y + self.height + self.padding.top + self.padding.bottom - ) - end - if self.border.left then - love.graphics.line(self.x, self.y, self.x, self.y + self.height + self.padding.top + self.padding.bottom) - end - if self.border.right then - love.graphics.line( - self.x + self.width + self.padding.left + self.padding.right, self.y, - self.x + self.width + self.padding.left + self.padding.right, - self.y + self.height + self.padding.top + self.padding.bottom + self.width + self.padding.left + self.padding.right, + self.height + self.padding.top + self.padding.bottom, + self.cornerRadius ) + else + -- Draw individual borders (without rounded corners for partial borders) + if self.border.top then + love.graphics.line(self.x, self.y, self.x + self.width + self.padding.left + self.padding.right, self.y) + end + if self.border.bottom then + love.graphics.line( + self.x, + self.y + self.height + self.padding.top + self.padding.bottom, + self.x + self.width + self.padding.left + self.padding.right, + self.y + self.height + self.padding.top + self.padding.bottom + ) + end + if self.border.left then + love.graphics.line(self.x, self.y, self.x, self.y + self.height + self.padding.top + self.padding.bottom) + end + if self.border.right then + love.graphics.line( + self.x + self.width + self.padding.left + self.padding.right, + self.y, + self.x + self.width + self.padding.left + self.padding.right, + self.y + self.height + self.padding.top + self.padding.bottom + ) + end end -- Draw element text if present @@ -2294,8 +2479,8 @@ function Element:draw() end end - -- Draw visual feedback when element is pressed (if it has a callback) - if self.callback then + -- Draw visual feedback when element is pressed (if it has a callback and highlight is not disabled) + if self.callback and not self.disableHighlight then -- Check if any button is pressed local anyPressed = false for _, pressed in pairs(self._pressed) do @@ -2306,12 +2491,13 @@ function Element:draw() end if anyPressed then love.graphics.setColor(0.5, 0.5, 0.5, 0.3 * self.opacity) -- Semi-transparent gray for pressed state with opacity - love.graphics.rectangle( + RoundedRect.draw( "fill", self.x, self.y, self.width + self.padding.left + self.padding.right, - self.height + self.padding.top + self.padding.bottom + self.height + self.padding.top + self.padding.bottom, + self.cornerRadius ) end end @@ -2325,8 +2511,35 @@ function Element:draw() return a.z < b.z end) - for _, child in ipairs(sortedChildren) do - child:draw() + -- Check if we need to clip children to rounded corners + local hasRoundedCorners = self.cornerRadius.topLeft > 0 + or self.cornerRadius.topRight > 0 + or self.cornerRadius.bottomLeft > 0 + or self.cornerRadius.bottomRight > 0 + + if hasRoundedCorners and #sortedChildren > 0 then + -- Use stencil to clip children to rounded rectangle + local stencilFunc = RoundedRect.stencilFunction( + self.x, + self.y, + self.width + self.padding.left + self.padding.right, + self.height + self.padding.top + self.padding.bottom, + self.cornerRadius + ) + + love.graphics.stencil(stencilFunc, "replace", 1) + love.graphics.setStencilTest("greater", 0) + + for _, child in ipairs(sortedChildren) do + child:draw() + end + + love.graphics.setStencilTest() + else + -- No clipping needed + for _, child in ipairs(sortedChildren) do + child:draw() + end end end diff --git a/examples/CornerRadiusDemo.lua b/examples/CornerRadiusDemo.lua new file mode 100644 index 0000000..acc1b1b --- /dev/null +++ b/examples/CornerRadiusDemo.lua @@ -0,0 +1,312 @@ +-- Demo showing corner radius functionality + +local FlexLove = require("FlexLove") +local Gui = FlexLove.GUI +local Color = FlexLove.Color + +function love.load() + Gui.init({ + baseScale = { width = 1920, height = 1080 } + }) + + -- Create main container + local container = Gui.new({ + x = 50, + y = 50, + width = 1100, + height = 600, + backgroundColor = Color.new(0.1, 0.1, 0.15, 1), + cornerRadius = 20, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(0.5, 0.5, 0.6, 1), + positioning = "flex", + flexDirection = "vertical", + gap = 20, + padding = { top = 20, right = 20, bottom = 20, left = 20 }, + }) + + -- Title + Gui.new({ + parent = container, + height = 50, + text = "Corner Radius Demo", + textSize = 28, + textAlign = "center", + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.2, 0.2, 0.3, 1), + cornerRadius = 10, + }) + + -- Row 1: Uniform corner radius + local row1 = Gui.new({ + parent = container, + height = 150, + positioning = "flex", + flexDirection = "horizontal", + gap = 20, + backgroundColor = Color.new(0.12, 0.12, 0.17, 0.5), + cornerRadius = 8, + padding = { top = 15, right = 15, bottom = 15, left = 15 }, + }) + + Gui.new({ + parent = row1, + width = 150, + height = 100, + text = "radius: 0", + textAlign = "center", + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.8, 0.2, 0.2, 1), + cornerRadius = 0, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(1, 1, 1, 0.5), + }) + + Gui.new({ + parent = row1, + width = 150, + height = 100, + text = "radius: 10", + textAlign = "center", + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.2, 0.8, 0.2, 1), + cornerRadius = 10, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(1, 1, 1, 0.5), + }) + + Gui.new({ + parent = row1, + width = 150, + height = 100, + text = "radius: 25", + textAlign = "center", + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.2, 0.2, 0.8, 1), + cornerRadius = 25, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(1, 1, 1, 0.5), + }) + + Gui.new({ + parent = row1, + width = 150, + height = 100, + text = "radius: 50\n(pill)", + textAlign = "center", + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.8, 0.2, 0.8, 1), + cornerRadius = 50, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(1, 1, 1, 0.5), + }) + + -- Row 2: Individual corner radii + local row2 = Gui.new({ + parent = container, + height = 150, + positioning = "flex", + flexDirection = "horizontal", + gap = 20, + backgroundColor = Color.new(0.12, 0.12, 0.17, 0.5), + cornerRadius = 8, + padding = { top = 15, right = 15, bottom = 15, left = 15 }, + }) + + Gui.new({ + parent = row2, + width = 150, + height = 100, + text = "Top-Left\nOnly", + textAlign = "center", + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.9, 0.5, 0.2, 1), + cornerRadius = { topLeft = 30, topRight = 0, bottomLeft = 0, bottomRight = 0 }, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(1, 1, 1, 0.5), + }) + + Gui.new({ + parent = row2, + width = 150, + height = 100, + text = "Top\nCorners", + textAlign = "center", + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.2, 0.9, 0.5, 1), + cornerRadius = { topLeft = 25, topRight = 25, bottomLeft = 0, bottomRight = 0 }, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(1, 1, 1, 0.5), + }) + + Gui.new({ + parent = row2, + width = 150, + height = 100, + text = "Diagonal\nCorners", + textAlign = "center", + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.5, 0.2, 0.9, 1), + cornerRadius = { topLeft = 30, topRight = 0, bottomLeft = 0, bottomRight = 30 }, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(1, 1, 1, 0.5), + }) + + Gui.new({ + parent = row2, + width = 150, + height = 100, + text = "Mixed\nRadii", + textAlign = "center", + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.9, 0.9, 0.2, 1), + cornerRadius = { topLeft = 5, topRight = 15, bottomLeft = 25, bottomRight = 35 }, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(1, 1, 1, 0.5), + }) + + -- Row 3: Interactive buttons with corner radius + local row3 = Gui.new({ + parent = container, + height = 180, + positioning = "flex", + flexDirection = "vertical", + gap = 15, + backgroundColor = Color.new(0.12, 0.12, 0.17, 0.5), + cornerRadius = 8, + padding = { top = 15, right = 15, bottom = 15, left = 15 }, + }) + + Gui.new({ + parent = row3, + height = 25, + text = "Interactive Buttons with Corner Radius:", + textSize = 16, + textColor = Color.new(0.8, 0.9, 1, 1), + backgroundColor = Color.new(0, 0, 0, 0), + }) + + local buttonRow = Gui.new({ + parent = row3, + height = 80, + positioning = "flex", + flexDirection = "horizontal", + gap = 15, + backgroundColor = Color.new(0, 0, 0, 0), + }) + + Gui.new({ + parent = buttonRow, + width = 180, + height = 60, + text = "Click Me!", + textAlign = "center", + textSize = 18, + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.2, 0.6, 0.9, 1), + cornerRadius = 15, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(1, 1, 1, 0.3), + callback = function(element, event) + if event.type == "click" then + print("Button 1 clicked!") + end + end + }) + + Gui.new({ + parent = buttonRow, + width = 180, + height = 60, + text = "Pill Button", + textAlign = "center", + textSize = 18, + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.9, 0.3, 0.4, 1), + cornerRadius = 30, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(1, 1, 1, 0.3), + callback = function(element, event) + if event.type == "click" then + print("Button 2 clicked!") + end + end + }) + + Gui.new({ + parent = buttonRow, + width = 180, + height = 60, + text = "Sharp Top", + textAlign = "center", + textSize = 18, + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.3, 0.8, 0.4, 1), + cornerRadius = { topLeft = 0, topRight = 0, bottomLeft = 20, bottomRight = 20 }, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(1, 1, 1, 0.3), + callback = function(element, event) + if event.type == "click" then + print("Button 3 clicked!") + end + end + }) + + -- Clipping demo + local clippingDemo = Gui.new({ + x = 50, + y = 670, + width = 500, + height = 150, + backgroundColor = Color.new(0.2, 0.2, 0.3, 1), + cornerRadius = 20, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(0.8, 0.8, 0.9, 1), + positioning = "flex", + flexDirection = "vertical", + gap = 10, + padding = { top = 15, right = 15, bottom = 15, left = 15 }, + }) + + Gui.new({ + parent = clippingDemo, + height = 25, + text = "Clipping Demo: Children clipped to parent's rounded corners", + textSize = 14, + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0, 0, 0, 0), + }) + + -- Child that extends beyond parent (will be clipped) + Gui.new({ + parent = clippingDemo, + x = -10, + y = 40, + width = 520, + height = 80, + backgroundColor = Color.new(0.9, 0.5, 0.2, 0.8), + text = "This element extends beyond parent but is clipped!", + textAlign = "center", + textSize = 14, + textColor = Color.new(1, 1, 1, 1), + positioning = "absolute", + }) +end + +function love.update(dt) + Gui.update(dt) +end + +function love.draw() + love.graphics.clear(0.05, 0.05, 0.1, 1) + Gui.draw() + + -- Draw instructions + love.graphics.setColor(1, 1, 1, 1) + love.graphics.print("Corner Radius System", 10, 10) + love.graphics.print("Supports uniform radius (number) or individual corners (table)", 10, 30) +end + +function love.resize(w, h) + Gui.resize() +end diff --git a/examples/DisableHighlightDemo.lua b/examples/DisableHighlightDemo.lua new file mode 100644 index 0000000..3544166 --- /dev/null +++ b/examples/DisableHighlightDemo.lua @@ -0,0 +1,239 @@ +-- Demo showing disableHighlight property + +local FlexLove = require("FlexLove") +local Gui = FlexLove.GUI +local Theme = FlexLove.Theme +local Color = FlexLove.Color + +function love.load() + Gui.init({ + baseScale = { width = 1920, height = 1080 } + }) + + -- Try to load space theme (optional) + pcall(function() + Theme.load("space") + Theme.setActive("space") + end) + + -- Create main container + local container = Gui.new({ + x = 50, + y = 50, + width = 900, + height = 550, + backgroundColor = Color.new(0.1, 0.1, 0.15, 1), + cornerRadius = 20, + border = { top = true, bottom = true, left = true, right = true }, + borderColor = Color.new(0.5, 0.5, 0.6, 1), + positioning = "flex", + flexDirection = "vertical", + gap = 20, + padding = { top = 20, right = 20, bottom = 20, left = 20 }, + }) + + -- Title + Gui.new({ + parent = container, + height = 50, + text = "disableHighlight Property Demo", + textSize = 24, + textAlign = "center", + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.2, 0.2, 0.3, 1), + cornerRadius = 10, + }) + + -- Description + Gui.new({ + parent = container, + height = 70, + text = "Click buttons to see the difference.\nButtons with themeComponent automatically disable highlight (can be overridden).\nRegular buttons show highlight by default.", + textSize = 13, + textAlign = "center", + textColor = Color.new(0.8, 0.9, 1, 1), + backgroundColor = Color.new(0.15, 0.15, 0.2, 0.8), + cornerRadius = 8, + padding = { top = 10, right = 10, bottom = 10, left = 10 }, + }) + + -- Row 1: Regular buttons + local row1 = Gui.new({ + parent = container, + height = 130, + positioning = "flex", + flexDirection = "vertical", + gap = 10, + backgroundColor = Color.new(0.12, 0.12, 0.17, 0.5), + cornerRadius = 8, + padding = { top = 15, right = 15, bottom = 15, left = 15 }, + }) + + Gui.new({ + parent = row1, + height = 20, + text = "Regular Buttons (no theme):", + textSize = 14, + textColor = Color.new(0.8, 0.9, 1, 1), + backgroundColor = Color.new(0, 0, 0, 0), + }) + + local regularRow = Gui.new({ + parent = row1, + height = 80, + positioning = "flex", + flexDirection = "horizontal", + gap = 15, + backgroundColor = Color.new(0, 0, 0, 0), + }) + + Gui.new({ + parent = regularRow, + width = 250, + height = 70, + text = "Default\n(shows highlight)", + textAlign = "center", + textSize = 14, + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.2, 0.6, 0.9, 1), + cornerRadius = 12, + callback = function(element, event) + if event.type == "click" then + print("Regular button with highlight clicked!") + end + end + }) + + Gui.new({ + parent = regularRow, + width = 250, + height = 70, + text = "disableHighlight = true\n(no highlight)", + textAlign = "center", + textSize = 14, + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.9, 0.3, 0.4, 1), + cornerRadius = 12, + disableHighlight = true, + callback = function(element, event) + if event.type == "click" then + print("Regular button without highlight clicked!") + end + end + }) + + -- Row 2: Themed buttons + local row2 = Gui.new({ + parent = container, + height = 150, + positioning = "flex", + flexDirection = "vertical", + gap = 10, + backgroundColor = Color.new(0.12, 0.12, 0.17, 0.5), + cornerRadius = 8, + padding = { top = 15, right = 15, bottom = 15, left = 15 }, + }) + + Gui.new({ + parent = row2, + height = 20, + text = "Themed Buttons (with themeComponent):", + textSize = 14, + textColor = Color.new(0.8, 0.9, 1, 1), + backgroundColor = Color.new(0, 0, 0, 0), + }) + + local themedRow = Gui.new({ + parent = row2, + height = 100, + positioning = "flex", + flexDirection = "horizontal", + gap = 15, + backgroundColor = Color.new(0, 0, 0, 0), + }) + + Gui.new({ + parent = themedRow, + width = 250, + height = 80, + text = "Default\n(auto-disables highlight)", + textAlign = "center", + textSize = 14, + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.2, 0.6, 0.9, 0.3), + cornerRadius = 12, + themeComponent = "button", + callback = function(element, event) + if event.type == "click" then + print("Themed button (auto-disabled highlight) clicked!") + end + end + }) + + Gui.new({ + parent = themedRow, + width = 250, + height = 80, + text = "disableHighlight = false\n(forced highlight)", + textAlign = "center", + textSize = 14, + textColor = Color.new(1, 1, 1, 1), + backgroundColor = Color.new(0.9, 0.3, 0.4, 0.3), + cornerRadius = 12, + themeComponent = "button", + disableHighlight = false, + callback = function(element, event) + if event.type == "click" then + print("Themed button (forced highlight) clicked!") + end + end + }) + + -- Summary + local summary = Gui.new({ + parent = container, + height = 70, + positioning = "flex", + flexDirection = "vertical", + gap = 5, + backgroundColor = Color.new(0.15, 0.2, 0.15, 0.8), + cornerRadius = 8, + padding = { top = 10, right = 10, bottom = 10, left = 10 }, + }) + + Gui.new({ + parent = summary, + height = 18, + text = "Summary:", + textSize = 14, + textColor = Color.new(0.8, 1, 0.8, 1), + backgroundColor = Color.new(0, 0, 0, 0), + }) + + Gui.new({ + parent = summary, + height = 40, + text = "• Regular buttons: highlight enabled by default\n• Themed buttons: highlight disabled by default (themes provide their own feedback)\n• Both can be explicitly overridden with disableHighlight property", + textSize = 11, + textColor = Color.new(0.9, 0.9, 0.9, 1), + backgroundColor = Color.new(0, 0, 0, 0), + }) +end + +function love.update(dt) + Gui.update(dt) +end + +function love.draw() + love.graphics.clear(0.05, 0.05, 0.1, 1) + Gui.draw() + + -- Draw instructions + love.graphics.setColor(1, 1, 1, 1) + love.graphics.print("disableHighlight Property Demo", 10, 10) + love.graphics.print("Press and hold buttons to see the difference", 10, 30) +end + +function love.resize(w, h) + Gui.resize() +end