Skip to content

Commit d8dae7e

Browse files
authored
Merge pull request #147 from endlessm/decouple-ui-from-codegen
Decouple UI from Code Generation
2 parents 1fd3db8 + 6bc2e8e commit d8dae7e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1512
-1593
lines changed

addons/block_code/block_code_plugin.gd

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ const DISABLED_CLASSES := [
2424
"ParameterBlock",
2525
"StatementBlock",
2626
"SnapPoint",
27-
"BlockSerialization",
28-
"BlockSerializedProperties",
2927
"BlockScriptSerialization",
3028
"CategoryFactory",
3129
]

addons/block_code/blocks/communication/define_method.tres

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
[resource]
66
script = ExtResource("1_6e473")
77
name = &"define_method"
8+
target_node_class = ""
89
description = "Define a method/function with following statements"
910
category = "Communication | Methods"
1011
type = 1
1112
variant_type = 0
12-
display_template = "Define method {method_name: NIL}"
13+
display_template = "Define method {method_name: STRING_NAME}"
1314
code_template = "func {method_name}():"
1415
defaults = {}
1516
signal_name = ""

addons/block_code/blocks/graphics/animationplayer_play.tres

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[gd_resource type="Resource" load_steps=4 format=3 uid="uid://c5e1byehtxwc0"]
22

33
[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_emeuv"]
4-
[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/option_data.gd" id="1_xu43h"]
4+
[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_xu43h"]
55

66
[sub_resource type="Resource" id="Resource_vnp2w"]
77
script = ExtResource("1_xu43h")

addons/block_code/blocks/logic/compare.tres

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[gd_resource type="Resource" load_steps=4 format=3 uid="uid://pr5wnn3ltkbo"]
22

3-
[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/option_data.gd" id="1_hcv2h"]
3+
[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_hcv2h"]
44
[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_wp40r"]
55

66
[sub_resource type="Resource" id="Resource_ie4sg"]

addons/block_code/blocks/sounds/pause_continue_sound.tres

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[gd_resource type="Resource" load_steps=4 format=3 uid="uid://wpdspamg3f6g"]
22

3-
[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/option_data.gd" id="1_ilhdq"]
3+
[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_ilhdq"]
44
[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_q04gm"]
55

66
[sub_resource type="Resource" id="Resource_lalgp"]
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
extends RefCounted
2+
3+
const Types = preload("res://addons/block_code/types/types.gd")
4+
const BlockAST = preload("res://addons/block_code/code_generation/block_ast.gd")
5+
6+
var array: Array[ASTPair]
7+
8+
9+
class ASTPair:
10+
var ast: BlockAST
11+
var canvas_position: Vector2
12+
13+
func _init(p_ast: BlockAST, p_canvas_position: Vector2):
14+
ast = p_ast
15+
canvas_position = p_canvas_position
16+
17+
18+
func _init():
19+
array = []
20+
21+
22+
func append(ast: BlockAST, canvas_position: Vector2):
23+
array.append(ASTPair.new(ast, canvas_position))
24+
25+
26+
func clear():
27+
array.clear()
28+
29+
30+
func get_top_level_nodes_of_type(block_type: Types.BlockType) -> Array[BlockAST]:
31+
var asts: Array[BlockAST] = []
32+
33+
for ast_pair in array:
34+
if ast_pair.ast.root.data.type == block_type:
35+
asts.append(ast_pair.ast)
36+
37+
return asts
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
extends RefCounted
2+
3+
const BlockAST = preload("res://addons/block_code/code_generation/block_ast.gd")
4+
const OptionData = preload("res://addons/block_code/code_generation/option_data.gd")
5+
const Types = preload("res://addons/block_code/types/types.gd")
6+
7+
var root: ASTNode
8+
9+
10+
class IDHandler:
11+
static var counts: Dictionary = {}
12+
13+
static func reset():
14+
counts = {}
15+
16+
static func get_unique_id(str: String) -> int:
17+
if not counts.has(str):
18+
counts[str] = 0
19+
20+
counts[str] += 1
21+
22+
return counts[str]
23+
24+
static func make_unique(formatted_string: String) -> String:
25+
var unique_string = formatted_string
26+
var regex = RegEx.new()
27+
regex.compile("\\b__[^\\s]+")
28+
var ids: Dictionary = {}
29+
for result in regex.search_all(formatted_string):
30+
var result_string = result.get_string()
31+
if not ids.has(result_string):
32+
ids[result_string] = get_unique_id(result_string)
33+
unique_string = unique_string.replace(result_string, result_string + "_%d" % ids[result_string])
34+
35+
return unique_string
36+
37+
38+
class ASTNode:
39+
var data #: BlockDefinition
40+
var children: Array[ASTNode]
41+
var arguments: Dictionary # String, ASTValueNode
42+
43+
func _init():
44+
children = []
45+
arguments = {}
46+
47+
func get_code_block() -> String:
48+
var code_block: String = data.code_template # get multiline code_template from block definition
49+
50+
# insert args
51+
52+
# check if args match an overload in the resource
53+
54+
for arg_name in arguments:
55+
# Use parentheses to be safe
56+
var argument = arguments[arg_name]
57+
var code_string: String
58+
if argument is ASTValueNode:
59+
code_string = argument.get_code()
60+
else:
61+
code_string = BlockAST.raw_input_to_code_string(argument)
62+
63+
code_block = code_block.replace("{%s}" % arg_name, code_string)
64+
65+
return IDHandler.make_unique(code_block)
66+
67+
func get_code(depth: int) -> String:
68+
var code: String = ""
69+
70+
# append code block
71+
var code_block := get_code_block()
72+
code_block = code_block.indent("\t".repeat(depth))
73+
74+
code += code_block + "\n"
75+
76+
# fill empty entry and control blocks with pass
77+
if children.is_empty() and (data.type == Types.BlockType.ENTRY || data.type == Types.BlockType.CONTROL):
78+
code += "pass\n".indent("\t".repeat(depth + 1))
79+
80+
for child in children:
81+
code += child.get_code(depth + 1)
82+
83+
return code
84+
85+
86+
class ASTValueNode:
87+
var data #: BlockDefinition
88+
var arguments: Dictionary # String, ASTValueNode
89+
90+
func _init():
91+
arguments = {}
92+
93+
func get_code() -> String:
94+
var code: String = data.code_template # get code_template from block definition
95+
96+
# check if args match an overload in the resource
97+
98+
for arg_name in arguments:
99+
# Use parentheses to be safe
100+
var argument = arguments[arg_name]
101+
var code_string: String
102+
if argument is ASTValueNode:
103+
code_string = argument.get_code()
104+
else:
105+
code_string = BlockAST.raw_input_to_code_string(argument)
106+
107+
code = code.replace("{%s}" % arg_name, code_string)
108+
109+
return IDHandler.make_unique("(%s)" % code)
110+
111+
112+
func get_code() -> String:
113+
IDHandler.reset()
114+
return root.get_code(0)
115+
116+
117+
func _to_string():
118+
return to_string_recursive(root, 0)
119+
120+
121+
func to_string_recursive(node: ASTNode, depth: int) -> String:
122+
var string: String = "%s %s\n" % ["-".repeat(depth), node.data.display_template]
123+
124+
for c in node.children:
125+
string += to_string_recursive(c, depth + 1)
126+
127+
return string
128+
129+
130+
static func raw_input_to_code_string(input) -> String:
131+
match typeof(input):
132+
TYPE_STRING:
133+
return "'%s'" % input.replace("\\", "\\\\").replace("'", "\\'")
134+
TYPE_VECTOR2:
135+
return "Vector2%s" % str(input)
136+
TYPE_COLOR:
137+
return "Color%s" % str(input)
138+
TYPE_OBJECT:
139+
if input is OptionData:
140+
var option_data := input as OptionData
141+
return option_data.items[option_data.selected]
142+
_:
143+
return "%s" % input
144+
145+
return ""

addons/block_code/code_generation/blocks_catalog.gd

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
extends Object
22

33
const BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd")
4+
const OptionData = preload("res://addons/block_code/code_generation/option_data.gd")
45
const Types = preload("res://addons/block_code/types/types.gd")
56
const Util = preload("res://addons/block_code/code_generation/util.gd")
7+
const VariableDefinition = preload("res://addons/block_code/code_generation/variable_definition.gd")
68

79
const _BLOCKS_PATH = "res://addons/block_code/blocks/"
810

@@ -94,6 +96,40 @@ static func _setup_definitions_from_files():
9496
_by_class_name[target][block_definition.name] = block_definition
9597

9698

99+
static func _add_output_definitions(definitions: Array[BlockDefinition]):
100+
# Capture things of format [test]
101+
var _output_regex := RegEx.create_from_string("\\[([^\\]]+)\\]")
102+
103+
for definition in definitions:
104+
if definition.type != Types.BlockType.ENTRY:
105+
continue
106+
107+
for reg_match in _output_regex.search_all(definition.display_template):
108+
var parts := reg_match.get_string(1).split(": ")
109+
var param_name := parts[0]
110+
var param_type: Variant.Type = Types.STRING_TO_VARIANT_TYPE[parts[1]]
111+
112+
var output_def := BlockDefinition.new()
113+
output_def.name = &"%s_%s" % [definition.name, param_name]
114+
output_def.target_node_class = definition.target_node_class
115+
output_def.category = definition.category
116+
output_def.type = Types.BlockType.VALUE
117+
output_def.variant_type = param_type
118+
output_def.display_template = param_name
119+
output_def.code_template = param_name
120+
output_def.scope = definition.code_template
121+
122+
# Note that these are not added to the _by_class_name dict
123+
# because they only make sense within the entry block scope.
124+
_catalog[output_def.name] = output_def
125+
126+
127+
static func _setup_output_definitions():
128+
var definitions: Array[BlockDefinition]
129+
definitions.assign(_catalog.values())
130+
_add_output_definitions(definitions)
131+
132+
97133
static func _add_property_definitions(_class_name: String, property_list: Array[Dictionary], property_settings: Dictionary):
98134
for property in property_list:
99135
if not property.name in property_settings:
@@ -218,6 +254,7 @@ static func setup():
218254

219255
_catalog = {}
220256
_setup_definitions_from_files()
257+
_setup_output_definitions()
221258
_setup_properties_for_class()
222259
_setup_input_block()
223260

@@ -237,6 +274,45 @@ static func get_blocks_by_class(_class_name: String):
237274
return block_definitions.values()
238275

239276

277+
static func _get_builtin_parents(_class_name: String) -> Array[String]:
278+
var parents: Array[String] = []
279+
var current = _class_name
280+
281+
while current != "":
282+
parents.append(current)
283+
current = ClassDB.get_parent_class(current)
284+
285+
return parents
286+
287+
288+
static func _get_custom_parent_class_name(_custom_class_name: String) -> String:
289+
for class_dict in ProjectSettings.get_global_class_list():
290+
if class_dict.class != _custom_class_name:
291+
continue
292+
var script = load(class_dict.path)
293+
var builtin_class = script.get_instance_base_type()
294+
return builtin_class
295+
return "Node"
296+
297+
298+
static func _get_parents(_class_name: String) -> Array[String]:
299+
if ClassDB.class_exists(_class_name):
300+
return _get_builtin_parents(_class_name)
301+
var parents: Array[String] = [_class_name]
302+
var _parent_class_name = _get_custom_parent_class_name(_class_name)
303+
parents.append_array(_get_builtin_parents(_parent_class_name))
304+
return parents
305+
306+
307+
static func get_inherited_blocks(_class_name: String) -> Array[BlockDefinition]:
308+
setup()
309+
310+
var definitions: Array[BlockDefinition] = []
311+
for _parent_class_name in _get_parents(_class_name):
312+
definitions.append_array(get_blocks_by_class(_parent_class_name))
313+
return definitions
314+
315+
240316
static func add_custom_blocks(
241317
_class_name,
242318
block_definitions: Array[BlockDefinition] = [],
@@ -252,4 +328,32 @@ static func add_custom_blocks(
252328
_catalog[block_definition.name] = block_definition
253329
_by_class_name[_class_name][block_definition.name] = block_definition
254330

331+
_add_output_definitions(block_definitions)
255332
_add_property_definitions(_class_name, property_list, property_settings)
333+
334+
335+
static func get_variable_block_definitions(variables: Array[VariableDefinition]) -> Array[BlockDefinition]:
336+
var block_definitions: Array[BlockDefinition] = []
337+
for variable: VariableDefinition in variables:
338+
var type_string: String = Types.VARIANT_TYPE_TO_STRING[variable.var_type]
339+
340+
# Getter
341+
var block_def = BlockDefinition.new()
342+
block_def.name = "get_var_%s" % variable.var_name
343+
block_def.category = "Variables"
344+
block_def.type = Types.BlockType.VALUE
345+
block_def.variant_type = variable.var_type
346+
block_def.display_template = variable.var_name
347+
block_def.code_template = variable.var_name
348+
block_definitions.append(block_def)
349+
350+
# Setter
351+
block_def = BlockDefinition.new()
352+
block_def.name = "set_var_%s" % variable.var_name
353+
block_def.category = "Variables"
354+
block_def.type = Types.BlockType.STATEMENT
355+
block_def.display_template = "Set %s to {value: %s}" % [variable.var_name, type_string]
356+
block_def.code_template = "%s = {value}" % [variable.var_name]
357+
block_definitions.append(block_def)
358+
359+
return block_definitions

addons/block_code/ui/block_canvas/option_data.gd renamed to addons/block_code/code_generation/option_data.gd

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
class_name OptionData
21
extends Resource
32

43
@export var selected: int

0 commit comments

Comments
 (0)