@@ -9,110 +9,189 @@ signal block_modified
99@export var block_canvas_path : NodePath
1010
1111const Constants = preload ("res://addons/block_code/ui/constants.gd" )
12- const BLOCK_AUTO_PLACE_MARGIN : Vector2 = Vector2 (16 , 8 )
1312
14- var drag_offset : Vector2
15- var dragging : Block = null
13+ enum DragAction { NONE , PLACE , REMOVE }
14+
15+
16+ class Drag :
17+ extends Control
18+ var _block : Block
19+ var _block_canvas : BlockCanvas
20+ var _preview_block : Control
21+ var action : DragAction :
22+ get :
23+ return action
24+ set (value ):
25+ action = value
26+
27+ var target_snap_point : SnapPoint :
28+ get :
29+ return target_snap_point
30+ set (value ):
31+ if target_snap_point != value :
32+ target_snap_point = value
33+ _update_preview ()
34+
35+ var snap_block : Block :
36+ get :
37+ return target_snap_point .block if target_snap_point else null
38+
39+ func _init (block : Block , offset : Vector2 , block_canvas : BlockCanvas ):
40+ assert (block .get_parent () == null )
41+
42+ add_child (block )
43+ block .on_canvas = false
44+ block .position = - offset
45+
46+ _block = block
47+ _block_canvas = block_canvas
48+
49+ func apply_drag () -> Block :
50+ if action == DragAction .PLACE :
51+ _place_block ()
52+ return _block
53+ elif action == DragAction .REMOVE :
54+ _remove_block ()
55+ return null
56+ else :
57+ return null
58+
59+ func _remove_block ():
60+ target_snap_point = null
61+ _block .g ()
62+
63+ func _place_block ():
64+ var canvas_rect : Rect2 = _block_canvas .get_global_rect ()
65+
66+ var position = _block .global_position - canvas_rect .position
67+
68+ remove_child (_block )
69+
70+ if target_snap_point :
71+ # Snap the block to the point
72+ var orphaned_block = target_snap_point .set_snapped_block (_block )
73+ if orphaned_block :
74+ # Place the orphan block somewhere outside the snap point
75+ _block_canvas .arrange_block (orphaned_block , snap_block )
76+ else :
77+ # Block goes on screen somewhere
78+ _block_canvas .add_block (_block , position )
79+
80+ target_snap_point = null
81+
82+ func snaps_to (node : Node ) -> bool :
83+ var _snap_point : SnapPoint = node as SnapPoint
84+
85+ if not _snap_point :
86+ push_error ("Warning: node %s is not of class SnapPoint." % node )
87+ return false
88+
89+ if _snap_point .block == null :
90+ push_error ("Warning: snap point %s does not reference its parent block." % _snap_point )
91+ return false
92+
93+ if not _snap_point .block .on_canvas :
94+ # We only snap to blocks on the canvas:
95+ return false
96+
97+ if _block .block_type != _snap_point .block_type :
98+ # We only snap to the same block type:
99+ return false
100+
101+ if _block .block_type == Types .BlockType .VALUE and not Types .can_cast (_block .variant_type , _snap_point .variant_type ):
102+ # We only snap Value blocks to snaps that can cast to same variant:
103+ return false
104+
105+ if _get_distance_to_snap_point (_snap_point ) > Constants .MINIMUM_SNAP_DISTANCE :
106+ return false
107+
108+ # Check if any parent node is this node
109+ var parent = _snap_point
110+ while parent is SnapPoint :
111+ if parent .block == _block :
112+ return false
113+
114+ parent = parent .block .get_parent ()
115+
116+ return true
117+
118+ func sort_snap_points_by_distance (a : SnapPoint , b : SnapPoint ):
119+ return _get_distance_to_snap_point (a ) < _get_distance_to_snap_point (b )
120+
121+ func _get_distance_to_snap_point (snap_point : SnapPoint ) -> float :
122+ var from_global : Vector2 = _block .global_position
123+ return from_global .distance_to (snap_point .global_position )
124+
125+ func _update_preview ():
126+ if _preview_block :
127+ _preview_block .queue_free ()
128+ _preview_block = null
129+
130+ if target_snap_point :
131+ # Make preview block
132+ _preview_block = Control .new ()
133+ _preview_block .set_script (preload ("res://addons/block_code/ui/blocks/utilities/background/background.gd" ))
134+
135+ _preview_block .color = Color (1 , 1 , 1 , 0.5 )
136+ _preview_block .custom_minimum_size = _block .get_global_rect ().size
137+ _preview_block .size_flags_horizontal = Control .SIZE_SHRINK_BEGIN
138+ _preview_block .size_flags_vertical = Control .SIZE_SHRINK_BEGIN
139+
140+ target_snap_point .add_child (_preview_block )
16141
17- var previewing_snap_point : SnapPoint = null
18- var preview_block : Control = null
19142
20143var _picker : Picker
21144var _block_canvas : BlockCanvas
22145
146+ var drag : Drag = null
147+
23148
24149func _ready ():
25150 _picker = get_node (picker_path )
26151 _block_canvas = get_node (block_canvas_path )
27152
28153
29154func _process (_delta ):
30- var mouse_pos : Vector2 = get_local_mouse_position ()
31- if dragging :
32- dragging .position = mouse_pos - drag_offset
33-
34- var dragging_global_pos : Vector2 = dragging .get_global_rect ().position
35-
36- # Find closest snap point not child of current node
37- var closest_snap_point : SnapPoint = null
38- var closest_dist : float = INF
39- var snap_points : Array [Node ] = get_tree ().get_nodes_in_group ("snap_point" )
40- for snap_point in snap_points :
41- if not snap_point is SnapPoint :
42- push_error ('Warning: node %s in group "snap_point" is not of class SnapPoint.' % snap_point )
43- continue
44- if snap_point .block == null :
45- push_error ("Warning: snap point %s does not reference it's parent block." % snap_point )
46- continue
47- if not snap_point .block .on_canvas :
48- # We only snap to blocks on the canvas:
49- continue
50- if dragging .block_type != snap_point .block_type :
51- # We only snap to the same block type:
52- continue
53- if dragging .block_type == Types .BlockType .VALUE and not Types .can_cast (dragging .variant_type , snap_point .variant_type ):
54- # We only snap Value blocks to snaps that can cast to same variant:
55- continue
56- var snap_global_pos : Vector2 = snap_point .get_global_rect ().position
57- var temp_dist : float = dragging_global_pos .distance_to (snap_global_pos )
58- if temp_dist <= Constants .MINIMUM_SNAP_DISTANCE and temp_dist < closest_dist :
59- # Check if any parent node is this node
60- var is_child : bool = false
61- var parent = snap_point
62- while parent is SnapPoint :
63- if parent .block == dragging :
64- is_child = true
65-
66- parent = parent .block .get_parent ()
67-
68- if not is_child :
69- closest_dist = temp_dist
70- closest_snap_point = snap_point
71-
72- if closest_snap_point != previewing_snap_point :
73- _update_preview (closest_snap_point )
74-
75-
76- func _update_preview (snap_point : SnapPoint ):
77- previewing_snap_point = snap_point
78-
79- if preview_block :
80- preview_block .free ()
81- preview_block = null
82-
83- if previewing_snap_point :
84- # Make preview block
85- preview_block = Control .new ()
86- preview_block .set_script (preload ("res://addons/block_code/ui/blocks/utilities/background/background.gd" ))
87-
88- preview_block .color = Color (1 , 1 , 1 , 0.5 )
89- preview_block .custom_minimum_size = dragging .get_global_rect ().size
90- preview_block .size_flags_horizontal = Control .SIZE_SHRINK_BEGIN
91- preview_block .size_flags_vertical = Control .SIZE_SHRINK_BEGIN
92-
93- previewing_snap_point .add_child (preview_block )
155+ _update_drag_position ()
156+
157+
158+ func _update_drag_position ():
159+ if not drag :
160+ return
161+
162+ drag .position = get_local_mouse_position ()
163+
164+ if _picker .get_global_rect ().has_point (get_global_mouse_position ()):
165+ drag .action = DragAction .REMOVE
166+ else :
167+ drag .action = DragAction .PLACE
168+
169+ # Find closest snap point not child of current node
170+ var snap_points : Array [Node ] = get_tree ().get_nodes_in_group ("snap_point" ).filter (drag .snaps_to )
171+ snap_points .sort_custom (drag .sort_snap_points_by_distance )
172+
173+ drag .target_snap_point = snap_points [0 ] if snap_points .size () > 0 else null
94174
95175
96176func drag_block (block : Block , copied_from : Block = null ):
97- var new_pos : Vector2 = - get_global_rect (). position
177+ var offset : Vector2
98178
99179 if copied_from :
100- new_pos += copied_from . get_global_rect (). position
180+ offset = get_global_mouse_position () - copied_from . global_position
101181 else :
102- new_pos += block . get_global_rect (). position
182+ offset = get_global_mouse_position () - block . global_position
103183
104184 var parent = block .get_parent ()
185+
105186 if parent is SnapPoint :
106187 parent .remove_snapped_block (block )
107188 elif parent :
108189 parent .remove_child (block )
109190
110- block .position = new_pos
111- block .on_canvas = false
112- add_child (block )
191+ block .disconnect_signals ()
113192
114- drag_offset = get_local_mouse_position () - block . position
115- dragging = block
193+ drag = Drag . new ( block , offset , _block_canvas )
194+ add_child ( drag )
116195
117196
118197func copy_block (block : Block ) -> Block :
@@ -121,47 +200,24 @@ func copy_block(block: Block) -> Block:
121200
122201func copy_picked_block_and_drag (block : Block ):
123202 var new_block : Block = copy_block (block )
124-
125203 drag_block (new_block , block )
126204
127205
128206func drag_ended ():
129- if dragging :
130- var block_rect : Rect2 = dragging .get_global_rect ()
131-
132- # Check if in BlockCanvas
133- var block_canvas_rect : Rect2 = _block_canvas .get_global_rect ()
134-
135- if block_canvas_rect .encloses (block_rect ):
136- dragging .disconnect_signals () # disconnect previous on canvas signal connections
137- connect_block_canvas_signals (dragging )
138- remove_child (dragging )
139- dragging .on_canvas = true
140-
141- if previewing_snap_point :
142- # Can snap block
143- var orphaned_block = previewing_snap_point .set_snapped_block (dragging )
144- if orphaned_block :
145- # Place the orphan block somewhere outside the snap point
146- orphaned_block .position = (
147- (previewing_snap_point .block .global_position - block_canvas_rect .position ) + (previewing_snap_point .block .get_size () * Vector2 .RIGHT ) + BLOCK_AUTO_PLACE_MARGIN
148- )
149- _block_canvas .add_block (orphaned_block )
150- else :
151- # Block goes on screen somewhere
152- dragging .position = (get_global_mouse_position () - block_canvas_rect .position - drag_offset )
153- _block_canvas .add_block (dragging )
154- else :
155- dragging .queue_free ()
207+ if not drag :
208+ return
209+
210+ _update_drag_position ()
211+
212+ var block = drag .apply_drag ()
156213
157- if preview_block :
158- preview_block .queue_free ()
159- preview_block = null
214+ if block :
215+ connect_block_canvas_signals (block )
160216
161- previewing_snap_point = null
162- dragging = null
217+ drag . queue_free ()
218+ drag = null
163219
164- block_dropped .emit ()
220+ block_dropped .emit ()
165221
166222
167223func connect_block_canvas_signals (block : Block ):
0 commit comments