Skip to content

Commit 240038a

Browse files
authored
Merge pull request #75 from endlessm/T35524-block-snap-fixes
Reset snap point variables in DragManager.drag_end
2 parents 90d8a17 + 29ef166 commit 240038a

File tree

6 files changed

+196
-132
lines changed

6 files changed

+196
-132
lines changed

addons/block_code/drag_manager/drag_manager.gd

Lines changed: 177 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -9,111 +9,197 @@ signal block_modified
99
@export var block_canvas_path: NodePath
1010

1111
const 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

21151
var _picker: Picker
22152
var _block_canvas: BlockCanvas
23153

154+
var drag: Drag = null
155+
24156

25157
func _ready():
26158
_picker = get_node(picker_path)
27159
_block_canvas = get_node(block_canvas_path)
28160

29161

30162
func _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

97184
func 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

119205
func copy_block(block: Block) -> Block:
@@ -122,42 +208,24 @@ func copy_block(block: Block) -> Block:
122208

123209
func copy_picked_block_and_drag(block: Block):
124210
var new_block: Block = copy_block(block)
125-
126211
drag_block(new_block, block)
127212

128213

129214
func 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

163231
func connect_block_canvas_signals(block: Block):

addons/block_code/ui/block_canvas/block_canvas.gd

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ class_name BlockCanvas
33
extends MarginContainer
44

55
const EXTEND_MARGIN: float = 800
6+
const BLOCK_AUTO_PLACE_MARGIN: Vector2 = Vector2(16, 8)
67

78
@onready var _window: Control = %Window
89
@onready var _window_scroll: ScrollContainer = %WindowScroll
@@ -28,12 +29,18 @@ func _populate_block_scenes_by_class():
2829
_block_scenes_by_class[_class.class] = _script.get_scene_path()
2930

3031

31-
func add_block(block: Block) -> void:
32+
func add_block(block: Block, position: Vector2 = Vector2.ZERO) -> void:
33+
block.position = position
3234
block.position.y += _window_scroll.scroll_vertical
3335
_window.add_child(block)
3436
_window.custom_minimum_size.y = max(block.position.y + EXTEND_MARGIN, _window.custom_minimum_size.y)
3537

3638

39+
func arrange_block(block: Block, nearby_block: Block) -> void:
40+
add_block(block)
41+
block.global_position = (nearby_block.global_position + (nearby_block.get_size() * Vector2.RIGHT) + BLOCK_AUTO_PLACE_MARGIN)
42+
43+
3744
func set_child(n: Node):
3845
n.owner = _window
3946
for c in n.get_children():
@@ -75,7 +82,6 @@ func load_tree(parent: Node, node: SerializedBlockTreeNode):
7582
for prop_pair in node.serialized_block.serialized_props:
7683
scene.set(prop_pair[0], prop_pair[1])
7784

78-
scene.on_canvas = true
7985
parent.add_child(scene)
8086

8187
var scene_block: Block = scene as Block

addons/block_code/ui/blocks/block/block.gd

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ signal modified
2323
## The next block in the line of execution (can be null if end)
2424
@export var bottom_snap_path: NodePath
2525

26-
var on_canvas: bool = false
27-
2826
var bottom_snap: SnapPoint
2927

3028

0 commit comments

Comments
 (0)