helium/core/input.lua
Elmārs Āboliņš a200960ad1 big chungus commit
2020-09-28 00:16:50 +03:00

322 lines
7.1 KiB
Lua

local path = string.sub(..., 1, string.len(...) - string.len(".core.input"))
local stack = require(path .. ".core.stack")
local input = {
eventHandlers = {},
subscriptions = {},
activeEvents = {}
}
input.__index = input
local function sortFunc(t1, t2)
if t1.stack.temporalZ.z == t2.stack.temporalZ.z then
print('same Z ???',t1.stack.temporalZ.z, t2.stack.temporalZ.z)
end
if t1 == t2 then
print(tostring(t1), tostring(t2))
return false
end
return t1.stack.temporalZ.z > t2.stack.temporalZ.z
end
function input.sortZ()
for i, subs in pairs(input.subscriptions) do
table.sort(subs, sortFunc)
print(#subs)
end
end
local dummyfunc = function() end
---@class subscription
local subscription = {}
subscription.__index = subscription
function input.unload()
input.subscriptions = {}
input.activeEvents = {}
end
--[[Event types
###SIMPLE EVENTS###
mousepressed,--press started
mousereleased,--press released after an event inside started
mousepressed_outside --mousepressed outside of the subscription
mousereleased_outside --mousereleased outside of the subscription
keypressed,--key pressed
keyreleased,--key released
###COMPLEX EVENTS###
dragged,
clicked,
hover,
]]
---@param x number
---@param y number
---@param w number
---@param h number
---@param eventType string
---@param callback function
---@param doff boolean
---@return subscription
function subscription.create(x, y, w, h, eventType, callback, doff)
local stack = stack.getContext()
local xn, yn = stack:normalizePos(x, y)
local wn, hn = stack:normalizeSize(w, h)
local sub = setmetatable({
x = xn,
y = yn,
w = wn,
h = hn,
origX = x,
origY = y,
origW = w,
origH = h,
eventType = eventType,
active = true,
stack = stack,
callback = callback
},subscription)
sub.onSizeChange = stack:onSizeChange(function()
sub.w, sub.h = sub.stack:normalizeSize(sub.origW, sub.origH)
end)
sub.onPosChange = stack:onPosChange(function()
sub.x, sub.y = sub.stack:normalizePos(sub.origX, sub.origY)
print(sub.y, sub.stack.absY)
end)
if doff == false then
sub:off()
end
if not input.subscriptions[eventType] then
input.subscriptions[eventType] = {}
end
table.insert(input.subscriptions[eventType],1,sub)
input.sortZ()
return sub
end
function subscription:off()
self.active = false
end
function subscription:on()
self.active = true
end
function subscription:remove()
--input.subscriptions[self.eventType][self] = nil
input.sortZ()
end
function subscription:emit(...)
return self.callback(...)
end
function subscription:checkInside(x, y)
return x>self.x and x<self.x+self.w and y>self.y and y<self.y+self.h
end
function subscription:checkOutside(x, y)
return not (x>self.x and x<self.x+self.w and y>self.y and y<self.y+self.h)
end
input.subscribe = subscription.create
---@param eventType string
---@param callback function
---@param cbOff boolean
---@param x number
---@param y number
---@param w number
---@param h number
input.__call = function(self, eventType, callback, cbOff, x, y, w, h)
x = x or 0
y = y or 0
w = w or 1
h = h or 1
return subscription.create(x,y,w,h,eventType,callback,cbOff)
end
--Since the introduction of the relative subscriptions, there is more utility in ommiting coordinates by default
setmetatable(input, input)
function input.eventHandlers.mousereleased(x, y, btn)
local captured = false
if input.subscriptions.clicked then
for index, sub in ipairs(input.subscriptions.clicked) do
if sub.currentEvent then
sub.currentEvent = false
captured = true
if sub.cleanUp then
sub.cleanUp(x, y, btn)
end
end
end
end
if input.subscriptions.dragged then
for index, sub in ipairs(input.subscriptions.dragged) do
if sub.currentEvent then
sub.currentEvent = false
captured = true
if sub.cleanUp then
sub.cleanUp(x, y)
end
end
end
end
if input.subscriptions.mousereleased then
for index, sub in ipairs(input.subscriptions.mousereleased) do
if sub.active and sub:checkInside(x, y) then
sub:emit(x, y, btn)
captured = true
end
end
end
if input.subscriptions.mousereleased_outside then
for index, sub in ipairs(input.subscriptions.mousereleased_outside) do
if sub.active and sub:checkOutside(x, y) then
sub:emit(x, y, btn)
captured = true
end
end
end
return captured
end
function input.eventHandlers.mousepressed(x, y, btn)
local captured = false
if input.subscriptions.clicked then
for index, sub in ipairs(input.subscriptions.clicked) do
local succ = sub:checkInside(x, y)
if succ and sub.active then
sub.cleanUp = sub:emit(x, y, btn) or dummyfunc
sub.currentEvent = true
return true
end
end
end
if input.subscriptions.dragged then
for index, sub in ipairs(input.subscriptions.dragged) do
if sub.active and sub:checkInside(x, y) then
sub.currentEvent = true
return true
end
end
end
if input.subscriptions.mousepressed then
for index, sub in ipairs(input.subscriptions.mousepressed) do
if sub.active and sub:checkInside(x, y) then
sub:emit(x, y, btn)
return true
end
end
end
if input.subscriptions.mousepressed_outside then
for index, sub in ipairs(input.subscriptions.mousepressed_outside) do
if sub.active and sub:checkOutside(x, y) then
sub:emit(x, y, btn)
return true
end
end
end
return captured
end
function input.eventHandlers.keypressed(btn)
local captured = false
if input.subscriptions.keypressed then
for index, sub in ipairs(input.subscriptions.keypressed) do
if sub.active then
sub:emit( btn)
captured = true
end
end
end
return captured
end
function input.eventHandlers.keyreleased(btn)
local captured = false
if input.subscriptions.keyreleased then
for index, sub in ipairs(input.subscriptions.keyreleased) do
if sub.active then
sub:emit(btn)
captured = true
end
end
end
return captured
end
function input.eventHandlers.mousemoved(x, y, dx, dy)
local captured = false
if input.subscriptions.hover then
for index, sub in ipairs(input.subscriptions.hover) do
local succ = sub:checkInside(x, y)
if sub.active and not sub.currentEvent and succ then
sub.cleanUp = sub:emit(x, y, dx, dy) or dummyfunc
sub.currentEvent = true
return true
elseif sub.currentEvent and not succ then
sub.currentEvent = false
if sub.cleanUp then
sub.cleanUp(x, y)
end
return true
end
end
end
if input.subscriptions.dragged then
for index, sub in ipairs(input.subscriptions.dragged) do
if sub.active and sub.currentEvent then
if not sub.cleanUp then
sub.cleanUp = sub:emit(x, y, dx, dy) or dummyfunc
else
sub:emit(x, y, dx, dy)
end
return true
end
end
end
return captured
end
--Typescript
input.default = input
return input