2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* graph_edit.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2015-01-03 20:52:37 +01:00
# include "graph_edit.h"
2017-08-27 21:07:15 +02:00
2020-04-28 15:19:37 +02:00
# include "core/input/input.h"
2020-11-06 20:16:45 +01:00
# include "core/math/math_funcs.h"
2018-09-11 18:13:45 +02:00
# include "core/os/keyboard.h"
2016-01-19 02:10:44 +01:00
# include "scene/gui/box_container.h"
2020-06-19 20:49:04 +02:00
# include "scene/gui/button.h"
2023-07-10 17:26:02 +02:00
# include "scene/gui/graph_edit_arranger.h"
2021-09-28 18:00:16 +02:00
# include "scene/gui/view_panner.h"
2023-07-14 22:35:39 +02:00
# include "scene/resources/style_box_flat.h"
2016-01-23 22:49:26 +01:00
2021-04-17 20:02:04 +02:00
constexpr int MINIMAP_OFFSET = 12 ;
constexpr int MINIMAP_PADDING = 5 ;
2023-07-10 17:26:02 +02:00
constexpr int MIN_DRAG_DISTANCE_FOR_VALID_CONNECTION = 20 ;
constexpr int MAX_CONNECTION_LINE_CURVE_TESSELATION_STAGES = 5 ;
constexpr int GRID_MINOR_STEPS_PER_MAJOR_LINE = 10 ;
constexpr int GRID_MIN_SNAPPING_DISTANCE = 2 ;
constexpr int GRID_MAX_SNAPPING_DISTANCE = 100 ;
constexpr float CONNECTING_TARGET_LINE_COLOR_BRIGHTENING = 0.4 ;
2020-11-06 20:16:45 +01:00
2015-01-03 20:52:37 +01:00
bool GraphEditFilter : : has_point ( const Point2 & p_point ) const {
2015-07-21 03:15:06 +02:00
return ge - > _filter_input ( p_point ) ;
2015-01-03 20:52:37 +01:00
}
GraphEditFilter : : GraphEditFilter ( GraphEdit * p_edit ) {
2015-07-21 03:15:06 +02:00
ge = p_edit ;
2015-01-03 20:52:37 +01:00
}
2020-11-06 20:16:45 +01:00
GraphEditMinimap : : GraphEditMinimap ( GraphEdit * p_edit ) {
ge = p_edit ;
graph_proportions = Vector2 ( 1 , 1 ) ;
graph_padding = Vector2 ( 0 , 0 ) ;
camera_position = Vector2 ( 100 , 50 ) ;
camera_size = Vector2 ( 200 , 200 ) ;
minimap_padding = Vector2 ( MINIMAP_PADDING , MINIMAP_PADDING ) ;
minimap_offset = minimap_padding + _convert_from_graph_position ( graph_padding ) ;
is_pressing = false ;
is_resizing = false ;
}
2023-05-07 16:14:57 +02:00
Control : : CursorShape GraphEditMinimap : : get_cursor_shape ( const Point2 & p_pos ) const {
Ref < Texture2D > resizer = get_theme_icon ( SNAME ( " resizer " ) ) ;
if ( is_resizing | | ( p_pos . x < resizer - > get_width ( ) & & p_pos . y < resizer - > get_height ( ) ) ) {
return CURSOR_FDIAGSIZE ;
}
return Control : : get_cursor_shape ( p_pos ) ;
}
2020-11-06 20:16:45 +01:00
void GraphEditMinimap : : update_minimap ( ) {
Vector2 graph_offset = _get_graph_offset ( ) ;
Vector2 graph_size = _get_graph_size ( ) ;
2023-07-10 17:26:02 +02:00
camera_position = ge - > get_scroll_offset ( ) - graph_offset ;
2020-11-06 20:16:45 +01:00
camera_size = ge - > get_size ( ) ;
Vector2 render_size = _get_render_size ( ) ;
2023-07-10 17:26:02 +02:00
float target_ratio = render_size . width / render_size . height ;
float graph_ratio = graph_size . width / graph_size . height ;
2020-11-06 20:16:45 +01:00
graph_proportions = graph_size ;
graph_padding = Vector2 ( 0 , 0 ) ;
if ( graph_ratio > target_ratio ) {
2023-07-10 17:26:02 +02:00
graph_proportions . width = graph_size . width ;
graph_proportions . height = graph_size . width / target_ratio ;
graph_padding . y = Math : : abs ( graph_size . height - graph_proportions . y ) / 2 ;
2020-11-06 20:16:45 +01:00
} else {
2023-07-10 17:26:02 +02:00
graph_proportions . width = graph_size . height * target_ratio ;
graph_proportions . height = graph_size . height ;
graph_padding . x = Math : : abs ( graph_size . width - graph_proportions . x ) / 2 ;
2020-11-06 20:16:45 +01:00
}
// This centers minimap inside the minimap rectangle.
minimap_offset = minimap_padding + _convert_from_graph_position ( graph_padding ) ;
}
Rect2 GraphEditMinimap : : get_camera_rect ( ) {
Vector2 camera_center = _convert_from_graph_position ( camera_position + camera_size / 2 ) + minimap_offset ;
Vector2 camera_viewport = _convert_from_graph_position ( camera_size ) ;
2022-09-29 11:53:28 +02:00
Vector2 camera_pos = ( camera_center - camera_viewport / 2 ) ;
return Rect2 ( camera_pos , camera_viewport ) ;
2020-11-06 20:16:45 +01:00
}
Vector2 GraphEditMinimap : : _get_render_size ( ) {
if ( ! is_inside_tree ( ) ) {
return Vector2 ( 0 , 0 ) ;
}
return get_size ( ) - 2 * minimap_padding ;
}
Vector2 GraphEditMinimap : : _get_graph_offset ( ) {
2023-07-10 17:26:02 +02:00
return Vector2 ( ge - > h_scrollbar - > get_min ( ) , ge - > v_scrollbar - > get_min ( ) ) ;
2020-11-06 20:16:45 +01:00
}
Vector2 GraphEditMinimap : : _get_graph_size ( ) {
2023-07-10 17:26:02 +02:00
Vector2 graph_size = Vector2 ( ge - > h_scrollbar - > get_max ( ) , ge - > v_scrollbar - > get_max ( ) ) - Vector2 ( ge - > h_scrollbar - > get_min ( ) , ge - > v_scrollbar - > get_min ( ) ) ;
2020-11-06 20:16:45 +01:00
2023-07-10 17:26:02 +02:00
if ( graph_size . width = = 0 ) {
graph_size . width = 1 ;
2020-11-06 20:16:45 +01:00
}
2023-07-10 17:26:02 +02:00
if ( graph_size . height = = 0 ) {
graph_size . height = 1 ;
2020-11-06 20:16:45 +01:00
}
return graph_size ;
}
Vector2 GraphEditMinimap : : _convert_from_graph_position ( const Vector2 & p_position ) {
Vector2 map_position = Vector2 ( 0 , 0 ) ;
Vector2 render_size = _get_render_size ( ) ;
2023-07-10 17:26:02 +02:00
map_position . x = p_position . x * render_size . width / graph_proportions . x ;
map_position . y = p_position . y * render_size . height / graph_proportions . y ;
2020-11-06 20:16:45 +01:00
return map_position ;
}
Vector2 GraphEditMinimap : : _convert_to_graph_position ( const Vector2 & p_position ) {
Vector2 graph_position = Vector2 ( 0 , 0 ) ;
Vector2 render_size = _get_render_size ( ) ;
2023-07-10 17:26:02 +02:00
graph_position . x = p_position . x * graph_proportions . x / render_size . width ;
graph_position . y = p_position . y * graph_proportions . y / render_size . height ;
2020-11-06 20:16:45 +01:00
return graph_position ;
}
2021-08-22 17:37:22 +02:00
void GraphEditMinimap : : gui_input ( const Ref < InputEvent > & p_ev ) {
2021-04-05 08:52:21 +02:00
ERR_FAIL_COND ( p_ev . is_null ( ) ) ;
2021-01-25 15:37:05 +01:00
if ( ! ge - > is_minimap_enabled ( ) ) {
return ;
}
2020-11-06 20:16:45 +01:00
Ref < InputEventMouseButton > mb = p_ev ;
Ref < InputEventMouseMotion > mm = p_ev ;
2021-08-13 23:31:57 +02:00
if ( mb . is_valid ( ) & & mb - > get_button_index ( ) = = MouseButton : : LEFT ) {
2020-11-06 20:16:45 +01:00
if ( mb - > is_pressed ( ) ) {
is_pressing = true ;
2021-07-17 23:22:52 +02:00
Ref < Texture2D > resizer = get_theme_icon ( SNAME ( " resizer " ) ) ;
2020-11-06 20:16:45 +01:00
Rect2 resizer_hitbox = Rect2 ( Point2 ( ) , resizer - > get_size ( ) ) ;
if ( resizer_hitbox . has_point ( mb - > get_position ( ) ) ) {
is_resizing = true ;
} else {
Vector2 click_position = _convert_to_graph_position ( mb - > get_position ( ) - minimap_padding ) - graph_padding ;
_adjust_graph_scroll ( click_position ) ;
}
} else {
is_pressing = false ;
is_resizing = false ;
}
accept_event ( ) ;
} else if ( mm . is_valid ( ) & & is_pressing ) {
if ( is_resizing ) {
2023-07-10 17:26:02 +02:00
// Prevent setting minimap wider than GraphEdit.
2021-03-20 15:04:23 +01:00
Vector2 new_minimap_size ;
2023-07-10 17:26:02 +02:00
new_minimap_size . width = MIN ( get_size ( ) . width - mm - > get_relative ( ) . x , ge - > get_size ( ) . width - 2.0 * minimap_padding . x ) ;
new_minimap_size . height = MIN ( get_size ( ) . height - mm - > get_relative ( ) . y , ge - > get_size ( ) . height - 2.0 * minimap_padding . y ) ;
2021-03-20 15:04:23 +01:00
ge - > set_minimap_size ( new_minimap_size ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2020-11-06 20:16:45 +01:00
} else {
Vector2 click_position = _convert_to_graph_position ( mm - > get_position ( ) - minimap_padding ) - graph_padding ;
_adjust_graph_scroll ( click_position ) ;
}
accept_event ( ) ;
}
}
void GraphEditMinimap : : _adjust_graph_scroll ( const Vector2 & p_offset ) {
Vector2 graph_offset = _get_graph_offset ( ) ;
2023-07-10 17:26:02 +02:00
ge - > set_scroll_offset ( p_offset + graph_offset - camera_size / 2 ) ;
2020-11-06 20:16:45 +01:00
}
2023-05-07 16:14:57 +02:00
Control : : CursorShape GraphEdit : : get_cursor_shape ( const Point2 & p_pos ) const {
if ( moving_selection ) {
return CURSOR_MOVE ;
}
return Control : : get_cursor_shape ( p_pos ) ;
}
2022-09-19 17:43:15 +02:00
PackedStringArray GraphEdit : : get_configuration_warnings ( ) const {
PackedStringArray warnings = Control : : get_configuration_warnings ( ) ;
2022-09-14 18:09:21 +02:00
2023-02-09 02:39:55 +01:00
warnings . push_back ( RTR ( " Please be aware that GraphEdit and GraphNode will undergo extensive refactoring in a future 4.x version involving compatibility-breaking API changes. " ) ) ;
2022-09-14 18:09:21 +02:00
return warnings ;
}
2015-01-03 20:52:37 +01:00
Error GraphEdit : : connect_node ( const StringName & p_from , int p_from_port , const StringName & p_to , int p_to_port ) {
2020-05-14 16:41:43 +02:00
if ( is_node_connected ( p_from , p_from_port , p_to , p_to_port ) ) {
2015-07-21 03:15:06 +02:00
return OK ;
2020-05-14 16:41:43 +02:00
}
2015-07-21 03:15:06 +02:00
Connection c ;
2023-07-10 17:26:02 +02:00
c . from_node = p_from ;
2015-07-21 03:15:06 +02:00
c . from_port = p_from_port ;
2023-07-10 17:26:02 +02:00
c . to_node = p_to ;
2015-07-21 03:15:06 +02:00
c . to_port = p_to_port ;
2018-06-19 03:10:48 +02:00
c . activity = 0 ;
2015-07-21 03:15:06 +02:00
connections . push_back ( c ) ;
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2015-07-21 03:15:06 +02:00
return OK ;
2015-01-03 20:52:37 +01:00
}
bool GraphEdit : : is_node_connected ( const StringName & p_from , int p_from_port , const StringName & p_to , int p_to_port ) {
2021-07-24 15:46:25 +02:00
for ( const Connection & E : connections ) {
2023-07-10 17:26:02 +02:00
if ( E . from_node = = p_from & & E . from_port = = p_from_port & & E . to_node = = p_to & & E . to_port = = p_to_port ) {
2015-07-21 03:15:06 +02:00
return true ;
2020-05-14 16:41:43 +02:00
}
2015-07-21 03:15:06 +02:00
}
2015-01-03 20:52:37 +01:00
2015-07-21 03:15:06 +02:00
return false ;
2015-01-03 20:52:37 +01:00
}
void GraphEdit : : disconnect_node ( const StringName & p_from , int p_from_port , const StringName & p_to , int p_to_port ) {
2021-07-16 05:45:57 +02:00
for ( const List < Connection > : : Element * E = connections . front ( ) ; E ; E = E - > next ( ) ) {
2023-07-10 17:26:02 +02:00
if ( E - > get ( ) . from_node = = p_from & & E - > get ( ) . from_port = = p_from_port & & E - > get ( ) . to_node = = p_to & & E - > get ( ) . to_port = = p_to_port ) {
2015-07-21 03:15:06 +02:00
connections . erase ( E ) ;
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2015-07-21 03:15:06 +02:00
return ;
}
}
2015-01-03 20:52:37 +01:00
}
2015-01-08 04:41:34 +01:00
void GraphEdit : : get_connection_list ( List < Connection > * r_connections ) const {
2015-07-21 03:15:06 +02:00
* r_connections = connections ;
2015-01-03 20:52:37 +01:00
}
2023-07-10 17:26:02 +02:00
void GraphEdit : : set_scroll_offset ( const Vector2 & p_offset ) {
setting_scroll_offset = true ;
h_scrollbar - > set_value ( p_offset . x ) ;
v_scrollbar - > set_value ( p_offset . y ) ;
2016-08-07 00:00:54 +02:00
_update_scroll ( ) ;
2023-07-10 17:26:02 +02:00
setting_scroll_offset = false ;
2016-08-07 00:00:54 +02:00
}
2023-07-10 17:26:02 +02:00
Vector2 GraphEdit : : get_scroll_offset ( ) const {
return Vector2 ( h_scrollbar - > get_value ( ) , v_scrollbar - > get_value ( ) ) ;
2016-01-17 22:26:32 +01:00
}
2015-01-03 20:52:37 +01:00
2016-01-17 22:26:32 +01:00
void GraphEdit : : _scroll_moved ( double ) {
2016-08-31 04:44:14 +02:00
if ( ! awaiting_scroll_offset_update ) {
2023-07-10 17:26:02 +02:00
callable_mp ( this , & GraphEdit : : _update_scroll_offset ) . call_deferred ( ) ;
2016-08-31 04:44:14 +02:00
awaiting_scroll_offset_update = true ;
}
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
2015-01-03 20:52:37 +01:00
}
void GraphEdit : : _update_scroll_offset ( ) {
2016-08-31 04:44:14 +02:00
set_block_minimum_size_adjust ( true ) ;
2015-07-21 03:15:06 +02:00
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2023-08-09 18:31:15 +02:00
GraphElement * graph_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
if ( ! graph_element ) {
2015-07-21 03:15:06 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2015-01-03 20:52:37 +01:00
2023-08-09 18:31:15 +02:00
Point2 pos = graph_element - > get_position_offset ( ) * zoom ;
2023-07-10 17:26:02 +02:00
pos - = Point2 ( h_scrollbar - > get_value ( ) , v_scrollbar - > get_value ( ) ) ;
2023-08-09 18:31:15 +02:00
graph_element - > set_position ( pos ) ;
if ( graph_element - > get_scale ( ) ! = Vector2 ( zoom , zoom ) ) {
graph_element - > set_scale ( Vector2 ( zoom , zoom ) ) ;
2016-08-31 04:44:14 +02:00
}
2015-07-21 03:15:06 +02:00
}
2015-01-03 20:52:37 +01:00
2023-07-10 17:26:02 +02:00
connections_layer - > set_position ( - Point2 ( h_scrollbar - > get_value ( ) , v_scrollbar - > get_value ( ) ) ) ;
2016-08-31 04:44:14 +02:00
set_block_minimum_size_adjust ( false ) ;
awaiting_scroll_offset_update = false ;
2023-01-31 23:11:35 +01:00
2023-07-10 17:26:02 +02:00
// In Godot, signals on value change are avoided by convention.
if ( ! setting_scroll_offset ) {
emit_signal ( SNAME ( " scroll_offset_changed " ) , get_scroll_offset ( ) ) ;
2023-01-31 23:11:35 +01:00
}
2015-01-03 20:52:37 +01:00
}
void GraphEdit : : _update_scroll ( ) {
2020-05-14 16:41:43 +02:00
if ( updating ) {
2015-07-21 03:15:06 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2015-07-21 03:15:06 +02:00
updating = true ;
2016-08-31 04:44:14 +02:00
set_block_minimum_size_adjust ( true ) ;
2023-07-10 17:26:02 +02:00
Rect2 screen_rect ;
2015-07-21 03:15:06 +02:00
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2023-08-09 18:31:15 +02:00
GraphElement * graph_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
if ( ! graph_element ) {
2015-07-21 03:15:06 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2015-01-03 20:52:37 +01:00
2023-07-10 17:26:02 +02:00
Rect2 node_rect ;
2023-08-09 18:31:15 +02:00
node_rect . position = graph_element - > get_position_offset ( ) * zoom ;
node_rect . size = graph_element - > get_size ( ) * zoom ;
2023-07-10 17:26:02 +02:00
screen_rect = screen_rect . merge ( node_rect ) ;
2015-07-21 03:15:06 +02:00
}
2015-01-03 20:52:37 +01:00
2023-07-10 17:26:02 +02:00
screen_rect . position - = get_size ( ) ;
screen_rect . size + = get_size ( ) * 2.0 ;
2015-01-08 04:41:34 +01:00
2023-07-10 17:26:02 +02:00
h_scrollbar - > set_min ( screen_rect . position . x ) ;
h_scrollbar - > set_max ( screen_rect . position . x + screen_rect . size . width ) ;
h_scrollbar - > set_page ( get_size ( ) . x ) ;
if ( h_scrollbar - > get_max ( ) - h_scrollbar - > get_min ( ) < = h_scrollbar - > get_page ( ) ) {
h_scrollbar - > hide ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2023-07-10 17:26:02 +02:00
h_scrollbar - > show ( ) ;
2020-05-14 16:41:43 +02:00
}
2015-01-03 20:52:37 +01:00
2023-07-10 17:26:02 +02:00
v_scrollbar - > set_min ( screen_rect . position . y ) ;
v_scrollbar - > set_max ( screen_rect . position . y + screen_rect . size . height ) ;
v_scrollbar - > set_page ( get_size ( ) . height ) ;
2015-01-03 20:52:37 +01:00
2023-07-10 17:26:02 +02:00
if ( v_scrollbar - > get_max ( ) - v_scrollbar - > get_min ( ) < = v_scrollbar - > get_page ( ) ) {
v_scrollbar - > hide ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2023-07-10 17:26:02 +02:00
v_scrollbar - > show ( ) ;
2020-05-14 16:41:43 +02:00
}
2015-01-03 20:52:37 +01:00
2023-07-10 17:26:02 +02:00
Size2 hmin = h_scrollbar - > get_combined_minimum_size ( ) ;
Size2 vmin = v_scrollbar - > get_combined_minimum_size ( ) ;
2020-01-14 02:49:17 +01:00
// Avoid scrollbar overlapping.
2023-07-10 17:26:02 +02:00
h_scrollbar - > set_anchor_and_offset ( SIDE_RIGHT , ANCHOR_END , v_scrollbar - > is_visible ( ) ? - vmin . width : 0 ) ;
v_scrollbar - > set_anchor_and_offset ( SIDE_BOTTOM , ANCHOR_END , h_scrollbar - > is_visible ( ) ? - hmin . height : 0 ) ;
2020-01-14 02:49:17 +01:00
2016-08-31 04:44:14 +02:00
set_block_minimum_size_adjust ( false ) ;
if ( ! awaiting_scroll_offset_update ) {
2023-07-10 17:26:02 +02:00
callable_mp ( this , & GraphEdit : : _update_scroll_offset ) . call_deferred ( ) ;
2016-08-31 04:44:14 +02:00
awaiting_scroll_offset_update = true ;
}
2015-07-21 03:15:06 +02:00
updating = false ;
2015-01-03 20:52:37 +01:00
}
2023-08-09 18:31:15 +02:00
void GraphEdit : : _graph_element_moved_to_front ( Node * p_node ) {
GraphElement * graph_element = Object : : cast_to < GraphElement > ( p_node ) ;
ERR_FAIL_NULL ( graph_element ) ;
2023-07-11 01:15:57 +02:00
2023-08-09 18:31:15 +02:00
graph_element - > move_to_front ( ) ;
2022-08-24 16:57:17 +02:00
}
2023-08-09 18:31:15 +02:00
void GraphEdit : : _graph_element_selected ( Node * p_node ) {
GraphElement * graph_element = Object : : cast_to < GraphElement > ( p_node ) ;
ERR_FAIL_NULL ( graph_element ) ;
2022-08-24 16:57:17 +02:00
2023-08-09 18:31:15 +02:00
emit_signal ( SNAME ( " node_selected " ) , graph_element ) ;
2022-08-24 16:57:17 +02:00
}
2023-08-09 18:31:15 +02:00
void GraphEdit : : _graph_element_deselected ( Node * p_node ) {
GraphElement * graph_element = Object : : cast_to < GraphElement > ( p_node ) ;
ERR_FAIL_NULL ( graph_element ) ;
2023-07-10 17:26:02 +02:00
2023-08-09 18:31:15 +02:00
emit_signal ( SNAME ( " node_deselected " ) , graph_element ) ;
2023-07-10 17:26:02 +02:00
}
2023-08-09 18:31:15 +02:00
void GraphEdit : : _graph_element_resized ( Vector2 p_new_minsize , Node * p_node ) {
GraphElement * graph_element = Object : : cast_to < GraphElement > ( p_node ) ;
ERR_FAIL_NULL ( graph_element ) ;
2022-08-24 16:57:17 +02:00
2023-08-09 18:31:15 +02:00
graph_element - > set_size ( p_new_minsize ) ;
2015-01-03 20:52:37 +01:00
}
2023-08-09 18:31:15 +02:00
void GraphEdit : : _graph_element_moved ( Node * p_node ) {
GraphElement * graph_element = Object : : cast_to < GraphElement > ( p_node ) ;
ERR_FAIL_NULL ( graph_element ) ;
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2015-01-03 20:52:37 +01:00
}
2023-08-09 18:31:15 +02:00
void GraphEdit : : _graph_node_slot_updated ( int p_index , Node * p_node ) {
GraphNode * graph_node = Object : : cast_to < GraphNode > ( p_node ) ;
2023-07-10 17:26:02 +02:00
ERR_FAIL_NULL ( graph_node ) ;
2023-08-09 18:31:15 +02:00
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2021-02-10 15:18:34 +01:00
}
2015-01-03 20:52:37 +01:00
void GraphEdit : : add_child_notify ( Node * p_child ) {
2016-08-06 03:46:45 +02:00
Control : : add_child_notify ( p_child ) ;
2023-07-10 17:26:02 +02:00
// Keep the top layer always on top!
callable_mp ( ( CanvasItem * ) top_layer , & CanvasItem : : move_to_front ) . call_deferred ( ) ;
2020-11-06 20:16:45 +01:00
2023-08-09 18:31:15 +02:00
GraphElement * graph_element = Object : : cast_to < GraphElement > ( p_child ) ;
if ( graph_element ) {
graph_element - > connect ( " position_offset_changed " , callable_mp ( this , & GraphEdit : : _graph_element_moved ) . bind ( graph_element ) ) ;
graph_element - > connect ( " node_selected " , callable_mp ( this , & GraphEdit : : _graph_element_selected ) . bind ( graph_element ) ) ;
graph_element - > connect ( " node_deselected " , callable_mp ( this , & GraphEdit : : _graph_element_deselected ) . bind ( graph_element ) ) ;
GraphNode * graph_node = Object : : cast_to < GraphNode > ( graph_element ) ;
if ( graph_node ) {
graph_element - > connect ( " slot_updated " , callable_mp ( this , & GraphEdit : : _graph_node_slot_updated ) . bind ( graph_element ) ) ;
}
graph_element - > connect ( " raise_request " , callable_mp ( this , & GraphEdit : : _graph_element_moved_to_front ) . bind ( graph_element ) ) ;
graph_element - > connect ( " resize_request " , callable_mp ( this , & GraphEdit : : _graph_element_resized ) . bind ( graph_element ) ) ;
graph_element - > connect ( " item_rect_changed " , callable_mp ( ( CanvasItem * ) connections_layer , & CanvasItem : : queue_redraw ) ) ;
graph_element - > connect ( " item_rect_changed " , callable_mp ( ( CanvasItem * ) minimap , & GraphEditMinimap : : queue_redraw ) ) ;
graph_element - > set_scale ( Vector2 ( zoom , zoom ) ) ;
_graph_element_moved ( graph_element ) ;
graph_element - > set_mouse_filter ( MOUSE_FILTER_PASS ) ;
2015-07-21 03:15:06 +02:00
}
2015-01-03 20:52:37 +01:00
}
void GraphEdit : : remove_child_notify ( Node * p_child ) {
2016-08-06 03:46:45 +02:00
Control : : remove_child_notify ( p_child ) ;
2020-11-06 20:16:45 +01:00
2021-01-13 01:49:49 +01:00
if ( p_child = = top_layer ) {
top_layer = nullptr ;
minimap = nullptr ;
} else if ( p_child = = connections_layer ) {
connections_layer = nullptr ;
}
if ( top_layer ! = nullptr & & is_inside_tree ( ) ) {
2023-07-10 17:26:02 +02:00
// Keep the top layer always on top!
callable_mp ( ( CanvasItem * ) top_layer , & CanvasItem : : move_to_front ) . call_deferred ( ) ;
2019-03-01 21:24:57 +01:00
}
2020-11-06 20:16:45 +01:00
2023-08-09 18:31:15 +02:00
GraphElement * graph_element = Object : : cast_to < GraphElement > ( p_child ) ;
if ( graph_element ) {
graph_element - > disconnect ( " position_offset_changed " , callable_mp ( this , & GraphEdit : : _graph_element_moved ) ) ;
graph_element - > disconnect ( " node_selected " , callable_mp ( this , & GraphEdit : : _graph_element_selected ) ) ;
graph_element - > disconnect ( " node_deselected " , callable_mp ( this , & GraphEdit : : _graph_element_deselected ) ) ;
GraphNode * graph_node = Object : : cast_to < GraphNode > ( graph_element ) ;
if ( graph_node ) {
graph_element - > disconnect ( " slot_updated " , callable_mp ( this , & GraphEdit : : _graph_node_slot_updated ) ) ;
}
graph_element - > disconnect ( " raise_request " , callable_mp ( this , & GraphEdit : : _graph_element_moved_to_front ) ) ;
graph_element - > disconnect ( " resize_request " , callable_mp ( this , & GraphEdit : : _graph_element_resized ) ) ;
2021-01-13 01:49:49 +01:00
// In case of the whole GraphEdit being destroyed these references can already be freed.
if ( connections_layer ! = nullptr & & connections_layer - > is_inside_tree ( ) ) {
2023-08-09 18:31:15 +02:00
graph_element - > disconnect ( " item_rect_changed " , callable_mp ( ( CanvasItem * ) connections_layer , & CanvasItem : : queue_redraw ) ) ;
2021-01-13 01:49:49 +01:00
}
if ( minimap ! = nullptr & & minimap - > is_inside_tree ( ) ) {
2023-08-09 18:31:15 +02:00
graph_element - > disconnect ( " item_rect_changed " , callable_mp ( ( CanvasItem * ) minimap , & GraphEditMinimap : : queue_redraw ) ) ;
2021-01-13 01:49:49 +01:00
}
2015-07-21 03:15:06 +02:00
}
2015-01-03 20:52:37 +01:00
}
void GraphEdit : : _notification ( int p_what ) {
2022-02-15 18:06:48 +01:00
switch ( p_what ) {
case NOTIFICATION_THEME_CHANGED : {
2022-05-30 15:48:58 +02:00
port_hotzone_inner_extent = get_theme_constant ( " port_hotzone_inner_extent " ) ;
port_hotzone_outer_extent = get_theme_constant ( " port_hotzone_outer_extent " ) ;
2022-02-15 18:06:48 +01:00
2023-07-10 17:26:02 +02:00
zoom_minus_button - > set_icon ( get_theme_icon ( SNAME ( " zoom_out " ) ) ) ;
zoom_reset_button - > set_icon ( get_theme_icon ( SNAME ( " zoom_reset " ) ) ) ;
zoom_plus_button - > set_icon ( get_theme_icon ( SNAME ( " zoom_in " ) ) ) ;
toggle_snapping_button - > set_icon ( get_theme_icon ( SNAME ( " snapping_toggle " ) ) ) ;
show_grid_button - > set_icon ( get_theme_icon ( SNAME ( " grid_toggle " ) ) ) ;
minimap_button - > set_icon ( get_theme_icon ( SNAME ( " minimap_toggle " ) ) ) ;
2022-02-15 18:06:48 +01:00
layout_button - > set_icon ( get_theme_icon ( SNAME ( " layout " ) ) ) ;
zoom_label - > set_custom_minimum_size ( Size2 ( 48 , 0 ) * get_theme_default_base_scale ( ) ) ;
} break ;
case NOTIFICATION_READY : {
2023-07-10 17:26:02 +02:00
Size2 hmin = h_scrollbar - > get_combined_minimum_size ( ) ;
Size2 vmin = v_scrollbar - > get_combined_minimum_size ( ) ;
h_scrollbar - > set_anchor_and_offset ( SIDE_LEFT , ANCHOR_BEGIN , 0 ) ;
h_scrollbar - > set_anchor_and_offset ( SIDE_RIGHT , ANCHOR_END , 0 ) ;
h_scrollbar - > set_anchor_and_offset ( SIDE_TOP , ANCHOR_END , - hmin . height ) ;
h_scrollbar - > set_anchor_and_offset ( SIDE_BOTTOM , ANCHOR_END , 0 ) ;
v_scrollbar - > set_anchor_and_offset ( SIDE_LEFT , ANCHOR_END , - vmin . width ) ;
v_scrollbar - > set_anchor_and_offset ( SIDE_RIGHT , ANCHOR_END , 0 ) ;
v_scrollbar - > set_anchor_and_offset ( SIDE_TOP , ANCHOR_BEGIN , 0 ) ;
v_scrollbar - > set_anchor_and_offset ( SIDE_BOTTOM , ANCHOR_END , 0 ) ;
2022-02-15 18:06:48 +01:00
} break ;
case NOTIFICATION_DRAW : {
2023-07-10 17:26:02 +02:00
// Draw background fill.
draw_style_box ( get_theme_stylebox ( SNAME ( " panel " ) ) , Rect2 ( Point2 ( ) , get_size ( ) ) ) ;
2022-02-15 18:06:48 +01:00
2023-07-10 17:26:02 +02:00
// Draw background grid.
if ( show_grid ) {
Vector2 offset = get_scroll_offset ( ) / zoom ;
2022-02-15 18:06:48 +01:00
Size2 size = get_size ( ) / zoom ;
2023-07-10 17:26:02 +02:00
Point2i from_pos = ( offset / float ( snapping_distance ) ) . floor ( ) ;
Point2i len = ( size / float ( snapping_distance ) ) . floor ( ) + Vector2 ( 1 , 1 ) ;
2022-02-15 18:06:48 +01:00
Color grid_minor = get_theme_color ( SNAME ( " grid_minor " ) ) ;
Color grid_major = get_theme_color ( SNAME ( " grid_major " ) ) ;
2023-07-10 17:26:02 +02:00
for ( int i = from_pos . x ; i < from_pos . x + len . x ; i + + ) {
2022-02-15 18:06:48 +01:00
Color color ;
2023-07-10 17:26:02 +02:00
if ( ABS ( i ) % GRID_MINOR_STEPS_PER_MAJOR_LINE = = 0 ) {
2022-02-15 18:06:48 +01:00
color = grid_major ;
} else {
color = grid_minor ;
}
2023-07-10 17:26:02 +02:00
float base_offset = i * snapping_distance * zoom - offset . x * zoom ;
draw_line ( Vector2 ( base_offset , 0 ) , Vector2 ( base_offset , get_size ( ) . height ) , color ) ;
2020-05-14 16:41:43 +02:00
}
2016-08-04 05:05:35 +02:00
2023-07-10 17:26:02 +02:00
for ( int i = from_pos . y ; i < from_pos . y + len . y ; i + + ) {
2022-02-15 18:06:48 +01:00
Color color ;
2016-08-04 05:05:35 +02:00
2023-07-10 17:26:02 +02:00
if ( ABS ( i ) % GRID_MINOR_STEPS_PER_MAJOR_LINE = = 0 ) {
2022-02-15 18:06:48 +01:00
color = grid_major ;
} else {
color = grid_minor ;
}
2016-08-04 05:05:35 +02:00
2023-07-10 17:26:02 +02:00
float base_offset = i * snapping_distance * zoom - offset . y * zoom ;
draw_line ( Vector2 ( 0 , base_offset ) , Vector2 ( get_size ( ) . width , base_offset ) , color ) ;
2020-05-14 16:41:43 +02:00
}
2016-08-04 05:05:35 +02:00
}
2022-02-15 18:06:48 +01:00
} break ;
2015-01-03 20:52:37 +01:00
2022-02-15 18:06:48 +01:00
case NOTIFICATION_RESIZED : {
_update_scroll ( ) ;
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
2022-02-15 18:06:48 +01:00
} break ;
2015-07-21 03:15:06 +02:00
}
2015-01-03 20:52:37 +01:00
}
bool GraphEdit : : _filter_input ( const Point2 & p_point ) {
2022-05-30 15:48:58 +02:00
Ref < Texture2D > port_icon = get_theme_icon ( SNAME ( " port " ) , SNAME ( " GraphNode " ) ) ;
2015-01-03 20:52:37 +01:00
2015-07-21 03:15:06 +02:00
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-07-10 17:26:02 +02:00
GraphNode * graph_node = Object : : cast_to < GraphNode > ( get_child ( i ) ) ;
if ( ! graph_node | | ! graph_node - > is_visible_in_tree ( ) ) {
2015-07-21 03:15:06 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2015-01-03 20:52:37 +01:00
2023-08-09 18:31:15 +02:00
for ( int j = 0 ; j < graph_node - > get_input_port_count ( ) ; j + + ) {
2022-05-30 15:48:58 +02:00
Vector2i port_size = Vector2i ( port_icon - > get_width ( ) , port_icon - > get_height ( ) ) ;
2023-07-10 17:26:02 +02:00
// Determine slot height.
2023-08-09 18:31:15 +02:00
int slot_index = graph_node - > get_input_port_slot ( j ) ;
2023-07-10 17:26:02 +02:00
Control * child = Object : : cast_to < Control > ( graph_node - > get_child ( slot_index ) ) ;
port_size . height = MAX ( port_size . height , child ? child - > get_size ( ) . y : 0 ) ;
if ( is_in_input_hotzone ( graph_node , j , p_point / zoom , port_size ) ) {
2015-07-21 03:15:06 +02:00
return true ;
2020-05-14 16:41:43 +02:00
}
2015-07-21 03:15:06 +02:00
}
2015-01-03 20:52:37 +01:00
2023-08-09 18:31:15 +02:00
for ( int j = 0 ; j < graph_node - > get_output_port_count ( ) ; j + + ) {
2022-05-30 15:48:58 +02:00
Vector2i port_size = Vector2i ( port_icon - > get_width ( ) , port_icon - > get_height ( ) ) ;
2023-07-10 17:26:02 +02:00
// Determine slot height.
2023-08-09 18:31:15 +02:00
int slot_index = graph_node - > get_output_port_slot ( j ) ;
2023-07-10 17:26:02 +02:00
Control * child = Object : : cast_to < Control > ( graph_node - > get_child ( slot_index ) ) ;
port_size . height = MAX ( port_size . height , child ? child - > get_size ( ) . y : 0 ) ;
if ( is_in_output_hotzone ( graph_node , j , p_point / zoom , port_size ) ) {
2015-07-21 03:15:06 +02:00
return true ;
2016-09-07 00:55:22 +02:00
}
2015-07-21 03:15:06 +02:00
}
}
2015-01-03 20:52:37 +01:00
2015-07-21 03:15:06 +02:00
return false ;
2015-01-03 20:52:37 +01:00
}
2017-05-20 17:38:03 +02:00
void GraphEdit : : _top_layer_input ( const Ref < InputEvent > & p_ev ) {
Ref < InputEventMouseButton > mb = p_ev ;
2021-08-13 23:31:57 +02:00
if ( mb . is_valid ( ) & & mb - > get_button_index ( ) = = MouseButton : : LEFT & & mb - > is_pressed ( ) ) {
2022-05-30 15:48:58 +02:00
Ref < Texture2D > port_icon = get_theme_icon ( SNAME ( " port " ) , SNAME ( " GraphNode " ) ) ;
2021-08-11 16:43:05 +02:00
connecting_valid = false ;
2021-02-26 08:31:39 +01:00
click_pos = mb - > get_position ( ) / zoom ;
2015-07-21 03:15:06 +02:00
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-07-10 17:26:02 +02:00
GraphNode * graph_node = Object : : cast_to < GraphNode > ( get_child ( i ) ) ;
if ( ! graph_node | | ! graph_node - > is_visible_in_tree ( ) ) {
2015-07-21 03:15:06 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2015-01-03 20:52:37 +01:00
2023-08-09 18:31:15 +02:00
for ( int j = 0 ; j < graph_node - > get_output_port_count ( ) ; j + + ) {
Vector2 pos = graph_node - > get_output_port_position ( j ) * zoom + graph_node - > get_position ( ) ;
2022-05-30 15:48:58 +02:00
Vector2i port_size = Vector2i ( port_icon - > get_width ( ) , port_icon - > get_height ( ) ) ;
2023-07-10 17:26:02 +02:00
// Determine slot height.
2023-08-09 18:31:15 +02:00
int slot_index = graph_node - > get_output_port_slot ( j ) ;
2023-07-10 17:26:02 +02:00
Control * child = Object : : cast_to < Control > ( graph_node - > get_child ( slot_index ) ) ;
port_size . height = MAX ( port_size . height , child ? child - > get_size ( ) . y : 0 ) ;
if ( is_in_output_hotzone ( graph_node , j , click_pos , port_size ) ) {
2023-08-09 18:31:15 +02:00
if ( valid_left_disconnect_types . has ( graph_node - > get_output_port_type ( j ) ) ) {
2023-07-10 17:26:02 +02:00
// Check disconnect.
2021-07-24 15:46:25 +02:00
for ( const Connection & E : connections ) {
2023-07-10 17:26:02 +02:00
if ( E . from_node = = graph_node - > get_name ( ) & & E . from_port = = j ) {
Node * to = get_node ( NodePath ( E . to_node ) ) ;
2017-08-24 22:58:51 +02:00
if ( Object : : cast_to < GraphNode > ( to ) ) {
2023-07-10 17:26:02 +02:00
connecting_from = E . to_node ;
2021-07-16 05:45:57 +02:00
connecting_index = E . to_port ;
2016-08-03 00:11:05 +02:00
connecting_out = false ;
2023-08-09 18:31:15 +02:00
connecting_type = Object : : cast_to < GraphNode > ( to ) - > get_input_port_type ( E . to_port ) ;
connecting_color = Object : : cast_to < GraphNode > ( to ) - > get_input_port_color ( E . to_port ) ;
2016-08-03 00:11:05 +02:00
connecting_target = false ;
connecting_to = pos ;
2022-05-04 07:31:53 +02:00
if ( connecting_type > = 0 ) {
just_disconnected = true ;
2023-07-10 17:26:02 +02:00
emit_signal ( SNAME ( " disconnection_request " ) , E . from_node , E . from_port , E . to_node , E . to_port ) ;
2022-08-12 13:06:08 +02:00
to = get_node ( NodePath ( connecting_from ) ) ; // Maybe it was erased.
2022-05-04 07:31:53 +02:00
if ( Object : : cast_to < GraphNode > ( to ) ) {
connecting = true ;
emit_signal ( SNAME ( " connection_drag_started " ) , connecting_from , connecting_index , false ) ;
}
2016-08-03 00:11:05 +02:00
}
return ;
}
}
}
}
2023-07-10 17:26:02 +02:00
connecting_from = graph_node - > get_name ( ) ;
2015-07-21 03:15:06 +02:00
connecting_index = j ;
connecting_out = true ;
2023-08-09 18:31:15 +02:00
connecting_type = graph_node - > get_output_port_type ( j ) ;
connecting_color = graph_node - > get_output_port_color ( j ) ;
2015-07-21 03:15:06 +02:00
connecting_target = false ;
connecting_to = pos ;
2022-05-04 07:31:53 +02:00
if ( connecting_type > = 0 ) {
connecting = true ;
just_disconnected = false ;
emit_signal ( SNAME ( " connection_drag_started " ) , connecting_from , connecting_index , true ) ;
}
2015-07-21 03:15:06 +02:00
return ;
}
}
2015-01-03 20:52:37 +01:00
2023-08-09 18:31:15 +02:00
for ( int j = 0 ; j < graph_node - > get_input_port_count ( ) ; j + + ) {
Vector2 pos = graph_node - > get_input_port_position ( j ) * zoom + graph_node - > get_position ( ) ;
2022-05-30 15:48:58 +02:00
Vector2i port_size = Vector2i ( port_icon - > get_width ( ) , port_icon - > get_height ( ) ) ;
2023-07-10 17:26:02 +02:00
// Determine slot height.
2023-08-09 18:31:15 +02:00
int slot_index = graph_node - > get_input_port_slot ( j ) ;
2023-07-10 17:26:02 +02:00
Control * child = Object : : cast_to < Control > ( graph_node - > get_child ( slot_index ) ) ;
port_size . height = MAX ( port_size . height , child ? child - > get_size ( ) . y : 0 ) ;
if ( is_in_input_hotzone ( graph_node , j , click_pos , port_size ) ) {
2023-08-09 18:31:15 +02:00
if ( right_disconnects | | valid_right_disconnect_types . has ( graph_node - > get_input_port_type ( j ) ) ) {
2022-08-12 13:06:08 +02:00
// Check disconnect.
2021-07-24 15:46:25 +02:00
for ( const Connection & E : connections ) {
2023-07-10 17:26:02 +02:00
if ( E . to_node = = graph_node - > get_name ( ) & & E . to_port = = j ) {
Node * fr = get_node ( NodePath ( E . from_node ) ) ;
2017-08-24 22:58:51 +02:00
if ( Object : : cast_to < GraphNode > ( fr ) ) {
2023-07-10 17:26:02 +02:00
connecting_from = E . from_node ;
2021-07-16 05:45:57 +02:00
connecting_index = E . from_port ;
2015-07-21 03:15:06 +02:00
connecting_out = true ;
2023-08-09 18:31:15 +02:00
connecting_type = Object : : cast_to < GraphNode > ( fr ) - > get_output_port_type ( E . from_port ) ;
connecting_color = Object : : cast_to < GraphNode > ( fr ) - > get_output_port_color ( E . from_port ) ;
2015-07-21 03:15:06 +02:00
connecting_target = false ;
connecting_to = pos ;
2018-10-19 13:31:35 +02:00
just_disconnected = true ;
2015-01-07 05:45:46 +01:00
2022-05-04 07:31:53 +02:00
if ( connecting_type > = 0 ) {
2023-07-10 17:26:02 +02:00
emit_signal ( SNAME ( " disconnection_request " ) , E . from_node , E . from_port , E . to_node , E . to_port ) ;
fr = get_node ( NodePath ( connecting_from ) ) ;
2022-05-04 07:31:53 +02:00
if ( Object : : cast_to < GraphNode > ( fr ) ) {
connecting = true ;
emit_signal ( SNAME ( " connection_drag_started " ) , connecting_from , connecting_index , true ) ;
}
2015-07-21 03:15:06 +02:00
}
return ;
}
}
}
}
2015-01-07 05:45:46 +01:00
2023-07-10 17:26:02 +02:00
connecting_from = graph_node - > get_name ( ) ;
2015-07-21 03:15:06 +02:00
connecting_index = j ;
connecting_out = false ;
2023-08-09 18:31:15 +02:00
connecting_type = graph_node - > get_input_port_type ( j ) ;
connecting_color = graph_node - > get_input_port_color ( j ) ;
2015-07-21 03:15:06 +02:00
connecting_target = false ;
connecting_to = pos ;
2022-05-04 07:31:53 +02:00
if ( connecting_type > = 0 ) {
connecting = true ;
just_disconnected = false ;
emit_signal ( SNAME ( " connection_drag_started " ) , connecting_from , connecting_index , false ) ;
}
2015-07-21 03:15:06 +02:00
return ;
}
}
}
}
2015-01-03 20:52:37 +01:00
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseMotion > mm = p_ev ;
if ( mm . is_valid ( ) & & connecting ) {
2017-06-03 10:54:24 +02:00
connecting_to = mm - > get_position ( ) ;
2015-07-21 03:15:06 +02:00
connecting_target = false ;
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
2023-07-10 17:26:02 +02:00
connecting_valid = just_disconnected | | click_pos . distance_to ( connecting_to / zoom ) > MIN_DRAG_DISTANCE_FOR_VALID_CONNECTION ;
2020-07-04 07:00:17 +02:00
if ( connecting_valid ) {
2021-02-26 08:31:39 +01:00
Vector2 mpos = mm - > get_position ( ) / zoom ;
2020-07-04 07:00:17 +02:00
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2022-05-30 15:48:58 +02:00
Ref < Texture2D > port_icon = get_theme_icon ( SNAME ( " port " ) , SNAME ( " GraphNode " ) ) ;
2023-07-10 17:26:02 +02:00
GraphNode * graph_node = Object : : cast_to < GraphNode > ( get_child ( i ) ) ;
if ( ! graph_node | | ! graph_node - > is_visible_in_tree ( ) ) {
2020-07-04 07:00:17 +02:00
continue ;
}
2015-01-03 20:52:37 +01:00
2020-07-04 07:00:17 +02:00
if ( ! connecting_out ) {
2023-08-09 18:31:15 +02:00
for ( int j = 0 ; j < graph_node - > get_output_port_count ( ) ; j + + ) {
Vector2 pos = graph_node - > get_output_port_position ( j ) * zoom + graph_node - > get_position ( ) ;
2022-05-30 15:48:58 +02:00
Vector2i port_size = Vector2i ( port_icon - > get_width ( ) , port_icon - > get_height ( ) ) ;
2023-07-10 17:26:02 +02:00
// Determine slot height.
2023-08-09 18:31:15 +02:00
int slot_index = graph_node - > get_output_port_slot ( j ) ;
2023-07-10 17:26:02 +02:00
Control * child = Object : : cast_to < Control > ( graph_node - > get_child ( slot_index ) ) ;
port_size . height = MAX ( port_size . height , child ? child - > get_size ( ) . y : 0 ) ;
2023-08-09 18:31:15 +02:00
int type = graph_node - > get_output_port_type ( j ) ;
2023-07-10 17:26:02 +02:00
if ( ( type = = connecting_type | |
2023-09-13 03:46:21 +02:00
valid_connection_types . has ( ConnectionType ( type , connecting_type ) ) ) & &
2023-07-10 17:26:02 +02:00
is_in_output_hotzone ( graph_node , j , mpos , port_size ) ) {
if ( ! is_node_hover_valid ( graph_node - > get_name ( ) , j , connecting_from , connecting_index ) ) {
2021-08-01 03:19:55 +02:00
continue ;
}
2020-07-04 07:00:17 +02:00
connecting_target = true ;
connecting_to = pos ;
2023-07-10 17:26:02 +02:00
connecting_target_to = graph_node - > get_name ( ) ;
2020-07-04 07:00:17 +02:00
connecting_target_index = j ;
return ;
}
2015-07-21 03:15:06 +02:00
}
2020-07-04 07:00:17 +02:00
} else {
2023-08-09 18:31:15 +02:00
for ( int j = 0 ; j < graph_node - > get_input_port_count ( ) ; j + + ) {
Vector2 pos = graph_node - > get_input_port_position ( j ) * zoom + graph_node - > get_position ( ) ;
2022-05-30 15:48:58 +02:00
Vector2i port_size = Vector2i ( port_icon - > get_width ( ) , port_icon - > get_height ( ) ) ;
2023-07-10 17:26:02 +02:00
// Determine slot height.
2023-08-09 18:31:15 +02:00
int slot_index = graph_node - > get_input_port_slot ( j ) ;
2023-07-10 17:26:02 +02:00
Control * child = Object : : cast_to < Control > ( graph_node - > get_child ( slot_index ) ) ;
port_size . height = MAX ( port_size . height , child ? child - > get_size ( ) . y : 0 ) ;
2023-08-09 18:31:15 +02:00
int type = graph_node - > get_input_port_type ( j ) ;
2023-07-10 17:26:02 +02:00
if ( ( type = = connecting_type | | valid_connection_types . has ( ConnectionType ( connecting_type , type ) ) ) & &
is_in_input_hotzone ( graph_node , j , mpos , port_size ) ) {
if ( ! is_node_hover_valid ( connecting_from , connecting_index , graph_node - > get_name ( ) , j ) ) {
2021-08-01 03:19:55 +02:00
continue ;
}
2020-07-04 07:00:17 +02:00
connecting_target = true ;
connecting_to = pos ;
2023-07-10 17:26:02 +02:00
connecting_target_to = graph_node - > get_name ( ) ;
2020-07-04 07:00:17 +02:00
connecting_target_index = j ;
return ;
}
2015-07-21 03:15:06 +02:00
}
}
}
}
}
2015-01-03 20:52:37 +01:00
2021-08-13 23:31:57 +02:00
if ( mb . is_valid ( ) & & mb - > get_button_index ( ) = = MouseButton : : LEFT & & ! mb - > is_pressed ( ) ) {
2020-07-04 07:00:17 +02:00
if ( connecting_valid ) {
if ( connecting & & connecting_target ) {
2022-08-12 13:06:08 +02:00
if ( connecting_out ) {
emit_signal ( SNAME ( " connection_request " ) , connecting_from , connecting_index , connecting_target_to , connecting_target_index ) ;
} else {
emit_signal ( SNAME ( " connection_request " ) , connecting_target_to , connecting_target_index , connecting_from , connecting_index ) ;
2020-07-04 07:00:17 +02:00
}
} else if ( ! just_disconnected ) {
2022-08-12 13:06:08 +02:00
if ( connecting_out ) {
emit_signal ( SNAME ( " connection_to_empty " ) , connecting_from , connecting_index , mb - > get_position ( ) ) ;
2020-07-04 07:00:17 +02:00
} else {
2022-08-12 13:06:08 +02:00
emit_signal ( SNAME ( " connection_from_empty " ) , connecting_from , connecting_index , mb - > get_position ( ) ) ;
2020-07-04 07:00:17 +02:00
}
2019-06-26 20:50:38 +02:00
}
2015-07-21 03:15:06 +02:00
}
2019-06-26 20:50:38 +02:00
2021-07-26 16:31:31 +02:00
if ( connecting ) {
force_connection_drag_end ( ) ;
}
2015-07-21 03:15:06 +02:00
}
2015-01-03 20:52:37 +01:00
}
2022-05-30 15:48:58 +02:00
bool GraphEdit : : _check_clickable_control ( Control * p_control , const Vector2 & mpos , const Vector2 & p_offset ) {
if ( p_control - > is_set_as_top_level ( ) | | ! p_control - > is_visible ( ) | | ! p_control - > is_inside_tree ( ) ) {
2018-08-20 18:38:18 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2018-08-20 18:38:18 +02:00
2022-05-30 15:48:58 +02:00
Rect2 control_rect = p_control - > get_rect ( ) ;
control_rect . position * = zoom ;
2023-06-25 17:13:53 +02:00
control_rect . size * = zoom ;
2022-05-30 15:48:58 +02:00
control_rect . position + = p_offset ;
if ( ! control_rect . has_point ( mpos ) | | p_control - > get_mouse_filter ( ) = = MOUSE_FILTER_IGNORE ) {
// Test children.
2018-08-20 18:38:18 +02:00
for ( int i = 0 ; i < p_control - > get_child_count ( ) ; i + + ) {
2022-05-30 15:48:58 +02:00
Control * child_rect = Object : : cast_to < Control > ( p_control - > get_child ( i ) ) ;
if ( ! child_rect ) {
2018-08-20 18:38:18 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2022-05-30 15:48:58 +02:00
if ( _check_clickable_control ( child_rect , mpos , control_rect . position ) ) {
2018-08-20 18:38:18 +02:00
return true ;
}
}
return false ;
} else {
return true ;
}
}
2023-08-09 18:31:15 +02:00
bool GraphEdit : : is_in_input_hotzone ( GraphNode * p_graph_node , int p_port_idx , const Vector2 & p_mouse_pos , const Vector2i & p_port_size ) {
2021-12-29 18:27:44 +01:00
bool success ;
2023-08-09 18:31:15 +02:00
if ( GDVIRTUAL_CALL ( _is_in_input_hotzone , p_graph_node , p_port_idx , p_mouse_pos , success ) ) {
2021-12-29 18:27:44 +01:00
return success ;
2021-08-11 16:43:05 +02:00
} else {
2023-08-09 18:31:15 +02:00
Vector2 pos = p_graph_node - > get_input_port_position ( p_port_idx ) * zoom + p_graph_node - > get_position ( ) ;
2021-08-23 14:04:18 +02:00
return is_in_port_hotzone ( pos / zoom , p_mouse_pos , p_port_size , true ) ;
}
}
2023-08-09 18:31:15 +02:00
bool GraphEdit : : is_in_output_hotzone ( GraphNode * p_graph_node , int p_port_idx , const Vector2 & p_mouse_pos , const Vector2i & p_port_size ) {
2023-07-10 17:26:02 +02:00
if ( p_graph_node - > is_resizable ( ) ) {
Ref < Texture2D > resizer = p_graph_node - > get_theme_icon ( SNAME ( " resizer " ) ) ;
Rect2 resizer_rect = Rect2 ( p_graph_node - > get_position ( ) / zoom + p_graph_node - > get_size ( ) - resizer - > get_size ( ) , resizer - > get_size ( ) ) ;
2023-02-23 05:40:27 +01:00
if ( resizer_rect . has_point ( p_mouse_pos ) ) {
return false ;
}
}
2021-12-29 18:27:44 +01:00
bool success ;
2023-08-09 18:31:15 +02:00
if ( GDVIRTUAL_CALL ( _is_in_output_hotzone , p_graph_node , p_port_idx , p_mouse_pos , success ) ) {
2021-12-29 18:27:44 +01:00
return success ;
2021-08-23 14:04:18 +02:00
} else {
2023-08-09 18:31:15 +02:00
Vector2 pos = p_graph_node - > get_output_port_position ( p_port_idx ) * zoom + p_graph_node - > get_position ( ) ;
2021-08-23 14:04:18 +02:00
return is_in_port_hotzone ( pos / zoom , p_mouse_pos , p_port_size , false ) ;
}
}
2023-02-23 05:40:27 +01:00
bool GraphEdit : : is_in_port_hotzone ( const Vector2 & p_pos , const Vector2 & p_mouse_pos , const Vector2i & p_port_size , bool p_left ) {
2022-05-30 15:48:58 +02:00
Rect2 hotzone = Rect2 (
2023-02-23 05:40:27 +01:00
p_pos . x - ( p_left ? port_hotzone_outer_extent : port_hotzone_inner_extent ) ,
p_pos . y - p_port_size . height / 2.0 ,
2022-05-30 15:48:58 +02:00
port_hotzone_inner_extent + port_hotzone_outer_extent ,
p_port_size . height ) ;
if ( ! hotzone . has_point ( p_mouse_pos ) ) {
2021-08-23 14:04:18 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2018-08-20 18:38:18 +02:00
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2023-02-12 01:31:58 +01:00
GraphNode * child = Object : : cast_to < GraphNode > ( get_child ( i ) ) ;
2020-05-14 16:41:43 +02:00
if ( ! child ) {
2018-08-20 18:38:18 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-08-20 18:38:18 +02:00
2023-06-25 17:13:53 +02:00
Rect2 child_rect = child - > get_rect ( ) ;
2022-05-30 15:48:58 +02:00
if ( child_rect . has_point ( p_mouse_pos * zoom ) ) {
2018-08-20 18:38:18 +02:00
for ( int j = 0 ; j < child - > get_child_count ( ) ; j + + ) {
Control * subchild = Object : : cast_to < Control > ( child - > get_child ( j ) ) ;
2020-05-14 16:41:43 +02:00
if ( ! subchild ) {
2018-08-20 18:38:18 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-08-20 18:38:18 +02:00
2022-05-30 15:48:58 +02:00
if ( _check_clickable_control ( subchild , p_mouse_pos * zoom , child_rect . position ) ) {
2018-08-20 18:38:18 +02:00
return false ;
}
}
}
}
return true ;
2018-05-13 05:34:35 +02:00
}
2020-07-19 19:11:02 +02:00
PackedVector2Array GraphEdit : : get_connection_line ( const Vector2 & p_from , const Vector2 & p_to ) {
2021-08-22 17:37:22 +02:00
Vector < Vector2 > ret ;
if ( GDVIRTUAL_CALL ( _get_connection_line , p_from , p_to , ret ) ) {
return ret ;
2016-08-23 15:15:47 +02:00
}
2022-05-30 15:38:13 +02:00
float x_diff = ( p_to . x - p_from . x ) ;
float cp_offset = x_diff * lines_curvature ;
if ( x_diff < 0 ) {
cp_offset * = - 1 ;
}
2020-07-19 19:11:02 +02:00
Curve2D curve ;
curve . add_point ( p_from ) ;
2022-05-30 15:38:13 +02:00
curve . set_point_out ( 0 , Vector2 ( cp_offset , 0 ) ) ;
2020-07-19 19:11:02 +02:00
curve . add_point ( p_to ) ;
2022-05-30 15:38:13 +02:00
curve . set_point_in ( 1 , Vector2 ( - cp_offset , 0 ) ) ;
if ( lines_curvature > 0 ) {
2023-07-10 17:26:02 +02:00
return curve . tessellate ( MAX_CONNECTION_LINE_CURVE_TESSELATION_STAGES , 2.0 ) ;
2022-05-30 15:38:13 +02:00
} else {
return curve . tessellate ( 1 ) ;
}
2020-07-19 19:11:02 +02:00
}
2017-07-01 02:30:17 +02:00
2021-08-22 11:08:37 +02:00
void GraphEdit : : _draw_connection_line ( CanvasItem * p_where , const Vector2 & p_from , const Vector2 & p_to , const Color & p_color , const Color & p_to_color , float p_width , float p_zoom ) {
Vector < Vector2 > points = get_connection_line ( p_from / p_zoom , p_to / p_zoom ) ;
Vector < Vector2 > scaled_points ;
2017-07-01 02:30:17 +02:00
Vector < Color > colors ;
2021-08-22 21:09:16 +02:00
float length = ( p_from / p_zoom ) . distance_to ( p_to / p_zoom ) ;
2020-07-19 19:11:02 +02:00
for ( int i = 0 ; i < points . size ( ) ; i + + ) {
2021-08-22 21:09:16 +02:00
float d = ( p_from / p_zoom ) . distance_to ( points [ i ] ) / length ;
2020-07-19 19:11:02 +02:00
colors . push_back ( p_color . lerp ( p_to_color , d ) ) ;
2021-08-22 11:08:37 +02:00
scaled_points . push_back ( points [ i ] * p_zoom ) ;
2020-07-19 19:11:02 +02:00
}
2017-07-01 02:30:17 +02:00
2023-01-27 16:32:23 +01:00
// Thickness below 0.5 doesn't look good on the graph or its minimap.
p_where - > draw_polyline_colors ( scaled_points , colors , MAX ( 0.5 , Math : : floor ( p_width * get_theme_default_base_scale ( ) ) ) , lines_antialiased ) ;
2015-01-03 20:52:37 +01:00
}
2016-08-31 04:44:14 +02:00
void GraphEdit : : _connections_layer_draw ( ) {
2021-07-17 23:22:52 +02:00
Color activity_color = get_theme_color ( SNAME ( " activity " ) ) ;
2023-07-10 17:26:02 +02:00
2022-08-12 13:06:08 +02:00
// Draw connections.
2017-08-27 21:07:15 +02:00
List < List < Connection > : : Element * > to_erase ;
for ( List < Connection > : : Element * E = connections . front ( ) ; E ; E = E - > next ( ) ) {
2022-08-12 13:06:08 +02:00
const Connection & c = E - > get ( ) ;
2016-08-31 04:44:14 +02:00
2023-07-10 17:26:02 +02:00
Node * from = get_node ( NodePath ( c . from_node ) ) ;
GraphNode * gnode_from = Object : : cast_to < GraphNode > ( from ) ;
2016-08-31 04:44:14 +02:00
2023-07-10 17:26:02 +02:00
if ( ! gnode_from ) {
2017-08-27 21:07:15 +02:00
to_erase . push_back ( E ) ;
continue ;
}
2016-08-31 04:44:14 +02:00
2023-07-10 17:26:02 +02:00
Node * to = get_node ( NodePath ( c . to_node ) ) ;
GraphNode * gnode_to = Object : : cast_to < GraphNode > ( to ) ;
2016-08-31 04:44:14 +02:00
2023-07-10 17:26:02 +02:00
if ( ! gnode_to ) {
2017-08-27 21:07:15 +02:00
to_erase . push_back ( E ) ;
continue ;
2016-08-31 04:44:14 +02:00
}
2023-08-09 18:31:15 +02:00
Vector2 frompos = gnode_from - > get_output_port_position ( c . from_port ) * zoom + gnode_from - > get_position_offset ( ) * zoom ;
Color color = gnode_from - > get_output_port_color ( c . from_port ) ;
Vector2 topos = gnode_to - > get_input_port_position ( c . to_port ) * zoom + gnode_to - > get_position_offset ( ) * zoom ;
Color tocolor = gnode_to - > get_input_port_color ( c . to_port ) ;
2018-06-19 03:10:48 +02:00
2022-08-12 13:06:08 +02:00
if ( c . activity > 0 ) {
color = color . lerp ( activity_color , c . activity ) ;
tocolor = tocolor . lerp ( activity_color , c . activity ) ;
2018-06-19 03:10:48 +02:00
}
2021-08-22 11:08:37 +02:00
_draw_connection_line ( connections_layer , frompos , topos , color , tocolor , lines_thickness , zoom ) ;
2017-08-27 21:07:15 +02:00
}
2022-08-12 13:06:08 +02:00
for ( List < Connection > : : Element * & E : to_erase ) {
connections . erase ( E ) ;
2016-08-31 04:44:14 +02:00
}
}
2015-01-03 20:52:37 +01:00
void GraphEdit : : _top_layer_draw ( ) {
2015-07-21 03:15:06 +02:00
_update_scroll ( ) ;
if ( connecting ) {
2023-07-10 17:26:02 +02:00
Node * node_from = get_node_or_null ( NodePath ( connecting_from ) ) ;
ERR_FAIL_NULL ( node_from ) ;
GraphNode * graph_node_from = Object : : cast_to < GraphNode > ( node_from ) ;
ERR_FAIL_NULL ( graph_node_from ) ;
2015-07-21 03:15:06 +02:00
Vector2 pos ;
2020-05-14 16:41:43 +02:00
if ( connecting_out ) {
2023-08-09 18:31:15 +02:00
pos = graph_node_from - > get_output_port_position ( connecting_index ) * zoom ;
2020-05-14 16:41:43 +02:00
} else {
2023-08-09 18:31:15 +02:00
pos = graph_node_from - > get_input_port_position ( connecting_index ) * zoom ;
2020-05-14 16:41:43 +02:00
}
2023-07-10 17:26:02 +02:00
pos + = graph_node_from - > get_position ( ) ;
2015-07-21 03:15:06 +02:00
2023-07-10 17:26:02 +02:00
Vector2 to_pos = connecting_to ;
Color line_color = connecting_color ;
2015-07-21 03:15:06 +02:00
2023-07-10 17:26:02 +02:00
// Draw the line to the mouse cursor brighter when it's over a valid target port.
2015-07-21 03:15:06 +02:00
if ( connecting_target ) {
2023-07-10 17:26:02 +02:00
line_color . r + = CONNECTING_TARGET_LINE_COLOR_BRIGHTENING ;
line_color . g + = CONNECTING_TARGET_LINE_COLOR_BRIGHTENING ;
line_color . b + = CONNECTING_TARGET_LINE_COLOR_BRIGHTENING ;
2015-07-21 03:15:06 +02:00
}
2016-08-25 22:45:20 +02:00
if ( ! connecting_out ) {
2023-07-10 17:26:02 +02:00
SWAP ( pos , to_pos ) ;
2016-08-25 22:45:20 +02:00
}
2023-07-10 17:26:02 +02:00
_draw_connection_line ( top_layer , pos , to_pos , line_color , line_color , lines_thickness , zoom ) ;
2015-07-21 03:15:06 +02:00
}
2019-08-15 22:17:08 +02:00
if ( box_selecting ) {
2021-07-17 23:22:52 +02:00
top_layer - > draw_rect ( box_selecting_rect , get_theme_color ( SNAME ( " selection_fill " ) ) ) ;
top_layer - > draw_rect ( box_selecting_rect , get_theme_color ( SNAME ( " selection_stroke " ) ) , false ) ;
2019-08-15 22:17:08 +02:00
}
2015-01-03 20:52:37 +01:00
}
2020-11-06 20:16:45 +01:00
void GraphEdit : : _minimap_draw ( ) {
if ( ! is_minimap_enabled ( ) ) {
return ;
}
minimap - > update_minimap ( ) ;
// Draw the minimap background.
Rect2 minimap_rect = Rect2 ( Point2 ( ) , minimap - > get_size ( ) ) ;
2023-07-10 17:26:02 +02:00
minimap - > draw_style_box ( minimap - > get_theme_stylebox ( SNAME ( " panel " ) ) , minimap_rect ) ;
2020-11-06 20:16:45 +01:00
Vector2 graph_offset = minimap - > _get_graph_offset ( ) ;
Vector2 minimap_offset = minimap - > minimap_offset ;
2023-07-10 17:26:02 +02:00
// Draw graph nodes.
2020-11-06 20:16:45 +01:00
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-07-10 17:26:02 +02:00
GraphNode * graph_node = Object : : cast_to < GraphNode > ( get_child ( i ) ) ;
if ( ! graph_node | | ! graph_node - > is_visible ( ) ) {
2020-11-06 20:16:45 +01:00
continue ;
}
2023-07-10 17:26:02 +02:00
Vector2 node_position = minimap - > _convert_from_graph_position ( graph_node - > get_position_offset ( ) * zoom - graph_offset ) + minimap_offset ;
Vector2 node_size = minimap - > _convert_from_graph_position ( graph_node - > get_size ( ) * zoom ) ;
2020-11-06 20:16:45 +01:00
Rect2 node_rect = Rect2 ( node_position , node_size ) ;
2021-07-17 23:22:52 +02:00
Ref < StyleBoxFlat > sb_minimap = minimap - > get_theme_stylebox ( SNAME ( " node " ) ) - > duplicate ( ) ;
2020-11-06 20:16:45 +01:00
// Override default values with colors provided by the GraphNode's stylebox, if possible.
2023-08-09 18:31:15 +02:00
Ref < StyleBoxFlat > sb_frame = graph_node - > get_theme_stylebox ( graph_node - > is_selected ( ) ? " panel_selected " : " panel " ) ;
2023-07-10 17:26:02 +02:00
if ( sb_frame . is_valid ( ) ) {
Color node_color = sb_frame - > get_bg_color ( ) ;
2020-11-06 20:16:45 +01:00
sb_minimap - > set_bg_color ( node_color ) ;
}
minimap - > draw_style_box ( sb_minimap , node_rect ) ;
}
// Draw node connections.
2021-07-17 23:22:52 +02:00
Color activity_color = get_theme_color ( SNAME ( " activity " ) ) ;
2021-07-24 15:46:25 +02:00
for ( const Connection & E : connections ) {
2023-07-10 17:26:02 +02:00
Node * from = get_node ( NodePath ( E . from_node ) ) ;
GraphNode * graph_node_from = Object : : cast_to < GraphNode > ( from ) ;
if ( ! graph_node_from ) {
2020-11-06 20:16:45 +01:00
continue ;
}
2023-07-10 17:26:02 +02:00
Node * node_to = get_node ( NodePath ( E . to_node ) ) ;
GraphNode * graph_node_to = Object : : cast_to < GraphNode > ( node_to ) ;
if ( ! graph_node_to ) {
2020-11-06 20:16:45 +01:00
continue ;
}
2023-08-09 18:31:15 +02:00
Vector2 from_port_position = graph_node_from - > get_position_offset ( ) * zoom + graph_node_from - > get_output_port_position ( E . from_port ) * zoom ;
2022-09-09 15:29:51 +02:00
Vector2 from_position = minimap - > _convert_from_graph_position ( from_port_position - graph_offset ) + minimap_offset ;
2023-08-09 18:31:15 +02:00
Color from_color = graph_node_from - > get_output_port_color ( E . from_port ) ;
Vector2 to_port_position = graph_node_to - > get_position_offset ( ) * zoom + graph_node_to - > get_input_port_position ( E . to_port ) * zoom ;
2022-09-09 15:29:51 +02:00
Vector2 to_position = minimap - > _convert_from_graph_position ( to_port_position - graph_offset ) + minimap_offset ;
2023-08-09 18:31:15 +02:00
Color to_color = graph_node_to - > get_input_port_color ( E . to_port ) ;
2020-11-06 20:16:45 +01:00
2021-07-16 05:45:57 +02:00
if ( E . activity > 0 ) {
from_color = from_color . lerp ( activity_color , E . activity ) ;
to_color = to_color . lerp ( activity_color , E . activity ) ;
2020-11-06 20:16:45 +01:00
}
2023-01-27 16:32:23 +01:00
_draw_connection_line ( minimap , from_position , to_position , from_color , to_color , 0.5 , minimap - > _convert_from_graph_position ( Vector2 ( zoom , zoom ) ) . length ( ) ) ;
2020-11-06 20:16:45 +01:00
}
// Draw the "camera" viewport.
Rect2 camera_rect = minimap - > get_camera_rect ( ) ;
2021-07-17 23:22:52 +02:00
minimap - > draw_style_box ( minimap - > get_theme_stylebox ( SNAME ( " camera " ) ) , camera_rect ) ;
2020-11-06 20:16:45 +01:00
// Draw the resizer control.
2021-07-17 23:22:52 +02:00
Ref < Texture2D > resizer = minimap - > get_theme_icon ( SNAME ( " resizer " ) ) ;
Color resizer_color = minimap - > get_theme_color ( SNAME ( " resizer_color " ) ) ;
2020-11-06 20:16:45 +01:00
minimap - > draw_texture ( resizer , Point2 ( ) , resizer_color ) ;
}
2016-08-03 00:11:05 +02:00
void GraphEdit : : set_selected ( Node * p_child ) {
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-07-10 17:26:02 +02:00
GraphNode * graph_node = Object : : cast_to < GraphNode > ( get_child ( i ) ) ;
if ( ! graph_node ) {
2016-08-03 00:11:05 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-08-03 00:11:05 +02:00
2023-07-10 17:26:02 +02:00
graph_node - > set_selected ( graph_node = = p_child ) ;
2016-08-03 00:11:05 +02:00
}
}
2021-08-22 17:37:22 +02:00
void GraphEdit : : gui_input ( const Ref < InputEvent > & p_ev ) {
2021-04-05 08:52:21 +02:00
ERR_FAIL_COND ( p_ev . is_null ( ) ) ;
2022-01-19 19:59:12 +01:00
if ( panner - > gui_input ( p_ev , warped_panning ? get_global_rect ( ) : Rect2 ( ) ) ) {
return ;
}
2021-04-05 08:52:21 +02:00
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseMotion > mm = p_ev ;
2015-07-19 06:48:46 +02:00
2017-05-20 17:38:03 +02:00
if ( mm . is_valid ( ) & & dragging ) {
2020-10-20 03:25:07 +02:00
if ( ! moving_selection ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " begin_node_move " ) ) ;
2020-10-20 03:25:07 +02:00
moving_selection = true ;
}
2015-07-21 03:15:06 +02:00
just_selected = true ;
2020-05-04 20:36:52 +02:00
drag_accum + = mm - > get_relative ( ) ;
2015-07-21 03:15:06 +02:00
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-08-09 18:31:15 +02:00
GraphElement * graph_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
if ( graph_element & & graph_element - > is_selected ( ) & & graph_element - > is_draggable ( ) ) {
Vector2 pos = ( graph_element - > get_drag_from ( ) * zoom + drag_accum ) / zoom ;
2019-11-15 19:34:44 +01:00
// Snapping can be toggled temporarily by holding down Ctrl.
// This is done here as to not toggle the grid when holding down Ctrl.
2023-07-10 17:26:02 +02:00
if ( snapping_enabled ^ Input : : get_singleton ( ) - > is_key_pressed ( Key : : CTRL ) ) {
pos = pos . snapped ( Vector2 ( snapping_distance , snapping_distance ) ) ;
2016-08-04 05:05:35 +02:00
}
2023-08-09 18:31:15 +02:00
graph_element - > set_position_offset ( pos ) ;
2016-08-04 05:05:35 +02:00
}
2015-07-21 03:15:06 +02:00
}
}
2015-07-19 06:48:46 +02:00
2017-05-20 17:38:03 +02:00
if ( mm . is_valid ( ) & & box_selecting ) {
2020-05-04 20:36:52 +02:00
box_selecting_to = mm - > get_position ( ) ;
2015-07-25 02:59:48 +02:00
2021-09-29 05:51:34 +02:00
box_selecting_rect = Rect2 ( box_selecting_from . min ( box_selecting_to ) , ( box_selecting_from - box_selecting_to ) . abs ( ) ) ;
2015-07-25 02:59:48 +02:00
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-08-09 18:31:15 +02:00
GraphElement * graph_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
if ( ! graph_element ) {
2015-07-25 02:59:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2015-07-25 02:59:48 +02:00
2023-08-09 18:31:15 +02:00
Rect2 r = graph_element - > get_rect ( ) ;
2016-01-19 00:32:37 +01:00
bool in_box = r . intersects ( box_selecting_rect ) ;
2015-07-25 02:59:48 +02:00
2020-05-14 16:41:43 +02:00
if ( in_box ) {
2023-08-09 18:31:15 +02:00
graph_element - > set_selected ( box_selection_mode_additive ) ;
2020-05-14 16:41:43 +02:00
} else {
2023-08-09 18:31:15 +02:00
graph_element - > set_selected ( prev_selected . find ( graph_element ) ! = nullptr ) ;
2020-05-14 16:41:43 +02:00
}
2015-07-25 02:59:48 +02:00
}
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
2015-07-25 02:59:48 +02:00
}
2023-02-23 05:40:27 +01:00
Ref < InputEventMouseButton > mb = p_ev ;
if ( mb . is_valid ( ) ) {
if ( mb - > get_button_index ( ) = = MouseButton : : RIGHT & & mb - > is_pressed ( ) ) {
2015-07-25 02:59:48 +02:00
if ( box_selecting ) {
box_selecting = false ;
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-08-09 18:31:15 +02:00
GraphElement * graph_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
if ( ! graph_element ) {
2015-07-25 02:59:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2015-07-25 02:59:48 +02:00
2023-08-09 18:31:15 +02:00
graph_element - > set_selected ( prev_selected . find ( graph_element ) ! = nullptr ) ;
2015-07-25 02:59:48 +02:00
}
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
2015-07-25 02:59:48 +02:00
} else {
2016-01-19 00:32:37 +01:00
if ( connecting ) {
2021-07-26 16:31:31 +02:00
force_connection_drag_end ( ) ;
2016-01-19 00:32:37 +01:00
} else {
2023-02-23 05:40:27 +01:00
emit_signal ( SNAME ( " popup_request " ) , mb - > get_position ( ) ) ;
2016-01-19 00:32:37 +01:00
}
2015-07-25 02:59:48 +02:00
}
2015-07-21 03:15:06 +02:00
}
2015-07-19 06:48:46 +02:00
2023-02-23 05:40:27 +01:00
if ( mb - > get_button_index ( ) = = MouseButton : : LEFT & & ! mb - > is_pressed ( ) & & dragging ) {
2021-08-13 23:31:57 +02:00
if ( ! just_selected & & drag_accum = = Vector2 ( ) & & Input : : get_singleton ( ) - > is_key_pressed ( Key : : CTRL ) ) {
2023-07-10 17:26:02 +02:00
// Deselect current node.
2015-07-21 03:15:06 +02:00
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-08-09 18:31:15 +02:00
GraphElement * graph_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
2015-07-19 06:48:46 +02:00
2023-08-09 18:31:15 +02:00
if ( graph_element ) {
Rect2 r = graph_element - > get_rect ( ) ;
2023-02-23 05:40:27 +01:00
if ( r . has_point ( mb - > get_position ( ) ) ) {
2023-08-09 18:31:15 +02:00
graph_element - > set_selected ( false ) ;
2020-05-14 16:41:43 +02:00
}
2016-01-19 00:32:37 +01:00
}
2015-07-21 03:15:06 +02:00
}
}
2015-07-19 06:48:46 +02:00
2015-07-21 03:15:06 +02:00
if ( drag_accum ! = Vector2 ( ) ) {
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-08-09 18:31:15 +02:00
GraphElement * graph_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
if ( graph_element & & graph_element - > is_selected ( ) ) {
graph_element - > set_drag ( false ) ;
2020-05-14 16:41:43 +02:00
}
2015-07-21 03:15:06 +02:00
}
2020-10-20 03:25:07 +02:00
}
2015-07-19 06:48:46 +02:00
2020-10-20 03:25:07 +02:00
if ( moving_selection ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " end_node_move " ) ) ;
2020-10-20 03:25:07 +02:00
moving_selection = false ;
2015-07-21 03:15:06 +02:00
}
2015-07-19 06:48:46 +02:00
2015-07-21 03:15:06 +02:00
dragging = false ;
2015-07-19 06:48:46 +02:00
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2015-07-21 03:15:06 +02:00
}
2015-07-19 06:48:46 +02:00
2023-07-10 17:26:02 +02:00
// Node selection logic.
2023-02-23 05:40:27 +01:00
if ( mb - > get_button_index ( ) = = MouseButton : : LEFT & & mb - > is_pressed ( ) ) {
2023-08-09 18:31:15 +02:00
GraphElement * graph_element = nullptr ;
2017-08-08 15:57:33 +02:00
2022-08-24 16:57:17 +02:00
// Find node which was clicked on.
2015-07-21 03:15:06 +02:00
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-08-09 18:31:15 +02:00
GraphElement * selected_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
2015-07-19 06:48:46 +02:00
2023-08-09 18:31:15 +02:00
if ( ! selected_element ) {
2022-08-24 16:57:17 +02:00
continue ;
}
2016-08-25 22:45:20 +02:00
2023-08-09 18:31:15 +02:00
if ( selected_element - > is_resizing ( ) ) {
2022-08-24 16:57:17 +02:00
continue ;
}
2023-08-09 18:31:15 +02:00
if ( selected_element - > has_point ( ( mb - > get_position ( ) - selected_element - > get_position ( ) ) / zoom ) ) {
graph_element = selected_element ;
2022-08-24 16:57:17 +02:00
break ;
2016-01-19 00:32:37 +01:00
}
2015-07-21 03:15:06 +02:00
}
2015-07-19 06:48:46 +02:00
2023-08-09 18:31:15 +02:00
if ( graph_element ) {
2023-02-23 05:40:27 +01:00
if ( _filter_input ( mb - > get_position ( ) ) ) {
2015-07-21 03:15:06 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2015-07-19 06:48:46 +02:00
2022-08-24 16:57:17 +02:00
// Left-clicked on a node, select it.
2015-07-21 03:15:06 +02:00
dragging = true ;
drag_accum = Vector2 ( ) ;
2023-08-09 18:31:15 +02:00
just_selected = ! graph_element - > is_selected ( ) ;
if ( ! graph_element - > is_selected ( ) & & ! Input : : get_singleton ( ) - > is_key_pressed ( Key : : CTRL ) ) {
2015-07-21 03:15:06 +02:00
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2023-08-09 18:31:15 +02:00
GraphElement * child_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
if ( ! child_element ) {
2022-08-24 16:57:17 +02:00
continue ;
2020-04-10 22:00:50 +02:00
}
2022-08-24 16:57:17 +02:00
2023-08-09 18:31:15 +02:00
child_element - > set_selected ( child_element = = graph_element ) ;
2015-07-21 03:15:06 +02:00
}
}
2015-07-19 06:48:46 +02:00
2023-08-09 18:31:15 +02:00
graph_element - > set_selected ( true ) ;
2015-07-21 03:15:06 +02:00
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2023-08-09 18:31:15 +02:00
GraphElement * child_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
if ( ! child_element ) {
2015-07-21 03:15:06 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2023-08-09 18:31:15 +02:00
if ( child_element - > is_selected ( ) ) {
child_element - > set_drag ( true ) ;
2020-05-14 16:41:43 +02:00
}
2015-07-21 03:15:06 +02:00
}
2015-07-19 06:48:46 +02:00
2015-07-21 03:15:06 +02:00
} else {
2023-02-23 05:40:27 +01:00
if ( _filter_input ( mb - > get_position ( ) ) ) {
2016-01-19 00:32:37 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2022-01-19 19:59:12 +01:00
if ( panner - > is_panning ( ) ) {
2016-02-08 20:28:12 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2016-01-19 00:32:37 +01:00
2022-08-24 16:57:17 +02:00
// Left-clicked on empty space, start box select.
2015-07-25 02:59:48 +02:00
box_selecting = true ;
2023-02-23 05:40:27 +01:00
box_selecting_from = mb - > get_position ( ) ;
if ( mb - > is_ctrl_pressed ( ) ) {
2020-04-10 22:00:50 +02:00
box_selection_mode_additive = true ;
2023-07-10 17:26:02 +02:00
prev_selected . clear ( ) ;
2015-07-25 02:59:48 +02:00
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-08-09 18:31:15 +02:00
GraphElement * child_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
if ( ! child_element | | ! child_element - > is_selected ( ) ) {
2015-07-25 02:59:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2015-07-25 02:59:48 +02:00
2023-08-09 18:31:15 +02:00
prev_selected . push_back ( child_element ) ;
2015-07-25 02:59:48 +02:00
}
2023-02-23 05:40:27 +01:00
} else if ( mb - > is_shift_pressed ( ) ) {
2020-04-10 22:00:50 +02:00
box_selection_mode_additive = false ;
2023-07-10 17:26:02 +02:00
prev_selected . clear ( ) ;
2015-07-25 02:59:48 +02:00
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-08-09 18:31:15 +02:00
GraphElement * child_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
if ( ! child_element | | ! child_element - > is_selected ( ) ) {
2015-07-25 02:59:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2015-07-19 06:48:46 +02:00
2023-08-09 18:31:15 +02:00
prev_selected . push_back ( child_element ) ;
2015-07-25 02:59:48 +02:00
}
} else {
2020-04-10 22:00:50 +02:00
box_selection_mode_additive = true ;
2023-07-10 17:26:02 +02:00
prev_selected . clear ( ) ;
2015-07-25 02:59:48 +02:00
for ( int i = get_child_count ( ) - 1 ; i > = 0 ; i - - ) {
2023-08-09 18:31:15 +02:00
GraphElement * child_element = Object : : cast_to < GraphElement > ( get_child ( i ) ) ;
if ( ! child_element ) {
2015-07-25 02:59:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2022-08-24 16:57:17 +02:00
2023-08-09 18:31:15 +02:00
child_element - > set_selected ( false ) ;
2015-07-25 02:59:48 +02:00
}
2015-07-21 03:15:06 +02:00
}
}
}
2015-07-25 02:59:48 +02:00
2023-02-23 05:40:27 +01:00
if ( mb - > get_button_index ( ) = = MouseButton : : LEFT & & ! mb - > is_pressed ( ) & & box_selecting ) {
2022-08-24 16:57:17 +02:00
// Box selection ended. Nodes were selected during mouse movement.
2015-07-25 02:59:48 +02:00
box_selecting = false ;
2021-03-25 05:21:34 +01:00
box_selecting_rect = Rect2 ( ) ;
2023-07-10 17:26:02 +02:00
prev_selected . clear ( ) ;
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
2015-07-25 02:59:48 +02:00
}
2015-07-21 03:15:06 +02:00
}
2015-07-26 02:16:07 +02:00
2020-12-07 12:32:00 +01:00
if ( p_ev - > is_pressed ( ) ) {
2022-09-24 10:01:02 +02:00
if ( p_ev - > is_action ( " ui_graph_duplicate " , true ) ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " duplicate_nodes_request " ) ) ;
2019-07-12 19:36:33 +02:00
accept_event ( ) ;
2022-09-24 10:01:02 +02:00
} else if ( p_ev - > is_action ( " ui_copy " , true ) ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " copy_nodes_request " ) ) ;
2019-07-12 19:36:33 +02:00
accept_event ( ) ;
2022-09-24 10:01:02 +02:00
} else if ( p_ev - > is_action ( " ui_paste " , true ) ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " paste_nodes_request " ) ) ;
2019-07-12 19:36:33 +02:00
accept_event ( ) ;
2022-09-24 10:01:02 +02:00
} else if ( p_ev - > is_action ( " ui_graph_delete " , true ) ) {
2022-05-17 12:13:35 +02:00
TypedArray < StringName > nodes ;
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
GraphNode * gn = Object : : cast_to < GraphNode > ( get_child ( i ) ) ;
if ( ! gn ) {
continue ;
}
2023-07-10 17:26:02 +02:00
if ( gn - > is_selected ( ) ) {
2022-05-17 12:13:35 +02:00
nodes . push_back ( gn - > get_name ( ) ) ;
}
}
2023-08-09 18:31:15 +02:00
emit_signal ( SNAME ( " close_nodes_request " ) , nodes ) ;
2019-07-12 19:36:33 +02:00
accept_event ( ) ;
}
2015-07-27 02:57:27 +02:00
}
2021-09-28 18:00:16 +02:00
}
2023-01-19 17:50:51 +01:00
void GraphEdit : : _pan_callback ( Vector2 p_scroll_vec , Ref < InputEvent > p_event ) {
2023-07-10 17:26:02 +02:00
h_scrollbar - > set_value ( h_scrollbar - > get_value ( ) - p_scroll_vec . x ) ;
v_scrollbar - > set_value ( v_scrollbar - > get_value ( ) - p_scroll_vec . y ) ;
2021-09-28 18:00:16 +02:00
}
2023-01-19 17:50:51 +01:00
void GraphEdit : : _zoom_callback ( float p_zoom_factor , Vector2 p_origin , Ref < InputEvent > p_event ) {
set_zoom_custom ( zoom * p_zoom_factor , p_origin ) ;
2021-09-28 18:00:16 +02:00
}
2018-06-19 03:10:48 +02:00
void GraphEdit : : set_connection_activity ( const StringName & p_from , int p_from_port , const StringName & p_to , int p_to_port , float p_activity ) {
2021-07-16 05:45:57 +02:00
for ( Connection & E : connections ) {
2023-07-10 17:26:02 +02:00
if ( E . from_node = = p_from & & E . from_port = = p_from_port & & E . to_node = = p_to & & E . to_port = = p_to_port ) {
2021-07-16 05:45:57 +02:00
if ( Math : : is_equal_approx ( E . activity , p_activity ) ) {
2023-07-10 17:26:02 +02:00
// Update only if changed.
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2018-06-19 03:10:48 +02:00
}
2021-07-16 05:45:57 +02:00
E . activity = p_activity ;
2018-06-19 03:10:48 +02:00
return ;
}
}
}
2015-01-03 20:52:37 +01:00
void GraphEdit : : clear_connections ( ) {
2015-07-21 03:15:06 +02:00
connections . clear ( ) ;
2022-08-13 23:21:24 +02:00
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2015-01-03 20:52:37 +01:00
}
2021-07-26 16:31:31 +02:00
void GraphEdit : : force_connection_drag_end ( ) {
ERR_FAIL_COND_MSG ( ! connecting , " Drag end requested without active drag! " ) ;
connecting = false ;
connecting_valid = false ;
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2021-07-26 16:31:31 +02:00
emit_signal ( SNAME ( " connection_drag_ended " ) ) ;
}
2021-08-01 03:19:55 +02:00
bool GraphEdit : : is_node_hover_valid ( const StringName & p_from , const int p_from_port , const StringName & p_to , const int p_to_port ) {
2022-10-18 18:47:44 +02:00
bool valid = true ;
GDVIRTUAL_CALL ( _is_node_hover_valid , p_from , p_from_port , p_to , p_to_port , valid ) ;
return valid ;
2021-08-01 03:19:55 +02:00
}
2021-09-28 18:00:16 +02:00
void GraphEdit : : set_panning_scheme ( PanningScheme p_scheme ) {
panning_scheme = p_scheme ;
panner - > set_control_scheme ( ( ViewPanner : : ControlScheme ) p_scheme ) ;
}
GraphEdit : : PanningScheme GraphEdit : : get_panning_scheme ( ) const {
return panning_scheme ;
}
2016-01-19 00:32:37 +01:00
void GraphEdit : : set_zoom ( float p_zoom ) {
2017-11-01 21:49:39 +01:00
set_zoom_custom ( p_zoom , get_size ( ) / 2 ) ;
}
void GraphEdit : : set_zoom_custom ( float p_zoom , const Vector2 & p_center ) {
2021-06-16 15:14:25 +02:00
p_zoom = CLAMP ( p_zoom , zoom_min , zoom_max ) ;
2020-05-14 16:41:43 +02:00
if ( zoom = = p_zoom ) {
2016-01-19 00:32:37 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2016-01-19 00:32:37 +01:00
2023-07-10 17:26:02 +02:00
Vector2 scrollbar_offset = ( Vector2 ( h_scrollbar - > get_value ( ) , v_scrollbar - > get_value ( ) ) + p_center ) / zoom ;
2016-02-08 20:28:12 +01:00
2016-01-19 00:32:37 +01:00
zoom = p_zoom ;
2022-08-13 23:21:24 +02:00
top_layer - > queue_redraw ( ) ;
2016-02-08 20:28:12 +01:00
2023-07-10 17:26:02 +02:00
zoom_minus_button - > set_disabled ( zoom = = zoom_min ) ;
zoom_plus_button - > set_disabled ( zoom = = zoom_max ) ;
2021-06-16 15:14:25 +02:00
2016-02-08 20:28:12 +01:00
_update_scroll ( ) ;
2022-08-13 23:21:24 +02:00
minimap - > queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2016-02-08 20:28:12 +01:00
2017-01-13 14:45:50 +01:00
if ( is_visible_in_tree ( ) ) {
2023-07-10 17:26:02 +02:00
Vector2 offset = scrollbar_offset * zoom - p_center ;
h_scrollbar - > set_value ( offset . x ) ;
v_scrollbar - > set_value ( offset . y ) ;
2016-01-19 00:32:37 +01:00
}
2016-02-08 20:28:12 +01:00
2021-06-16 15:14:25 +02:00
_update_zoom_label ( ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2016-01-19 00:32:37 +01:00
}
float GraphEdit : : get_zoom ( ) const {
return zoom ;
}
2015-01-03 20:52:37 +01:00
2021-06-16 15:14:25 +02:00
void GraphEdit : : set_zoom_step ( float p_zoom_step ) {
p_zoom_step = abs ( p_zoom_step ) ;
2023-01-13 06:59:29 +01:00
ERR_FAIL_COND ( ! isfinite ( p_zoom_step ) ) ;
2021-06-16 15:14:25 +02:00
if ( zoom_step = = p_zoom_step ) {
return ;
}
zoom_step = p_zoom_step ;
2023-01-19 17:50:51 +01:00
panner - > set_scroll_zoom_factor ( zoom_step ) ;
2021-06-16 15:14:25 +02:00
}
float GraphEdit : : get_zoom_step ( ) const {
return zoom_step ;
}
void GraphEdit : : set_zoom_min ( float p_zoom_min ) {
ERR_FAIL_COND_MSG ( p_zoom_min > zoom_max , " Cannot set min zoom level greater than max zoom level. " ) ;
if ( zoom_min = = p_zoom_min ) {
return ;
}
zoom_min = p_zoom_min ;
set_zoom ( zoom ) ;
}
float GraphEdit : : get_zoom_min ( ) const {
return zoom_min ;
}
void GraphEdit : : set_zoom_max ( float p_zoom_max ) {
ERR_FAIL_COND_MSG ( p_zoom_max < zoom_min , " Cannot set max zoom level lesser than min zoom level. " ) ;
if ( zoom_max = = p_zoom_max ) {
return ;
}
zoom_max = p_zoom_max ;
set_zoom ( zoom ) ;
}
float GraphEdit : : get_zoom_max ( ) const {
return zoom_max ;
}
void GraphEdit : : set_show_zoom_label ( bool p_enable ) {
if ( zoom_label - > is_visible ( ) = = p_enable ) {
return ;
}
zoom_label - > set_visible ( p_enable ) ;
}
bool GraphEdit : : is_showing_zoom_label ( ) const {
return zoom_label - > is_visible ( ) ;
}
2015-01-07 05:45:46 +01:00
void GraphEdit : : set_right_disconnects ( bool p_enable ) {
2015-07-21 03:15:06 +02:00
right_disconnects = p_enable ;
2015-01-07 05:45:46 +01:00
}
bool GraphEdit : : is_right_disconnects_enabled ( ) const {
2015-07-21 03:15:06 +02:00
return right_disconnects ;
2015-01-07 05:45:46 +01:00
}
2016-08-03 00:11:05 +02:00
void GraphEdit : : add_valid_right_disconnect_type ( int p_type ) {
valid_right_disconnect_types . insert ( p_type ) ;
}
void GraphEdit : : remove_valid_right_disconnect_type ( int p_type ) {
valid_right_disconnect_types . erase ( p_type ) ;
}
void GraphEdit : : add_valid_left_disconnect_type ( int p_type ) {
valid_left_disconnect_types . insert ( p_type ) ;
}
void GraphEdit : : remove_valid_left_disconnect_type ( int p_type ) {
valid_left_disconnect_types . erase ( p_type ) ;
}
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > GraphEdit : : _get_connection_list ( ) const {
2015-07-21 03:15:06 +02:00
List < Connection > conns ;
get_connection_list ( & conns ) ;
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > arr ;
2021-07-24 15:46:25 +02:00
for ( const Connection & E : conns ) {
2015-07-21 03:15:06 +02:00
Dictionary d ;
2023-07-10 17:26:02 +02:00
d [ " from_node " ] = E . from_node ;
2021-07-16 05:45:57 +02:00
d [ " from_port " ] = E . from_port ;
2023-07-10 17:26:02 +02:00
d [ " to_node " ] = E . to_node ;
2021-07-16 05:45:57 +02:00
d [ " to_port " ] = E . to_port ;
2015-07-21 03:15:06 +02:00
arr . push_back ( d ) ;
}
return arr ;
2015-01-08 04:41:34 +01:00
}
2016-02-08 20:28:12 +01:00
void GraphEdit : : _zoom_minus ( ) {
2021-06-16 15:14:25 +02:00
set_zoom ( zoom / zoom_step ) ;
2016-02-08 20:28:12 +01:00
}
2020-05-14 14:29:06 +02:00
2016-02-08 20:28:12 +01:00
void GraphEdit : : _zoom_reset ( ) {
set_zoom ( 1 ) ;
}
void GraphEdit : : _zoom_plus ( ) {
2021-06-16 15:14:25 +02:00
set_zoom ( zoom * zoom_step ) ;
}
void GraphEdit : : _update_zoom_label ( ) {
int zoom_percent = static_cast < int > ( Math : : round ( zoom * 100 ) ) ;
String zoom_text = itos ( zoom_percent ) + " % " ;
zoom_label - > set_text ( zoom_text ) ;
2016-02-08 20:28:12 +01:00
}
2016-08-03 00:11:05 +02:00
void GraphEdit : : add_valid_connection_type ( int p_type , int p_with_type ) {
2023-07-10 17:26:02 +02:00
ConnectionType ct ( p_type , p_with_type ) ;
2016-08-03 00:11:05 +02:00
valid_connection_types . insert ( ct ) ;
}
void GraphEdit : : remove_valid_connection_type ( int p_type , int p_with_type ) {
2023-07-10 17:26:02 +02:00
ConnectionType ct ( p_type , p_with_type ) ;
2016-08-03 00:11:05 +02:00
valid_connection_types . erase ( ct ) ;
}
bool GraphEdit : : is_valid_connection_type ( int p_type , int p_with_type ) const {
2023-07-10 17:26:02 +02:00
ConnectionType ct ( p_type , p_with_type ) ;
2016-08-03 00:11:05 +02:00
return valid_connection_types . has ( ct ) ;
}
2023-07-10 17:26:02 +02:00
void GraphEdit : : set_snapping_enabled ( bool p_enable ) {
if ( snapping_enabled = = p_enable ) {
2022-03-16 08:50:48 +01:00
return ;
}
2023-07-10 17:26:02 +02:00
snapping_enabled = p_enable ;
toggle_snapping_button - > set_pressed ( p_enable ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2016-08-04 05:05:35 +02:00
}
2023-07-10 17:26:02 +02:00
bool GraphEdit : : is_snapping_enabled ( ) const {
return snapping_enabled ;
}
void GraphEdit : : set_snapping_distance ( int p_snapping_distance ) {
ERR_FAIL_COND_MSG ( p_snapping_distance < GRID_MIN_SNAPPING_DISTANCE | | p_snapping_distance > GRID_MAX_SNAPPING_DISTANCE ,
vformat ( " GraphEdit's snapping distance must be between %d and %d (inclusive) " , GRID_MIN_SNAPPING_DISTANCE , GRID_MAX_SNAPPING_DISTANCE ) ) ;
snapping_distance = p_snapping_distance ;
snapping_distance_spinbox - > set_value ( p_snapping_distance ) ;
queue_redraw ( ) ;
2016-08-04 05:05:35 +02:00
}
2023-07-10 17:26:02 +02:00
int GraphEdit : : get_snapping_distance ( ) const {
return snapping_distance ;
2016-08-04 05:05:35 +02:00
}
2023-07-10 17:26:02 +02:00
void GraphEdit : : set_show_grid ( bool p_show ) {
if ( show_grid = = p_show ) {
return ;
}
show_grid = p_show ;
show_grid_button - > set_pressed ( p_show ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2016-08-04 05:05:35 +02:00
}
2020-05-14 14:29:06 +02:00
2023-07-10 17:26:02 +02:00
bool GraphEdit : : is_showing_grid ( ) const {
return show_grid ;
}
void GraphEdit : : _snapping_toggled ( ) {
snapping_enabled = toggle_snapping_button - > is_pressed ( ) ;
}
void GraphEdit : : _snapping_distance_changed ( double ) {
snapping_distance = snapping_distance_spinbox - > get_value ( ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2016-08-04 05:05:35 +02:00
}
2023-07-10 17:26:02 +02:00
void GraphEdit : : _show_grid_toggled ( ) {
show_grid = show_grid_button - > is_pressed ( ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2016-08-04 05:05:35 +02:00
}
2020-11-06 20:16:45 +01:00
void GraphEdit : : set_minimap_size ( Vector2 p_size ) {
minimap - > set_size ( p_size ) ;
Vector2 minimap_size = minimap - > get_size ( ) ; // The size might've been adjusted by the minimum size.
minimap - > set_anchors_preset ( Control : : PRESET_BOTTOM_RIGHT ) ;
2023-07-10 17:26:02 +02:00
minimap - > set_offset ( Side : : SIDE_LEFT , - minimap_size . width - MINIMAP_OFFSET ) ;
minimap - > set_offset ( Side : : SIDE_TOP , - minimap_size . height - MINIMAP_OFFSET ) ;
2020-12-22 17:24:29 +01:00
minimap - > set_offset ( Side : : SIDE_RIGHT , - MINIMAP_OFFSET ) ;
minimap - > set_offset ( Side : : SIDE_BOTTOM , - MINIMAP_OFFSET ) ;
2022-08-13 23:21:24 +02:00
minimap - > queue_redraw ( ) ;
2020-11-06 20:16:45 +01:00
}
Vector2 GraphEdit : : get_minimap_size ( ) const {
return minimap - > get_size ( ) ;
}
void GraphEdit : : set_minimap_opacity ( float p_opacity ) {
2022-03-16 08:50:48 +01:00
if ( minimap - > get_modulate ( ) . a = = p_opacity ) {
return ;
}
2020-11-06 20:16:45 +01:00
minimap - > set_modulate ( Color ( 1 , 1 , 1 , p_opacity ) ) ;
2022-08-13 23:21:24 +02:00
minimap - > queue_redraw ( ) ;
2020-11-06 20:16:45 +01:00
}
float GraphEdit : : get_minimap_opacity ( ) const {
Color minimap_modulate = minimap - > get_modulate ( ) ;
return minimap_modulate . a ;
}
void GraphEdit : : set_minimap_enabled ( bool p_enable ) {
2022-03-16 08:50:48 +01:00
if ( minimap_button - > is_pressed ( ) = = p_enable ) {
return ;
}
2020-11-06 20:16:45 +01:00
minimap_button - > set_pressed ( p_enable ) ;
2022-01-26 03:04:02 +01:00
_minimap_toggled ( ) ;
2022-08-13 23:21:24 +02:00
minimap - > queue_redraw ( ) ;
2020-11-06 20:16:45 +01:00
}
bool GraphEdit : : is_minimap_enabled ( ) const {
return minimap_button - > is_pressed ( ) ;
}
2022-05-04 07:31:53 +02:00
void GraphEdit : : set_arrange_nodes_button_hidden ( bool p_enable ) {
arrange_nodes_button_hidden = p_enable ;
if ( arrange_nodes_button_hidden ) {
layout_button - > hide ( ) ;
} else {
layout_button - > show ( ) ;
}
}
bool GraphEdit : : is_arrange_nodes_button_hidden ( ) const {
return arrange_nodes_button_hidden ;
}
2020-11-06 20:16:45 +01:00
void GraphEdit : : _minimap_toggled ( ) {
2021-03-01 16:19:48 +01:00
if ( is_minimap_enabled ( ) ) {
minimap - > set_visible ( true ) ;
2022-08-13 23:21:24 +02:00
minimap - > queue_redraw ( ) ;
2021-03-01 16:19:48 +01:00
} else {
minimap - > set_visible ( false ) ;
}
2020-11-06 20:16:45 +01:00
}
2022-05-30 15:38:13 +02:00
void GraphEdit : : set_connection_lines_curvature ( float p_curvature ) {
lines_curvature = p_curvature ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2022-05-30 15:38:13 +02:00
}
float GraphEdit : : get_connection_lines_curvature ( ) const {
return lines_curvature ;
}
2020-12-18 14:13:28 +01:00
void GraphEdit : : set_connection_lines_thickness ( float p_thickness ) {
2022-03-16 08:50:48 +01:00
if ( lines_thickness = = p_thickness ) {
return ;
}
2020-12-18 14:13:28 +01:00
lines_thickness = p_thickness ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2020-12-18 14:13:28 +01:00
}
float GraphEdit : : get_connection_lines_thickness ( ) const {
return lines_thickness ;
}
void GraphEdit : : set_connection_lines_antialiased ( bool p_antialiased ) {
2022-03-16 08:50:48 +01:00
if ( lines_antialiased = = p_antialiased ) {
return ;
}
2020-12-18 14:13:28 +01:00
lines_antialiased = p_antialiased ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2020-12-18 14:13:28 +01:00
}
bool GraphEdit : : is_connection_lines_antialiased ( ) const {
return lines_antialiased ;
}
2023-07-10 17:26:02 +02:00
HBoxContainer * GraphEdit : : get_menu_hbox ( ) {
return menu_hbox ;
2018-06-19 03:10:48 +02:00
}
2022-01-19 19:59:12 +01:00
Ref < ViewPanner > GraphEdit : : get_panner ( ) {
return panner ;
}
void GraphEdit : : set_warped_panning ( bool p_warped ) {
warped_panning = p_warped ;
}
2021-08-10 21:14:19 +02:00
void GraphEdit : : arrange_nodes ( ) {
2023-07-10 17:26:02 +02:00
arranger - > arrange_nodes ( ) ;
2021-08-10 21:14:19 +02:00
}
2015-01-03 20:52:37 +01:00
void GraphEdit : : _bind_methods ( ) {
2022-09-09 15:29:51 +02:00
ClassDB : : bind_method ( D_METHOD ( " connect_node " , " from_node " , " from_port " , " to_node " , " to_port " ) , & GraphEdit : : connect_node ) ;
ClassDB : : bind_method ( D_METHOD ( " is_node_connected " , " from_node " , " from_port " , " to_node " , " to_port " ) , & GraphEdit : : is_node_connected ) ;
ClassDB : : bind_method ( D_METHOD ( " disconnect_node " , " from_node " , " from_port " , " to_node " , " to_port " ) , & GraphEdit : : disconnect_node ) ;
ClassDB : : bind_method ( D_METHOD ( " set_connection_activity " , " from_node " , " from_port " , " to_node " , " to_port " , " amount " ) , & GraphEdit : : set_connection_activity ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_connection_list " ) , & GraphEdit : : _get_connection_list ) ;
2018-01-31 01:09:41 +01:00
ClassDB : : bind_method ( D_METHOD ( " clear_connections " ) , & GraphEdit : : clear_connections ) ;
2021-07-26 16:31:31 +02:00
ClassDB : : bind_method ( D_METHOD ( " force_connection_drag_end " ) , & GraphEdit : : force_connection_drag_end ) ;
2023-07-10 17:26:02 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_scroll_offset " ) , & GraphEdit : : get_scroll_offset ) ;
ClassDB : : bind_method ( D_METHOD ( " set_scroll_offset " , " offset " ) , & GraphEdit : : set_scroll_offset ) ;
2016-01-19 00:32:37 +01:00
2018-01-31 01:09:41 +01:00
ClassDB : : bind_method ( D_METHOD ( " add_valid_right_disconnect_type " , " type " ) , & GraphEdit : : add_valid_right_disconnect_type ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_valid_right_disconnect_type " , " type " ) , & GraphEdit : : remove_valid_right_disconnect_type ) ;
ClassDB : : bind_method ( D_METHOD ( " add_valid_left_disconnect_type " , " type " ) , & GraphEdit : : add_valid_left_disconnect_type ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_valid_left_disconnect_type " , " type " ) , & GraphEdit : : remove_valid_left_disconnect_type ) ;
ClassDB : : bind_method ( D_METHOD ( " add_valid_connection_type " , " from_type " , " to_type " ) , & GraphEdit : : add_valid_connection_type ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_valid_connection_type " , " from_type " , " to_type " ) , & GraphEdit : : remove_valid_connection_type ) ;
ClassDB : : bind_method ( D_METHOD ( " is_valid_connection_type " , " from_type " , " to_type " ) , & GraphEdit : : is_valid_connection_type ) ;
2022-09-09 15:29:51 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_connection_line " , " from_node " , " to_node " ) , & GraphEdit : : get_connection_line ) ;
2018-01-31 01:09:41 +01:00
2021-09-28 18:00:16 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_panning_scheme " , " scheme " ) , & GraphEdit : : set_panning_scheme ) ;
ClassDB : : bind_method ( D_METHOD ( " get_panning_scheme " ) , & GraphEdit : : get_panning_scheme ) ;
2021-02-04 14:38:51 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_zoom " , " zoom " ) , & GraphEdit : : set_zoom ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_zoom " ) , & GraphEdit : : get_zoom ) ;
2016-08-04 05:05:35 +02:00
2021-06-16 15:14:25 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_zoom_min " , " zoom_min " ) , & GraphEdit : : set_zoom_min ) ;
ClassDB : : bind_method ( D_METHOD ( " get_zoom_min " ) , & GraphEdit : : get_zoom_min ) ;
ClassDB : : bind_method ( D_METHOD ( " set_zoom_max " , " zoom_max " ) , & GraphEdit : : set_zoom_max ) ;
ClassDB : : bind_method ( D_METHOD ( " get_zoom_max " ) , & GraphEdit : : get_zoom_max ) ;
ClassDB : : bind_method ( D_METHOD ( " set_zoom_step " , " zoom_step " ) , & GraphEdit : : set_zoom_step ) ;
ClassDB : : bind_method ( D_METHOD ( " get_zoom_step " ) , & GraphEdit : : get_zoom_step ) ;
ClassDB : : bind_method ( D_METHOD ( " set_show_zoom_label " , " enable " ) , & GraphEdit : : set_show_zoom_label ) ;
ClassDB : : bind_method ( D_METHOD ( " is_showing_zoom_label " ) , & GraphEdit : : is_showing_zoom_label ) ;
2023-07-10 17:26:02 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_show_grid " , " enable " ) , & GraphEdit : : set_show_grid ) ;
ClassDB : : bind_method ( D_METHOD ( " is_showing_grid " ) , & GraphEdit : : is_showing_grid ) ;
ClassDB : : bind_method ( D_METHOD ( " set_snapping_enabled " , " enable " ) , & GraphEdit : : set_snapping_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_snapping_enabled " ) , & GraphEdit : : is_snapping_enabled ) ;
2016-08-04 05:05:35 +02:00
2023-07-10 17:26:02 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_snapping_distance " , " pixels " ) , & GraphEdit : : set_snapping_distance ) ;
ClassDB : : bind_method ( D_METHOD ( " get_snapping_distance " ) , & GraphEdit : : get_snapping_distance ) ;
2015-01-07 05:45:46 +01:00
2022-05-30 15:38:13 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_connection_lines_curvature " , " curvature " ) , & GraphEdit : : set_connection_lines_curvature ) ;
ClassDB : : bind_method ( D_METHOD ( " get_connection_lines_curvature " ) , & GraphEdit : : get_connection_lines_curvature ) ;
2020-12-18 14:13:28 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_connection_lines_thickness " , " pixels " ) , & GraphEdit : : set_connection_lines_thickness ) ;
ClassDB : : bind_method ( D_METHOD ( " get_connection_lines_thickness " ) , & GraphEdit : : get_connection_lines_thickness ) ;
ClassDB : : bind_method ( D_METHOD ( " set_connection_lines_antialiased " , " pixels " ) , & GraphEdit : : set_connection_lines_antialiased ) ;
ClassDB : : bind_method ( D_METHOD ( " is_connection_lines_antialiased " ) , & GraphEdit : : is_connection_lines_antialiased ) ;
2021-02-04 14:38:51 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_minimap_size " , " size " ) , & GraphEdit : : set_minimap_size ) ;
2020-11-06 20:16:45 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_minimap_size " ) , & GraphEdit : : get_minimap_size ) ;
2021-02-04 14:38:51 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_minimap_opacity " , " opacity " ) , & GraphEdit : : set_minimap_opacity ) ;
2020-11-06 20:16:45 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_minimap_opacity " ) , & GraphEdit : : get_minimap_opacity ) ;
ClassDB : : bind_method ( D_METHOD ( " set_minimap_enabled " , " enable " ) , & GraphEdit : : set_minimap_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_minimap_enabled " ) , & GraphEdit : : is_minimap_enabled ) ;
2022-05-04 07:31:53 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_arrange_nodes_button_hidden " , " enable " ) , & GraphEdit : : set_arrange_nodes_button_hidden ) ;
ClassDB : : bind_method ( D_METHOD ( " is_arrange_nodes_button_hidden " ) , & GraphEdit : : is_arrange_nodes_button_hidden ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_right_disconnects " , " enable " ) , & GraphEdit : : set_right_disconnects ) ;
ClassDB : : bind_method ( D_METHOD ( " is_right_disconnects_enabled " ) , & GraphEdit : : is_right_disconnects_enabled ) ;
2015-01-03 20:52:37 +01:00
2022-09-09 15:29:51 +02:00
GDVIRTUAL_BIND ( _is_in_input_hotzone , " in_node " , " in_port " , " mouse_position " ) ;
GDVIRTUAL_BIND ( _is_in_output_hotzone , " in_node " , " in_port " , " mouse_position " ) ;
2016-08-31 04:44:14 +02:00
2023-07-10 17:26:02 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_menu_hbox " ) , & GraphEdit : : get_menu_hbox ) ;
2018-06-19 03:10:48 +02:00
2021-08-10 21:14:19 +02:00
ClassDB : : bind_method ( D_METHOD ( " arrange_nodes " ) , & GraphEdit : : arrange_nodes ) ;
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_selected " , " node " ) , & GraphEdit : : set_selected ) ;
2015-01-03 20:52:37 +01:00
2022-09-09 15:29:51 +02:00
GDVIRTUAL_BIND ( _get_connection_line , " from_position " , " to_position " )
GDVIRTUAL_BIND ( _is_node_hover_valid , " from_node " , " from_port " , " to_node " , " to_port " ) ;
2020-07-19 19:11:02 +02:00
2018-01-11 23:35:12 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " right_disconnects " ) , " set_right_disconnects " , " is_right_disconnects_enabled " ) ;
2023-07-10 17:26:02 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : VECTOR2 , " scroll_offset " , PROPERTY_HINT_NONE , " suffix:px " ) , " set_scroll_offset " , " get_scroll_offset " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " show_grid " ) , " set_show_grid " , " is_showing_grid " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " snapping_enabled " ) , " set_snapping_enabled " , " is_snapping_enabled " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " snapping_distance " , PROPERTY_HINT_NONE , " suffix:px " ) , " set_snapping_distance " , " get_snapping_distance " ) ;
2021-09-28 18:00:16 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " panning_scheme " , PROPERTY_HINT_ENUM , " Scroll Zooms,Scroll Pans " ) , " set_panning_scheme " , " get_panning_scheme " ) ;
2021-06-16 15:14:25 +02:00
ADD_GROUP ( " Connection Lines " , " connection_lines " ) ;
2022-05-30 15:38:13 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " connection_lines_curvature " ) , " set_connection_lines_curvature " , " get_connection_lines_curvature " ) ;
2022-05-20 07:24:41 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " connection_lines_thickness " , PROPERTY_HINT_NONE , " suffix:px " ) , " set_connection_lines_thickness " , " get_connection_lines_thickness " ) ;
2020-12-18 14:13:28 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " connection_lines_antialiased " ) , " set_connection_lines_antialiased " , " is_connection_lines_antialiased " ) ;
2021-06-16 15:14:25 +02:00
ADD_GROUP ( " Zoom " , " " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " zoom " ) , " set_zoom " , " get_zoom " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " zoom_min " ) , " set_zoom_min " , " get_zoom_min " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " zoom_max " ) , " set_zoom_max " , " get_zoom_max " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " zoom_step " ) , " set_zoom_step " , " get_zoom_step " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " show_zoom_label " ) , " set_show_zoom_label " , " is_showing_zoom_label " ) ;
2022-11-25 05:48:37 +01:00
ADD_GROUP ( " Minimap " , " minimap_ " ) ;
2020-11-06 20:16:45 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " minimap_enabled " ) , " set_minimap_enabled " , " is_minimap_enabled " ) ;
2022-05-20 07:24:41 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : VECTOR2 , " minimap_size " , PROPERTY_HINT_NONE , " suffix:px " ) , " set_minimap_size " , " get_minimap_size " ) ;
2020-11-06 20:16:45 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " minimap_opacity " ) , " set_minimap_opacity " , " get_minimap_opacity " ) ;
2018-01-11 23:35:12 +01:00
2022-05-04 07:31:53 +02:00
ADD_GROUP ( " UI " , " " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " arrange_nodes_button_hidden " ) , " set_arrange_nodes_button_hidden " , " is_arrange_nodes_button_hidden " ) ;
2022-09-09 15:29:51 +02:00
ADD_SIGNAL ( MethodInfo ( " connection_request " , PropertyInfo ( Variant : : STRING_NAME , " from_node " ) , PropertyInfo ( Variant : : INT , " from_port " ) , PropertyInfo ( Variant : : STRING_NAME , " to_node " ) , PropertyInfo ( Variant : : INT , " to_port " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " disconnection_request " , PropertyInfo ( Variant : : STRING_NAME , " from_node " ) , PropertyInfo ( Variant : : INT , " from_port " ) , PropertyInfo ( Variant : : STRING_NAME , " to_node " ) , PropertyInfo ( Variant : : INT , " to_port " ) ) ) ;
2018-12-27 11:10:09 +01:00
ADD_SIGNAL ( MethodInfo ( " popup_request " , PropertyInfo ( Variant : : VECTOR2 , " position " ) ) ) ;
2015-07-26 02:16:07 +02:00
ADD_SIGNAL ( MethodInfo ( " duplicate_nodes_request " ) ) ;
2019-07-12 19:36:33 +02:00
ADD_SIGNAL ( MethodInfo ( " copy_nodes_request " ) ) ;
ADD_SIGNAL ( MethodInfo ( " paste_nodes_request " ) ) ;
2018-09-01 12:05:51 +02:00
ADD_SIGNAL ( MethodInfo ( " node_selected " , PropertyInfo ( Variant : : OBJECT , " node " , PROPERTY_HINT_RESOURCE_TYPE , " Node " ) ) ) ;
2020-12-21 11:26:41 +01:00
ADD_SIGNAL ( MethodInfo ( " node_deselected " , PropertyInfo ( Variant : : OBJECT , " node " , PROPERTY_HINT_RESOURCE_TYPE , " Node " ) ) ) ;
2022-09-09 15:29:51 +02:00
ADD_SIGNAL ( MethodInfo ( " connection_to_empty " , PropertyInfo ( Variant : : STRING_NAME , " from_node " ) , PropertyInfo ( Variant : : INT , " from_port " ) , PropertyInfo ( Variant : : VECTOR2 , " release_position " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " connection_from_empty " , PropertyInfo ( Variant : : STRING_NAME , " to_node " ) , PropertyInfo ( Variant : : INT , " to_port " ) , PropertyInfo ( Variant : : VECTOR2 , " release_position " ) ) ) ;
2023-08-09 18:31:15 +02:00
ADD_SIGNAL ( MethodInfo ( " close_nodes_request " , PropertyInfo ( Variant : : ARRAY , " nodes " , PROPERTY_HINT_ARRAY_TYPE , " StringName " ) ) ) ;
2020-10-20 08:22:40 +02:00
ADD_SIGNAL ( MethodInfo ( " begin_node_move " ) ) ;
ADD_SIGNAL ( MethodInfo ( " end_node_move " ) ) ;
2022-04-14 23:20:28 +02:00
ADD_SIGNAL ( MethodInfo ( " scroll_offset_changed " , PropertyInfo ( Variant : : VECTOR2 , " offset " ) ) ) ;
2022-08-12 13:06:08 +02:00
ADD_SIGNAL ( MethodInfo ( " connection_drag_started " , PropertyInfo ( Variant : : STRING_NAME , " from_node " ) , PropertyInfo ( Variant : : INT , " from_port " ) , PropertyInfo ( Variant : : BOOL , " is_output " ) ) ) ;
2021-07-26 16:31:31 +02:00
ADD_SIGNAL ( MethodInfo ( " connection_drag_ended " ) ) ;
2021-09-28 18:00:16 +02:00
BIND_ENUM_CONSTANT ( SCROLL_ZOOMS ) ;
BIND_ENUM_CONSTANT ( SCROLL_PANS ) ;
2015-01-03 20:52:37 +01:00
}
GraphEdit : : GraphEdit ( ) {
2015-07-26 02:16:07 +02:00
set_focus_mode ( FOCUS_ALL ) ;
2021-06-16 15:14:25 +02:00
// Allow dezooming 8 times from the default zoom level.
// At low zoom levels, text is unreadable due to its small size and poor filtering,
2021-04-17 20:02:04 +02:00
// but this is still useful for previewing and navigation.
2021-06-16 15:14:25 +02:00
zoom_min = ( 1 / Math : : pow ( zoom_step , 8 ) ) ;
// Allow zooming 4 times from the default zoom level.
zoom_max = ( 1 * Math : : pow ( zoom_step , 4 ) ) ;
2021-09-28 18:00:16 +02:00
panner . instantiate ( ) ;
2023-01-19 17:50:51 +01:00
panner - > set_callbacks ( callable_mp ( this , & GraphEdit : : _pan_callback ) , callable_mp ( this , & GraphEdit : : _zoom_callback ) ) ;
2021-09-28 18:00:16 +02:00
2015-07-21 03:15:06 +02:00
top_layer = memnew ( GraphEditFilter ( this ) ) ;
2021-08-25 15:49:30 +02:00
add_child ( top_layer , false , INTERNAL_MODE_BACK ) ;
2017-01-08 23:54:19 +01:00
top_layer - > set_mouse_filter ( MOUSE_FILTER_PASS ) ;
2022-03-19 01:02:57 +01:00
top_layer - > set_anchors_and_offsets_preset ( Control : : PRESET_FULL_RECT ) ;
2020-02-21 18:28:45 +01:00
top_layer - > connect ( " draw " , callable_mp ( this , & GraphEdit : : _top_layer_draw ) ) ;
top_layer - > connect ( " gui_input " , callable_mp ( this , & GraphEdit : : _top_layer_input ) ) ;
2022-01-19 19:59:12 +01:00
top_layer - > connect ( " focus_exited " , callable_mp ( panner . ptr ( ) , & ViewPanner : : release_pan_key ) ) ;
2015-01-03 20:52:37 +01:00
2016-08-31 04:44:14 +02:00
connections_layer = memnew ( Control ) ;
2023-07-10 17:26:02 +02:00
add_child ( connections_layer , false ) ;
2020-02-21 18:28:45 +01:00
connections_layer - > connect ( " draw " , callable_mp ( this , & GraphEdit : : _connections_layer_draw ) ) ;
2023-07-10 17:26:02 +02:00
connections_layer - > set_name ( " _connection_layer " ) ;
connections_layer - > set_disable_visibility_clip ( true ) ; // Necessary, so it can draw freely and be offset.
2017-08-08 15:57:33 +02:00
connections_layer - > set_mouse_filter ( MOUSE_FILTER_IGNORE ) ;
2016-08-31 04:44:14 +02:00
2023-07-10 17:26:02 +02:00
h_scrollbar = memnew ( HScrollBar ) ;
h_scrollbar - > set_name ( " _h_scroll " ) ;
top_layer - > add_child ( h_scrollbar ) ;
2015-01-03 20:52:37 +01:00
2023-07-10 17:26:02 +02:00
v_scrollbar = memnew ( VScrollBar ) ;
v_scrollbar - > set_name ( " _v_scroll " ) ;
top_layer - > add_child ( v_scrollbar ) ;
2020-01-14 22:19:12 +01:00
2023-07-10 17:26:02 +02:00
// Set large minmax so it can scroll even if not resized yet.
h_scrollbar - > set_min ( - 10000 ) ;
h_scrollbar - > set_max ( 10000 ) ;
2016-08-07 00:00:54 +02:00
2023-07-10 17:26:02 +02:00
v_scrollbar - > set_min ( - 10000 ) ;
v_scrollbar - > set_max ( 10000 ) ;
2016-08-07 00:00:54 +02:00
2023-07-10 17:26:02 +02:00
h_scrollbar - > connect ( " value_changed " , callable_mp ( this , & GraphEdit : : _scroll_moved ) ) ;
v_scrollbar - > connect ( " value_changed " , callable_mp ( this , & GraphEdit : : _scroll_moved ) ) ;
2016-01-19 00:32:37 +01:00
2023-07-10 17:26:02 +02:00
menu_hbox = memnew ( HBoxContainer ) ;
top_layer - > add_child ( menu_hbox ) ;
menu_hbox - > set_position ( Vector2 ( 10 , 10 ) ) ;
2016-02-08 20:28:12 +01:00
2021-06-16 15:14:25 +02:00
zoom_label = memnew ( Label ) ;
2023-07-10 17:26:02 +02:00
menu_hbox - > add_child ( zoom_label ) ;
2021-06-16 15:14:25 +02:00
zoom_label - > set_visible ( false ) ;
zoom_label - > set_v_size_flags ( Control : : SIZE_SHRINK_CENTER ) ;
2021-11-25 03:58:47 +01:00
zoom_label - > set_horizontal_alignment ( HORIZONTAL_ALIGNMENT_CENTER ) ;
2021-06-16 15:14:25 +02:00
zoom_label - > set_custom_minimum_size ( Size2 ( 48 , 0 ) ) ;
_update_zoom_label ( ) ;
2023-07-10 17:26:02 +02:00
zoom_minus_button = memnew ( Button ) ;
zoom_minus_button - > set_flat ( true ) ;
menu_hbox - > add_child ( zoom_minus_button ) ;
zoom_minus_button - > set_tooltip_text ( RTR ( " Zoom Out " ) ) ;
zoom_minus_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : _zoom_minus ) ) ;
zoom_minus_button - > set_focus_mode ( FOCUS_NONE ) ;
zoom_reset_button = memnew ( Button ) ;
zoom_reset_button - > set_flat ( true ) ;
menu_hbox - > add_child ( zoom_reset_button ) ;
zoom_reset_button - > set_tooltip_text ( RTR ( " Zoom Reset " ) ) ;
zoom_reset_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : _zoom_reset ) ) ;
zoom_reset_button - > set_focus_mode ( FOCUS_NONE ) ;
zoom_plus_button = memnew ( Button ) ;
zoom_plus_button - > set_flat ( true ) ;
menu_hbox - > add_child ( zoom_plus_button ) ;
zoom_plus_button - > set_tooltip_text ( RTR ( " Zoom In " ) ) ;
zoom_plus_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : _zoom_plus ) ) ;
zoom_plus_button - > set_focus_mode ( FOCUS_NONE ) ;
show_grid_button = memnew ( Button ) ;
show_grid_button - > set_flat ( true ) ;
show_grid_button - > set_toggle_mode ( true ) ;
show_grid_button - > set_tooltip_text ( RTR ( " Toggle the visual grid. " ) ) ;
show_grid_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : _show_grid_toggled ) ) ;
show_grid_button - > set_pressed ( true ) ;
show_grid_button - > set_focus_mode ( FOCUS_NONE ) ;
menu_hbox - > add_child ( show_grid_button ) ;
toggle_snapping_button = memnew ( Button ) ;
toggle_snapping_button - > set_flat ( true ) ;
toggle_snapping_button - > set_toggle_mode ( true ) ;
toggle_snapping_button - > set_tooltip_text ( RTR ( " Toggle snapping to the grid. " ) ) ;
toggle_snapping_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : _snapping_toggled ) ) ;
toggle_snapping_button - > set_pressed ( snapping_enabled ) ;
toggle_snapping_button - > set_focus_mode ( FOCUS_NONE ) ;
menu_hbox - > add_child ( toggle_snapping_button ) ;
snapping_distance_spinbox = memnew ( SpinBox ) ;
snapping_distance_spinbox - > set_min ( GRID_MIN_SNAPPING_DISTANCE ) ;
snapping_distance_spinbox - > set_max ( GRID_MAX_SNAPPING_DISTANCE ) ;
snapping_distance_spinbox - > set_step ( 1 ) ;
snapping_distance_spinbox - > set_value ( snapping_distance ) ;
snapping_distance_spinbox - > set_tooltip_text ( RTR ( " Change the snapping distance. " ) ) ;
snapping_distance_spinbox - > connect ( " value_changed " , callable_mp ( this , & GraphEdit : : _snapping_distance_changed ) ) ;
menu_hbox - > add_child ( snapping_distance_spinbox ) ;
2016-08-04 05:05:35 +02:00
2020-11-06 20:16:45 +01:00
minimap_button = memnew ( Button ) ;
minimap_button - > set_flat ( true ) ;
minimap_button - > set_toggle_mode ( true ) ;
2023-07-10 17:26:02 +02:00
minimap_button - > set_tooltip_text ( RTR ( " Toggle the graph minimap. " ) ) ;
2020-11-06 20:16:45 +01:00
minimap_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : _minimap_toggled ) ) ;
2023-07-10 17:26:02 +02:00
minimap_button - > set_pressed ( show_grid ) ;
2020-11-06 20:16:45 +01:00
minimap_button - > set_focus_mode ( FOCUS_NONE ) ;
2023-07-10 17:26:02 +02:00
menu_hbox - > add_child ( minimap_button ) ;
2020-11-06 20:16:45 +01:00
2021-08-10 21:14:19 +02:00
layout_button = memnew ( Button ) ;
layout_button - > set_flat ( true ) ;
2023-07-10 17:26:02 +02:00
menu_hbox - > add_child ( layout_button ) ;
layout_button - > set_tooltip_text ( RTR ( " Automatically arrange selected nodes. " ) ) ;
2021-08-10 21:14:19 +02:00
layout_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : arrange_nodes ) ) ;
layout_button - > set_focus_mode ( FOCUS_NONE ) ;
2020-11-06 20:16:45 +01:00
Vector2 minimap_size = Vector2 ( 240 , 160 ) ;
2020-12-17 18:40:32 +01:00
float minimap_opacity = 0.65 ;
2020-11-06 20:16:45 +01:00
minimap = memnew ( GraphEditMinimap ( this ) ) ;
top_layer - > add_child ( minimap ) ;
minimap - > set_name ( " _minimap " ) ;
minimap - > set_modulate ( Color ( 1 , 1 , 1 , minimap_opacity ) ) ;
2021-01-25 15:37:05 +01:00
minimap - > set_mouse_filter ( MOUSE_FILTER_PASS ) ;
2020-11-06 20:16:45 +01:00
minimap - > set_custom_minimum_size ( Vector2 ( 50 , 50 ) ) ;
minimap - > set_size ( minimap_size ) ;
minimap - > set_anchors_preset ( Control : : PRESET_BOTTOM_RIGHT ) ;
2023-07-10 17:26:02 +02:00
minimap - > set_offset ( Side : : SIDE_LEFT , - minimap_size . width - MINIMAP_OFFSET ) ;
minimap - > set_offset ( Side : : SIDE_TOP , - minimap_size . height - MINIMAP_OFFSET ) ;
2020-12-22 17:24:29 +01:00
minimap - > set_offset ( Side : : SIDE_RIGHT , - MINIMAP_OFFSET ) ;
minimap - > set_offset ( Side : : SIDE_BOTTOM , - MINIMAP_OFFSET ) ;
2020-11-06 20:16:45 +01:00
minimap - > connect ( " draw " , callable_mp ( this , & GraphEdit : : _minimap_draw ) ) ;
2017-01-09 19:50:08 +01:00
set_clip_contents ( true ) ;
2023-07-10 17:26:02 +02:00
arranger = Ref < GraphEditArranger > ( memnew ( GraphEditArranger ( this ) ) ) ;
2015-01-03 20:52:37 +01:00
}