@@ -41,9 +41,45 @@ 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 = _icon_pin
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 ()
82+ @onready var _icon_pin := EditorInterface .get_editor_theme ().get_icon ("Pin" , "EditorIcons" )
4783
4884
4985func _ready ():
@@ -163,24 +199,62 @@ func _on_block_extension_changed():
163199
164200func _gui_input (event ):
165201 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 )
202+ if event .pressed :
203+ if event .keycode == KEY_DELETE :
204+ # Always accept the Delete key so it doesn't propagate to the
205+ # BlockCode node in the scene tree.
206+ accept_event ()
207+ confirm_delete ()
208+ elif event .ctrl_pressed and not event .shift_pressed and not event .alt_pressed and not event .meta_pressed :
209+ # Should not accept when other keys are pressed
210+ if event .keycode == KEY_D :
211+ accept_event ()
212+ confirm_duplicate ()
213+ elif event .keycode == KEY_P :
214+ accept_event ()
215+ pinned = not pinned
216+ _pin_snapped_blocks (self , pinned )
217+
218+
219+ func confirm_delete ():
220+ if not can_delete :
221+ return
222+
223+ var dialog := ConfirmationDialog .new ()
224+ var num_blocks = _count_child_blocks (self ) + 1
225+ # FIXME: Maybe this should use block_name or label, but that
226+ # requires one to be both unique and human friendly.
227+ if num_blocks > 1 :
228+ dialog .dialog_text = "Delete %d blocks?" % num_blocks
229+ else :
230+ dialog .dialog_text = "Delete block?"
231+ dialog .confirmed .connect (remove_from_tree )
232+ EditorInterface .popup_dialog_centered (dialog )
233+
234+
235+ func confirm_duplicate ():
236+ if not can_delete :
237+ return
238+
239+ var new_block : Block = _context .block_script .instantiate_block (definition )
240+
241+ var new_parent : Node = get_parent ()
242+ while not new_parent .name == "Window" :
243+ new_parent = new_parent .get_parent ()
244+
245+ if not _block_canvas :
246+ _block_canvas = get_parent ()
247+ while not _block_canvas .name == "BlockCanvas" :
248+ _block_canvas = _block_canvas .get_parent ()
249+
250+ new_parent .add_child (new_block )
251+ new_block .global_position = global_position + (Vector2 (100 , 50 ) * new_parent .scale )
252+
253+ _copy_snapped_blocks (self , new_block )
254+
255+ _block_canvas .reconnect_block .emit (new_block )
256+
257+ modified .emit ()
184258
185259
186260func remove_from_tree ():
@@ -200,7 +274,8 @@ static func get_scene_path():
200274
201275
202276func _drag_started (offset : Vector2 = Vector2 .ZERO ):
203- drag_started .emit (self , offset )
277+ if not pinned :
278+ drag_started .emit (self , offset )
204279
205280
206281func disconnect_signals ():
@@ -235,6 +310,42 @@ func _count_child_blocks(node: Node) -> int:
235310 for child in node .get_children ():
236311 if child is SnapPoint and child .has_snapped_block ():
237312 count += 1
238- count += _count_child_blocks (child )
313+
314+ if child is Container :
315+ count += _count_child_blocks (child )
239316
240317 return count
318+
319+
320+ func _copy_snapped_blocks (copy_from : Node , copy_to : Node ):
321+ var copy_to_child : Node
322+ var child_index := 0
323+ var maximum_count := copy_to .get_child_count ()
324+
325+ for copy_from_child in copy_from .get_children ():
326+ if child_index + 1 > maximum_count :
327+ return
328+
329+ copy_to_child = copy_to .get_child (child_index )
330+ child_index += 1
331+
332+ if copy_from_child is SnapPoint and copy_from_child .has_snapped_block ():
333+ copy_to_child .add_child (_context .block_script .instantiate_block (copy_from_child .snapped_block .definition ))
334+ _block_canvas .reconnect_block .emit (copy_to_child .snapped_block )
335+ elif copy_from_child .name .begins_with ("ParameterInput" ):
336+ var raw_input = copy_from_child .get_raw_input ()
337+
338+ if not raw_input is Block :
339+ copy_to_child .set_raw_input (copy_from_child .get_raw_input ())
340+
341+ if copy_from_child is Container :
342+ _copy_snapped_blocks (copy_from_child , copy_to_child )
343+
344+
345+ func _pin_snapped_blocks (node : Node , _is_pinned : bool ):
346+ for child in node .get_children ():
347+ if child is SnapPoint and child .has_snapped_block ():
348+ child .snapped_block .pinned = _is_pinned
349+
350+ if child is Container :
351+ _pin_snapped_blocks (child , _is_pinned )
0 commit comments