From be8f7f85bf592e84f29a60126d4b775b4a3a1349 Mon Sep 17 00:00:00 2001 From: Jian-Hong Pan Date: Mon, 11 Nov 2024 17:51:02 +0800 Subject: [PATCH 1/5] blocks_catalog: Introduce get_variable_(get|set)ter_block_definition() Extract the getter and setter block definition code from get_variable_block_definitions() as get_variable_getter_block_definition() and get_variable_setter_block_definition(). They can be used by more places latter. --- .../code_generation/blocks_catalog.gd | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/addons/block_code/code_generation/blocks_catalog.gd b/addons/block_code/code_generation/blocks_catalog.gd index e4ece40d..4f8ca748 100644 --- a/addons/block_code/code_generation/blocks_catalog.gd +++ b/addons/block_code/code_generation/blocks_catalog.gd @@ -253,25 +253,40 @@ static func add_custom_blocks( static func get_variable_block_definitions(variables: Array[VariableDefinition]) -> Array[BlockDefinition]: var block_definitions: Array[BlockDefinition] = [] for variable: VariableDefinition in variables: - var type_string: String = Types.VARIANT_TYPE_TO_STRING[variable.var_type] + var block_def: BlockDefinition # Getter - var block_def = BlockDefinition.new() - block_def.name = "get_var_%s" % variable.var_name - block_def.category = "Variables" - block_def.type = Types.BlockType.VALUE - block_def.variant_type = variable.var_type - block_def.display_template = variable.var_name - block_def.code_template = variable.var_name + block_def = get_variable_getter_block_definition(variable) block_definitions.append(block_def) # Setter - block_def = BlockDefinition.new() - block_def.name = "set_var_%s" % variable.var_name - block_def.category = "Variables" - block_def.type = Types.BlockType.STATEMENT - block_def.display_template = "Set %s to {value: %s}" % [variable.var_name, type_string] - block_def.code_template = "%s = {value}" % [variable.var_name] + block_def = get_variable_setter_block_definition(variable) block_definitions.append(block_def) return block_definitions + + +static func get_variable_getter_block_definition(variable: VariableDefinition) -> BlockDefinition: + var block_def := BlockDefinition.new() + + block_def.name = "get_var_%s" % variable.var_name + block_def.category = "Variables" + block_def.type = Types.BlockType.VALUE + block_def.variant_type = variable.var_type + block_def.display_template = variable.var_name + block_def.code_template = variable.var_name + + return block_def + + +static func get_variable_setter_block_definition(variable: VariableDefinition) -> BlockDefinition: + var type_string: String = Types.VARIANT_TYPE_TO_STRING[variable.var_type] + var block_def := BlockDefinition.new() + + block_def.name = "set_var_%s" % variable.var_name + block_def.category = "Variables" + block_def.type = Types.BlockType.STATEMENT + block_def.display_template = "Set %s to {value: %s}" % [variable.var_name, type_string] + block_def.code_template = "%s = {value}" % [variable.var_name] + + return block_def From 98db5ef737d2bca49a06ef7684ef458e6c070fe1 Mon Sep 17 00:00:00 2001 From: Jian-Hong Pan Date: Mon, 11 Nov 2024 18:23:21 +0800 Subject: [PATCH 2/5] BlockCanvas: Introduce get_property_(get|set)ter_block_definition for drop object property The object property's getter & setter blocks generating code is similar to get_variable_(get|set)ter_block_definition(). So, reuse them within get_property_(get|set)ter_block_definition() to generate property's blocks including the description. And, the block name will change to: "(get|set)_var_". Fixes: 89beea94eefa ("BlockCanvas: Implement drag & drop the node's property from Inspector") https://phabricator.endlessm.com/T35649 --- .../code_generation/blocks_catalog.gd | 14 ++++++- .../ui/block_canvas/block_canvas.gd | 40 +++++-------------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/addons/block_code/code_generation/blocks_catalog.gd b/addons/block_code/code_generation/blocks_catalog.gd index 4f8ca748..683852bf 100644 --- a/addons/block_code/code_generation/blocks_catalog.gd +++ b/addons/block_code/code_generation/blocks_catalog.gd @@ -287,6 +287,18 @@ static func get_variable_setter_block_definition(variable: VariableDefinition) - block_def.category = "Variables" block_def.type = Types.BlockType.STATEMENT block_def.display_template = "Set %s to {value: %s}" % [variable.var_name, type_string] - block_def.code_template = "%s = {value}" % [variable.var_name] + block_def.code_template = "%s = {value}" % variable.var_name return block_def + + +static func get_property_getter_block_definition(variable: VariableDefinition) -> BlockDefinition: + var block_def := get_variable_getter_block_definition(variable) + block_def.description = "The %s property" % variable.var_name + return block_def + + +static func get_property_setter_block_definition(variable: VariableDefinition) -> BlockDefinition: + var block_def := get_variable_setter_block_definition(variable) + block_def.description = "Set the %s property" % variable.var_name + return block_def diff --git a/addons/block_code/ui/block_canvas/block_canvas.gd b/addons/block_code/ui/block_canvas/block_canvas.gd index 2f4fcc51..9bbbe7dd 100644 --- a/addons/block_code/ui/block_canvas/block_canvas.gd +++ b/addons/block_code/ui/block_canvas/block_canvas.gd @@ -3,6 +3,7 @@ extends MarginContainer const ASTList = preload("res://addons/block_code/code_generation/ast_list.gd") const BlockAST = preload("res://addons/block_code/code_generation/block_ast.gd") +const BlocksCatalog = preload("res://addons/block_code/code_generation/blocks_catalog.gd") const BlockCodePlugin = preload("res://addons/block_code/block_code_plugin.gd") const BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd") const BlockTreeUtil = preload("res://addons/block_code/ui/block_tree_util.gd") @@ -10,6 +11,7 @@ const DragManager = preload("res://addons/block_code/drag_manager/drag_manager.g const ScriptGenerator = preload("res://addons/block_code/code_generation/script_generator.gd") const Types = preload("res://addons/block_code/types/types.gd") const Util = preload("res://addons/block_code/ui/util.gd") +const VariableDefinition = preload("res://addons/block_code/code_generation/variable_definition.gd") const EXTEND_MARGIN: float = 800 const BLOCK_AUTO_PLACE_MARGIN: Vector2 = Vector2(25, 8) @@ -124,44 +126,20 @@ func _drop_node(at_position: Vector2, data: Variant) -> void: func _drop_obj_property(at_position: Vector2, data: Variant) -> void: - var object_name = str(data["object"]).get_slice(":", 0) var property_name = data["property"] var property_value = data["value"] + var is_getter = !_modifier_ctrl # Prepare a Variable block to set / get the property's value according to # the modifier KEY_CTRL pressing. + var variable := VariableDefinition.new(property_name, typeof(property_value)) var block_definition: BlockDefinition - var property_type = typeof(property_value) - if _modifier_ctrl: - var type_string: String = Types.VARIANT_TYPE_TO_STRING[property_type] - block_definition = ( - BlockDefinition - . new( - &"%s_set_%s" % [object_name, property_name], - object_name, - "Set the %s property" % property_name, - "Variables", - Types.BlockType.STATEMENT, - property_type, - "set %s to {value: %s}" % [property_name.capitalize().to_lower(), type_string], - "%s = {value}" % property_name, - {"value": property_value}, - ) - ) + + if is_getter: + block_definition = BlocksCatalog.get_property_getter_block_definition(variable) else: - block_definition = ( - BlockDefinition - . new( - &"%s_get_%s" % [object_name, property_name], - object_name, - "The %s property" % property_name, - "Variables", - Types.BlockType.VALUE, - property_type, - "%s" % property_name.capitalize().to_lower(), - "%s" % property_name, - ) - ) + block_definition = BlocksCatalog.get_property_setter_block_definition(variable) + block_definition.defaults = {"value": property_value} var block = _context.block_script.instantiate_block(block_definition) add_block(block, at_position) From 3b0aa951026fa229808120fb4d39ed4df5a264ef Mon Sep 17 00:00:00 2001 From: Jian-Hong Pan Date: Thu, 14 Nov 2024 14:03:07 +0800 Subject: [PATCH 3/5] BlockScriptSerialization: Build the bidirectional link between the block code node and script Build the bidirectional link between the block code node and the block script. So, the block script can access this block node and the parent node later easily. https://phabricator.endlessm.com/T35649 --- addons/block_code/block_code_node/block_code.gd | 14 +++++++++++++- .../serialization/block_script_serialization.gd | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/addons/block_code/block_code_node/block_code.gd b/addons/block_code/block_code_node/block_code.gd index 659719c9..6ea295ba 100644 --- a/addons/block_code/block_code_node/block_code.gd +++ b/addons/block_code/block_code_node/block_code.gd @@ -5,7 +5,8 @@ extends Node const TxUtils := preload("res://addons/block_code/translation/utils.gd") -@export var block_script: BlockScriptSerialization = null +@export var block_script: BlockScriptSerialization = null: + set = _set_block_script func _init(): @@ -37,6 +38,17 @@ func _enter_tree(): block_script = new_block_script +func _set_block_script(value): + if value == null: + # Wipe out the bidirectional link between this block code node and the + # block script + if block_script: + block_script.block_code_node = null + else: + value.block_code_node = self + block_script = value + + func _update_parent_script(): if Engine.is_editor_hint(): push_error("Updating the parent script must happen in game.") diff --git a/addons/block_code/serialization/block_script_serialization.gd b/addons/block_code/serialization/block_script_serialization.gd index 5f3051e3..e0847f02 100644 --- a/addons/block_code/serialization/block_script_serialization.gd +++ b/addons/block_code/serialization/block_script_serialization.gd @@ -28,6 +28,8 @@ const SCENE_PER_TYPE = { @export var generated_script: String @export var version: int +var block_code_node: BlockCode + var _available_blocks: Array[BlockDefinition] var _categories: Array[BlockCategory] From c7062ba57520dc938cea6fe48dc88daa7357fe10 Mon Sep 17 00:00:00 2001 From: Jian-Hong Pan Date: Mon, 11 Nov 2024 18:27:29 +0800 Subject: [PATCH 4/5] BlockScriptSerialization: Re-generate block definition for object property blocks The drag & drop object property's blocks disappear after save & re-open the Godot project. And, shows error: core/variant/variant_utility.cpp:1092 - Cannot construct block from null block definition. res://addons/block_code/ui/block_canvas/block_canvas.gd:348 - Invalid call. Nonexistent function 'set_parameter_values_on_ready' in base 'Nil'. It is because the object property blocks are not the predefined blocks in the catalog. So, Block Coding plugin cannot find the block definition from the catalog when places the object property blocks into the cavas via _block_to_ast_node() after re-open the project. Therefore, introduce _get_obj_property_block_definition() generating object property's getter/setter block definition for get_block_definition(). Besides, it also needs the object property's value type. So, visit the block code node's parent node to finding the property by _get_parent_node_property_info() to have the type. https://phabricator.endlessm.com/T35649 --- .../block_script_serialization.gd | 67 ++++++++++++++++--- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/addons/block_code/serialization/block_script_serialization.gd b/addons/block_code/serialization/block_script_serialization.gd index e0847f02..3d6210a5 100644 --- a/addons/block_code/serialization/block_script_serialization.gd +++ b/addons/block_code/serialization/block_script_serialization.gd @@ -83,22 +83,26 @@ func instantiate_block_by_name(block_name: String) -> Block: func get_block_definition(block_name: String) -> BlockDefinition: var split := block_name.split(":", true, 1) + var block_definition: BlockDefinition if len(split) > 1: return _get_parameter_block_definition(split[0], split[1]) - var block_definition = _get_base_block_definition(block_name) + block_definition = _get_base_block_definition(block_name) + if block_definition != null: + return block_definition - if block_definition == null: - # FIXME: This is a workaround for old-style output block references. - # These were generated ahead of time using a block name that has - # a "_" before the parameter name. Now, these parameter blocks - # are generated on demand for any block name containing a ":". - # Please remove this fallback when it is no longer necessary. - split = block_name.rsplit("_", true, 1) - return _get_parameter_block_definition(split[0], split[1]) + block_definition = _get_obj_property_block_definition(block_name) + if block_definition != null: + return block_definition - return block_definition + # FIXME: This is a workaround for old-style output block references. + # These were generated ahead of time using a block name that has + # a "_" before the parameter name. Now, these parameter blocks + # are generated on demand for any block name containing a ":". + # Please remove this fallback when it is no longer necessary. + split = block_name.rsplit("_", true, 1) + return _get_parameter_block_definition(split[0], split[1]) func _get_base_block_definition(block_name: String) -> BlockDefinition: @@ -135,6 +139,49 @@ func _get_parameter_block_definition(block_name: String, parameter_name: String) return block_definition +func _get_obj_property_block_definition(block_name: String) -> BlockDefinition: + var block_definition: BlockDefinition + var variable: VariableDefinition + var property_name: String + var is_getter = true + + if block_name.begins_with("get_var_"): + property_name = block_name.get_slice("get_var_", 1) + elif block_name.begins_with("set_var_"): + property_name = block_name.get_slice("set_var_", 1) + is_getter = false + else: + return null + + # Getter block needs the property's variant type information by visiting the + # block_code_node's parent node because the type is not saved as a key of + # the resource in the scene file + var property_info := _get_parent_node_property_info(property_name) + if not property_info.has("type"): + return null + + if is_getter: + variable = VariableDefinition.new(property_name, property_info["type"]) + block_definition = BlocksCatalog.get_property_getter_block_definition(variable) + else: + variable = VariableDefinition.new(property_name, property_info["type"]) + block_definition = BlocksCatalog.get_property_setter_block_definition(variable) + + return block_definition + + +func _get_parent_node_property_info(property_name: String) -> Dictionary: + if not block_code_node: + return {} + + var properties := block_code_node.get_parent().get_property_list() + for property in properties: + if property["name"] == property_name: + return property + + return {} + + func _update_block_definitions(): _available_blocks.clear() _available_blocks.append_array(_get_inherited_block_definitions()) From 5c3358d3fb6ca4c2f5d5072f934cd56f7786352f Mon Sep 17 00:00:00 2001 From: Jian-Hong Pan Date: Thu, 14 Nov 2024 15:04:37 +0800 Subject: [PATCH 5/5] BlockCanvas: Only can drop properties of the BlockCode's parent node Godot allows drag any thing. However, if it is a property, block code script only manipulates the properties of BlockCode node's parent node. So, when drag a property, only allow drop the parent node's property. Fixes: 89beea94eefa ("BlockCanvas: Implement drag & drop the node's property from Inspector") https://phabricator.endlessm.com/T35649 --- addons/block_code/ui/block_canvas/block_canvas.gd | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/block_code/ui/block_canvas/block_canvas.gd b/addons/block_code/ui/block_canvas/block_canvas.gd index 9bbbe7dd..10be16db 100644 --- a/addons/block_code/ui/block_canvas/block_canvas.gd +++ b/addons/block_code/ui/block_canvas/block_canvas.gd @@ -81,8 +81,10 @@ func _can_drop_data(at_position: Vector2, data: Variant) -> bool: if typeof(data) != TYPE_DICTIONARY: return false - # Allow dropping property block + # Allow dropping property block of the block code node's parent node if data.get("type", "") == "obj_property": + if data["object"] != _context.parent_node: + return false return true var nodes: Array = data.get("nodes", [])