Skip to content

Commit

Permalink
Add a feature to override BlockCode from a child scene
Browse files Browse the repository at this point in the history
This reorganizes the empty state widgets in BlockCanvas, adding a new
set of actions when the user selects a node which contains block code,
but the block code can't be edited. This commit also includes changes
to better handle when a BlockCode node is inside a node from a different
scene but with editable children.

https://phabricator.endlessm.com/T35541
  • Loading branch information
dylanmccall committed Jul 5, 2024
1 parent 2160632 commit 5264ba3
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 39 deletions.
55 changes: 45 additions & 10 deletions addons/block_code/block_code_plugin.gd
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ static var block_code_button: Button

var editor_inspector: EditorInspector

var selected_block_code_node: BlockCode

var old_feature_profile: String = ""

const DISABLED_CLASSES := [
Expand Down Expand Up @@ -93,6 +95,7 @@ func _exit_tree():
func _ready():
connect("scene_changed", _on_scene_changed)
editor_inspector.connect("edited_object_changed", _on_editor_inspector_edited_object_changed)
editor_inspector.connect("property_edited", _on_editor_inspector_property_edited)
_on_scene_changed(EditorInterface.get_edited_scene_root())
_on_editor_inspector_edited_object_changed()

Expand All @@ -103,19 +106,51 @@ func _on_scene_changed(scene_root: Node):


func _on_editor_inspector_edited_object_changed():
var edited_node: Node = editor_inspector.get_edited_object() as Node
var block_code: BlockCode = edited_node as BlockCode
if block_code == null and edited_node:
# As a fallback, check if the edited node has a BlockCode node as a
# child and use that. We will only check one level deep to avoid
# confusing scenarios where a node could have multiple children, each
# with their own BlockCode nodes.
block_code = edited_node.find_children("*", "BlockCode", false).pop_front()
BlockCodePlugin.main_panel.switch_script(block_code)
if block_code:
var edited_node = editor_inspector.get_edited_object() as Node

# We will edit either the selected node (if it is a BlockCode node) or
# the first BlockCode child of that node.
selected_block_code_node = list_block_code_for_node(edited_node).pop_front()
if not is_block_code_editable(selected_block_code_node):
selected_block_code_node = null

BlockCodePlugin.main_panel.switch_block_code_node(selected_block_code_node)
if selected_block_code_node:
make_bottom_panel_item_visible(main_panel)


static func is_block_code_editable(block_code: BlockCode) -> bool:
if not block_code:
return false

# A BlockCode node can be edited if it belongs to the edited scene, or it
# is an editable instance.

var scene_node = EditorInterface.get_edited_scene_root()

return block_code == scene_node or block_code.owner == scene_node or scene_node.is_editable_instance(block_code.owner)


static func node_has_block_code(node: Node, recursive: bool = false) -> bool:
return list_block_code_for_node(node, recursive).size() > 0


static func list_block_code_for_node(node: Node, recursive: bool = false) -> Array[BlockCode]:
var result: Array[BlockCode] = []

if node is BlockCode:
result.append(node)
elif node:
result.append_array(node.find_children("*", "BlockCode", recursive))

return result


func _on_editor_inspector_property_edited(property: String):
if selected_block_code_node:
_on_editor_inspector_edited_object_changed()


func _get_plugin_name():
return "Block Code"

Expand Down
83 changes: 66 additions & 17 deletions addons/block_code/ui/block_canvas/block_canvas.gd
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,27 @@ const BLOCK_AUTO_PLACE_MARGIN: Vector2 = Vector2(16, 8)

@onready var _window: Control = %Window
@onready var _window_scroll: ScrollContainer = %WindowScroll
@onready var _select_node_box: BoxContainer = %SelectNodeBox
@onready var _create_block_code_box: BoxContainer = %CreateBlockCodeBox
@onready var _create_block_code_label: Label = %CreateBlockCodeBox/Label
@onready var _create_block_code_button: Button = %CreateBlockCodeBox/Button
@onready var _create_block_code_label_format: String = _create_block_code_label.text
@onready var _empty_box: BoxContainer = %EmptyBox

@onready var _selected_node_box: BoxContainer = %SelectedNodeBox
@onready var _selected_node_label: Label = %SelectedNodeBox/Label
@onready var _selected_node_label_format: String = _selected_node_label.text

@onready var _selected_node_with_block_code_box: BoxContainer = %SelectedNodeWithBlockCodeBox
@onready var _selected_node_with_block_code_label: Label = %SelectedNodeWithBlockCodeBox/Label
@onready var _selected_node_with_block_code_label_format: String = _selected_node_with_block_code_label.text

@onready var _add_block_code_button: Button = %AddBlockCodeButton
@onready var _open_scene_button: Button = %OpenSceneButton
@onready var _replace_block_code_button: Button = %ReplaceBlockCodeButton

var _block_scenes_by_class = {}

signal reconnect_block(block: Block)


func _ready():
_open_scene_button.icon = _open_scene_button.get_theme_icon("Load", "EditorIcons")
_populate_block_scenes_by_class()


Expand Down Expand Up @@ -62,19 +71,36 @@ func set_child(n: Node):
func bsd_selected(bsd: BlockScriptData):
clear_canvas()

_select_node_box.visible = false
_create_block_code_box.visible = false
_create_block_code_button.disabled = true
var edited_node = EditorInterface.get_inspector().get_edited_object() as Node

_empty_box.visible = false
_selected_node_box.visible = false
_selected_node_with_block_code_box.visible = false
_add_block_code_button.disabled = true
_open_scene_button.disabled = true
_replace_block_code_button.disabled = true

if bsd != null:
_load_bsd(bsd)
elif edited_node == null:
_empty_box.visible = true
elif BlockCodePlugin.node_has_block_code(edited_node):
# If the selected node has a block code node, but BlockCodePlugin didn't
# provide it to bsd_selected, we assume the block code itself is not
# editable. In that case, provide options to either edit the node's
# scene file, or override the BlockCode node. This is mostly to avoid
# creating a situation where a node has multiple BlockCode nodes.
_selected_node_with_block_code_box.visible = true
_selected_node_with_block_code_label.text = _selected_node_with_block_code_label_format.format({"node": edited_node.name})
_open_scene_button.disabled = false if edited_node.scene_file_path else true
_replace_block_code_button.disabled = false
else:
_selected_node_box.visible = true
_selected_node_label.text = _selected_node_label_format.format({"node": edited_node.name})
_add_block_code_button.disabled = false

if not bsd and EditorInterface.get_inspector().get_edited_object() is Node:
_create_block_code_box.visible = true
_create_block_code_label.text = _create_block_code_label_format.format({"node": EditorInterface.get_inspector().get_edited_object().name})
_create_block_code_button.disabled = false
return
elif not bsd:
_select_node_box.visible = true
return

func _load_bsd(bsd: BlockScriptData):
for tree in bsd.block_trees.array:
load_tree(_window, tree)

Expand Down Expand Up @@ -160,7 +186,7 @@ func release_scope():


func _on_add_block_code_button_pressed():
_create_block_code_button.disabled = true
_add_block_code_button.disabled = true

var edited_node: Node = EditorInterface.get_inspector().get_edited_object() as Node
var scene_root: Node = EditorInterface.get_edited_scene_root()
Expand All @@ -175,3 +201,26 @@ func _on_add_block_code_button_pressed():

EditorInterface.get_selection().clear()
EditorInterface.get_selection().add_node(block_code)


func _on_open_scene_button_pressed():
_open_scene_button.disabled = true

var edited_node: Node = EditorInterface.get_inspector().get_edited_object() as Node

if edited_node == null or edited_node.owner == null:
return

EditorInterface.open_scene_from_path(edited_node.scene_file_path)


func _on_replace_block_code_button_pressed():
var edited_node: Node = EditorInterface.get_inspector().get_edited_object() as Node
var scene_root: Node = EditorInterface.get_edited_scene_root()

scene_root.set_editable_instance(edited_node, true)

var block_code_nodes = BlockCodePlugin.list_block_code_for_node(edited_node)
#
EditorInterface.get_selection().clear()
EditorInterface.get_selection().add_node(block_code_nodes.pop_front() if block_code_nodes.size() > 0 else edited_node)
66 changes: 58 additions & 8 deletions addons/block_code/ui/block_canvas/block_canvas.tscn
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
[gd_scene load_steps=3 format=3 uid="uid://c6vumewgnfquy"]
[gd_scene load_steps=5 format=3 uid="uid://c6vumewgnfquy"]

[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/block_canvas.gd" id="1_tk8h2"]
[ext_resource type="Texture2D" uid="uid://cmusxj1ppspnp" path="res://addons/block_code/block_code_node/block_code_node.svg" id="2_710vn"]

[sub_resource type="Image" id="Image_p0ogw"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
"height": 16,
"mipmaps": false,
"width": 16
}

[sub_resource type="ImageTexture" id="ImageTexture_vt8ew"]
image = SubResource("Image_p0ogw")

[node name="BlockCanvas" type="MarginContainer"]
anchors_preset = 15
anchor_right = 1.0
Expand All @@ -26,34 +38,72 @@ layout_mode = 2
size_flags_horizontal = 3
mouse_filter = 1

[node name="SelectNodeBox" type="VBoxContainer" parent="."]
[node name="EmptyBox" type="VBoxContainer" parent="."]
unique_name_in_owner = true
visible = false
layout_mode = 2
size_flags_vertical = 4

[node name="Label" type="Label" parent="SelectNodeBox"]
[node name="Label" type="Label" parent="EmptyBox"]
layout_mode = 2
text = "First, select a node in the current scene."
text = "Select a node to create and edit block code."
horizontal_alignment = 1

[node name="CreateBlockCodeBox" type="VBoxContainer" parent="."]
[node name="SelectedNodeBox" type="VBoxContainer" parent="."]
unique_name_in_owner = true
visible = false
layout_mode = 2
size_flags_vertical = 4

[node name="Label" type="Label" parent="CreateBlockCodeBox"]
[node name="Label" type="Label" parent="SelectedNodeBox"]
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
text = "Use block coding to create custom behavior and game mechanics for \"{node}\"."
horizontal_alignment = 1
autowrap_mode = 2

[node name="Button" type="Button" parent="CreateBlockCodeBox"]
[node name="ButtonsBox" type="HBoxContainer" parent="SelectedNodeBox"]
layout_mode = 2
size_flags_horizontal = 4

[node name="AddBlockCodeButton" type="Button" parent="SelectedNodeBox/ButtonsBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 4
text = "Add Block Code"
icon = ExtResource("2_710vn")

[connection signal="pressed" from="CreateBlockCodeBox/Button" to="." method="_on_add_block_code_button_pressed"]
[node name="SelectedNodeWithBlockCodeBox" type="VBoxContainer" parent="."]
unique_name_in_owner = true
visible = false
layout_mode = 2
size_flags_vertical = 4

[node name="Label" type="Label" parent="SelectedNodeWithBlockCodeBox"]
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
text = "\"{node}\" uses block coding."
horizontal_alignment = 1
autowrap_mode = 2

[node name="ButtonsBox" type="HBoxContainer" parent="SelectedNodeWithBlockCodeBox"]
layout_mode = 2
size_flags_horizontal = 4

[node name="OpenSceneButton" type="Button" parent="SelectedNodeWithBlockCodeBox/ButtonsBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 4
text = "Open in Editor"
icon = SubResource("ImageTexture_vt8ew")

[node name="ReplaceBlockCodeButton" type="Button" parent="SelectedNodeWithBlockCodeBox/ButtonsBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 4
text = "Override Block Code"
icon = ExtResource("2_710vn")

[connection signal="pressed" from="SelectedNodeBox/ButtonsBox/AddBlockCodeButton" to="." method="_on_add_block_code_button_pressed"]
[connection signal="pressed" from="SelectedNodeWithBlockCodeBox/ButtonsBox/OpenSceneButton" to="." method="_on_open_scene_button_pressed"]
[connection signal="pressed" from="SelectedNodeWithBlockCodeBox/ButtonsBox/ReplaceBlockCodeButton" to="." method="_on_replace_block_code_button_pressed"]
4 changes: 4 additions & 0 deletions addons/block_code/ui/main_panel.gd
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ func save_script():

var scene_node = EditorInterface.get_edited_scene_root()

if not BlockCodePlugin.is_block_code_editable(_current_block_code_node):
print("Block code for {node} is not editable.".format({"node": _current_block_code_node}))
return

var block_script: BlockScriptData = _current_block_code_node.block_script

var resource_path_split = block_script.resource_path.split("::", true, 1)
Expand Down
11 changes: 7 additions & 4 deletions addons/block_code/ui/title_bar/title_bar.gd
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func _ready():


func scene_selected(scene_root: Node):
_update_node_option_button_options()
_update_node_option_button_items()
var current_block_code = _editor_inspector.get_edited_object() as BlockCode
if not current_block_code:
bsd_selected(null)
Expand All @@ -27,22 +27,25 @@ func bsd_selected(bsd: BlockScriptData):
# we'll crudely update the list of BlockCode nodes whenever the
# selection changes.

_update_node_option_button_options()
_update_node_option_button_items()

var select_index = _get_index_for_bsd(bsd)
if _node_option_button.selected != select_index:
_node_option_button.select(select_index)


func _update_node_option_button_options():
func _update_node_option_button_items():
_node_option_button.clear()

var scene_root = EditorInterface.get_edited_scene_root()

if not scene_root:
return

for block_code_node in scene_root.find_children("*", "BlockCode"):
for block_code_node in BlockCodePlugin.list_block_code_for_node(scene_root, true):
if not BlockCodePlugin.is_block_code_editable(block_code_node):
continue

var node_item_index = _node_option_button.item_count
var node_label = "{name} ({type})".format({"name": scene_root.get_path_to(block_code_node).get_concatenated_names(), "type": block_code_node.block_script.script_inherits})
_node_option_button.add_item(node_label)
Expand Down

0 comments on commit 5264ba3

Please sign in to comment.