91 lines
2.1 KiB
Plaintext
91 lines
2.1 KiB
Plaintext
--!strict
|
|
|
|
--[[
|
|
Constructs a new state object which can listen for updates on another state
|
|
object.
|
|
|
|
FIXME: enabling strict types here causes free types to leak
|
|
]]
|
|
|
|
local PubTypes = require "../PubTypes"
|
|
local Types = require "../Types"
|
|
local External = require "../External"
|
|
|
|
type Set<T> = { [T]: any }
|
|
|
|
local class = {}
|
|
local CLASS_METATABLE = { __index = class }
|
|
|
|
-- Table used to hold Observer objects in memory.
|
|
local strongRefs: Set<Types.Observer> = {}
|
|
|
|
--[[
|
|
Called when the watched state changes value.
|
|
]]
|
|
function class:update(): boolean
|
|
for _, callback in pairs(self._changeListeners) do
|
|
External.doTaskImmediate(callback)
|
|
end
|
|
return false
|
|
end
|
|
|
|
--[[
|
|
Adds a change listener. When the watched state changes value, the listener
|
|
will be fired.
|
|
|
|
Returns a function which, when called, will disconnect the change listener.
|
|
As long as there is at least one active change listener, this Observer
|
|
will be held in memory, preventing GC, so disconnecting is important.
|
|
]]
|
|
function class:onChange(callback: () -> ()): () -> ()
|
|
local uniqueIdentifier = {}
|
|
|
|
self._numChangeListeners += 1
|
|
self._changeListeners[uniqueIdentifier] = callback
|
|
|
|
-- disallow gc (this is important to make sure changes are received)
|
|
strongRefs[self] = true
|
|
|
|
local disconnected = false
|
|
return function()
|
|
if disconnected then
|
|
return
|
|
end
|
|
disconnected = true
|
|
self._changeListeners[uniqueIdentifier] = nil
|
|
self._numChangeListeners -= 1
|
|
|
|
if self._numChangeListeners == 0 then
|
|
-- allow gc if all listeners are disconnected
|
|
strongRefs[self] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Similar to `class:onChange()`, however it runs the provided callback
|
|
immediately.
|
|
]]
|
|
function class:onBind(callback: () -> ()): () -> ()
|
|
External.doTaskImmediate(callback)
|
|
return self:onChange(callback)
|
|
end
|
|
|
|
local function Observer(watchedState: PubTypes.Value<any>): Types.Observer
|
|
local self = setmetatable({
|
|
type = "State",
|
|
kind = "Observer",
|
|
dependencySet = { [watchedState] = true },
|
|
dependentSet = {},
|
|
_changeListeners = {},
|
|
_numChangeListeners = 0,
|
|
}, CLASS_METATABLE)
|
|
|
|
-- add this object to the watched state's dependent set
|
|
watchedState.dependentSet[self] = true
|
|
|
|
return self
|
|
end
|
|
|
|
return Observer
|