|
tool |
|
extends Panel |
|
|
|
# Node references |
|
var graph_edit : GraphEdit |
|
var graph_hscroll : HScrollBar |
|
var graph_vscroll : VScrollBar |
|
|
|
var minimap_button : Button |
|
|
|
# Public properties |
|
export var toolbar_icon : Texture |
|
|
|
# Private properties |
|
var _graph_nodes : Array = [] # of Dictionary |
|
var _graph_lines : Array = [] # of Dictionary |
|
var _camera_position : Vector2 = Vector2(100, 50) |
|
var _camera_size : Vector2 = Vector2(200, 200) |
|
var _graph_proportions : Vector2 = Vector2(1, 1) |
|
var _zoom_level : float = 1.0 |
|
|
|
var _map_padding : Vector2 = Vector2(5, 5) |
|
var _graph_padding : Vector2 = Vector2(0, 0) |
|
|
|
var _is_pressing : bool = false |
|
|
|
var _icon_modulate_color : Color = Color.white.linear_interpolate(Color.black, 0.25) |
|
var _icon_modulate_active_color : Color = Color.white |
|
|
|
func _enter_tree() -> void: |
|
_update_node_references() |
|
|
|
func _ready() -> void: |
|
_update_theme() |
|
_update_map() |
|
|
|
if (graph_edit): |
|
minimap_button = ToolButton.new() |
|
minimap_button.icon = toolbar_icon |
|
minimap_button.modulate = _icon_modulate_color |
|
minimap_button.hint_tooltip = "Toggle graph minimap." |
|
graph_edit.get_zoom_hbox().add_child(minimap_button) |
|
minimap_button.connect("pressed", self, "_on_minimap_button_pressed") |
|
|
|
graph_edit.connect("draw", self, "_on_graph_edit_changed") |
|
graph_edit.connect("update_minimap", self, "_on_graph_edit_changed") |
|
|
|
func _gui_input(event : InputEvent) -> void: |
|
if (event is InputEventMouseButton && event.button_index == BUTTON_LEFT): |
|
if (event.is_pressed()): |
|
_is_pressing = true |
|
var mouse_event = (event as InputEventMouseButton) |
|
var click_location = _convert_to_graph_position(mouse_event.position - _map_padding) - _graph_padding |
|
|
|
if (graph_edit): |
|
var scroll_offset = get_scroll_offset() |
|
graph_edit.scroll_offset = click_location + scroll_offset - _camera_size / 2 |
|
elif (_is_pressing): |
|
_is_pressing = false |
|
|
|
accept_event() |
|
elif (event is InputEventMouseMotion && _is_pressing): |
|
var mouse_event = (event as InputEventMouseMotion) |
|
var click_location = _convert_to_graph_position(mouse_event.position - _map_padding) - _graph_padding |
|
|
|
if (graph_edit): |
|
var scroll_offset = get_scroll_offset() |
|
graph_edit.scroll_offset = click_location + scroll_offset - _camera_size / 2 |
|
|
|
accept_event() |
|
|
|
func _draw() -> void: |
|
var centering_offset = _convert_from_graph_position(_graph_padding) |
|
var map_offset = _map_padding + centering_offset |
|
|
|
for node in _graph_nodes: |
|
var position = _convert_from_graph_position(node.position) + map_offset |
|
var size = _convert_from_graph_position(node.size) |
|
|
|
if (node.is_comment): |
|
var node_shape = Rect2(position - size / 2, size) |
|
var comment_background = node.node_color.linear_interpolate(Color.black, 0.65) |
|
draw_rect(node_shape, comment_background, true) |
|
draw_rect(node_shape, node.node_color, false) |
|
else: |
|
draw_circle(position, 3, node.node_color) |
|
|
|
for line in _graph_lines: |
|
var from_position = _convert_from_graph_position(line.from_position) + map_offset |
|
var to_position = _convert_from_graph_position(line.to_position) + map_offset |
|
|
|
var bezier_len_pos = get_constant("bezier_len_pos", "GraphEdit") / 2 |
|
var bezier_len_neg = get_constant("bezier_len_neg", "GraphEdit") / 2 |
|
DrawUtils.draw_cos_line(self, from_position, to_position, line.from_color, line.to_color, bezier_len_pos, bezier_len_neg) |
|
|
|
var camera_center = _convert_from_graph_position(_camera_position + _camera_size / 2) + map_offset |
|
var camera_viewport = _convert_from_graph_position(_camera_size) |
|
var camera_position = (camera_center - camera_viewport / 2) |
|
draw_rect(Rect2(camera_position, camera_viewport), Color(0.65, 0.65, 0.65, 0.2), true) |
|
draw_rect(Rect2(camera_position, camera_viewport), Color(0.65, 0.65, 0.65, 0.45), false) |
|
|
|
func _update_theme() -> void: |
|
if (!Engine.editor_hint || !is_inside_tree()): |
|
return |
|
|
|
modulate.a = 0.85 |
|
_icon_modulate_color = get_color("font_color", "Button") |
|
_icon_modulate_active_color = get_color("accent_color", "Editor") |
|
|
|
func _update_map() -> void: |
|
if (!graph_edit): |
|
return |
|
|
|
# Update graph spatial information |
|
var scroll_offset = get_scroll_offset() |
|
var graph_size = get_graph_size() |
|
|
|
_zoom_level = graph_edit.zoom |
|
_camera_position = graph_edit.scroll_offset - scroll_offset |
|
_camera_size = graph_edit.rect_size |
|
|
|
var render_size = get_render_size() |
|
var target_ratio = render_size.x / render_size.y |
|
var graph_ratio = graph_size.x / graph_size.y |
|
|
|
_graph_proportions = graph_size |
|
_graph_padding = Vector2(0, 0) |
|
if (graph_ratio > target_ratio): |
|
_graph_proportions.x = graph_size.x |
|
_graph_proportions.y = graph_size.x / target_ratio |
|
_graph_padding.y = abs(graph_size.y - _graph_proportions.y) / 2 |
|
else: |
|
_graph_proportions.x = graph_size.y * target_ratio |
|
_graph_proportions.y = graph_size.y |
|
_graph_padding.x = abs(graph_size.x - _graph_proportions.x) / 2 |
|
|
|
# Update node information |
|
_graph_nodes = [] |
|
_graph_lines = [] |
|
|
|
for child in graph_edit.get_children(): |
|
if !(child is GraphNode): |
|
continue |
|
|
|
var node_data := { |
|
"position": (child.offset + child.rect_size / 2) * _zoom_level - scroll_offset, |
|
"size": child.rect_size * _zoom_level, |
|
"node_color": Color.white, |
|
"is_comment": child.comment, |
|
} |
|
|
|
var child_color = child.get("minimap_color") |
|
if (child_color): |
|
node_data.node_color = child_color |
|
|
|
_graph_nodes.append(node_data) |
|
|
|
for connection in graph_edit.get_connection_list(): |
|
var from_child = graph_edit.get_node_or_null(connection.from) |
|
var to_child = graph_edit.get_node_or_null(connection.to) |
|
if (!from_child || !to_child): |
|
continue # We got caught in between two resources switching, some data is outdated |
|
|
|
var from_slot_position = from_child.rect_size if from_child.comment else from_child.rect_size / 2 |
|
var to_slot_position = 0 if from_child.comment else to_child.rect_size / 2 |
|
|
|
var line_data := { |
|
"from_position": (from_child.offset + from_slot_position) * _zoom_level - scroll_offset, |
|
"to_position": (to_child.offset + to_slot_position) * _zoom_level - scroll_offset, |
|
"from_color": Color.white, |
|
"to_color": Color.white, |
|
} |
|
|
|
var from_child_color = from_child.get("minimap_color") |
|
if (from_child_color): |
|
line_data.from_color = from_child_color |
|
var to_child_color = to_child.get("minimap_color") |
|
if (to_child_color): |
|
line_data.to_color = to_child_color |
|
|
|
_graph_lines.append(line_data) |
|
|
|
update() |
|
|
|
func _update_node_references() -> void: |
|
var parent_node = get_parent() |
|
if !(parent_node is GraphEdit): |
|
return |
|
|
|
graph_edit = (parent_node as GraphEdit) |
|
var top_layer = NodeUtils.get_child_by_class(graph_edit, "GraphEditFilter") |
|
graph_hscroll = NodeUtils.get_child_by_class(top_layer, "HScrollBar") |
|
graph_vscroll = NodeUtils.get_child_by_class(top_layer, "VScrollBar") |
|
|
|
### Helpers |
|
func _convert_from_graph_position(graph_position : Vector2) -> Vector2: |
|
var position = Vector2(0, 0) |
|
var render_size = get_render_size() |
|
|
|
position.x = graph_position.x * render_size.x / _graph_proportions.x |
|
position.y = graph_position.y * render_size.y / _graph_proportions.y |
|
|
|
return position |
|
|
|
func _convert_to_graph_position(position : Vector2) -> Vector2: |
|
var graph_position = Vector2(0, 0) |
|
var render_size = get_render_size() |
|
|
|
graph_position.x = position.x * _graph_proportions.x / render_size.x |
|
graph_position.y = position.y * _graph_proportions.y / render_size.y |
|
|
|
return graph_position |
|
|
|
func get_render_size() -> Vector2: |
|
if (!is_inside_tree()): |
|
return Vector2.ZERO |
|
|
|
return rect_size - 2 * _map_padding |
|
|
|
func get_scroll_offset() -> Vector2: |
|
if (!graph_hscroll || !graph_vscroll): |
|
return Vector2(0, 0) |
|
|
|
return Vector2(graph_hscroll.min_value, graph_vscroll.min_value) |
|
|
|
func get_graph_size() -> Vector2: |
|
if (!graph_hscroll || !graph_vscroll): |
|
return Vector2(1, 1) |
|
|
|
var scroll_offset = get_scroll_offset() |
|
var graph_size = Vector2(graph_hscroll.max_value, graph_vscroll.max_value) - scroll_offset |
|
|
|
if (graph_size.x == 0): |
|
graph_size.x = 1 |
|
if (graph_size.y == 0): |
|
graph_size.y = 1 |
|
|
|
return graph_size |
|
|
|
### Event handlers |
|
func _on_graph_edit_changed() -> void: |
|
_update_map() |
|
|
|
func _on_minimap_button_pressed() -> void: |
|
if (visible): |
|
hide() |
|
minimap_button.modulate = _icon_modulate_color |
|
else: |
|
show() |
|
minimap_button.modulate = _icon_modulate_active_color |