Skip to content

Commit b5754fc

Browse files
authored
Merge pull request #120 from endlessm/zoom-and-pan
Add zoom and pan to block canvas
2 parents d82ce7d + 9929a18 commit b5754fc

File tree

3 files changed

+122
-13
lines changed

3 files changed

+122
-13
lines changed

addons/block_code/drag_manager/drag_manager.gd

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,14 @@ class Drag:
5757
func add_delete_area(delete_area: Rect2):
5858
_delete_areas.append(delete_area)
5959

60-
func update_drag_position():
60+
func update_drag_state():
6161
global_position = get_global_mouse_position()
6262

63+
if _block_canvas.is_mouse_over():
64+
scale = Vector2(_block_canvas.zoom, _block_canvas.zoom)
65+
else:
66+
scale = Vector2(1, 1)
67+
6368
for rect in _delete_areas:
6469
if rect.has_point(get_global_mouse_position()):
6570
action = DragAction.REMOVE
@@ -71,7 +76,7 @@ class Drag:
7176
target_snap_point = _find_closest_snap_point()
7277

7378
func apply_drag() -> Block:
74-
update_drag_position()
79+
update_drag_state()
7580

7681
if action == DragAction.PLACE:
7782
_place_block()
@@ -147,7 +152,7 @@ class Drag:
147152
var closest_distance: int
148153
for snap_point in _snap_points:
149154
var distance = _get_distance_to_snap_point(snap_point)
150-
if distance > Constants.MINIMUM_SNAP_DISTANCE:
155+
if distance > Constants.MINIMUM_SNAP_DISTANCE * _block_canvas.zoom:
151156
continue
152157
elif closest_snap_point == null or distance < closest_distance:
153158
closest_snap_point = snap_point
@@ -202,7 +207,7 @@ func _ready():
202207

203208
func _process(_delta):
204209
if drag:
205-
drag.update_drag_position()
210+
drag.update_drag_state()
206211

207212

208213
func drag_block(block: Block, copied_from: Block = null):
@@ -215,6 +220,9 @@ func drag_block(block: Block, copied_from: Block = null):
215220
else:
216221
offset = Vector2.ZERO
217222

223+
if _block_canvas.is_ancestor_of(block):
224+
offset /= _block_canvas.zoom
225+
218226
var parent = block.get_parent()
219227

220228
if parent:

addons/block_code/ui/block_canvas/block_canvas.gd

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ extends MarginContainer
44

55
const EXTEND_MARGIN: float = 800
66
const BLOCK_AUTO_PLACE_MARGIN: Vector2 = Vector2(16, 8)
7+
const ZOOM_FACTOR: float = 1.1
78

89
@onready var _window: Control = %Window
9-
@onready var _window_scroll: ScrollContainer = %WindowScroll
1010
@onready var _empty_box: BoxContainer = %EmptyBox
1111

1212
@onready var _selected_node_box: BoxContainer = %SelectedNodeBox
@@ -23,7 +23,16 @@ const BLOCK_AUTO_PLACE_MARGIN: Vector2 = Vector2(16, 8)
2323

2424
@onready var _open_scene_icon = _open_scene_button.get_theme_icon("Load", "EditorIcons")
2525

26+
@onready var _mouse_override: Control = %MouseOverride
27+
@onready var _zoom_label: Label = %ZoomLabel
28+
2629
var _block_scenes_by_class = {}
30+
var _panning := false
31+
var zoom: float:
32+
set(value):
33+
_window.scale = Vector2(value, value)
34+
get:
35+
return _window.scale.x
2736

2837
signal reconnect_block(block: Block)
2938
signal add_block_code
@@ -48,10 +57,8 @@ func _populate_block_scenes_by_class():
4857

4958

5059
func add_block(block: Block, position: Vector2 = Vector2.ZERO) -> void:
51-
block.position = position
52-
block.position.y += _window_scroll.scroll_vertical
60+
block.position = canvas_to_window(position)
5361
_window.add_child(block)
54-
_window.custom_minimum_size.y = max(block.position.y + EXTEND_MARGIN, _window.custom_minimum_size.y)
5562

5663

5764
func get_blocks() -> Array[Block]:
@@ -79,6 +86,10 @@ func bsd_selected(bsd: BlockScriptData):
7986

8087
var edited_node = EditorInterface.get_inspector().get_edited_object() as Node
8188

89+
_window.position = Vector2(0, 0)
90+
zoom = 1
91+
_zoom_label.visible = false
92+
8293
_empty_box.visible = false
8394
_selected_node_box.visible = false
8495
_selected_node_with_block_code_box.visible = false
@@ -88,6 +99,7 @@ func bsd_selected(bsd: BlockScriptData):
8899

89100
if bsd != null:
90101
_load_bsd(bsd)
102+
_zoom_label.visible = true
91103
elif edited_node == null:
92104
_empty_box.visible = true
93105
elif BlockCodePlugin.node_has_block_code(edited_node):
@@ -207,3 +219,58 @@ func _on_replace_block_code_button_pressed():
207219
_replace_block_code_button.disabled = true
208220

209221
replace_block_code.emit()
222+
223+
224+
func _input(event):
225+
if event is InputEventKey:
226+
if event.keycode == KEY_SHIFT:
227+
set_mouse_override(event.pressed)
228+
229+
if event is InputEventMouseButton:
230+
if event.button_index == MOUSE_BUTTON_LEFT or event.button_index == MOUSE_BUTTON_MIDDLE:
231+
if event.pressed and is_mouse_over():
232+
_panning = true
233+
else:
234+
_panning = false
235+
236+
if event.button_index == MOUSE_BUTTON_MIDDLE:
237+
set_mouse_override(event.pressed)
238+
239+
var relative_mouse_pos := get_global_mouse_position() - get_global_rect().position
240+
241+
if is_mouse_over():
242+
var old_mouse_window_pos := canvas_to_window(relative_mouse_pos)
243+
244+
if event.button_index == MOUSE_BUTTON_WHEEL_UP and zoom < 2:
245+
zoom *= ZOOM_FACTOR
246+
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN and zoom > 0.2:
247+
zoom /= ZOOM_FACTOR
248+
249+
_zoom_label.text = "%.1fx" % zoom
250+
251+
_window.position -= (old_mouse_window_pos - canvas_to_window(relative_mouse_pos)) * zoom
252+
253+
if event is InputEventMouseMotion:
254+
if (Input.is_key_pressed(KEY_SHIFT) and _panning) or (Input.is_mouse_button_pressed(MOUSE_BUTTON_MIDDLE) and _panning):
255+
_window.position += event.relative
256+
257+
258+
func canvas_to_window(v: Vector2) -> Vector2:
259+
return _window.get_transform().affine_inverse() * v
260+
261+
262+
func window_to_canvas(v: Vector2) -> Vector2:
263+
return _window.get_transform() * v
264+
265+
266+
func is_mouse_over() -> bool:
267+
return get_global_rect().has_point(get_global_mouse_position())
268+
269+
270+
func set_mouse_override(override: bool):
271+
if override:
272+
_mouse_override.mouse_filter = Control.MOUSE_FILTER_PASS
273+
_mouse_override.mouse_default_cursor_shape = Control.CURSOR_MOVE
274+
else:
275+
_mouse_override.mouse_filter = Control.MOUSE_FILTER_IGNORE
276+
_mouse_override.mouse_default_cursor_shape = Control.CURSOR_ARROW

addons/block_code/ui/block_canvas/block_canvas.tscn

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/block_canvas.gd" id="1_tk8h2"]
44
[ext_resource type="Texture2D" uid="uid://cmusxj1ppspnp" path="res://addons/block_code/block_code_node/block_code_node.svg" id="2_710vn"]
55

6-
[sub_resource type="Image" id="Image_2jxnn"]
6+
[sub_resource type="Image" id="Image_0g48l"]
77
data = {
88
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
99
"format": "RGBA8",
@@ -13,7 +13,7 @@ data = {
1313
}
1414

1515
[sub_resource type="ImageTexture" id="ImageTexture_jgo72"]
16-
image = SubResource("Image_2jxnn")
16+
image = SubResource("Image_0g48l")
1717

1818
[node name="BlockCanvas" type="MarginContainer"]
1919
anchors_preset = 15
@@ -28,16 +28,50 @@ script = ExtResource("1_tk8h2")
2828
[node name="Panel" type="Panel" parent="."]
2929
layout_mode = 2
3030

31-
[node name="WindowScroll" type="ScrollContainer" parent="."]
32-
unique_name_in_owner = true
31+
[node name="WindowContainer" type="MarginContainer" parent="."]
32+
clip_contents = true
3333
layout_mode = 2
3434

35-
[node name="Window" type="Control" parent="WindowScroll"]
35+
[node name="Window" type="Control" parent="WindowContainer"]
3636
unique_name_in_owner = true
3737
layout_mode = 2
3838
size_flags_horizontal = 3
3939
mouse_filter = 1
4040

41+
[node name="Overlay" type="Control" parent="WindowContainer"]
42+
layout_mode = 2
43+
mouse_filter = 2
44+
45+
[node name="MarginContainer" type="MarginContainer" parent="WindowContainer/Overlay"]
46+
layout_mode = 1
47+
anchors_preset = 3
48+
anchor_left = 1.0
49+
anchor_top = 1.0
50+
anchor_right = 1.0
51+
anchor_bottom = 1.0
52+
offset_left = -40.0
53+
offset_top = -40.0
54+
grow_horizontal = 0
55+
grow_vertical = 0
56+
mouse_filter = 2
57+
theme_override_constants/margin_left = 4
58+
theme_override_constants/margin_top = 4
59+
theme_override_constants/margin_right = 4
60+
theme_override_constants/margin_bottom = 4
61+
62+
[node name="ZoomLabel" type="Label" parent="WindowContainer/Overlay/MarginContainer"]
63+
unique_name_in_owner = true
64+
layout_mode = 2
65+
theme_override_colors/font_color = Color(1, 1, 1, 0.196078)
66+
theme_override_font_sizes/font_size = 24
67+
text = "1x"
68+
horizontal_alignment = 2
69+
70+
[node name="MouseOverride" type="MarginContainer" parent="."]
71+
unique_name_in_owner = true
72+
layout_mode = 2
73+
mouse_filter = 2
74+
4175
[node name="EmptyBox" type="VBoxContainer" parent="."]
4276
unique_name_in_owner = true
4377
visible = false

0 commit comments

Comments
 (0)