--- Signal management. -- -- No dependency. -- @module signal --- 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. signals = {}, --- Bind one or several functions to a signal name. bind = :(name, fn, ...) if not @signals[name] then @signals[name] = {} end table.insert(@signals[name], fn) if ... then return @bind(name, ...) end end, --- Unbind one or several functions to a signal name. 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 return @unbind(name, ...) end end, --- Remove every bound function to a signal name. unbindAll = :(name) @signals[name] = nil end, --- Replace a bound function with another function. 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. 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 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