@@ -41,8 +41,43 @@ signal modified
4141## Whether the block can be deleted by the Delete key.
4242var can_delete : bool = true
4343
44+ # FIXME: Variable pinned should be saved with the scene
45+ ## Whether the block is pinned
46+ var pinned : bool :
47+ set (value ):
48+ if not can_delete :
49+ return
50+
51+ pinned = value
52+
53+ if pinned :
54+ block_pinned_container = Container .new ()
55+ block_pinned_container .mouse_filter = Control .MOUSE_FILTER_IGNORE
56+
57+ var block_pinned_panel := Panel .new ()
58+ block_pinned_panel .custom_minimum_size = Vector2 (16 , 16 )
59+ block_pinned_panel .grow_horizontal = 2
60+ block_pinned_panel .grow_vertical = 2
61+ block_pinned_panel .self_modulate = Color (1 , 1 , 1 , 0.75 )
62+
63+ var block_pinned_icon := TextureRect .new ()
64+ block_pinned_icon .texture = EditorInterface .get_editor_theme ().get_icon ("Pin" , "EditorIcons" )
65+
66+ block_pinned_panel .add_child (block_pinned_icon )
67+ block_pinned_container .add_child (block_pinned_panel )
68+ add_child (block_pinned_container )
69+ else :
70+ remove_child (block_pinned_container )
71+ block_pinned_container .queue_free ()
72+
73+ block_pinned_container .visible = pinned
74+
75+ var block_pinned_container : Container
76+
4477var _block_extension : BlockExtension
4578
79+ var _block_canvas : Node
80+
4681@onready var _context := BlockEditorContext .get_default ()
4782
4883
@@ -163,24 +198,62 @@ func _on_block_extension_changed():
163198
164199func _gui_input (event ):
165200 if event is InputEventKey :
166- if event .pressed and event .keycode == KEY_DELETE :
167- # Always accept the Delete key so it doesn't propagate to the
168- # BlockCode node in the scene tree.
169- accept_event ()
170-
171- if not can_delete :
172- return
173-
174- var dialog := ConfirmationDialog .new ()
175- var num_blocks = _count_child_blocks (self ) + 1
176- # FIXME: Maybe this should use block_name or label, but that
177- # requires one to be both unique and human friendly.
178- if num_blocks > 1 :
179- dialog .dialog_text = "Delete %d blocks?" % num_blocks
180- else :
181- dialog .dialog_text = "Delete block?"
182- dialog .confirmed .connect (remove_from_tree )
183- EditorInterface .popup_dialog_centered (dialog )
201+ if event .pressed :
202+ if event .keycode == KEY_DELETE :
203+ # Always accept the Delete key so it doesn't propagate to the
204+ # BlockCode node in the scene tree.
205+ accept_event ()
206+ confirm_delete ()
207+ elif event .ctrl_pressed and not event .shift_pressed and not event .alt_pressed and not event .meta_pressed :
208+ # Should not accept when other keys are pressed
209+ if event .keycode == KEY_D :
210+ accept_event ()
211+ confirm_duplicate ()
212+ elif event .keycode == KEY_P :
213+ accept_event ()
214+ pinned = not pinned
215+ _pin_snapped_blocks (self , pinned )
216+
217+
218+ func confirm_delete ():
219+ if not can_delete :
220+ return
221+
222+ var dialog := ConfirmationDialog .new ()
223+ var num_blocks = _count_child_blocks (self ) + 1
224+ # FIXME: Maybe this should use block_name or label, but that
225+ # requires one to be both unique and human friendly.
226+ if num_blocks > 1 :
227+ dialog .dialog_text = "Delete %d blocks?" % num_blocks
228+ else :
229+ dialog .dialog_text = "Delete block?"
230+ dialog .confirmed .connect (remove_from_tree )
231+ EditorInterface .popup_dialog_centered (dialog )
232+
233+
234+ func confirm_duplicate ():
235+ if not can_delete :
236+ return
237+
238+ var new_block : Block = _context .block_script .instantiate_block (definition )
239+
240+ var new_parent : Node = get_parent ()
241+ while not new_parent .name == "Window" :
242+ new_parent = new_parent .get_parent ()
243+
244+ if not _block_canvas :
245+ _block_canvas = get_parent ()
246+ while not _block_canvas .name == "BlockCanvas" :
247+ _block_canvas = _block_canvas .get_parent ()
248+
249+ new_parent .add_child (new_block )
250+ new_block .global_position = global_position + (Vector2 (100 , 50 ) * new_parent .scale )
251+
252+ _copy_snapped_blocks (self , new_block )
253+
254+ _block_canvas .reconnect_block .emit (new_block )
255+
256+ modified .emit ()
184257
185258
186259func remove_from_tree ():
@@ -200,7 +273,8 @@ static func get_scene_path():
200273
201274
202275func _drag_started (offset : Vector2 = Vector2 .ZERO ):
203- drag_started .emit (self , offset )
276+ if not pinned :
277+ drag_started .emit (self , offset )
204278
205279
206280func disconnect_signals ():
@@ -235,6 +309,42 @@ func _count_child_blocks(node: Node) -> int:
235309 for child in node .get_children ():
236310 if child is SnapPoint and child .has_snapped_block ():
237311 count += 1
238- count += _count_child_blocks (child )
312+
313+ if child is Container :
314+ count += _count_child_blocks (child )
239315
240316 return count
317+
318+
319+ func _copy_snapped_blocks (copy_from : Node , copy_to : Node ):
320+ var copy_to_child : Node
321+ var child_index := 0
322+ var maximum_count := copy_to .get_child_count ()
323+
324+ for copy_from_child in copy_from .get_children ():
325+ if child_index + 1 > maximum_count :
326+ return
327+
328+ copy_to_child = copy_to .get_child (child_index )
329+ child_index += 1
330+
331+ if copy_from_child is SnapPoint and copy_from_child .has_snapped_block ():
332+ copy_to_child .add_child (_context .block_script .instantiate_block (copy_from_child .snapped_block .definition ))
333+ _block_canvas .reconnect_block .emit (copy_to_child .snapped_block )
334+ elif copy_from_child .name .begins_with ("ParameterInput" ):
335+ var raw_input = copy_from_child .get_raw_input ()
336+
337+ if not raw_input is Block :
338+ copy_to_child .set_raw_input (raw_input )
339+
340+ if copy_from_child is Container :
341+ _copy_snapped_blocks (copy_from_child , copy_to_child )
342+
343+
344+ func _pin_snapped_blocks (node : Node , _is_pinned : bool ):
345+ for child in node .get_children ():
346+ if child is SnapPoint and child .has_snapped_block ():
347+ child .snapped_block .pinned = _is_pinned
348+
349+ if child is Container :
350+ _pin_snapped_blocks (child , _is_pinned )
0 commit comments