294 lines
7.9 KiB
GDScript3
294 lines
7.9 KiB
GDScript3
|
|
extends ReactiveCollectionBase
|
||
|
|
class_name ReactiveCollection
|
||
|
|
|
||
|
|
var _count : int
|
||
|
|
var _data : Array
|
||
|
|
var _observers : Dictionary
|
||
|
|
var _rwlock : ReadWriteLock
|
||
|
|
|
||
|
|
var is_disposed : bool
|
||
|
|
|
||
|
|
func _get_subscription(event_class, notify_count = false) -> Callable:
|
||
|
|
var wself : WeakRef = weakref(self)
|
||
|
|
|
||
|
|
return func(observer : ObserverBase, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||
|
|
var prop : ReactiveCollection = wself.get_ref()
|
||
|
|
|
||
|
|
if not prop or prop.is_disposed:
|
||
|
|
observer.on_completed()
|
||
|
|
return Disposable.new()
|
||
|
|
|
||
|
|
if notify_count:
|
||
|
|
observer.on_next(prop.Count)
|
||
|
|
|
||
|
|
if true:
|
||
|
|
var __ = ReadWriteLockGuard.new(prop._rwlock, false)
|
||
|
|
if not (event_class in prop._observers):
|
||
|
|
prop._observers[event_class] = []
|
||
|
|
prop._observers[event_class].push_back(observer)
|
||
|
|
|
||
|
|
var dispose_ = func():
|
||
|
|
var _prop = wself.get_ref()
|
||
|
|
if not _prop:
|
||
|
|
return
|
||
|
|
var __ = ReadWriteLockGuard.new(prop._rwlock, false)
|
||
|
|
prop._observers[event_class].erase(observer)
|
||
|
|
|
||
|
|
return Disposable.new(dispose_)
|
||
|
|
|
||
|
|
func _notify_all(event_class, event):
|
||
|
|
var observers_ : Array
|
||
|
|
if true:
|
||
|
|
var __ = ReadWriteLockGuard.new(self._rwlock, true)
|
||
|
|
if event_class in self._observers:
|
||
|
|
observers_ = self._observers[event_class].duplicate()
|
||
|
|
for observer in observers_:
|
||
|
|
observer.on_next(event)
|
||
|
|
|
||
|
|
func _disconnect_all(event_class):
|
||
|
|
var observers_ : Array
|
||
|
|
if true:
|
||
|
|
var __ = ReadWriteLockGuard.new(this._rwlock, true)
|
||
|
|
if event_class in this._observers:
|
||
|
|
observers_ = this._observers[event_class].duplicate()
|
||
|
|
for observer in observers_:
|
||
|
|
observer.on_completed()
|
||
|
|
|
||
|
|
func _init(collection = []):
|
||
|
|
super._init()
|
||
|
|
|
||
|
|
self._count = 0
|
||
|
|
self._data = []
|
||
|
|
self._observers = {}
|
||
|
|
self._rwlock = ReadWriteLock.new()
|
||
|
|
self.is_disposed = false
|
||
|
|
|
||
|
|
var it : Iterator = GDRx.iter(collection)
|
||
|
|
while it.has_next():
|
||
|
|
self._add_item(it.next())
|
||
|
|
|
||
|
|
self._observe_add = Observable.new(self._get_subscription(CollectionAddEvent))
|
||
|
|
self._observe_move = Observable.new(self._get_subscription(CollectionMoveEvent))
|
||
|
|
self._observe_remove = Observable.new(self._get_subscription(CollectionRemoveEvent))
|
||
|
|
self._observe_replace = Observable.new(self._get_subscription(CollectionReplaceEvent))
|
||
|
|
self._observe_reset = Observable.new(self._get_subscription("Reset"))
|
||
|
|
|
||
|
|
func ObserveCountChanged(notify_current_count : bool = false) -> Observable:
|
||
|
|
return Observable.new(self._get_subscription("CountChanged", notify_current_count))
|
||
|
|
|
||
|
|
func _add_item(item) -> int:
|
||
|
|
self._data.append(item)
|
||
|
|
self._count += 1
|
||
|
|
return self._count - 1
|
||
|
|
|
||
|
|
func add_item(item) -> int:
|
||
|
|
if self.is_disposed:
|
||
|
|
DisposedError.raise()
|
||
|
|
return -1
|
||
|
|
var index = self._add_item(item)
|
||
|
|
var event_add = CollectionAddEvent.new(index, item)
|
||
|
|
self._notify_all(CollectionAddEvent, event_add)
|
||
|
|
self._notify_all("CountChanged", self._count)
|
||
|
|
return index
|
||
|
|
|
||
|
|
func _remove_item(item) -> int:
|
||
|
|
var index = self._data.find(item)
|
||
|
|
if index >= 0:
|
||
|
|
self._data.remove_at(index)
|
||
|
|
self._count -= 1
|
||
|
|
return index
|
||
|
|
|
||
|
|
func remove_item(item) -> int:
|
||
|
|
if self.is_disposed:
|
||
|
|
DisposedError.raise()
|
||
|
|
return -1
|
||
|
|
var index = self._remove_item(item)
|
||
|
|
if index >= 0:
|
||
|
|
var event_remove = CollectionRemoveEvent.new(index, item)
|
||
|
|
self._notify_all(CollectionRemoveEvent, event_remove)
|
||
|
|
self._notify_all("CountChanged", self._count)
|
||
|
|
return index
|
||
|
|
|
||
|
|
func _remove_at(index : int) -> Variant:
|
||
|
|
if index >= self._count:
|
||
|
|
return null
|
||
|
|
var value = self._data[index]
|
||
|
|
self._data.remove_at(index)
|
||
|
|
self._count -= 1
|
||
|
|
return value
|
||
|
|
|
||
|
|
func remove_at(index : int) -> Variant:
|
||
|
|
if self.is_disposed:
|
||
|
|
return DisposedError.raise()
|
||
|
|
var value = self._remove_at(index)
|
||
|
|
if value != null:
|
||
|
|
var event_remove = CollectionRemoveEvent.new(index, value)
|
||
|
|
self._notify_all(CollectionRemoveEvent, event_remove)
|
||
|
|
self._notify_all("CountChanged", self._count)
|
||
|
|
return value
|
||
|
|
|
||
|
|
func _replace_item(item, with) -> int:
|
||
|
|
var index = self._data.find(item)
|
||
|
|
if index >= 0:
|
||
|
|
self._data[index] = with
|
||
|
|
return index
|
||
|
|
|
||
|
|
func replace_item(item, with) -> int:
|
||
|
|
if self.is_disposed:
|
||
|
|
return DisposedError.raise()
|
||
|
|
if GDRx.eq(item, with):
|
||
|
|
return self._data.find(item)
|
||
|
|
var index : int = self._replace_item(item, with)
|
||
|
|
if index >= 0:
|
||
|
|
var event_replace = CollectionReplaceEvent.new(index, item, with)
|
||
|
|
self._notify_all(CollectionReplaceEvent, event_replace)
|
||
|
|
return index
|
||
|
|
|
||
|
|
func _replace_at(index : int, item) -> Variant:
|
||
|
|
if index >= self._count:
|
||
|
|
return null
|
||
|
|
var value = self._data[index]
|
||
|
|
self._data[index] = item
|
||
|
|
return value
|
||
|
|
|
||
|
|
func replace_at(index : int, item) -> Variant:
|
||
|
|
if self.is_disposed:
|
||
|
|
return DisposedError.raise()
|
||
|
|
var value = self._replace_at(index, item)
|
||
|
|
if value != null and GDRx.neq(value, item):
|
||
|
|
var event_replace = CollectionReplaceEvent.new(index, value, item)
|
||
|
|
self._notify_all(CollectionReplaceEvent, event_replace)
|
||
|
|
return value
|
||
|
|
|
||
|
|
func _swap(idx1 : int, idx2 : int) -> Tuple:
|
||
|
|
if idx1 >= self._count or idx2 >= self._count:
|
||
|
|
return null
|
||
|
|
var tmp = self._data[idx1]
|
||
|
|
self._data[idx1] = self._data[idx2]
|
||
|
|
self._data[idx2] = tmp
|
||
|
|
return Tuple.new([self._data[idx2], self._data[idx1]])
|
||
|
|
|
||
|
|
func swap(idx1 : int, idx2 : int) -> Tuple:
|
||
|
|
if self.is_disposed:
|
||
|
|
return DisposedError.raise()
|
||
|
|
if idx1 >= self._count or idx2 >= self._count:
|
||
|
|
return
|
||
|
|
var pair = self._swap(idx1, idx2)
|
||
|
|
if GDRx.eq(pair.at(0), pair.at(1)):
|
||
|
|
return pair
|
||
|
|
var event_move1 = CollectionMoveEvent.new(idx1, idx2, pair.at(0))
|
||
|
|
var event_move2 = CollectionMoveEvent.new(idx2, idx1, pair.at(1))
|
||
|
|
self._notify_all(CollectionMoveEvent, event_move1)
|
||
|
|
self._notify_all(CollectionMoveEvent, event_move2)
|
||
|
|
return pair
|
||
|
|
|
||
|
|
func _move_to(curr_index : int, new_index : int):
|
||
|
|
if curr_index >= self._count or new_index >= self._count:
|
||
|
|
return
|
||
|
|
var tmp = self._data[curr_index]
|
||
|
|
self._data.remove_at(curr_index)
|
||
|
|
self._data.insert(new_index, tmp)
|
||
|
|
|
||
|
|
func move_to(old_index : int, new_index : int):
|
||
|
|
if self.is_disposed:
|
||
|
|
return DisposedError.raise()
|
||
|
|
if old_index >= self._count or new_index >= self._count or old_index == new_index:
|
||
|
|
return
|
||
|
|
var moved = self._data[old_index]
|
||
|
|
self._move_to(old_index, new_index)
|
||
|
|
var event_move = CollectionMoveEvent.new(old_index, new_index, moved)
|
||
|
|
self._notify_all(CollectionMoveEvent, event_move)
|
||
|
|
|
||
|
|
func _insert_at(index : int, elem):
|
||
|
|
if index > self._count:
|
||
|
|
return
|
||
|
|
self._data.insert(index, elem)
|
||
|
|
self._count += 1
|
||
|
|
|
||
|
|
func insert_at(index : int, elem):
|
||
|
|
if self.is_disposed:
|
||
|
|
return DisposedError.raise()
|
||
|
|
if index > self._count:
|
||
|
|
return
|
||
|
|
self._insert_at(index, elem)
|
||
|
|
var event_add = CollectionAddEvent.new(index, elem)
|
||
|
|
self._notify_all(CollectionAddEvent, event_add)
|
||
|
|
self._notify_all("CountChanged", self._count)
|
||
|
|
|
||
|
|
func _at(index : int):
|
||
|
|
return self._data[index]
|
||
|
|
|
||
|
|
func at(index : int):
|
||
|
|
if self.is_disposed:
|
||
|
|
return DisposedError.raise()
|
||
|
|
return self._at(index)
|
||
|
|
|
||
|
|
func _find(item) -> int:
|
||
|
|
return self._data.find(item)
|
||
|
|
|
||
|
|
func find(item) -> int:
|
||
|
|
if self.is_disposed:
|
||
|
|
DisposedError.raise()
|
||
|
|
return -1
|
||
|
|
return self._find(item)
|
||
|
|
|
||
|
|
func _reset():
|
||
|
|
self._data.clear()
|
||
|
|
self._count = 0
|
||
|
|
|
||
|
|
func reset():
|
||
|
|
if self.is_disposed:
|
||
|
|
return DisposedError.raise()
|
||
|
|
var c = self._count
|
||
|
|
self._reset()
|
||
|
|
self._notify_all("Reset", StreamItem.Unit())
|
||
|
|
if self._count != c:
|
||
|
|
self._notify_all("CountChanged", self._count)
|
||
|
|
|
||
|
|
func iter() -> Iterator:
|
||
|
|
if self.is_disposed:
|
||
|
|
return DisposedError.raise()
|
||
|
|
return GDRx.iter(self._data)
|
||
|
|
|
||
|
|
func _to_list() -> Array:
|
||
|
|
return self._data.duplicate()
|
||
|
|
|
||
|
|
func to_list() -> Array:
|
||
|
|
if self.is_disposed:
|
||
|
|
DisposedError.raise()
|
||
|
|
return []
|
||
|
|
return self._data.duplicate()
|
||
|
|
|
||
|
|
func _size() -> int:
|
||
|
|
return self._count
|
||
|
|
|
||
|
|
func size() -> int:
|
||
|
|
if self.is_disposed:
|
||
|
|
DisposedError.raise()
|
||
|
|
return -1
|
||
|
|
return self._count
|
||
|
|
|
||
|
|
func dispose():
|
||
|
|
if this.is_disposed:
|
||
|
|
return
|
||
|
|
this.is_disposed = true
|
||
|
|
|
||
|
|
this._disconnect_all(CollectionAddEvent)
|
||
|
|
this._disconnect_all(CollectionMoveEvent)
|
||
|
|
this._disconnect_all(CollectionRemoveEvent)
|
||
|
|
this._disconnect_all(CollectionReplaceEvent)
|
||
|
|
this._disconnect_all("Reset")
|
||
|
|
this._disconnect_all("CountChanged")
|
||
|
|
|
||
|
|
this._data = []
|
||
|
|
this._count = -1
|
||
|
|
this._observers = {}
|
||
|
|
|
||
|
|
func to_readonly() -> ReadOnlyReactiveCollection:
|
||
|
|
return ReadOnlyReactiveCollection.new(self)
|
||
|
|
|
||
|
|
func _to_string() -> String:
|
||
|
|
if self.is_disposed:
|
||
|
|
return "<<Disposed ReactiveCollection>>"
|
||
|
|
return str(self._data)
|