1
0
Fork 0
mirror of https://github.com/Reuh/ubiquitousse.git synced 2025-10-27 09:09:30 +00:00
ubiquitousse/signal/signal.can

184 lines
5.2 KiB
Text

--- Signal management for Lua.
--
-- No dependency.
-- Optional dependency: LÖVE to hook into LÖVE events.
-- @module signal
-- @usage
-- TODO
--- Signal registry.
--
-- A SignalRegistry is a separate ubiquitousse.signal instance: its signals will be independant from other registries.
-- @type SignalRegistry
let registry_mt = {
--- Map of signals to list of listeners.
-- @ftype {["name"]={fn,...}}
signals = {},
--- Bind one or several functions to a signal name.
-- @tparam string name the name of the signal
-- @tparam function fn the function to bind to the signal
-- @tparam function,... ... other function to bind to the signal
bind = :(name, fn, ...)
if not @signals[name] then
@signals[name] = {}
end
table.insert(@signals[name], fn)
if ... then
@bind(name, ...)
end
end,
--- Unbind one or several functions to a signal name.
-- @tparam string name the name of the signal
-- @tparam function fn the function to unbind to the signal
-- @tparam function,... ... other function to unbind to the signal
unbind = :(name, fn, ...)
if not @signals[name] then
return
end
for i=#@signals[name], 1, -1 do
if @signals[name] == fn then
table.remove(@signals[name], i)
end
end
if ... then
@unbind(name, ...)
end
end,
--- Remove every bound function to a signal name.
-- @tparam string name the name of the signal
unbindAll = :(name)
@signals[name] = nil
end,
--- Replace a bound function with another function.
-- @tparam string name the name of the signal
-- @tparam function sourceFn the function currently bound to the signal
-- @tparam function destFn the function that will replace the previous one
replace = :(name, sourceFn, destFn)
if not @signals[name] then
@signals[name] = {}
end
for i, fn in ipairs(@signals[name]) do
if fn == sourceFn then
@signals[name][i] = destFn
break
end
end
end,
--- Remove every bound function to every signal.
clear = :()
@signals = {}
end,
--- Emit a signal, i.e. call every function bound to it, with the given arguments.
-- @tparam string name the name of the signal
-- @param ... arguments to pass to the functions bound to this signal
emit = :(name, ...)
if @signals[name] then
for _, fn in ipairs(@signals[name]) do
fn(...)
end
end
end
}
registry_mt.__index = registry_mt
--- Module.
--
-- This module also acts as a global `SignalRegistry`, so you can call the `:bind`, `:emit`, etc. methods directly on the module
-- if you don't need to isolate your signals in separate registries.
-- @section module
let signal = {
--- Creates and return a new SignalRegistry.
-- @treturn SignalRegistry
new = ()
return setmetatable({ signals = {} }, registry_mt)
end,
-- Global SignalRegistry.
signals = {},
bind = (...)
return registry_mt.bind(signal, ...)
end,
unbind = (...)
return registry_mt.unbind(signal, ...)
end,
unbindAll = (...)
return registry_mt.unbindAll(signal, ...)
end,
replace = (...)
return registry_mt.replace(signal, ...)
end,
clear = (...)
return registry_mt.clear(signal, ...)
end,
emit = (...)
return registry_mt.emit(signal, ...)
end,
--- `SignalRegistry` which will be used to bind signals that need to be called on game engine event; other ubiquitousse modules may bind to this registry
-- if avaible.
--
-- For example, every ubiquitousse module with a "update" function will bind it to the "update" signal in the registry;
-- you can then call this signal on each game update to update every ubiquitousse module easily.
--
-- Provided signals:
--
-- * `update(dt)`, should be called on every game update
-- * `draw()`, should be called on every game draw
-- * for LÖVE, there are callbacks for every LÖVE callback function that need to be called on their corresponding LÖVE callback
-- @ftype SignalRegistry
event = nil,
--- Call this function to hook `signal.event` signals to LÖVE events.
-- This means overriding every existing LÖVE callback. If a callback is already defined, the new one will call the old function along with the signal:emit.
-- @require love
registerEvents = ()
local callbacks = { -- everything except run, errorhandler, threaderror
"displayrotated", "draw", "load", "lowmemory", "quit", "update",
"directorydropped", "filedropped", "focus", "mousefocus", "resize", "visible",
"keypressed", "keyreleased", "textedited", "textinput",
"mousemoved", "mousepressed", "mousereleased", "wheelmoved",
"gamepadaxis", "gamepadpressed", "gamepadreleased",
"joystickadded", "joystickaxis", "joystickhat", "joystickpressed", "joystickreleased", "joystickremoved",
"touchmoved", "touchpressed", "touchreleased"
}
local event = signal.event
for _, callback in ipairs(callbacks) do
if callback == "update" then
if love[callback] then
local old = love[callback]
love[callback] = function(dt)
old(dt)
event:emit(callback, dt)
end
else
love[callback] = function(dt)
event:emit(callback, dt)
end
end
else
if love[callback] then
local old = love[callback]
love[callback] = function(...)
old(...)
event:emit(callback, ...)
end
else
love[callback] = function(...)
event:emit(callback, ...)
end
end
end
end
end
}
signal.event = signal.new()
return signal