Skip to content

Commit a064abb

Browse files
committed
DragManager: Move Drag class to its own file
1 parent 13cdca3 commit a064abb

File tree

2 files changed

+198
-182
lines changed

2 files changed

+198
-182
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
@tool
2+
extends Control
3+
4+
const Constants = preload("res://addons/block_code/ui/constants.gd")
5+
const DragManager = preload("res://addons/block_code/drag_manager/drag_manager.gd")
6+
7+
var _block: Block
8+
var _block_scope: String
9+
var _block_canvas: BlockCanvas
10+
var _preview_block: Control
11+
var _snap_points: Array[Node]
12+
var _delete_areas: Array[Rect2]
13+
var action: DragManager.DragAction:
14+
get:
15+
return action
16+
set(value):
17+
if action != value:
18+
action = value
19+
_update_action_hint()
20+
21+
var target_snap_point: SnapPoint:
22+
get:
23+
return target_snap_point
24+
set(value):
25+
if target_snap_point != value:
26+
target_snap_point = value
27+
_update_preview()
28+
29+
var snap_block: Block:
30+
get:
31+
return target_snap_point.get_parent_block() if target_snap_point else null
32+
33+
34+
func _init(block: Block, block_scope: String, offset: Vector2, block_canvas: BlockCanvas):
35+
assert(block.get_parent() == null)
36+
37+
add_child(block)
38+
block.position = -offset
39+
40+
_block = block
41+
_block_scope = block_scope
42+
_block_canvas = block_canvas
43+
44+
45+
func set_snap_points(snap_points: Array[Node]):
46+
_snap_points = snap_points.filter(_snaps_to)
47+
48+
49+
func add_delete_area(delete_area: Rect2):
50+
_delete_areas.append(delete_area)
51+
52+
53+
func update_drag_state():
54+
global_position = get_global_mouse_position()
55+
56+
if _block_canvas.is_mouse_over():
57+
scale = Vector2(_block_canvas.zoom, _block_canvas.zoom)
58+
else:
59+
scale = Vector2(1, 1)
60+
61+
for rect in _delete_areas:
62+
if rect.has_point(get_global_mouse_position()):
63+
action = DragManager.DragAction.REMOVE
64+
target_snap_point = null
65+
return
66+
67+
action = DragManager.DragAction.PLACE
68+
69+
target_snap_point = _find_closest_snap_point()
70+
71+
72+
func apply_drag() -> Block:
73+
update_drag_state()
74+
75+
if action == DragManager.DragAction.PLACE:
76+
_place_block()
77+
return _block
78+
elif action == DragManager.DragAction.REMOVE:
79+
_remove_block()
80+
return null
81+
else:
82+
return null
83+
84+
85+
func _remove_block():
86+
target_snap_point = null
87+
_block.queue_free()
88+
89+
90+
func _place_block():
91+
var canvas_rect: Rect2 = _block_canvas.get_global_rect()
92+
93+
var position = _block.global_position - canvas_rect.position
94+
95+
remove_child(_block)
96+
97+
if target_snap_point:
98+
# Snap the block to the point
99+
var orphaned_block = target_snap_point.insert_snapped_block(_block)
100+
if orphaned_block:
101+
# Place the orphan block somewhere outside the snap point
102+
_block_canvas.arrange_block(orphaned_block, snap_block)
103+
else:
104+
# Block goes on screen somewhere
105+
_block_canvas.add_block(_block, position)
106+
107+
target_snap_point = null
108+
109+
110+
func _snaps_to(node: Node) -> bool:
111+
var _snap_point: SnapPoint = node as SnapPoint
112+
113+
if not _snap_point:
114+
push_error("Warning: node %s is not of class SnapPoint." % node)
115+
return false
116+
117+
if not _block_canvas.is_ancestor_of(_snap_point):
118+
# We only snap to blocks on the canvas:
119+
return false
120+
121+
if _block.block_type != _snap_point.block_type:
122+
# We only snap to the same block type:
123+
return false
124+
125+
if _block.block_type == Types.BlockType.VALUE and not Types.can_cast(_block.variant_type, _snap_point.variant_type):
126+
# We only snap Value blocks to snaps that can cast to same variant:
127+
return false
128+
129+
# Check if any parent node is this node
130+
if _snap_point.is_ancestor_of(_block):
131+
return false
132+
133+
var top_block = _get_top_block_for_node(_snap_point)
134+
135+
# Check if scope is valid
136+
if _block_scope != "":
137+
if top_block is EntryBlock:
138+
if _block_scope != top_block.get_entry_statement():
139+
return false
140+
elif top_block:
141+
var tree_scope := DragManager.get_tree_scope(top_block)
142+
if tree_scope != "" and _block_scope != tree_scope:
143+
return false
144+
145+
return true
146+
147+
148+
func _find_closest_snap_point() -> Node:
149+
var closest_snap_point: SnapPoint = null
150+
var closest_distance: int
151+
for snap_point in _snap_points:
152+
var distance = _get_distance_to_snap_point(snap_point)
153+
if distance > Constants.MINIMUM_SNAP_DISTANCE * _block_canvas.zoom:
154+
continue
155+
elif closest_snap_point == null or distance < closest_distance:
156+
closest_snap_point = snap_point
157+
closest_distance = distance
158+
return closest_snap_point
159+
160+
161+
func _get_top_block_for_node(node: Node) -> Block:
162+
for top_block in _block_canvas.get_blocks():
163+
if top_block.is_ancestor_of(node):
164+
return top_block
165+
return null
166+
167+
168+
func _get_distance_to_snap_point(snap_point: SnapPoint) -> float:
169+
var from_global: Vector2 = _block.global_position
170+
return from_global.distance_to(snap_point.global_position)
171+
172+
173+
func _update_action_hint():
174+
match action:
175+
DragManager.DragAction.REMOVE:
176+
_block.modulate = Color(1.0, 1.0, 1.0, 0.5)
177+
_:
178+
_block.modulate = Color.WHITE
179+
180+
181+
func _update_preview():
182+
if _preview_block:
183+
_preview_block.queue_free()
184+
_preview_block = null
185+
186+
if target_snap_point:
187+
# Make preview block
188+
_preview_block = Control.new()
189+
_preview_block.set_script(preload("res://addons/block_code/ui/blocks/utilities/background/background.gd"))
190+
191+
_preview_block.color = Color(1, 1, 1, 0.5)
192+
_preview_block.custom_minimum_size = _block.get_global_rect().size
193+
_preview_block.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
194+
_preview_block.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
195+
196+
target_snap_point.add_child(_preview_block)

addons/block_code/drag_manager/drag_manager.gd

Lines changed: 2 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -5,195 +5,15 @@ extends Control
55
signal block_dropped
66
signal block_modified
77

8+
const Drag = preload("res://addons/block_code/drag_manager/drag.gd")
9+
810
@export var picker_path: NodePath
911
@export var block_canvas_path: NodePath
1012

1113
const Constants = preload("res://addons/block_code/ui/constants.gd")
1214

1315
enum DragAction { NONE, PLACE, REMOVE }
1416

15-
16-
class Drag:
17-
extends Control
18-
var _block: Block
19-
var _block_scope: String
20-
var _block_canvas: BlockCanvas
21-
var _preview_block: Control
22-
var _snap_points: Array[Node]
23-
var _delete_areas: Array[Rect2]
24-
var action: DragAction:
25-
get:
26-
return action
27-
set(value):
28-
if action != value:
29-
action = value
30-
_update_action_hint()
31-
32-
var target_snap_point: SnapPoint:
33-
get:
34-
return target_snap_point
35-
set(value):
36-
if target_snap_point != value:
37-
target_snap_point = value
38-
_update_preview()
39-
40-
var snap_block: Block:
41-
get:
42-
return target_snap_point.get_parent_block() if target_snap_point else null
43-
44-
func _init(block: Block, block_scope: String, offset: Vector2, block_canvas: BlockCanvas):
45-
assert(block.get_parent() == null)
46-
47-
add_child(block)
48-
block.position = -offset
49-
50-
_block = block
51-
_block_scope = block_scope
52-
_block_canvas = block_canvas
53-
54-
func set_snap_points(snap_points: Array[Node]):
55-
_snap_points = snap_points.filter(_snaps_to)
56-
57-
func add_delete_area(delete_area: Rect2):
58-
_delete_areas.append(delete_area)
59-
60-
func update_drag_state():
61-
global_position = get_global_mouse_position()
62-
63-
if _block_canvas.is_mouse_over():
64-
scale = Vector2(_block_canvas.zoom, _block_canvas.zoom)
65-
else:
66-
scale = Vector2(1, 1)
67-
68-
for rect in _delete_areas:
69-
if rect.has_point(get_global_mouse_position()):
70-
action = DragAction.REMOVE
71-
target_snap_point = null
72-
return
73-
74-
action = DragAction.PLACE
75-
76-
target_snap_point = _find_closest_snap_point()
77-
78-
func apply_drag() -> Block:
79-
update_drag_state()
80-
81-
if action == DragAction.PLACE:
82-
_place_block()
83-
return _block
84-
elif action == DragAction.REMOVE:
85-
_remove_block()
86-
return null
87-
else:
88-
return null
89-
90-
func _remove_block():
91-
target_snap_point = null
92-
_block.queue_free()
93-
94-
func _place_block():
95-
var canvas_rect: Rect2 = _block_canvas.get_global_rect()
96-
97-
var position = _block.global_position - canvas_rect.position
98-
99-
remove_child(_block)
100-
101-
if target_snap_point:
102-
# Snap the block to the point
103-
var orphaned_block = target_snap_point.insert_snapped_block(_block)
104-
if orphaned_block:
105-
# Place the orphan block somewhere outside the snap point
106-
_block_canvas.arrange_block(orphaned_block, snap_block)
107-
else:
108-
# Block goes on screen somewhere
109-
_block_canvas.add_block(_block, position)
110-
111-
target_snap_point = null
112-
113-
func _snaps_to(node: Node) -> bool:
114-
var _snap_point: SnapPoint = node as SnapPoint
115-
116-
if not _snap_point:
117-
push_error("Warning: node %s is not of class SnapPoint." % node)
118-
return false
119-
120-
if not _block_canvas.is_ancestor_of(_snap_point):
121-
# We only snap to blocks on the canvas:
122-
return false
123-
124-
if _block.block_type != _snap_point.block_type:
125-
# We only snap to the same block type:
126-
return false
127-
128-
if _block.block_type == Types.BlockType.VALUE and not Types.can_cast(_block.variant_type, _snap_point.variant_type):
129-
# We only snap Value blocks to snaps that can cast to same variant:
130-
return false
131-
132-
# Check if any parent node is this node
133-
if _snap_point.is_ancestor_of(_block):
134-
return false
135-
136-
var top_block = _get_top_block_for_node(_snap_point)
137-
138-
# Check if scope is valid
139-
if _block_scope != "":
140-
if top_block is EntryBlock:
141-
if _block_scope != top_block.get_entry_statement():
142-
return false
143-
elif top_block:
144-
var tree_scope := DragManager.get_tree_scope(top_block)
145-
if tree_scope != "" and _block_scope != tree_scope:
146-
return false
147-
148-
return true
149-
150-
func _find_closest_snap_point() -> Node:
151-
var closest_snap_point: SnapPoint = null
152-
var closest_distance: int
153-
for snap_point in _snap_points:
154-
var distance = _get_distance_to_snap_point(snap_point)
155-
if distance > Constants.MINIMUM_SNAP_DISTANCE * _block_canvas.zoom:
156-
continue
157-
elif closest_snap_point == null or distance < closest_distance:
158-
closest_snap_point = snap_point
159-
closest_distance = distance
160-
return closest_snap_point
161-
162-
func _get_top_block_for_node(node: Node) -> Block:
163-
for top_block in _block_canvas.get_blocks():
164-
if top_block.is_ancestor_of(node):
165-
return top_block
166-
return null
167-
168-
func _get_distance_to_snap_point(snap_point: SnapPoint) -> float:
169-
var from_global: Vector2 = _block.global_position
170-
return from_global.distance_to(snap_point.global_position)
171-
172-
func _update_action_hint():
173-
match action:
174-
DragAction.REMOVE:
175-
_block.modulate = Color(1.0, 1.0, 1.0, 0.5)
176-
_:
177-
_block.modulate = Color.WHITE
178-
179-
func _update_preview():
180-
if _preview_block:
181-
_preview_block.queue_free()
182-
_preview_block = null
183-
184-
if target_snap_point:
185-
# Make preview block
186-
_preview_block = Control.new()
187-
_preview_block.set_script(preload("res://addons/block_code/ui/blocks/utilities/background/background.gd"))
188-
189-
_preview_block.color = Color(1, 1, 1, 0.5)
190-
_preview_block.custom_minimum_size = _block.get_global_rect().size
191-
_preview_block.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
192-
_preview_block.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
193-
194-
target_snap_point.add_child(_preview_block)
195-
196-
19717
var _picker: Picker
19818
var _block_canvas: BlockCanvas
19919

0 commit comments

Comments
 (0)