.
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
||||
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
/android/
|
||||
17
addons/Todo_Manager/ColourPicker.gd
Normal file
@ -0,0 +1,17 @@
|
||||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
var colour : Color
|
||||
var title : String:
|
||||
set = set_title
|
||||
var index : int
|
||||
|
||||
@onready var colour_picker := $TODOColourPickerButton
|
||||
|
||||
func _ready() -> void:
|
||||
$TODOColourPickerButton.color = colour
|
||||
$Label.text = title
|
||||
|
||||
func set_title(value: String) -> void:
|
||||
title = value
|
||||
$Label.text = value
|
||||
44
addons/Todo_Manager/Current.gd
Normal file
@ -0,0 +1,44 @@
|
||||
@tool
|
||||
extends Panel
|
||||
|
||||
signal tree_built # used for debugging
|
||||
|
||||
const Todo := preload("res://addons/Todo_Manager/todo_class.gd")
|
||||
const TodoItem := preload("res://addons/Todo_Manager/todoItem_class.gd")
|
||||
|
||||
var _sort_alphabetical := true
|
||||
|
||||
@onready var tree := $Tree as Tree
|
||||
|
||||
func build_tree(todo_item : TodoItem, patterns : Array, cased_patterns : Array[String]) -> void:
|
||||
tree.clear()
|
||||
var root := tree.create_item()
|
||||
root.set_text(0, "Scripts")
|
||||
var script := tree.create_item(root)
|
||||
script.set_text(0, todo_item.get_short_path() + " -------")
|
||||
script.set_metadata(0, todo_item)
|
||||
for todo in todo_item.todos:
|
||||
var item := tree.create_item(script)
|
||||
var content_header : String = todo.content
|
||||
if "\n" in todo.content:
|
||||
content_header = content_header.split("\n")[0] + "..."
|
||||
item.set_text(0, "(%0) - %1".format([todo.line_number, content_header], "%_"))
|
||||
item.set_tooltip_text(0, todo.content)
|
||||
item.set_metadata(0, todo)
|
||||
for i in range(0, len(cased_patterns)):
|
||||
if cased_patterns[i] == todo.pattern:
|
||||
item.set_custom_color(0, patterns[i][1])
|
||||
emit_signal("tree_built")
|
||||
|
||||
|
||||
func sort_alphabetical(a, b) -> bool:
|
||||
if a.script_path > b.script_path:
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
func sort_backwards(a, b) -> bool:
|
||||
if a.script_path < b.script_path:
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
297
addons/Todo_Manager/Dock.gd
Normal file
@ -0,0 +1,297 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
#signal tree_built # used for debugging
|
||||
enum { CASE_INSENSITIVE, CASE_SENSITIVE }
|
||||
|
||||
const Project := preload("res://addons/Todo_Manager/Project.gd")
|
||||
const Current := preload("res://addons/Todo_Manager/Current.gd")
|
||||
|
||||
const Todo := preload("res://addons/Todo_Manager/todo_class.gd")
|
||||
const TodoItem := preload("res://addons/Todo_Manager/todoItem_class.gd")
|
||||
const ColourPicker := preload("res://addons/Todo_Manager/UI/ColourPicker.tscn")
|
||||
const Pattern := preload("res://addons/Todo_Manager/UI/Pattern.tscn")
|
||||
const DEFAULT_PATTERNS := [["\\bTODO\\b", Color("96f1ad"), CASE_INSENSITIVE], ["\\bHACK\\b", Color("d5bc70"), CASE_INSENSITIVE], ["\\bFIXME\\b", Color("d57070"), CASE_INSENSITIVE]]
|
||||
const DEFAULT_SCRIPT_COLOUR := Color("ccced3")
|
||||
const DEFAULT_SCRIPT_NAME := false
|
||||
const DEFAULT_SORT := true
|
||||
|
||||
var plugin : EditorPlugin
|
||||
|
||||
var todo_items : Array
|
||||
|
||||
var script_colour := Color("ccced3")
|
||||
var ignore_paths : Array[String] = []
|
||||
var full_path := false
|
||||
var auto_refresh := true
|
||||
var builtin_enabled := false
|
||||
var _sort_alphabetical := true
|
||||
|
||||
var patterns := [["\\bTODO\\b", Color("96f1ad"), CASE_INSENSITIVE], ["\\bHACK\\b", Color("d5bc70"), CASE_INSENSITIVE], ["\\bFIXME\\b", Color("d57070"), CASE_INSENSITIVE]]
|
||||
|
||||
|
||||
@onready var tabs := $VBoxContainer/TabContainer as TabContainer
|
||||
@onready var project := $VBoxContainer/TabContainer/Project as Project
|
||||
@onready var current := $VBoxContainer/TabContainer/Current as Current
|
||||
@onready var project_tree := $VBoxContainer/TabContainer/Project/Tree as Tree
|
||||
@onready var current_tree := $VBoxContainer/TabContainer/Current/Tree as Tree
|
||||
@onready var settings_panel := $VBoxContainer/TabContainer/Settings as Panel
|
||||
@onready var colours_container := $VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer3/Colours as VBoxContainer
|
||||
@onready var pattern_container := $VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4/Patterns as VBoxContainer
|
||||
@onready var ignore_textbox := $VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/IgnorePaths/TextEdit as LineEdit
|
||||
@onready var auto_refresh_button := $VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns/RefreshCheckButton as CheckButton
|
||||
|
||||
func _ready() -> void:
|
||||
load_config()
|
||||
populate_settings()
|
||||
|
||||
|
||||
func build_tree() -> void:
|
||||
if tabs:
|
||||
match tabs.current_tab:
|
||||
0:
|
||||
project.build_tree(todo_items, ignore_paths, patterns, plugin.cased_patterns, _sort_alphabetical, full_path)
|
||||
create_config_file()
|
||||
1:
|
||||
current.build_tree(get_active_script(), patterns, plugin.cased_patterns)
|
||||
create_config_file()
|
||||
2:
|
||||
pass
|
||||
_:
|
||||
pass
|
||||
|
||||
|
||||
func get_active_script() -> TodoItem:
|
||||
var current_script : Script = plugin.get_editor_interface().get_script_editor().get_current_script()
|
||||
if current_script:
|
||||
var script_path = current_script.resource_path
|
||||
for todo_item in todo_items:
|
||||
if todo_item.script_path == script_path:
|
||||
return todo_item
|
||||
|
||||
# nothing found
|
||||
var todo_item := TodoItem.new(script_path, [])
|
||||
return todo_item
|
||||
else:
|
||||
# not a script
|
||||
var todo_item := TodoItem.new("res://Documentation", [])
|
||||
return todo_item
|
||||
|
||||
|
||||
func go_to_script(script_path: String, line_number : int = 0) -> void:
|
||||
if plugin.get_editor_interface().get_editor_settings().get_setting("text_editor/external/use_external_editor"):
|
||||
var exec_path = plugin.get_editor_interface().get_editor_settings().get_setting("text_editor/external/exec_path")
|
||||
var args := get_exec_flags(exec_path, script_path, line_number)
|
||||
OS.execute(exec_path, args)
|
||||
else:
|
||||
var script := load(script_path)
|
||||
plugin.get_editor_interface().edit_resource(script)
|
||||
plugin.get_editor_interface().get_script_editor().goto_line(line_number - 1)
|
||||
|
||||
func get_exec_flags(editor_path : String, script_path : String, line_number : int) -> PackedStringArray:
|
||||
var args : PackedStringArray
|
||||
var script_global_path = ProjectSettings.globalize_path(script_path)
|
||||
|
||||
if editor_path.ends_with("code.cmd") or editor_path.ends_with("code"): ## VS Code
|
||||
args.append(ProjectSettings.globalize_path("res://"))
|
||||
args.append("--goto")
|
||||
args.append(script_global_path + ":" + str(line_number))
|
||||
|
||||
elif editor_path.ends_with("rider64.exe") or editor_path.ends_with("rider"): ## Rider
|
||||
args.append("--line")
|
||||
args.append(str(line_number))
|
||||
args.append(script_global_path)
|
||||
|
||||
else: ## Atom / Sublime
|
||||
args.append(script_global_path + ":" + str(line_number))
|
||||
|
||||
return args
|
||||
|
||||
func sort_alphabetical(a, b) -> bool:
|
||||
if a.script_path > b.script_path:
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
func sort_backwards(a, b) -> bool:
|
||||
if a.script_path < b.script_path:
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
|
||||
func populate_settings() -> void:
|
||||
for i in patterns.size():
|
||||
## Create Colour Pickers
|
||||
var colour_picker: Variant = ColourPicker.instantiate()
|
||||
colour_picker.colour = patterns[i][1]
|
||||
colour_picker.title = patterns[i][0]
|
||||
colour_picker.index = i
|
||||
colours_container.add_child(colour_picker)
|
||||
colour_picker.colour_picker.color_changed.connect(change_colour.bind(i))
|
||||
|
||||
## Create Patterns
|
||||
var pattern_edit: Variant = Pattern.instantiate()
|
||||
pattern_edit.text = patterns[i][0]
|
||||
pattern_edit.index = i
|
||||
pattern_container.add_child(pattern_edit)
|
||||
pattern_edit.line_edit.text_changed.connect(change_pattern.bind(i,
|
||||
colour_picker))
|
||||
pattern_edit.remove_button.pressed.connect(remove_pattern.bind(i,
|
||||
pattern_edit, colour_picker))
|
||||
pattern_edit.case_checkbox.button_pressed = patterns[i][2]
|
||||
pattern_edit.case_checkbox.toggled.connect(case_sensitive_pattern.bind(i))
|
||||
|
||||
var pattern_button := $VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4/Patterns/AddPatternButton
|
||||
$VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4/Patterns.move_child(pattern_button, 0)
|
||||
|
||||
# path filtering
|
||||
var ignore_paths_field := ignore_textbox
|
||||
if not ignore_paths_field.is_connected("text_changed", _on_ignore_paths_changed):
|
||||
ignore_paths_field.connect("text_changed", _on_ignore_paths_changed)
|
||||
var ignore_paths_text := ""
|
||||
for path in ignore_paths:
|
||||
ignore_paths_text += path + ", "
|
||||
ignore_paths_text = ignore_paths_text.trim_suffix(", ")
|
||||
ignore_paths_field.text = ignore_paths_text
|
||||
|
||||
auto_refresh_button.button_pressed = auto_refresh
|
||||
|
||||
|
||||
func rebuild_settings() -> void:
|
||||
for node in colours_container.get_children():
|
||||
node.queue_free()
|
||||
for node in pattern_container.get_children():
|
||||
if node is Button:
|
||||
continue
|
||||
node.queue_free()
|
||||
populate_settings()
|
||||
|
||||
|
||||
#### CONFIG FILE ####
|
||||
func create_config_file() -> void:
|
||||
var config = ConfigFile.new()
|
||||
config.set_value("scripts", "full_path", full_path)
|
||||
config.set_value("scripts", "sort_alphabetical", _sort_alphabetical)
|
||||
config.set_value("scripts", "script_colour", script_colour)
|
||||
config.set_value("scripts", "ignore_paths", ignore_paths)
|
||||
|
||||
config.set_value("patterns", "patterns", patterns)
|
||||
|
||||
config.set_value("config", "auto_refresh", auto_refresh)
|
||||
config.set_value("config", "builtin_enabled", builtin_enabled)
|
||||
|
||||
var err = config.save("res://addons/Todo_Manager/todo.cfg")
|
||||
|
||||
|
||||
func load_config() -> void:
|
||||
var config := ConfigFile.new()
|
||||
if config.load("res://addons/Todo_Manager/todo.cfg") == OK:
|
||||
full_path = config.get_value("scripts", "full_path", DEFAULT_SCRIPT_NAME)
|
||||
_sort_alphabetical = config.get_value("scripts", "sort_alphabetical", DEFAULT_SORT)
|
||||
script_colour = config.get_value("scripts", "script_colour", DEFAULT_SCRIPT_COLOUR)
|
||||
ignore_paths = config.get_value("scripts", "ignore_paths", [] as Array[String])
|
||||
patterns = config.get_value("patterns", "patterns", DEFAULT_PATTERNS)
|
||||
auto_refresh = config.get_value("config", "auto_refresh", true)
|
||||
builtin_enabled = config.get_value("config", "builtin_enabled", false)
|
||||
else:
|
||||
create_config_file()
|
||||
|
||||
|
||||
#### Events ####
|
||||
func _on_SettingsButton_toggled(button_pressed: bool) -> void:
|
||||
settings_panel.visible = button_pressed
|
||||
if button_pressed == false:
|
||||
create_config_file()
|
||||
# plugin.find_tokens_from_path(plugin.script_cache)
|
||||
if auto_refresh:
|
||||
plugin.rescan_files(true)
|
||||
|
||||
func _on_Tree_item_activated() -> void:
|
||||
var item : TreeItem
|
||||
match tabs.current_tab:
|
||||
0:
|
||||
item = project_tree.get_selected()
|
||||
1:
|
||||
item = current_tree.get_selected()
|
||||
if item.get_metadata(0) is Todo:
|
||||
var todo : Todo = item.get_metadata(0)
|
||||
call_deferred("go_to_script", todo.script_path, todo.line_number)
|
||||
else:
|
||||
var todo_item = item.get_metadata(0)
|
||||
call_deferred("go_to_script", todo_item.script_path)
|
||||
|
||||
func _on_FullPathCheckBox_toggled(button_pressed: bool) -> void:
|
||||
full_path = button_pressed
|
||||
|
||||
func _on_ScriptColourPickerButton_color_changed(color: Color) -> void:
|
||||
script_colour = color
|
||||
|
||||
func _on_RescanButton_pressed() -> void:
|
||||
plugin.rescan_files(true)
|
||||
|
||||
func change_colour(colour: Color, index: int) -> void:
|
||||
patterns[index][1] = colour
|
||||
|
||||
func change_pattern(value: String, index: int, this_colour: Node) -> void:
|
||||
patterns[index][0] = value
|
||||
this_colour.title = value
|
||||
plugin.rescan_files(true)
|
||||
|
||||
func remove_pattern(index: int, this: Node, this_colour: Node) -> void:
|
||||
patterns.remove_at(index)
|
||||
this.queue_free()
|
||||
this_colour.queue_free()
|
||||
plugin.rescan_files(true)
|
||||
|
||||
func case_sensitive_pattern(active: bool, index: int) -> void:
|
||||
if active:
|
||||
patterns[index][2] = CASE_SENSITIVE
|
||||
else:
|
||||
patterns[index][2] = CASE_INSENSITIVE
|
||||
plugin.rescan_files(true)
|
||||
|
||||
func _on_DefaultButton_pressed() -> void:
|
||||
patterns = DEFAULT_PATTERNS.duplicate(true)
|
||||
_sort_alphabetical = DEFAULT_SORT
|
||||
script_colour = DEFAULT_SCRIPT_COLOUR
|
||||
full_path = DEFAULT_SCRIPT_NAME
|
||||
rebuild_settings()
|
||||
plugin.rescan_files(true)
|
||||
|
||||
func _on_AlphSortCheckBox_toggled(button_pressed: bool) -> void:
|
||||
_sort_alphabetical = button_pressed
|
||||
plugin.rescan_files(true)
|
||||
|
||||
func _on_AddPatternButton_pressed() -> void:
|
||||
patterns.append(["\\bplaceholder\\b", Color.WHITE, CASE_INSENSITIVE])
|
||||
rebuild_settings()
|
||||
|
||||
func _on_RefreshCheckButton_toggled(button_pressed: bool) -> void:
|
||||
auto_refresh = button_pressed
|
||||
|
||||
func _on_Timer_timeout() -> void:
|
||||
plugin.refresh_lock = false
|
||||
|
||||
func _on_ignore_paths_changed(new_text: String) -> void:
|
||||
var text = ignore_textbox.text
|
||||
var split: Array = text.split(',')
|
||||
ignore_paths.clear()
|
||||
for elem in split:
|
||||
if elem == " " || elem == "":
|
||||
continue
|
||||
ignore_paths.push_front(elem.lstrip(' ').rstrip(' '))
|
||||
# validate so no empty string slips through (all paths ignored)
|
||||
var i := 0
|
||||
for path in ignore_paths:
|
||||
if (path == "" || path == " "):
|
||||
ignore_paths.remove_at(i)
|
||||
i += 1
|
||||
plugin.rescan_files(true)
|
||||
|
||||
func _on_TabContainer_tab_changed(tab: int) -> void:
|
||||
build_tree()
|
||||
|
||||
func _on_BuiltInCheckButton_toggled(button_pressed: bool) -> void:
|
||||
builtin_enabled = button_pressed
|
||||
plugin.rescan_files(true)
|
||||
19
addons/Todo_Manager/Pattern.gd
Normal file
@ -0,0 +1,19 @@
|
||||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
var text : String : set = set_text
|
||||
var disabled : bool
|
||||
var index : int
|
||||
|
||||
@onready var line_edit := $LineEdit as LineEdit
|
||||
@onready var remove_button := $RemoveButton as Button
|
||||
@onready var case_checkbox := %CaseSensativeCheckbox as CheckBox
|
||||
|
||||
func _ready() -> void:
|
||||
line_edit.text = text
|
||||
remove_button.disabled = disabled
|
||||
|
||||
func set_text(value: String) -> void:
|
||||
text = value
|
||||
if line_edit:
|
||||
line_edit.text = value
|
||||
73
addons/Todo_Manager/Project.gd
Normal file
@ -0,0 +1,73 @@
|
||||
@tool
|
||||
extends Panel
|
||||
|
||||
signal tree_built # used for debugging
|
||||
|
||||
const Todo := preload("res://addons/Todo_Manager/todo_class.gd")
|
||||
|
||||
var _sort_alphabetical := true
|
||||
var _full_path := false
|
||||
|
||||
@onready var tree := $Tree as Tree
|
||||
|
||||
func build_tree(todo_items : Array, ignore_paths : Array, patterns : Array, cased_patterns: Array[String], sort_alphabetical : bool, full_path : bool) -> void:
|
||||
_full_path = full_path
|
||||
tree.clear()
|
||||
if sort_alphabetical:
|
||||
todo_items.sort_custom(Callable(self, "sort_alphabetical"))
|
||||
else:
|
||||
todo_items.sort_custom(Callable(self, "sort_backwards"))
|
||||
var root := tree.create_item()
|
||||
root.set_text(0, "Scripts")
|
||||
for todo_item in todo_items:
|
||||
var ignore := false
|
||||
for ignore_path in ignore_paths:
|
||||
var script_path : String = todo_item.script_path
|
||||
if script_path.begins_with(ignore_path) or script_path.begins_with("res://" + ignore_path) or script_path.begins_with("res:///" + ignore_path):
|
||||
ignore = true
|
||||
break
|
||||
if ignore:
|
||||
continue
|
||||
var script := tree.create_item(root)
|
||||
if full_path:
|
||||
script.set_text(0, todo_item.script_path + " -------")
|
||||
else:
|
||||
script.set_text(0, todo_item.get_short_path() + " -------")
|
||||
script.set_metadata(0, todo_item)
|
||||
for todo in todo_item.todos:
|
||||
var item := tree.create_item(script)
|
||||
var content_header : String = todo.content
|
||||
if "\n" in todo.content:
|
||||
content_header = content_header.split("\n")[0] + "..."
|
||||
item.set_text(0, "(%0) - %1".format([todo.line_number, content_header], "%_"))
|
||||
item.set_tooltip_text(0, todo.content)
|
||||
item.set_metadata(0, todo)
|
||||
for i in range(0, len(cased_patterns)):
|
||||
if cased_patterns[i] == todo.pattern:
|
||||
item.set_custom_color(0, patterns[i][1])
|
||||
emit_signal("tree_built")
|
||||
|
||||
|
||||
func sort_alphabetical(a, b) -> bool:
|
||||
if _full_path:
|
||||
if a.script_path < b.script_path:
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
else:
|
||||
if a.get_short_path() < b.get_short_path():
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
func sort_backwards(a, b) -> bool:
|
||||
if _full_path:
|
||||
if a.script_path > b.script_path:
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
else:
|
||||
if a.get_short_path() > b.get_short_path():
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
21
addons/Todo_Manager/UI/ColourPicker.tscn
Normal file
@ -0,0 +1,21 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bie1xn8v1kd66"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/Todo_Manager/ColourPicker.gd" id="1"]
|
||||
|
||||
[node name="TODOColour" type="HBoxContainer"]
|
||||
offset_right = 105.0
|
||||
offset_bottom = 31.0
|
||||
script = ExtResource("1")
|
||||
metadata/_edit_use_custom_anchors = false
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
offset_top = 4.0
|
||||
offset_right = 1.0
|
||||
offset_bottom = 27.0
|
||||
|
||||
[node name="TODOColourPickerButton" type="ColorPickerButton" parent="."]
|
||||
custom_minimum_size = Vector2(40, 0)
|
||||
offset_left = 65.0
|
||||
offset_right = 105.0
|
||||
offset_bottom = 31.0
|
||||
size_flags_horizontal = 10
|
||||
315
addons/Todo_Manager/UI/Dock.tscn
Normal file
@ -0,0 +1,315 @@
|
||||
[gd_scene load_steps=6 format=3 uid="uid://b6k0dtftankcx"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/Todo_Manager/Dock.gd" id="1"]
|
||||
[ext_resource type="Script" path="res://addons/Todo_Manager/Project.gd" id="2"]
|
||||
[ext_resource type="Script" path="res://addons/Todo_Manager/Current.gd" id="3"]
|
||||
|
||||
[sub_resource type="ButtonGroup" id="ButtonGroup_kqxcu"]
|
||||
|
||||
[sub_resource type="ButtonGroup" id="ButtonGroup_kltg3"]
|
||||
|
||||
[node name="Dock" type="Control"]
|
||||
custom_minimum_size = Vector2(0, 200)
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = 4.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
metadata/_edit_layout_mode = 1
|
||||
|
||||
[node name="Header" type="HBoxContainer" parent="VBoxContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HeaderLeft" type="HBoxContainer" parent="VBoxContainer/Header"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Title" type="Label" parent="VBoxContainer/Header/HeaderLeft"]
|
||||
layout_mode = 2
|
||||
text = "Todo Dock:"
|
||||
|
||||
[node name="HeaderRight" type="HBoxContainer" parent="VBoxContainer/Header"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
alignment = 2
|
||||
|
||||
[node name="SettingsButton" type="Button" parent="VBoxContainer/Header/HeaderRight"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
text = "Settings"
|
||||
|
||||
[node name="TabContainer" type="TabContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Project" type="Panel" parent="VBoxContainer/TabContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("2")
|
||||
|
||||
[node name="Tree" type="Tree" parent="VBoxContainer/TabContainer/Project"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
hide_root = true
|
||||
|
||||
[node name="Current" type="Panel" parent="VBoxContainer/TabContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("3")
|
||||
|
||||
[node name="Tree" type="Tree" parent="VBoxContainer/TabContainer/Current"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
hide_folding = true
|
||||
hide_root = true
|
||||
|
||||
[node name="Settings" type="Panel" parent="VBoxContainer/TabContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/TabContainer/Settings"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Scripts" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/Scripts"]
|
||||
layout_mode = 2
|
||||
text = "Scripts:"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/Scripts"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 5
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Scripts" type="VBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ScriptName" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptName"]
|
||||
layout_mode = 2
|
||||
text = "Script Name:"
|
||||
|
||||
[node name="FullPathCheckBox" type="CheckBox" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptName"]
|
||||
layout_mode = 2
|
||||
button_group = SubResource("ButtonGroup_kqxcu")
|
||||
text = "Full path"
|
||||
|
||||
[node name="ShortNameCheckBox" type="CheckBox" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptName"]
|
||||
layout_mode = 2
|
||||
button_pressed = true
|
||||
button_group = SubResource("ButtonGroup_kqxcu")
|
||||
text = "Short name"
|
||||
|
||||
[node name="ScriptSort" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptSort"]
|
||||
layout_mode = 2
|
||||
text = "Sort Order:"
|
||||
|
||||
[node name="AlphSortCheckBox" type="CheckBox" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptSort"]
|
||||
layout_mode = 2
|
||||
button_pressed = true
|
||||
button_group = SubResource("ButtonGroup_kltg3")
|
||||
text = "Alphabetical"
|
||||
|
||||
[node name="RAlphSortCheckBox" type="CheckBox" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptSort"]
|
||||
layout_mode = 2
|
||||
button_group = SubResource("ButtonGroup_kltg3")
|
||||
text = "Reverse Alphabetical"
|
||||
|
||||
[node name="ScriptColour" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptColour"]
|
||||
layout_mode = 2
|
||||
text = "Script Colour:"
|
||||
|
||||
[node name="ScriptColourPickerButton" type="ColorPickerButton" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptColour"]
|
||||
custom_minimum_size = Vector2(40, 0)
|
||||
layout_mode = 2
|
||||
color = Color(0.8, 0.807843, 0.827451, 1)
|
||||
|
||||
[node name="IgnorePaths" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/IgnorePaths"]
|
||||
layout_mode = 2
|
||||
text = "Ignore Paths:"
|
||||
|
||||
[node name="TextEdit" type="LineEdit" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/IgnorePaths"]
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
expand_to_text_length = true
|
||||
|
||||
[node name="Label3" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/IgnorePaths"]
|
||||
layout_mode = 2
|
||||
text = "(Separated by commas)"
|
||||
|
||||
[node name="TODOColours" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/TODOColours"]
|
||||
layout_mode = 2
|
||||
text = "TODO Colours:"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/TODOColours"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="HBoxContainer3" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer3"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Colours" type="VBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer3"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Patterns" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/Patterns"]
|
||||
layout_mode = 2
|
||||
text = "Patterns:"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/Patterns"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="HBoxContainer4" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Patterns" type="VBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="AddPatternButton" type="Button" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4/Patterns"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
text = "Add"
|
||||
|
||||
[node name="Config" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/Config"]
|
||||
layout_mode = 2
|
||||
text = "Config:"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/Config"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="HBoxContainer5" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Patterns" type="VBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RefreshCheckButton" type="CheckButton" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
button_pressed = true
|
||||
text = "Auto Refresh"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="BuiltInCheckButton" type="CheckButton" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Scan Built-in Scripts"
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="DefaultButton" type="Button" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
text = "Reset to default"
|
||||
|
||||
[node name="Timer" type="Timer" parent="."]
|
||||
one_shot = true
|
||||
|
||||
[node name="RescanButton" type="Button" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -102.0
|
||||
offset_top = 3.0
|
||||
offset_bottom = 34.0
|
||||
grow_horizontal = 0
|
||||
text = "Rescan Files"
|
||||
flat = true
|
||||
|
||||
[connection signal="toggled" from="VBoxContainer/Header/HeaderRight/SettingsButton" to="." method="_on_SettingsButton_toggled"]
|
||||
[connection signal="tab_changed" from="VBoxContainer/TabContainer" to="." method="_on_TabContainer_tab_changed"]
|
||||
[connection signal="item_activated" from="VBoxContainer/TabContainer/Project/Tree" to="." method="_on_Tree_item_activated"]
|
||||
[connection signal="item_activated" from="VBoxContainer/TabContainer/Current/Tree" to="." method="_on_Tree_item_activated"]
|
||||
[connection signal="toggled" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptName/FullPathCheckBox" to="." method="_on_FullPathCheckBox_toggled"]
|
||||
[connection signal="toggled" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptSort/AlphSortCheckBox" to="." method="_on_AlphSortCheckBox_toggled"]
|
||||
[connection signal="color_changed" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptColour/ScriptColourPickerButton" to="." method="_on_ScriptColourPickerButton_color_changed"]
|
||||
[connection signal="pressed" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4/Patterns/AddPatternButton" to="." method="_on_AddPatternButton_pressed"]
|
||||
[connection signal="toggled" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns/RefreshCheckButton" to="." method="_on_RefreshCheckButton_toggled"]
|
||||
[connection signal="toggled" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns/HBoxContainer/BuiltInCheckButton" to="." method="_on_BuiltInCheckButton_toggled"]
|
||||
[connection signal="pressed" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns/DefaultButton" to="." method="_on_DefaultButton_pressed"]
|
||||
[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"]
|
||||
[connection signal="pressed" from="RescanButton" to="." method="_on_RescanButton_pressed"]
|
||||
26
addons/Todo_Manager/UI/Pattern.tscn
Normal file
@ -0,0 +1,26 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bx11sel2q5wli"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/Todo_Manager/Pattern.gd" id="1"]
|
||||
|
||||
[node name="Pattern" type="HBoxContainer"]
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
expand_to_text_length = true
|
||||
|
||||
[node name="RemoveButton" type="Button" parent="."]
|
||||
layout_mode = 2
|
||||
text = "-"
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
custom_minimum_size = Vector2(20, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
|
||||
[node name="CaseSensativeCheckbox" type="CheckBox" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
text = "Case Sensitive"
|
||||
7
addons/Todo_Manager/doc/example.gd
Normal file
@ -0,0 +1,7 @@
|
||||
extends Node
|
||||
|
||||
# TODO: this is a TODO
|
||||
# HACK: this is a HACK
|
||||
# FIXME: this is a FIXME
|
||||
# TODO this works too
|
||||
#Hack any format will do
|
||||
BIN
addons/Todo_Manager/doc/images/Instruct1.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
34
addons/Todo_Manager/doc/images/Instruct1.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dx5m1i244ub28"
|
||||
path="res://.godot/imported/Instruct1.png-698c6faa3ef3ac4960807faa3e028bd6.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Todo_Manager/doc/images/Instruct1.png"
|
||||
dest_files=["res://.godot/imported/Instruct1.png-698c6faa3ef3ac4960807faa3e028bd6.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
BIN
addons/Todo_Manager/doc/images/Instruct2.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
34
addons/Todo_Manager/doc/images/Instruct2.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://kaiwqtyohu0e"
|
||||
path="res://.godot/imported/Instruct2.png-4e8664e49d00a397bbd539f7dee976ff.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Todo_Manager/doc/images/Instruct2.png"
|
||||
dest_files=["res://.godot/imported/Instruct2.png-4e8664e49d00a397bbd539f7dee976ff.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
BIN
addons/Todo_Manager/doc/images/Instruct3.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
34
addons/Todo_Manager/doc/images/Instruct3.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dy2822d83t0aa"
|
||||
path="res://.godot/imported/Instruct3.png-3cc62ed99bf29d90b803cb8eb40881e9.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Todo_Manager/doc/images/Instruct3.png"
|
||||
dest_files=["res://.godot/imported/Instruct3.png-3cc62ed99bf29d90b803cb8eb40881e9.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
BIN
addons/Todo_Manager/doc/images/Instruct4.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
34
addons/Todo_Manager/doc/images/Instruct4.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bvf32pbtep5eq"
|
||||
path="res://.godot/imported/Instruct4.png-bf5aa1cffc066175cecf9281b0774809.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Todo_Manager/doc/images/Instruct4.png"
|
||||
dest_files=["res://.godot/imported/Instruct4.png-bf5aa1cffc066175cecf9281b0774809.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
BIN
addons/Todo_Manager/doc/images/Instruct5.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
34
addons/Todo_Manager/doc/images/Instruct5.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ddoxpi6ium7hq"
|
||||
path="res://.godot/imported/Instruct5.png-001538ed8b5682dcf232de08035aab38.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Todo_Manager/doc/images/Instruct5.png"
|
||||
dest_files=["res://.godot/imported/Instruct5.png-001538ed8b5682dcf232de08035aab38.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
BIN
addons/Todo_Manager/doc/images/TODO_Manager_Logo.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
34
addons/Todo_Manager/doc/images/TODO_Manager_Logo.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://vi5mj4m4i70i"
|
||||
path="res://.godot/imported/TODO_Manager_Logo.png-e07d7ec75201c66b732ef87ec1bece15.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Todo_Manager/doc/images/TODO_Manager_Logo.png"
|
||||
dest_files=["res://.godot/imported/TODO_Manager_Logo.png-e07d7ec75201c66b732ef87ec1bece15.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
BIN
addons/Todo_Manager/doc/images/TodoExternal.gif
Normal file
|
After Width: | Height: | Size: 243 KiB |
BIN
addons/Todo_Manager/doc/images/example1.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
34
addons/Todo_Manager/doc/images/example1.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dm4238rk6sken"
|
||||
path="res://.godot/imported/example1.png-6386c332ca46e1e62ea061b956a901cd.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Todo_Manager/doc/images/example1.png"
|
||||
dest_files=["res://.godot/imported/example1.png-6386c332ca46e1e62ea061b956a901cd.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
BIN
addons/Todo_Manager/doc/images/example2.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
34
addons/Todo_Manager/doc/images/example2.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://csmkq6165b5yj"
|
||||
path="res://.godot/imported/example2.png-2e3a8f9cd1e178daf22b83dc0513f37a.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Todo_Manager/doc/images/example2.png"
|
||||
dest_files=["res://.godot/imported/example2.png-2e3a8f9cd1e178daf22b83dc0513f37a.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
7
addons/Todo_Manager/plugin.cfg
Normal file
@ -0,0 +1,7 @@
|
||||
[plugin]
|
||||
|
||||
name="Todo Manager"
|
||||
description="Dock for housing TODO messages."
|
||||
author="Peter de Vroom"
|
||||
version="2.3.1"
|
||||
script="plugin.gd"
|
||||
286
addons/Todo_Manager/plugin.gd
Normal file
@ -0,0 +1,286 @@
|
||||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
const DockScene := preload("res://addons/Todo_Manager/UI/Dock.tscn")
|
||||
const Dock := preload("res://addons/Todo_Manager/Dock.gd")
|
||||
const Todo := preload("res://addons/Todo_Manager/todo_class.gd")
|
||||
const TodoItem := preload("res://addons/Todo_Manager/todoItem_class.gd")
|
||||
|
||||
var _dockUI : Dock
|
||||
|
||||
class TodoCacheValue:
|
||||
var todos: Array
|
||||
var last_modified_time: int
|
||||
|
||||
func _init(todos: Array, last_modified_time: int):
|
||||
self.todos = todos
|
||||
self.last_modified_time = last_modified_time
|
||||
|
||||
var todo_cache : Dictionary # { key: script_path, value: TodoCacheValue }
|
||||
var remove_queue : Array
|
||||
var combined_pattern : String
|
||||
var cased_patterns : Array[String]
|
||||
|
||||
var refresh_lock := false # makes sure _on_filesystem_changed only triggers once
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
_dockUI = DockScene.instantiate() as Control
|
||||
add_control_to_bottom_panel(_dockUI, "TODO")
|
||||
get_editor_interface().get_resource_filesystem().connect("filesystem_changed",
|
||||
_on_filesystem_changed)
|
||||
get_editor_interface().get_file_system_dock().connect("file_removed", queue_remove)
|
||||
get_editor_interface().get_script_editor().connect("editor_script_changed",
|
||||
_on_active_script_changed)
|
||||
_dockUI.plugin = self
|
||||
|
||||
combined_pattern = combine_patterns(_dockUI.patterns)
|
||||
find_tokens_from_path(find_scripts())
|
||||
_dockUI.build_tree()
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
_dockUI.create_config_file()
|
||||
remove_control_from_bottom_panel(_dockUI)
|
||||
_dockUI.free()
|
||||
|
||||
|
||||
func queue_remove(file: String):
|
||||
for i in _dockUI.todo_items.size() - 1:
|
||||
if _dockUI.todo_items[i].script_path == file:
|
||||
_dockUI.todo_items.remove_at(i)
|
||||
|
||||
|
||||
func find_tokens_from_path(scripts: Array[String]) -> void:
|
||||
for script_path in scripts:
|
||||
var file := FileAccess.open(script_path, FileAccess.READ)
|
||||
var contents := file.get_as_text()
|
||||
if script_path.ends_with(".tscn"):
|
||||
handle_built_in_scripts(contents, script_path)
|
||||
else:
|
||||
find_tokens(contents, script_path)
|
||||
|
||||
|
||||
func handle_built_in_scripts(contents: String, resource_path: String):
|
||||
var s := contents.split("sub_resource type=\"GDScript\"")
|
||||
if s.size() <= 1:
|
||||
return
|
||||
for i in range(1, s.size()):
|
||||
var script_components := s[i].split("script/source")
|
||||
var script_name = script_components[0].substr(5, 14)
|
||||
find_tokens(script_components[1], resource_path + "::" + script_name)
|
||||
|
||||
|
||||
func find_tokens(text: String, script_path: String) -> void:
|
||||
var cached_todos = get_cached_todos(script_path)
|
||||
if cached_todos.size() != 0:
|
||||
# var i := 0
|
||||
# for todo_item in _dockUI.todo_items:
|
||||
# if todo_item.script_path == script_path:
|
||||
# _dockUI.todo_items.remove_at(i)
|
||||
# i += 1
|
||||
var todo_item := TodoItem.new(script_path, cached_todos)
|
||||
_dockUI.todo_items.append(todo_item)
|
||||
else:
|
||||
var regex = RegEx.new()
|
||||
# if regex.compile("#\\s*\\bTODO\\b.*|#\\s*\\bHACK\\b.*") == OK:
|
||||
if regex.compile(combined_pattern) == OK:
|
||||
var result : Array[RegExMatch] = regex.search_all(text)
|
||||
if result.is_empty():
|
||||
for i in _dockUI.todo_items.size():
|
||||
if _dockUI.todo_items[i].script_path == script_path:
|
||||
_dockUI.todo_items.remove_at(i)
|
||||
return # No tokens found
|
||||
var match_found : bool
|
||||
var i := 0
|
||||
for todo_item in _dockUI.todo_items:
|
||||
if todo_item.script_path == script_path:
|
||||
match_found = true
|
||||
var updated_todo_item := update_todo_item(todo_item, result, text, script_path)
|
||||
_dockUI.todo_items.remove_at(i)
|
||||
_dockUI.todo_items.insert(i, updated_todo_item)
|
||||
break
|
||||
i += 1
|
||||
if !match_found:
|
||||
_dockUI.todo_items.append(create_todo_item(result, text, script_path))
|
||||
|
||||
|
||||
func create_todo_item(regex_results: Array[RegExMatch], text: String, script_path: String) -> TodoItem:
|
||||
var todo_item = TodoItem.new(script_path, [])
|
||||
todo_item.script_path = script_path
|
||||
var last_line_number := 0
|
||||
var lines := text.split("\n")
|
||||
for r in regex_results:
|
||||
var new_todo : Todo = create_todo(r.get_string(), script_path)
|
||||
new_todo.line_number = get_line_number(r.get_string(), text, last_line_number)
|
||||
# GD Multiline comment
|
||||
var trailing_line := new_todo.line_number
|
||||
var should_break = false
|
||||
while trailing_line < lines.size() and lines[trailing_line].dedent().begins_with("#"):
|
||||
for other_r in regex_results:
|
||||
if lines[trailing_line] in other_r.get_string():
|
||||
should_break = true
|
||||
break
|
||||
if should_break:
|
||||
break
|
||||
|
||||
new_todo.content += "\n" + lines[trailing_line]
|
||||
trailing_line += 1
|
||||
|
||||
last_line_number = new_todo.line_number
|
||||
todo_item.todos.append(new_todo)
|
||||
cache_todos(todo_item.todos, script_path)
|
||||
return todo_item
|
||||
|
||||
|
||||
func update_todo_item(todo_item: TodoItem, regex_results: Array[RegExMatch], text: String, script_path: String) -> TodoItem:
|
||||
todo_item.todos.clear()
|
||||
var lines := text.split("\n")
|
||||
for r in regex_results:
|
||||
var new_todo : Todo = create_todo(r.get_string(), script_path)
|
||||
new_todo.line_number = get_line_number(r.get_string(), text)
|
||||
# GD Multiline comment
|
||||
var trailing_line := new_todo.line_number
|
||||
var should_break = false
|
||||
while trailing_line < lines.size() and lines[trailing_line].dedent().begins_with("#"):
|
||||
for other_r in regex_results:
|
||||
if lines[trailing_line] in other_r.get_string():
|
||||
should_break = true
|
||||
break
|
||||
if should_break:
|
||||
break
|
||||
|
||||
new_todo.content += "\n" + lines[trailing_line]
|
||||
trailing_line += 1
|
||||
todo_item.todos.append(new_todo)
|
||||
return todo_item
|
||||
|
||||
|
||||
func get_line_number(what: String, from: String, start := 0) -> int:
|
||||
what = what.split('\n')[0] # Match first line of multiline C# comments
|
||||
var temp_array := from.split('\n')
|
||||
var lines := Array(temp_array)
|
||||
var line_number# = lines.find(what) + 1
|
||||
for i in range(start, lines.size()):
|
||||
if what in lines[i]:
|
||||
line_number = i + 1 # +1 to account of 0-based array vs 1-based line numbers
|
||||
break
|
||||
else:
|
||||
line_number = 0 # This is an error
|
||||
return line_number
|
||||
|
||||
|
||||
func _on_filesystem_changed() -> void:
|
||||
if !refresh_lock:
|
||||
if _dockUI.auto_refresh:
|
||||
refresh_lock = true
|
||||
_dockUI.get_node("Timer").start()
|
||||
rescan_files(false)
|
||||
|
||||
|
||||
func find_scripts() -> Array[String]:
|
||||
var scripts : Array[String]
|
||||
var directory_queue : Array[String]
|
||||
var dir := DirAccess.open("res://")
|
||||
if dir.get_open_error() == OK:
|
||||
get_dir_contents(dir, scripts, directory_queue)
|
||||
else:
|
||||
printerr("TODO_Manager: There was an error during find_scripts()")
|
||||
|
||||
while not directory_queue.is_empty():
|
||||
if dir.change_dir(directory_queue[0]) == OK:
|
||||
get_dir_contents(dir, scripts, directory_queue)
|
||||
else:
|
||||
printerr("TODO_Manager: There was an error at: " + directory_queue[0])
|
||||
directory_queue.pop_front()
|
||||
|
||||
return scripts
|
||||
|
||||
|
||||
func cache_todos(todos: Array, script_path: String) -> void:
|
||||
var last_modified_time = FileAccess.get_modified_time(script_path)
|
||||
todo_cache[script_path] = TodoCacheValue.new(todos, last_modified_time)
|
||||
|
||||
|
||||
func get_cached_todos(script_path: String) -> Array:
|
||||
if todo_cache.has(script_path) and !script_path.contains("tscn::"):
|
||||
var cached_value: TodoCacheValue = todo_cache[script_path]
|
||||
if cached_value.last_modified_time == FileAccess.get_modified_time(script_path):
|
||||
|
||||
return cached_value.todos
|
||||
return []
|
||||
|
||||
func get_dir_contents(dir: DirAccess, scripts: Array[String], directory_queue: Array[String]) -> void:
|
||||
dir.include_navigational = false
|
||||
dir.include_hidden = false
|
||||
dir.list_dir_begin()
|
||||
var file_name : String = dir.get_next()
|
||||
|
||||
while file_name != "":
|
||||
if dir.current_is_dir():
|
||||
if file_name == ".import" or file_name == ".mono": # Skip .import folder which should never have scripts
|
||||
pass
|
||||
else:
|
||||
directory_queue.append(dir.get_current_dir().path_join(file_name))
|
||||
else:
|
||||
if file_name.ends_with(".gd") or file_name.ends_with(".cs") \
|
||||
or file_name.ends_with(".c") or file_name.ends_with(".cpp") or file_name.ends_with(".h") \
|
||||
or ((file_name.ends_with(".tscn") and _dockUI.builtin_enabled)):
|
||||
scripts.append(dir.get_current_dir().path_join(file_name))
|
||||
file_name = dir.get_next()
|
||||
|
||||
|
||||
func rescan_files(clear_cache: bool) -> void:
|
||||
_dockUI.todo_items.clear()
|
||||
if clear_cache:
|
||||
todo_cache.clear()
|
||||
combined_pattern = combine_patterns(_dockUI.patterns)
|
||||
find_tokens_from_path(find_scripts())
|
||||
_dockUI.build_tree()
|
||||
|
||||
|
||||
func combine_patterns(patterns: Array) -> String:
|
||||
# Case Sensitivity
|
||||
cased_patterns = []
|
||||
for pattern in patterns:
|
||||
if pattern[2] == _dockUI.CASE_INSENSITIVE:
|
||||
cased_patterns.append(pattern[0].insert(0, "((?i)") + ")")
|
||||
else:
|
||||
cased_patterns.append("(" + pattern[0] + ")")
|
||||
|
||||
if patterns.size() == 1:
|
||||
return cased_patterns[0]
|
||||
else:
|
||||
var pattern_string := "((\\/\\*)|(#|\\/\\/))\\s*("
|
||||
for i in range(patterns.size()):
|
||||
if i == 0:
|
||||
pattern_string += cased_patterns[i]
|
||||
else:
|
||||
pattern_string += "|" + cased_patterns[i]
|
||||
pattern_string += ")(?(2)[\\s\\S]*?\\*\\/|.*)"
|
||||
return pattern_string
|
||||
|
||||
|
||||
func create_todo(todo_string: String, script_path: String) -> Todo:
|
||||
var todo := Todo.new()
|
||||
var regex = RegEx.new()
|
||||
for pattern in cased_patterns:
|
||||
if regex.compile(pattern) == OK:
|
||||
var result : RegExMatch = regex.search(todo_string)
|
||||
if result:
|
||||
todo.pattern = pattern
|
||||
todo.title = result.strings[0]
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
printerr("Error compiling " + pattern)
|
||||
|
||||
todo.content = todo_string
|
||||
todo.script_path = script_path
|
||||
return todo
|
||||
|
||||
|
||||
func _on_active_script_changed(script) -> void:
|
||||
if _dockUI:
|
||||
if _dockUI.tabs.current_tab == 1:
|
||||
_dockUI.build_tree()
|
||||
15
addons/Todo_Manager/todo.cfg
Normal file
@ -0,0 +1,15 @@
|
||||
[scripts]
|
||||
|
||||
full_path=false
|
||||
sort_alphabetical=true
|
||||
script_colour=Color(0.8, 0.807843, 0.827451, 1)
|
||||
ignore_paths=Array[String]([])
|
||||
|
||||
[patterns]
|
||||
|
||||
patterns=[["\\bTODO\\b", Color(0.588235, 0.945098, 0.678431, 1), 0], ["\\bHACK\\b", Color(0.835294, 0.737255, 0.439216, 1), 0], ["\\bFIXME\\b", Color(0.835294, 0.439216, 0.439216, 1), 0]]
|
||||
|
||||
[config]
|
||||
|
||||
auto_refresh=true
|
||||
builtin_enabled=false
|
||||
18
addons/Todo_Manager/todoItem_class.gd
Normal file
@ -0,0 +1,18 @@
|
||||
@tool
|
||||
extends RefCounted
|
||||
|
||||
var script_path : String
|
||||
var todos : Array
|
||||
|
||||
func _init(script_path: String, todos: Array):
|
||||
self.script_path = script_path
|
||||
self.todos = todos
|
||||
|
||||
func get_short_path() -> String:
|
||||
var temp_array := script_path.rsplit('/', false, 1)
|
||||
var short_path : String
|
||||
if not temp_array.size() > 1:
|
||||
short_path = "(!)" + temp_array[0]
|
||||
else:
|
||||
short_path = temp_array[1]
|
||||
return short_path
|
||||
9
addons/Todo_Manager/todo_class.gd
Normal file
@ -0,0 +1,9 @@
|
||||
@tool
|
||||
extends RefCounted
|
||||
|
||||
|
||||
var pattern : String
|
||||
var title : String
|
||||
var content : String
|
||||
var script_path : String
|
||||
var line_number : int
|
||||
21
addons/reactivex/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Neroware
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
422
addons/reactivex/README.md
Normal file
@ -0,0 +1,422 @@
|
||||
# GodotRx - Reactive Extensions for the Godot Game Engine version 4 (GDRx)
|
||||
|
||||
(For the native version,
|
||||
go to https://github.com/Neroware/NativeGodotRx)
|
||||
|
||||
## Warning
|
||||
**Untested** While it is almost a direct port of RxPY, this library has not
|
||||
yet been fully battle tested in action. Proceed with caution! Test submissions
|
||||
and bug reports are welcome!
|
||||
|
||||
## What is GodotRx?
|
||||
GodotRx (short: GDRx) is a full implementation of ReactiveX for the Godot Game
|
||||
Engine 4. The code was originally ported from RxPY (see:
|
||||
https://github.com/ReactiveX/RxPY) as Python shares a lot of similarities with
|
||||
GDScript.
|
||||
|
||||
**Why Rx?** ReactiveX allows a more declarative programming style working on
|
||||
observable data streams. It encourages high cohesion
|
||||
and low coupling rendering the code more easily readable and extendable.
|
||||
|
||||
The Godot Engine offers a robust event system in form of signals and a seamless implementation of coroutines, making it easy to execute asynchronous code. This allows you to run code outside of the typical sequential order, which is essential for handling complex tasks like user inputs, network responses, and animations.
|
||||
|
||||
In this context, an observer listens to an observable event, which triggers when something significant occurs in the program, leading to side effects in the connected instances. For example, this could be a player attacking an enemy on button press or picking up an item when a collision is detected.
|
||||
|
||||
GDRx enhances this idea by converting all forms of data within the program, such as GD-signals, GD-lifecycle events, callbacks, data structures, coroutines, etc., into observable data streams that emit items. These data streams, known as 'Observables', are immutable and can be transformed using functional programming techniques to describe more complex behavior. (Say hello to Flat-Map, Filter, Reduce, and their functional friends!)
|
||||
|
||||
## Installation
|
||||
You can add GDRx to your Godot 4 project as followed:
|
||||
|
||||
1. Download this repository as an archive.
|
||||
2. Navigate to your project's root folder.
|
||||
3. Extract GDRx into your project's `addons` directory. The path needs to be `res://addons/reactivex/`.
|
||||
4. Ensure that the plugin is enabled.
|
||||
5. Add the singleton script at `res://addons/reactivex/__gdrxsingleton__.gd` to autoload as `GDRx`.
|
||||
6. GDRx should now be ready to use. Try creating a simple Observable using:
|
||||
|
||||
```swift
|
||||
GDRx.just(42).subscribe(func(i): print("The answer: " + str(i)))
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
GDRx is a full implementation of the Observer design pattern combined with the Iterator design pattern.
|
||||
The following classes are featured:
|
||||
|
||||
**Observer:** An observer is an entity that listens to an observable sequence. In standard GDScript, you would connect a `Callable` to a `Signal` creating an implicit observer that receives a notification from the signal and forwards it to the corresponding callback. In GDRx, each observer follows a strict contract consisting of the three notifications `on_next(item)`, `on_error(error)` and `on_completed()`.
|
||||
|
||||
**Observable:** An observable is an entity that can be subscribed to by an observer. This is achieved through the subscription-operator, a method with the signature `subscribe(on_next, on_error, on_completed)`. Whenever an observer subscribes to the observables via the subscription-method it receives notifications from the observer-observable-contract over the course of the observer's active subscription.
|
||||
|
||||
|
||||
**Disposable:** Disposables are entities that represent subscriptions in GDRx. They are used for clean-up and dispose themselves automatically when they go out of scope or when the method `dispose()` is called explicitly. The subscription-operator returns the new subscription (in Godot you would call it connection) as a disposable. Since disposables delete themselves when going out of scope, their lifetime can be linked to another object's lifetime via the method `dispose_with(obj)`.
|
||||
*Also a huge shoutout to Semickolon (https://github.com/semickolon/GodotRx) for his amazing
|
||||
hack which automatically disposes subscriptions on instance death. Good on ya!*
|
||||
|
||||
**Scheduler:** Schedulers schedule pieces of work (actions) for execution. The most prominent scheduling strategies in GDRx are as followed:
|
||||
|
||||
- *ImmediateScheduler:* Schedules actions to be executed immediately. This would be equivalent to invoking the action directly.
|
||||
- *TrampolineScheduler:* A scheduler with additional protection against recursive scheduling.
|
||||
- *CurrentThreadScheduler:* A TrampolineScheduler that schedules actions on the current thread. This is usually the default scheduling strategy.
|
||||
- *EventLoopScheduler:* Creates a new thread and schedules all actions on it.
|
||||
- *NewThreadScheduler:* Creates a new thread for each scheduled action.
|
||||
- *SceneTreeTimeoutScheduler:* Schedules actions for execution after a timeout has expired. The timer is based on Godot's `SceneTreeTimer`.
|
||||
- *ThreadedTimeoutscheduler:* Schedules actions for execution after a timeout has expired. The timer is run on a separate thread.
|
||||
- *PeriodicScheduler:* Allows periodic scheduling of actions.
|
||||
- *GodotSignalScheduler:* Schedules an action for execution after a `Signal` is received.
|
||||
|
||||
**Iterable:** Iterables are sequences that can be iterated using Godot's `for`-loop. They become relevant when working with observable data streams and can also be infinite.
|
||||
|
||||
**Subject:** A subject implements both observable and observer behavior meaning it can receive notifications and allow other observers to subscribe to it at the same time.
|
||||
|
||||
**ReactiveProperty:** A reactive property is a special form of observable that maintains a value and sends notifications whenever a change to said value occurs. Highly useful!
|
||||
|
||||
**Operator:** An operator is a function taking an observable sequence and transforming it to another. A large set of functional operators can be used to transform observables. **Be careful! I have not tested them all...! Test submissions are welcome!** For more info, also check out the comments in the operator scripts!
|
||||
|
||||
**Throwable:** The item of an `on_error`-notification. Raising an error sends a notification to all observers of the corresponding observable sequence, which terminates the stream ungracefully. This does not work with observables containing coroutines due to Godot's technical limitations!
|
||||
|
||||
*For more information, it is recommended to read the RxPY documentation, which covers all the features of GDRx that are not directly related to the Godot Engine.*
|
||||
|
||||
## Usage
|
||||
|
||||
### Basics
|
||||
|
||||
In GodotRx, an observer listens to an observable sequence. The `GDRx`-singleton contains a selection of constructors:
|
||||
|
||||
```swift
|
||||
var observable = GDRx.from([1, 2, 3, 4])
|
||||
```
|
||||
|
||||
A connection can be established via `Observable.subscribe(...)` as followed:
|
||||
|
||||
```swift
|
||||
var subscription = observable.subscribe(
|
||||
func(i): print("next> ", i),
|
||||
func(e): print("Err> ", e),
|
||||
func(): print("Completed!"))
|
||||
```
|
||||
|
||||
A subscription is automatically disposed whenever it goes out of scope. Do not forget to link subscription lifetime to an object via `Disposable.dispose_with(obj)`:
|
||||
|
||||
```swift
|
||||
GDRx.start_periodic_timer(1.0) \
|
||||
.subscribe(func(i): print("Tick: ", i)) \
|
||||
.dispose_with(self)
|
||||
```
|
||||
|
||||
### Timers
|
||||
|
||||
Timers were already possible either by using the `Timer`-Node or by combining a coroutine with an awaited timeout signal of a `SceneTreeTimer`. For periodic timers, code gets even more convoluted. GDRx drastically simplifies creating timers.
|
||||
|
||||
```swift
|
||||
func _ready():
|
||||
GDRx.start_periodic_timer(1.0) \
|
||||
.subscribe(func(i): print("Periodic: ", i)) \
|
||||
.dispose_with(self)
|
||||
GDRx.start_timer(2.0) \
|
||||
.subscribe(func(i): print("One shot: ", i)) \
|
||||
.dispose_with(self)
|
||||
```
|
||||
|
||||
If you want to schedule a timer running on a separate thread, the
|
||||
ThreadedTimeoutScheduler Singleton allows you to do so. **Careful:** Once the thread
|
||||
is started it will not stop until the interval has passed!
|
||||
|
||||
```swift
|
||||
GDRx.start_timer(3.0, ThreadedTimeoutScheduler.singleton()) \
|
||||
.subscribe(func(i): print("Threaded one shot: ", i)) \
|
||||
.dispose_with(self)
|
||||
GDRx.start_periodic_timer(2.0, ThreadedTimeoutScheduler.singleton()) \
|
||||
.subscribe(func(i): print("Threaded periodic: ", i)) \
|
||||
.dispose_with(self)
|
||||
```
|
||||
|
||||
Additionally, various process and pause modes are possible. I created
|
||||
a list with various versions of the SceneTreeTimeoutScheduler for this. Access
|
||||
them like this:
|
||||
|
||||
```swift
|
||||
Engine.time_scale = 0.5
|
||||
|
||||
var process_always = false
|
||||
var process_in_physics = false
|
||||
var ignore_time_scale = false
|
||||
|
||||
var scheduler = SceneTreeTimeoutScheduler.singleton(
|
||||
process_always, process_in_physics, ignore_time_scale)
|
||||
```
|
||||
|
||||
Note that the default SceneTreeTimeoutScheduler runs at process timestep scaling with
|
||||
`Engine.time_scale` and also considers pause mode.
|
||||
|
||||
|
||||
### Transforming signals
|
||||
|
||||
A very nice feature of GodotRx are signal transformations through observables. Let us take a simple example with two signals. Assume, we only want to execute logic, when both signals are emitted. In Godot, this would require some additional logic in the signal's callbacks. In GDRx, this can be achieved this behavior through observable transformations.
|
||||
|
||||
```swift
|
||||
signal signal_a(a)
|
||||
signal signal_b(b)
|
||||
|
||||
var combined_signal : Observable
|
||||
|
||||
func _ready():
|
||||
combined_signal = GDRx.from_signal(signal_a) \
|
||||
.zip([GDRx.from_signal(signal_b)])
|
||||
```
|
||||
|
||||
Using the `from_signal`-constructor, an observable can be created on top of a signal, which emits items whenever the signal is emitted. Using the `zip`-operator, the resulting observable only emits items, when both signals have been emitted. This even has the advantages that the resulting observable can be passed around like a `Signal` instance as first-class-citizen.
|
||||
|
||||
### Error handling
|
||||
|
||||
GDRx features custom error handling. Raising an error inside an observable sequence causes all observers to be notified with said error. The following is an example of a safe division operation.
|
||||
|
||||
```swift
|
||||
var safe_division = func(a, b):
|
||||
return a / b if b != 0 else DividedByZeroError.raise(-1)
|
||||
var mapped = GDRx.of([6, 2, 1, 0, 2, 1]) \
|
||||
.pairwise() \
|
||||
.map(func(tup : Tuple): return safe_division.call(tup.first, tup.second))
|
||||
```
|
||||
|
||||
This code results in the sequence "3, 2" after which the observers are notified with a "DividedByZeroError" notification terminating the observable.
|
||||
|
||||
**Warning** It is currently technically impossible to get the error handling working with asynchronous GDScript, meaning this will break in scenarios were errors are raised after an `await`-statement. If somebody has a solution to this problem, feel free to send me an E-Mail or answer to issue #20!
|
||||
|
||||
### Coroutines
|
||||
|
||||
In GDRx, sequential execution of coroutines can be managed through observable streams, which helps readability.
|
||||
|
||||
```swift
|
||||
var _reference
|
||||
|
||||
func coroutine1():
|
||||
# ...
|
||||
print("Do something.")
|
||||
# ...
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
# ...
|
||||
print("Do something.")
|
||||
# ...
|
||||
|
||||
func coroutine3():
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
print("Done.")
|
||||
|
||||
func _ready():
|
||||
GDRx.concat_streams([
|
||||
GDRx.from_coroutine(coroutine1),
|
||||
GDRx.if_then(
|
||||
func(): return self._reference != null,
|
||||
GDRx.from_coroutine(func(): await self._reference.coroutine2())
|
||||
),
|
||||
GDRx.from_coroutine(coroutine3),
|
||||
]).subscribe().dispose_with(self)
|
||||
```
|
||||
|
||||
### Type fixation
|
||||
|
||||
GDScript is a fully dynamically typed language. This has many advantages, however,
|
||||
at some point, we might want to fix types of a certain computation.
|
||||
After all, variables can get type hints as well! Since Godot does not support
|
||||
generic types of Observables, we can still fix the type of a sequence with the
|
||||
`oftype` operator. Now observers can be sure to always receive items of the wanted type.
|
||||
Generating a wrong type will cause an error notification via the `on_error` contract. Per
|
||||
default, it also notifies the programmer via a push-error message in the editor.
|
||||
|
||||
This would be a good style, I think:
|
||||
```swift
|
||||
var _obs1 : Observable
|
||||
var _obs2 : Observable
|
||||
|
||||
var Obs1 : Observable : #[int]
|
||||
get: return self._obs1.oftype(TYPE_INT)
|
||||
var Obs2 : Observable : #[RefValue]
|
||||
get: return self._obs2.oftype(RefValue)
|
||||
|
||||
func _ready():
|
||||
self._obs1 : Observable = GDRx.from_array([1, 2, 3])
|
||||
self._obs2 : Observable = GDRx.just(RefValue.Set(42))
|
||||
```
|
||||
|
||||
### Multithreading
|
||||
|
||||
With GDRx multithreading is just one scheduling away.
|
||||
|
||||
```swift
|
||||
var nfs : NewThreadScheduler = NewThreadScheduler.singleton()
|
||||
GDRx.just(0, nfs) \
|
||||
.repeat(10) \
|
||||
.subscribe(func(__): print("Thread ID: ", OS.get_thread_caller_id())) \
|
||||
.dispose_with(self)
|
||||
```
|
||||
|
||||
Threads terminate automatically when they finish computation. No need to call `Thread.wait_to_finish()`.
|
||||
|
||||
## Godot Features
|
||||
|
||||
### Reactive Properties
|
||||
|
||||
Reactive Properties are a special kind of Observable (and Disposable) which emit items whenever
|
||||
their value is changed. This is very useful e.g. for UI implementations.
|
||||
Creating a ReactiveProperty instance is straight forward. Access its contents
|
||||
via the `Value` property inside the ReactiveProperty instance.
|
||||
|
||||
```swift
|
||||
var prop = ReactiveProperty.new(42)
|
||||
prop.subscribe(func(i): print(">> ", i))
|
||||
|
||||
# Emits an item on the stream
|
||||
prop.Value += 42
|
||||
|
||||
# Sends completion notification to observers and disposes the ReactiveProperty
|
||||
prop.dispose()
|
||||
|
||||
```
|
||||
|
||||
Sometimes we want to construct a ReactiveProperty from a class member. This can
|
||||
be done via the `ReactiveProperty.FromMember()` constructor. The changed value
|
||||
is reflected onto the class member, though changing the member will NOT change
|
||||
the value of the ReactiveProperty.
|
||||
|
||||
```swift
|
||||
var _hp : int = 100
|
||||
|
||||
var _stamina : float = 1.0
|
||||
var _attack_damage : int = 100
|
||||
|
||||
func _ready():
|
||||
# Create ReactiveProperty from member
|
||||
var _Hp : ReactiveProperty = ReactiveProperty.FromMember(self, "_hp")
|
||||
var __ = _Hp.subscribe(func(i): print("Changed Hp ", i))
|
||||
_Hp.Value += 10
|
||||
print("Reflected: ", self._hp)
|
||||
```
|
||||
|
||||
A ReadOnlyReactiveProperty with read-only access can be created via the
|
||||
`ReactiveProperty.to_readonly()` method. Trying to set the value will throw
|
||||
an error.
|
||||
|
||||
```swift
|
||||
# To ReadOnlyReactiveProperty
|
||||
var Hp : ReadOnlyReactiveProperty = _Hp.to_readonly()
|
||||
|
||||
# Writing to ReadOnlyReactiveProperty causes an error
|
||||
GDRx.try(func():
|
||||
Hp.Value = -100
|
||||
) \
|
||||
.catch("Error", func(exc):
|
||||
print("Err: ", exc)
|
||||
) \
|
||||
.end_try_catch()
|
||||
```
|
||||
|
||||
A ReactiveProperty can also be created from a Setter and a Getter function
|
||||
|
||||
```swift
|
||||
# Create Reactive Property from getter and setter
|
||||
var set_stamina = func(v):
|
||||
print("Setter Callback")
|
||||
self._stamina = v
|
||||
|
||||
var get_stamina = func() -> float:
|
||||
print("Getter Callback")
|
||||
return self._stamina
|
||||
|
||||
var _Stamina = ReactiveProperty.FromGetSet(get_stamina, set_stamina)
|
||||
_Stamina.Value = 0.8
|
||||
print("Reflected> ", self._stamina)
|
||||
```
|
||||
|
||||
A ReadOnlyReactiveProperty can also represent a computational step from a set
|
||||
of other properties. When one of the underlying properties is changed, the
|
||||
computed ReadOnlyReactiveProperty emits an item accordingly. A computed
|
||||
ReadOnlyReactiveProperty can be created via the `ReactiveProperty.Computed{n}()`
|
||||
constructor.
|
||||
|
||||
```swift
|
||||
var Stamina : ReadOnlyReactiveProperty = _Stamina.to_readonly()
|
||||
var _AttackDamage : ReactiveProperty = ReactiveProperty.FromMember(
|
||||
self, "_attack_damage")
|
||||
var AttackDamage : ReadOnlyReactiveProperty = _AttackDamage.to_readonly()
|
||||
|
||||
# Create a computed ReadOnlyReactiveProperty
|
||||
var TrueDamage : ReadOnlyReactiveProperty = ReactiveProperty.Computed2(
|
||||
Stamina, AttackDamage,
|
||||
func(st : float, ad : int): return (st * ad) as int
|
||||
)
|
||||
TrueDamage.subscribe(func(i): print("True Damage: ", i)).dispose_with(self)
|
||||
_Stamina.Value = 0.2
|
||||
_AttackDamage.Value = 90
|
||||
```
|
||||
|
||||
### Reactive Collections
|
||||
|
||||
A ReactiveCollection works similar to a ReactiveProperty with the main difference
|
||||
that it represents not a single value but a listing of values.
|
||||
|
||||
```swift
|
||||
var collection : ReactiveCollection = ReactiveCollection.new(["a", "b", "c", "d", "e", "f"])
|
||||
```
|
||||
|
||||
Its constructor supports generators of type `IterableBase` as well...
|
||||
|
||||
### Input Events
|
||||
|
||||
Very frequent input events are included as observables:
|
||||
|
||||
```swift
|
||||
GDRx.on_mouse_down() \
|
||||
.filter(func(ev : InputEventMouseButton): return ev.button_index == 1) \
|
||||
.subscribe(func(__): print("Left Mouse Down!")) \
|
||||
.dispose_with(self)
|
||||
|
||||
GDRx.on_mouse_double_click() \
|
||||
.filter(func(ev : InputEventMouseButton): return ev.button_index == 1) \
|
||||
.subscribe(func(__): print("Left Mouse Double-Click!")) \
|
||||
.dispose_with(self)
|
||||
|
||||
GDRx.on_key_pressed(KEY_W) \
|
||||
.subscribe(func(__): print("W")) \
|
||||
.dispose_with(self)
|
||||
```
|
||||
|
||||
### Frame Events
|
||||
|
||||
Main frame events can be directly accessed as observables as well:
|
||||
|
||||
```swift
|
||||
# Do stuff before `_process(delta)` calls.
|
||||
GDRx.on_idle_frame() \
|
||||
.subscribe(func(delta : float): print("delta> ", delta)) \
|
||||
.dispose_with(self)
|
||||
|
||||
# Do stuff before `_physics_process(delta)` calls.
|
||||
GDRx.on_physics_step() \
|
||||
.subscribe(func(delta : float): print("delta> ", delta)) \
|
||||
.dispose_with(self)
|
||||
|
||||
# Emits items at pre-draw
|
||||
GDRx.on_frame_pre_draw() \
|
||||
.subscribe(func(__): print("Pre Draw!")) \
|
||||
.dispose_with(self)
|
||||
|
||||
# Emits items at post-draw
|
||||
GDRx.on_frame_post_draw() \
|
||||
.subscribe(func(__): print("Post Draw!")) \
|
||||
.dispose_with(self)
|
||||
```
|
||||
|
||||
## Final Thoughts
|
||||
|
||||
I hope I could clarify the usage of GDRx a bit using some of these examples.
|
||||
|
||||
I do not know if this library is useful in the case of Godot 4 but if you are
|
||||
familiar with and into ReactiveX, go for it!
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions and bug reports are always welcome! We also invite folks to submit unit tests verifiying the functionality of GDRx ;)
|
||||
|
||||
## License
|
||||
|
||||
Distributed under the [MIT License](https://github.com/Neroware/GodotRx/blob/master/LICENSE).
|
||||
24
addons/reactivex/__gdrxinit__.gd
Normal file
@ -0,0 +1,24 @@
|
||||
class_name __GDRx_Init__
|
||||
## Provides access to GDRx-library types.
|
||||
##
|
||||
## Bridge between GDRx-library type implementations and [__GDRx_Singleton__]
|
||||
|
||||
# =========================================================================== #
|
||||
# Notification
|
||||
# =========================================================================== #
|
||||
var NotificationOnNext_ = load("res://addons/reactivex/notification/onnext.gd")
|
||||
var NotificationOnError_ = load("res://addons/reactivex/notification/onerror.gd")
|
||||
var NotificationOnCompleted_ = load("res://addons/reactivex/notification/oncompleted.gd")
|
||||
|
||||
# =========================================================================== #
|
||||
# Internals
|
||||
# =========================================================================== #
|
||||
var Heap_ = load("res://addons/reactivex/internal/heap.gd")
|
||||
var Basic_ = load("res://addons/reactivex/internal/basic.gd")
|
||||
var Concurrency_ = load("res://addons/reactivex/internal/concurrency.gd")
|
||||
var Util_ = load("res://addons/reactivex/internal/utils.gd")
|
||||
|
||||
# =========================================================================== #
|
||||
# Pipe
|
||||
# =========================================================================== #
|
||||
var Pipe_ = load("res://addons/reactivex/pipe.gd")
|
||||
140
addons/reactivex/__gdrxobs__.gd
Normal file
@ -0,0 +1,140 @@
|
||||
class_name __GDRx_Obs__
|
||||
## Provides access to [Observable] constructor.
|
||||
##
|
||||
## Bridge between [Observable] constructor implementations and [__GDRx_Singleton__]
|
||||
|
||||
var _Amb_ = load("res://addons/reactivex/observable/amb.gd")
|
||||
var _Case_ = load("res://addons/reactivex/observable/case.gd")
|
||||
var _Catch_ = load("res://addons/reactivex/observable/catch.gd")
|
||||
var _CombineLatest_ = load("res://addons/reactivex/observable/combinelatest.gd")
|
||||
var _Concat_ = load("res://addons/reactivex/observable/concat.gd")
|
||||
var _Defer_ = load("res://addons/reactivex/observable/defer.gd")
|
||||
var _Empty_ = load("res://addons/reactivex/observable/empty.gd")
|
||||
var _ForkJoin_ = load("res://addons/reactivex/observable/forkjoin.gd")
|
||||
var _FromCallback_ = load("res://addons/reactivex/observable/fromcallback.gd")
|
||||
var _FromIterable_ = load("res://addons/reactivex/observable/fromiterable.gd")
|
||||
var _Generate_ = load("res://addons/reactivex/observable/generate.gd")
|
||||
var _GenerateWithRealtiveTime_ = load("res://addons/reactivex/observable/generatewithrelativetime.gd")
|
||||
var _IfThen_ = load("res://addons/reactivex/observable/ifthen.gd")
|
||||
var _Interval_ = load("res://addons/reactivex/observable/interval.gd")
|
||||
var _Merge_ = load("res://addons/reactivex/observable/merge.gd")
|
||||
var _Never_ = load("res://addons/reactivex/observable/never.gd")
|
||||
var _OnErrorResumeNext_ = load("res://addons/reactivex/observable/onerrorresumenext.gd")
|
||||
var _Range_ = load("res://addons/reactivex/observable/range.gd")
|
||||
var _Repeat_ = load("res://addons/reactivex/observable/repeat.gd")
|
||||
var _ReturnValue_ = load("res://addons/reactivex/observable/returnvalue.gd")
|
||||
var _Throw_ = load("res://addons/reactivex/observable/throw.gd")
|
||||
var _Timer_ = load("res://addons/reactivex/observable/timer.gd")
|
||||
var _ToAsync_ = load("res://addons/reactivex/observable/toasync.gd")
|
||||
var _Using_ = load("res://addons/reactivex/observable/using.gd")
|
||||
var _WithLatestFrom_ = load("res://addons/reactivex/observable/withlatestfrom.gd")
|
||||
var _Zip_ = load("res://addons/reactivex/observable/zip.gd")
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/amb.gd[/b]
|
||||
func amb(sources) -> Observable:
|
||||
return _Amb_.amb_(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/case.gd[/b]
|
||||
func case(mapper : Callable, sources : Dictionary, default_source : Observable = null) -> Observable:
|
||||
return _Case_.case_(mapper, sources, default_source)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/catch.gd[/b]
|
||||
func catch_with_iterable(sources : IterableBase) -> Observable:
|
||||
return _Catch_.catch_with_iterable_(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/combinelatest.gd[/b]
|
||||
func combine_latest(sources) -> Observable:
|
||||
return _CombineLatest_.combine_latest_(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/concat.gd[/b]
|
||||
func concat_with_iterable(sources : IterableBase) -> Observable:
|
||||
return _Concat_.concat_with_iterable_(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/defer.gd[/b]
|
||||
func defer(factory : Callable = GDRx.basic.default_factory) -> Observable:
|
||||
return _Defer_.defer_(factory)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/empty.gd[/b]
|
||||
func empty(scheduler : SchedulerBase = null) -> Observable:
|
||||
return _Empty_.empty_(scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/forkjoin.gd[/b]
|
||||
func fork_join(sources) -> Observable:
|
||||
return _ForkJoin_.fork_join_(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/fromcallback.gd[/b]
|
||||
func from_callback(fun : Callable = func(_args : Array, _cb : Callable): return, mapper = null) -> Callable:
|
||||
return _FromCallback_.from_callback_(fun, mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/fromiterable.gd[/b]
|
||||
func from_iterable(iterable : IterableBase, scheduler : SchedulerBase = null) -> Observable:
|
||||
return _FromIterable_.from_iterable_(iterable, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/generate.gd[/b]
|
||||
func generate(initial_state, condition : Callable = GDRx.basic.default_condition, iterate : Callable = GDRx.basic.identity) -> Observable:
|
||||
return _Generate_.generate_(initial_state, condition, iterate)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/generatewithrealtivetime.gd[/b]
|
||||
func generate_with_relative_time(initial_state, condition : Callable = GDRx.basic.default_condition, iterate : Callable = GDRx.basic.identity, time_mapper : Callable = func(_state) -> float: return 1.0) -> Observable:
|
||||
return _GenerateWithRealtiveTime_.generate_with_relative_time_(initial_state, condition, iterate, time_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/ifthen.gd[/b]
|
||||
func if_then(condition : Callable = GDRx.basic.default_condition, then_source : Observable = null, else_source : Observable = null) -> Observable:
|
||||
return _IfThen_.if_then_(condition, then_source, else_source)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/interval.gd[/b]
|
||||
func interval(period : float, scheduler : SchedulerBase = null) -> ObservableBase:
|
||||
return _Interval_.interval_(period, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/merge.gd[/b]
|
||||
func merge(sources) -> Observable:
|
||||
return _Merge_.merge_(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/never.gd[/b]
|
||||
func never() -> Observable:
|
||||
return _Never_.never_()
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/onerrorresumenext.gd[/b]
|
||||
func on_error_resume_next(sources) -> Observable:
|
||||
return _OnErrorResumeNext_.on_error_resume_next_(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/range.gd[/b]
|
||||
@warning_ignore("shadowed_global_identifier")
|
||||
func range(start : int, stop = null, step = null, scheduler : SchedulerBase = null) -> Observable:
|
||||
return _Range_.range_(start, stop, step, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/repeat.gd[/b]
|
||||
func repeat_value(value, repeat_count = null) -> Observable:
|
||||
return _Repeat_.repeat_value_(value, repeat_count)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/returnvalue.gd[/b]
|
||||
func return_value(value, scheduler : SchedulerBase = null) -> Observable:
|
||||
return _ReturnValue_.return_value_(value, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/returnvalue.gd[/b]
|
||||
func from_callable(supplier : Callable, scheduler : SchedulerBase = null) -> Observable:
|
||||
return _ReturnValue_.from_callable_(supplier, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/throw.gd[/b]
|
||||
func throw(err, scheduler : SchedulerBase = null) -> Observable:
|
||||
return _Throw_.throw_(err, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/timer.gd[/b]
|
||||
func timer(duetime : float, time_absolute : bool, period = null, scheduler : SchedulerBase = null) -> Observable:
|
||||
return _Timer_.timer_(duetime, time_absolute, period, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/toasync.gd[/b]
|
||||
func to_async(fun : Callable, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _ToAsync_.to_async_(fun, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/using.gd[/b]
|
||||
func using(resource_factory : Callable, observable_factory : Callable,) -> Observable:
|
||||
return _Using_.using_(resource_factory, observable_factory)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/withlatestfrom.gd[/b]
|
||||
func with_latest_from(parent : Observable, sources) -> Observable:
|
||||
return _WithLatestFrom_.with_latest_from_(parent, sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/zip.gd[/b]
|
||||
func zip(sources) -> Observable:
|
||||
return _Zip_.zip_(sources)
|
||||
647
addons/reactivex/__gdrxop__.gd
Normal file
@ -0,0 +1,647 @@
|
||||
class_name __GDRx_Op__
|
||||
## Provides access to [Observable] operators.
|
||||
##
|
||||
## Bridge between operator implementations and [__GDRx_Singleton__]
|
||||
|
||||
var _RefCount_ = load("res://addons/reactivex/operators/connectable/_refcount.gd")
|
||||
|
||||
var _All_ = load("res://addons/reactivex/operators/_all.gd")
|
||||
var _Amb_ = load("res://addons/reactivex/operators/_amb.gd")
|
||||
var _AsObservable_ = load("res://addons/reactivex/operators/_asobservable.gd")
|
||||
var _Average_ = load("res://addons/reactivex/operators/_average.gd")
|
||||
var _Buffer_ = load("res://addons/reactivex/operators/_buffer.gd")
|
||||
var _BufferWithTime_ = load("res://addons/reactivex/operators/_bufferwithtime.gd")
|
||||
var _BufferWithTimeOrCount_ = load("res://addons/reactivex/operators/_bufferwithtimeorcount.gd")
|
||||
var _Catch_ = load("res://addons/reactivex/operators/_catch.gd")
|
||||
var _CombineLatest_ = load("res://addons/reactivex/operators/_combinelatest.gd")
|
||||
var _Concat_ = load("res://addons/reactivex/operators/_concat.gd")
|
||||
var _Contains_ = load("res://addons/reactivex/operators/_contains.gd")
|
||||
var _Count_ = load("res://addons/reactivex/operators/_count.gd")
|
||||
var _Debounce_ = load("res://addons/reactivex/operators/_debounce.gd")
|
||||
var _DefaultIfEmpty_ = load("res://addons/reactivex/operators/_defaultifempty.gd")
|
||||
var _Delay_ = load("res://addons/reactivex/operators/_delay.gd")
|
||||
var _DelaySubscription_ = load("res://addons/reactivex/operators/_delaysubscription.gd")
|
||||
var _DelayWithMapper_ = load("res://addons/reactivex/operators/_delaywithmapper.gd")
|
||||
var _Dematerialize_ = load("res://addons/reactivex/operators/_dematerialize.gd")
|
||||
var _Distinct_ = load("res://addons/reactivex/operators/_distinct.gd")
|
||||
var _DistinctUntilChanged_ = load("res://addons/reactivex/operators/_distinctuntilchanged.gd")
|
||||
var _Do_ = load("res://addons/reactivex/operators/_do.gd")
|
||||
var _DoWhile_ = load("res://addons/reactivex/operators/_dowhile.gd")
|
||||
var _ElementAtOrDefault_ = load("res://addons/reactivex/operators/_elementatordefault.gd")
|
||||
var _Exclusive_ = load("res://addons/reactivex/operators/_exclusive.gd")
|
||||
var _Expand_ = load("res://addons/reactivex/operators/_expand.gd")
|
||||
var _Filter_ = load("res://addons/reactivex/operators/_filter.gd")
|
||||
var _FinallyAction_ = load("res://addons/reactivex/operators/_finallyaction.gd")
|
||||
var _Find_ = load("res://addons/reactivex/operators/_find.gd")
|
||||
var _First_ = load("res://addons/reactivex/operators/_first.gd")
|
||||
var _FirstOrDefault_ = load("res://addons/reactivex/operators/_firstordefault.gd")
|
||||
var _FlatMap_ = load("res://addons/reactivex/operators/_flatmap.gd")
|
||||
var _ForkJoin_ = load("res://addons/reactivex/operators/_forkjoin.gd")
|
||||
var _GroupBy_ = load("res://addons/reactivex/operators/_groupby.gd")
|
||||
var _GroupByUntil_ = load("res://addons/reactivex/operators/_groupbyuntil.gd")
|
||||
var _GroupJoin_ = load("res://addons/reactivex/operators/_groupjoin.gd")
|
||||
var _IgnoreElements_ = load("res://addons/reactivex/operators/_ignoreelements.gd")
|
||||
var _IsEmpty_ = load("res://addons/reactivex/operators/_isempty.gd")
|
||||
var _Join_ = load("res://addons/reactivex/operators/_join.gd")
|
||||
var _Last_ = load("res://addons/reactivex/operators/_last.gd")
|
||||
var _LastOrDefault_ = load("res://addons/reactivex/operators/_lastordefault.gd")
|
||||
var _Map_ = load("res://addons/reactivex/operators/_map.gd")
|
||||
var _Materialize_ = load("res://addons/reactivex/operators/_materialize.gd")
|
||||
var _Merge_ = load("res://addons/reactivex/operators/_merge.gd")
|
||||
var _Max_ = load("res://addons/reactivex/operators/_max.gd")
|
||||
var _MaxBy_ = load("res://addons/reactivex/operators/_maxby.gd")
|
||||
var _Min_ = load("res://addons/reactivex/operators/_min.gd")
|
||||
var _MinBy_ = load("res://addons/reactivex/operators/_minby.gd")
|
||||
var _Multicast_ = load("res://addons/reactivex/operators/_multicast.gd")
|
||||
var _ObserveOn_ = load("res://addons/reactivex/operators/_observeon.gd")
|
||||
var _OfType_ = load("res://addons/reactivex/operators/_oftype.gd")
|
||||
var _OnErrorResumeNext_ = load("res://addons/reactivex/operators/_onerrorresumenext.gd")
|
||||
var _Pairwise_ = load("res://addons/reactivex/operators/_pairwise.gd")
|
||||
var _Partition_ = load("res://addons/reactivex/operators/_partition.gd")
|
||||
var _Pluck_ = load("res://addons/reactivex/operators/_pluck.gd")
|
||||
var _Publish_ = load("res://addons/reactivex/operators/_publish.gd")
|
||||
var _PublishValue_ = load("res://addons/reactivex/operators/_publishvalue.gd")
|
||||
var _Reduce_ = load("res://addons/reactivex/operators/_reduce.gd")
|
||||
var _Repeat_ = load("res://addons/reactivex/operators/_repeat.gd")
|
||||
var _Replay_ = load("res://addons/reactivex/operators/_replay.gd")
|
||||
var _Retry_ = load("res://addons/reactivex/operators/_retry.gd")
|
||||
var _Sample_ = load("res://addons/reactivex/operators/_sample.gd")
|
||||
var _Scan_ = load("res://addons/reactivex/operators/_scan.gd")
|
||||
var _SequenceEqual_ = load("res://addons/reactivex/operators/_sequenceequal.gd")
|
||||
var _Single_ = load("res://addons/reactivex/operators/_single.gd")
|
||||
var _SingleOrDefault_ = load("res://addons/reactivex/operators/_singleordefault.gd")
|
||||
var _Skip_ = load("res://addons/reactivex/operators/_skip.gd")
|
||||
var _SkipLast_ = load("res://addons/reactivex/operators/_skiplast.gd")
|
||||
var _SkipLastWithTime_ = load("res://addons/reactivex/operators/_skiplastwithtime.gd")
|
||||
var _SkipUntil_ = load("res://addons/reactivex/operators/_skipuntil.gd")
|
||||
var _SkipUntilWithTime_ = load("res://addons/reactivex/operators/_skipuntilwithtime.gd")
|
||||
var _SkipWhile_ = load("res://addons/reactivex/operators/_skipwhile.gd")
|
||||
var _SkipWithTime_ = load("res://addons/reactivex/operators/_skipwithtime.gd")
|
||||
var _Slice_ = load("res://addons/reactivex/operators/_slice.gd")
|
||||
var _Some_ = load("res://addons/reactivex/operators/_some.gd")
|
||||
var _StartWith_ = load("res://addons/reactivex/operators/_startswith.gd")
|
||||
var _SubscribeOn_ = load("res://addons/reactivex/operators/_subscribeon.gd")
|
||||
var _Sum_ = load("res://addons/reactivex/operators/_sum.gd")
|
||||
var _SwitchLatest_ = load("res://addons/reactivex/operators/_switchlatest.gd")
|
||||
var _TakeLast_ = load("res://addons/reactivex/operators/_takelast.gd")
|
||||
var _Take_ = load("res://addons/reactivex/operators/_take.gd")
|
||||
var _TakeLastBuffer_ = load("res://addons/reactivex/operators/_takelastbuffer.gd")
|
||||
var _TakeLastWithTime_ = load("res://addons/reactivex/operators/_takelastwithtime.gd")
|
||||
var _TakeUntil_ = load("res://addons/reactivex/operators/_takeuntil.gd")
|
||||
var _TakeUntilWithTime_ = load("res://addons/reactivex/operators/_takeuntilwithtime.gd")
|
||||
var _TakeWhile_ = load("res://addons/reactivex/operators/_takewhile.gd")
|
||||
var _TakeWithTime_ = load("res://addons/reactivex/operators/_takewithtime.gd")
|
||||
var _ThrottleFirst_ = load("res://addons/reactivex/operators/_throttlefirst.gd")
|
||||
var _TimeInterval_ = load("res://addons/reactivex/operators/_timeinterval.gd")
|
||||
var _Timeout_ = load("res://addons/reactivex/operators/_timeout.gd")
|
||||
var _TimeoutWithMapper_ = load("res://addons/reactivex/operators/_timeoutwithmapper.gd")
|
||||
var _TimeStamp_ = load("res://addons/reactivex/operators/_timestamp.gd")
|
||||
var _ToDict_ = load("res://addons/reactivex/operators/_todict.gd")
|
||||
var _ToIterable_ = load("res://addons/reactivex/operators/_toiterable.gd")
|
||||
var _ToList_ = load("res://addons/reactivex/operators/_tolist.gd")
|
||||
var _ToSet_ = load("res://addons/reactivex/operators/_toset.gd")
|
||||
var _WhileDo_ = load("res://addons/reactivex/operators/_whiledo.gd")
|
||||
var _Window_ = load("res://addons/reactivex/operators/_window.gd")
|
||||
var _WindowWithCount_ = load("res://addons/reactivex/operators/_windowwithcount.gd")
|
||||
var _WindowWithTime_ = load("res://addons/reactivex/operators/_windowwithtime.gd")
|
||||
var _WindowWithTimeOrCount_ = load("res://addons/reactivex/operators/_windowwithtimeorcount.gd")
|
||||
var _WithLatestFrom_ = load("res://addons/reactivex/operators/_withlatestfrom.gd")
|
||||
var _Zip_ = load("res://addons/reactivex/operators/_zip.gd")
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/connectable/_refcount.gd[/b]
|
||||
func ref_count() -> Callable:
|
||||
return _RefCount_.ref_count_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_all.gd[/b]
|
||||
func all(predicate : Callable) -> Callable:
|
||||
return _All_.all_(predicate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_amb.gd[/b]
|
||||
func amb(right_source : Observable) -> Callable:
|
||||
return _Amb_.amb_(right_source)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_asobservable.gd[/b]
|
||||
func as_observable() -> Callable:
|
||||
return _AsObservable_.as_observable_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_average.gd[/b]
|
||||
func average(key_mapper = null) -> Callable:
|
||||
return _Average_.average_(key_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_buffer.gd[/b]
|
||||
func buffer(boundaries : Observable) -> Callable:
|
||||
return _Buffer_.buffer_(boundaries)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_buffer.gd[/b]
|
||||
func buffer_when(closing_mapper : Callable) -> Callable:
|
||||
return _Buffer_.buffer_when_(closing_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_buffer.gd[/b]
|
||||
func buffer_toggle(openings : Observable, closing_mapper : Callable) -> Callable:
|
||||
return _Buffer_.buffer_toggle_(openings, closing_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_buffer.gd[/b]
|
||||
func buffer_with_count(count_ : int, skip_ = null) -> Callable:
|
||||
return _Buffer_.buffer_with_count_(count_, skip_)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_bufferwithtime.gd[/b]
|
||||
func buffer_with_time(timespan : float, timeshift = null, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _BufferWithTime_.buffer_with_time_(timespan, timeshift, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_bufferwithtimeourcount.gd[/b]
|
||||
func buffer_with_time_or_count(timespan : float, count_ : int, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _BufferWithTimeOrCount_.buffer_with_time_or_count_(timespan, count_, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_catch.gd[/b]
|
||||
func catch_handler(source : Observable, handler : Callable) -> Observable:
|
||||
return _Catch_.catch_handler(source, handler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_catch.gd[/b]
|
||||
func catch(handler) -> Callable:
|
||||
return _Catch_.catch_(handler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_combinelatest.gd[/b]
|
||||
func combine_latest(others) -> Callable:
|
||||
return _CombineLatest_.combine_latest_(others)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_concat.gd[/b]
|
||||
func concat(sources) -> Callable:
|
||||
return _Concat_.concat_(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_contains.gd[/b]
|
||||
func contains(value, comparer = GDRx.basic.default_comparer) -> Callable:
|
||||
return _Contains_.contains_(value, comparer)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_count.gd[/b]
|
||||
func count(predicate = null) -> Callable:
|
||||
return _Count_.count_(predicate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_debounce.gd[/b]
|
||||
func debounce(duetime : float, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _Debounce_.debounce_(duetime, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_debounce.gd[/b]
|
||||
func throttle_with_mapper(throttle_duration_mapper : Callable) -> Callable:
|
||||
return _Debounce_.throttle_with_mapper_(throttle_duration_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_defaultifempty.gd[/b]
|
||||
func default_if_empty(default_value = null) -> Callable:
|
||||
return _DefaultIfEmpty_.default_if_empty_(default_value)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_delay.gd[/b]
|
||||
func observable_delay_timespan(source : Observable, duetime : float, scheduler : SchedulerBase = null) -> Observable:
|
||||
return _Delay_.observable_delay_timespan(source, duetime, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_delay.gd[/b]
|
||||
func delay(duetime : float, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _Delay_.delay_(duetime, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_delaysubscription.gd[/b]
|
||||
func delay_subscription(duetime : float, time_absolute : bool = false, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _DelaySubscription_.delay_subscription_(duetime, time_absolute, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_delaywithmapper.gd[/b]
|
||||
func delay_with_mapper(subscription_delay = null, delay_duration_mapper = null) -> Callable:
|
||||
return _DelayWithMapper_.delay_with_mapper_(subscription_delay, delay_duration_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_dematerialize.gd[/b]
|
||||
func dematerialize() -> Callable:
|
||||
return _Dematerialize_.dematerialize_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_distinct.gd[/b]
|
||||
func distinct(key_mapper : Callable = GDRx.basic.identity, comparer : Callable = GDRx.basic.default_comparer) -> Callable:
|
||||
return _Distinct_.distinct_(key_mapper, comparer)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_distinctuntilchanged.gd[/b]
|
||||
func distinct_until_changed(key_mapper : Callable = GDRx.basic.identity, comparer : Callable = GDRx.basic.default_comparer) -> Callable:
|
||||
return _DistinctUntilChanged_.distinct_until_changed_(key_mapper, comparer)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_do.gd[/b]
|
||||
func do_action(on_next = null, on_error = null, on_completed = null) -> Callable:
|
||||
return _Do_.do_action_(on_next, on_error, on_completed)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_do.gd[/b]
|
||||
func do(observer : ObserverBase) -> Callable:
|
||||
return _Do_.do_(observer)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_do.gd[/b]
|
||||
func do_after_next(source : Observable, after_next : Callable) -> Observable:
|
||||
return _Do_.do_after_next(source, after_next)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_do.gd[/b]
|
||||
func do_on_subscribe(source : Observable, on_subscribe : Callable) -> Observable:
|
||||
return _Do_.do_on_subscribe(source, on_subscribe)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_do.gd[/b]
|
||||
func do_on_dispose(source : Observable, on_dispose : Callable) -> Observable:
|
||||
return _Do_.do_on_dispose(source, on_dispose)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_do.gd[/b]
|
||||
func do_on_terminate(source : Observable, on_terminate : Callable) -> Observable:
|
||||
return _Do_.do_on_terminate(source, on_terminate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_do.gd[/b]
|
||||
func do_after_terminate(source : Observable, after_terminate : Callable) -> Observable:
|
||||
return _Do_.do_after_terminate(source, after_terminate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_do.gd[/b]
|
||||
func do_finally(finally_action_ : Callable) -> Callable:
|
||||
return _Do_.do_finally(finally_action_)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_dowhile.gd[/b]
|
||||
func do_while(condition : Callable) -> Callable:
|
||||
return _DoWhile_.do_while_(condition)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_elementordefault.gd[/b]
|
||||
func element_at_or_default(index : int, has_default : bool = false, default_value = GDRx.util.GetNotSet()) -> Callable:
|
||||
return _ElementAtOrDefault_.element_at_or_default_(index, has_default, default_value)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_exclusive.gd[/b]
|
||||
func exclusive() -> Callable:
|
||||
return _Exclusive_.exclusive_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_expand.gd[/b]
|
||||
func expand(mapper : Callable) -> Callable:
|
||||
return _Expand_.expand_(mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_filter.gd[/b]
|
||||
func filter(predicate : Callable = GDRx.basic.default_condition) -> Callable:
|
||||
return _Filter_.filter_(predicate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_filter.gd[/b]
|
||||
func filter_indexed(predicate : Callable = GDRx.basic.default_condition) -> Callable:
|
||||
return _Filter_.filter_indexed_(predicate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_finallyaction.gd[/b]
|
||||
func finally_action(action : Callable) -> Callable:
|
||||
return _FinallyAction_.finally_action_(action)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_find.gd[/b]
|
||||
func find_value(predicate : Callable, yield_index : bool) -> Callable:
|
||||
return _Find_.find_value_(predicate, yield_index)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_first.gd[/b]
|
||||
func first(predicate = null) -> Callable:
|
||||
return _First_.first_(predicate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_firstordefault.gd[/b]
|
||||
func first_or_default_async(has_default : bool = false, default_value = null) -> Callable:
|
||||
return _FirstOrDefault_.first_or_default_async_(has_default, default_value)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_firstordefault.gd[/b]
|
||||
func first_or_default(predicate = null, default_value = null) -> Callable:
|
||||
return _FirstOrDefault_.first_or_default_(predicate, default_value)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_flatmap.gd[/b]
|
||||
func flat_map(mapper = null) -> Callable:
|
||||
return _FlatMap_.flat_map_(mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_flatmap.gd[/b]
|
||||
func flat_map_indexed(mapper_indexed = null) -> Callable:
|
||||
return _FlatMap_.flat_map_indexed_(mapper_indexed)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_flatmap.gd[/b]
|
||||
func flat_map_latest(mapper = null) -> Callable:
|
||||
return _FlatMap_.flat_map_latest_(mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_forkjoin.gd[/b]
|
||||
func fork_join(args) -> Callable:
|
||||
return _ForkJoin_.fork_join_(args)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_groupby.gd[/b]
|
||||
func group_by(key_mapper : Callable, element_mapper = null, subject_mapper = null) -> Callable:
|
||||
return _GroupBy_.group_by_(key_mapper, element_mapper, subject_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_groupbyuntil.gd[/b]
|
||||
func group_by_until(key_mapper : Callable, duration_mapper : Callable, element_mapper = null, subject_mapper = null) -> Callable:
|
||||
return _GroupByUntil_.group_by_until_(key_mapper, duration_mapper, element_mapper, subject_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_groupjoin.gd[/b]
|
||||
func group_join(right : Observable, left_duration_mapper : Callable, right_duration_mapper : Callable) -> Callable:
|
||||
return _GroupJoin_.group_join_(right, left_duration_mapper, right_duration_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_ignoreelements.gd[/b]
|
||||
func ignore_elements() -> Callable:
|
||||
return _IgnoreElements_.ignore_elements_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_isempty.gd[/b]
|
||||
func is_empty() -> Callable:
|
||||
return _IsEmpty_.is_empty_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_join.gd[/b]
|
||||
func join(right : Observable, left_duration_mapper : Callable, right_duration_mapper : Callable) -> Callable:
|
||||
return _Join_.join_(right, left_duration_mapper, right_duration_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_last.gd[/b]
|
||||
func last(predicate = null) -> Callable:
|
||||
return _Last_.last_(predicate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_lastordefault.gd[/b]
|
||||
func last_or_default_async(source : Observable, has_default : bool = false, default_value = null) -> Observable:
|
||||
return _LastOrDefault_.last_or_default_async(source, has_default, default_value)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_lastordefault.gd[/b]
|
||||
func last_or_default(default_value = null, predicate = null) -> Callable:
|
||||
return _LastOrDefault_.last_or_default_(default_value, predicate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_map.gd[/b]
|
||||
func map(mapper : Callable = GDRx.basic.identity) -> Callable:
|
||||
return _Map_.map_(mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_map.gd[/b]
|
||||
func map_indexed(mapper_indexed : Callable = GDRx.basic.identity) -> Callable:
|
||||
return _Map_.map_indexed_(mapper_indexed)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_materialize.gd[/b]
|
||||
func materialize() -> Callable:
|
||||
return _Materialize_.materialize_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_max.gd[/b]
|
||||
@warning_ignore("shadowed_global_identifier")
|
||||
func max(comparer = null) -> Callable:
|
||||
return _Max_.max_(comparer)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_maxby.gd[/b]
|
||||
func max_by(key_mapper : Callable, comparer = null) -> Callable:
|
||||
return _MaxBy_.max_by_(key_mapper, comparer)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_merge.gd[/b]
|
||||
func merge(sources, max_concorrent : int = -1) -> Callable:
|
||||
return _Merge_.merge_(sources, max_concorrent)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_merge.gd[/b]
|
||||
func merge_all() -> Callable:
|
||||
return _Merge_.merge_all_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_min.gd[/b]
|
||||
@warning_ignore("shadowed_global_identifier")
|
||||
func min(comparer = null) -> Callable:
|
||||
return _Min_.min_(comparer)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_minby.gd[/b]
|
||||
func extrema_by(source : Observable, key_mapper : Callable, comparer : Callable) -> Observable:
|
||||
return _MinBy_.extrema_by(source, key_mapper, comparer)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_minby.gd[/b]
|
||||
func min_by(key_mapper : Callable, comparer = null) -> Callable:
|
||||
return _MinBy_.min_by_(key_mapper, comparer)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_multicast.gd[/b]
|
||||
func multicast(subject : SubjectBase = null, subject_factory = null, mapper = null) -> Callable:
|
||||
return _Multicast_.multicast_(subject, subject_factory, mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_observeon.gd[/b]
|
||||
func observe_on(scheduler : SchedulerBase) -> Callable:
|
||||
return _ObserveOn_.observe_on_(scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_oftype.gd[/b]
|
||||
func oftype(type, push_err : bool = true, type_equality : Callable = GDRx.basic.default_type_equality) -> Callable:
|
||||
return _OfType_.oftype_(type, push_err, type_equality)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_onerrorresumenext.gd[/b]
|
||||
func on_error_resume_next(second : Observable) -> Callable:
|
||||
return _OnErrorResumeNext_.on_error_resume_next_(second)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_pairwise.gd[/b]
|
||||
func pairwise() -> Callable:
|
||||
return _Pairwise_.pairwise_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_partiton.gd[/b]
|
||||
func partition(predicate : Callable = GDRx.basic.default_condition) -> Callable:
|
||||
return _Partition_.partition_(predicate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_partition.gd[/b]
|
||||
func partition_indexed(predicate_indexed : Callable = GDRx.basic.default_condition) -> Callable:
|
||||
return _Partition_.partition_indexed_(predicate_indexed)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_pluck.gd[/b]
|
||||
func pluck(key) -> Callable:
|
||||
return _Pluck_.pluck_(key)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_pluck.gd[/b]
|
||||
func pluck_attr(prop : String) -> Callable:
|
||||
return _Pluck_.pluck_attr_(prop)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_publish.gd[/b]
|
||||
func publish(mapper = null) -> Callable:
|
||||
return _Publish_.publish_(mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_publish.gd[/b]
|
||||
func share() -> Callable:
|
||||
return _Publish_.share_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_publishvalue.gd[/b]
|
||||
func publish_value(initial_value, mapper = null) -> Callable:
|
||||
return _PublishValue_.publish_value_(initial_value, mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_reduce.gd[/b]
|
||||
func reduce(accumulator : Callable, seed_ = GDRx.util.GetNotSet()) -> Callable:
|
||||
return _Reduce_.reduce_(accumulator, seed_)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_repeat.gd[/b]
|
||||
func repeat(repeat_count = null) -> Callable:
|
||||
return _Repeat_.repeat_(repeat_count)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_replay.gd[/b]
|
||||
func replay(mapper = null, buffer_size = null, window_ = null, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _Replay_.replay_(mapper, buffer_size, window_, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_retry.gd[/b]
|
||||
func retry(retry_count : int = -1) -> Callable:
|
||||
return _Retry_.retry_(retry_count)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_sample.gd[/b]
|
||||
func sample_observable(source : Observable, sampler : Observable) -> Observable:
|
||||
return _Sample_.sample_observable(source, sampler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_sample.gd[/b]
|
||||
func sample(sampler : Observable, sampler_time : float = NAN, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _Sample_.sample_(sampler, sampler_time, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_scan.gd[/b]
|
||||
func scan(accumulator : Callable, seed_ = GDRx.util.GetNotSet()) -> Callable:
|
||||
return _Scan_.scan_(accumulator, seed_)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_sequenceequal.gd[/b]
|
||||
func sequence_equal(second, comparer = null, second_it : IterableBase = null) -> Callable:
|
||||
return _SequenceEqual_.sequence_equal_(second, comparer, second_it)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_single.gd[/b]
|
||||
func single(predicate = null) -> Callable:
|
||||
return _Single_.single_(predicate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_singleordefault.gd[/b]
|
||||
func single_or_default_async(has_default : bool = false, default_value = null) -> Callable:
|
||||
return _SingleOrDefault_.single_or_default_async_(has_default, default_value)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_singleordefault.gd[/b]
|
||||
func single_or_default(predicate = null, default_value = null) -> Callable:
|
||||
return _SingleOrDefault_.single_or_default_(predicate, default_value)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_skip.gd[/b]
|
||||
func skip(count_ : int) -> Callable:
|
||||
return _Skip_.skip_(count_)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_skiplast.gd[/b]
|
||||
func skip_last(count_ : int) -> Callable:
|
||||
return _SkipLast_.skip_last_(count_)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_skiplastwithtime.gd[/b]
|
||||
func skip_last_with_time(duration : float, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _SkipLastWithTime_.skip_last_with_time_(duration, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_skipuntil.gd[/b]
|
||||
func skip_until(other : Observable) -> Callable:
|
||||
return _SkipUntil_.skip_until_(other)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_skipuntilwithtime.gd[/b]
|
||||
func skip_until_with_time(start_time : float, time_absolute : bool = false, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _SkipUntilWithTime_.skip_until_with_time_(start_time, time_absolute, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_skipwhile.gd[/b]
|
||||
func skip_while(predicate : Callable) -> Callable:
|
||||
return _SkipWhile_.skip_while_(predicate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_skipwhile.gd[/b]
|
||||
func skip_while_indexed(predicate : Callable) -> Callable:
|
||||
return _SkipWhile_.skip_while_indexed_(predicate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_skipwithtime.gd[/b]
|
||||
func skip_with_time(duration : float, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _SkipWithTime_.skip_with_time_(duration, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_slice.gd[/b]
|
||||
func slice(start : int = 0, stop : int = GDRx.util.MAX_SIZE, step : int = 1) -> Callable:
|
||||
return _Slice_.slice_(start, stop, step)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_some.gd[/b]
|
||||
func some(predicate = null) -> Callable:
|
||||
return _Some_.some_(predicate)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_startswith.gd[/b]
|
||||
func start_with(args) -> Callable:
|
||||
return _StartWith_.start_with_(args)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_subscribeon.gd[/b]
|
||||
func subscribe_on(scheduler : SchedulerBase) -> Callable:
|
||||
return _SubscribeOn_.subscribe_on_(scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_sum.gd[/b]
|
||||
func sum(key_mapper = null) -> Callable:
|
||||
return _Sum_.sum_(key_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_switchlatest.gd[/b]
|
||||
func switch_latest() -> Callable:
|
||||
return _SwitchLatest_.switch_latest_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_take.gd[/b]
|
||||
func take(count_ : int) -> Callable:
|
||||
return _Take_.take_(count_)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_takelast.gd[/b]
|
||||
func take_last(count_ : int) -> Callable:
|
||||
return _TakeLast_.take_last_(count_)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_takelastbuffer.gd[/b]
|
||||
func take_last_buffer(count_ : int) -> Callable:
|
||||
return _TakeLastBuffer_.take_last_buffer_(count_)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_takelastwithtime.gd[/b]
|
||||
func take_last_with_time(duration : float, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _TakeLastWithTime_.take_last_with_time_(duration, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_takeuntil.gd[/b]
|
||||
func take_until(other : Observable) -> Callable:
|
||||
return _TakeUntil_.take_until_(other)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_takeuntilwithtime.gd[/b]
|
||||
func take_until_with_time(end_time : float, absolute : bool = false, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _TakeUntilWithTime_.take_until_with_time_(end_time, absolute, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_takewhile.gd[/b]
|
||||
func take_while(predicate : Callable = GDRx.basic.default_condition, inclusive : bool = false) -> Callable:
|
||||
return _TakeWhile_.take_while_(predicate, inclusive)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_takewhile.gd[/b]
|
||||
func take_while_indexed(predicate : Callable = GDRx.basic.default_condition, inclusive : bool = false) -> Callable:
|
||||
return _TakeWhile_.take_while_indexed_(predicate, inclusive)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_takewithtime.gd[/b]
|
||||
func take_with_time(duration : float, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _TakeWithTime_.take_with_time_(duration, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_throttlefirst.gd[/b]
|
||||
func throttle_first(window_duration : float, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _ThrottleFirst_.throttle_first_(window_duration, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_timeinterval.gd[/b]
|
||||
func time_interval(scheduler : SchedulerBase = null) -> Callable:
|
||||
return _TimeInterval_.time_interval_(scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_timeout.gd[/b]
|
||||
func timeout(duetime : float, absolute : bool = false, other : Observable = null, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _Timeout_.timeout_(duetime, absolute, other, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_timeoutwithmapper.gd[/b]
|
||||
func timeout_with_mapper(first_timeout : Observable = null, timeout_duration_mapper : Callable = func(__) -> Observable: return GDRx.obs.never(), other : Observable = null) -> Callable:
|
||||
return _TimeoutWithMapper_.timeout_with_mapper_(first_timeout, timeout_duration_mapper, other)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_timestamp.gd[/b]
|
||||
func timestamp(scheduler : SchedulerBase = null) -> Callable:
|
||||
return _TimeStamp_.timestamp_(scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_todict.gd[/b]
|
||||
func to_dict(key_mapper : Callable, element_mapper : Callable = GDRx.basic.identity) -> Callable:
|
||||
return _ToDict_.to_dict_(key_mapper, element_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_toiterable.gd[/b]
|
||||
func to_iterable() -> Callable:
|
||||
return _ToIterable_.to_iterable_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_tolist.gd[/b]
|
||||
func to_list() -> Callable:
|
||||
return _ToList_.to_list_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_toset.gd[/b]
|
||||
func to_set() -> Callable:
|
||||
return _ToSet_.to_set_()
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_whiledo.gd[/b]
|
||||
func while_do(condition : Callable = GDRx.basic.default_condition) -> Callable:
|
||||
return _WhileDo_.while_do_(condition)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_window.gd[/b]
|
||||
func window_toggle(openings : Observable, closing_mapper : Callable) -> Callable:
|
||||
return _Window_.window_toggle_(openings, closing_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_window.gd[/b]
|
||||
func window(boundaries : Observable) -> Callable:
|
||||
return _Window_.window_(boundaries)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_window.gd[/b]
|
||||
func window_when(closing_mapper : Callable) -> Callable:
|
||||
return _Window_.window_when_(closing_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_windowwithcount.gd[/b]
|
||||
func window_with_count(count_ : int, skip_ = null) -> Callable:
|
||||
return _WindowWithCount_.window_with_count_(count_, skip_)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_windowwithtime.gd[/b]
|
||||
func window_with_time(timespan : float, timeshift = null, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _WindowWithTime_.window_with_time_(timespan, timeshift, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_windowwithtimeorcount.gd[/b]
|
||||
func window_with_time_or_count(timespan : float, count_ : int, scheduler : SchedulerBase = null) -> Callable:
|
||||
return _WindowWithTimeOrCount_.window_with_time_or_count_(timespan, count_, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_withlatestfrom.gd[/b]
|
||||
func with_latest_from(sources) -> Callable:
|
||||
return _WithLatestFrom_.with_latest_from_(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_zip.gd[/b]
|
||||
func zip(args) -> Callable:
|
||||
return _Zip_.zip_(args)
|
||||
|
||||
## See: [b]res://addons/reactivex/operators/_zip.gd[/b]
|
||||
func zip_with_iterable(seq : IterableBase) -> Callable:
|
||||
return _Zip_.zip_with_iterable_(seq)
|
||||
528
addons/reactivex/__gdrxsingleton__.gd
Normal file
@ -0,0 +1,528 @@
|
||||
extends Node
|
||||
class_name __GDRx_Singleton__
|
||||
|
||||
## GDRx Singleton Script
|
||||
##
|
||||
## Provides access to [Observable] constructors and operators.
|
||||
## [color=yellow] Please add to autoload with name [code]GDRx[/code]![/color]
|
||||
|
||||
# =========================================================================== #
|
||||
# Init script database
|
||||
# =========================================================================== #
|
||||
## Access to internals
|
||||
var __init__ : __GDRx_Init__ = __GDRx_Init__.new()
|
||||
## [Observable] constructor functions
|
||||
var obs : __GDRx_Obs__ = __GDRx_Obs__.new()
|
||||
## [Observable] operator functions
|
||||
var op : __GDRx_Op__ = __GDRx_Op__.new()
|
||||
## Engine Backend
|
||||
var gd : __GDRx_Engine__ = __GDRx_Engine__.new()
|
||||
## See [OnNextNotification]
|
||||
var OnNext = __init__.NotificationOnNext_
|
||||
## See [OnErrorNotification]
|
||||
var OnError = __init__.NotificationOnError_
|
||||
## See [OnCompletedNotification]
|
||||
var OnCompleted = __init__.NotificationOnCompleted_
|
||||
|
||||
## Internal heap implementation
|
||||
var heap = __init__.Heap_.new()
|
||||
## Basic functions & types
|
||||
var basic = __init__.Basic_.new()
|
||||
## Concurrency functions & types
|
||||
var concur = __init__.Concurrency_.new()
|
||||
## Utility functions & types
|
||||
var util = __init__.Util_.new()
|
||||
## Access to pipe operators
|
||||
var pipe = __init__.Pipe_.new()
|
||||
|
||||
# =========================================================================== #
|
||||
# Constructor/Destructor
|
||||
# =========================================================================== #
|
||||
|
||||
func _init():
|
||||
## Insert Main Thread
|
||||
if true:
|
||||
var reglock : ReadWriteLock = self.THREAD_MANAGER.THREAD_REGISTRY.at(0)
|
||||
reglock.w_lock()
|
||||
self.THREAD_MANAGER.THREAD_REGISTRY.at(1)[MAIN_THREAD_ID] = MAIN_THREAD
|
||||
reglock.w_unlock()
|
||||
|
||||
## Init [SceneTreeScheduler] singleton
|
||||
for i in range(8):
|
||||
var process_always : bool = bool(i & 0b1)
|
||||
var process_in_physics : bool = bool(i & 0b10)
|
||||
var ignore_time_scale : bool = bool(i & 0b100)
|
||||
self.SceneTreeTimeoutScheduler_.push_back(SceneTreeTimeoutScheduler.new(
|
||||
"GDRx", process_always, process_in_physics, ignore_time_scale))
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_PREDELETE:
|
||||
self.THREAD_MANAGER.stop_and_join()
|
||||
self.THREAD_MANAGER.free()
|
||||
|
||||
# =========================================================================== #
|
||||
# Multi-Threading
|
||||
# =========================================================================== #
|
||||
|
||||
## Dummy instance for the Main Thread
|
||||
var MAIN_THREAD = concur.MainThreadDummy_.new()
|
||||
## ID of the main thread
|
||||
var MAIN_THREAD_ID = OS.get_thread_caller_id()
|
||||
## Thread Manager
|
||||
var THREAD_MANAGER = concur.ThreadManager.new()
|
||||
|
||||
## Returns the caller's current [Thread]
|
||||
## If the caller thread is the main thread, it returns [b]MAIN_THREAD[/b].
|
||||
func get_current_thread() -> Thread:
|
||||
var id = OS.get_thread_caller_id()
|
||||
var l : ReadWriteLock = self.THREAD_MANAGER.THREAD_REGISTRY.at(0)
|
||||
l.r_lock()
|
||||
id = self.THREAD_MANAGER.THREAD_REGISTRY.at(1)[id]
|
||||
l.r_unlock()
|
||||
return id
|
||||
|
||||
# =========================================================================== #
|
||||
# Scheduler Singletons
|
||||
# =========================================================================== #
|
||||
|
||||
## [ImmediateScheduler] Singleton; [color=red]Do [b]NOT[/b] access directly![/color]
|
||||
var ImmediateScheduler_ : ImmediateScheduler = ImmediateScheduler.new("GDRx")
|
||||
## [SceneTreeTimeoutScheduler] Singleton; [color=red]Do [b]NOT[/b] access directly![/color]
|
||||
var SceneTreeTimeoutScheduler_ : Array[SceneTreeTimeoutScheduler]
|
||||
## [ThreadedTimeoutScheduler] Singleton; [color=red]Do [b]NOT[/b] access directly![/color]
|
||||
var ThreadedTimeoutScheduler_ : ThreadedTimeoutScheduler = ThreadedTimeoutScheduler.new("GDRx")
|
||||
## [NewThreadScheduler] Singleton; [color=red]Do [b]NOT[/b] access directly![/color]
|
||||
var NewThreadScheduler_ : NewThreadScheduler = NewThreadScheduler.new(self.concur.default_thread_factory)
|
||||
## [GodotSignalScheduler] Singleton; [color=red]Do [b]NOT[/b] access directly![/color]
|
||||
var GodotSignalScheduler_ : GodotSignalScheduler = GodotSignalScheduler.new("GDRx")
|
||||
|
||||
## Global singleton of [CurrentThreadScheduler]
|
||||
var CurrentThreadScheduler_global_ : WeakKeyDictionary = WeakKeyDictionary.new()
|
||||
## Thread local singleton of [CurrentThreadScheduler]
|
||||
var CurrentThreadScheduler_local_ = CurrentThreadScheduler._Local.new()
|
||||
|
||||
# =========================================================================== #
|
||||
# Error Handler Singleton
|
||||
# =========================================================================== #
|
||||
## [ErrorHandler] singleton; [color=red]Leave it alone![/color]
|
||||
var ErrorHandler_ : WeakKeyDictionary = WeakKeyDictionary.new()
|
||||
|
||||
# =========================================================================== #
|
||||
# Helper functions
|
||||
# =========================================================================== #
|
||||
|
||||
## Equality
|
||||
func eq(x, y) -> bool:
|
||||
return GDRx.basic.default_comparer(x, y)
|
||||
## Negated equality
|
||||
func neq(x, y) -> bool:
|
||||
return !eq(x, y)
|
||||
## Less than operator
|
||||
func lt(x, y) -> bool:
|
||||
return x.lt(y) if (x is Object and x.has_method("lt")) else x < y
|
||||
## Greater than operator
|
||||
func gt(x, y) -> bool:
|
||||
return x.gt(y) if (x is Object and x.has_method("gt")) else x > y
|
||||
## Greater than equals operator
|
||||
func gte(x, y) -> bool:
|
||||
return x.gte(y) if (x is Object and x.has_method("gte")) else x >= y
|
||||
## Less than equals operator
|
||||
func lte(x, y) -> bool:
|
||||
return x.lte(y) if (x is Object and x.has_method("lte")) else x <= y
|
||||
|
||||
## Raises a [AssertionFailedError] and returns [b]true[/b]
|
||||
## should the assertion fail.
|
||||
func assert_(assertion : bool, message : String = "Assertion failed!") -> bool:
|
||||
if not assertion:
|
||||
AssertionFailedError.new(message).throw()
|
||||
return not assertion
|
||||
|
||||
## Creates a new [TryCatch] Statement
|
||||
func try(fun : Callable) -> TryCatch:
|
||||
return TryCatch.new(fun)
|
||||
|
||||
## Raises a [ThrowableBase]
|
||||
func raise(exc_ : ThrowableBase, default = null) -> Variant:
|
||||
return ErrorHandler.singleton().raise(exc_, default)
|
||||
|
||||
## Raises a [RxBaseError] containing the given message
|
||||
func raise_message(msg : String, default = null):
|
||||
return RxBaseError.raise(default, msg)
|
||||
|
||||
## Construct an [IterableBase] onto x.
|
||||
func to_iterable(x) -> IterableBase:
|
||||
return Iterator.to_iterable(x)
|
||||
|
||||
### Construct an [Iterator] onto x.
|
||||
func iter(x) -> Iterator:
|
||||
return Iterator.iter(x)
|
||||
|
||||
### Creates a [WhileIterable] from a given condition and another [IterableBase]
|
||||
func take_while(cond : Callable, it : IterableBase) -> IterableBase:
|
||||
return WhileIterable.new(it, cond)
|
||||
|
||||
### Generates an [InfiniteIterable] sequence of a given value.
|
||||
func infinite(infval = NOT_SET) -> IterableBase:
|
||||
return InfiniteIterable.new(infval)
|
||||
|
||||
### NOT Set value
|
||||
var NOT_SET:
|
||||
get: return util.NOT_SET
|
||||
|
||||
## Is NOT Set value
|
||||
func not_set(value) -> bool:
|
||||
return NOT_SET.eq(value)
|
||||
|
||||
## Unit item
|
||||
var UNIT:
|
||||
get: return StreamItem.Unit()
|
||||
|
||||
## Alias for [code]GDRx.util.add_ref()[/code]
|
||||
func add_ref(xs : Observable, r : RefCountDisposable) -> Observable:
|
||||
return util.add_ref(xs, r)
|
||||
|
||||
## Create an observable sequence from an array
|
||||
func of(data : Array, scheduler : SchedulerBase = null) -> Observable:
|
||||
return self.from_array(data, scheduler)
|
||||
|
||||
## Create an observable on any given iterable sequence
|
||||
func from(seq, scheduler : SchedulerBase = null) -> Observable:
|
||||
return self.from_iterable(to_iterable(seq), scheduler)
|
||||
|
||||
## Alias for [code]GDRx.obs.return_value[/code]
|
||||
func just(value, scheduler : SchedulerBase = null) -> Observable:
|
||||
return self.return_value(value, scheduler)
|
||||
|
||||
## Empty operation as defined in [code]GDRx.basic.noop[/code]
|
||||
func noop(__ = null, ___ = null):
|
||||
GDRx.basic.noop(__, ___)
|
||||
|
||||
## Identity mapping as defined in [code]GDRx.basic.identity[/code]
|
||||
func identity(x, __ = null):
|
||||
return GDRx.basic.identity(x, __)
|
||||
|
||||
# =========================================================================== #
|
||||
# Observable Constructors
|
||||
# =========================================================================== #
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/amb.gd[/b]
|
||||
func amb(sources) -> Observable:
|
||||
return obs.amb(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/case.gd[/b]
|
||||
func case(mapper : Callable, sources : Dictionary, default_source : Observable = null) -> Observable:
|
||||
return obs.case(mapper, sources, default_source)
|
||||
|
||||
## Create a new observable
|
||||
func create(subscribe : Callable = func(_observer : ObserverBase, _scheduler : SchedulerBase = null) -> DisposableBase: return Disposable.new()) -> Observable:
|
||||
return Observable.new(subscribe)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/catch.gd[/b]
|
||||
func catch(sources : Array[Observable]) -> Observable:
|
||||
return obs.catch_with_iterable(to_iterable(sources))
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/catch.gd[/b]
|
||||
func catch_with_iterable(sources : IterableBase) -> Observable:
|
||||
return obs.catch_with_iterable(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/combinelatest.gd[/b]
|
||||
func combine_latest(sources) -> Observable:
|
||||
return obs.combine_latest(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/concat.gd[/b]
|
||||
func concat_streams(sources : Array[Observable]) -> Observable:
|
||||
return obs.concat_with_iterable(to_iterable(sources))
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/concat.gd[/b]
|
||||
func concat_with_iterable(sources : IterableBase) -> Observable:
|
||||
return obs.concat_with_iterable(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/defer.gd[/b]
|
||||
func defer(factory : Callable = GDRx.basic.default_factory) -> Observable:
|
||||
return obs.defer(factory)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/empty.gd[/b]
|
||||
func empty(scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.empty(scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/forkjoin.gd[/b]
|
||||
func fork_join(sources) -> Observable:
|
||||
return obs.fork_join(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/fromcallback.gd[/b]
|
||||
func from_callback(fun : Callable = func(_args : Array, _cb : Callable): return, mapper = null) -> Callable:
|
||||
return obs.from_callback(fun, mapper)
|
||||
|
||||
## Transforms an array into an observable sequence.
|
||||
func from_array(array : Array, scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.from_iterable(to_iterable(array), scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/fromiterable.gd[/b]
|
||||
func from_iterable(iterable : IterableBase, scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.from_iterable(iterable, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/generate.gd[/b]
|
||||
func generate(initial_state, condition : Callable = GDRx.basic.default_condition, iterate : Callable = GDRx.basic.identity) -> Observable:
|
||||
return obs.generate(initial_state, condition, iterate)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/generatewithrealtivetime.gd[/b]
|
||||
func generate_with_relative_time(initial_state, condition : Callable = GDRx.basic.default_condition, iterate : Callable = GDRx.basic.identity, time_mapper : Callable = func(_state) -> float: return 1.0) -> Observable:
|
||||
return obs.generate_with_relative_time(initial_state, condition, iterate, time_mapper)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/ifthen.gd[/b]
|
||||
func if_then(condition : Callable = GDRx.basic.default_condition, then_source : Observable = null, else_source : Observable = null) -> Observable:
|
||||
return obs.if_then(condition, then_source, else_source)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/interval.gd[/b]
|
||||
func interval(period : float, scheduler : SchedulerBase = null) -> ObservableBase:
|
||||
return obs.interval(period, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/merge.gd[/b]
|
||||
func merge(sources) -> Observable:
|
||||
return obs.merge(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/never.gd[/b]
|
||||
func never() -> Observable:
|
||||
return obs.never()
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/onerrorresumenext.gd[/b]
|
||||
func on_error_resume_next(sources : Array) -> Observable:
|
||||
return obs.on_error_resume_next(sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/range.gd[/b]
|
||||
@warning_ignore("shadowed_global_identifier")
|
||||
func range(start : int, stop = null, step = null, scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.range(start, stop, step, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/repeat.gd[/b]
|
||||
func repeat_value(value, repeat_count = null) -> Observable:
|
||||
return obs.repeat_value(value, repeat_count)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/returnvalue.gd[/b]
|
||||
func return_value(value, scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.return_value(value, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/returnvalue.gd[/b]
|
||||
func from_callable(supplier : Callable, scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.from_callable(supplier, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/throw.gd[/b]
|
||||
func throw(err, scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.throw(err, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/timer.gd[/b]
|
||||
func timer(duetime : float, time_absolute : bool, period = null, scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.timer(duetime, time_absolute, period, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/toasync.gd[/b]
|
||||
func to_async(fun : Callable, scheduler : SchedulerBase = null) -> Callable:
|
||||
return obs.to_async(fun, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/using.gd[/b]
|
||||
func using(resource_factory : Callable, observable_factory : Callable) -> Observable:
|
||||
return obs.using(resource_factory, observable_factory)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/withlatestfrom.gd[/b]
|
||||
func with_latest_from(sources) -> Observable:
|
||||
var _sources : Array[Observable] = util.unpack_arg(sources)
|
||||
assert_(_sources.size() > 0)
|
||||
var parent = _sources.pop_front()
|
||||
return obs.with_latest_from(parent, _sources)
|
||||
|
||||
## See: [b]res://addons/reactivex/observable/zip.gd[/b]
|
||||
func zip(sources) -> Observable:
|
||||
return obs.zip(sources)
|
||||
|
||||
# =========================================================================== #
|
||||
# Timers
|
||||
# =========================================================================== #
|
||||
|
||||
## Creates an observable timer
|
||||
func start_timer(timespan_sec : float, scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.timer(timespan_sec, false, null, scheduler)
|
||||
|
||||
## Creates an observable periodic timer
|
||||
func start_periodic_timer(period_sec : float, scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.timer(period_sec, false, period_sec, scheduler)
|
||||
|
||||
## Creates an observable periodic timer which starts after a timespan has passed
|
||||
func start_periodic_timer_after_timespan(timespan_sec : float, period_sec : float, scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.timer(timespan_sec, false, period_sec, scheduler)
|
||||
|
||||
## Creates an observable timer
|
||||
func schedule_datetime(datetime_sec : float, scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.timer(datetime_sec, true, null, scheduler)
|
||||
|
||||
## Creates an observable periodic timer which starts at a given timestamp.
|
||||
func start_periodic_timer_at_datetime(datetime_sec : float, period_sec : float, scheduler : SchedulerBase = null) -> Observable:
|
||||
return obs.timer(datetime_sec, true, period_sec, scheduler)
|
||||
|
||||
# =========================================================================== #
|
||||
# Godot-specific Observable Constructors
|
||||
# =========================================================================== #
|
||||
|
||||
## Creates an observable from a Godot Signal
|
||||
func from_signal(sig : Signal) -> Observable:
|
||||
return gd.from_godot_signal(sig)
|
||||
|
||||
## Creates an observable from a Coroutine
|
||||
func from_coroutine(fun : Callable, bindings : Array = [], scheduler : SchedulerBase = null) -> Observable:
|
||||
return gd.from_godot_coroutine(fun, bindings, scheduler)
|
||||
|
||||
## Emits items from [method Node._process].
|
||||
func on_process_as_observable(conn : Node) -> Observable:
|
||||
return gd.from_godot_node_lifecycle_event(conn, 0)
|
||||
|
||||
## Emits items from [method Node._physics_process].
|
||||
func on_physics_process_as_observable(conn : Node) -> Observable:
|
||||
return gd.from_godot_node_lifecycle_event(conn, 1)
|
||||
|
||||
## Emits items from [method Node._input].
|
||||
func on_input_as_observable(conn : Node) -> Observable:
|
||||
return gd.from_godot_node_lifecycle_event(conn, 2)
|
||||
|
||||
## Emits items from [method Node._shortcut_input].
|
||||
func on_shortcut_input_as_observable(conn : Node) -> Observable:
|
||||
return gd.from_godot_node_lifecycle_event(conn, 3)
|
||||
|
||||
## Emits items from [method Node._unhandled_input].
|
||||
func on_unhandled_input_as_observable(conn : Node) -> Observable:
|
||||
return gd.from_godot_node_lifecycle_event(conn, 4)
|
||||
|
||||
## Emits items from [method Node._unhandled_key_input].
|
||||
func on_unhandled_key_input_as_observable(conn : Node) -> Observable:
|
||||
return gd.from_godot_node_lifecycle_event(conn, 5)
|
||||
|
||||
## Tranforms an input action into an observable sequence emiting items on check.
|
||||
func input_action(input_action_ : String, checks : Observable) -> Observable:
|
||||
return gd.from_godot_input_action(input_action_, checks)
|
||||
|
||||
## Creates a new Compute Shader as [Observable].
|
||||
func from_compute_shader(shader_path : String, rd : RenderingDevice, work_groups : Vector3i, uniform_sets : Array = [], scheduler : SchedulerBase = null) -> Observable:
|
||||
return gd.from_compute_shader(shader_path, rd, work_groups, uniform_sets, scheduler)
|
||||
|
||||
## Emits items when the node enters the scene tree
|
||||
func on_tree_enter_as_observable(node : Node) -> Observable:
|
||||
return from_signal(node.tree_entered)
|
||||
|
||||
## Emits items when the node just exited the scene tree
|
||||
func on_tree_exit_as_observable(node : Node) -> Observable:
|
||||
return from_signal(node.tree_exited)
|
||||
|
||||
## Emits items when the node is about to exit the scene tree
|
||||
func on_tree_exiting_as_observable(node : Node) -> Observable:
|
||||
return from_signal(node.tree_exiting)
|
||||
|
||||
## Creates an HTTP Request
|
||||
func from_http_request(url : String, request_data = "", raw : bool = false, encoding : String = "", requester : HTTPRequest = null, custom_headers : PackedStringArray = PackedStringArray(), method : HTTPClient.Method = HTTPClient.METHOD_GET) -> Observable:
|
||||
return gd.from_http_request(url, request_data, raw, encoding, requester, custom_headers, method)
|
||||
|
||||
# =========================================================================== #
|
||||
# Some useful Input Observables
|
||||
# =========================================================================== #
|
||||
|
||||
## Emits item when mouse button is just pressed
|
||||
func on_mouse_down() -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventMouseButton) \
|
||||
.filter(func(ev : InputEventMouseButton): return ev.is_pressed())
|
||||
|
||||
## Emits item when mouse button is just released
|
||||
func on_mouse_up() -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventMouseButton) \
|
||||
.filter(func(ev : InputEventMouseButton): return not ev.is_pressed())
|
||||
|
||||
## Emits item on mouse double-click
|
||||
func on_mouse_double_click() -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventMouseButton) \
|
||||
.filter(func(ev : InputEventMouseButton): return ev.is_pressed() and ev.double_click)
|
||||
|
||||
## Emits items on mouse motion
|
||||
func on_mouse_motion() -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventMouseMotion)
|
||||
|
||||
## Emits the relative mouse motion as a [Vector2].
|
||||
func relative_mouse_movement_as_observable() -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventMouseMotion) \
|
||||
.map(func(ev : InputEventMouseMotion): return ev.relative)
|
||||
|
||||
## Emits an item when the given keycode is just pressed.
|
||||
func on_key_just_pressed(key : int) -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventKey) \
|
||||
.filter(func(ev : InputEventKey): return ev.keycode == key and ev.pressed and not ev.echo)
|
||||
|
||||
## Emits an item when the given keycode is pressed.
|
||||
func on_key_pressed(key : int) -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventKey) \
|
||||
.filter(func(ev : InputEventKey): return ev.keycode == key and ev.pressed)
|
||||
|
||||
## Emits an item when the given keycode is just released.
|
||||
func on_key_just_released(key : int) -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventKey) \
|
||||
.filter(func(ev : InputEventKey): return ev.keycode == key and not ev.pressed)
|
||||
|
||||
## Emits an item, when the screen is touched (touch devices).
|
||||
func on_screen_touch() -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventScreenTouch)
|
||||
|
||||
## Emits an item, when the touch screen notices a drag gesture (touch devices)
|
||||
func on_screen_drag() -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventScreenDrag)
|
||||
|
||||
## Emits an item on Midi event.
|
||||
func on_midi_event() -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventMIDI)
|
||||
|
||||
## Emits an item, when a joypad button is just pressed.
|
||||
func on_joypad_button_down() -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventJoypadButton) \
|
||||
.filter(func(ev : InputEventJoypadButton): return not ev.is_echo() and ev.is_pressed())
|
||||
|
||||
## Emits an item, when a joypad button is pressed.
|
||||
func on_joypad_button_pressed() -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventJoypadButton) \
|
||||
.filter(func(ev : InputEventJoypadButton): return ev.is_pressed())
|
||||
|
||||
## Emits an item, when a joypad button is just released.
|
||||
func on_joypad_button_released() -> Observable:
|
||||
return on_input_as_observable(self) \
|
||||
.filter(func(ev : InputEvent): return ev is InputEventJoypadButton) \
|
||||
.filter(func(ev : InputEventJoypadButton): return not ev.is_pressed())
|
||||
|
||||
# =========================================================================== #
|
||||
# Frame Events
|
||||
# =========================================================================== #
|
||||
|
||||
## Emits items on idle frame events.
|
||||
func on_idle_frame() -> Observable:
|
||||
return from_signal(self.get_tree().process_frame) \
|
||||
.map(func(__): return get_process_delta_time())
|
||||
|
||||
## Emits items on physics frame events.
|
||||
func on_physics_step() -> Observable:
|
||||
return from_signal(self.get_tree().physics_frame) \
|
||||
.map(func(__): return get_physics_process_delta_time())
|
||||
|
||||
## Emits an item when the scene tree has changed.
|
||||
func on_tree_changed() -> Observable:
|
||||
return from_signal(self.get_tree().tree_changed)
|
||||
|
||||
## Emits an item at post-draw frame event.
|
||||
func on_frame_post_draw() -> Observable:
|
||||
return from_signal(RenderingServer.frame_post_draw)
|
||||
|
||||
## Emits an item at pre-draw frame event.
|
||||
func on_frame_pre_draw() -> Observable:
|
||||
return from_signal(RenderingServer.frame_pre_draw)
|
||||
27
addons/reactivex/abc/comparable.gd
Normal file
@ -0,0 +1,27 @@
|
||||
class_name Comparable
|
||||
|
||||
## Interface for comparable objects
|
||||
##
|
||||
## This interface provides comparison functions. It is needed because GDScript
|
||||
## does not allow defining custom operators.
|
||||
|
||||
func eq(_other) -> bool:
|
||||
NotImplementedError.raise()
|
||||
return false
|
||||
|
||||
func lt(_other) -> bool:
|
||||
NotImplementedError.raise()
|
||||
return false
|
||||
|
||||
func gt(_other) -> bool:
|
||||
NotImplementedError.raise()
|
||||
return false
|
||||
|
||||
func gte(other) -> bool:
|
||||
return eq(other) or gt(other)
|
||||
|
||||
func lte(other) -> bool:
|
||||
return eq(other) or lt(other)
|
||||
|
||||
func neq(other) -> bool:
|
||||
return not eq(other)
|
||||
28
addons/reactivex/abc/disposable.gd
Normal file
@ -0,0 +1,28 @@
|
||||
class_name DisposableBase
|
||||
|
||||
## Disposables represent Subscriptions in GDRx
|
||||
## [br][br]
|
||||
## Whenever [method dispose] is called, the corresponding
|
||||
## action is performed, e.g. to destroy and clean up a subscription.
|
||||
|
||||
## This is the [b]this[/b] reference. It acts like
|
||||
## [code]std::enable_shared_from_this<>[/code] and is ignored in ref-counting.
|
||||
## Yes, I do know what I am doing! I coded something similar in C++!
|
||||
var this
|
||||
|
||||
func _init():
|
||||
this = self
|
||||
this.unreference()
|
||||
|
||||
## Disposes the disposable and executes a defined action.
|
||||
func dispose():
|
||||
NotImplementedError.raise()
|
||||
|
||||
## Links disposable to [Object] lifetime via an [AutoDisposer]
|
||||
func dispose_with(_obj : Object) -> DisposableBase:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_PREDELETE:
|
||||
this.dispose()
|
||||
28
addons/reactivex/abc/iterable.gd
Normal file
@ -0,0 +1,28 @@
|
||||
class_name IterableBase
|
||||
|
||||
## An iterable type
|
||||
##
|
||||
## An [IterableBase] constructs an [IteratorBase] to iterate over it.
|
||||
## This is done using the [method iter] method.
|
||||
|
||||
var __it__ : Iterator
|
||||
var __curr__ : Variant
|
||||
|
||||
## Returns an iterator onto the given iterable sequence.
|
||||
func iter() -> Iterator:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
func _iter_init(arg):
|
||||
self.__it__ = iter()
|
||||
var continue_ = self.__it__.has_next()
|
||||
self.__curr__ = self.__it__.next()
|
||||
return continue_
|
||||
|
||||
func _iter_next(arg):
|
||||
var continue_ = self.__it__.has_next()
|
||||
self.__curr__ = self.__it__.next()
|
||||
return continue_
|
||||
|
||||
func _iter_get(arg):
|
||||
return self.__curr__
|
||||
36
addons/reactivex/abc/iterator.gd
Normal file
@ -0,0 +1,36 @@
|
||||
class_name IteratorBase
|
||||
|
||||
## An iterator type
|
||||
##
|
||||
## An [IteratorBase] iterates over an [IterableBase] using [method next].
|
||||
|
||||
## Returns next element in the iterable sequence. Return instance of
|
||||
## [ItEnd] when end is reached.
|
||||
func next() -> Variant:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
## Return 'true' if the sequence has another element.
|
||||
func has_next() -> bool:
|
||||
NotImplementedError.raise()
|
||||
return false
|
||||
|
||||
## Returns the first element within the sequence
|
||||
func front() -> Variant:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
## Returns the last element within the sequence
|
||||
func back() -> Variant:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
## Returns 'true' if the iterable sequence is empty.
|
||||
func empty() -> bool:
|
||||
NotImplementedError.raise()
|
||||
return false
|
||||
|
||||
## Returns the n-th element within the sequence.
|
||||
func at(_n : int) -> Variant:
|
||||
NotImplementedError.raise()
|
||||
return false
|
||||
2
addons/reactivex/abc/iteratorend.gd
Normal file
@ -0,0 +1,2 @@
|
||||
## Represents the end of an iterable sequence.
|
||||
class_name ItEnd
|
||||
25
addons/reactivex/abc/lock.gd
Normal file
@ -0,0 +1,25 @@
|
||||
class_name LockBase
|
||||
|
||||
## Interface of a Lock
|
||||
##
|
||||
## Allows a thread to aquire and release it.
|
||||
|
||||
func lock():
|
||||
NotImplementedError.raise()
|
||||
|
||||
func unlock():
|
||||
NotImplementedError.raise()
|
||||
|
||||
func try_lock() -> bool:
|
||||
NotImplementedError.raise()
|
||||
return false
|
||||
|
||||
func is_locking_thread() -> bool:
|
||||
NotImplementedError.raise()
|
||||
return false
|
||||
|
||||
func _unlock_and_store_recursion_depth():
|
||||
NotImplementedError.raise()
|
||||
|
||||
func _lock_and_restore_recursion_depth():
|
||||
NotImplementedError.raise()
|
||||
60
addons/reactivex/abc/observable.gd
Normal file
@ -0,0 +1,60 @@
|
||||
class_name ObservableBase
|
||||
|
||||
## Interface of an observable stream/subject/signal etc.
|
||||
##
|
||||
## All communication in GDRx is handled asynchronously via observable data
|
||||
## streams, so-called [Observable]s. An [Observer] can subscribe to an
|
||||
## [Observable] to receive emitted items sent on the stream.
|
||||
## Create a new subscription via [method subscribe].
|
||||
|
||||
func _init():
|
||||
pass
|
||||
|
||||
## Creates a new subscription.
|
||||
## [br]
|
||||
## There are two ways to invoke this method:
|
||||
## [br]
|
||||
## 1. Subscribes an instance of [ObserverBase].
|
||||
## [br]
|
||||
## 2. Builds a new Observer in accordance to the Observer-Observable-Contract
|
||||
## (see [ObserverBase]) from callbacks and subscribes it.
|
||||
##
|
||||
## [codeblock]
|
||||
## var disp = obs.subscribe(observer)
|
||||
## var disp = obs.subscribe(func(i) ..., func(e): ..., func(): ...)
|
||||
## [/codeblock]
|
||||
## [br]
|
||||
## Since GDScript has no overloading to this date, use [code]subscribe{n}(...)[/code]
|
||||
## for faster access!
|
||||
func subscribe(
|
||||
_on_next, # Callable or Observer or Object with callbacks
|
||||
_on_error : Callable = GDRx.basic.noop,
|
||||
_on_completed : Callable = GDRx.basic.noop,
|
||||
_scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe1(obv : ObserverBase = null, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(obv, GDRx.basic.noop, GDRx.basic.noop, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe2(on_next : Callable = GDRx.basic.noop, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(on_next, GDRx.basic.noop, GDRx.basic.noop, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe3(on_error : Callable = GDRx.basic.noop, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(GDRx.basic.noop, on_error, GDRx.basic.noop, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe4(on_completed : Callable = GDRx.basic.noop, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(GDRx.basic.noop, GDRx.basic.noop, on_completed, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe5(on_next : Callable = GDRx.basic.noop, on_completed : Callable = GDRx.basic.noop, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(on_next, GDRx.basic.noop, on_completed, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe6(on_next : Callable = GDRx.basic.noop, on_error : Callable = GDRx.basic.noop, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(on_next, on_error, GDRx.basic.noop, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe7(on_completed : Callable = GDRx.basic.noop, on_error : Callable = GDRx.basic.noop, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(GDRx.basic.noop, on_error, on_completed, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe8(_scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(GDRx.basic.noop, GDRx.basic.noop, GDRx.basic.noop, _scheduler)
|
||||
25
addons/reactivex/abc/observer.gd
Normal file
@ -0,0 +1,25 @@
|
||||
class_name ObserverBase
|
||||
|
||||
## Interface of an observer
|
||||
##
|
||||
## All communication in GDRx is handled asynchronously via observable data
|
||||
## streams, so-called [Observable]s. An [Observer] can subscribe to an
|
||||
## [Observable] to receive emitted items sent on the stream.
|
||||
## Create a new subscription via [method ObservableBase.subscribe].
|
||||
## An [Observer] needs to abide by the Observer-Observable-Contract as defined
|
||||
## in this interface.
|
||||
##
|
||||
## Notifications are [method on_next], [method on_error] and
|
||||
## [method on_completed].
|
||||
|
||||
## Called when the [Observable] emits a new item on the stream
|
||||
func on_next(_i):
|
||||
NotImplementedError.raise()
|
||||
|
||||
## Called when the [Observable] emits an error on the stream
|
||||
func on_error(_e):
|
||||
NotImplementedError.raise()
|
||||
|
||||
## Called when the [Observable] is finished and no more items are sent.
|
||||
func on_completed():
|
||||
NotImplementedError.raise()
|
||||
15
addons/reactivex/abc/periodicscheduler.gd
Normal file
@ -0,0 +1,15 @@
|
||||
extends SchedulerBase
|
||||
class_name PeriodicSchedulerBase
|
||||
|
||||
## A scheduler for periodic actions
|
||||
##
|
||||
## Periodically schedules actions for repeated future execution.
|
||||
|
||||
## Schedule a periodic action for repeated execution every time
|
||||
## [code]period[/code] seconds have expired.
|
||||
func schedule_periodic(
|
||||
_period : float,
|
||||
_action : Callable,
|
||||
_state = null) -> DisposableBase:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
36
addons/reactivex/abc/scheduler.gd
Normal file
@ -0,0 +1,36 @@
|
||||
class_name SchedulerBase
|
||||
|
||||
## A scheduler performs a scheduled action at some future point.
|
||||
##
|
||||
## Schedules actions for execution at some point in the future.
|
||||
## [br]
|
||||
## [color=yellow]Important: We will always use time values of type
|
||||
## [float] representing seconds![/color]
|
||||
|
||||
func _init():
|
||||
pass
|
||||
|
||||
## Invoke the given action.
|
||||
func invoke_action(_action : Callable, _state = null) -> DisposableBase:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
## Returns the current point in time (timestamp)
|
||||
func now() -> float:
|
||||
NotImplementedError.raise()
|
||||
return -1.0
|
||||
|
||||
## Schedule a new action for future execution
|
||||
func schedule(_action : Callable, _state = null) -> DisposableBase:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
## Schedule a new action for future execution in [code]duetime[/code] seconds.
|
||||
func schedule_relative(_duetime : float, _action : Callable, _state = null) -> DisposableBase:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
## Schedule a new action for future execution at [code]duetime[/code].
|
||||
func schedule_absolute(_duetime : float, _action : Callable, _state = null) -> DisposableBase:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
7
addons/reactivex/abc/startable.gd
Normal file
@ -0,0 +1,7 @@
|
||||
class_name StartableBase
|
||||
|
||||
func start():
|
||||
NotImplementedError.raise()
|
||||
|
||||
func wait_to_finish():
|
||||
NotImplementedError.raise()
|
||||
72
addons/reactivex/abc/subject.gd
Normal file
@ -0,0 +1,72 @@
|
||||
class_name SubjectBase
|
||||
|
||||
## Interface of a Subject.
|
||||
##
|
||||
## A subject is both an [Observer] and [Observable] in RxPY,
|
||||
## meaning it implements both interfaces, however, in GDScript, this is not
|
||||
## allowed! So, this interface provides all interface methods from
|
||||
## [ObserverBase] and [ObservableBase].
|
||||
|
||||
func _init():
|
||||
pass
|
||||
|
||||
## Creates a new subscription.
|
||||
## [br]
|
||||
## There are two ways to invoke this method:
|
||||
## [br]
|
||||
## 1. Subscribes an instance of [ObserverBase].
|
||||
## [br]
|
||||
## 2. Builds a new Observer in accordance to the Observer-Observable-Contract
|
||||
## (see [ObserverBase]) from callbacks and subscribes it.
|
||||
##
|
||||
## [codeblock]
|
||||
## var disp = obs.subscribe(observer)
|
||||
## var disp = obs.subscribe(func(i) ..., func(e): ..., func(): ...)
|
||||
## [/codeblock]
|
||||
## [br]
|
||||
## Since GDScript has no overloading to this date, use [code]subscribe{n}(...)[/code]
|
||||
## for faster access!
|
||||
func subscribe(
|
||||
_on_next, # Callable or Observer or Object with callbacks
|
||||
_on_error : Callable = GDRx.basic.noop,
|
||||
_on_completed : Callable = GDRx.basic.noop,
|
||||
_scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe1(obv : ObserverBase = null, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(obv, GDRx.basic.noop, GDRx.basic.noop, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe2(_on_next : Callable = GDRx.basic.noop, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(_on_next, GDRx.basic.noop, GDRx.basic.noop, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe3(_on_error : Callable = GDRx.basic.noop, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(GDRx.basic.noop, _on_error, GDRx.basic.noop, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe4(_on_completed : Callable = GDRx.basic.noop, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(GDRx.basic.noop, GDRx.basic.noop, _on_completed, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe5(_on_next : Callable = GDRx.basic.noop, _on_completed : Callable = GDRx.basic.noop, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(_on_next, GDRx.basic.noop, _on_completed, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe6(_on_next : Callable = GDRx.basic.noop, _on_error : Callable = GDRx.basic.noop, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(_on_next, _on_error, GDRx.basic.noop, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe7(_on_completed : Callable = GDRx.basic.noop, _on_error : Callable = GDRx.basic.noop, _scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(GDRx.basic.noop, _on_error, _on_completed, _scheduler)
|
||||
## Simulated overload for [code]subscribe[/code]
|
||||
func subscribe8(_scheduler : SchedulerBase = null) -> DisposableBase:
|
||||
return self.subscribe(GDRx.basic.noop, GDRx.basic.noop, GDRx.basic.noop, _scheduler)
|
||||
|
||||
## Called when the [Observable] emits a new item on the stream
|
||||
func on_next(_i):
|
||||
NotImplementedError.raise()
|
||||
|
||||
## Called when the [Observable] emits an error on the stream
|
||||
func on_error(_e):
|
||||
NotImplementedError.raise()
|
||||
|
||||
## Called when the [Observable] is finished and no more items are sent.
|
||||
func on_completed():
|
||||
NotImplementedError.raise()
|
||||
14
addons/reactivex/abc/throwable.gd
Normal file
@ -0,0 +1,14 @@
|
||||
class_name ThrowableBase
|
||||
|
||||
## Interface for throwable objects
|
||||
##
|
||||
## Objects that implement this interface can be used within a try-catch-
|
||||
## structure.
|
||||
|
||||
func throw(_default = null) -> Variant:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
func tags() -> Array[String]:
|
||||
NotImplementedError.raise()
|
||||
return []
|
||||
43
addons/reactivex/disposable/autodisposer.gd
Normal file
@ -0,0 +1,43 @@
|
||||
class_name AutoDisposer
|
||||
|
||||
## Links a [DisposableBase] to an [Object]'s lifetime.
|
||||
|
||||
var _disp : DisposableBase
|
||||
|
||||
func _init(disp : DisposableBase):
|
||||
self._disp = disp
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_PREDELETE:
|
||||
self._disp.dispose()
|
||||
|
||||
static func _meta_key(obj : Object, disp : DisposableBase):
|
||||
var disp_id : int = disp.get_instance_id()
|
||||
var obj_id : int = obj.get_instance_id()
|
||||
var meta_entry = "autodispose_" + \
|
||||
("0" if disp_id > 0 else "1") + \
|
||||
("0" if obj_id > 0 else "1") + \
|
||||
"_d" + str(abs(disp_id)) + "_o" + str(abs(obj_id))
|
||||
return meta_entry
|
||||
|
||||
static func _collect_garbage(obj : Object):
|
||||
for meta_key in obj.get_meta_list():
|
||||
var meta_entry = obj.get_meta(meta_key)
|
||||
if meta_entry is AutoDisposer:
|
||||
if meta_entry._disp.get("is_disposed"):
|
||||
obj.remove_meta(meta_entry)
|
||||
|
||||
static func add(obj : Object, disp : DisposableBase) -> AutoDisposer:
|
||||
AutoDisposer._collect_garbage(obj)
|
||||
var auto_disposer : AutoDisposer = AutoDisposer.new(disp)
|
||||
var meta_key = AutoDisposer._meta_key(obj, disp)
|
||||
obj.set_meta(meta_key, auto_disposer)
|
||||
return auto_disposer
|
||||
|
||||
static func remove(obj : Object, disp : DisposableBase):
|
||||
var meta_key = AutoDisposer._meta_key(obj, disp)
|
||||
obj.remove_meta(meta_key)
|
||||
|
||||
static func remove_and_dispose(obj : Object, disp : DisposableBase):
|
||||
AutoDisposer.remove(obj, disp)
|
||||
disp.dispose()
|
||||
19
addons/reactivex/disposable/booleandisposable.gd
Normal file
@ -0,0 +1,19 @@
|
||||
extends DisposableBase
|
||||
class_name BooleanDisposable
|
||||
|
||||
var is_disposed : bool
|
||||
var lock : RLock
|
||||
|
||||
func _init():
|
||||
self.is_disposed = false
|
||||
self.lock = RLock.new()
|
||||
|
||||
super._init()
|
||||
|
||||
func dispose():
|
||||
this.is_disposed = true
|
||||
|
||||
## Links disposable to [Object] lifetime via an [AutoDisposer]
|
||||
func dispose_with(obj : Object) -> DisposableBase:
|
||||
AutoDisposer.add(obj, self)
|
||||
return self
|
||||
88
addons/reactivex/disposable/compositedisposable.gd
Normal file
@ -0,0 +1,88 @@
|
||||
extends DisposableBase
|
||||
class_name CompositeDisposable
|
||||
|
||||
## A collection of disposables
|
||||
##
|
||||
## When disposed, the underlying composition of disposables are disposed as well.
|
||||
|
||||
var disposable : Array
|
||||
var is_disposed : bool
|
||||
var lock : RLock
|
||||
|
||||
|
||||
func _init(sources_ = []):
|
||||
var sources = GDRx.iter(sources_)
|
||||
while sources.has_next():
|
||||
self.disposable.push_back(sources.next())
|
||||
|
||||
self.is_disposed = false
|
||||
self.lock = RLock.new()
|
||||
super._init()
|
||||
|
||||
func add(item : DisposableBase):
|
||||
var should_dispose = false
|
||||
if true:
|
||||
var __ = LockGuard.new(self.lock)
|
||||
if self.is_disposed:
|
||||
should_dispose = true
|
||||
else:
|
||||
self.disposable.append(item)
|
||||
|
||||
if should_dispose:
|
||||
item.dispose()
|
||||
|
||||
func remove(item : DisposableBase) -> bool:
|
||||
if self.is_disposed:
|
||||
return false
|
||||
|
||||
var should_dispose = false
|
||||
if true:
|
||||
var __ = LockGuard.new(self.lock)
|
||||
if item in self.disposable:
|
||||
self.disposable.erase(item)
|
||||
should_dispose = true
|
||||
|
||||
if should_dispose:
|
||||
item.dispose()
|
||||
|
||||
return should_dispose
|
||||
|
||||
func dispose():
|
||||
if this.is_disposed:
|
||||
return
|
||||
|
||||
var current_disposable
|
||||
if true:
|
||||
var __ = LockGuard.new(this.lock)
|
||||
this.is_disposed = true
|
||||
current_disposable = this.disposable
|
||||
this.disposable = []
|
||||
|
||||
for disp in current_disposable:
|
||||
disp.dispose()
|
||||
|
||||
func clear():
|
||||
var current_disposable
|
||||
if true:
|
||||
var __ = LockGuard.new(self.lock)
|
||||
current_disposable = self.disposable
|
||||
self.disposable = []
|
||||
|
||||
for _disposable in current_disposable:
|
||||
_disposable.dispose()
|
||||
|
||||
func contains(item : DisposableBase) -> bool:
|
||||
return item in self.disposable
|
||||
|
||||
func to_list() -> Array[DisposableBase]:
|
||||
return self.disposable.duplicate()
|
||||
|
||||
func size() -> int:
|
||||
return self.disposable.size()
|
||||
|
||||
var length : int: get = size
|
||||
|
||||
## Links disposable to [Object] lifetime via an [AutoDisposer]
|
||||
func dispose_with(obj : Object) -> DisposableBase:
|
||||
AutoDisposer.add(obj, self)
|
||||
return self
|
||||
52
addons/reactivex/disposable/disposable.gd
Normal file
@ -0,0 +1,52 @@
|
||||
extends DisposableBase
|
||||
class_name Disposable
|
||||
## Main disposable class
|
||||
##
|
||||
## Invokes specified action when disposed.
|
||||
|
||||
var is_disposed : bool
|
||||
var action : Callable
|
||||
|
||||
var lock : RLock
|
||||
|
||||
## Creates a disposable object that invokes the specified
|
||||
## action when disposed.
|
||||
## [br]
|
||||
## [b]Args:[/b]
|
||||
## [br]
|
||||
## [code]action[/code] Action to run during the first call to dispose.
|
||||
## The action is guaranteed to be run at most once.
|
||||
## [br][br]
|
||||
## [b]Returns:[/b]
|
||||
## [br]
|
||||
## The disposable object that runs the given action upon
|
||||
## disposal.
|
||||
func _init(action_ : Callable = GDRx.basic.noop):
|
||||
self.is_disposed = false
|
||||
self.action = action_
|
||||
self.lock = RLock.new()
|
||||
|
||||
super._init()
|
||||
|
||||
## Performs the task of cleaning up resources.
|
||||
func dispose():
|
||||
var disposed = false
|
||||
if true:
|
||||
var __ = LockGuard.new(this.lock)
|
||||
if not this.is_disposed:
|
||||
disposed = true
|
||||
this.is_disposed = true
|
||||
|
||||
if disposed:
|
||||
this.action.call()
|
||||
|
||||
## Links disposable to [Object] lifetime via an [AutoDisposer]
|
||||
func dispose_with(obj : Object) -> DisposableBase:
|
||||
AutoDisposer.add(obj, self)
|
||||
return self
|
||||
|
||||
## Casts any object with [code]dispose()[/code] to a [DisposableBase].
|
||||
static func Cast(obj) -> DisposableBase:
|
||||
if obj.has_method("dispose"):
|
||||
return Disposable.new(func(): obj.dispose())
|
||||
return Disposable.new()
|
||||
50
addons/reactivex/disposable/multiassignmentdisposable.gd
Normal file
@ -0,0 +1,50 @@
|
||||
extends DisposableBase
|
||||
class_name MultipleAssignmentDisposable
|
||||
|
||||
|
||||
var current : DisposableBase
|
||||
var is_disposed : bool
|
||||
var lock : RLock
|
||||
|
||||
func _init():
|
||||
self.current = null
|
||||
self.is_disposed = false
|
||||
self.lock = RLock.new()
|
||||
|
||||
super._init()
|
||||
|
||||
func get_disposable() -> DisposableBase:
|
||||
return self.current
|
||||
|
||||
func set_disposable(value : DisposableBase):
|
||||
var should_dispose : bool
|
||||
if true:
|
||||
var __ = LockGuard.new(self.lock)
|
||||
should_dispose = self.is_disposed
|
||||
if not should_dispose:
|
||||
self.current = value
|
||||
|
||||
if should_dispose and value != null:
|
||||
value.dispose()
|
||||
|
||||
var disposable : DisposableBase:
|
||||
set(value): set_disposable(value)
|
||||
get: return get_disposable()
|
||||
|
||||
func dispose():
|
||||
var old = null
|
||||
|
||||
if true:
|
||||
var __ = LockGuard.new(this.lock)
|
||||
if not this.is_disposed:
|
||||
this.is_disposed = true
|
||||
old = this.current
|
||||
this.current = null
|
||||
|
||||
if old != null:
|
||||
old.dispose()
|
||||
|
||||
## Links disposable to [Object] lifetime via an [AutoDisposer]
|
||||
func dispose_with(obj : Object) -> DisposableBase:
|
||||
AutoDisposer.add(obj, self)
|
||||
return self
|
||||
85
addons/reactivex/disposable/refcountdisposable.gd
Normal file
@ -0,0 +1,85 @@
|
||||
extends DisposableBase
|
||||
class_name RefCountDisposable
|
||||
|
||||
class InnerDisposable extends DisposableBase:
|
||||
var parent : RefCountDisposable
|
||||
var is_disposed : bool
|
||||
var lock : RLock
|
||||
|
||||
func _init(parent_ : RefCountDisposable):
|
||||
self.parent = parent_
|
||||
self.is_disposed = false
|
||||
self.lock = RLock.new()
|
||||
super._init()
|
||||
|
||||
func dispose():
|
||||
var _parent
|
||||
if true:
|
||||
var __ = LockGuard.new(this.lock)
|
||||
_parent = this.parent
|
||||
this.parent = null
|
||||
|
||||
if _parent != null:
|
||||
_parent.release()
|
||||
|
||||
var underlying_disposable : DisposableBase
|
||||
var is_primary_disposed : bool
|
||||
var is_disposed : bool
|
||||
var lock : RLock
|
||||
var count : int
|
||||
|
||||
func _init(disposable_ : DisposableBase):
|
||||
self.underlying_disposable = disposable_
|
||||
self.is_primary_disposed = false
|
||||
self.is_disposed = false
|
||||
self.lock = RLock.new()
|
||||
self.count = 0
|
||||
|
||||
super._init()
|
||||
|
||||
func dispose():
|
||||
if this.is_disposed:
|
||||
return
|
||||
|
||||
var _underlying_disposable = null
|
||||
if true:
|
||||
var __ = LockGuard.new(this.lock)
|
||||
if not this.is_primary_disposed:
|
||||
this.is_primary_disposed = true
|
||||
if not bool(this.count):
|
||||
this.is_disposed = true
|
||||
_underlying_disposable = this.underlying_disposable
|
||||
|
||||
if _underlying_disposable != null:
|
||||
_underlying_disposable.dispose()
|
||||
|
||||
func release():
|
||||
if self.is_disposed:
|
||||
return
|
||||
|
||||
var should_dispose = false
|
||||
if true:
|
||||
var __ = LockGuard.new(self.lock)
|
||||
self.count -= 1
|
||||
if not bool(self.count) and self.is_primary_disposed:
|
||||
self.is_disposed = true
|
||||
should_dispose = true
|
||||
|
||||
if should_dispose:
|
||||
self.underlying_disposable.dispose()
|
||||
|
||||
func get_disposable() -> DisposableBase:
|
||||
var __ = LockGuard.new(self.lock)
|
||||
if self.is_disposed:
|
||||
return Disposable.new()
|
||||
|
||||
self.count += 1
|
||||
return InnerDisposable.new(self)
|
||||
|
||||
var disposable : DisposableBase:
|
||||
get: return get_disposable()
|
||||
|
||||
## Links disposable to [Object] lifetime via an [AutoDisposer]
|
||||
func dispose_with(obj : Object) -> DisposableBase:
|
||||
AutoDisposer.add(obj, self)
|
||||
return self
|
||||
28
addons/reactivex/disposable/scheduleddisposable.gd
Normal file
@ -0,0 +1,28 @@
|
||||
extends DisposableBase
|
||||
class_name ScheduledDisposable
|
||||
|
||||
|
||||
var scheduler : SchedulerBase
|
||||
var disposable : SingleAssignmentDisposable
|
||||
var lock : RLock
|
||||
|
||||
func _init(scheduler_ : SchedulerBase, disposable_ : DisposableBase):
|
||||
self.scheduler = scheduler_
|
||||
self.disposable = SingleAssignmentDisposable.new()
|
||||
self.disposable.disposable = disposable_
|
||||
self.lock = RLock.new()
|
||||
|
||||
super._init()
|
||||
|
||||
var is_disposed:
|
||||
get: return self.disposable.is_disposed
|
||||
|
||||
func dispose():
|
||||
var action = func(_scheduler : SchedulerBase, _state):
|
||||
this.disposable.dispose()
|
||||
this.scheduler.schedule(action)
|
||||
|
||||
## Links disposable to [Object] lifetime via an [AutoDisposer]
|
||||
func dispose_with(obj : Object) -> DisposableBase:
|
||||
AutoDisposer.add(obj, self)
|
||||
return self
|
||||
55
addons/reactivex/disposable/serialdisposable.gd
Normal file
@ -0,0 +1,55 @@
|
||||
extends DisposableBase
|
||||
class_name SerialDisposable
|
||||
|
||||
var current : DisposableBase
|
||||
var is_disposed : bool
|
||||
var lock : RLock
|
||||
|
||||
func _init():
|
||||
self.current = null
|
||||
self.is_disposed = false
|
||||
self.lock = RLock.new()
|
||||
|
||||
super._init()
|
||||
|
||||
func get_disposable() -> DisposableBase:
|
||||
return self.current
|
||||
|
||||
func set_disposable(value : DisposableBase):
|
||||
var old = null
|
||||
|
||||
var should_dispose : bool
|
||||
if true:
|
||||
var __ = LockGuard.new(self.lock)
|
||||
should_dispose = self.is_disposed
|
||||
if not should_dispose:
|
||||
old = self.current
|
||||
self.current = value
|
||||
|
||||
if old != null:
|
||||
old.dispose()
|
||||
|
||||
if should_dispose and value != null:
|
||||
value.dispose()
|
||||
|
||||
var disposable : DisposableBase:
|
||||
get: return get_disposable()
|
||||
set(value): set_disposable(value)
|
||||
|
||||
func dispose():
|
||||
var old = null
|
||||
|
||||
if true:
|
||||
var __ = LockGuard.new(this.lock)
|
||||
if not this.is_disposed:
|
||||
this.is_disposed = true
|
||||
old = this.current
|
||||
this.current = null
|
||||
|
||||
if old != null:
|
||||
old.dispose()
|
||||
|
||||
## Links disposable to [Object] lifetime via an [AutoDisposer]
|
||||
func dispose_with(obj : Object) -> DisposableBase:
|
||||
AutoDisposer.add(obj, self)
|
||||
return self
|
||||
53
addons/reactivex/disposable/singleassignmentdisposable.gd
Normal file
@ -0,0 +1,53 @@
|
||||
extends DisposableBase
|
||||
class_name SingleAssignmentDisposable
|
||||
|
||||
var is_disposed : bool
|
||||
var current : DisposableBase
|
||||
var lock : RLock
|
||||
|
||||
func _init():
|
||||
self.is_disposed = false
|
||||
self.current = null
|
||||
self.lock = RLock.new()
|
||||
|
||||
super._init()
|
||||
|
||||
func get_disposabe() -> DisposableBase:
|
||||
return self.current
|
||||
|
||||
func set_disposable(value : DisposableBase):
|
||||
if self.current != null:
|
||||
DisposedError.raise()
|
||||
return
|
||||
|
||||
var should_dispose : bool
|
||||
if true:
|
||||
var __ = LockGuard.new(self.lock)
|
||||
should_dispose = self.is_disposed
|
||||
if not should_dispose:
|
||||
self.current = value
|
||||
|
||||
if self.is_disposed and value != null:
|
||||
value.dispose()
|
||||
|
||||
var disposable : DisposableBase:
|
||||
set(value): set_disposable(value)
|
||||
get: return get_disposabe()
|
||||
|
||||
func dispose():
|
||||
var old = null
|
||||
|
||||
if true:
|
||||
var __ = LockGuard.new(this.lock)
|
||||
if not this.is_disposed:
|
||||
this.is_disposed = true
|
||||
old = this.current
|
||||
this.current = null
|
||||
|
||||
if old != null:
|
||||
old.dispose()
|
||||
|
||||
## Links disposable to [Object] lifetime via an [AutoDisposer]
|
||||
func dispose_with(obj : Object) -> DisposableBase:
|
||||
AutoDisposer.add(obj, self)
|
||||
return self
|
||||
48
addons/reactivex/engine/__gdrxengine__.gd
Normal file
@ -0,0 +1,48 @@
|
||||
class_name __GDRx_Engine__
|
||||
## Provides access to Godot-specific [Observable] constructors.
|
||||
##
|
||||
## Bridge between Godot-specific implementations and [__GDRx_Singleton__]
|
||||
|
||||
# =========================================================================== #
|
||||
# Observables
|
||||
# =========================================================================== #
|
||||
var _GodotSignal_ = load("res://addons/reactivex/engine/observable/godotsignal.gd")
|
||||
var _GodotLifecycle_ = load("res://addons/reactivex/engine/observable/godotnodelifecycle.gd")
|
||||
var _GodotInputAction_ = load("res://addons/reactivex/engine/observable/godotinputaction.gd")
|
||||
var _GodotCoroutine_ = load("res://addons/reactivex/engine/observable/godotcoroutine.gd")
|
||||
var _ComputeShader_ = load("res://addons/reactivex/engine/observable/computeshader.gd")
|
||||
var _HttpRequest_ = load("res://addons/reactivex/engine/observable/httprequest.gd")
|
||||
|
||||
var _ProcessTimeInterval_ = load("res://addons/reactivex/engine/operators/_processtimeinterval.gd")
|
||||
|
||||
## See: [b]res://addons/reactivex/engine/observable/godotsignal.gd[/b]
|
||||
func from_godot_signal(sig : Signal, scheduler : SchedulerBase = null) -> Observable:
|
||||
return _GodotSignal_.from_godot_signal_(sig, scheduler)
|
||||
|
||||
## See: [b]res://addons/reactivex/engine/observable/godotnodelifecycle.gd[/b]
|
||||
func from_godot_node_lifecycle_event(conn : Node, type : int) -> Observable:
|
||||
return _GodotLifecycle_.from_godot_node_lifecycle_event_(conn, type)
|
||||
|
||||
## See: [b]res://addons/reactivex/engine/observable/godotinputaction.gd[/b]
|
||||
func from_godot_input_action(input_action : String, checks : Observable) -> Observable:
|
||||
return _GodotInputAction_.from_godot_input_action_(input_action, checks)
|
||||
|
||||
## See: [b]res://addons/reactivex/engine/observable/godotcoroutine.gd[/b]
|
||||
func from_godot_coroutine(fun : Callable, bindings : Array = [], scheduler : SchedulerBase = null) -> Observable:
|
||||
return _GodotCoroutine_.from_godot_coroutine_(fun, bindings, scheduler)
|
||||
|
||||
## See: [b]"res://addons/reactivex/engine/observable/computeshader.gd"[/b]
|
||||
func from_compute_shader(shader_path : String, rd : RenderingDevice, work_groups : Vector3i, uniform_sets : Array = [], scheduler : SchedulerBase = null) -> Observable:
|
||||
return _ComputeShader_.from_compute_shader_(shader_path, rd, work_groups, uniform_sets, scheduler)
|
||||
|
||||
## See: [b]"res://addons/reactivex/engine/observable/httprequest.gd"[/b]
|
||||
func from_http_request(url : String, request_data = "", raw : bool = false, encoding : String = "", requester : HTTPRequest = null, custom_headers : PackedStringArray = PackedStringArray(), method : HTTPClient.Method = HTTPClient.METHOD_GET) -> Observable:
|
||||
return _HttpRequest_.from_http_request_(url, request_data, raw, encoding, requester, custom_headers, method)
|
||||
|
||||
## See: [b]"res://addons/reactivex/engine/operators/_processtimeinterval.gd"[/b]
|
||||
func process_time_interval(initial_time : float = 0.0) -> Callable:
|
||||
return _ProcessTimeInterval_.process_time_interval_(initial_time)
|
||||
|
||||
## See: [b]"res://addons/reactivex/engine/operators/_processtimeinterval.gd"[/b]
|
||||
func physics_time_interval(initial_time : float = 0.0) -> Callable:
|
||||
return _ProcessTimeInterval_.physics_time_interval_(initial_time)
|
||||
17
addons/reactivex/engine/abc/godotsignalscheduler.gd
Normal file
@ -0,0 +1,17 @@
|
||||
extends SchedulerBase
|
||||
class_name GodotSignalSchedulerBase
|
||||
|
||||
## A scheduler for Godot signals
|
||||
##
|
||||
## Schedules actions for repeated future execution when a Godot [Signal] is emitted.
|
||||
|
||||
## Schedule a signal callback for repeated execution every time a Godot [Signal]
|
||||
## is emitted.
|
||||
func schedule_signal(
|
||||
_sig : Signal,
|
||||
_n_args : int,
|
||||
_action : Callable,
|
||||
_state = null
|
||||
) -> DisposableBase:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
36
addons/reactivex/engine/abc/reactivecollection.gd
Normal file
@ -0,0 +1,36 @@
|
||||
extends ReadOnlyReactiveCollectionBase
|
||||
class_name ReactiveCollectionBase
|
||||
|
||||
func reset():
|
||||
NotImplementedError.raise()
|
||||
|
||||
func add_item(item) -> int:
|
||||
NotImplementedError.raise()
|
||||
return -1
|
||||
|
||||
func remove_item(item) -> int:
|
||||
NotImplementedError.raise()
|
||||
return -1
|
||||
|
||||
func remove_at(index : int) -> Variant:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
func replace_item(item, with) -> int:
|
||||
NotImplementedError.raise()
|
||||
return -1
|
||||
|
||||
func replace_at(index : int, item) -> Variant:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
func swap(idx1 : int, idx2 : int) -> Tuple:
|
||||
NotImplementedError.raise()
|
||||
return Tuple.Empty()
|
||||
|
||||
func move_to(curr_index : int, new_index : int):
|
||||
NotImplementedError.raise()
|
||||
|
||||
func insert_at(index : int, elem):
|
||||
NotImplementedError.raise()
|
||||
|
||||
12
addons/reactivex/engine/abc/reactivedictionary.gd
Normal file
@ -0,0 +1,12 @@
|
||||
extends ReadOnlyReactiveDictionaryBase
|
||||
class_name ReactiveDictionaryBase
|
||||
|
||||
func clear():
|
||||
NotImplementedError.raise()
|
||||
|
||||
func erase(key) -> bool:
|
||||
NotImplementedError.raise()
|
||||
return false
|
||||
|
||||
func set_pair(key, value):
|
||||
NotImplementedError.raise()
|
||||
5
addons/reactivex/engine/abc/reactiveproperty.gd
Normal file
@ -0,0 +1,5 @@
|
||||
extends ReadOnlyReactivePropertyBase
|
||||
class_name ReactivePropertyBase
|
||||
|
||||
func _set_value(_value):
|
||||
NotImplementedError.raise()
|
||||
49
addons/reactivex/engine/abc/reactivesignal.gd
Normal file
@ -0,0 +1,49 @@
|
||||
extends Observable
|
||||
class_name ReactiveSignalBase
|
||||
|
||||
var this
|
||||
|
||||
func _init(subscribe_ : Callable):
|
||||
this = self
|
||||
this.unreference()
|
||||
|
||||
super._init(subscribe_)
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_PREDELETE:
|
||||
this.dispose()
|
||||
|
||||
func _emit(_args = []):
|
||||
NotImplementedError.raise()
|
||||
|
||||
func attach(_cb : Callable):
|
||||
NotImplementedError.raise()
|
||||
|
||||
func detach(_cb : Callable):
|
||||
NotImplementedError.raise()
|
||||
|
||||
func emit():
|
||||
self._emit()
|
||||
func emit0():
|
||||
self._emit([])
|
||||
func emit1(arg0):
|
||||
self._emit([arg0])
|
||||
func emit2(arg0, arg1):
|
||||
self._emit([arg0, arg1])
|
||||
func emit3(arg0, arg1, arg2):
|
||||
self._emit([arg0, arg1, arg2])
|
||||
func emit4(arg0, arg1, arg2, arg3):
|
||||
self._emit([arg0, arg1, arg2, arg3])
|
||||
func emit5(arg0, arg1, arg2, arg3, arg4):
|
||||
self._emit([arg0, arg1, arg2, arg3, arg4])
|
||||
func emit6(arg0, arg1, arg2, arg3, arg4, arg5):
|
||||
self._emit([arg0, arg1, arg2, arg3, arg4, arg5])
|
||||
func emit7(arg0, arg1, arg2, arg3, arg4, arg5, arg6):
|
||||
self._emit([arg0, arg1, arg2, arg3, arg4, arg5, arg6])
|
||||
func emit8(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7):
|
||||
self._emit([arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7])
|
||||
func emit_unit():
|
||||
self._emit([StreamItem.Unit()])
|
||||
|
||||
func dispose():
|
||||
NotImplementedError.raise()
|
||||
190
addons/reactivex/engine/abc/readonlyreactivecollection.gd
Normal file
@ -0,0 +1,190 @@
|
||||
extends Comparable
|
||||
class_name ReadOnlyReactiveCollectionBase
|
||||
|
||||
class CollectionAddEvent extends Comparable:
|
||||
var index : int
|
||||
var value
|
||||
|
||||
func _init(index_ : int, value_):
|
||||
self.index = index_
|
||||
self.value = value_
|
||||
|
||||
func _to_string() -> String:
|
||||
return "Index: " + str(index) + " Value: " + str(value)
|
||||
|
||||
func get_hash_code() -> int:
|
||||
return hash(index) ^ hash(value) << 2
|
||||
|
||||
func eq(other_ : Comparable) -> bool:
|
||||
if not (other_ is ReadOnlyReactiveCollectionBase.CollectionAddEvent):
|
||||
return false
|
||||
var other = other_ as ReadOnlyReactiveCollectionBase.CollectionAddEvent
|
||||
if index != other.index:
|
||||
return false
|
||||
return GDRx.eq(value, other.value)
|
||||
|
||||
class CollectionRemoveEvent extends Comparable:
|
||||
var index : int
|
||||
var value
|
||||
|
||||
func _init(index_ : int, value_):
|
||||
self.index = index_
|
||||
self.value = value_
|
||||
|
||||
func _to_string() -> String:
|
||||
return "Index: " + str(index) + " Value: " + str(value)
|
||||
|
||||
func get_hash_code() -> int:
|
||||
return hash(index) ^ hash(value) << 2
|
||||
|
||||
func eq(other_ : Comparable) -> bool:
|
||||
if not (other_ is ReadOnlyReactiveCollectionBase.CollectionRemoveEvent):
|
||||
return false
|
||||
var other = other_ as ReadOnlyReactiveCollectionBase.CollectionRemoveEvent
|
||||
if index != other.index:
|
||||
return false
|
||||
return GDRx.eq(value, other.value)
|
||||
|
||||
class CollectionMoveEvent extends Comparable:
|
||||
var old_index : int
|
||||
var new_index : int
|
||||
var value
|
||||
|
||||
func _init(old_index_ : int, new_index_ : int, value_):
|
||||
self.old_index = old_index_
|
||||
self.new_index = new_index_
|
||||
self.value = value_
|
||||
|
||||
func _to_string() -> String:
|
||||
return "OldIndex: " + str(old_index) + " NewIndex: " + str(new_index) \
|
||||
+ " Value: " + str(value)
|
||||
|
||||
func get_hash_code() -> int:
|
||||
return hash(old_index) ^ hash(new_index) << 2 ^ hash(value)
|
||||
|
||||
func eq(other_ : Comparable) -> bool:
|
||||
if not (other_ is ReadOnlyReactiveCollectionBase.CollectionMoveEvent):
|
||||
return false
|
||||
var other = other_ as ReadOnlyReactiveCollectionBase.CollectionMoveEvent
|
||||
if old_index != other.old_index || new_index != other.new_index:
|
||||
return false
|
||||
return GDRx.eq(value, other.value)
|
||||
|
||||
class CollectionReplaceEvent extends Comparable:
|
||||
var index : int
|
||||
var old_value
|
||||
var new_value
|
||||
|
||||
func _init(index_ : int, old_value_, new_value_):
|
||||
self.index = index_
|
||||
self.old_value = old_value_
|
||||
self.new_value = new_value_
|
||||
|
||||
func _to_string() -> String:
|
||||
return "Index: " + str(index) + " OldValue: " + str(old_value) \
|
||||
+ " NewValue: " + str(new_value)
|
||||
|
||||
func get_hash_code() -> int:
|
||||
return hash(index) ^ hash(old_value) << 2 ^ hash(new_value)
|
||||
|
||||
func eq(other_ : Comparable) -> bool:
|
||||
if not (other_ is ReadOnlyReactiveCollectionBase.CollectionReplaceEvent):
|
||||
return false
|
||||
var other = other_ as ReadOnlyReactiveCollectionBase.CollectionReplaceEvent
|
||||
if index != other.index:
|
||||
return false
|
||||
return GDRx.eq(old_value, other.old_value) and GDRx.eq(new_value, other.new_value)
|
||||
|
||||
var Count : int:
|
||||
get: return self.size()
|
||||
|
||||
## [Observable]<[ReadOnlyReactiveCollectionBase.CollectionAddEvent]>
|
||||
var ObserveAdd : Observable:
|
||||
get: return self._observe_add.oftype(ReadOnlyReactiveCollectionBase.CollectionAddEvent)
|
||||
var _observe_add : Observable = GDRx.throw(NotImplementedError.new())
|
||||
|
||||
## Creates an [Observable] which emits the collection's current element count
|
||||
## when the size changes.
|
||||
func ObserveCountChanged(_notify_current_count : bool = false) -> Observable:
|
||||
NotImplementedError.raise()
|
||||
return Observable.new()
|
||||
|
||||
## [Observable]<[int]>
|
||||
var ObserveCount : Observable:
|
||||
get: return ObserveCountChanged(true).oftype(TYPE_INT)
|
||||
|
||||
## [Observable]<[ReadOnlyReactiveCollectionBase.CollectionMoveEvent]>
|
||||
var ObserveMove : Observable:
|
||||
get: return self._observe_move.oftype(ReadOnlyReactiveCollectionBase.CollectionMoveEvent)
|
||||
var _observe_move : Observable = GDRx.throw(NotImplementedError.new())
|
||||
|
||||
## [Observable]<[ReadOnlyReactiveCollectionBase.CollectionRemoveEvent]>
|
||||
var ObserveRemove : Observable:
|
||||
get: return self._observe_remove.oftype(ReadOnlyReactiveCollectionBase.CollectionRemoveEvent)
|
||||
var _observe_remove : Observable = GDRx.throw(NotImplementedError.new())
|
||||
|
||||
## [Observable]<[ReadOnlyReactiveCollectionBase.CollectionReplaceEvent]>
|
||||
var ObserveReplace : Observable:
|
||||
get: return self._observe_replace.oftype(ReadOnlyReactiveCollectionBase.CollectionReplaceEvent)
|
||||
var _observe_replace : Observable = GDRx.throw(NotImplementedError.new())
|
||||
|
||||
## [Observable]<[StreamItem._Unit]>
|
||||
var ObserveReset : Observable:
|
||||
get: return self._observe_reset.oftype(StreamItem._Unit)
|
||||
var _observe_reset : Observable = GDRx.throw(NotImplementedError.new())
|
||||
|
||||
var this
|
||||
|
||||
func _init():
|
||||
this = self
|
||||
this.unreference()
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_PREDELETE:
|
||||
this.dispose()
|
||||
|
||||
## Override from [Comparable]
|
||||
func eq(other) -> bool:
|
||||
if other is ReadOnlyReactiveCollectionBase:
|
||||
return self.to_list().hash() == other.to_list().hash()
|
||||
return false
|
||||
|
||||
func at(index : int):
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
func find(item) -> int:
|
||||
NotImplementedError.raise()
|
||||
return -1
|
||||
|
||||
func to_list() -> Array:
|
||||
NotImplementedError.raise()
|
||||
return []
|
||||
|
||||
func iter() -> Iterator:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
func size() -> int:
|
||||
NotImplementedError.raise()
|
||||
return -1
|
||||
|
||||
func dispose():
|
||||
NotImplementedError.raise()
|
||||
|
||||
var __it__ : Iterator
|
||||
var __curr__ : Variant
|
||||
|
||||
func _iter_init(arg):
|
||||
self.__it__ = iter()
|
||||
var continue_ = self.__it__.has_next()
|
||||
self.__curr__ = self.__it__.next()
|
||||
return continue_
|
||||
|
||||
func _iter_next(arg):
|
||||
var continue_ = self.__it__.has_next()
|
||||
self.__curr__ = self.__it__.next()
|
||||
return continue_
|
||||
|
||||
func _iter_get(arg):
|
||||
return self.__curr__
|
||||
161
addons/reactivex/engine/abc/readonlyreactivedictionary.gd
Normal file
@ -0,0 +1,161 @@
|
||||
extends Comparable
|
||||
class_name ReadOnlyReactiveDictionaryBase
|
||||
|
||||
class DictionaryAddKeyEvent extends Comparable:
|
||||
var key
|
||||
var value
|
||||
|
||||
func _init(key_, value_):
|
||||
self.key = key_
|
||||
self.value = value_
|
||||
|
||||
func _to_string() -> String:
|
||||
return "Key: " + str(key) + " Value: " + str(value)
|
||||
|
||||
func get_hash_code() -> int:
|
||||
return hash(key) ^ hash(value) << 2
|
||||
|
||||
func eq(other) -> bool:
|
||||
if not (other is DictionaryAddKeyEvent):
|
||||
return false
|
||||
return GDRx.eq(key, other.key) and GDRx.eq(value, other.value)
|
||||
|
||||
class DictionaryRemoveKeyEvent extends Comparable:
|
||||
var key
|
||||
var value
|
||||
|
||||
func _init(key_, value_):
|
||||
self.key = key_
|
||||
self.value = value_
|
||||
|
||||
func _to_string() -> String:
|
||||
return "Key: " + str(key) + " Value: " + str(value)
|
||||
|
||||
func get_hash_code() -> int:
|
||||
return hash(key) ^ hash(value) << 2
|
||||
|
||||
func eq(other) -> bool:
|
||||
if not (other is DictionaryRemoveKeyEvent):
|
||||
return false
|
||||
return GDRx.eq(key, other.key) and GDRx.eq(value, other.value)
|
||||
|
||||
class DictionaryUpdateValueEvent extends Comparable:
|
||||
var key
|
||||
var value
|
||||
|
||||
func _init(key_, value_):
|
||||
self.key = key_
|
||||
self.value = value_
|
||||
|
||||
func _to_string() -> String:
|
||||
return "Key: " + str(key) + " Value: " + str(value)
|
||||
|
||||
func get_hash_code() -> int:
|
||||
return hash(key) ^ hash(value) << 2
|
||||
|
||||
func eq(other) -> bool:
|
||||
if not (other is DictionaryUpdateValueEvent):
|
||||
return false
|
||||
return GDRx.eq(key, other.key) and GDRx.eq(value, other.value)
|
||||
|
||||
var Count : int:
|
||||
get: return self.size()
|
||||
|
||||
## [Observable]<[ReadOnlyReactiveDictionaryBase.DictionaryAddKeyEvent]>
|
||||
var ObserveAddKey : Observable:
|
||||
get: return self._observe_add_key.oftype(ReadOnlyReactiveDictionaryBase.DictionaryAddKeyEvent)
|
||||
var _observe_add_key : Observable = GDRx.throw(NotImplementedError.new())
|
||||
|
||||
## Creates an [Observable] which emits the dictionary's current key count
|
||||
## when the size changes.
|
||||
func ObserveCountChanged(_notify_current_count : bool = false) -> Observable:
|
||||
return GDRx.throw(NotImplementedError.new())
|
||||
|
||||
|
||||
## [Observable]<[int]>
|
||||
var ObserveCount : Observable:
|
||||
get: return ObserveCountChanged(true).oftype(TYPE_INT)
|
||||
|
||||
## [Observable]<[ReadOnlyReactiveDictionaryBase.DictionaryRemoveKeyEvent]>
|
||||
var ObserveRemoveKey : Observable:
|
||||
get: return self._observe_remove_key.oftype(ReadOnlyReactiveDictionaryBase.DictionaryRemoveKeyEvent)
|
||||
var _observe_remove_key : Observable = GDRx.throw(NotImplementedError.new())
|
||||
|
||||
## [Observable]<[ReadOnlyReactiveDictionaryBase.DictionaryUpdateValueEvent]>
|
||||
var ObserveUpdateValue : Observable:
|
||||
get: return self._observer_update_value.oftype(ReadOnlyReactiveDictionaryBase.DictionaryUpdateValueEvent)
|
||||
var _observer_update_value : Observable = GDRx.throw(NotImplementedError.new())
|
||||
|
||||
var this
|
||||
|
||||
func _init():
|
||||
this = self
|
||||
this.unreference()
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_PREDELETE:
|
||||
this.dispose()
|
||||
|
||||
func to_dict() -> Dictionary:
|
||||
NotImplementedError.raise()
|
||||
return {}
|
||||
|
||||
func find_key(value) -> Variant:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
func get_value(key, default = null) -> Variant:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
func has_key(key) -> bool:
|
||||
NotImplementedError.raise()
|
||||
return false
|
||||
|
||||
func has_all(keys : Array) -> bool:
|
||||
NotImplementedError.raise()
|
||||
return false
|
||||
|
||||
func hash() -> int:
|
||||
NotImplementedError.raise()
|
||||
return 0
|
||||
|
||||
func is_empty() -> bool:
|
||||
NotImplementedError.raise()
|
||||
return false
|
||||
|
||||
func keys() -> Array:
|
||||
NotImplementedError.raise()
|
||||
return []
|
||||
|
||||
func size() -> int:
|
||||
NotImplementedError.raise()
|
||||
return -1
|
||||
|
||||
func values() -> Array:
|
||||
NotImplementedError.raise()
|
||||
return []
|
||||
|
||||
func dispose():
|
||||
NotImplementedError.raise()
|
||||
|
||||
func iter() -> Iterator:
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
var __it__ : Iterator
|
||||
var __curr__ : Variant
|
||||
|
||||
func _iter_init(arg):
|
||||
self.__it__ = self.iter()
|
||||
var continue_ = self.__it__.has_next()
|
||||
self.__curr__ = self.__it__.next()
|
||||
return continue_
|
||||
|
||||
func _iter_next(arg):
|
||||
var continue_ = self.__it__.has_next()
|
||||
self.__curr__ = self.__it__.next()
|
||||
return continue_
|
||||
|
||||
func _iter_get(arg):
|
||||
return self.__curr__
|
||||
34
addons/reactivex/engine/abc/readonlyreactiveproperty.gd
Normal file
@ -0,0 +1,34 @@
|
||||
extends Observable
|
||||
class_name ReadOnlyReactivePropertyBase
|
||||
|
||||
## Wrapped value
|
||||
var Value:
|
||||
set(value): self._set_value(value)
|
||||
get: return self._get_value()
|
||||
|
||||
var this
|
||||
|
||||
func _init(subscribe : Callable):
|
||||
this = self
|
||||
this.unreference()
|
||||
|
||||
super._init(subscribe)
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_PREDELETE:
|
||||
this.dispose()
|
||||
|
||||
func _set_value(__):
|
||||
GDRx.raise_message("Tried to write to a ReadOnlyReactiveProperty")
|
||||
|
||||
func _get_value():
|
||||
NotImplementedError.raise()
|
||||
return null
|
||||
|
||||
func eq(other) -> bool:
|
||||
if other is ReadOnlyReactivePropertyBase:
|
||||
return GDRx.eq(Value, other.Value)
|
||||
return GDRx.eq(Value, other)
|
||||
|
||||
func dispose():
|
||||
NotImplementedError.raise()
|
||||
47
addons/reactivex/engine/await/await.gd
Normal file
@ -0,0 +1,47 @@
|
||||
class_name ObservableAwait
|
||||
|
||||
signal _on_next(item : Variant)
|
||||
signal _on_error(error : Variant)
|
||||
signal _on_completed()
|
||||
|
||||
func on_next(obs : Observable) -> Variant:
|
||||
var fired_item = RefValue.Set(GDRx.NOT_SET)
|
||||
|
||||
var on_next_ = func(i):
|
||||
fired_item.v = i
|
||||
self._on_next.emit(i)
|
||||
|
||||
var __ = obs.take(1).subscribe(on_next_)
|
||||
|
||||
if not GDRx.not_set(fired_item.v):
|
||||
return fired_item.v
|
||||
var i = await self._on_next
|
||||
return i
|
||||
|
||||
func on_error(obs : Observable) -> Variant:
|
||||
var fired_error = RefValue.Set(GDRx.NOT_SET)
|
||||
|
||||
var on_error_ = func(e):
|
||||
fired_error.v = e
|
||||
self._on_error.emit(e)
|
||||
|
||||
var __ = obs.subscribe3(on_error_)
|
||||
|
||||
if not GDRx.not_set(fired_error.v):
|
||||
return fired_error.v
|
||||
var e = await self._on_error
|
||||
return e
|
||||
|
||||
func on_completed(obs : Observable):
|
||||
var fired_complete = RefValue.Set(false)
|
||||
|
||||
var on_completed_ = func():
|
||||
fired_complete.v = true
|
||||
self._on_completed.emit()
|
||||
|
||||
var __ = obs.subscribe4(on_completed_)
|
||||
|
||||
if fired_complete.v:
|
||||
return
|
||||
await self._on_completed
|
||||
return
|
||||
74
addons/reactivex/engine/observable/computeshader.gd
Normal file
@ -0,0 +1,74 @@
|
||||
## Represents a Compute Shader as an Observable Sequence.
|
||||
## [br][br]
|
||||
## Please note: This is a [b]cold[/b] [Observable]. It transfers data to GPU
|
||||
## on subscribe. Thus, each subscription causes one computational step.
|
||||
## When the compute shader finishes, the [RenderingDevice] is emitted as item.
|
||||
## Afterwards the stream completes.
|
||||
## [br][br]
|
||||
## [b]Args:[/b]
|
||||
## [br]
|
||||
## [code]shader_path[/code] the path to the GLSL file with the shader source
|
||||
## [br]
|
||||
## [code]rd[/code] The current instance of [RenderingDevice]
|
||||
## [br]
|
||||
## [code]work_groups[/code]: Set x, y and z dimensions of work groups.
|
||||
## [br]
|
||||
## [code]uniform_sets[/code]: A list containing lists of [RDUniform]
|
||||
## representing n uniform sets starting with [code]set = 0[/code] going
|
||||
## upwards.
|
||||
## [br]
|
||||
## [code]scheduler[/code]: Schedules the CPU side of the shader computation
|
||||
## [br][br]
|
||||
## [b]Return:[/b]
|
||||
## An observable sequence which performs a new computation on the device,
|
||||
## each time an [Observer] subscribes.
|
||||
static func from_compute_shader_(
|
||||
shader_path : String,
|
||||
rd : RenderingDevice,
|
||||
work_groups : Vector3i,
|
||||
uniform_sets : Array = [],
|
||||
scheduler : SchedulerBase = null
|
||||
) -> Observable:
|
||||
|
||||
# Create shader and pipeline
|
||||
var shader_file = load(shader_path)
|
||||
var shader_spirv = shader_file.get_spirv()
|
||||
var shader = rd.shader_create_from_spirv(shader_spirv)
|
||||
var pipeline = rd.compute_pipeline_create(shader)
|
||||
|
||||
var uniform_sets_rids = []
|
||||
for sid in range(uniform_sets.size()):
|
||||
uniform_sets_rids.append(
|
||||
rd.uniform_set_create(uniform_sets[sid], shader, sid)
|
||||
)
|
||||
|
||||
var subscribe = func(
|
||||
observer : ObserverBase,
|
||||
scheduler_ : SchedulerBase = null
|
||||
) -> DisposableBase:
|
||||
|
||||
var _scheduler
|
||||
if scheduler != null: _scheduler = scheduler
|
||||
elif scheduler_ != null: _scheduler = scheduler_
|
||||
else: _scheduler = CurrentThreadScheduler.singleton()
|
||||
|
||||
var action = func(_scheduler = null, _state = null):
|
||||
# Setup compute pipeline and uniforms
|
||||
var compute_list = rd.compute_list_begin()
|
||||
rd.compute_list_bind_compute_pipeline(compute_list, pipeline)
|
||||
for sid in range(uniform_sets_rids.size()):
|
||||
rd.compute_list_bind_uniform_set(compute_list, uniform_sets_rids[sid], sid)
|
||||
rd.compute_list_dispatch(compute_list, work_groups.x, work_groups.y, work_groups.z)
|
||||
rd.compute_list_end()
|
||||
|
||||
# Send to GPU
|
||||
rd.submit()
|
||||
# Sync with CPU
|
||||
rd.sync()
|
||||
|
||||
observer.on_next(rd)
|
||||
observer.on_completed()
|
||||
|
||||
return _scheduler.schedule(action)
|
||||
|
||||
return Observable.new(subscribe)
|
||||
42
addons/reactivex/engine/observable/godotcoroutine.gd
Normal file
@ -0,0 +1,42 @@
|
||||
## Represents a Godot coroutine as an observable sequence
|
||||
static func from_godot_coroutine_(
|
||||
fun : Callable,
|
||||
bindings : Array = [],
|
||||
scheduler : SchedulerBase = null
|
||||
) -> Observable:
|
||||
var subscribe = func(
|
||||
observer : ObserverBase,
|
||||
scheduler_ : SchedulerBase = null
|
||||
) -> DisposableBase:
|
||||
var coroutine : Callable = fun
|
||||
var n_bind = bindings.size()
|
||||
for argi in range(n_bind):
|
||||
coroutine = coroutine.bind(bindings[n_bind - argi - 1])
|
||||
|
||||
var _scheduler
|
||||
if scheduler != null: _scheduler = scheduler
|
||||
elif scheduler_ != null: _scheduler = scheduler_
|
||||
else: _scheduler = CurrentThreadScheduler.singleton()
|
||||
|
||||
var action = func(_scheduler : SchedulerBase, _state = null):
|
||||
var res = RefValue.Null()
|
||||
|
||||
if GDRx.try(func():
|
||||
# WARNING! This await is not redundant as it seems.
|
||||
# At this point we do not know if 'coroutine' really is a
|
||||
# coroutine but either way should this code work! If 'coroutine'
|
||||
# was a member function Godot would throw an error telling us
|
||||
# that this is, in fact, a coroutine if it contains an 'await'.
|
||||
@warning_ignore("redundant_await")
|
||||
res.v = await coroutine.call()
|
||||
observer.on_next(res.v)
|
||||
observer.on_completed()
|
||||
) \
|
||||
.catch("Error", func(e):
|
||||
observer.on_error(e)
|
||||
) \
|
||||
.end_try_catch(): return
|
||||
|
||||
return _scheduler.schedule(action)
|
||||
|
||||
return Observable.new(subscribe)
|
||||
29
addons/reactivex/engine/observable/godotinputaction.gd
Normal file
@ -0,0 +1,29 @@
|
||||
enum EInputState
|
||||
{
|
||||
RELEASED = 0, JUST_PRESSED = 1, PRESSED = 2, JUST_RELEASED = 3
|
||||
}
|
||||
|
||||
## Represents an input action as an observable sequence which emits input state
|
||||
## each time [code]checks[/code] emits an item.
|
||||
static func from_godot_input_action_(input_action : String, checks : Observable) -> Observable:
|
||||
var subscribe = func(
|
||||
observer : ObserverBase,
|
||||
scheduler : SchedulerBase = null
|
||||
) -> DisposableBase:
|
||||
|
||||
var prev_pressed = RefValue.Set(false)
|
||||
var curr_pressed = RefValue.Set(false)
|
||||
|
||||
var on_next = func(__):
|
||||
prev_pressed.v = curr_pressed.v
|
||||
curr_pressed.v = Input.is_action_pressed(input_action)
|
||||
if prev_pressed.v != curr_pressed.v:
|
||||
observer.on_next(EInputState.JUST_PRESSED if curr_pressed.v else EInputState.JUST_RELEASED)
|
||||
observer.on_next(EInputState.PRESSED if curr_pressed.v else EInputState.RELEASED)
|
||||
|
||||
return checks.subscribe(
|
||||
on_next, observer.on_error, observer.on_completed,
|
||||
scheduler
|
||||
)
|
||||
|
||||
return Observable.new(subscribe)
|
||||
75
addons/reactivex/engine/observable/godotnodelifecycle.gd
Normal file
@ -0,0 +1,75 @@
|
||||
class _NodeLifecycleListener extends Node:
|
||||
signal on_event(data)
|
||||
|
||||
class _ListenerOnProcess extends _NodeLifecycleListener:
|
||||
func _process(delta : float):
|
||||
on_event.emit(delta)
|
||||
|
||||
class _ListenerOnPhysicsProcess extends _NodeLifecycleListener:
|
||||
func _physics_process(delta : float):
|
||||
on_event.emit(delta)
|
||||
|
||||
class _ListenerOnInput extends _NodeLifecycleListener:
|
||||
func _input(event : InputEvent):
|
||||
on_event.emit(event)
|
||||
|
||||
class _ListenerOnShortcutInput extends _NodeLifecycleListener:
|
||||
func _shortcut_input(event : InputEvent):
|
||||
on_event.emit(event)
|
||||
|
||||
class _ListenerOnUnhandledInput extends _NodeLifecycleListener:
|
||||
func _unhandled_input(event : InputEvent):
|
||||
on_event.emit(event)
|
||||
|
||||
class _ListenerOnUnhandledKeyInput extends _NodeLifecycleListener:
|
||||
func _unhandled_key_input(event : InputEvent):
|
||||
on_event.emit(event)
|
||||
|
||||
## Represents [Node] lifecycle events like [method Node._process].
|
||||
## Observable emits argument from call as item on the stream.
|
||||
## [br][br]
|
||||
## [color=yellow]Warning![/color] This only creates a Node of type [b]_NodeLifecycleListener[/b]
|
||||
## which is added as a child since it is not possible to get signals on lifecycle callbacks.
|
||||
static func from_godot_node_lifecycle_event_(conn : Node, type : int) -> Observable:
|
||||
var listener : RefValue = RefValue.Null()
|
||||
var count : RefValue = RefValue.Set(0)
|
||||
|
||||
var subscribe = func(
|
||||
observer : ObserverBase,
|
||||
scheduler : SchedulerBase = null
|
||||
) -> DisposableBase:
|
||||
if count.v == 0:
|
||||
match type:
|
||||
0:
|
||||
listener.v = _ListenerOnProcess.new()
|
||||
1:
|
||||
listener.v = _ListenerOnPhysicsProcess.new()
|
||||
2:
|
||||
listener.v = _ListenerOnInput.new()
|
||||
3:
|
||||
listener.v = _ListenerOnShortcutInput.new()
|
||||
4:
|
||||
listener.v = _ListenerOnUnhandledInput.new()
|
||||
5:
|
||||
listener.v = _ListenerOnUnhandledKeyInput.new()
|
||||
listener.v.name = "__Listener$" + str(conn.get_instance_id()) + "__"
|
||||
listener.v.process_priority = conn.process_priority
|
||||
listener.v.process_mode = conn.process_mode
|
||||
listener.v.process_physics_priority = conn.process_physics_priority
|
||||
conn.call_deferred("add_child", listener.v)
|
||||
count.v += 1
|
||||
|
||||
var dispose = func():
|
||||
count.v -= 1
|
||||
if count.v == 0 and listener.v != null:
|
||||
listener.v.queue_free()
|
||||
|
||||
var subscription = GDRx.gd.from_godot_signal(listener.v.on_event).subscribe(
|
||||
observer, GDRx.basic.noop, GDRx.basic.noop,
|
||||
scheduler
|
||||
)
|
||||
|
||||
var cd : CompositeDisposable = CompositeDisposable.new([subscription, Disposable.new(dispose)])
|
||||
return cd
|
||||
|
||||
return Observable.new(subscribe)
|
||||
69
addons/reactivex/engine/observable/godotsignal.gd
Normal file
@ -0,0 +1,69 @@
|
||||
## Represents a Godot [Signal] as an observable sequence
|
||||
static func from_godot_signal_(
|
||||
sig : Signal,
|
||||
scheduler : SchedulerBase = null
|
||||
) -> Observable:
|
||||
var subscribe = func(
|
||||
observer : ObserverBase,
|
||||
scheduler_ : SchedulerBase = null
|
||||
) -> DisposableBase:
|
||||
var _scheduler : SchedulerBase = null
|
||||
if scheduler != null: _scheduler = scheduler
|
||||
elif scheduler_ != null: _scheduler = scheduler_
|
||||
else: _scheduler = GodotSignalScheduler.singleton()
|
||||
|
||||
var obj = instance_from_id(sig.get_object_id())
|
||||
if obj == null:
|
||||
GDRx.raise(NullReferenceError.new())
|
||||
return Disposable.new()
|
||||
var n_args = -1
|
||||
var sig_lst = obj.get_signal_list()
|
||||
for dict in sig_lst:
|
||||
if dict["name"] == sig.get_name():
|
||||
n_args = dict["args"].size()
|
||||
break
|
||||
|
||||
var action : Callable
|
||||
match n_args:
|
||||
0:
|
||||
action = func():
|
||||
observer.on_next(Tuple.new([]))
|
||||
1:
|
||||
action = func(arg1):
|
||||
if arg1 is Array:
|
||||
observer.on_next(Tuple.new(arg1))
|
||||
else:
|
||||
observer.on_next(arg1)
|
||||
2:
|
||||
action = func(arg1, arg2):
|
||||
observer.on_next(Tuple.new([arg1, arg2]))
|
||||
3:
|
||||
action = func(arg1, arg2, arg3):
|
||||
observer.on_next(Tuple.new([arg1, arg2, arg3]))
|
||||
4:
|
||||
action = func(arg1, arg2, arg3, arg4):
|
||||
observer.on_next(Tuple.new([arg1, arg2, arg3, arg4]))
|
||||
5:
|
||||
action = func(arg1, arg2, arg3, arg4, arg5):
|
||||
observer.on_next(Tuple.new([arg1, arg2, arg3, arg4, arg5]))
|
||||
6:
|
||||
action = func(arg1, arg2, arg3, arg4, arg5, arg6):
|
||||
observer.on_next(Tuple.new([arg1, arg2, arg3, arg4, arg5, arg6]))
|
||||
7:
|
||||
action = func(arg1, arg2, arg3, arg4, arg5, arg6, arg7):
|
||||
observer.on_next(Tuple.new([arg1, arg2, arg3, arg4, arg5, arg6, arg7]))
|
||||
8:
|
||||
action = func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8):
|
||||
observer.on_next(Tuple.new([arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8]))
|
||||
_:
|
||||
GDRx.raise(TooManyArgumentsError.new(
|
||||
"Only up to 8 signal parameters supported! Use lists instead!"))
|
||||
return Disposable.new()
|
||||
|
||||
if not _scheduler is GodotSignalScheduler:
|
||||
_scheduler = GodotSignalScheduler.singleton()
|
||||
|
||||
var godot_signal_scheduler : GodotSignalScheduler = _scheduler
|
||||
return godot_signal_scheduler.schedule_signal(sig, n_args, action)
|
||||
|
||||
return Observable.new(subscribe)
|
||||
77
addons/reactivex/engine/observable/httprequest.gd
Normal file
@ -0,0 +1,77 @@
|
||||
## Struct representing the results of a [HTTPRequest]
|
||||
class HttpRequestResult:
|
||||
var result : int
|
||||
var response_code : int
|
||||
var headers : PackedStringArray
|
||||
var body : PackedByteArray
|
||||
var decoded : String = ""
|
||||
|
||||
## Performs an [HTTPRequest] on subscribe
|
||||
static func from_http_request_(
|
||||
url : String,
|
||||
request_data = "",
|
||||
raw : bool = false,
|
||||
encoding : String = "",
|
||||
requester : HTTPRequest = null,
|
||||
custom_headers : PackedStringArray = PackedStringArray(),
|
||||
# tls_validate_domain : bool = true,
|
||||
method : HTTPClient.Method = HTTPClient.METHOD_GET
|
||||
) -> Observable:
|
||||
|
||||
var subscribe = func(
|
||||
observer : ObserverBase,
|
||||
scheduler : SchedulerBase = null
|
||||
) -> DisposableBase:
|
||||
|
||||
var gss : GodotSignalScheduler
|
||||
if scheduler != null and scheduler is GodotSignalScheduler:
|
||||
gss = scheduler as GodotSignalScheduler
|
||||
else: gss = GodotSignalScheduler.singleton()
|
||||
|
||||
var _requester : HTTPRequest = requester if requester != null else HTTPRequest.new()
|
||||
if requester == null: GDRx.add_child(_requester)
|
||||
var n_bytes = _requester.get_downloaded_bytes()
|
||||
|
||||
var action = func(result : int, response_code : int, headers : PackedStringArray, body : PackedByteArray):
|
||||
if requester == null: _requester.queue_free()
|
||||
if _requester.get_downloaded_bytes() - n_bytes == 0:
|
||||
observer.on_error(HttpRequestFailedError.new(url, 0, "No data received"))
|
||||
return
|
||||
var http_request_result : HttpRequestResult = HttpRequestResult.new()
|
||||
http_request_result.result = result
|
||||
http_request_result.response_code = response_code
|
||||
http_request_result.headers = headers
|
||||
http_request_result.body = body
|
||||
if not encoding.is_empty():
|
||||
var encodings = {
|
||||
"ascii" : func(): return body.get_string_from_ascii(),
|
||||
"utf8" : func(): return body.get_string_from_utf8(),
|
||||
"utf16" : func(): return body.get_string_from_utf16(),
|
||||
"utf32" : func(): return body.get_string_from_utf32()
|
||||
}
|
||||
if not encoding in encodings:
|
||||
observer.on_error(BadArgumentError.new("Unknown encoding: " + str(encoding)))
|
||||
return
|
||||
var get_string : Callable = encodings[encoding]
|
||||
http_request_result.decoded = get_string.call()
|
||||
observer.on_next(http_request_result)
|
||||
observer.on_completed()
|
||||
|
||||
var dispose = func():
|
||||
if requester == null:
|
||||
_requester.cancel_request()
|
||||
_requester.queue_free()
|
||||
|
||||
var request_cb : Callable = _requester.request_raw if raw else _requester.request
|
||||
var error_code = request_cb.call(url, custom_headers, method, request_data)
|
||||
if error_code:
|
||||
dispose.call()
|
||||
observer.on_error(HttpRequestFailedError.new(
|
||||
url, error_code, "Unable to create request"))
|
||||
return Disposable.new()
|
||||
|
||||
var subscription = gss.schedule_signal(_requester.request_completed, 4, action)
|
||||
var cd = CompositeDisposable.new([Disposable.new(dispose), subscription])
|
||||
return cd
|
||||
|
||||
return Observable.new(subscribe)
|
||||
@ -0,0 +1,293 @@
|
||||
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)
|
||||
@ -0,0 +1,187 @@
|
||||
extends ReactiveDictionaryBase
|
||||
class_name ReactiveDictionary
|
||||
|
||||
var _count : int
|
||||
var _data : Dictionary
|
||||
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 : ReactiveDictionary = 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(dict : Dictionary = {}):
|
||||
super._init()
|
||||
|
||||
self._count = 0
|
||||
self._data = dict.duplicate()
|
||||
self._observers = {}
|
||||
self._rwlock = ReadWriteLock.new()
|
||||
self.is_disposed = false
|
||||
|
||||
self._observe_add_key = Observable.new(self._get_subscription(DictionaryAddKeyEvent))
|
||||
self._observe_remove_key = Observable.new(self._get_subscription(DictionaryRemoveKeyEvent))
|
||||
self._observer_update_value = Observable.new(self._get_subscription(DictionaryUpdateValueEvent))
|
||||
|
||||
func ObserveCountChanged(notify_current_count : bool = false) -> Observable:
|
||||
return Observable.new(self._get_subscription("CountChanged", notify_current_count))
|
||||
|
||||
func clear():
|
||||
var keys = self.keys()
|
||||
for key in keys:
|
||||
self.erase(key)
|
||||
|
||||
func erase(key) -> bool:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return false
|
||||
if self._data.has(key):
|
||||
var value = self._data[key]
|
||||
self._data.erase(key)
|
||||
self._count -= 1
|
||||
self._notify_all("CountChanged", self._count)
|
||||
self._notify_all(DictionaryRemoveKeyEvent, DictionaryRemoveKeyEvent.new(key, value))
|
||||
return true
|
||||
return false
|
||||
|
||||
func set_pair(key, value):
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return null
|
||||
if not self._data.has(key):
|
||||
self._count += 1
|
||||
self._data[key] = value
|
||||
self._notify_all("CountChanged", self._count)
|
||||
self._notify_all(DictionaryAddKeyEvent, DictionaryAddKeyEvent.new(key, value))
|
||||
else:
|
||||
self._data[key] = value
|
||||
self._notify_all(DictionaryUpdateValueEvent, DictionaryUpdateValueEvent.new(key, value))
|
||||
|
||||
func to_dict() -> Dictionary:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return {}
|
||||
return self._data.duplicate()
|
||||
|
||||
func find_key(value) -> Variant:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return null
|
||||
return self._data.find_key(value)
|
||||
|
||||
func get_value(key, default = null) -> Variant:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return null
|
||||
return self._data.get(key, default)
|
||||
|
||||
func has_key(key) -> bool:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return false
|
||||
return self._data.has(key)
|
||||
|
||||
func has_all(keys : Array) -> bool:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return false
|
||||
return self._data.has_all(keys)
|
||||
|
||||
func hash() -> int:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return 0
|
||||
return self._data.hash()
|
||||
|
||||
func is_empty() -> bool:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return false
|
||||
return self._data.is_empty()
|
||||
|
||||
func keys() -> Array:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return []
|
||||
return self._data.keys()
|
||||
|
||||
func size() -> int:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return -1
|
||||
return self._count
|
||||
|
||||
func values() -> Array:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return []
|
||||
return self._data.values()
|
||||
|
||||
func dispose():
|
||||
if this.is_disposed:
|
||||
return
|
||||
this.is_disposed = true
|
||||
|
||||
this._disconnect_all(DictionaryAddKeyEvent)
|
||||
this._disconnect_all(DictionaryRemoveKeyEvent)
|
||||
this._disconnect_all(DictionaryUpdateValueEvent)
|
||||
this._disconnect_all("CountChanged")
|
||||
|
||||
this._data = {}
|
||||
this._count = -1
|
||||
this._observers = {}
|
||||
|
||||
func to_readonly() -> ReadOnlyReactiveDictionary:
|
||||
return ReadOnlyReactiveDictionary.new(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if self.is_disposed:
|
||||
return "<<Disposed ReactiveDictionary>>"
|
||||
return str(self._data)
|
||||
|
||||
func iter() -> Iterator:
|
||||
return GDRx.iter(self._data)
|
||||
@ -0,0 +1,248 @@
|
||||
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)
|
||||
)
|
||||
@ -0,0 +1,147 @@
|
||||
extends ReactiveSignalBase
|
||||
class_name ReactiveSignal
|
||||
|
||||
const MAX_ARGS : int = 8
|
||||
|
||||
signal _signal0
|
||||
signal _signal1(arg0)
|
||||
signal _signal2(arg0, arg1)
|
||||
signal _signal3(arg0, arg1, arg2)
|
||||
signal _signal4(arg0, arg1, arg2, arg3)
|
||||
signal _signal5(arg0, arg1, arg2, arg3, arg4)
|
||||
signal _signal6(arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
signal _signal7(arg0, arg1, arg2, arg3, arg4, arg5, arg6)
|
||||
signal _signal8(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
||||
|
||||
var is_disposed : bool
|
||||
|
||||
var _signal : Signal
|
||||
var _n_args : int
|
||||
var _observers : Array[ObserverBase]
|
||||
var _connections : Array[Callable]
|
||||
|
||||
func _init(n_args : int = 1):
|
||||
if n_args > MAX_ARGS:
|
||||
GDRx.raise(TooManyArgumentsError.new(
|
||||
"Only up to 8 signal parameters supported! Use lists instead!"))
|
||||
super._init(func(__, ___ = null): return Disposable.new())
|
||||
return
|
||||
|
||||
self.is_disposed = false
|
||||
|
||||
self._n_args = n_args
|
||||
self._observers = []
|
||||
match self._n_args:
|
||||
0: self._signal = self._signal0
|
||||
1: self._signal = self._signal1
|
||||
2: self._signal = self._signal2
|
||||
3: self._signal = self._signal3
|
||||
4: self._signal = self._signal4
|
||||
5: self._signal = self._signal5
|
||||
6: self._signal = self._signal6
|
||||
7: self._signal = self._signal7
|
||||
8: self._signal = self._signal8
|
||||
_: assert(false) # "should not happen"
|
||||
|
||||
var wself = weakref(self)
|
||||
|
||||
var subscribe_ = func(observer : ObserverBase, scheduler_ : SchedulerBase = null) -> DisposableBase:
|
||||
var rself : ReactiveSignal = wself.get_ref()
|
||||
if rself == null:
|
||||
return Disposable.new()
|
||||
|
||||
var scheduler : GodotSignalScheduler = scheduler_ if scheduler_ is GodotSignalScheduler else GodotSignalScheduler.singleton()
|
||||
|
||||
rself._observers.push_back(observer)
|
||||
var on_dispose = func():
|
||||
var _rself : ReactiveSignal = wself.get_ref()
|
||||
if _rself == null:
|
||||
return
|
||||
_rself._observers.erase(observer)
|
||||
|
||||
var action : Callable
|
||||
match n_args:
|
||||
0:
|
||||
action = func():
|
||||
observer.on_next(Tuple.new([]))
|
||||
1:
|
||||
action = func(arg1):
|
||||
if arg1 is Array:
|
||||
observer.on_next(Tuple.new(arg1))
|
||||
else:
|
||||
observer.on_next(arg1)
|
||||
2:
|
||||
action = func(arg1, arg2):
|
||||
observer.on_next(Tuple.new([arg1, arg2]))
|
||||
3:
|
||||
action = func(arg1, arg2, arg3):
|
||||
observer.on_next(Tuple.new([arg1, arg2, arg3]))
|
||||
4:
|
||||
action = func(arg1, arg2, arg3, arg4):
|
||||
observer.on_next(Tuple.new([arg1, arg2, arg3, arg4]))
|
||||
5:
|
||||
action = func(arg1, arg2, arg3, arg4, arg5):
|
||||
observer.on_next(Tuple.new([arg1, arg2, arg3, arg4, arg5]))
|
||||
6:
|
||||
action = func(arg1, arg2, arg3, arg4, arg5, arg6):
|
||||
observer.on_next(Tuple.new([arg1, arg2, arg3, arg4, arg5, arg6]))
|
||||
7:
|
||||
action = func(arg1, arg2, arg3, arg4, arg5, arg6, arg7):
|
||||
observer.on_next(Tuple.new([arg1, arg2, arg3, arg4, arg5, arg6, arg7]))
|
||||
8:
|
||||
action = func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8):
|
||||
observer.on_next(Tuple.new([arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8]))
|
||||
_:
|
||||
GDRx.raise(TooManyArgumentsError.new(
|
||||
"Only up to 8 signal parameters supported! Use lists instead!"))
|
||||
return Disposable.new()
|
||||
|
||||
var sub = scheduler.schedule_signal(rself._signal, rself._n_args, action)
|
||||
var cd = CompositeDisposable.new([sub, Disposable.new(on_dispose)])
|
||||
return cd
|
||||
|
||||
super._init(subscribe_)
|
||||
|
||||
func _emit(args = []):
|
||||
if self.is_disposed:
|
||||
return
|
||||
|
||||
var args_ = []
|
||||
GDRx.iter(args).foreach(func(arg):
|
||||
args_.push_back(arg))
|
||||
|
||||
match self._n_args:
|
||||
0: self._signal.emit()
|
||||
1: self._signal.emit(args_[0])
|
||||
2: self._signal.emit(args_[0], args_[1])
|
||||
3: self._signal.emit(args_[0], args_[1], args_[2])
|
||||
4: self._signal.emit(args_[0], args_[1], args_[2], args_[3])
|
||||
5: self._signal.emit(args_[0], args_[1], args_[2], args_[3], args_[4])
|
||||
6: self._signal.emit(args_[0], args_[1], args_[2], args_[3], args_[4], args_[5])
|
||||
7: self._signal.emit(args_[0], args_[1], args_[2], args_[3], args_[4], args_[5], args_[6])
|
||||
8: self._signal.emit(args_[0], args_[1], args_[2], args_[3], args_[4], args_[5], args_[6], args_[7])
|
||||
_: assert(false) # "should not happen"
|
||||
|
||||
func attach(cb : Callable):
|
||||
if self.is_disposed:
|
||||
return
|
||||
if not cb in self._connections:
|
||||
self._signal.connect(cb)
|
||||
self._connections.push_back(cb)
|
||||
|
||||
func detach(cb : Callable):
|
||||
if self.is_disposed:
|
||||
return
|
||||
if cb in self._connections:
|
||||
self._signal.disconnect(cb)
|
||||
self._connections.erase(cb)
|
||||
|
||||
func dispose():
|
||||
if this.is_disposed:
|
||||
return
|
||||
this.is_disposed = true
|
||||
|
||||
for observer in this._observers:
|
||||
observer.on_completed()
|
||||
for conn in this._connections:
|
||||
self._signal.disconnect(conn)
|
||||
@ -0,0 +1,67 @@
|
||||
extends ReadOnlyReactiveCollectionBase
|
||||
class_name ReadOnlyReactiveCollection
|
||||
|
||||
var _collection : ReactiveCollectionBase
|
||||
var _observers : Array[ObserverBase]
|
||||
|
||||
var is_disposed : bool
|
||||
|
||||
class _Observable extends Observable:
|
||||
func _init(source : Observable, observers : Array[ObserverBase]):
|
||||
var subscribe_ = func(observer, scheduler = null):
|
||||
if not observer in observers:
|
||||
observers.push_back(observer)
|
||||
return source.subscribe1(observer, scheduler)
|
||||
super._init(subscribe_)
|
||||
|
||||
func _init(collection : ReactiveCollectionBase):
|
||||
super._init()
|
||||
|
||||
self.is_disposed = false
|
||||
|
||||
self._collection = collection
|
||||
self._observe_add = self._Observable.new(collection.ObserveAdd, self._observers)
|
||||
self._observe_move = self._Observable.new(collection.ObserveMove, self._observers)
|
||||
self._observe_remove = self._Observable.new(collection.ObserveRemove, self._observers)
|
||||
self._observe_replace = self._Observable.new(collection.ObserveReplace, self._observers)
|
||||
self._observe_reset = self._Observable.new(collection.ObserveReset, self._observers)
|
||||
|
||||
func ObserveCountChanged(_notify_current_count : bool = false) -> Observable:
|
||||
return self._collection.ObserveCountChanged(_notify_current_count)
|
||||
|
||||
func at(index : int):
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return null
|
||||
return self._collection.at(index)
|
||||
|
||||
func find(item) -> int:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return -1
|
||||
return self._collection.find(item)
|
||||
|
||||
func to_list() -> Array:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return []
|
||||
return self._collection.to_list()
|
||||
|
||||
func iter() -> Iterator:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return null
|
||||
return self._collection.iter()
|
||||
|
||||
func size() -> int:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return -1
|
||||
return self._collection.size()
|
||||
|
||||
func dispose():
|
||||
if this.is_disposed:
|
||||
return
|
||||
this.is_disposed = true
|
||||
for observer in this._observers:
|
||||
observer.on_completed()
|
||||
@ -0,0 +1,98 @@
|
||||
extends ReadOnlyReactiveDictionaryBase
|
||||
class_name ReadOnlyReactiveDictionary
|
||||
|
||||
var _dict : ReactiveDictionaryBase
|
||||
var _observers : Array[ObserverBase]
|
||||
|
||||
var is_disposed : bool
|
||||
|
||||
class _Observable extends Observable:
|
||||
func _init(source : Observable, observers : Array[ObserverBase]):
|
||||
var subscribe_ = func(observer, scheduler = null):
|
||||
if not observer in observers:
|
||||
observers.push_back(observer)
|
||||
return source.subscribe1(observer, scheduler)
|
||||
super._init(subscribe_)
|
||||
|
||||
func _init(dict : ReactiveDictionary):
|
||||
super._init()
|
||||
|
||||
self.is_disposed = false
|
||||
|
||||
self._dict = dict
|
||||
self._observe_add_key = self._Observable.new(dict.ObserveAddKey, self._observers)
|
||||
self._observe_remove_key = self._Observable.new(dict.ObserveRemoveKey, self._observers)
|
||||
self._observer_update_value = self._Observable.new(dict.ObserveUpdateValue, self._observers)
|
||||
|
||||
func ObserveCountChanged(notify_current_count : bool = false) -> Observable:
|
||||
return self._dict.ObserveCountChanged(notify_current_count)
|
||||
|
||||
func to_dict() -> Dictionary:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return {}
|
||||
return self._dict.to_dict()
|
||||
|
||||
func find_key(value) -> Variant:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return null
|
||||
return self._dict.find_key(value)
|
||||
|
||||
func get_value(key, default = null) -> Variant:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return null
|
||||
return self._dict.get_value(key, default)
|
||||
|
||||
func has_key(key) -> bool:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return false
|
||||
return self._dict.has_key(key)
|
||||
|
||||
func has_all(keys : Array) -> bool:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return false
|
||||
return self._dict.has_all(keys)
|
||||
|
||||
func hash() -> int:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return 0
|
||||
return self._dict.hash()
|
||||
|
||||
func is_empty() -> bool:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return false
|
||||
return self._dict.is_empty()
|
||||
|
||||
func keys() -> Array:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return []
|
||||
return self._dict.keys()
|
||||
|
||||
func size() -> int:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return -1
|
||||
return self._dict.size()
|
||||
|
||||
func values() -> Array:
|
||||
if self.is_disposed:
|
||||
DisposedError.raise()
|
||||
return []
|
||||
return self._dict.value()
|
||||
|
||||
func dispose():
|
||||
if this.is_disposed:
|
||||
return
|
||||
this.is_disposed = true
|
||||
for observer in this._observers:
|
||||
observer.on_completed()
|
||||
|
||||
func iter() -> Iterator:
|
||||
return GDRx.iter(self._dict)
|
||||
@ -0,0 +1,118 @@
|
||||
extends ReadOnlyReactivePropertyBase
|
||||
class_name ReadOnlyReactiveProperty
|
||||
|
||||
## An observable property with read-only access.
|
||||
##
|
||||
## 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 _init(
|
||||
source : ObservableBase,
|
||||
initial_value_,
|
||||
distinct_until_changed_ : bool = true,
|
||||
raise_latest_value_on_subscribe_ : bool = true
|
||||
):
|
||||
var wself : WeakRef = weakref(self)
|
||||
|
||||
self._observers = []
|
||||
self._rwlock = ReadWriteLock.new()
|
||||
self.is_disposed = false
|
||||
|
||||
self._source_subscription = source.subscribe(
|
||||
func(i): wself.get_ref()._on_next(i),
|
||||
func(e): wself.get_ref()._on_error(e),
|
||||
func(): wself.get_ref()._on_completed()
|
||||
)
|
||||
self._latest_value = initial_value_
|
||||
self._distinct_until_changed = distinct_until_changed_
|
||||
self._raise_latest_value_on_subscribe = raise_latest_value_on_subscribe_
|
||||
|
||||
@warning_ignore("shadowed_variable")
|
||||
var subscribe = func(
|
||||
observer : ObserverBase,
|
||||
_scheduler : SchedulerBase = null
|
||||
) -> DisposableBase:
|
||||
var prop : ReadOnlyReactiveProperty = 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.append(observer)
|
||||
|
||||
if prop._raise_latest_value_on_subscribe:
|
||||
observer.on_next(prop._latest_value)
|
||||
|
||||
var dispose_ = func():
|
||||
var _prop = wself.get_ref()
|
||||
if not _prop:
|
||||
return
|
||||
if true:
|
||||
var __ = ReadWriteLockGuard.new(_prop._rwlock, false)
|
||||
_prop._observers.erase(observer)
|
||||
|
||||
return Disposable.new(dispose_)
|
||||
|
||||
super._init(subscribe)
|
||||
|
||||
func _get_value():
|
||||
return self._latest_value
|
||||
|
||||
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()
|
||||
|
||||
this._source_subscription.dispose()
|
||||
|
||||
func _on_next(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 _on_error(error_):
|
||||
var observers_ : Array[ObserverBase]
|
||||
if true:
|
||||
var __ = ReadWriteLockGuard.new(self._rwlock, true)
|
||||
observers_ = self._observers.duplicate()
|
||||
for obs in observers_:
|
||||
obs.on_error(error_)
|
||||
|
||||
func _on_completed():
|
||||
self.dispose()
|
||||
|
||||
func _to_string() -> String:
|
||||
if self.is_disposed:
|
||||
return "<<Disposed ReadOnlyReactiveProperty>>"
|
||||
return str(self.Value)
|
||||
@ -0,0 +1,64 @@
|
||||
extends ReactiveCollection
|
||||
class_name ReactiveCollectionT
|
||||
|
||||
var _type
|
||||
var _type_equality : Callable
|
||||
|
||||
var _push_type_err : bool
|
||||
|
||||
var T:
|
||||
get: return self._type
|
||||
|
||||
func _init(collection = [], type_ = TYPE_MAX, push_type_err : bool = true, type_equality : Callable = GDRx.basic.default_type_equality):
|
||||
self._type = type_
|
||||
self._push_type_err = push_type_err
|
||||
self._type_equality = type_equality
|
||||
super._init(collection)
|
||||
|
||||
func _type_check(value) -> bool:
|
||||
return not self._type_equality.call(self._type, value)
|
||||
|
||||
func _type_check_fail(value, default = null):
|
||||
var exc = TypeMismatchError.new(value)
|
||||
if self._push_type_err: push_error(exc)
|
||||
exc.throw(value)
|
||||
return default
|
||||
|
||||
func add_item(item) -> int:
|
||||
if self._type_check(item):
|
||||
return self._type_check_fail(item, -1)
|
||||
return super.add_item(item)
|
||||
|
||||
func remove_item(item) -> int:
|
||||
if self._type_check(item):
|
||||
return self._type_check_fail(item, -1)
|
||||
return super.remove_item(item)
|
||||
|
||||
func replace_item(item, with) -> int:
|
||||
if self._type_check(item):
|
||||
return self._type_check_fail(item, -1)
|
||||
if self._type_check(with):
|
||||
return self._type_check_fail(with, -1)
|
||||
return super.replace_item(item, with)
|
||||
|
||||
func replace_at(index : int, item) -> Variant:
|
||||
if self._type_check(item):
|
||||
return self._type_check_fail(item)
|
||||
return super.replace_at(index, item)
|
||||
|
||||
func insert_at(index : int, item):
|
||||
if self._type_check(item):
|
||||
self._type_check_fail(item)
|
||||
return
|
||||
super.insert_at(index, item)
|
||||
|
||||
func find(item) -> int:
|
||||
if self._type_check(item):
|
||||
return self._type_check_fail(item, -1)
|
||||
return super.find(item)
|
||||
|
||||
func to_readonly() -> ReadOnlyReactiveCollection:
|
||||
return ReadOnlyReactiveCollectionT.new(self)
|
||||
|
||||
func to_typed_readonly() -> ReadOnlyReactiveCollectionT:
|
||||
return self.to_readonly() as ReadOnlyReactiveCollectionT
|
||||
@ -0,0 +1,45 @@
|
||||
extends ReactiveProperty
|
||||
class_name ReactivePropertyT
|
||||
|
||||
var _type
|
||||
var _type_equality : Callable
|
||||
|
||||
var _push_type_err : bool
|
||||
|
||||
var T:
|
||||
get: return self._type
|
||||
|
||||
func _init(
|
||||
initial_value_,
|
||||
type_ = TYPE_MAX,
|
||||
distinct_until_changed_ : bool = true,
|
||||
raise_latest_value_on_subscribe_ : bool = true,
|
||||
source : Observable = null,
|
||||
push_type_err : bool = true,
|
||||
type_equality : Callable = GDRx.basic.default_type_equality
|
||||
):
|
||||
self._type = type_
|
||||
self._push_type_err = push_type_err
|
||||
self._type_equality = type_equality
|
||||
super._init(initial_value_, distinct_until_changed_, raise_latest_value_on_subscribe_, source)
|
||||
|
||||
func _type_check_fail(value, default = null):
|
||||
var exc = TypeMismatchError.new(value)
|
||||
if self._push_type_err: push_error(exc)
|
||||
exc.throw(value)
|
||||
return default
|
||||
|
||||
func _set_value(value):
|
||||
if not self._type_equality.call(self._type, value):
|
||||
self._type_check_fail(value)
|
||||
return
|
||||
super._set_value(value)
|
||||
|
||||
func to_readonly() -> ReadOnlyReactiveProperty:
|
||||
return ReadOnlyReactivePropertyT.new(
|
||||
self, Value, self._distinct_until_changed,
|
||||
self._raise_latest_value_on_subscribe
|
||||
)
|
||||
|
||||
func to_typed_readonly() -> ReadOnlyReactivePropertyT:
|
||||
return self.to_readonly() as ReadOnlyReactivePropertyT
|
||||
@ -0,0 +1,57 @@
|
||||
extends ReactiveSignal
|
||||
class_name ReactiveSignalT
|
||||
|
||||
var _type_list : Array
|
||||
var _type_equality : Callable
|
||||
|
||||
var _arg_names : Array[StringName]
|
||||
|
||||
var _push_type_err : bool
|
||||
|
||||
var TList : Array:
|
||||
get: return self._type_list
|
||||
|
||||
var ArgNames : Array[StringName]:
|
||||
get: return self._arg_names
|
||||
|
||||
func _init(
|
||||
n_args : int,
|
||||
type_list_ : Array = [],
|
||||
arg_names_ : Array[StringName] = [],
|
||||
push_type_err : bool = true,
|
||||
type_equality : Callable = GDRx.basic.default_type_equality):
|
||||
var type_list = type_list_.duplicate()
|
||||
while type_list.size() < n_args:
|
||||
type_list.push_back(TYPE_MAX)
|
||||
self._type_list = type_list
|
||||
self._type_list.make_read_only()
|
||||
|
||||
var c = 0
|
||||
var arg_names : Array[StringName] = arg_names_.duplicate()
|
||||
while arg_names.size() < n_args:
|
||||
arg_names.push_back("arg" + str(c))
|
||||
c += 1
|
||||
self._arg_names = arg_names
|
||||
self._arg_names.make_read_only()
|
||||
|
||||
self._push_type_err = push_type_err
|
||||
self._type_equality = type_equality
|
||||
|
||||
super._init(n_args)
|
||||
|
||||
func _type_check_fail(value, default = null):
|
||||
var exc = TypeMismatchError.new(value)
|
||||
if self._push_type_err: push_error(exc)
|
||||
exc.throw(value)
|
||||
return default
|
||||
|
||||
func _emit(args = []):
|
||||
var args_valid = RefValue.Set(true)
|
||||
GDRx.iter(args).enumerate(func(value, i : int):
|
||||
if not self._type_equality.call(self._type_list[i], value):
|
||||
args_valid.v = false
|
||||
self._type_check_fail(value)
|
||||
return false)
|
||||
|
||||
if args_valid.v:
|
||||
super._emit(args)
|
||||
@ -0,0 +1,10 @@
|
||||
extends ReadOnlyReactiveCollection
|
||||
class_name ReadOnlyReactiveCollectionT
|
||||
|
||||
var _type
|
||||
var T:
|
||||
get: return self._type
|
||||
|
||||
func _init(collection : ReactiveCollectionT):
|
||||
self._type = collection.T
|
||||
super._init(collection)
|
||||
@ -0,0 +1,15 @@
|
||||
extends ReadOnlyReactiveProperty
|
||||
class_name ReadOnlyReactivePropertyT
|
||||
|
||||
var _type
|
||||
var T:
|
||||
get: return self._type
|
||||
|
||||
func _init(
|
||||
source : ReactivePropertyT,
|
||||
initial_value_,
|
||||
distinct_until_changed_ : bool = true,
|
||||
raise_latest_value_on_subscribe_ : bool = true
|
||||
):
|
||||
self._type = source.T
|
||||
super._init(source, initial_value_, distinct_until_changed_, raise_latest_value_on_subscribe_)
|
||||
69
addons/reactivex/engine/operators/_processtimeinterval.gd
Normal file
@ -0,0 +1,69 @@
|
||||
## Returns the amount of process time passed between two items using idle time.
|
||||
## The emitted item is a [Tuple] containing the real item as first and the
|
||||
## time delta as second component.
|
||||
static func process_time_interval_(initial_time : float = 0.0) -> Callable:
|
||||
var process_time_interval = func(source : Observable) -> Observable:
|
||||
|
||||
var subscribe = func(
|
||||
observer : ObserverBase,
|
||||
scheduler : SchedulerBase = null
|
||||
) -> DisposableBase:
|
||||
var _scheduler : SchedulerBase
|
||||
_scheduler = scheduler if scheduler != null else CurrentThreadScheduler.singleton()
|
||||
|
||||
var dt = RefValue.Set(initial_time)
|
||||
|
||||
var on_process = func(delta : float):
|
||||
dt.v += delta
|
||||
var process_sub : DisposableBase = GDRx.on_idle_frame().subscribe(on_process)
|
||||
|
||||
var on_next = func(value):
|
||||
var span : float = dt.v
|
||||
dt.v = 0.0
|
||||
observer.on_next(Tuple.new([value, span]))
|
||||
|
||||
var sub : DisposableBase = source.subscribe(
|
||||
on_next, observer.on_error, observer.on_completed,
|
||||
_scheduler
|
||||
)
|
||||
|
||||
return CompositeDisposable.new([process_sub, sub])
|
||||
|
||||
return Observable.new(subscribe)
|
||||
|
||||
return process_time_interval
|
||||
|
||||
## Returns the amount of time passed between two items using physics steps.
|
||||
## The emitted item is a [Tuple] containing the real item as first and the
|
||||
## time delta as second component.
|
||||
static func physics_time_interval_(initial_time : float = 0.0) -> Callable:
|
||||
var physics_time_interval = func(source : Observable) -> Observable:
|
||||
|
||||
var subscribe = func(
|
||||
observer : ObserverBase,
|
||||
scheduler : SchedulerBase = null
|
||||
) -> DisposableBase:
|
||||
var _scheduler : SchedulerBase
|
||||
_scheduler = scheduler if scheduler != null else CurrentThreadScheduler.singleton()
|
||||
|
||||
var dt = RefValue.Set(initial_time)
|
||||
|
||||
var on_process = func(delta : float):
|
||||
dt.v += delta
|
||||
var process_sub : DisposableBase = GDRx.on_physics_step().subscribe(on_process)
|
||||
|
||||
var on_next = func(value):
|
||||
var span : float = dt.v
|
||||
dt.v = 0.0
|
||||
observer.on_next(Tuple.new([value, span]))
|
||||
|
||||
var sub : DisposableBase = source.subscribe(
|
||||
on_next, observer.on_error, observer.on_completed,
|
||||
_scheduler
|
||||
)
|
||||
|
||||
return CompositeDisposable.new([process_sub, sub])
|
||||
|
||||
return Observable.new(subscribe)
|
||||
|
||||
return physics_time_interval
|
||||
213
addons/reactivex/engine/scheduler/godotsignalscheduler.gd
Normal file
@ -0,0 +1,213 @@
|
||||
extends GodotSignalSchedulerBase
|
||||
class_name GodotSignalScheduler
|
||||
|
||||
const UTC_ZERO : float = Scheduler.UTC_ZERO
|
||||
const DELTA_ZERO : float = Scheduler.DELTA_ZERO
|
||||
|
||||
func _init(verify_ = null):
|
||||
if not verify_ == "GDRx":
|
||||
push_warning("Warning! Must only instance Scheduler from GDRx singleton!")
|
||||
|
||||
static func singleton() -> GodotSignalScheduler:
|
||||
return GDRx.GodotSignalScheduler_
|
||||
|
||||
## Represents a notion of time for this scheduler. Tasks being
|
||||
## scheduled on a scheduler will adhere to the time denoted by this
|
||||
## property.
|
||||
## [br]
|
||||
## [b]Returns:[/b]
|
||||
## [br]
|
||||
## The scheduler's current time, as a datetime instance.
|
||||
func now() -> float:
|
||||
return GDRx.basic.default_now()
|
||||
|
||||
## Invoke the given given action. This is typically called by instances
|
||||
## of [ScheduledItem].
|
||||
## [br]
|
||||
## [b]Args:[/b]
|
||||
## [br]
|
||||
## [code]action[/code] Action to be executed.
|
||||
## [br]
|
||||
## [code]state[/code] [Optional] state to be given to the action function.
|
||||
## [br][br]
|
||||
## [b]Returns:[/b]
|
||||
## [br]
|
||||
## The disposable object returned by the action, if any; or a new
|
||||
## (no-op) disposable otherwise.
|
||||
func invoke_action(action : Callable, state = null) -> DisposableBase:
|
||||
var ret = action.call(self, state)
|
||||
if ret is DisposableBase:
|
||||
return ret
|
||||
return Disposable.new()
|
||||
|
||||
## Schedules an action to be executed when the [Signal] is emitted.
|
||||
## [br]
|
||||
## [b]Args:[/b]
|
||||
## [br]
|
||||
## [code]sig[/code] A Godot [Signal] instance to schedule.
|
||||
## [br]
|
||||
## [code]n_args[/code] Number of signal arguments. This is the same number
|
||||
## of parameters for [code]action[/code].
|
||||
## [br]
|
||||
## [code]action[/code] Scheduled callback action for the signal. This is a
|
||||
## function with [code]n_args[/code] parameters.
|
||||
## [br]
|
||||
## [code]state[/code] [Optional] state to be given to the action function.
|
||||
## [br][br]
|
||||
##
|
||||
## [b]Returns:[/b]
|
||||
## [br]
|
||||
## The disposable object used to cancel the scheduled action.
|
||||
func schedule_signal(
|
||||
sig : Signal,
|
||||
n_args : int,
|
||||
action : Callable,
|
||||
state = null
|
||||
) -> DisposableBase:
|
||||
|
||||
var disp : Disposable = Disposable.new()
|
||||
|
||||
var signal_callback : Callable
|
||||
match n_args:
|
||||
0:
|
||||
signal_callback = func():
|
||||
var action_ = func(_scheduler : SchedulerBase, _state = null):
|
||||
action.call()
|
||||
disp = self.invoke_action(action_, state)
|
||||
1:
|
||||
signal_callback = func(arg1):
|
||||
var action_ = func(_scheduler : SchedulerBase, _state = null):
|
||||
action.call(arg1)
|
||||
disp = self.invoke_action(action_, state)
|
||||
2:
|
||||
signal_callback = func(arg1, arg2):
|
||||
var action_ = func(_scheduler : SchedulerBase, _state = null):
|
||||
action.call(arg1, arg2)
|
||||
disp = self.invoke_action(action_, state)
|
||||
3:
|
||||
signal_callback = func(arg1, arg2, arg3):
|
||||
var action_ = func(_scheduler : SchedulerBase, _state = null):
|
||||
action.call(arg1, arg2, arg3)
|
||||
disp = self.invoke_action(action_, state)
|
||||
4:
|
||||
signal_callback = func(arg1, arg2, arg3, arg4):
|
||||
var action_ = func(_scheduler : SchedulerBase, _state = null):
|
||||
action.call(arg1, arg2, arg3, arg4)
|
||||
disp = self.invoke_action(action_, state)
|
||||
5:
|
||||
signal_callback = func(arg1, arg2, arg3, arg4, arg5):
|
||||
var action_ = func(_scheduler : SchedulerBase, _state = null):
|
||||
action.call(arg1, arg2, arg3, arg4, arg5)
|
||||
disp = self.invoke_action(action_, state)
|
||||
6:
|
||||
signal_callback = func(arg1, arg2, arg3, arg4, arg5, arg6):
|
||||
var action_ = func(_scheduler : SchedulerBase, _state = null):
|
||||
action.call(arg1, arg2, arg3, arg4, arg5, arg6)
|
||||
disp = self.invoke_action(action_, state)
|
||||
7:
|
||||
signal_callback = func(arg1, arg2, arg3, arg4, arg5, arg6, arg7):
|
||||
var action_ = func(_scheduler : SchedulerBase, _state = null):
|
||||
action.call(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
||||
disp = self.invoke_action(action_, state)
|
||||
8:
|
||||
signal_callback = func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8):
|
||||
var action_ = func(_scheduler : SchedulerBase, _state = null):
|
||||
action.call(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
|
||||
disp = self.invoke_action(action_, state)
|
||||
_:
|
||||
GDRx.raise(TooManyArgumentsError.new(
|
||||
"Only up to 8 signal parameters supported! Use lists instead!"))
|
||||
return null
|
||||
|
||||
var obj = instance_from_id(sig.get_object_id())
|
||||
|
||||
var dispose = func():
|
||||
if obj != null:
|
||||
sig.disconnect(signal_callback)
|
||||
|
||||
sig.connect(signal_callback)
|
||||
|
||||
var cd : CompositeDisposable = CompositeDisposable.new([disp, Disposable.new(dispose)])
|
||||
return cd
|
||||
|
||||
## Schedules an action to be executed.
|
||||
## [br]
|
||||
## [b]Args:[/b]
|
||||
## [br]
|
||||
## [code]action[/code] Action to be executed.
|
||||
## [br]
|
||||
## [code]state[/code] [Optional] state to be given to the action function.
|
||||
## [br][br]
|
||||
## [b]Returns:[/b]
|
||||
## [br]
|
||||
## The disposable object used to cancel the scheduled action
|
||||
## (best effort).
|
||||
func schedule(action : Callable, state = null) -> DisposableBase:
|
||||
var sad : SingleAssignmentDisposable = SingleAssignmentDisposable.new()
|
||||
|
||||
var interval = func():
|
||||
sad.disposable = self.invoke_action(action, state)
|
||||
|
||||
var timer : SceneTreeTimer = GDRx.get_tree().create_timer(0.0)
|
||||
timer.connect("timeout", func(): interval.call() ; _cancel_timer(timer))
|
||||
|
||||
var dispose = func():
|
||||
_cancel_timer(timer)
|
||||
|
||||
return CompositeDisposable.new([sad, Disposable.new(dispose)])
|
||||
|
||||
## Schedules an action to be executed after duetime.
|
||||
## [br]
|
||||
## [b]Args:[/b]
|
||||
## [br]
|
||||
## [code]duetime[/code] Relative time after which to execute the action.
|
||||
## [br]
|
||||
## [code]action[/code] Action to be executed.
|
||||
## [br]
|
||||
## [code]state[/code] [Optional] state to be given to the action function.
|
||||
## [br][br]
|
||||
##
|
||||
## [b]Returns:[/b]
|
||||
## [br]
|
||||
## The disposable object used to cancel the scheduled action
|
||||
## (best effort).
|
||||
func schedule_relative(duetime, action : Callable, state = null) -> DisposableBase:
|
||||
var seconds : float = duetime
|
||||
if seconds <= 0.0:
|
||||
return self.schedule(action, state)
|
||||
|
||||
var sad : SingleAssignmentDisposable = SingleAssignmentDisposable.new()
|
||||
|
||||
var interval = func():
|
||||
sad.disposable = self.invoke_action(action, state)
|
||||
|
||||
var timer = GDRx.get_tree().create_timer(seconds)
|
||||
timer.connect("timeout", func(): interval.call() ; _cancel_timer(timer))
|
||||
|
||||
var dispose = func():
|
||||
_cancel_timer(timer)
|
||||
|
||||
return CompositeDisposable.new([sad, Disposable.new(dispose)])
|
||||
|
||||
## Schedules an action to be executed at duetime.
|
||||
## [br]
|
||||
## [b]Args:[/b]
|
||||
## [br]
|
||||
## [code]duetime[/code] Absolute time at which to execute the action.
|
||||
## [br]
|
||||
## [code]action[/code] Action to be executed.
|
||||
## [br]
|
||||
## [code]state[/code] [Optional] state to be given to the action function.
|
||||
## [br][br]
|
||||
##
|
||||
## [b]Returns:[/b]
|
||||
## [br]
|
||||
## The disposable object used to cancel the scheduled action
|
||||
## (best effort).
|
||||
func schedule_absolute(duetime, action : Callable, state = null) -> DisposableBase:
|
||||
return self.schedule_relative(duetime - self.now(), action, state)
|
||||
|
||||
## Utility function to cancel a timer
|
||||
func _cancel_timer(timer : SceneTreeTimer):
|
||||
for conn in timer.timeout.get_connections():
|
||||
timer.timeout.disconnect(conn["callable"])
|
||||
31
addons/reactivex/error/error.gd
Normal file
@ -0,0 +1,31 @@
|
||||
extends ThrowableBase
|
||||
class_name RxBaseError
|
||||
|
||||
const BASE_ERROR_TAG = "Error"
|
||||
|
||||
var _msg : String
|
||||
var _tags : ArraySet
|
||||
|
||||
func _init(msg : String, tag = "Error"):
|
||||
self._msg = msg
|
||||
self._tags = ArraySet.new()
|
||||
self._tags.add(BASE_ERROR_TAG)
|
||||
self._tags.add(tag)
|
||||
|
||||
func _to_string() -> String:
|
||||
return "[" + self.tags().back() + "::" + self._msg + "]"
|
||||
|
||||
func get_message() -> String:
|
||||
return self._msg
|
||||
|
||||
func throw(default = null) -> Variant:
|
||||
return ErrorHandler.singleton().raise(self, default)
|
||||
|
||||
func tags() -> Array[String]:
|
||||
var txs : Array[String] = []
|
||||
for t in self._tags.to_list():
|
||||
txs.append(t as String)
|
||||
return txs
|
||||
|
||||
static func raise(default = null, msg : String = "An error occured"):
|
||||
return GDRx.raise(RxBaseError.new(msg), default)
|
||||
49
addons/reactivex/error/errorhandler.gd
Normal file
@ -0,0 +1,49 @@
|
||||
class_name ErrorHandler
|
||||
|
||||
## Handles raised Errors
|
||||
##
|
||||
## Objects of type [ThrowableBase] are handled by this type's singleton
|
||||
|
||||
var _try_catch_stack : Array[TryCatch]
|
||||
var _has_failed : Dictionary
|
||||
|
||||
func _init():
|
||||
self._try_catch_stack = []
|
||||
self._has_failed = {}
|
||||
|
||||
static func singleton() -> ErrorHandler:
|
||||
var thread = GDRx.get_current_thread()
|
||||
if not GDRx.ErrorHandler_.has_key(thread):
|
||||
GDRx.ErrorHandler_.set_pair(thread, ErrorHandler.new())
|
||||
return GDRx.ErrorHandler_.get_value(thread)
|
||||
|
||||
func run(stmt : TryCatch) -> bool:
|
||||
self._has_failed[stmt] = false
|
||||
|
||||
self._try_catch_stack.push_back(stmt)
|
||||
stmt.risky_code.call()
|
||||
self._try_catch_stack.pop_back()
|
||||
|
||||
var res = self._has_failed[stmt]
|
||||
self._has_failed.erase(stmt)
|
||||
return res
|
||||
|
||||
func raise(exc : ThrowableBase, default = null) -> Variant:
|
||||
var handler : Callable = GDRx.basic.default_crash
|
||||
|
||||
if self._try_catch_stack == null or self._try_catch_stack.is_empty():
|
||||
handler.call(exc)
|
||||
return default
|
||||
|
||||
handler = GDRx.basic.noop
|
||||
|
||||
var stmt : TryCatch = self._try_catch_stack.pop_back()
|
||||
self._has_failed[stmt] = true
|
||||
for type in exc.tags():
|
||||
if type in stmt.caught_types:
|
||||
handler = stmt.caught_types[type]
|
||||
break
|
||||
|
||||
handler.call(exc)
|
||||
self._try_catch_stack.push_back(stmt)
|
||||
return default
|
||||
29
addons/reactivex/error/trycatch.gd
Normal file
@ -0,0 +1,29 @@
|
||||
class_name TryCatch
|
||||
|
||||
var _risky_code : Callable
|
||||
var _caught_types : Dictionary
|
||||
|
||||
var caught_types : Dictionary:
|
||||
get: return self._caught_types.duplicate()
|
||||
var risky_code : Callable:
|
||||
get: return self._risky_code
|
||||
|
||||
func _init(fun : Callable = GDRx.basic.noop):
|
||||
self._risky_code = fun
|
||||
self._caught_types = {}
|
||||
|
||||
func end_try_catch() -> bool:
|
||||
return ErrorHandler.singleton().run(self)
|
||||
|
||||
func catch(type : String, fun : Callable = GDRx.basic.noop) -> TryCatch:
|
||||
if self._caught_types.has(type):
|
||||
return
|
||||
self._caught_types[type] = fun
|
||||
return self
|
||||
|
||||
func catch_all(types : Array[String], fun : Callable = GDRx.basic.noop) -> TryCatch:
|
||||
for type in types:
|
||||
if self._caught_types.has(type):
|
||||
continue
|
||||
self._caught_types[type] = fun
|
||||
return self
|
||||
11
addons/reactivex/error/types/argumentoutofrangeerror.gd
Normal file
@ -0,0 +1,11 @@
|
||||
extends RxBaseError
|
||||
class_name ArgumentOutOfRangeError
|
||||
|
||||
const ERROR_TYPE = "ArgumentOutOfRangeError"
|
||||
const ERROR_MESSAGE = "Argument out of range"
|
||||
|
||||
func _init(msg : String = ERROR_MESSAGE):
|
||||
super._init(msg, ERROR_TYPE)
|
||||
|
||||
static func raise(default = null, msg : String = ERROR_MESSAGE):
|
||||
return GDRx.raise(ArgumentOutOfRangeError.new(msg), default)
|
||||
11
addons/reactivex/error/types/assertionfailederror.gd
Normal file
@ -0,0 +1,11 @@
|
||||
extends RxBaseError
|
||||
class_name AssertionFailedError
|
||||
|
||||
const ERROR_TYPE = "AssertionFailedError"
|
||||
const ERROR_MESSAGE = "Assertion failed"
|
||||
|
||||
func _init(msg : String = ERROR_MESSAGE):
|
||||
super._init(msg, ERROR_TYPE)
|
||||
|
||||
static func raise(default = null, msg : String = ERROR_MESSAGE):
|
||||
return GDRx.raise(AssertionFailedError.new(msg), default)
|
||||
11
addons/reactivex/error/types/badargumenterror.gd
Normal file
@ -0,0 +1,11 @@
|
||||
extends RxBaseError
|
||||
class_name BadArgumentError
|
||||
|
||||
const ERROR_TYPE = "BadArgumentError"
|
||||
const ERROR_MESSAGE = "An argument contained bad input"
|
||||
|
||||
func _init(msg : String = ERROR_MESSAGE):
|
||||
super._init(msg, ERROR_TYPE)
|
||||
|
||||
static func raise(default = null, msg : String = ERROR_MESSAGE):
|
||||
return GDRx.raise(BadArgumentError.new(msg), default)
|
||||
11
addons/reactivex/error/types/badmappingerror.gd
Normal file
@ -0,0 +1,11 @@
|
||||
extends RxBaseError
|
||||
class_name BadMappingError
|
||||
|
||||
const ERROR_TYPE = "BadMappingError"
|
||||
const ERROR_MESSAGE = "A mapping did not succeed"
|
||||
|
||||
func _init(msg : String = ERROR_MESSAGE):
|
||||
super._init(msg, ERROR_TYPE)
|
||||
|
||||
static func raise(default = null, msg : String = ERROR_MESSAGE):
|
||||
return GDRx.raise(BadMappingError.new(msg), default)
|
||||
11
addons/reactivex/error/types/badpredicateerror.gd
Normal file
@ -0,0 +1,11 @@
|
||||
extends RxBaseError
|
||||
class_name BadPredicateError
|
||||
|
||||
const ERROR_TYPE = "BadPredicateError"
|
||||
const ERROR_MESSAGE = "A predicate failed"
|
||||
|
||||
func _init(msg : String = ERROR_MESSAGE):
|
||||
super._init(msg, ERROR_TYPE)
|
||||
|
||||
static func raise(default = null, msg : String = ERROR_MESSAGE):
|
||||
return GDRx.raise(BadPredicateError.new(msg), default)
|
||||
11
addons/reactivex/error/types/disposederror.gd
Normal file
@ -0,0 +1,11 @@
|
||||
extends RxBaseError
|
||||
class_name DisposedError
|
||||
|
||||
const ERROR_TYPE = "DisposedError"
|
||||
const ERROR_MESSAGE = "The requested element was disposed before"
|
||||
|
||||
func _init(msg : String = ERROR_MESSAGE):
|
||||
super._init(msg, ERROR_TYPE)
|
||||
|
||||
static func raise(default = null, msg : String = ERROR_MESSAGE):
|
||||
return GDRx.raise(DisposedError.new(msg), default)
|
||||