mirror of
https://github.com/Reuh/ubiquitousse.git
synced 2025-10-27 09:09:30 +00:00
184 lines
5.2 KiB
Text
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
|