Files
BusyRabbit/Content/Lua/Core/Reactive.lua
2025-07-09 01:08:35 +08:00

164 lines
4.3 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local Module = {}
local ReactiveWatcherMeta = {}
local ReactivePropertyMeta = {}
local reactive_watcher_stack = {}
local auto_run_watcher_mapping = {}
local triggered_watchers = {} -- 由SetProperty激活的watcher列表
local function CreateReactiveWatcher(auto_run)
local watcher = rawget(auto_run_watcher_mapping, auto_run)
if watcher == nil then
watcher = {
auto_run = auto_run,
properties = {},
}
rawset(auto_run_watcher_mapping, auto_run, watcher)
setmetatable(watcher, ReactiveWatcherMeta)
end
return watcher
end
local function PushWatcher(watcher)
table.insert(reactive_watcher_stack, watcher)
end
local function PeekWatcher()
local watcher_stack_deepth = #reactive_watcher_stack
if watcher_stack_deepth > 0 then
return reactive_watcher_stack[watcher_stack_deepth]
else
return nil
end
end
local function PopWatcher()
local watcher = PeekWatcher()
if watcher then
table.remove(reactive_watcher_stack)
end
return watcher
end
local function AddPropertyToWatcher(watcher, property, key)
local properties = watcher.properties
local keys = properties[property] or {}
keys[key] = true
properties[property] = keys
end
local function ClearAllPropertiesOfWatcher(watcher)
local properties = watcher.properties
for property, keys in pairs(properties) do
local property_watchers = property.watchers
for key, state in pairs(keys) do
if state == true then
local key_watchers = property_watchers[key]
key_watchers[watcher] = nil
end
end
end
rawset(watcher, "properties", {})
end
local function RunFunctionOfWatcher(watcher)
local auto_run = watcher.auto_run
assert(auto_run ~= nil, "can't find auto run function")
ClearAllPropertiesOfWatcher(watcher) -- 解绑所有的binding由auto_run重新生成
PushWatcher(watcher)
auto_run()
local poped_watcher = PopWatcher()
assert(watcher == poped_watcher, "fatal")
end
local function GetReactiveProperty(property, key)
local meta = property.meta
local property_watchers = property.watchers
local current_watcher = PeekWatcher()
if current_watcher ~= nil then
local key_watchers = property_watchers[key] or {}
key_watchers[current_watcher] = true
property_watchers[key] = key_watchers
AddPropertyToWatcher(current_watcher, property, key)
end
return rawget(meta, key)
end
local function SetReactiveProperty(property, key, value)
local meta = property.meta
local property_watchers = property.watchers
rawset(meta, key, value)
local key_watchers = property_watchers[key] or {}
for watcher, state in pairs(key_watchers) do
if state == true then
triggered_watchers[watcher] = true
end
end
end
ReactiveWatcherMeta.__index = ReactiveWatcherMeta
ReactivePropertyMeta.__index = GetReactiveProperty
ReactivePropertyMeta.__newindex = SetReactiveProperty
-- 销毁Reactive Watcher
function ReactiveWatcherMeta:Destroy()
ClearAllPropertiesOfWatcher(self)
auto_run_watcher_mapping[self.auto_run] = nil
self.auto_run = nil
end
function ReactivePropertyMeta:Destroy()
local watchers = rawget(self, "watchers")
return watchers
end
-- 创建auto_run的watcher, 并运行auto_run函数
function Module.Watcher(auto_run)
local watcher = CreateReactiveWatcher(auto_run)
RunFunctionOfWatcher(watcher)
return watcher
end
function Module.ReactiveProperty(data)
local Property = {
meta = data or {},
watchers = {},
}
return setmetatable(Property, ReactivePropertyMeta)
end
function Module.RawGet(property, key)
local meta = rawget(property, "meta")
return rawget(meta, key)
end
function Module.RawSet(property, key, value)
local meta = rawget(property, "meta")
rawset(meta, key, value)
end
function Module.Process()
local limit = 64
while next(triggered_watchers) and limit > 0 do
local triggered_list = {}
for w, state in pairs(triggered_watchers) do
if state == true then table.insert(triggered_list, w) end
end
for _, w in ipairs(triggered_list) do
limit = limit - 1
triggered_watchers[w] = nil
RunFunctionOfWatcher(w)
end
end
end
return Module