fade fixes
This commit is contained in:
462
FlexLove.lua
462
FlexLove.lua
@@ -1,12 +1,69 @@
|
|||||||
-- Utility class for color handling
|
-- Utility class for color handling
|
||||||
---@class Color
|
---@class Color
|
||||||
---@field r number
|
---@field r number -- Red component (0-1)
|
||||||
---@field g number
|
---@field g number -- Green component (0-1)
|
||||||
---@field b number
|
---@field b number -- Blue component (0-1)
|
||||||
---@field a number
|
---@field a number -- Alpha component (0-1)
|
||||||
|
---@field toHex fun(self:Color): string -- Converts color to hex string
|
||||||
|
---@field toRGBA fun(self:Color): number, number, number, number -- Returns RGBA components as numbers
|
||||||
local Color = {}
|
local Color = {}
|
||||||
Color.__index = Color
|
Color.__index = Color
|
||||||
|
|
||||||
|
--- Create a new color instance
|
||||||
|
---@param r number -- Red component (0-1)
|
||||||
|
---@param g number -- Green component (0-1)
|
||||||
|
---@param b number -- Blue component (0-1)
|
||||||
|
---@param a number? -- Alpha component (0-1), default 1
|
||||||
|
---@return Color
|
||||||
|
function Color.new(r, g, b, a)
|
||||||
|
local self = setmetatable({}, Color)
|
||||||
|
self.r = r or 0
|
||||||
|
self.g = g or 0
|
||||||
|
self.b = b or 0
|
||||||
|
self.a = a or 1
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Convert hex string to color
|
||||||
|
---@param hex string -- e.g. "#RRGGBB" or "#RRGGBBAA"
|
||||||
|
---@return Color
|
||||||
|
function Color.fromHex(hex)
|
||||||
|
local hex = hex:gsub("#", "")
|
||||||
|
if #hex == 6 then
|
||||||
|
local r = tonumber("0x" .. hex:sub(1, 2))
|
||||||
|
local g = tonumber("0x" .. hex:sub(3, 4))
|
||||||
|
local b = tonumber("0x" .. hex:sub(5, 6))
|
||||||
|
return Color.new(r, g, b, 1)
|
||||||
|
elseif #hex == 8 then
|
||||||
|
local r = tonumber("0x" .. hex:sub(1, 2))
|
||||||
|
local g = tonumber("0x" .. hex:sub(3, 4))
|
||||||
|
local b = tonumber("0x" .. hex:sub(5, 6))
|
||||||
|
local a = tonumber("0x" .. hex:sub(7, 8)) / 255
|
||||||
|
return Color.new(r, g, b, a)
|
||||||
|
else
|
||||||
|
error("Invalid hex string")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Convert color to hex string
|
||||||
|
---@return string -- Hex color string in format "#RRGGBB" or "#RRGGBBAA"
|
||||||
|
function Color:toHex()
|
||||||
|
local r = math.floor(self.r * 255)
|
||||||
|
local g = math.floor(self.g * 255)
|
||||||
|
local b = math.floor(self.b * 255)
|
||||||
|
local a = math.floor(self.a * 255)
|
||||||
|
if self.a ~= 1 then
|
||||||
|
return string.format("#%02X%02X%02X%02X", r, g, b, a)
|
||||||
|
else
|
||||||
|
return string.format("#%02X%02X%02X", r, g, b)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return number r, number g, number b, number a
|
||||||
|
function Color:toRGBA()
|
||||||
|
return self.r, self.g, self.b, self.a
|
||||||
|
end
|
||||||
|
|
||||||
--- Create a new color instance
|
--- Create a new color instance
|
||||||
---@param r number
|
---@param r number
|
||||||
---@param g number
|
---@param g number
|
||||||
@@ -94,6 +151,17 @@ enums.JustifyContent = {
|
|||||||
SPACE_BETWEEN = "space-between",
|
SPACE_BETWEEN = "space-between",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- @enum JustifySelf
|
||||||
|
enums.JustifySelf = {
|
||||||
|
AUTO = "auto",
|
||||||
|
FLEX_START = "flex-start",
|
||||||
|
CENTER = "center",
|
||||||
|
FLEX_END = "flex-end",
|
||||||
|
SPACE_AROUND = "space-around",
|
||||||
|
SPACE_EVENLY = "space-evenly",
|
||||||
|
SPACE_BETWEEN = "space-between",
|
||||||
|
}
|
||||||
|
|
||||||
--- @enum AlignItems
|
--- @enum AlignItems
|
||||||
enums.AlignItems = {
|
enums.AlignItems = {
|
||||||
STRETCH = "stretch",
|
STRETCH = "stretch",
|
||||||
@@ -103,6 +171,16 @@ enums.AlignItems = {
|
|||||||
BASELINE = "baseline",
|
BASELINE = "baseline",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- @enum AlignSelf
|
||||||
|
enums.AlignSelf = {
|
||||||
|
AUTO = "auto",
|
||||||
|
STRETCH = "stretch",
|
||||||
|
FLEX_START = "flex-start",
|
||||||
|
FLEX_END = "flex-end",
|
||||||
|
CENTER = "center",
|
||||||
|
BASELINE = "baseline",
|
||||||
|
}
|
||||||
|
|
||||||
--- @enum AlignContent
|
--- @enum AlignContent
|
||||||
enums.AlignContent = {
|
enums.AlignContent = {
|
||||||
STRETCH = "stretch",
|
STRETCH = "stretch",
|
||||||
@@ -113,10 +191,23 @@ enums.AlignContent = {
|
|||||||
SPACE_AROUND = "space-around",
|
SPACE_AROUND = "space-around",
|
||||||
}
|
}
|
||||||
|
|
||||||
local Positioning, FlexDirection, JustifyContent, AlignContent, AlignItems, TextAlign =
|
local Positioning, FlexDirection, JustifyContent, AlignContent, AlignItems, TextAlign, AlignSelf, JustifySelf =
|
||||||
enums.Positioning, enums.FlexDirection, enums.JustifyContent, enums.AlignContent, enums.AlignItems, enums.TextAlign
|
enums.Positioning,
|
||||||
|
enums.FlexDirection,
|
||||||
|
enums.JustifyContent,
|
||||||
|
enums.AlignContent,
|
||||||
|
enums.AlignItems,
|
||||||
|
enums.TextAlign,
|
||||||
|
enums.AlignSelf,
|
||||||
|
enums.JustifySelf
|
||||||
|
|
||||||
--- Top level GUI manager
|
--- Top level GUI manager
|
||||||
|
---@class Gui
|
||||||
|
---@field topWindows table<integer, Window>
|
||||||
|
---@field resize fun(): void
|
||||||
|
---@field draw fun(): void
|
||||||
|
---@field update fun(dt:number): void
|
||||||
|
---@field destroy fun(): void
|
||||||
local Gui = { topWindows = {} }
|
local Gui = { topWindows = {} }
|
||||||
|
|
||||||
function Gui.resize()
|
function Gui.resize()
|
||||||
@@ -156,23 +247,27 @@ end
|
|||||||
|
|
||||||
---@class Animation
|
---@class Animation
|
||||||
---@field duration number
|
---@field duration number
|
||||||
---@field start table{width:number,height:number}
|
---@field start {width?:number, height?:number, opacity?:number}
|
||||||
---@field final table{width:number,height:number}
|
---@field final {width?:number, height?:number, opacity?:number}
|
||||||
---@field elapsed number
|
---@field elapsed number
|
||||||
---@field transform table?
|
---@field transform table?
|
||||||
---@field transition table?
|
---@field transition table?
|
||||||
|
---@field update fun(self:Animation, dt:number): boolean
|
||||||
|
---@field interpolate fun(self:Animation): table
|
||||||
|
---@field apply fun(self:Animation, element:Window|Button): void
|
||||||
local Animation = {}
|
local Animation = {}
|
||||||
Animation.__index = Animation
|
Animation.__index = Animation
|
||||||
|
|
||||||
---@class AnimationProps
|
---@class AnimationProps
|
||||||
---@field duration number
|
---@field duration number
|
||||||
---@field start table{width:number,height:number}
|
---@field start {width?:number, height?:number, opacity?:number}
|
||||||
---@field final table{width:number,height:number}
|
---@field final {width?:number, height?:number, opacity?:number}
|
||||||
---@field transform table?
|
---@field transform table?
|
||||||
---@field transition table?
|
---@field transition table?
|
||||||
local AnimationProps = {}
|
local AnimationProps = {}
|
||||||
|
|
||||||
---@param props AnimationProps
|
---@param props AnimationProps
|
||||||
|
---@return Animation
|
||||||
function Animation.new(props)
|
function Animation.new(props)
|
||||||
local self = setmetatable({}, Animation)
|
local self = setmetatable({}, Animation)
|
||||||
self.duration = props.duration
|
self.duration = props.duration
|
||||||
@@ -195,18 +290,29 @@ end
|
|||||||
|
|
||||||
function Animation:interpolate()
|
function Animation:interpolate()
|
||||||
local t = math.min(self.elapsed / self.duration, 1)
|
local t = math.min(self.elapsed / self.duration, 1)
|
||||||
local result = {
|
local result = {}
|
||||||
width = self.start.width * (1 - t) + self.final.width * t,
|
|
||||||
height = self.start.height * (1 - t) + self.final.height * t,
|
-- Handle width and height if present
|
||||||
}
|
if self.start.width and self.final.width then
|
||||||
|
result.width = self.start.width * (1 - t) + self.final.width * t
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.start.height and self.final.height then
|
||||||
|
result.height = self.start.height * (1 - t) + self.final.height * t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle other properties like opacity
|
||||||
|
if self.start.opacity and self.final.opacity then
|
||||||
|
result.opacity = self.start.opacity * (1 - t) + self.final.opacity * t
|
||||||
|
end
|
||||||
|
|
||||||
-- Apply transform if present
|
-- Apply transform if present
|
||||||
if self.transform then
|
if self.transform then
|
||||||
for key, value in pairs(self.transform) do
|
for key, value in pairs(self.transform) do
|
||||||
result[key] = value
|
result[key] = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -232,7 +338,7 @@ function Animation.fade(duration, fromOpacity, toOpacity)
|
|||||||
start = { opacity = fromOpacity },
|
start = { opacity = fromOpacity },
|
||||||
final = { opacity = toOpacity },
|
final = { opacity = toOpacity },
|
||||||
transform = {},
|
transform = {},
|
||||||
transition = {}
|
transition = {},
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -247,7 +353,7 @@ function Animation.scale(duration, fromScale, toScale)
|
|||||||
start = { width = fromScale.width, height = fromScale.height },
|
start = { width = fromScale.width, height = fromScale.height },
|
||||||
final = { width = toScale.width, height = toScale.height },
|
final = { width = toScale.width, height = toScale.height },
|
||||||
transform = {},
|
transform = {},
|
||||||
transition = {}
|
transition = {},
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -284,58 +390,72 @@ end
|
|||||||
-- Window Object
|
-- Window Object
|
||||||
-- ====================
|
-- ====================
|
||||||
---@class Window
|
---@class Window
|
||||||
---@field autosizing boolean
|
---@field autosizing boolean -- Whether the window should automatically size to fit its children
|
||||||
---@field x number
|
---@field x number -- X coordinate of the window
|
||||||
---@field y number
|
---@field y number -- Y coordinate of the window
|
||||||
---@field z number -- default: 0
|
---@field z number -- Z-index for layering (default: 0)
|
||||||
---@field width number
|
---@field width number -- Width of the window
|
||||||
---@field height number
|
---@field height number -- Height of the window
|
||||||
---@field children table<integer, Button|Window>
|
---@field children table<integer, Button|Window> -- Children of this window
|
||||||
---@field parent Window?
|
---@field parent Window? -- Parent window (nil if top-level)
|
||||||
---@field border Border
|
---@field border Border -- Border configuration for the window
|
||||||
---@field borderColor Color
|
---@field borderColor Color -- Color of the border
|
||||||
---@field background Color
|
---@field background Color -- Background color of the window
|
||||||
---@field prevGameSize {width:number, height:number}
|
---@field prevGameSize {width:number, height:number} -- Previous game size for resize calculations
|
||||||
---@field text string?
|
---@field text string? -- Text content to display in the window
|
||||||
---@field textColor Color
|
---@field textColor Color -- Color of the text content
|
||||||
---@field textAlign TextAlign
|
---@field textAlign TextAlign -- Alignment of the text content
|
||||||
---@field gap number
|
---@field gap number -- Space between children elements (default: 10)
|
||||||
---@field px number
|
---@field px number -- Horizontal padding around children (default: 0)
|
||||||
---@field py number
|
---@field py number -- Vertical padding around children (default: 0)
|
||||||
---@field positioning Positioning -- default: ABSOLUTE
|
---@field positioning Positioning -- Layout positioning mode (default: ABSOLUTE)
|
||||||
---@field flexDirection FlexDirection -- default: horizontal
|
---@field flexDirection FlexDirection -- Direction of flex layout (default: HORIZONTAL)
|
||||||
---@field justifyContent JustifyContent -- default: start
|
---@field justifyContent JustifyContent -- Alignment of items along main axis (default: FLEX_START)
|
||||||
---@field alignItems AlignItems -- default: start
|
---@field alignItems AlignItems -- Alignment of items along cross axis (default: STRETCH)
|
||||||
---@field alignContent AlignContent -- default: start
|
---@field alignContent AlignContent -- Alignment of lines in multi-line flex containers (default: STRETCH)
|
||||||
---@field textSize number?
|
---@field justifySelf JustifySelf -- Alignment of the item itself along main axis (default: AUTO)
|
||||||
---@field transform table
|
---@field alignSelf AlignSelf -- Alignment of the item itself along cross axis (default: AUTO)
|
||||||
---@field transition table
|
---@field textSize number? -- Font size for text content
|
||||||
|
---@field transform table -- Transform properties for animations and styling
|
||||||
|
---@field transition table -- Transition settings for animations
|
||||||
|
---@field getBounds fun(self:Window): {x:number, y:number, width:number, height:number} -- Returns window bounds
|
||||||
|
---@field addChild fun(self:Window, child:Button|Window): void -- Adds a child to this window
|
||||||
|
---@field layoutChildren fun(self:Window): void -- Layouts all children based on current settings
|
||||||
|
---@field destroy fun(self:Window): void -- Destroys the window and its children
|
||||||
|
---@field draw fun(self:Window): void -- Draws the window and its children
|
||||||
|
---@field update fun(self:Window, dt:number): void -- Updates the window and its children
|
||||||
|
---@field resize fun(self:Window, newGameWidth:number, newGameHeight:number): void -- Resizes the window based on game size change
|
||||||
|
---@field calculateAutoWidth fun(self:Window): void -- Calculates auto width based on children
|
||||||
|
---@field calculateAutoHeight fun(self:Window): void -- Calculates auto height based on children
|
||||||
|
---@field updateAutoSize fun(self:Window): void -- Updates the window size to fit its children automatically
|
||||||
local Window = {}
|
local Window = {}
|
||||||
Window.__index = Window
|
Window.__index = Window
|
||||||
|
|
||||||
---@class WindowProps
|
---@class WindowProps
|
||||||
---@field parent Window?
|
---@field parent Window? -- Parent window for hierarchical structure
|
||||||
---@field x number?
|
---@field x number? -- X coordinate of the window (default: 0)
|
||||||
---@field y number?
|
---@field y number? -- Y coordinate of the window (default: 0)
|
||||||
---@field z number? -- default: 0
|
---@field z number? -- Z-index for layering (default: 0)
|
||||||
---@field w number?
|
---@field w number? -- Width of the window (default: calculated automatically)
|
||||||
---@field h number?
|
---@field h number? -- Height of the window (default: calculated automatically)
|
||||||
---@field border Border?
|
---@field border Border? -- Border configuration for the window
|
||||||
---@field borderColor Color? -- default: black? -- default: none
|
---@field borderColor Color? -- Color of the border (default: black)
|
||||||
---@field background Color? --default: transparent
|
---@field background Color? -- Background color (default: transparent)
|
||||||
---@field gap number? -- default: 10
|
---@field gap number? -- Space between children elements (default: 10)
|
||||||
---@field px number? -- default: 0
|
---@field px number? -- Horizontal padding around children (default: 0)
|
||||||
---@field py number? -- default: 0
|
---@field py number? -- Vertical padding around children (default: 0)
|
||||||
---@field text string? -- default: nil
|
---@field text string? -- Text content to display (default: nil)
|
||||||
---@field titleColor Color? -- default: black
|
---@field titleColor Color? -- Color of the text content (default: black)
|
||||||
---@field textAlign TextAlign?
|
---@field textAlign TextAlign? -- Alignment of the text content (default: START)
|
||||||
---@field textColor Color? -- default: black
|
---@field textColor Color? -- Color of the text content (default: black)
|
||||||
---@field textSize number? -- default: nil
|
---@field textSize number? -- Font size for text content (default: nil)
|
||||||
---@field positioning Positioning? -- default: ABSOLUTE
|
---@field positioning Positioning? -- Layout positioning mode (default: ABSOLUTE)
|
||||||
---@field flexDirection FlexDirection? -- default: HORIZONTAL
|
---@field flexDirection FlexDirection? -- Direction of flex layout (default: HORIZONTAL)
|
||||||
---@field justifyContent JustifyContent? -- default: FLEX_START
|
---@field justifyContent JustifyContent? -- Alignment of items along main axis (default: FLEX_START)
|
||||||
---@field alignItems AlignItems? -- default: STRETCH
|
---@field alignItems AlignItems? -- Alignment of items along cross axis (default: STRETCH)
|
||||||
---@field alignContent AlignContent? -- default: STRETCH
|
---@field alignContent AlignContent? -- Alignment of lines in multi-line flex containers (default: STRETCH)
|
||||||
|
---@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)
|
||||||
local WindowProps = {}
|
local WindowProps = {}
|
||||||
|
|
||||||
---@param props WindowProps
|
---@param props WindowProps
|
||||||
@@ -411,6 +531,8 @@ function Window.new(props)
|
|||||||
self.justifyContent = props.justifyContent or JustifyContent.FLEX_START
|
self.justifyContent = props.justifyContent or JustifyContent.FLEX_START
|
||||||
self.alignItems = props.alignItems or AlignItems.STRETCH
|
self.alignItems = props.alignItems or AlignItems.STRETCH
|
||||||
self.alignContent = props.alignContent or AlignContent.STRETCH
|
self.alignContent = props.alignContent or AlignContent.STRETCH
|
||||||
|
self.justifySelf = props.justifySelf or AlignSelf.AUTO
|
||||||
|
self.alignSelf = props.alignSelf or AlignSelf.AUTO
|
||||||
end
|
end
|
||||||
|
|
||||||
local gw, gh = love.window.getMode()
|
local gw, gh = love.window.getMode()
|
||||||
@@ -422,12 +544,16 @@ function Window.new(props)
|
|||||||
self.transform = props.transform or {}
|
self.transform = props.transform or {}
|
||||||
self.transition = props.transition or {}
|
self.transition = props.transition or {}
|
||||||
|
|
||||||
|
-- Initialize opacity for animations to work properly
|
||||||
|
self.opacity = self.background.a
|
||||||
|
|
||||||
if not props.parent then
|
if not props.parent then
|
||||||
table.insert(Gui.topWindows, self)
|
table.insert(Gui.topWindows, self)
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get window bounds
|
||||||
---@return { x:number, y:number, width:number, height:number }
|
---@return { x:number, y:number, width:number, height:number }
|
||||||
function Window:getBounds()
|
function Window:getBounds()
|
||||||
return { x = self.x, y = self.y, width = self.width, height = self.height }
|
return { x = self.x, y = self.y, width = self.width, height = self.height }
|
||||||
@@ -490,6 +616,30 @@ function Window:layoutChildren()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Apply justifySelf for individual children
|
||||||
|
local childSpacing = {}
|
||||||
|
for i, child in ipairs(self.children) do
|
||||||
|
if child.justifySelf == JustifySelf.FLEX_START then
|
||||||
|
childSpacing[i] = 0
|
||||||
|
elseif child.justifySelf == JustifySelf.CENTER then
|
||||||
|
childSpacing[i] = freeSpace / 2
|
||||||
|
elseif child.justifySelf == JustifySelf.FLEX_END then
|
||||||
|
childSpacing[i] = freeSpace
|
||||||
|
elseif child.justifySelf == JustifySelf.SPACE_AROUND then
|
||||||
|
childSpacing[i] = freeSpace / (childCount + 1)
|
||||||
|
elseif child.justifySelf == JustifySelf.SPACE_EVENLY then
|
||||||
|
childSpacing[i] = freeSpace / (childCount + 1)
|
||||||
|
elseif child.justifySelf == JustifySelf.SPACE_BETWEEN then
|
||||||
|
if childCount > 1 then
|
||||||
|
childSpacing[i] = freeSpace / (childCount - 1)
|
||||||
|
else
|
||||||
|
childSpacing[i] = 0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
childSpacing[i] = 0 -- default to flex-start
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Position children
|
-- Position children
|
||||||
local currentPos = spacing
|
local currentPos = spacing
|
||||||
for _, child in ipairs(self.children) do
|
for _, child in ipairs(self.children) do
|
||||||
@@ -510,6 +660,18 @@ function Window:layoutChildren()
|
|||||||
elseif self.alignItems == AlignItems.STRETCH then
|
elseif self.alignItems == AlignItems.STRETCH then
|
||||||
child.height = self.height
|
child.height = self.height
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Apply self alignment to vertical axis (alignSelf)
|
||||||
|
if child.alignSelf == AlignSelf.FLEX_START then
|
||||||
|
--nothing, currentPos is all
|
||||||
|
elseif child.alignSelf == AlignSelf.CENTER then
|
||||||
|
child.y = (self.height - (child.height or 0)) / 2
|
||||||
|
elseif child.alignSelf == AlignSelf.FLEX_END then
|
||||||
|
child.y = self.height - (child.height or 0)
|
||||||
|
elseif child.alignSelf == AlignSelf.STRETCH then
|
||||||
|
child.height = self.height
|
||||||
|
end
|
||||||
|
|
||||||
currentPos = currentPos + (child.width or 0) + self.gap
|
currentPos = currentPos + (child.width or 0) + self.gap
|
||||||
else
|
else
|
||||||
child.y = currentPos
|
child.y = currentPos
|
||||||
@@ -524,6 +686,17 @@ function Window:layoutChildren()
|
|||||||
child.width = self.width
|
child.width = self.width
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Apply self alignment to horizontal axis (alignSelf)
|
||||||
|
if child.alignSelf == AlignSelf.FLEX_START then
|
||||||
|
--nothing, currentPos is all
|
||||||
|
elseif child.alignSelf == AlignSelf.CENTER then
|
||||||
|
child.x = (self.width - (child.width or 0)) / 2
|
||||||
|
elseif child.alignSelf == AlignSelf.FLEX_END then
|
||||||
|
child.x = self.width - (child.width or 0)
|
||||||
|
elseif child.alignSelf == AlignSelf.STRETCH then
|
||||||
|
child.width = self.width
|
||||||
|
end
|
||||||
|
|
||||||
currentPos = currentPos + (child.height or 0) + self.gap
|
currentPos = currentPos + (child.height or 0) + self.gap
|
||||||
end
|
end
|
||||||
::continue::
|
::continue::
|
||||||
@@ -569,7 +742,16 @@ end
|
|||||||
|
|
||||||
--- Draw window and its children
|
--- Draw window and its children
|
||||||
function Window:draw()
|
function Window:draw()
|
||||||
love.graphics.setColor(self.background:toRGBA())
|
-- Handle opacity during animation
|
||||||
|
local drawBackground = self.background
|
||||||
|
if self.animation then
|
||||||
|
local anim = self.animation:interpolate()
|
||||||
|
if anim.opacity then
|
||||||
|
drawBackground = Color.new(self.background.r, self.background.g, self.background.b, anim.opacity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
love.graphics.setColor(drawBackground:toRGBA())
|
||||||
love.graphics.rectangle("fill", self.x, self.y, self.width, self.height)
|
love.graphics.rectangle("fill", self.x, self.y, self.width, self.height)
|
||||||
-- Draw borders based on border property
|
-- Draw borders based on border property
|
||||||
love.graphics.setColor(self.borderColor:toRGBA())
|
love.graphics.setColor(self.borderColor:toRGBA())
|
||||||
@@ -640,8 +822,13 @@ function Window:update(dt)
|
|||||||
else
|
else
|
||||||
-- Apply animation interpolation during update
|
-- Apply animation interpolation during update
|
||||||
local anim = self.animation:interpolate()
|
local anim = self.animation:interpolate()
|
||||||
self.width = anim.width
|
self.width = anim.width or self.width
|
||||||
self.height = anim.height
|
self.height = anim.height or self.height
|
||||||
|
self.opacity = anim.opacity or self.opacity
|
||||||
|
-- Update background color with interpolated opacity
|
||||||
|
if anim.opacity then
|
||||||
|
self.background.a = anim.opacity
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -749,11 +936,21 @@ end
|
|||||||
---@field parent Window
|
---@field parent Window
|
||||||
---@field callback function
|
---@field callback function
|
||||||
---@field textColor Color?
|
---@field textColor Color?
|
||||||
---@field _touchPressed boolean
|
---@field _touchPressed table<number, boolean>
|
||||||
---@field positioning Positioning --default: ABSOLUTE (checks parent first)
|
---@field positioning Positioning --default: ABSOLUTE (checks parent first)
|
||||||
---@field textSize number?
|
---@field textSize number?
|
||||||
|
---@field justifySelf JustifySelf -- default: auto
|
||||||
|
---@field alignSelf AlignSelf -- default: auto
|
||||||
---@field transform table
|
---@field transform table
|
||||||
---@field transition table
|
---@field transition table
|
||||||
|
---@field bounds fun(self:Button): {x:number, y:number, width:number, height:number}
|
||||||
|
---@field resize fun(self:Button, ratioW?:number, ratioH?:number): void
|
||||||
|
---@field updateText fun(self:Button, newText:string, autoresize?:boolean): void
|
||||||
|
---@field draw fun(self:Button): void
|
||||||
|
---@field calculateTextWidth fun(self:Button): number
|
||||||
|
---@field calculateTextHeight fun(self:Button): number
|
||||||
|
---@field update fun(self:Button, dt:number): void
|
||||||
|
---@field destroy fun(self:Button): void
|
||||||
local Button = {}
|
local Button = {}
|
||||||
Button.__index = Button
|
Button.__index = Button
|
||||||
|
|
||||||
@@ -774,6 +971,8 @@ Button.__index = Button
|
|||||||
---@field textColor Color? -- default: black,
|
---@field textColor Color? -- default: black,
|
||||||
---@field textSize number? -- default: nil
|
---@field textSize number? -- default: nil
|
||||||
---@field positioning Positioning? --default: ABSOLUTE (checks parent first)
|
---@field positioning Positioning? --default: ABSOLUTE (checks parent first)
|
||||||
|
---@field justifySelf JustifySelf? -- default: AUTO
|
||||||
|
---@field alignSelf AlignSelf? -- default: AUTO
|
||||||
local ButtonProps = {}
|
local ButtonProps = {}
|
||||||
|
|
||||||
---@param props ButtonProps
|
---@param props ButtonProps
|
||||||
@@ -807,6 +1006,8 @@ function Button.new(props)
|
|||||||
self.background = props.background or Color.new(0, 0, 0, 0)
|
self.background = props.background or Color.new(0, 0, 0, 0)
|
||||||
|
|
||||||
self.positioning = props.positioning or props.parent.positioning
|
self.positioning = props.positioning or props.parent.positioning
|
||||||
|
self.justifySelf = props.justifySelf or AlignSelf.AUTO
|
||||||
|
self.alignSelf = props.alignSelf or AlignSelf.AUTO
|
||||||
|
|
||||||
self.z = props.z or 0
|
self.z = props.z or 0
|
||||||
|
|
||||||
@@ -818,6 +1019,9 @@ function Button.new(props)
|
|||||||
self.transform = props.transform or {}
|
self.transform = props.transform or {}
|
||||||
self.transition = props.transition or {}
|
self.transition = props.transition or {}
|
||||||
|
|
||||||
|
-- Initialize opacity for animations to work properly
|
||||||
|
self.opacity = self.background.a
|
||||||
|
|
||||||
props.parent:addChild(self)
|
props.parent:addChild(self)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -933,7 +1137,7 @@ function Button:calculateTextHeight()
|
|||||||
return height
|
return height
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if mouse is over button and handle click
|
--- Update button (propagate to children)
|
||||||
---@param dt number
|
---@param dt number
|
||||||
function Button:update(dt)
|
function Button:update(dt)
|
||||||
local mx, my = love.mouse.getPosition()
|
local mx, my = love.mouse.getPosition()
|
||||||
@@ -961,6 +1165,24 @@ function Button:update(dt)
|
|||||||
self._touchPressed[id] = false
|
self._touchPressed[id] = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Update animation if exists (similar to Window:update)
|
||||||
|
if self.animation then
|
||||||
|
local finished = self.animation:update(dt)
|
||||||
|
if finished then
|
||||||
|
self.animation = nil -- remove finished animation
|
||||||
|
else
|
||||||
|
-- Apply animation interpolation during update
|
||||||
|
local anim = self.animation:interpolate()
|
||||||
|
self.width = anim.width or self.width
|
||||||
|
self.height = anim.height or self.height
|
||||||
|
self.opacity = anim.opacity or self.opacity
|
||||||
|
-- Update background color with interpolated opacity
|
||||||
|
if anim.opacity then
|
||||||
|
self.background.a = anim.opacity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Destroy button
|
--- Destroy button
|
||||||
@@ -981,6 +1203,104 @@ function Button:destroy()
|
|||||||
self._touchPressed = nil
|
self._touchPressed = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Find a child element by name or id (if applicable)
|
||||||
|
---@param name string -- Name or id to search for
|
||||||
|
---@return Button|Window|nil
|
||||||
|
function Window:findChild(name)
|
||||||
|
for _, child in ipairs(self.children) do
|
||||||
|
if child.name == name or child.id == name then
|
||||||
|
return child
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get all children of a specific type
|
||||||
|
---@param type string -- "Button" or "Window"
|
||||||
|
---@return table<integer, Button|Window>
|
||||||
|
function Window:getChildrenOfType(type)
|
||||||
|
local result = {}
|
||||||
|
for _, child in ipairs(self.children) do
|
||||||
|
if getmetatable(child).__name == type then
|
||||||
|
table.insert(result, child)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the visibility of this window and its children
|
||||||
|
---@param visible boolean -- Whether to show or hide the window
|
||||||
|
function Window:setVisible(visible)
|
||||||
|
self.visible = visible
|
||||||
|
for _, child in ipairs(self.children) do
|
||||||
|
if child.setVisible then
|
||||||
|
child:setVisible(visible)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the absolute position of this window relative to screen
|
||||||
|
---@return number x, number y
|
||||||
|
function Window:getAbsolutePosition()
|
||||||
|
local x, y = self.x, self.y
|
||||||
|
local parent = self.parent
|
||||||
|
while parent do
|
||||||
|
x = x + parent.x
|
||||||
|
y = y + parent.y
|
||||||
|
parent = parent.parent
|
||||||
|
end
|
||||||
|
return x, y
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the absolute bounds of this window
|
||||||
|
---@return {x:number, y:number, width:number, height:number}
|
||||||
|
function Window:getAbsoluteBounds()
|
||||||
|
local x, y = self:getAbsolutePosition()
|
||||||
|
return {
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = self.width,
|
||||||
|
height = self.height,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the size of this window and all its children proportionally
|
||||||
|
---@param width number -- New width
|
||||||
|
---@param height number -- New height
|
||||||
|
function Window:setSize(width, height)
|
||||||
|
local oldWidth = self.width
|
||||||
|
local oldHeight = self.height
|
||||||
|
if oldWidth > 0 and oldHeight > 0 then
|
||||||
|
local ratioW = width / oldWidth
|
||||||
|
local ratioH = height / oldHeight
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
-- Resize children proportionally
|
||||||
|
for _, child in ipairs(self.children) do
|
||||||
|
if child.resize then
|
||||||
|
child:resize(ratioW, ratioH)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Center this window within its parent or screen
|
||||||
|
---@param parent Window? -- Parent window to center within (optional)
|
||||||
|
function Window:center(parent)
|
||||||
|
local parentWidth, parentHeight = love.window.getMode()
|
||||||
|
if parent then
|
||||||
|
parentWidth = parent.width
|
||||||
|
parentHeight = parent.height
|
||||||
|
end
|
||||||
|
|
||||||
|
self.x = (parentWidth - self.width) / 2
|
||||||
|
self.y = (parentHeight - self.height) / 2
|
||||||
|
end
|
||||||
|
|
||||||
Gui.Button = Button
|
Gui.Button = Button
|
||||||
Gui.Window = Window
|
Gui.Window = Window
|
||||||
|
Gui.Animation = Animation
|
||||||
return { GUI = Gui, Color = Color, enums = enums }
|
return { GUI = Gui, Color = Color, enums = enums }
|
||||||
|
|||||||
Submodule game/libs deleted from 899a76b4a5
Reference in New Issue
Block a user