class_name R static func state(value: Variant, options: Dictionary={}): if value is Dictionary: return store(value) return RdotState.new(value, options) static func store(value: Dictionary): return RdotStore.new(value) static func computed(computation: Callable, options: Dictionary={}): return RdotComputed.new(computation, options) ## DIY overloading of ## bind(target, prop, value) ## bind(target, prop, value, watch_signal) ## bind(target, prop, store, key, watch_signal) static func bind(target, prop, value, arg1=null, arg2=null): if value is RdotStore: return _bind_store(target, prop, value, arg1, arg2) if value is RdotState or value is RdotComputed: return _bind_state(target, prop, value, arg1) assert(false, "Invalid arguments to bind, value must be a RdotState, a RdotComputed or a RdotStore") static func _bind_store(target, prop, store: RdotStore, key, watch_signal=null): store._access_property(key) return _bind_state(target, prop, store._proxied_value[key], watch_signal) static func _bind_state(target, prop, value, watch_signal=null): assert(state != null, "target cannot be null") var graph = RdotGraph.getInstance() var watch_c = func(new_value): value.value = new_value var c = computed(func(_arg): var oldValue=target.get(prop) if oldValue != value.value: target.set(prop, value.value) ) if watch_signal: watch_signal.connect(watch_c) graph.watcher.watch([c]) c.do_get() return func(): graph.watcher.unwatch([c]) if watch_signal: watch_signal.disconnect(watch_c) static func effect(callback: Callable): var graph = RdotGraph.getInstance() var deconstructor := Callable() var c = computed(func(_arg): if !deconstructor.is_null(): deconstructor.call() var result=callback.call(_arg) if result is Callable: deconstructor=result ) graph.watcher.watch([c]) c.do_get() return func(): if !deconstructor.is_null(): deconstructor.call() graph.watcher.unwatch([c]) class RdotState: var node: RdotStateInternal var value = null: get: return do_get() set(value): do_set(value) func _init(value: Variant, options: Dictionary={}): var ref = RdotStateInternal.createSignal(value) var node = ref[1] self.node = node node.wrapper = self if !options.is_empty(): var equals = options.get("equals") if !equals.is_null(): node.equals = equals func do_get(): return RdotStateInternal.signalGetFn.call(self.node) func do_set(value: Variant): var graph = RdotGraph.getInstance() assert(graph.isInNotificationPhase() == false, "Writes to signals not permitted during Watcher callback") var ref = self.node RdotStateInternal.signalSetFn.call(ref, value) class RdotComputed: var node: RdotComputedInternal var value = null: get: return do_get() set(value): pass func _init(computation: Callable, options: Dictionary={}): var ref = RdotComputedInternal.createdComputed(computation) var node = ref[1] node.consumerAllowSignalWrites = true self.node = node node.wrapper = self if options: var equals = options.get("equals") if !equals.is_null(): node.equals = equals func do_get(): return RdotComputedInternal.computedGet(node) class Watcher: var node: RdotNode func _init(notify: Callable): var node = RdotNode.new() node.wrapper = self node.consumerMarkedDirty = notify node.consumerIsAlwaysLive = true node.consumerAllowSignalWrites = false node.producerNode = [] self.node = node ## signals: Array[RdotState | RComputed] func _assertSignals(signals: Array): for s in signals: assert(s is RdotState or s is RdotComputed, "Watcher expects signals to be RdotState or RdotComputed") func watch(signals:=[]): _assertSignals(signals) var graph = RdotGraph.getInstance() var node = self.node node.dirty = false var prev = graph.setActiveConsumer(node) for s in signals: graph.producerAccessed(s.node) graph.setActiveConsumer(prev) func unwatch(signals: Array): _assertSignals(signals) var graph = RdotGraph.getInstance() var node = self.node graph.assertConsumerNode(node) var indicesToShift = [] for i in range(node.producerNode.size()): if signals.has(node.producerNode[i].wrapper): graph.producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]) indicesToShift.append(i) for idx in indicesToShift: var lastIdx = node.producerNode.size() - 1 node.producerNode[idx] = node.producerNode[lastIdx] node.producerIndexOfThis[idx] = node.producerIndexOfThis[lastIdx] node.producerNode.pop_back() node.producerIndexOfThis.pop_back() node.nextProducerIndex -= 1 if idx < node.producerNode.size(): var idxConsumer = node.producerNode[idx] var producer = node.producerNode[idx] graph.assertProducerNode(producer) producer.liveConsumerIndexOfThis[idxConsumer] = idx ## Returns Array[RComputed] func getPending() -> Array: var node = self.node return node.producerNode.filter(func(n): return n.dirty).map(func(n): return n.wrapper)