Skip to content

T35536 decouple mid-term option #164

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Aug 2, 2024
21 changes: 21 additions & 0 deletions addons/block_code/block_definition.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
extends Resource

const Types = preload("res://addons/block_code/types/types.gd")

var name: StringName
var type: Types.BlockType
var description: String
var category: String

var label_template: String
var code_template: String
var defaults: Dictionary = {}

## Only for blocks of type Types.ENTRY. If non-empty, this block defines a
## callback that will be connected to the signal with this name.
var signal_name: String = ""


func _init(p_name: StringName, p_type: Types.BlockType):
name = p_name
type = p_type
35 changes: 35 additions & 0 deletions addons/block_code/blocks_catalog.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
extends Object

const BlockDefinition = preload("res://addons/block_code/block_definition.gd")
const Types = preload("res://addons/block_code/types/types.gd")

static var _catalog: Dictionary
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think since the dictionary defaults to empty, the _created boolean is not needed. You can just check if _catalog: return.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed! Thanks.



static func setup():
if _catalog:
return

_catalog = {}
var block_definition: BlockDefinition = BlockDefinition.new(&"ready_block", Types.BlockType.ENTRY)
block_definition.label_template = "On Ready"
block_definition.code_template = "func _ready():"
block_definition.description = 'Attached blocks will be executed once when the node is "ready"'
block_definition.category = "Lifecycle"
_catalog[&"ready_block"] = block_definition

block_definition = BlockDefinition.new(&"print", Types.BlockType.EXECUTE)
block_definition.label_template = "print {text: STRING}"
block_definition.code_template = "print({text})"
block_definition.defaults = {"text": "Hello"}
block_definition.description = "Print the text to output"
block_definition.category = "Log"
_catalog[&"print"] = block_definition


static func get_block(block_name: StringName):
return _catalog.get(block_name)


static func has_block(block_name: StringName):
return block_name in _catalog
758 changes: 427 additions & 331 deletions addons/block_code/examples/pong_game/pong_game.tscn

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions addons/block_code/ui/block_canvas/block_canvas.gd
Original file line number Diff line number Diff line change
@@ -161,10 +161,16 @@ func clear_canvas():


func load_tree(parent: Node, node: SerializedBlockTreeNode):
var _block_scene_path = _block_scenes_by_class[node.serialized_block.block_class]
var scene: Block = load(_block_scene_path).instantiate()
var scene: Block = Util.instantiate_block(node.name)

# TODO: Remove once the data/UI decouple is done.
if scene == null:
var _block_scene_path = _block_scenes_by_class[node.serialized_block.block_class]
scene = load(_block_scene_path).instantiate()
for prop_pair in node.serialized_block.serialized_props:
scene.set(prop_pair[0], prop_pair[1])

scene.position = node.position
scene.resource = node
parent.add_child(scene)

10 changes: 8 additions & 2 deletions addons/block_code/ui/block_canvas/serialized_block_tree_node.gd
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
class_name SerializedBlockTreeNode
extends Resource

@export var serialized_block: SerializedBlock
@export var name: StringName
@export var position: Vector2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing here: Why does every SerializedBlockTreeNode have its position serialized? Only the top level block really needs its position saved. Anyways, it still works fine, with nested blocks having position (0,0), and I wouldn't change it here unless you really want to because it allows you to make very minimal changes to BlockCanvas.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing here: Why does every SerializedBlockTreeNode have its position serialized? Only the top level block really needs its position saved. Anyways, it still works fine, with nested blocks having position (0,0), and I wouldn't change it here unless you really want to because it allows you to make very minimal changes to BlockCanvas.

Yes, I thought about this. For a snapped/attached block, as soon as the user detaches it by dragging it to the canvas, it will need a position. So I thought that it is better to keep the position as property to all the saved blocks, and just ignore it if they are attached. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that's totally fine. It makes the code a little simpler in this case!

@export var path_child_pairs: Array

# TODO: Remove once the data/UI decouple is done.
@export var serialized_block: SerializedBlock


func _init(p_serialized_block: SerializedBlock = null, p_path_child_pairs: Array = []):
func _init(p_name: StringName, p_position: Vector2 = Vector2.ZERO, p_serialized_block: SerializedBlock = null, p_path_child_pairs: Array = []):
name = p_name
position = p_position
serialized_block = p_serialized_block
path_child_pairs = p_path_child_pairs
18 changes: 15 additions & 3 deletions addons/block_code/ui/blocks/block/block.gd
Original file line number Diff line number Diff line change
@@ -2,14 +2,15 @@
class_name Block
extends MarginContainer

const BlocksCatalog = preload("res://addons/block_code/blocks_catalog.gd")
const InstructionTree = preload("res://addons/block_code/instruction_tree/instruction_tree.gd")
const Types = preload("res://addons/block_code/types/types.gd")

signal drag_started(block: Block)
signal modified

## Name of the block to be referenced by others in search
@export var block_name: String = ""
@export var block_name: StringName

## Label of block (optionally used to draw block labels)
@export var label: String = ""
@@ -83,18 +84,29 @@ func get_instruction_node() -> InstructionTree.TreeNode:
func update_resources(undo_redo: EditorUndoRedoManager):
if resource == null:
var serialized_block = SerializedBlock.new(get_block_class(), get_serialized_props())
resource = SerializedBlockTreeNode.new(serialized_block)
resource = SerializedBlockTreeNode.new(block_name, position, serialized_block)
return

if resource.position != position:
undo_redo.add_undo_property(resource, "position", resource.position)
undo_redo.add_do_property(resource, "position", position)

var serialized_props = get_serialized_props()

if serialized_props != resource.serialized_block.serialized_props:
undo_redo.add_undo_property(resource.serialized_block, "serialized_props", resource.serialized_block.serialized_props)
undo_redo.add_do_property(resource.serialized_block, "serialized_props", serialized_props)


# Override this method to add more serialized properties
func get_serialized_props() -> Array:
return serialize_props(["block_name", "label", "color", "block_type", "position", "scope"])
if not BlocksCatalog.has_block(block_name):
return serialize_props(["block_name", "label", "color", "block_type", "position", "scope"])

# TODO: Remove remaining serialization:
# - Derive color from category.
# - Handle scope in a different way?
return serialize_props(["color", "scope"])


func _to_string():
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
size_flags_horizontal = 0
mouse_filter = 2
script = ExtResource("1_2hbir")
block_name = "control_block"
block_name = &"control_block"
label = "Control Block"
color = Color(0.59979, 0.536348, 0.876215, 1)
bottom_snap_path = NodePath("VBoxContainer/SnapPoint")
3 changes: 2 additions & 1 deletion addons/block_code/ui/blocks/entry_block/entry_block.gd
Original file line number Diff line number Diff line change
@@ -30,5 +30,6 @@ func get_entry_statement() -> String:

func get_serialized_props() -> Array:
var props := super()
props.append_array(serialize_props(["signal_name"]))
if not BlocksCatalog.has_block(block_name):
props.append_array(serialize_props(["signal_name"]))
return props
3 changes: 1 addition & 2 deletions addons/block_code/ui/blocks/entry_block/entry_block.tscn
Original file line number Diff line number Diff line change
@@ -6,8 +6,7 @@
[node name="EntryBlock" instance=ExtResource("1_byjbb")]
script = ExtResource("2_3ik8h")
signal_name = ""
defaults = {}
block_name = "entry_block"
block_name = &"entry_block"
label = "EntryBlock"
block_type = 1

Original file line number Diff line number Diff line change
@@ -18,9 +18,9 @@ corner_radius_bottom_left = 16
offset_right = 16.0
offset_bottom = 8.0
size_flags_horizontal = 0
mouse_filter = 2
script = ExtResource("1_0hajy")
defaults = null
block_name = "parameter_block"
block_name = &"parameter_block"
label = "Param"
block_type = 3

Original file line number Diff line number Diff line change
@@ -36,7 +36,8 @@ func _on_drag_drop_area_mouse_down():

func get_serialized_props() -> Array:
var props := super()
props.append_array(serialize_props(["block_format", "statement", "defaults"]))
if not BlocksCatalog.has_block(block_name):
props.append_array(serialize_props(["block_format", "statement", "defaults"]))

var _param_input_strings: Dictionary = {}
for pair in param_name_input_pairs:
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@
size_flags_horizontal = 0
mouse_filter = 2
script = ExtResource("1_6wvlf")
block_name = "statement_block"
block_name = &"statement_block"
label = "StatementBlock"
bottom_snap_path = NodePath("VBoxContainer/SnapPoint")

29 changes: 0 additions & 29 deletions addons/block_code/ui/bsd_templates/default_blocktrees.tres

This file was deleted.

30 changes: 25 additions & 5 deletions addons/block_code/ui/bsd_templates/default_bsd.tres
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
[gd_resource type="Resource" script_class="BlockScriptData" load_steps=3 format=3 uid="uid://dit7fykhl3h48"]
[gd_resource type="Resource" script_class="BlockScriptData" load_steps=8 format=3 uid="uid://dit7fykhl3h48"]

[ext_resource type="Script" path="res://addons/block_code/block_script_data/block_script_data.gd" id="1_h8ggn"]
[ext_resource type="Resource" uid="uid://djn5nejdsfu2a" path="res://addons/block_code/ui/bsd_templates/default_blocktrees.tres" id="1_y70fv"]
[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/serialized_block_tree_node.gd" id="1_barc5"]
[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/serialized_block.gd" id="2_cgfpx"]
[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/serialized_block_tree_node_array.gd" id="3_gx4d7"]
[ext_resource type="Script" path="res://addons/block_code/block_script_data/block_script_data.gd" id="4_cqq7x"]

[sub_resource type="Resource" id="Resource_b0aen"]
script = ExtResource("2_cgfpx")
block_class = &"EntryBlock"
serialized_props = [["color", Color(0.92549, 0.231373, 0.34902, 1)], ["scope", ""], ["param_input_strings", {}]]

[sub_resource type="Resource" id="Resource_1h6wi"]
script = ExtResource("1_barc5")
name = &"ready_block"
position = Vector2(54, 47)
path_child_pairs = []
serialized_block = SubResource("Resource_b0aen")

[sub_resource type="Resource" id="Resource_nkub8"]
script = ExtResource("3_gx4d7")
array = Array[ExtResource("1_barc5")]([SubResource("Resource_1h6wi")])

[resource]
script = ExtResource("1_h8ggn")
script = ExtResource("4_cqq7x")
script_inherits = "INHERIT_DEFAULT"
block_trees = ExtResource("1_y70fv")
block_trees = SubResource("Resource_nkub8")
variables = Array[Resource("res://addons/block_code/ui/block_canvas/variable_resource.gd")]([])
generated_script = "extends INHERIT_DEFAULT"
version = 0
16 changes: 3 additions & 13 deletions addons/block_code/ui/picker/categories/category_factory.gd
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ extends Object

const BlockCategory = preload("res://addons/block_code/ui/picker/categories/block_category.gd")
const Types = preload("res://addons/block_code/types/types.gd")
const Util = preload("res://addons/block_code/ui/util.gd")

const BLOCKS: Dictionary = {
"control_block": preload("res://addons/block_code/ui/blocks/control_block/control_block.tscn"),
@@ -171,12 +172,7 @@ static func get_general_blocks() -> Array[Block]:

#region Lifecycle

b = BLOCKS["entry_block"].instantiate()
b.block_name = "ready_block"
b.block_format = "On Ready"
b.statement = "func _ready():"
b.tooltip_text = 'Attached blocks will be executed once when the node is "ready"'
b.category = "Lifecycle"
b = Util.instantiate_block(&"ready_block")
block_list.append(b)

b = BLOCKS["entry_block"].instantiate()
@@ -259,13 +255,7 @@ static func get_general_blocks() -> Array[Block]:
#endregion
#region Logs

b = BLOCKS["statement_block"].instantiate()
b.block_name = "print"
b.block_format = "print {text: STRING}"
b.statement = "print({text})"
b.defaults = {"text": "Hello"}
b.tooltip_text = "Print the text to output"
b.category = "Log"
b = Util.instantiate_block(&"print")
block_list.append(b)

#endregion
29 changes: 29 additions & 0 deletions addons/block_code/ui/util.gd
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
extends Object

const BlockDefinition = preload("res://addons/block_code/block_definition.gd")
const BlocksCatalog = preload("res://addons/block_code/blocks_catalog.gd")
const Types = preload("res://addons/block_code/types/types.gd")

const SCENE_PER_TYPE = {
Types.BlockType.ENTRY: preload("res://addons/block_code/ui/blocks/entry_block/entry_block.tscn"),
Types.BlockType.EXECUTE: preload("res://addons/block_code/ui/blocks/statement_block/statement_block.tscn"),
}


static func instantiate_block(block_name: StringName) -> Block:
BlocksCatalog.setup()
var block_definition: BlockDefinition = BlocksCatalog.get_block(block_name)
if block_definition == null:
push_error("The block %s is not in the catalog yet!" % block_name)
return

var scene = SCENE_PER_TYPE[block_definition.type]
var b = scene.instantiate()
b.block_name = block_definition.name
b.block_format = block_definition.label_template
b.statement = block_definition.code_template
b.defaults = block_definition.defaults
b.tooltip_text = block_definition.description
b.category = block_definition.category
if block_definition.type == Types.BlockType.ENTRY and block_definition.signal_name != "":
b.signal_name = block_definition.signal_name
return b


## Polyfill of Node.is_part_of_edited_scene(), available to GDScript in Godot 4.3+.
static func node_is_part_of_edited_scene(node: Node) -> bool: