@@ -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