reposition based on state

This commit is contained in:
Michael Freno
2025-10-17 17:06:54 -04:00
parent a6d0a0090b
commit bc8e1fdeaa

View File

@@ -2656,13 +2656,46 @@ function Element.new(props)
-- For auto-sized elements, this is content width; for explicit sizing, this is border-box width -- For auto-sized elements, this is content width; for explicit sizing, this is border-box width
local tempPadding local tempPadding
if use9PatchPadding then if use9PatchPadding then
-- Use 9-patch content padding directly (no need to resolve, it's already in pixels) -- Scale 9-patch content padding to match the actual rendered size
tempPadding = { -- The contentPadding values are in the original image's pixel coordinates,
left = ninePatchContentPadding.left, -- but we need to scale them proportionally to the element's actual size
top = ninePatchContentPadding.top, local themeToUse = self.theme and themes[self.theme] or Theme.getActive()
right = ninePatchContentPadding.right, if themeToUse and themeToUse.components[self.themeComponent] then
bottom = ninePatchContentPadding.bottom, local component = themeToUse.components[self.themeComponent]
} local atlasImage = component._loadedAtlas or themeToUse.atlas
if atlasImage and type(atlasImage) ~= "string" then
local originalWidth, originalHeight = atlasImage:getDimensions()
-- Calculate the scale factor based on the element's border-box size vs original image size
-- For explicit sizing, tempWidth/tempHeight represent the border-box dimensions
local scaleX = tempWidth / originalWidth
local scaleY = tempHeight / originalHeight
tempPadding = {
left = ninePatchContentPadding.left * scaleX,
top = ninePatchContentPadding.top * scaleY,
right = ninePatchContentPadding.right * scaleX,
bottom = ninePatchContentPadding.bottom * scaleY,
}
else
-- Fallback if atlas image not available
tempPadding = {
left = ninePatchContentPadding.left,
top = ninePatchContentPadding.top,
right = ninePatchContentPadding.right,
bottom = ninePatchContentPadding.bottom,
}
end
else
-- Fallback if theme not found
tempPadding = {
left = ninePatchContentPadding.left,
top = ninePatchContentPadding.top,
right = ninePatchContentPadding.right,
bottom = ninePatchContentPadding.bottom,
}
end
else else
tempPadding = Units.resolveSpacing(props.padding, self.width, self.height) tempPadding = Units.resolveSpacing(props.padding, self.width, self.height)
end end
@@ -3119,6 +3152,109 @@ function Element:getBorderBoxHeight()
return self._borderBoxHeight or (self.height + self.padding.top + self.padding.bottom) return self._borderBoxHeight or (self.height + self.padding.top + self.padding.bottom)
end end
--- Get the current state's scaled content padding
--- Returns the contentPadding for the current theme state, scaled to the element's size
---@return table|nil -- {left, top, right, bottom} or nil if no contentPadding
function Element:getScaledContentPadding()
if not self.themeComponent then
return nil
end
local themeToUse = self.theme and themes[self.theme] or Theme.getActive()
if not themeToUse or not themeToUse.components[self.themeComponent] then
return nil
end
local component = themeToUse.components[self.themeComponent]
-- Check for state-specific override
local state = self._themeState or "normal"
if state and state ~= "normal" and component.states and component.states[state] then
component = component.states[state]
end
if not component._ninePatchData or not component._ninePatchData.contentPadding then
return nil
end
local contentPadding = component._ninePatchData.contentPadding
-- Scale contentPadding to match the actual rendered size
local atlasImage = component._loadedAtlas or themeToUse.atlas
if atlasImage and type(atlasImage) ~= "string" then
local originalWidth, originalHeight = atlasImage:getDimensions()
local borderBoxWidth = self._borderBoxWidth or (self.width + self.padding.left + self.padding.right)
local borderBoxHeight = self._borderBoxHeight or (self.height + self.padding.top + self.padding.bottom)
local scaleX = borderBoxWidth / originalWidth
local scaleY = borderBoxHeight / originalHeight
return {
left = contentPadding.left * scaleX,
top = contentPadding.top * scaleY,
right = contentPadding.right * scaleX,
bottom = contentPadding.bottom * scaleY,
}
else
-- Return unscaled values as fallback
return {
left = contentPadding.left,
top = contentPadding.top,
right = contentPadding.right,
bottom = contentPadding.bottom,
}
end
end
--- Get available content width for children (accounting for 9-slice content padding)
--- This is the width that children should use when calculating percentage widths
---@return number
function Element:getAvailableContentWidth()
local availableWidth = self.width
local scaledContentPadding = self:getScaledContentPadding()
if scaledContentPadding then
-- Check if the element is using the scaled 9-patch contentPadding as its padding
-- Allow small floating point differences (within 0.1 pixels)
local usingContentPaddingAsPadding = (
math.abs(self.padding.left - scaledContentPadding.left) < 0.1
and math.abs(self.padding.right - scaledContentPadding.right) < 0.1
)
if not usingContentPaddingAsPadding then
-- Element has explicit padding different from contentPadding
-- Subtract scaled contentPadding to get the area children should use
availableWidth = availableWidth - scaledContentPadding.left - scaledContentPadding.right
end
end
return math.max(0, availableWidth)
end
--- Get available content height for children (accounting for 9-slice content padding)
--- This is the height that children should use when calculating percentage heights
---@return number
function Element:getAvailableContentHeight()
local availableHeight = self.height
local scaledContentPadding = self:getScaledContentPadding()
if scaledContentPadding then
-- Check if the element is using the scaled 9-patch contentPadding as its padding
-- Allow small floating point differences (within 0.1 pixels)
local usingContentPaddingAsPadding = (
math.abs(self.padding.top - scaledContentPadding.top) < 0.1
and math.abs(self.padding.bottom - scaledContentPadding.bottom) < 0.1
)
if not usingContentPaddingAsPadding then
-- Element has explicit padding different from contentPadding
-- Subtract scaled contentPadding to get the area children should use
availableHeight = availableHeight - scaledContentPadding.top - scaledContentPadding.bottom
end
end
return math.max(0, availableHeight)
end
--- Add child to element --- Add child to element
---@param child Element ---@param child Element
function Element:addChild(child) function Element:addChild(child)
@@ -3836,18 +3972,38 @@ function Element:draw()
local textWidth = font:getWidth(self.text) local textWidth = font:getWidth(self.text)
local textHeight = font:getHeight() local textHeight = font:getHeight()
local tx, ty local tx, ty
-- Text is drawn in the content box (inside padding) -- Text is drawn in the content box (inside padding)
local contentX = self.x + self.padding.left -- For 9-slice components, use contentPadding if available
local contentY = self.y + self.padding.top local textPaddingLeft = self.padding.left
local textPaddingTop = self.padding.top
local textAreaWidth = self.width
local textAreaHeight = self.height
-- Check if we should use 9-slice contentPadding for text positioning
local scaledContentPadding = self:getScaledContentPadding()
if scaledContentPadding then
local borderBoxWidth = self._borderBoxWidth or (self.width + self.padding.left + self.padding.right)
local borderBoxHeight = self._borderBoxHeight or (self.height + self.padding.top + self.padding.bottom)
textPaddingLeft = scaledContentPadding.left
textPaddingTop = scaledContentPadding.top
textAreaWidth = borderBoxWidth - scaledContentPadding.left - scaledContentPadding.right
textAreaHeight = borderBoxHeight - scaledContentPadding.top - scaledContentPadding.bottom
end
local contentX = self.x + textPaddingLeft
local contentY = self.y + textPaddingTop
if self.textAlign == TextAlign.START then if self.textAlign == TextAlign.START then
tx = contentX tx = contentX
ty = contentY ty = contentY
elseif self.textAlign == TextAlign.CENTER then elseif self.textAlign == TextAlign.CENTER then
tx = contentX + (self.width - textWidth) / 2 tx = contentX + (textAreaWidth - textWidth) / 2
ty = contentY + (self.height - textHeight) / 2 ty = contentY + (textAreaHeight - textHeight) / 2
elseif self.textAlign == TextAlign.END then elseif self.textAlign == TextAlign.END then
tx = contentX + self.width - textWidth - 10 tx = contentX + textAreaWidth - textWidth - 10
ty = contentY + self.height - textHeight - 10 ty = contentY + textAreaHeight - textHeight - 10
elseif self.textAlign == TextAlign.JUSTIFY then elseif self.textAlign == TextAlign.JUSTIFY then
--- need to figure out spreading --- need to figure out spreading
tx = contentX tx = contentX