immediate mode scroll regression fixed
This commit is contained in:
@@ -85,7 +85,11 @@ local function isPointInElement(element, x, y)
|
||||
local bw = element._borderBoxWidth or (element.width + element.padding.left + element.padding.right)
|
||||
local bh = element._borderBoxHeight or (element.height + element.padding.top + element.padding.bottom)
|
||||
|
||||
-- Walk up parent chain to check clipping and apply scroll offsets
|
||||
-- Calculate scroll offset from parent chain
|
||||
local scrollOffsetX = 0
|
||||
local scrollOffsetY = 0
|
||||
|
||||
-- Walk up parent chain to check clipping and accumulate scroll offsets
|
||||
local current = element.parent
|
||||
while current do
|
||||
local overflowX = current.overflowX or current.overflow
|
||||
@@ -101,12 +105,20 @@ local function isPointInElement(element, x, y)
|
||||
if x < parentX or x > parentX + parentW or y < parentY or y > parentY + parentH then
|
||||
return false -- Point is clipped by parent
|
||||
end
|
||||
|
||||
-- Accumulate scroll offset
|
||||
scrollOffsetX = scrollOffsetX + (current._scrollX or 0)
|
||||
scrollOffsetY = scrollOffsetY + (current._scrollY or 0)
|
||||
end
|
||||
|
||||
current = current.parent
|
||||
end
|
||||
|
||||
return x >= bx and x <= bx + bw and y >= by and y <= by + bh
|
||||
-- Adjust mouse position by scroll offset for hit testing
|
||||
local adjustedX = x + scrollOffsetX
|
||||
local adjustedY = y + scrollOffsetY
|
||||
|
||||
return adjustedX >= bx and adjustedX <= bx + bw and adjustedY >= by and adjustedY <= by + bh
|
||||
end
|
||||
|
||||
--- Get the topmost element at a screen position
|
||||
|
||||
@@ -540,6 +540,7 @@ function Element.new(props)
|
||||
Grid = Grid,
|
||||
Units = Units,
|
||||
Context = Context,
|
||||
ErrorHandler = ErrorHandler,
|
||||
})
|
||||
self._layoutEngine:initialize(self)
|
||||
|
||||
@@ -633,7 +634,10 @@ function Element.new(props)
|
||||
-- Pixel units
|
||||
self.textSize = value
|
||||
else
|
||||
ErrorHandler.error("Element", "Unknown textSize unit: " .. unit)
|
||||
ErrorHandler.error(
|
||||
"Element",
|
||||
string.format("Unknown textSize unit '%s'. Valid units: px, %%, vw, vh, ew, eh. Or use presets: xs, sm, md, lg, xl, xxl, 2xl, 3xl, 4xl", unit)
|
||||
)
|
||||
end
|
||||
else
|
||||
-- Validate pixel textSize value
|
||||
@@ -835,13 +839,18 @@ function Element.new(props)
|
||||
|
||||
-- Re-resolve ew/eh textSize units now that width/height are set
|
||||
if props.textSize and type(props.textSize) == "string" then
|
||||
local value, unit = Units.parse(props.textSize)
|
||||
if unit == "ew" then
|
||||
-- Element width relative (now that width is set)
|
||||
self.textSize = (value / 100) * self.width
|
||||
elseif unit == "eh" then
|
||||
-- Element height relative (now that height is set)
|
||||
self.textSize = (value / 100) * self.height
|
||||
-- Check if it's a preset first (presets don't need re-resolution)
|
||||
local presetValue, presetUnit = resolveTextSizePreset(props.textSize)
|
||||
if not presetValue then
|
||||
-- Not a preset, parse and check for ew/eh units
|
||||
local value, unit = Units.parse(props.textSize)
|
||||
if unit == "ew" then
|
||||
-- Element width relative (now that width is set)
|
||||
self.textSize = (value / 100) * self.width
|
||||
elseif unit == "eh" then
|
||||
-- Element height relative (now that height is set)
|
||||
self.textSize = (value / 100) * self.height
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1026,7 +1035,7 @@ function Element.new(props)
|
||||
-- Absolute positioning is relative to parent's content area (padding box)
|
||||
local baseX = self.parent.x + self.parent.padding.left
|
||||
local baseY = self.parent.y + self.parent.padding.top
|
||||
|
||||
|
||||
-- Handle x position with units
|
||||
if props.x then
|
||||
if type(props.x) == "string" then
|
||||
@@ -1253,16 +1262,36 @@ function Element.new(props)
|
||||
-- Update the LayoutEngine with actual layout properties
|
||||
-- (it was initialized early with defaults for auto-sizing calculations)
|
||||
self._layoutEngine.positioning = self.positioning
|
||||
if self.flexDirection then self._layoutEngine.flexDirection = self.flexDirection end
|
||||
if self.flexWrap then self._layoutEngine.flexWrap = self.flexWrap end
|
||||
if self.justifyContent then self._layoutEngine.justifyContent = self.justifyContent end
|
||||
if self.alignItems then self._layoutEngine.alignItems = self.alignItems end
|
||||
if self.alignContent then self._layoutEngine.alignContent = self.alignContent end
|
||||
if self.gap then self._layoutEngine.gap = self.gap end
|
||||
if self.gridRows then self._layoutEngine.gridRows = self.gridRows end
|
||||
if self.gridColumns then self._layoutEngine.gridColumns = self.gridColumns end
|
||||
if self.columnGap then self._layoutEngine.columnGap = self.columnGap end
|
||||
if self.rowGap then self._layoutEngine.rowGap = self.rowGap end
|
||||
if self.flexDirection then
|
||||
self._layoutEngine.flexDirection = self.flexDirection
|
||||
end
|
||||
if self.flexWrap then
|
||||
self._layoutEngine.flexWrap = self.flexWrap
|
||||
end
|
||||
if self.justifyContent then
|
||||
self._layoutEngine.justifyContent = self.justifyContent
|
||||
end
|
||||
if self.alignItems then
|
||||
self._layoutEngine.alignItems = self.alignItems
|
||||
end
|
||||
if self.alignContent then
|
||||
self._layoutEngine.alignContent = self.alignContent
|
||||
end
|
||||
if self.gap then
|
||||
self._layoutEngine.gap = self.gap
|
||||
end
|
||||
if self.gridRows then
|
||||
self._layoutEngine.gridRows = self.gridRows
|
||||
end
|
||||
if self.gridColumns then
|
||||
self._layoutEngine.gridColumns = self.gridColumns
|
||||
end
|
||||
if self.columnGap then
|
||||
self._layoutEngine.columnGap = self.columnGap
|
||||
end
|
||||
if self.rowGap then
|
||||
self._layoutEngine.rowGap = self.rowGap
|
||||
end
|
||||
|
||||
---animation
|
||||
self.transform = props.transform or {}
|
||||
@@ -1285,6 +1314,7 @@ function Element.new(props)
|
||||
_scrollY = props._scrollY,
|
||||
}, {
|
||||
utils = utils,
|
||||
Color = Color,
|
||||
})
|
||||
self._scrollManager:initialize(self)
|
||||
|
||||
@@ -1464,15 +1494,6 @@ function Element:_handleScrollbarRelease(button)
|
||||
return false
|
||||
end
|
||||
|
||||
--- Scroll to track click position (internal method used by ScrollManager)
|
||||
---@param mouseX number
|
||||
---@param mouseY number
|
||||
---@param component string -- "vertical" or "horizontal"
|
||||
function Element:_scrollToTrackPosition(mouseX, mouseY, component)
|
||||
-- This method is now handled internally by ScrollManager
|
||||
-- Keeping empty stub for backward compatibility
|
||||
end
|
||||
|
||||
--- Handle mouse wheel scrolling (delegates to ScrollManager)
|
||||
---@param x number -- Horizontal scroll amount
|
||||
---@param y number -- Vertical scroll amount
|
||||
@@ -1794,10 +1815,11 @@ function Element:draw(backdropCanvas)
|
||||
self._renderer:drawText(self)
|
||||
|
||||
-- Draw visual feedback when element is pressed (if it has an onEvent handler and highlight is not disabled)
|
||||
if self.onEvent and not self.disableHighlight then
|
||||
if self.onEvent and not self.disableHighlight and self._eventHandler then
|
||||
-- Check if any button is pressed
|
||||
local anyPressed = false
|
||||
for _, pressed in pairs(self._pressed) do
|
||||
local pressedState = self._eventHandler:getState()._pressed or {}
|
||||
for _, pressed in pairs(pressedState) do
|
||||
if pressed then
|
||||
anyPressed = true
|
||||
break
|
||||
|
||||
@@ -57,6 +57,7 @@ function LayoutEngine.new(props, deps)
|
||||
self._Grid = deps.Grid
|
||||
self._Units = deps.Units
|
||||
self._Context = deps.Context
|
||||
self._ErrorHandler = deps.ErrorHandler
|
||||
self._Positioning = Positioning
|
||||
self._FlexDirection = FlexDirection
|
||||
self._JustifyContent = JustifyContent
|
||||
@@ -174,6 +175,22 @@ function LayoutEngine:layoutChildren()
|
||||
local isFlexChild = not (child.positioning == self._Positioning.ABSOLUTE and child._explicitlyAbsolute)
|
||||
if isFlexChild then
|
||||
table.insert(flexChildren, child)
|
||||
|
||||
-- Warn if child uses percentage sizing but parent has autosizing
|
||||
if self._ErrorHandler then
|
||||
if child.units and child.units.width then
|
||||
if child.units.width.unit == "%" and self.element.autosizing and self.element.autosizing.width then
|
||||
self._ErrorHandler.warn("LayoutEngine",
|
||||
string.format("Child '%s' uses percentage width but parent has auto-sizing enabled. This may cause unexpected results", child.id or "unnamed"))
|
||||
end
|
||||
end
|
||||
if child.units and child.units.height then
|
||||
if child.units.height.unit == "%" and self.element.autosizing and self.element.autosizing.height then
|
||||
self._ErrorHandler.warn("LayoutEngine",
|
||||
string.format("Child '%s' uses percentage height but parent has auto-sizing enabled. This may cause unexpected results", child.id or "unnamed"))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
local Units = {}
|
||||
|
||||
local Context = nil
|
||||
local ErrorHandler = nil
|
||||
|
||||
--- Initialize Units module with Context dependency
|
||||
---@param context table The Context module
|
||||
@@ -8,6 +9,12 @@ function Units.initialize(context)
|
||||
Context = context
|
||||
end
|
||||
|
||||
--- Initialize ErrorHandler dependency
|
||||
---@param errorHandler table The ErrorHandler module
|
||||
function Units.initializeErrorHandler(errorHandler)
|
||||
ErrorHandler = errorHandler
|
||||
end
|
||||
|
||||
---@param value string|number
|
||||
---@return number, string -- Returns numeric value and unit type ("px", "%", "vw", "vh")
|
||||
function Units.parse(value)
|
||||
@@ -16,20 +23,34 @@ function Units.parse(value)
|
||||
end
|
||||
|
||||
if type(value) ~= "string" then
|
||||
-- Fallback to 0px for invalid types
|
||||
if ErrorHandler then
|
||||
ErrorHandler.error("Units", string.format("Invalid unit value type. Expected string or number, got %s", type(value)))
|
||||
end
|
||||
return 0, "px"
|
||||
end
|
||||
|
||||
-- Check for invalid format (space between number and unit)
|
||||
if value:match("%d%s+%a") then
|
||||
if ErrorHandler then
|
||||
ErrorHandler.error("Units", string.format("Invalid unit string '%s' (contains space). Use format like '50px' or '50%%'", value))
|
||||
end
|
||||
return 0, "px"
|
||||
end
|
||||
|
||||
-- Match number followed by optional unit
|
||||
local numStr, unit = value:match("^([%-]?[%d%.]+)(.*)$")
|
||||
if not numStr then
|
||||
-- Fallback to 0px for invalid format
|
||||
if ErrorHandler then
|
||||
ErrorHandler.error("Units", string.format("Invalid unit format '%s'. Expected format: number + unit (e.g., '50px', '10%%', '2vw')", value))
|
||||
end
|
||||
return 0, "px"
|
||||
end
|
||||
|
||||
local num = tonumber(numStr)
|
||||
if not num then
|
||||
-- Fallback to 0px for invalid numeric value
|
||||
if ErrorHandler then
|
||||
ErrorHandler.error("Units", string.format("Invalid numeric value in '%s'", value))
|
||||
end
|
||||
return 0, "px"
|
||||
end
|
||||
|
||||
@@ -40,6 +61,9 @@ function Units.parse(value)
|
||||
|
||||
local validUnits = { px = true, ["%"] = true, vw = true, vh = true, ew = true, eh = true }
|
||||
if not validUnits[unit] then
|
||||
if ErrorHandler then
|
||||
ErrorHandler.error("Units", string.format("Unknown unit '%s' in '%s'. Valid units: px, %%, vw, vh, ew, eh", unit, value))
|
||||
end
|
||||
return num, "px"
|
||||
end
|
||||
|
||||
@@ -59,7 +83,11 @@ function Units.resolve(value, unit, viewportWidth, viewportHeight, parentSize)
|
||||
return value
|
||||
elseif unit == "%" then
|
||||
if not parentSize then
|
||||
error(formatError("Units", "Percentage units require parent dimension"))
|
||||
if ErrorHandler then
|
||||
ErrorHandler.error("Units", "Percentage units require parent dimension. Element has no parent or parent dimension not available")
|
||||
else
|
||||
error("Percentage units require parent dimension")
|
||||
end
|
||||
end
|
||||
return (value / 100) * parentSize
|
||||
elseif unit == "vw" then
|
||||
@@ -67,7 +95,11 @@ function Units.resolve(value, unit, viewportWidth, viewportHeight, parentSize)
|
||||
elseif unit == "vh" then
|
||||
return (value / 100) * viewportHeight
|
||||
else
|
||||
error(formatError("Units", string.format("Unknown unit type: '%s'. Valid units: px, %%, vw, vh, ew, eh", unit)))
|
||||
if ErrorHandler then
|
||||
ErrorHandler.error("Units", string.format("Unknown unit '%s'. Valid units: px, %%, vw, vh, ew, eh", unit))
|
||||
else
|
||||
error(string.format("Unknown unit type: '%s'", unit))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user