2025-01-11 00:25:52 +01:00

249 lines
7.5 KiB
GDScript

extends ReactivePropertyBase
class_name ReactiveProperty
## An observable property.
##
## Wraps a value and emits an item whenever it is changed.
## The emitted item is the new value of the [ReactiveProperty].
var _latest_value
var _source_subscription : DisposableBase
var _observers : Array[ObserverBase]
var _distinct_until_changed : bool
var _raise_latest_value_on_subscribe : bool
var _rwlock : ReadWriteLock
var is_disposed : bool
func _get_value():
return self._latest_value
func _set_value(value):
if self._distinct_until_changed and self._latest_value == value:
return
self._latest_value = value
var observers_ : Array[ObserverBase]
if true:
var __ = ReadWriteLockGuard.new(self._rwlock, true)
observers_ = self._observers.duplicate()
for obs in observers_:
obs.on_next(value)
func _to_string() -> String:
if self.is_disposed:
return "<<Disposed ReactiveProperty>>"
return str(self.Value)
func _init(
initial_value_,
distinct_until_changed_ : bool = true,
raise_latest_value_on_subscribe_ : bool = true,
source : Observable = null
):
var wself : WeakRef = weakref(self)
self.is_disposed = false
self._observers = []
self._rwlock = ReadWriteLock.new()
self._latest_value = initial_value_
self._distinct_until_changed = distinct_until_changed_
self._raise_latest_value_on_subscribe = raise_latest_value_on_subscribe_
if source != null:
self._source_subscription = source.subscribe(func(i): wself.get_ref().Value = i)
@warning_ignore("shadowed_variable")
var subscribe = func(
observer : ObserverBase,
_scheduler : SchedulerBase = null
) -> DisposableBase:
var prop : ReactiveProperty = wself.get_ref()
if not prop or prop.is_disposed:
observer.on_completed()
return Disposable.new()
if true:
var __ = ReadWriteLockGuard.new(prop._rwlock, false)
prop._observers.push_back(observer)
if prop._raise_latest_value_on_subscribe:
observer.on_next(prop._latest_value)
var dispose_ = func():
var _prop : ReactiveProperty = wself.get_ref()
if not _prop:
return
var __ = ReadWriteLockGuard.new(_prop._rwlock, false)
_prop._observers.erase(observer)
return Disposable.new(dispose_)
super._init(subscribe)
func dispose():
if this.is_disposed:
return
this.is_disposed = true
var observers_ : Array[ObserverBase]
if true:
var __ = ReadWriteLockGuard.new(this._rwlock, true)
observers_ = this._observers.duplicate()
for obs in observers_:
obs.on_completed()
if true:
var __ = ReadWriteLockGuard.new(this._rwlock, false)
this._observers.clear()
if this._source_subscription != null:
this._source_subscription.dispose()
func to_readonly() -> ReadOnlyReactiveProperty:
return ReadOnlyReactiveProperty.new(
self, Value, self._distinct_until_changed,
self._raise_latest_value_on_subscribe
)
static func FromGetSet(
getter : Callable,
setter : Callable,
distinct_until_changed_ : bool = true,
raise_latest_value_on_subscribe_ : bool = true
) -> ReactiveProperty:
var prop = ReactiveProperty.new(
getter.call(),
distinct_until_changed_,
raise_latest_value_on_subscribe_
)
prop.subscribe(func(x): setter.call(x)).dispose_with(prop)
return prop
static func FromMember(
target,
member_name : StringName,
convert_cb = GDRx.basic.identity,
convert_back_cb = GDRx.basic.identity,
distinct_until_changed_ : bool = true,
raise_latest_value_on_subscribe_ : bool = true
) -> ReactiveProperty:
var getter = func():
return target.get(member_name)
var setter = func(v):
target.set(member_name, v)
return FromGetSet(
func(): return convert_cb.call(getter.call()),
func(x): setter.call(convert_back_cb.call(x)),
distinct_until_changed_,
raise_latest_value_on_subscribe_
)
static func Derived(p : ReadOnlyReactiveProperty, fn : Callable) -> ReadOnlyReactiveProperty:
return ReadOnlyReactiveProperty.new(
p.map(fn),
fn.call(p.Value)
)
static func Computed1(p : ReadOnlyReactiveProperty, fn : Callable) -> ReadOnlyReactiveProperty:
return Derived(p, fn)
static func Computed2(
p1 : ReadOnlyReactiveProperty,
p2 : ReadOnlyReactiveProperty,
fn : Callable
) -> ReadOnlyReactiveProperty:
return ReadOnlyReactiveProperty.new(
p1.combine_latest([p2 as Observable]).map(func(tup : Tuple): return fn.call(tup.at(0), tup.at(1))),
fn.call(p1.Value, p2.Value)
)
static func Computed3(
p1 : ReadOnlyReactiveProperty,
p2 : ReadOnlyReactiveProperty,
p3 : ReadOnlyReactiveProperty,
fn : Callable
) -> ReadOnlyReactiveProperty:
return ReadOnlyReactiveProperty.new(
p1.combine_latest([p2 as Observable, p3 as Observable]).map(func(tup : Tuple): return fn.call(tup.at(0), tup.at(1), tup.at(2))),
fn.call(p1.Value, p2.Value, p3.Value)
)
static func Computed4(
p1 : ReadOnlyReactiveProperty,
p2 : ReadOnlyReactiveProperty,
p3 : ReadOnlyReactiveProperty,
p4 : ReadOnlyReactiveProperty,
fn : Callable
) -> ReadOnlyReactiveProperty:
return ReadOnlyReactiveProperty.new(
p1.combine_latest([p2 as Observable, p3 as Observable, p4 as Observable]).map(func(tup : Tuple): return fn.call(tup.at(0), tup.at(1), tup.at(2), tup.at(3))),
fn.call(p1.Value, p2.Value, p3.Value, p4.Value)
)
static func Computed5(
p1 : ReadOnlyReactiveProperty,
p2 : ReadOnlyReactiveProperty,
p3 : ReadOnlyReactiveProperty,
p4 : ReadOnlyReactiveProperty,
p5 : ReadOnlyReactiveProperty,
fn : Callable
) -> ReadOnlyReactiveProperty:
return ReadOnlyReactiveProperty.new(
p1.combine_latest([p2 as Observable, p3 as Observable, p4 as Observable, p5 as Observable]).map(func(tup : Tuple): return fn.call(tup.at(0), tup.at(1), tup.at(2), tup.at(3), tup.at(4))),
fn.call(p1.Value, p2.Value, p3.Value, p4.Value, p5.Value)
)
static func Computed6(
p1 : ReadOnlyReactiveProperty,
p2 : ReadOnlyReactiveProperty,
p3 : ReadOnlyReactiveProperty,
p4 : ReadOnlyReactiveProperty,
p5 : ReadOnlyReactiveProperty,
p6 : ReadOnlyReactiveProperty,
fn : Callable
) -> ReadOnlyReactiveProperty:
return ReadOnlyReactiveProperty.new(
p1.combine_latest([p2 as Observable, p3 as Observable, p4 as Observable, p5 as Observable, p6 as Observable]).map(func(tup : Tuple): return fn.call(tup.at(0), tup.at(1), tup.at(2), tup.at(3), tup.at(4), tup.at(5))),
fn.call(p1.Value, p2.Value, p3.Value, p4.Value, p5.Value, p6.Value)
)
static func Computed7(
p1 : ReadOnlyReactiveProperty,
p2 : ReadOnlyReactiveProperty,
p3 : ReadOnlyReactiveProperty,
p4 : ReadOnlyReactiveProperty,
p5 : ReadOnlyReactiveProperty,
p6 : ReadOnlyReactiveProperty,
p7 : ReadOnlyReactiveProperty,
fn : Callable
) -> ReadOnlyReactiveProperty:
return ReadOnlyReactiveProperty.new(
p1.combine_latest([p2 as Observable, p3 as Observable, p4 as Observable, p5 as Observable, p6 as Observable, p7 as Observable]).map(func(tup : Tuple): return fn.call(tup.at(0), tup.at(1), tup.at(2), tup.at(3), tup.at(4), tup.at(5), tup.at(6))),
fn.call(p1.Value, p2.Value, p3.Value, p4.Value, p5.Value, p6.Value, p7.Value)
)
static func Computed8(
p1 : ReadOnlyReactiveProperty,
p2 : ReadOnlyReactiveProperty,
p3 : ReadOnlyReactiveProperty,
p4 : ReadOnlyReactiveProperty,
p5 : ReadOnlyReactiveProperty,
p6 : ReadOnlyReactiveProperty,
p7 : ReadOnlyReactiveProperty,
p8 : ReadOnlyReactiveProperty,
fn : Callable
) -> ReadOnlyReactiveProperty:
return ReadOnlyReactiveProperty.new(
p1.combine_latest([p2 as Observable, p3 as Observable, p4 as Observable, p5 as Observable, p6 as Observable, p7 as Observable, p8 as Observable]).map(func(tup : Tuple): return fn.call(tup.at(0), tup.at(1), tup.at(2), tup.at(3), tup.at(4), tup.at(5), tup.at(6), tup.at(7))),
fn.call(p1.Value, p2.Value, p3.Value, p4.Value, p5.Value, p6.Value, p7.Value, p8.Value)
)