better callback(event) system
This commit is contained in:
195
FlexLove.lua
195
FlexLove.lua
@@ -475,6 +475,56 @@ end
|
||||
-- Simple GUI library for LOVE2D
|
||||
-- Provides element and button creation, drawing, and click handling.
|
||||
|
||||
-- ====================
|
||||
-- Event System
|
||||
-- ====================
|
||||
|
||||
---@class InputEvent
|
||||
---@field type "click"|"press"|"release"|"rightclick"|"middleclick"
|
||||
---@field button number -- Mouse button: 1 (left), 2 (right), 3 (middle)
|
||||
---@field x number -- Mouse X position
|
||||
---@field y number -- Mouse Y position
|
||||
---@field modifiers {shift:boolean, ctrl:boolean, alt:boolean, cmd:boolean}
|
||||
---@field clickCount number -- Number of clicks (for double/triple click detection)
|
||||
---@field timestamp number -- Time when event occurred
|
||||
local InputEvent = {}
|
||||
InputEvent.__index = InputEvent
|
||||
|
||||
---@class InputEventProps
|
||||
---@field type "click"|"press"|"release"|"rightclick"|"middleclick"
|
||||
---@field button number
|
||||
---@field x number
|
||||
---@field y number
|
||||
---@field modifiers {shift:boolean, ctrl:boolean, alt:boolean, cmd:boolean}
|
||||
---@field clickCount number?
|
||||
---@field timestamp number?
|
||||
|
||||
--- Create a new input event
|
||||
---@param props InputEventProps
|
||||
---@return InputEvent
|
||||
function InputEvent.new(props)
|
||||
local self = setmetatable({}, InputEvent)
|
||||
self.type = props.type
|
||||
self.button = props.button
|
||||
self.x = props.x
|
||||
self.y = props.y
|
||||
self.modifiers = props.modifiers
|
||||
self.clickCount = props.clickCount or 1
|
||||
self.timestamp = props.timestamp or love.timer.getTime()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get current keyboard modifiers state
|
||||
---@return {shift:boolean, ctrl:boolean, alt:boolean, cmd:boolean}
|
||||
local function getModifiers()
|
||||
return {
|
||||
shift = love.keyboard.isDown("lshift", "rshift"),
|
||||
ctrl = love.keyboard.isDown("lctrl", "rctrl"),
|
||||
alt = love.keyboard.isDown("lalt", "ralt"),
|
||||
cmd = love.keyboard.isDown("lgui", "rgui") -- Mac Command key
|
||||
}
|
||||
end
|
||||
|
||||
---@class Animation
|
||||
---@field duration number
|
||||
---@field start {width?:number, height?:number, opacity?:number}
|
||||
@@ -685,8 +735,13 @@ end
|
||||
---@field autoScaleText boolean -- Whether text should auto-scale with window size (default: true)
|
||||
---@field transform TransformProps -- Transform properties for animations and styling
|
||||
---@field transition TransitionProps -- Transition settings for animations
|
||||
---@field callback function? -- Callback function for click events
|
||||
---@field callback fun(element:Element, event:InputEvent)? -- Callback function for interaction events
|
||||
---@field units table -- Original unit specifications for responsive behavior
|
||||
---@field _pressed table<number, boolean> -- Track pressed state per mouse button
|
||||
---@field _lastClickTime number? -- Timestamp of last click for double-click detection
|
||||
---@field _lastClickButton number? -- Button of last click
|
||||
---@field _clickCount number -- Current click count for multi-click detection
|
||||
---@field _touchPressed table<any, boolean> -- Track touch pressed state
|
||||
---@field gridRows number? -- Number of rows in the grid
|
||||
---@field gridColumns number? -- Number of columns in the grid
|
||||
---@field columnGap number|string? -- Gap between grid columns
|
||||
@@ -727,7 +782,7 @@ Element.__index = Element
|
||||
---@field flexWrap FlexWrap? -- Whether children wrap to multiple lines (default: NOWRAP)
|
||||
---@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)
|
||||
---@field callback function? -- Callback function for click events
|
||||
---@field callback fun(element:Element, event:InputEvent)? -- Callback function for interaction events
|
||||
---@field transform table? -- Transform properties for animations and styling
|
||||
---@field transition table? -- Transition settings for animations
|
||||
---@field gridRows number? -- Number of rows in the grid (default: 1)
|
||||
@@ -744,6 +799,13 @@ function Element.new(props)
|
||||
self.callback = props.callback
|
||||
self.id = props.id or ""
|
||||
|
||||
-- Initialize click tracking for event system
|
||||
self._pressed = {} -- Track pressed state per mouse button
|
||||
self._lastClickTime = nil
|
||||
self._lastClickButton = nil
|
||||
self._clickCount = 0
|
||||
self._touchPressed = {}
|
||||
|
||||
-- Set parent first so it's available for size calculations
|
||||
self.parent = props.parent
|
||||
|
||||
@@ -1788,15 +1850,25 @@ function Element:draw()
|
||||
end
|
||||
|
||||
-- Draw visual feedback when element is pressed (if it has a callback)
|
||||
if self.callback and self._pressed 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(
|
||||
"fill",
|
||||
self.x,
|
||||
self.y,
|
||||
self.width + self.padding.left + self.padding.right,
|
||||
self.height + self.padding.top + self.padding.bottom
|
||||
)
|
||||
if self.callback then
|
||||
-- Check if any button is pressed
|
||||
local anyPressed = false
|
||||
for _, pressed in pairs(self._pressed) do
|
||||
if pressed then
|
||||
anyPressed = true
|
||||
break
|
||||
end
|
||||
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(
|
||||
"fill",
|
||||
self.x,
|
||||
self.y,
|
||||
self.width + self.padding.left + self.padding.right,
|
||||
self.height + self.padding.top + self.padding.bottom
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort children by z-index before drawing
|
||||
@@ -1838,7 +1910,7 @@ function Element:update(dt)
|
||||
end
|
||||
end
|
||||
|
||||
-- Handle click detection for element
|
||||
-- Handle click detection for element with enhanced event system
|
||||
if self.callback then
|
||||
local mx, my = love.mouse.getPosition()
|
||||
-- Clickable area is the border box (x, y already includes padding)
|
||||
@@ -1846,25 +1918,104 @@ function Element:update(dt)
|
||||
local by = self.y
|
||||
local bw = self.width + self.padding.left + self.padding.right
|
||||
local bh = self.height + self.padding.top + self.padding.bottom
|
||||
if mx >= bx and mx <= bx + bw and my >= by and my <= by + bh then
|
||||
if love.mouse.isDown(1) then
|
||||
-- set pressed flag
|
||||
self._pressed = true
|
||||
elseif not love.mouse.isDown(1) and self._pressed then
|
||||
self.callback(self)
|
||||
self._pressed = false
|
||||
local isHovering = mx >= bx and mx <= bx + bw and my >= by and my <= by + bh
|
||||
|
||||
-- Check all three mouse buttons
|
||||
local buttons = {1, 2, 3} -- left, right, middle
|
||||
|
||||
for _, button in ipairs(buttons) do
|
||||
if isHovering then
|
||||
if love.mouse.isDown(button) then
|
||||
-- Button is pressed down
|
||||
if not self._pressed[button] then
|
||||
-- Just pressed - fire press event
|
||||
local modifiers = getModifiers()
|
||||
local pressEvent = InputEvent.new({
|
||||
type = "press",
|
||||
button = button,
|
||||
x = mx,
|
||||
y = my,
|
||||
modifiers = modifiers,
|
||||
clickCount = 1,
|
||||
})
|
||||
self.callback(self, pressEvent)
|
||||
self._pressed[button] = true
|
||||
end
|
||||
elseif self._pressed[button] then
|
||||
-- Button was just released - fire click event
|
||||
local currentTime = love.timer.getTime()
|
||||
local modifiers = getModifiers()
|
||||
|
||||
-- Determine click count (double-click detection)
|
||||
local clickCount = 1
|
||||
local doubleClickThreshold = 0.3 -- 300ms for double-click
|
||||
|
||||
if self._lastClickTime
|
||||
and self._lastClickButton == button
|
||||
and (currentTime - self._lastClickTime) < doubleClickThreshold then
|
||||
clickCount = self._clickCount + 1
|
||||
else
|
||||
clickCount = 1
|
||||
end
|
||||
|
||||
self._clickCount = clickCount
|
||||
self._lastClickTime = currentTime
|
||||
self._lastClickButton = button
|
||||
|
||||
-- Determine event type based on button
|
||||
local eventType = "click"
|
||||
if button == 2 then
|
||||
eventType = "rightclick"
|
||||
elseif button == 3 then
|
||||
eventType = "middleclick"
|
||||
end
|
||||
|
||||
local clickEvent = InputEvent.new({
|
||||
type = eventType,
|
||||
button = button,
|
||||
x = mx,
|
||||
y = my,
|
||||
modifiers = modifiers,
|
||||
clickCount = clickCount,
|
||||
})
|
||||
|
||||
self.callback(self, clickEvent)
|
||||
self._pressed[button] = false
|
||||
|
||||
-- Fire release event
|
||||
local releaseEvent = InputEvent.new({
|
||||
type = "release",
|
||||
button = button,
|
||||
x = mx,
|
||||
y = my,
|
||||
modifiers = modifiers,
|
||||
clickCount = clickCount,
|
||||
})
|
||||
self.callback(self, releaseEvent)
|
||||
end
|
||||
else
|
||||
-- Mouse left the element - reset pressed state
|
||||
self._pressed[button] = false
|
||||
end
|
||||
else
|
||||
self._pressed = false
|
||||
end
|
||||
|
||||
-- Handle touch events (maintain backward compatibility)
|
||||
local touches = love.touch.getTouches()
|
||||
for _, id in ipairs(touches) do
|
||||
local tx, ty = love.touch.getPosition(id)
|
||||
if tx >= bx and tx <= bx + bw and ty >= by and ty <= by + bh then
|
||||
self._touchPressed[id] = true
|
||||
elseif self._touchPressed[id] then
|
||||
self.callback(self)
|
||||
-- Create touch event (treat as left click)
|
||||
local touchEvent = InputEvent.new({
|
||||
type = "click",
|
||||
button = 1,
|
||||
x = tx,
|
||||
y = ty,
|
||||
modifiers = getModifiers(),
|
||||
clickCount = 1,
|
||||
})
|
||||
self.callback(self, touchEvent)
|
||||
self._touchPressed[id] = false
|
||||
end
|
||||
end
|
||||
|
||||
171
examples/EventSystemDemo.lua
Normal file
171
examples/EventSystemDemo.lua
Normal file
@@ -0,0 +1,171 @@
|
||||
local FlexLove = require("FlexLove")
|
||||
local Gui = FlexLove.GUI
|
||||
local Color = FlexLove.Color
|
||||
|
||||
---@class EventSystemDemo
|
||||
---@field window Element
|
||||
---@field eventLog table<integer, string>
|
||||
---@field logDisplay Element
|
||||
local EventSystemDemo = {}
|
||||
EventSystemDemo.__index = EventSystemDemo
|
||||
|
||||
function EventSystemDemo.init()
|
||||
local self = setmetatable({}, EventSystemDemo)
|
||||
self.eventLog = {}
|
||||
|
||||
-- Create main demo window
|
||||
self.window = Gui.new({
|
||||
x = 50,
|
||||
y = 50,
|
||||
width = 700,
|
||||
height = 500,
|
||||
background = Color.new(0.15, 0.15, 0.2, 0.95),
|
||||
border = { top = true, bottom = true, left = true, right = true },
|
||||
borderColor = Color.new(0.8, 0.8, 0.8, 1),
|
||||
positioning = "flex",
|
||||
flexDirection = "vertical",
|
||||
gap = 20,
|
||||
padding = { top = 20, right = 20, bottom = 20, left = 20 },
|
||||
})
|
||||
|
||||
-- Title
|
||||
local title = Gui.new({
|
||||
parent = self.window,
|
||||
height = 40,
|
||||
text = "Event System Demo - Try different clicks and modifiers!",
|
||||
textSize = 18,
|
||||
textAlign = "center",
|
||||
textColor = Color.new(1, 1, 1, 1),
|
||||
background = Color.new(0.2, 0.2, 0.3, 1),
|
||||
})
|
||||
|
||||
-- Button container
|
||||
local buttonContainer = Gui.new({
|
||||
parent = self.window,
|
||||
height = 200,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal",
|
||||
gap = 15,
|
||||
background = Color.new(0.1, 0.1, 0.15, 0.5),
|
||||
padding = { top = 15, right = 15, bottom = 15, left = 15 },
|
||||
})
|
||||
|
||||
-- Helper function to add event to log
|
||||
local function logEvent(message)
|
||||
table.insert(self.eventLog, 1, message) -- Add to beginning
|
||||
if #self.eventLog > 10 then
|
||||
table.remove(self.eventLog) -- Keep only last 10 events
|
||||
end
|
||||
self:updateLogDisplay()
|
||||
end
|
||||
|
||||
-- Left Click Button
|
||||
local leftClickBtn = Gui.new({
|
||||
parent = buttonContainer,
|
||||
width = 150,
|
||||
height = 80,
|
||||
text = "Left Click Me",
|
||||
textAlign = "center",
|
||||
textColor = Color.new(1, 1, 1, 1),
|
||||
background = Color.new(0.2, 0.6, 0.9, 0.8),
|
||||
border = { top = true, bottom = true, left = true, right = true },
|
||||
borderColor = Color.new(0.4, 0.8, 1, 1),
|
||||
callback = function(element, event)
|
||||
local msg = string.format("[%s] Button: %d, Clicks: %d",
|
||||
event.type, event.button, event.clickCount)
|
||||
logEvent(msg)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Right Click Button
|
||||
local rightClickBtn = Gui.new({
|
||||
parent = buttonContainer,
|
||||
width = 150,
|
||||
height = 80,
|
||||
text = "Right Click Me",
|
||||
textAlign = "center",
|
||||
textColor = Color.new(1, 1, 1, 1),
|
||||
background = Color.new(0.9, 0.4, 0.4, 0.8),
|
||||
border = { top = true, bottom = true, left = true, right = true },
|
||||
borderColor = Color.new(1, 0.6, 0.6, 1),
|
||||
callback = function(element, event)
|
||||
if event.type == "rightclick" then
|
||||
logEvent("RIGHT CLICK detected!")
|
||||
elseif event.type == "click" then
|
||||
logEvent("Left click (try right click!)")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Modifier Button
|
||||
local modifierBtn = Gui.new({
|
||||
parent = buttonContainer,
|
||||
width = 150,
|
||||
height = 80,
|
||||
text = "Try Shift/Ctrl",
|
||||
textAlign = "center",
|
||||
textColor = Color.new(1, 1, 1, 1),
|
||||
background = Color.new(0.6, 0.9, 0.4, 0.8),
|
||||
border = { top = true, bottom = true, left = true, right = true },
|
||||
borderColor = Color.new(0.8, 1, 0.6, 1),
|
||||
callback = function(element, event)
|
||||
if event.type == "click" then
|
||||
local mods = {}
|
||||
if event.modifiers.shift then table.insert(mods, "SHIFT") end
|
||||
if event.modifiers.ctrl then table.insert(mods, "CTRL") end
|
||||
if event.modifiers.alt then table.insert(mods, "ALT") end
|
||||
if event.modifiers.cmd then table.insert(mods, "CMD") end
|
||||
|
||||
if #mods > 0 then
|
||||
logEvent("Modifiers: " .. table.concat(mods, "+"))
|
||||
else
|
||||
logEvent("No modifiers (try holding Shift/Ctrl)")
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Multi-Event Button (shows all event types)
|
||||
local multiEventBtn = Gui.new({
|
||||
parent = buttonContainer,
|
||||
width = 150,
|
||||
height = 80,
|
||||
text = "All Events",
|
||||
textAlign = "center",
|
||||
textColor = Color.new(1, 1, 1, 1),
|
||||
background = Color.new(0.9, 0.7, 0.3, 0.8),
|
||||
border = { top = true, bottom = true, left = true, right = true },
|
||||
borderColor = Color.new(1, 0.9, 0.5, 1),
|
||||
callback = function(element, event)
|
||||
local msg = string.format("[%s] Btn:%d at (%d,%d)",
|
||||
event.type, event.button, math.floor(event.x), math.floor(event.y))
|
||||
logEvent(msg)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Event log display area
|
||||
self.logDisplay = Gui.new({
|
||||
parent = self.window,
|
||||
height = 200,
|
||||
text = "Event log will appear here...",
|
||||
textSize = 14,
|
||||
textAlign = "start",
|
||||
textColor = Color.new(0.9, 0.9, 1, 1),
|
||||
background = Color.new(0.05, 0.05, 0.1, 1),
|
||||
border = { top = true, bottom = true, left = true, right = true },
|
||||
borderColor = Color.new(0.3, 0.3, 0.4, 1),
|
||||
padding = { top = 10, right = 10, bottom = 10, left = 10 },
|
||||
})
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function EventSystemDemo:updateLogDisplay()
|
||||
if #self.eventLog == 0 then
|
||||
self.logDisplay.text = "Event log will appear here..."
|
||||
else
|
||||
self.logDisplay.text = table.concat(self.eventLog, "\n")
|
||||
end
|
||||
end
|
||||
|
||||
return EventSystemDemo.init()
|
||||
347
testing/__tests__/16_event_system_tests.lua
Normal file
347
testing/__tests__/16_event_system_tests.lua
Normal file
@@ -0,0 +1,347 @@
|
||||
-- Event System Tests
|
||||
-- Tests for the enhanced callback system with InputEvent objects
|
||||
|
||||
package.path = package.path .. ";?.lua"
|
||||
|
||||
local lu = require("testing/luaunit")
|
||||
require("testing/loveStub") -- Required to mock LOVE functions
|
||||
local FlexLove = require("FlexLove")
|
||||
|
||||
local Gui = FlexLove.GUI
|
||||
local Color = FlexLove.Color
|
||||
|
||||
TestEventSystem = {}
|
||||
|
||||
function TestEventSystem:setUp()
|
||||
-- Initialize GUI before each test
|
||||
Gui.init({ baseScale = { width = 1920, height = 1080 } })
|
||||
love.window.setMode(1920, 1080)
|
||||
end
|
||||
|
||||
function TestEventSystem:tearDown()
|
||||
-- Clean up after each test
|
||||
Gui.destroy()
|
||||
end
|
||||
|
||||
-- Test 1: Event object structure
|
||||
function TestEventSystem:test_event_object_has_required_fields()
|
||||
local eventReceived = nil
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
eventReceived = event
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate mouse press and release
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
-- Verify event object structure
|
||||
lu.assertNotNil(eventReceived, "Event should be received")
|
||||
lu.assertNotNil(eventReceived.type, "Event should have type field")
|
||||
lu.assertNotNil(eventReceived.button, "Event should have button field")
|
||||
lu.assertNotNil(eventReceived.x, "Event should have x field")
|
||||
lu.assertNotNil(eventReceived.y, "Event should have y field")
|
||||
lu.assertNotNil(eventReceived.modifiers, "Event should have modifiers field")
|
||||
lu.assertNotNil(eventReceived.clickCount, "Event should have clickCount field")
|
||||
lu.assertNotNil(eventReceived.timestamp, "Event should have timestamp field")
|
||||
end
|
||||
|
||||
-- Test 2: Left click event
|
||||
function TestEventSystem:test_left_click_generates_click_event()
|
||||
local eventsReceived = {}
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
table.insert(eventsReceived, {type = event.type, button = event.button})
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate left click
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
-- Should receive press, click, and release events
|
||||
lu.assertTrue(#eventsReceived >= 2, "Should receive at least 2 events")
|
||||
|
||||
-- Check for click event
|
||||
local hasClickEvent = false
|
||||
for _, evt in ipairs(eventsReceived) do
|
||||
if evt.type == "click" and evt.button == 1 then
|
||||
hasClickEvent = true
|
||||
break
|
||||
end
|
||||
end
|
||||
lu.assertTrue(hasClickEvent, "Should receive click event for left button")
|
||||
end
|
||||
|
||||
-- Test 3: Right click event
|
||||
function TestEventSystem:test_right_click_generates_rightclick_event()
|
||||
local eventsReceived = {}
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
table.insert(eventsReceived, {type = event.type, button = event.button})
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate right click
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(2, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(2, false)
|
||||
button:update(0.016)
|
||||
|
||||
-- Check for rightclick event
|
||||
local hasRightClickEvent = false
|
||||
for _, evt in ipairs(eventsReceived) do
|
||||
if evt.type == "rightclick" and evt.button == 2 then
|
||||
hasRightClickEvent = true
|
||||
break
|
||||
end
|
||||
end
|
||||
lu.assertTrue(hasRightClickEvent, "Should receive rightclick event for right button")
|
||||
end
|
||||
|
||||
-- Test 4: Middle click event
|
||||
function TestEventSystem:test_middle_click_generates_middleclick_event()
|
||||
local eventsReceived = {}
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
table.insert(eventsReceived, {type = event.type, button = event.button})
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate middle click
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(3, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(3, false)
|
||||
button:update(0.016)
|
||||
|
||||
-- Check for middleclick event
|
||||
local hasMiddleClickEvent = false
|
||||
for _, evt in ipairs(eventsReceived) do
|
||||
if evt.type == "middleclick" and evt.button == 3 then
|
||||
hasMiddleClickEvent = true
|
||||
break
|
||||
end
|
||||
end
|
||||
lu.assertTrue(hasMiddleClickEvent, "Should receive middleclick event for middle button")
|
||||
end
|
||||
|
||||
-- Test 5: Modifier keys detection
|
||||
function TestEventSystem:test_modifier_keys_are_detected()
|
||||
local eventReceived = nil
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
if event.type == "click" then
|
||||
eventReceived = event
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate shift + click
|
||||
love.keyboard.setDown("lshift", true)
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
lu.assertNotNil(eventReceived, "Should receive click event")
|
||||
lu.assertTrue(eventReceived.modifiers.shift, "Shift modifier should be detected")
|
||||
end
|
||||
|
||||
-- Test 6: Double click detection
|
||||
function TestEventSystem:test_double_click_increments_click_count()
|
||||
local clickEvents = {}
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
if event.type == "click" then
|
||||
table.insert(clickEvents, event.clickCount)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate first click
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
-- Simulate second click quickly (double-click)
|
||||
love.timer.setTime(love.timer.getTime() + 0.1) -- 100ms later
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
lu.assertEquals(#clickEvents, 2, "Should receive 2 click events")
|
||||
lu.assertEquals(clickEvents[1], 1, "First click should have clickCount = 1")
|
||||
lu.assertEquals(clickEvents[2], 2, "Second click should have clickCount = 2")
|
||||
end
|
||||
|
||||
-- Test 7: Press and release events
|
||||
function TestEventSystem:test_press_and_release_events_are_fired()
|
||||
local eventsReceived = {}
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
table.insert(eventsReceived, event.type)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate click
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
-- Should receive press, click, and release
|
||||
lu.assertTrue(#eventsReceived >= 2, "Should receive multiple events")
|
||||
|
||||
local hasPress = false
|
||||
local hasRelease = false
|
||||
for _, eventType in ipairs(eventsReceived) do
|
||||
if eventType == "press" then hasPress = true end
|
||||
if eventType == "release" then hasRelease = true end
|
||||
end
|
||||
|
||||
lu.assertTrue(hasPress, "Should receive press event")
|
||||
lu.assertTrue(hasRelease, "Should receive release event")
|
||||
end
|
||||
|
||||
-- Test 8: Mouse position in event
|
||||
function TestEventSystem:test_event_contains_mouse_position()
|
||||
local eventReceived = nil
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
if event.type == "click" then
|
||||
eventReceived = event
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate click at specific position
|
||||
local mouseX, mouseY = 175, 125
|
||||
love.mouse.setPosition(mouseX, mouseY)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
lu.assertNotNil(eventReceived, "Should receive click event")
|
||||
lu.assertEquals(eventReceived.x, mouseX, "Event should contain correct mouse X position")
|
||||
lu.assertEquals(eventReceived.y, mouseY, "Event should contain correct mouse Y position")
|
||||
end
|
||||
|
||||
-- Test 9: No callback when mouse outside element
|
||||
function TestEventSystem:test_no_callback_when_clicking_outside_element()
|
||||
local callbackCalled = false
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
callbackCalled = true
|
||||
end,
|
||||
})
|
||||
|
||||
-- Click outside element
|
||||
love.mouse.setPosition(50, 50)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
lu.assertFalse(callbackCalled, "Callback should not be called when clicking outside element")
|
||||
end
|
||||
|
||||
-- Test 10: Multiple modifiers
|
||||
function TestEventSystem:test_multiple_modifiers_detected()
|
||||
local eventReceived = nil
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
if event.type == "click" then
|
||||
eventReceived = event
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate shift + ctrl + click
|
||||
love.keyboard.setDown("lshift", true)
|
||||
love.keyboard.setDown("lctrl", true)
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
lu.assertNotNil(eventReceived, "Should receive click event")
|
||||
lu.assertTrue(eventReceived.modifiers.shift, "Shift modifier should be detected")
|
||||
lu.assertTrue(eventReceived.modifiers.ctrl, "Ctrl modifier should be detected")
|
||||
end
|
||||
|
||||
return TestEventSystem
|
||||
@@ -19,6 +19,7 @@ local testFiles = {
|
||||
"testing/__tests__/13_relative_positioning_tests.lua",
|
||||
"testing/__tests__/14_text_scaling_basic_tests.lua",
|
||||
"testing/__tests__/15_grid_layout_tests.lua",
|
||||
"testing/__tests__/16_event_system_tests.lua",
|
||||
}
|
||||
|
||||
-- testingun all tests, but don't exit on error
|
||||
|
||||
Reference in New Issue
Block a user