scrollbars fixed
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
|||||||
Cartographer.lua
|
Cartographer.lua
|
||||||
OverlayStats.lua
|
OverlayStats.lua
|
||||||
|
lume.lua
|
||||||
|
lurker.lua
|
||||||
themes/metal/
|
themes/metal/
|
||||||
themes/space/
|
themes/space/
|
||||||
.DS_STORE
|
.DS_STORE
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ Public API methods to access internal state:
|
|||||||
---@field objectPosition string? -- Image position like "center center", "top left", "50% 50%" (default: "center center")
|
---@field objectPosition string? -- Image position like "center center", "top left", "50% 50%" (default: "center center")
|
||||||
---@field imageOpacity number? -- Image opacity 0-1 (default: 1, combines with element opacity)
|
---@field imageOpacity number? -- Image opacity 0-1 (default: 1, combines with element opacity)
|
||||||
---@field _loadedImage love.Image? -- Internal: cached loaded image
|
---@field _loadedImage love.Image? -- Internal: cached loaded image
|
||||||
|
---@field hideScrollbars boolean|{vertical:boolean, horizontal:boolean}? -- Hide scrollbars (boolean for both, or table for individual control)
|
||||||
local Element = {}
|
local Element = {}
|
||||||
Element.__index = Element
|
Element.__index = Element
|
||||||
|
|
||||||
@@ -1127,6 +1128,22 @@ function Element.new(props)
|
|||||||
self.scrollbarPadding = props.scrollbarPadding or 2
|
self.scrollbarPadding = props.scrollbarPadding or 2
|
||||||
self.scrollSpeed = props.scrollSpeed or 20
|
self.scrollSpeed = props.scrollSpeed or 20
|
||||||
|
|
||||||
|
-- hideScrollbars can be boolean or table {vertical: boolean, horizontal: boolean}
|
||||||
|
if props.hideScrollbars ~= nil then
|
||||||
|
if type(props.hideScrollbars) == "boolean" then
|
||||||
|
self.hideScrollbars = { vertical = props.hideScrollbars, horizontal = props.hideScrollbars }
|
||||||
|
elseif type(props.hideScrollbars) == "table" then
|
||||||
|
self.hideScrollbars = {
|
||||||
|
vertical = props.hideScrollbars.vertical ~= nil and props.hideScrollbars.vertical or false,
|
||||||
|
horizontal = props.hideScrollbars.horizontal ~= nil and props.hideScrollbars.horizontal or false,
|
||||||
|
}
|
||||||
|
else
|
||||||
|
self.hideScrollbars = { vertical = false, horizontal = false }
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.hideScrollbars = { vertical = false, horizontal = false }
|
||||||
|
end
|
||||||
|
|
||||||
-- Internal overflow state
|
-- Internal overflow state
|
||||||
self._overflowX = false
|
self._overflowX = false
|
||||||
self._overflowY = false
|
self._overflowY = false
|
||||||
@@ -1140,10 +1157,12 @@ function Element.new(props)
|
|||||||
self._maxScrollY = 0
|
self._maxScrollY = 0
|
||||||
|
|
||||||
-- Scrollbar interaction state
|
-- Scrollbar interaction state
|
||||||
self._scrollbarHovered = false
|
self._scrollbarHoveredVertical = false
|
||||||
|
self._scrollbarHoveredHorizontal = false
|
||||||
self._scrollbarDragging = false
|
self._scrollbarDragging = false
|
||||||
self._hoveredScrollbar = nil -- "vertical" or "horizontal"
|
self._hoveredScrollbar = nil -- "vertical" or "horizontal"
|
||||||
self._scrollbarDragOffset = 0 -- Offset from thumb top when drag started
|
self._scrollbarDragOffset = 0 -- Offset from thumb top when drag started
|
||||||
|
self._scrollbarPressHandled = false -- Track if scrollbar press was handled this frame
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -1312,21 +1331,21 @@ function Element:_drawScrollbars(dims)
|
|||||||
local x, y = self.x, self.y
|
local x, y = self.x, self.y
|
||||||
local w, h = self.width, self.height
|
local w, h = self.width, self.height
|
||||||
|
|
||||||
-- Determine thumb color based on state
|
-- Vertical scrollbar
|
||||||
|
if dims.vertical.visible and not self.hideScrollbars.vertical then
|
||||||
|
local trackX = x + w - self.scrollbarWidth - self.scrollbarPadding + self.padding.left
|
||||||
|
local trackY = y + self.scrollbarPadding + self.padding.top
|
||||||
|
|
||||||
|
-- Determine thumb color based on state (independent for vertical)
|
||||||
local thumbColor = self.scrollbarColor
|
local thumbColor = self.scrollbarColor
|
||||||
if self._scrollbarDragging then
|
if self._scrollbarDragging and self._hoveredScrollbar == "vertical" then
|
||||||
-- Active state: brighter
|
-- Active state: brighter
|
||||||
thumbColor = Color.new(math.min(1, thumbColor.r * 1.4), math.min(1, thumbColor.g * 1.4), math.min(1, thumbColor.b * 1.4), thumbColor.a)
|
thumbColor = Color.new(math.min(1, thumbColor.r * 1.4), math.min(1, thumbColor.g * 1.4), math.min(1, thumbColor.b * 1.4), thumbColor.a)
|
||||||
elseif self._scrollbarHovered then
|
elseif self._scrollbarHoveredVertical then
|
||||||
-- Hover state: slightly brighter
|
-- Hover state: slightly brighter
|
||||||
thumbColor = Color.new(math.min(1, thumbColor.r * 1.2), math.min(1, thumbColor.g * 1.2), math.min(1, thumbColor.b * 1.2), thumbColor.a)
|
thumbColor = Color.new(math.min(1, thumbColor.r * 1.2), math.min(1, thumbColor.g * 1.2), math.min(1, thumbColor.b * 1.2), thumbColor.a)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Vertical scrollbar
|
|
||||||
if dims.vertical.visible then
|
|
||||||
local trackX = x + w - self.scrollbarWidth - self.scrollbarPadding + self.padding.left
|
|
||||||
local trackY = y + self.scrollbarPadding + self.padding.top
|
|
||||||
|
|
||||||
-- Draw track
|
-- Draw track
|
||||||
love.graphics.setColor(self.scrollbarTrackColor:toRGBA())
|
love.graphics.setColor(self.scrollbarTrackColor:toRGBA())
|
||||||
love.graphics.rectangle("fill", trackX, trackY, self.scrollbarWidth, dims.vertical.trackHeight, self.scrollbarRadius)
|
love.graphics.rectangle("fill", trackX, trackY, self.scrollbarWidth, dims.vertical.trackHeight, self.scrollbarRadius)
|
||||||
@@ -1337,10 +1356,20 @@ function Element:_drawScrollbars(dims)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Horizontal scrollbar
|
-- Horizontal scrollbar
|
||||||
if dims.horizontal.visible then
|
if dims.horizontal.visible and not self.hideScrollbars.horizontal then
|
||||||
local trackX = x + self.scrollbarPadding + self.padding.left
|
local trackX = x + self.scrollbarPadding + self.padding.left
|
||||||
local trackY = y + h - self.scrollbarWidth - self.scrollbarPadding + self.padding.top
|
local trackY = y + h - self.scrollbarWidth - self.scrollbarPadding + self.padding.top
|
||||||
|
|
||||||
|
-- Determine thumb color based on state (independent for horizontal)
|
||||||
|
local thumbColor = self.scrollbarColor
|
||||||
|
if self._scrollbarDragging and self._hoveredScrollbar == "horizontal" then
|
||||||
|
-- Active state: brighter
|
||||||
|
thumbColor = Color.new(math.min(1, thumbColor.r * 1.4), math.min(1, thumbColor.g * 1.4), math.min(1, thumbColor.b * 1.4), thumbColor.a)
|
||||||
|
elseif self._scrollbarHoveredHorizontal then
|
||||||
|
-- Hover state: slightly brighter
|
||||||
|
thumbColor = Color.new(math.min(1, thumbColor.r * 1.2), math.min(1, thumbColor.g * 1.2), math.min(1, thumbColor.b * 1.2), thumbColor.a)
|
||||||
|
end
|
||||||
|
|
||||||
-- Draw track
|
-- Draw track
|
||||||
love.graphics.setColor(self.scrollbarTrackColor:toRGBA())
|
love.graphics.setColor(self.scrollbarTrackColor:toRGBA())
|
||||||
love.graphics.rectangle("fill", trackX, trackY, dims.horizontal.trackWidth, self.scrollbarWidth, self.scrollbarRadius)
|
love.graphics.rectangle("fill", trackX, trackY, dims.horizontal.trackWidth, self.scrollbarWidth, self.scrollbarRadius)
|
||||||
@@ -1370,8 +1399,8 @@ function Element:_getScrollbarAtPosition(mouseX, mouseY)
|
|||||||
local x, y = self.x, self.y
|
local x, y = self.x, self.y
|
||||||
local w, h = self.width, self.height
|
local w, h = self.width, self.height
|
||||||
|
|
||||||
-- Check vertical scrollbar
|
-- Check vertical scrollbar (only if not hidden)
|
||||||
if dims.vertical.visible then
|
if dims.vertical.visible and not self.hideScrollbars.vertical then
|
||||||
local trackX = x + w - self.scrollbarWidth - self.scrollbarPadding + self.padding.left
|
local trackX = x + w - self.scrollbarWidth - self.scrollbarPadding + self.padding.left
|
||||||
local trackY = y + self.scrollbarPadding + self.padding.top
|
local trackY = y + self.scrollbarPadding + self.padding.top
|
||||||
local trackW = self.scrollbarWidth
|
local trackW = self.scrollbarWidth
|
||||||
@@ -1389,8 +1418,8 @@ function Element:_getScrollbarAtPosition(mouseX, mouseY)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check horizontal scrollbar
|
-- Check horizontal scrollbar (only if not hidden)
|
||||||
if dims.horizontal.visible then
|
if dims.horizontal.visible and not self.hideScrollbars.horizontal then
|
||||||
local trackX = x + self.scrollbarPadding + self.padding.left
|
local trackX = x + self.scrollbarPadding + self.padding.left
|
||||||
local trackY = y + h - self.scrollbarWidth - self.scrollbarPadding + self.padding.top
|
local trackY = y + h - self.scrollbarWidth - self.scrollbarPadding + self.padding.top
|
||||||
local trackW = dims.horizontal.trackWidth
|
local trackW = dims.horizontal.trackWidth
|
||||||
@@ -2699,17 +2728,31 @@ function Element:update(dt)
|
|||||||
-- Handle scrollbar hover detection
|
-- Handle scrollbar hover detection
|
||||||
local mx, my = love.mouse.getPosition()
|
local mx, my = love.mouse.getPosition()
|
||||||
local scrollbar = self:_getScrollbarAtPosition(mx, my)
|
local scrollbar = self:_getScrollbarAtPosition(mx, my)
|
||||||
local wasHovered = self._scrollbarHovered
|
|
||||||
if scrollbar then
|
-- Update independent hover states for vertical and horizontal scrollbars
|
||||||
self._scrollbarHovered = true
|
if scrollbar and scrollbar.component == "vertical" then
|
||||||
self._hoveredScrollbar = scrollbar.component
|
self._scrollbarHoveredVertical = true
|
||||||
|
self._hoveredScrollbar = "vertical"
|
||||||
else
|
else
|
||||||
if not self._scrollbarDragging then
|
if not (self._scrollbarDragging and self._hoveredScrollbar == "vertical") then
|
||||||
self._scrollbarHovered = false
|
self._scrollbarHoveredVertical = false
|
||||||
self._hoveredScrollbar = nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if scrollbar and scrollbar.component == "horizontal" then
|
||||||
|
self._scrollbarHoveredHorizontal = true
|
||||||
|
self._hoveredScrollbar = "horizontal"
|
||||||
|
else
|
||||||
|
if not (self._scrollbarDragging and self._hoveredScrollbar == "horizontal") then
|
||||||
|
self._scrollbarHoveredHorizontal = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clear hoveredScrollbar if neither is hovered
|
||||||
|
if not scrollbar and not self._scrollbarDragging then
|
||||||
|
self._hoveredScrollbar = nil
|
||||||
|
end
|
||||||
|
|
||||||
-- Handle scrollbar dragging
|
-- Handle scrollbar dragging
|
||||||
if self._scrollbarDragging and love.mouse.isDown(1) then
|
if self._scrollbarDragging and love.mouse.isDown(1) then
|
||||||
self:_handleScrollbarDrag(mx, my)
|
self:_handleScrollbarDrag(mx, my)
|
||||||
@@ -2718,6 +2761,25 @@ function Element:update(dt)
|
|||||||
self._scrollbarDragging = false
|
self._scrollbarDragging = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Handle scrollbar click/press (independent of callback)
|
||||||
|
-- Check if we should handle scrollbar press for elements with overflow
|
||||||
|
local overflowX = self.overflowX or self.overflow
|
||||||
|
local overflowY = self.overflowY or self.overflow
|
||||||
|
local hasScrollableOverflow = (overflowX == "scroll" or overflowX == "auto" or overflowY == "scroll" or overflowY == "auto")
|
||||||
|
|
||||||
|
if hasScrollableOverflow and not self._scrollbarDragging then
|
||||||
|
-- Check for scrollbar press on left mouse button
|
||||||
|
if love.mouse.isDown(1) and not self._scrollbarPressHandled then
|
||||||
|
local scrollbarPressed = self:_handleScrollbarPress(mx, my, 1)
|
||||||
|
if scrollbarPressed then
|
||||||
|
self._scrollbarPressHandled = true
|
||||||
|
end
|
||||||
|
elseif not love.mouse.isDown(1) then
|
||||||
|
-- Reset press handled flag when button is released
|
||||||
|
self._scrollbarPressHandled = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Handle click detection for element with enhanced event system
|
-- Handle click detection for element with enhanced event system
|
||||||
if self.callback or self.themeComponent then
|
if self.callback or self.themeComponent then
|
||||||
-- Clickable area is the border box (x, y already includes padding)
|
-- Clickable area is the border box (x, y already includes padding)
|
||||||
@@ -2768,10 +2830,11 @@ function Element:update(dt)
|
|||||||
if love.mouse.isDown(button) then
|
if love.mouse.isDown(button) then
|
||||||
-- Button is pressed down
|
-- Button is pressed down
|
||||||
if not self._pressed[button] then
|
if not self._pressed[button] then
|
||||||
-- Check if press is on scrollbar first
|
-- Check if press is on scrollbar first (skip if already handled)
|
||||||
if button == 1 and self:_handleScrollbarPress(mx, my, button) then
|
if button == 1 and not self._scrollbarPressHandled and self:_handleScrollbarPress(mx, my, button) then
|
||||||
-- Scrollbar consumed the event, mark as pressed to prevent callback
|
-- Scrollbar consumed the event, mark as pressed to prevent callback
|
||||||
self._pressed[button] = true
|
self._pressed[button] = true
|
||||||
|
self._scrollbarPressHandled = true
|
||||||
else
|
else
|
||||||
-- Just pressed - fire press event and record drag start position
|
-- Just pressed - fire press event and record drag start position
|
||||||
local modifiers = getModifiers()
|
local modifiers = getModifiers()
|
||||||
|
|||||||
552
testing/__tests__/30_scrollbar_features_tests.lua
Normal file
552
testing/__tests__/30_scrollbar_features_tests.lua
Normal file
@@ -0,0 +1,552 @@
|
|||||||
|
package.path = package.path .. ";./?.lua;./game/?.lua;./game/utils/?.lua;./game/components/?.lua;./game/systems/?.lua"
|
||||||
|
|
||||||
|
local luaunit = require("testing.luaunit")
|
||||||
|
require("testing.loveStub") -- Required to mock LOVE functions
|
||||||
|
local FlexLove = require("FlexLove")
|
||||||
|
local Gui, enums, Color = FlexLove.GUI, FlexLove.enums, FlexLove.Color
|
||||||
|
|
||||||
|
local Positioning = enums.Positioning
|
||||||
|
|
||||||
|
-- Create test cases for scrollbar features
|
||||||
|
TestScrollbarFeatures = {}
|
||||||
|
|
||||||
|
function TestScrollbarFeatures:setUp()
|
||||||
|
-- Clean up before each test
|
||||||
|
Gui.destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestScrollbarFeatures:tearDown()
|
||||||
|
-- Clean up after each test
|
||||||
|
Gui.destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 1: hideScrollbars with boolean value
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testHideScrollbarsBooleanTrue()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
hideScrollbars = true,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Verify hideScrollbars is properly initialized
|
||||||
|
luaunit.assertNotNil(container.hideScrollbars)
|
||||||
|
luaunit.assertEquals(type(container.hideScrollbars), "table")
|
||||||
|
luaunit.assertEquals(container.hideScrollbars.vertical, true)
|
||||||
|
luaunit.assertEquals(container.hideScrollbars.horizontal, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestScrollbarFeatures:testHideScrollbarsBooleanFalse()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
hideScrollbars = false,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Verify hideScrollbars defaults to showing scrollbars
|
||||||
|
luaunit.assertNotNil(container.hideScrollbars)
|
||||||
|
luaunit.assertEquals(container.hideScrollbars.vertical, false)
|
||||||
|
luaunit.assertEquals(container.hideScrollbars.horizontal, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 2: hideScrollbars with table configuration
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testHideScrollbarsTableVerticalOnly()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
hideScrollbars = { vertical = true, horizontal = false },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Verify only vertical scrollbar is hidden
|
||||||
|
luaunit.assertEquals(container.hideScrollbars.vertical, true)
|
||||||
|
luaunit.assertEquals(container.hideScrollbars.horizontal, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestScrollbarFeatures:testHideScrollbarsTableHorizontalOnly()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
hideScrollbars = { vertical = false, horizontal = true },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Verify only horizontal scrollbar is hidden
|
||||||
|
luaunit.assertEquals(container.hideScrollbars.vertical, false)
|
||||||
|
luaunit.assertEquals(container.hideScrollbars.horizontal, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestScrollbarFeatures:testHideScrollbarsTableBothHidden()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
hideScrollbars = { vertical = false, horizontal = false },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Verify both scrollbars are shown
|
||||||
|
luaunit.assertEquals(container.hideScrollbars.vertical, false)
|
||||||
|
luaunit.assertEquals(container.hideScrollbars.horizontal, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 3: Default hideScrollbars behavior
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testHideScrollbarsDefault()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Verify default is to show scrollbars (backward compatibility)
|
||||||
|
luaunit.assertNotNil(container.hideScrollbars)
|
||||||
|
luaunit.assertEquals(container.hideScrollbars.vertical, false)
|
||||||
|
luaunit.assertEquals(container.hideScrollbars.horizontal, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 4: Independent hover states initialization
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testIndependentHoverStatesInitialization()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Verify independent hover states are initialized
|
||||||
|
luaunit.assertNotNil(container._scrollbarHoveredVertical)
|
||||||
|
luaunit.assertNotNil(container._scrollbarHoveredHorizontal)
|
||||||
|
luaunit.assertEquals(container._scrollbarHoveredVertical, false)
|
||||||
|
luaunit.assertEquals(container._scrollbarHoveredHorizontal, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 5: Scrollbar dimensions calculation
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testScrollbarDimensionsCalculation()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
positioning = Positioning.FLEX,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add child that overflows
|
||||||
|
local child = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 300,
|
||||||
|
height = 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Detect overflow
|
||||||
|
container:_detectOverflow()
|
||||||
|
|
||||||
|
-- Calculate scrollbar dimensions
|
||||||
|
local dims = container:_calculateScrollbarDimensions()
|
||||||
|
|
||||||
|
-- Verify dimensions structure
|
||||||
|
luaunit.assertNotNil(dims.vertical)
|
||||||
|
luaunit.assertNotNil(dims.horizontal)
|
||||||
|
luaunit.assertNotNil(dims.vertical.visible)
|
||||||
|
luaunit.assertNotNil(dims.horizontal.visible)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 6: Scroll position management
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testScrollPositionSetAndGet()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
positioning = Positioning.FLEX,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add child that overflows
|
||||||
|
local child = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 300,
|
||||||
|
height = 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Detect overflow to set max scroll
|
||||||
|
container:_detectOverflow()
|
||||||
|
|
||||||
|
-- Set scroll position
|
||||||
|
container:setScrollPosition(50, 100)
|
||||||
|
|
||||||
|
-- Get scroll position
|
||||||
|
local scrollX, scrollY = container:getScrollPosition()
|
||||||
|
luaunit.assertEquals(scrollX, 50)
|
||||||
|
luaunit.assertEquals(scrollY, 100)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestScrollbarFeatures:testScrollPositionClamping()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
positioning = Positioning.FLEX,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add child that overflows
|
||||||
|
local child = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 300,
|
||||||
|
height = 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Detect overflow to set max scroll
|
||||||
|
container:_detectOverflow()
|
||||||
|
|
||||||
|
-- Try to set scroll position beyond max
|
||||||
|
container:setScrollPosition(1000, 1000)
|
||||||
|
|
||||||
|
-- Get scroll position - should be clamped to max
|
||||||
|
local scrollX, scrollY = container:getScrollPosition()
|
||||||
|
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||||
|
luaunit.assertEquals(scrollX, maxScrollX)
|
||||||
|
luaunit.assertEquals(scrollY, maxScrollY)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 7: Scroll by delta
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testScrollByDelta()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
positioning = Positioning.FLEX,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add child that overflows
|
||||||
|
local child = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 300,
|
||||||
|
height = 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Detect overflow
|
||||||
|
container:_detectOverflow()
|
||||||
|
|
||||||
|
-- Initial scroll position
|
||||||
|
container:setScrollPosition(50, 50)
|
||||||
|
|
||||||
|
-- Scroll by delta
|
||||||
|
container:scrollBy(10, 20)
|
||||||
|
|
||||||
|
-- Verify new position
|
||||||
|
local scrollX, scrollY = container:getScrollPosition()
|
||||||
|
luaunit.assertEquals(scrollX, 60)
|
||||||
|
luaunit.assertEquals(scrollY, 70)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 8: Scroll to top/bottom/left/right
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testScrollToTop()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
positioning = Positioning.FLEX,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add child that overflows
|
||||||
|
local child = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 300,
|
||||||
|
height = 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Detect overflow
|
||||||
|
container:_detectOverflow()
|
||||||
|
|
||||||
|
-- Set initial scroll position
|
||||||
|
container:setScrollPosition(50, 50)
|
||||||
|
|
||||||
|
-- Scroll to top
|
||||||
|
container:scrollToTop()
|
||||||
|
|
||||||
|
-- Verify position
|
||||||
|
local scrollX, scrollY = container:getScrollPosition()
|
||||||
|
luaunit.assertEquals(scrollY, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestScrollbarFeatures:testScrollToBottom()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
positioning = Positioning.FLEX,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add child that overflows
|
||||||
|
local child = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 300,
|
||||||
|
height = 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Detect overflow
|
||||||
|
container:_detectOverflow()
|
||||||
|
|
||||||
|
-- Scroll to bottom
|
||||||
|
container:scrollToBottom()
|
||||||
|
|
||||||
|
-- Verify position
|
||||||
|
local scrollX, scrollY = container:getScrollPosition()
|
||||||
|
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||||
|
luaunit.assertEquals(scrollY, maxScrollY)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestScrollbarFeatures:testScrollToLeft()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
positioning = Positioning.FLEX,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add child that overflows
|
||||||
|
local child = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 300,
|
||||||
|
height = 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Detect overflow
|
||||||
|
container:_detectOverflow()
|
||||||
|
|
||||||
|
-- Set initial scroll position
|
||||||
|
container:setScrollPosition(50, 50)
|
||||||
|
|
||||||
|
-- Scroll to left
|
||||||
|
container:scrollToLeft()
|
||||||
|
|
||||||
|
-- Verify position
|
||||||
|
local scrollX, scrollY = container:getScrollPosition()
|
||||||
|
luaunit.assertEquals(scrollX, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestScrollbarFeatures:testScrollToRight()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
positioning = Positioning.FLEX,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add child that overflows
|
||||||
|
local child = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 300,
|
||||||
|
height = 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Detect overflow
|
||||||
|
container:_detectOverflow()
|
||||||
|
|
||||||
|
-- Scroll to right
|
||||||
|
container:scrollToRight()
|
||||||
|
|
||||||
|
-- Verify position
|
||||||
|
local scrollX, scrollY = container:getScrollPosition()
|
||||||
|
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||||
|
luaunit.assertEquals(scrollX, maxScrollX)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 9: Get scroll percentage
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testGetScrollPercentage()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
positioning = Positioning.FLEX,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add child that overflows
|
||||||
|
local child = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 300,
|
||||||
|
height = 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Detect overflow
|
||||||
|
container:_detectOverflow()
|
||||||
|
|
||||||
|
-- Scroll to middle
|
||||||
|
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||||
|
container:setScrollPosition(maxScrollX / 2, maxScrollY / 2)
|
||||||
|
|
||||||
|
-- Get scroll percentage
|
||||||
|
local percentX, percentY = container:getScrollPercentage()
|
||||||
|
luaunit.assertAlmostEquals(percentX, 0.5, 0.01)
|
||||||
|
luaunit.assertAlmostEquals(percentY, 0.5, 0.01)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 10: Has overflow detection
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testHasOverflow()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
positioning = Positioning.FLEX,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add child that overflows vertically
|
||||||
|
local child = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 150,
|
||||||
|
height = 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Detect overflow
|
||||||
|
container:_detectOverflow()
|
||||||
|
|
||||||
|
-- Check overflow
|
||||||
|
local hasOverflowX, hasOverflowY = container:hasOverflow()
|
||||||
|
luaunit.assertEquals(hasOverflowX, false)
|
||||||
|
luaunit.assertEquals(hasOverflowY, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 11: Get content size
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testGetContentSize()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
positioning = Positioning.FLEX,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add child with specific size
|
||||||
|
local child = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 300,
|
||||||
|
height = 400,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Detect overflow
|
||||||
|
container:_detectOverflow()
|
||||||
|
|
||||||
|
-- Get content size
|
||||||
|
local contentWidth, contentHeight = container:getContentSize()
|
||||||
|
luaunit.assertEquals(contentWidth, 300)
|
||||||
|
luaunit.assertEquals(contentHeight, 400)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 12: Scrollbar configuration options
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testScrollbarConfigurationOptions()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
scrollbarWidth = 20,
|
||||||
|
scrollbarRadius = 10,
|
||||||
|
scrollbarPadding = 5,
|
||||||
|
scrollSpeed = 30,
|
||||||
|
scrollbarColor = Color.new(1, 0, 0, 1),
|
||||||
|
scrollbarTrackColor = Color.new(0, 1, 0, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Verify custom configuration
|
||||||
|
luaunit.assertEquals(container.scrollbarWidth, 20)
|
||||||
|
luaunit.assertEquals(container.scrollbarRadius, 10)
|
||||||
|
luaunit.assertEquals(container.scrollbarPadding, 5)
|
||||||
|
luaunit.assertEquals(container.scrollSpeed, 30)
|
||||||
|
luaunit.assertEquals(container.scrollbarColor.r, 1)
|
||||||
|
luaunit.assertEquals(container.scrollbarTrackColor.g, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- Test 13: Wheel scroll handling
|
||||||
|
-- ========================================
|
||||||
|
function TestScrollbarFeatures:testWheelScrollHandling()
|
||||||
|
local container = Gui.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
overflow = "scroll",
|
||||||
|
positioning = Positioning.FLEX,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add child that overflows
|
||||||
|
local child = Gui.new({
|
||||||
|
parent = container,
|
||||||
|
width = 300,
|
||||||
|
height = 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Detect overflow
|
||||||
|
container:_detectOverflow()
|
||||||
|
|
||||||
|
-- Set initial position away from top so we can scroll up
|
||||||
|
container:setScrollPosition(nil, 50)
|
||||||
|
local initialScrollX, initialScrollY = container:getScrollPosition()
|
||||||
|
|
||||||
|
-- Handle wheel scroll (vertical) - positive y means scroll up
|
||||||
|
local handled = container:_handleWheelScroll(0, 1)
|
||||||
|
|
||||||
|
-- Verify scroll was handled and position changed (scrolled up means lower scroll value)
|
||||||
|
luaunit.assertEquals(handled, true)
|
||||||
|
local scrollX, scrollY = container:getScrollPosition()
|
||||||
|
luaunit.assertTrue(scrollY < initialScrollY, "Expected scroll position to decrease when scrolling up")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Run the tests
|
||||||
|
os.exit(luaunit.LuaUnit.run())
|
||||||
Reference in New Issue
Block a user