@@ -43,6 +43,8 @@ var can_delete: bool = true
4343
4444var _block_extension : BlockExtension
4545
46+ var _block_canvas : Node
47+
4648@onready var _context := BlockEditorContext .get_default ()
4749
4850
@@ -163,24 +165,30 @@ func _on_block_extension_changed():
163165
164166func _gui_input (event ):
165167 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 )
168+ if event .pressed :
169+ if event .keycode == KEY_DELETE :
170+ # Always accept the Delete key so it doesn't propagate to the
171+ # BlockCode node in the scene tree.
172+ accept_event ()
173+
174+ if not can_delete :
175+ return
176+
177+ var dialog := ConfirmationDialog .new ()
178+ var num_blocks = _count_child_blocks (self ) + 1
179+ # FIXME: Maybe this should use block_name or label, but that
180+ # requires one to be both unique and human friendly.
181+ if num_blocks > 1 :
182+ dialog .dialog_text = "Delete %d blocks?" % num_blocks
183+ else :
184+ dialog .dialog_text = "Delete block?"
185+ dialog .confirmed .connect (remove_from_tree )
186+ EditorInterface .popup_dialog_centered (dialog )
187+ elif event .ctrl_pressed and not event .shift_pressed and not event .alt_pressed and not event .meta_pressed :
188+ if event .keycode == KEY_D :
189+ # Handle duplicate key
190+ accept_event ()
191+ confirm_duplicate ()
184192
185193
186194func remove_from_tree ():
@@ -191,6 +199,31 @@ func remove_from_tree():
191199 modified .emit ()
192200
193201
202+ func confirm_duplicate ():
203+ if not can_delete :
204+ return
205+
206+ var new_block : Block = _context .block_script .instantiate_block (definition )
207+
208+ var new_parent : Node = get_parent ()
209+ while not new_parent .name == "Window" :
210+ new_parent = new_parent .get_parent ()
211+
212+ if not _block_canvas :
213+ _block_canvas = get_parent ()
214+ while not _block_canvas .name == "BlockCanvas" :
215+ _block_canvas = _block_canvas .get_parent ()
216+
217+ new_parent .add_child (new_block )
218+ new_block .global_position = global_position + (Vector2 (100 , 50 ) * new_parent .scale )
219+
220+ _copy_snapped_blocks (self , new_block )
221+
222+ _block_canvas .reconnect_block .emit (new_block )
223+
224+ modified .emit ()
225+
226+
194227static func get_block_class ():
195228 push_error ("Unimplemented." )
196229
@@ -239,3 +272,28 @@ func _count_child_blocks(node: Node) -> int:
239272 count += _count_child_blocks (child )
240273
241274 return count
275+
276+
277+ func _copy_snapped_blocks (copy_from : Node , copy_to : Node ):
278+ var copy_to_child : Node
279+ var child_index := 0
280+ var maximum_count := copy_to .get_child_count ()
281+
282+ for copy_from_child in copy_from .get_children ():
283+ if child_index + 1 > maximum_count :
284+ return
285+
286+ copy_to_child = copy_to .get_child (child_index )
287+ child_index += 1
288+
289+ if copy_from_child is SnapPoint and copy_from_child .has_snapped_block ():
290+ copy_to_child .add_child (_context .block_script .instantiate_block (copy_from_child .snapped_block .definition ))
291+ _block_canvas .reconnect_block .emit (copy_to_child .snapped_block )
292+ elif copy_from_child .name .begins_with ("ParameterInput" ):
293+ var raw_input = copy_from_child .get_raw_input ()
294+
295+ if not raw_input is Block :
296+ copy_to_child .set_raw_input (raw_input )
297+
298+ if copy_from_child is Container :
299+ _copy_snapped_blocks (copy_from_child , copy_to_child )
0 commit comments