@@ -9,111 +9,197 @@ 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+ if action != value :
26+ action = value
27+ _update_action_hint ()
28+
29+ var target_snap_point : SnapPoint :
30+ get :
31+ return target_snap_point
32+ set (value ):
33+ if target_snap_point != value :
34+ target_snap_point = value
35+ _update_preview ()
36+
37+ var snap_block : Block :
38+ get :
39+ return target_snap_point .block if target_snap_point else null
40+
41+ func _init (block : Block , offset : Vector2 , block_canvas : BlockCanvas ):
42+ assert (block .get_parent () == null )
43+
44+ add_child (block )
45+ block .position = - offset
46+
47+ _block = block
48+ _block_canvas = block_canvas
49+
50+ func apply_drag () -> Block :
51+ if action == DragAction .PLACE :
52+ _place_block ()
53+ return _block
54+ elif action == DragAction .REMOVE :
55+ _remove_block ()
56+ return null
57+ else :
58+ return null
59+
60+ func _remove_block ():
61+ target_snap_point = null
62+ _block .queue_free ()
63+
64+ func _place_block ():
65+ var canvas_rect : Rect2 = _block_canvas .get_global_rect ()
66+
67+ var position = _block .global_position - canvas_rect .position
68+
69+ remove_child (_block )
70+
71+ if target_snap_point :
72+ # Snap the block to the point
73+ var orphaned_block = target_snap_point .set_snapped_block (_block )
74+ if orphaned_block :
75+ # Place the orphan block somewhere outside the snap point
76+ _block_canvas .arrange_block (orphaned_block , snap_block )
77+ else :
78+ # Block goes on screen somewhere
79+ _block_canvas .add_block (_block , position )
80+
81+ target_snap_point = null
82+
83+ func snaps_to (node : Node ) -> bool :
84+ var _snap_point : SnapPoint = node as SnapPoint
85+
86+ if not _snap_point :
87+ push_error ("Warning: node %s is not of class SnapPoint." % node )
88+ return false
89+
90+ if _snap_point .block == null :
91+ push_error ("Warning: snap point %s does not reference its parent block." % _snap_point )
92+ return false
93+
94+ if not _block_canvas .is_ancestor_of (_snap_point ):
95+ # We only snap to blocks on the canvas:
96+ return false
97+
98+ if _block .block_type != _snap_point .block_type :
99+ # We only snap to the same block type:
100+ return false
101+
102+ if _block .block_type == Types .BlockType .VALUE and not Types .can_cast (_block .variant_type , _snap_point .variant_type ):
103+ # We only snap Value blocks to snaps that can cast to same variant:
104+ return false
105+
106+ if _get_distance_to_snap_point (_snap_point ) > Constants .MINIMUM_SNAP_DISTANCE :
107+ return false
108+
109+ # Check if any parent node is this node
110+ var parent = _snap_point
111+ while parent is SnapPoint :
112+ if parent .block == _block :
113+ return false
114+
115+ parent = parent .block .get_parent ()
116+
117+ return true
118+
119+ func sort_snap_points_by_distance (a : SnapPoint , b : SnapPoint ):
120+ return _get_distance_to_snap_point (a ) < _get_distance_to_snap_point (b )
121+
122+ func _get_distance_to_snap_point (snap_point : SnapPoint ) -> float :
123+ var from_global : Vector2 = _block .global_position
124+ return from_global .distance_to (snap_point .global_position )
125+
126+ func _update_action_hint ():
127+ match action :
128+ DragAction .REMOVE :
129+ _block .modulate = Color (1.0 , 1.0 , 1.0 , 0.5 )
130+ _ :
131+ _block .modulate = Color .WHITE
132+
133+ func _update_preview ():
134+ if _preview_block :
135+ _preview_block .queue_free ()
136+ _preview_block = null
137+
138+ if target_snap_point :
139+ # Make preview block
140+ _preview_block = Control .new ()
141+ _preview_block .set_script (preload ("res://addons/block_code/ui/blocks/utilities/background/background.gd" ))
142+
143+ _preview_block .color = Color (1 , 1 , 1 , 0.5 )
144+ _preview_block .custom_minimum_size = _block .get_global_rect ().size
145+ _preview_block .size_flags_horizontal = Control .SIZE_SHRINK_BEGIN
146+ _preview_block .size_flags_vertical = Control .SIZE_SHRINK_BEGIN
147+
148+ target_snap_point .add_child (_preview_block )
16149
17- var previewing_snap_point : SnapPoint = null
18- var preview_block : Control = null
19- var preview_owner : Block = null
20150
21151var _picker : Picker
22152var _block_canvas : BlockCanvas
23153
154+ var drag : Drag = null
155+
24156
25157func _ready ():
26158 _picker = get_node (picker_path )
27159 _block_canvas = get_node (block_canvas_path )
28160
29161
30162func _process (_delta ):
31- var mouse_pos : Vector2 = get_local_mouse_position ()
32- if dragging :
33- dragging .position = mouse_pos - drag_offset
34-
35- var dragging_global_pos : Vector2 = dragging .get_global_rect ().position
36-
37- # Find closest snap point not child of current node
38- var closest_snap_point : SnapPoint = null
39- var closest_dist : float = INF
40- var snap_points : Array [Node ] = get_tree ().get_nodes_in_group ("snap_point" )
41- for snap_point in snap_points :
42- if not snap_point is SnapPoint :
43- push_error ('Warning: node %s in group "snap_point" is not of class SnapPoint.' % snap_point )
44- continue
45- if snap_point .block == null :
46- push_error ("Warning: snap point %s does not reference it's parent block." % snap_point )
47- continue
48- if not snap_point .block .on_canvas :
49- # We only snap to blocks on the canvas:
50- continue
51- if dragging .block_type != snap_point .block_type :
52- # We only snap to the same block type:
53- continue
54- if dragging .block_type == Types .BlockType .VALUE and not Types .can_cast (dragging .variant_type , snap_point .variant_type ):
55- # We only snap Value blocks to snaps that can cast to same variant:
56- continue
57- var snap_global_pos : Vector2 = snap_point .get_global_rect ().position
58- var temp_dist : float = dragging_global_pos .distance_to (snap_global_pos )
59- if temp_dist <= Constants .MINIMUM_SNAP_DISTANCE and temp_dist < closest_dist :
60- # Check if any parent node is this node
61- var is_child : bool = false
62- var parent = snap_point
63- while parent is SnapPoint :
64- if parent .block == dragging :
65- is_child = true
66-
67- parent = parent .block .get_parent ()
68-
69- if not is_child :
70- closest_dist = temp_dist
71- closest_snap_point = snap_point
72-
73- if closest_snap_point != previewing_snap_point :
74- _update_preview (closest_snap_point )
75-
76-
77- func _update_preview (snap_point : SnapPoint ):
78- previewing_snap_point = snap_point
79-
80- if preview_block :
81- preview_block .free ()
82- preview_block = null
83-
84- if previewing_snap_point :
85- # Make preview block
86- preview_block = Control .new ()
87- preview_block .set_script (preload ("res://addons/block_code/ui/blocks/utilities/background/background.gd" ))
88-
89- preview_block .color = Color (1 , 1 , 1 , 0.5 )
90- preview_block .custom_minimum_size = dragging .get_global_rect ().size
91- preview_block .size_flags_horizontal = Control .SIZE_SHRINK_BEGIN
92- preview_block .size_flags_vertical = Control .SIZE_SHRINK_BEGIN
93-
94- previewing_snap_point .add_child (preview_block )
163+ _update_drag_position ()
164+
165+
166+ func _update_drag_position ():
167+ if not drag :
168+ return
169+
170+ drag .position = get_local_mouse_position ()
171+
172+ if _picker .get_global_rect ().has_point (get_global_mouse_position ()):
173+ drag .action = DragAction .REMOVE
174+ else :
175+ drag .action = DragAction .PLACE
176+
177+ # Find closest snap point not child of current node
178+ var snap_points : Array [Node ] = get_tree ().get_nodes_in_group ("snap_point" ).filter (drag .snaps_to )
179+ snap_points .sort_custom (drag .sort_snap_points_by_distance )
180+
181+ drag .target_snap_point = snap_points [0 ] if snap_points .size () > 0 else null
95182
96183
97184func drag_block (block : Block , copied_from : Block = null ):
98- var new_pos : Vector2 = - get_global_rect (). position
185+ var offset : Vector2
99186
100187 if copied_from :
101- new_pos += copied_from . get_global_rect (). position
188+ offset = get_global_mouse_position () - copied_from . global_position
102189 else :
103- new_pos += block . get_global_rect (). position
190+ offset = get_global_mouse_position () - block . global_position
104191
105192 var parent = block .get_parent ()
193+
106194 if parent is SnapPoint :
107195 parent .remove_snapped_block (block )
108196 elif parent :
109197 parent .remove_child (block )
110198
111- block .position = new_pos
112- block .on_canvas = false
113- add_child (block )
199+ block .disconnect_signals ()
114200
115- drag_offset = get_local_mouse_position () - block . position
116- dragging = block
201+ drag = Drag . new ( block , offset , _block_canvas )
202+ add_child ( drag )
117203
118204
119205func copy_block (block : Block ) -> Block :
@@ -122,42 +208,24 @@ func copy_block(block: Block) -> Block:
122208
123209func copy_picked_block_and_drag (block : Block ):
124210 var new_block : Block = copy_block (block )
125-
126211 drag_block (new_block , block )
127212
128213
129214func drag_ended ():
130- if dragging :
131- var block_rect : Rect2 = dragging .get_global_rect ()
132-
133- # Check if in BlockCanvas
134- var block_canvas_rect : Rect2 = _block_canvas .get_global_rect ()
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 preview_block :
142- # Can snap block
143- preview_block .free ()
144- preview_block = null
145- var orphaned_block = previewing_snap_point .set_snapped_block (dragging )
146- if orphaned_block :
147- # Place the orphan block somewhere outside the snap point
148- orphaned_block .position = (
149- (previewing_snap_point .block .global_position - block_canvas_rect .position ) + (previewing_snap_point .block .get_size () * Vector2 .RIGHT ) + BLOCK_AUTO_PLACE_MARGIN
150- )
151- _block_canvas .add_block (orphaned_block )
152- else :
153- # Block goes on screen somewhere
154- dragging .position = (get_global_mouse_position () - block_canvas_rect .position - drag_offset )
155- _block_canvas .add_block (dragging )
156- else :
157- dragging .queue_free ()
215+ if not drag :
216+ return
217+
218+ _update_drag_position ()
219+
220+ var block = drag .apply_drag ()
221+
222+ if block :
223+ connect_block_canvas_signals (block )
224+
225+ drag .queue_free ()
226+ drag = null
158227
159- dragging = null
160- block_dropped .emit ()
228+ block_dropped .emit ()
161229
162230
163231func connect_block_canvas_signals (block : Block ):
0 commit comments