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"
2023-10-02 13:43:08 +02:00
# include "graph_edit.compat.inc"
2017-08-27 21:07:15 +02:00
2020-04-28 15:19:37 +02:00
# include "core/input/input.h"
2024-01-18 16:16:17 +01:00
# include "core/math/geometry_2d.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"
2024-01-18 16:16:17 +01:00
# include "scene/2d/line_2d.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"
2023-10-02 13:43:08 +02:00
# include "scene/gui/label.h"
# include "scene/gui/panel_container.h"
# include "scene/gui/scroll_bar.h"
# include "scene/gui/spin_box.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"
2023-09-12 15:01:42 +02:00
# include "scene/theme/theme_db.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 ;
2020-11-06 20:16:45 +01:00
2017-03-05 16:44:50 +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 ) {
2017-03-05 16:44:50 +01:00
ge = p_edit ;
2015-01-03 20:52:37 +01:00
}
2023-05-07 16:14:57 +02:00
Control : : CursorShape GraphEditMinimap : : get_cursor_shape ( const Point2 & p_pos ) const {
2023-09-12 15:01:42 +02:00
if ( is_resizing | | ( p_pos . x < theme_cache . resizer - > get_width ( ) & & p_pos . y < theme_cache . resizer - > get_height ( ) ) ) {
2023-05-07 16:14:57 +02:00
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 ;
2023-09-12 15:01:42 +02:00
Rect2 resizer_hitbox = Rect2 ( Point2 ( ) , theme_cache . resizer - > get_size ( ) ) ;
2020-11-06 20:16:45 +01:00
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-09-12 15:01:42 +02:00
void GraphEditMinimap : : _bind_methods ( ) {
BIND_THEME_ITEM ( Theme : : DATA_TYPE_STYLEBOX , GraphEditMinimap , panel ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_STYLEBOX , GraphEditMinimap , node_style , " node " ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_STYLEBOX , GraphEditMinimap , camera_style , " camera " ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , GraphEditMinimap , resizer ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , GraphEditMinimap , resizer_color ) ;
}
GraphEditMinimap : : GraphEditMinimap ( GraphEdit * p_edit ) {
ge = p_edit ;
minimap_padding = Vector2 ( MINIMAP_PADDING , MINIMAP_PADDING ) ;
minimap_offset = minimap_padding + _convert_from_graph_position ( graph_padding ) ;
}
2024-01-18 16:16:17 +01:00
Ref < Shader > GraphEdit : : default_connections_shader ;
void GraphEdit : : init_shaders ( ) {
default_connections_shader . instantiate ( ) ;
default_connections_shader - > set_code ( R " (
// Connection lines shader.
shader_type canvas_item ;
render_mode blend_mix ;
uniform vec4 rim_color : source_color ;
uniform int from_type ;
uniform int to_type ;
uniform float line_width ;
void fragment ( ) {
float fake_aa_width = 1.5 / line_width ;
float rim_width = 1.5 / line_width ;
float dist = abs ( UV . y - 0.5 ) ;
float alpha = smoothstep ( 0.5 , 0.5 - fake_aa_width , dist ) ;
vec4 final_color = mix ( rim_color , COLOR , smoothstep ( 0.5 - rim_width , 0.5 - fake_aa_width - rim_width , dist ) ) ;
COLOR = vec4 ( final_color . rgb , final_color . a * alpha ) ;
}
) " );
}
void GraphEdit : : finish_shaders ( ) {
default_connections_shader . unref ( ) ;
}
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 ;
}
2017-03-05 16:44:50 +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
}
2024-01-18 16:16:17 +01:00
Ref < Connection > c ;
c . instantiate ( ) ;
c - > from_node = p_from ;
c - > from_port = p_from_port ;
c - > to_node = p_to ;
c - > to_port = p_to_port ;
c - > activity = 0 ;
2015-07-21 03:15:06 +02:00
connections . push_back ( c ) ;
2024-01-18 16:16:17 +01:00
connection_map [ p_from ] . push_back ( c ) ;
connection_map [ p_to ] . push_back ( c ) ;
Line2D * line = memnew ( Line2D ) ;
line - > set_texture_mode ( Line2D : : LineTextureMode : : LINE_TEXTURE_STRETCH ) ;
Ref < ShaderMaterial > line_material ;
line_material . instantiate ( ) ;
line_material - > set_shader ( connections_shader ) ;
float line_width = _get_shader_line_width ( ) ;
line_material - > set_shader_parameter ( " line_width " , line_width ) ;
line_material - > set_shader_parameter ( " from_type " , c - > from_port ) ;
line_material - > set_shader_parameter ( " to_type " , c - > to_port ) ;
Ref < StyleBoxFlat > bg_panel = theme_cache . panel ;
Color connection_line_rim_color = bg_panel . is_valid ( ) ? bg_panel - > get_bg_color ( ) : Color ( 0.0 , 0.0 , 0.0 , 0.0 ) ;
line_material - > set_shader_parameter ( " rim_color " , connection_line_rim_color ) ;
line - > set_material ( line_material ) ;
connections_layer - > add_child ( line ) ;
c - > _cache . line = line ;
2022-08-13 23:21:24 +02:00
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2024-01-18 16:16:17 +01:00
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
2015-07-21 03:15:06 +02:00
return OK ;
2015-01-03 20:52:37 +01:00
}
2017-03-05 16:44:50 +01:00
bool GraphEdit : : is_node_connected ( const StringName & p_from , int p_from_port , const StringName & p_to , int p_to_port ) {
2024-01-18 16:16:17 +01:00
for ( const Ref < Connection > & conn : connection_map [ p_from ] ) {
if ( conn - > from_node = = p_from & & conn - > from_port = = p_from_port & & conn - > to_node = = p_to & & conn - > 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
}
2017-03-05 16:44:50 +01:00
void GraphEdit : : disconnect_node ( const StringName & p_from , int p_from_port , const StringName & p_to , int p_to_port ) {
2024-01-18 16:16:17 +01:00
for ( const List < Ref < Connection > > : : Element * E = connections . front ( ) ; E ; E = E - > next ( ) ) {
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 ) {
connection_map [ p_from ] . erase ( E - > get ( ) ) ;
connection_map [ p_to ] . erase ( E - > get ( ) ) ;
E - > get ( ) - > _cache . line - > queue_free ( ) ;
2015-07-21 03:15:06 +02:00
connections . erase ( E ) ;
2024-01-18 16:16:17 +01:00
2022-08-13 23:21:24 +02:00
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2024-01-18 16:16:17 +01:00
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
2015-07-21 03:15:06 +02:00
return ;
}
}
2015-01-03 20:52:37 +01:00
}
2024-01-18 16:16:17 +01:00
const List < Ref < GraphEdit : : Connection > > & GraphEdit : : get_connection_list ( ) const {
return 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 ( ) ;
2017-03-05 16:44:50 +01:00
awaiting_scroll_offset_update = true ;
2016-08-31 04:44:14 +02:00
}
2022-08-13 23:21:24 +02:00
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
2024-01-18 16:16:17 +01:00
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
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 ) ;
2017-03-05 16:44:50 +01: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 ) ;
2017-03-05 16:44:50 +01:00
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
}
2017-03-05 16:44:50 +01: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 ;
2017-03-05 16:44:50 +01: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 ( ) ;
2017-03-05 16:44:50 +01:00
awaiting_scroll_offset_update = true ;
2016-08-31 04:44:14 +02:00
}
2017-03-05 16:44:50 +01: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
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2024-01-18 16:16:17 +01:00
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
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
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2024-01-18 16:16:17 +01:00
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
}
void GraphEdit : : _graph_node_rect_changed ( GraphNode * p_node ) {
// Only invalidate the cache when zooming or the node is moved/resized in graph space.
if ( panner - > is_panning ( ) ) {
return ;
}
for ( Ref < Connection > & c : connection_map [ p_node - > get_name ( ) ] ) {
c - > _cache . dirty = true ;
}
connections_layer - > queue_redraw ( ) ;
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
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 ) {
2024-01-18 16:16:17 +01:00
graph_node - > connect ( " slot_updated " , callable_mp ( this , & GraphEdit : : _graph_node_slot_updated ) . bind ( graph_element ) ) ;
graph_node - > connect ( " item_rect_changed " , callable_mp ( this , & GraphEdit : : _graph_node_rect_changed ) . bind ( graph_node ) ) ;
2023-08-09 18:31:15 +02:00
}
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 * ) 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 ) {
2024-01-18 16:16:17 +01:00
graph_node - > disconnect ( " slot_updated " , callable_mp ( this , & GraphEdit : : _graph_node_slot_updated ) ) ;
graph_node - > disconnect ( " item_rect_changed " , callable_mp ( this , & GraphEdit : : _graph_node_rect_changed ) ) ;
// Invalidate all adjacent connections, so that they are removed before the next redraw.
for ( const Ref < Connection > & conn : connection_map [ graph_node - > get_name ( ) ] ) {
conn - > _cache . dirty = true ;
}
connections_layer - > queue_redraw ( ) ;
2023-08-09 18:31:15 +02:00
}
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 ( 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
}
2023-09-12 15:01:42 +02:00
void GraphEdit : : _update_theme_item_cache ( ) {
Control : : _update_theme_item_cache ( ) ;
theme_cache . base_scale = get_theme_default_base_scale ( ) ;
}
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 : {
2023-09-12 15:01:42 +02:00
zoom_minus_button - > set_icon ( theme_cache . zoom_out ) ;
zoom_reset_button - > set_icon ( theme_cache . zoom_reset ) ;
zoom_plus_button - > set_icon ( theme_cache . zoom_in ) ;
2023-07-10 17:26:02 +02:00
2023-09-12 15:01:42 +02:00
toggle_snapping_button - > set_icon ( theme_cache . snapping_toggle ) ;
2023-10-02 13:43:08 +02:00
toggle_grid_button - > set_icon ( theme_cache . grid_toggle ) ;
2023-09-12 15:01:42 +02:00
minimap_button - > set_icon ( theme_cache . minimap_toggle ) ;
2023-10-02 13:43:08 +02:00
arrange_button - > set_icon ( theme_cache . layout ) ;
2022-02-15 18:06:48 +01:00
2023-09-12 15:01:42 +02:00
zoom_label - > set_custom_minimum_size ( Size2 ( 48 , 0 ) * theme_cache . base_scale ) ;
2023-10-02 13:43:08 +02:00
menu_panel - > add_theme_style_override ( " panel " , theme_cache . menu_panel ) ;
2022-02-15 18:06:48 +01:00
} 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.
2023-09-12 15:01:42 +02:00
draw_style_box ( theme_cache . 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 ) {
2023-10-22 18:01:03 +02:00
_draw_grid ( ) ;
2016-08-04 05:05:35 +02:00
}
2022-02-15 18:06:48 +01:00
} break ;
case NOTIFICATION_RESIZED : {
_update_scroll ( ) ;
2022-08-13 23:21:24 +02:00
minimap - > queue_redraw ( ) ;
2024-01-18 16:16:17 +01:00
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
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
}
2017-03-05 16:44:50 +01:00
bool GraphEdit : : _filter_input ( const Point2 & p_point ) {
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-09-12 15:01:42 +02:00
Ref < Texture2D > port_icon = graph_node - > theme_cache . port ;
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-10-24 16:19:33 +02:00
Control * child = Object : : cast_to < Control > ( graph_node - > get_child ( slot_index , false ) ) ;
2023-07-10 17:26:02 +02:00
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-10-24 16:19:33 +02:00
Control * child = Object : : cast_to < Control > ( graph_node - > get_child ( slot_index , false ) ) ;
2023-07-10 17:26:02 +02:00
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
}
2024-01-18 16:16:17 +01:00
void GraphEdit : : _top_connection_layer_input ( const Ref < InputEvent > & p_ev ) {
2017-05-20 17:38:03 +02:00
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 ( ) ) {
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 ;
2017-03-05 16:44:50 +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_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-09-12 15:01:42 +02:00
Ref < Texture2D > port_icon = graph_node - > theme_cache . port ;
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-10-24 16:19:33 +02:00
Control * child = Object : : cast_to < Control > ( graph_node - > get_child ( slot_index , false ) ) ;
2023-07-10 17:26:02 +02:00
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.
2024-01-18 16:16:17 +01:00
for ( const Ref < Connection > & conn : connection_map [ graph_node - > get_name ( ) ] ) {
if ( conn - > from_node = = graph_node - > get_name ( ) & & conn - > from_port = = j ) {
Node * to = get_node ( NodePath ( conn - > to_node ) ) ;
2017-08-24 22:58:51 +02:00
if ( Object : : cast_to < GraphNode > ( to ) ) {
2024-01-18 16:16:17 +01:00
connecting_from_node = conn - > to_node ;
connecting_from_port_index = conn - > to_port ;
connecting_from_output = false ;
connecting_type = Object : : cast_to < GraphNode > ( to ) - > get_input_port_type ( conn - > to_port ) ;
connecting_color = Object : : cast_to < GraphNode > ( to ) - > get_input_port_color ( conn - > to_port ) ;
connecting_target_valid = false ;
connecting_to_point = pos ;
2016-08-03 00:11:05 +02:00
2022-05-04 07:31:53 +02:00
if ( connecting_type > = 0 ) {
just_disconnected = true ;
2024-01-18 16:16:17 +01:00
emit_signal ( SNAME ( " disconnection_request " ) , conn - > from_node , conn - > from_port , conn - > to_node , conn - > to_port ) ;
to = get_node ( NodePath ( connecting_from_node ) ) ; // Maybe it was erased.
2022-05-04 07:31:53 +02:00
if ( Object : : cast_to < GraphNode > ( to ) ) {
connecting = true ;
2024-01-18 16:16:17 +01:00
emit_signal ( SNAME ( " connection_drag_started " ) , connecting_from_node , connecting_from_port_index , false ) ;
2022-05-04 07:31:53 +02:00
}
2016-08-03 00:11:05 +02:00
}
return ;
}
}
}
}
2024-01-18 16:16:17 +01:00
connecting_from_node = graph_node - > get_name ( ) ;
connecting_from_port_index = j ;
connecting_from_output = 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 ) ;
2024-01-18 16:16:17 +01:00
connecting_target_valid = false ;
connecting_to_point = pos ;
2022-05-04 07:31:53 +02:00
if ( connecting_type > = 0 ) {
connecting = true ;
just_disconnected = false ;
2024-01-18 16:16:17 +01:00
emit_signal ( SNAME ( " connection_drag_started " ) , connecting_from_node , connecting_from_port_index , true ) ;
2022-05-04 07:31:53 +02:00
}
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-10-24 16:19:33 +02:00
Control * child = Object : : cast_to < Control > ( graph_node - > get_child ( slot_index , false ) ) ;
2023-07-10 17:26:02 +02:00
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.
2024-01-18 16:16:17 +01:00
for ( const Ref < Connection > & conn : connection_map [ graph_node - > get_name ( ) ] ) {
if ( conn - > to_node = = graph_node - > get_name ( ) & & conn - > to_port = = j ) {
Node * fr = get_node ( NodePath ( conn - > from_node ) ) ;
2017-08-24 22:58:51 +02:00
if ( Object : : cast_to < GraphNode > ( fr ) ) {
2024-01-18 16:16:17 +01:00
connecting_from_node = conn - > from_node ;
connecting_from_port_index = conn - > from_port ;
connecting_from_output = true ;
connecting_type = Object : : cast_to < GraphNode > ( fr ) - > get_output_port_type ( conn - > from_port ) ;
connecting_color = Object : : cast_to < GraphNode > ( fr ) - > get_output_port_color ( conn - > from_port ) ;
connecting_target_valid = false ;
connecting_to_point = 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 ) {
2024-01-18 16:16:17 +01:00
emit_signal ( SNAME ( " disconnection_request " ) , conn - > from_node , conn - > from_port , conn - > to_node , conn - > to_port ) ;
fr = get_node ( NodePath ( connecting_from_node ) ) ;
2022-05-04 07:31:53 +02:00
if ( Object : : cast_to < GraphNode > ( fr ) ) {
connecting = true ;
2024-01-18 16:16:17 +01:00
emit_signal ( SNAME ( " connection_drag_started " ) , connecting_from_node , connecting_from_port_index , true ) ;
2022-05-04 07:31:53 +02:00
}
2015-07-21 03:15:06 +02:00
}
return ;
}
}
}
}
2015-01-07 05:45:46 +01:00
2024-01-18 16:16:17 +01:00
connecting_from_node = graph_node - > get_name ( ) ;
connecting_from_port_index = j ;
connecting_from_output = 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 ) ;
2024-01-18 16:16:17 +01:00
connecting_target_valid = false ;
connecting_to_point = pos ;
2022-05-04 07:31:53 +02:00
if ( connecting_type > = 0 ) {
connecting = true ;
just_disconnected = false ;
2024-01-18 16:16:17 +01:00
emit_signal ( SNAME ( " connection_drag_started " ) , connecting_from_node , connecting_from_port_index , false ) ;
2022-05-04 07:31:53 +02:00
}
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 ) {
2024-01-18 16:16:17 +01:00
connecting_to_point = mm - > get_position ( ) ;
2022-08-13 23:21:24 +02:00
minimap - > queue_redraw ( ) ;
2024-01-18 16:16:17 +01:00
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
2023-07-10 17:26:02 +02:00
2024-01-18 16:16:17 +01:00
connecting_valid = just_disconnected | | click_pos . distance_to ( connecting_to_point / 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 - - ) {
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
2023-09-12 15:01:42 +02:00
Ref < Texture2D > port_icon = graph_node - > theme_cache . port ;
2024-01-18 16:16:17 +01:00
if ( ! connecting_from_output ) {
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-10-24 16:19:33 +02:00
Control * child = Object : : cast_to < Control > ( graph_node - > get_child ( slot_index , false ) ) ;
2023-07-10 17:26:02 +02:00
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 ) ) {
2024-01-18 16:16:17 +01:00
if ( ! is_node_hover_valid ( graph_node - > get_name ( ) , j , connecting_from_node , connecting_from_port_index ) ) {
2021-08-01 03:19:55 +02:00
continue ;
}
2024-01-18 16:16:17 +01:00
connecting_target_valid = true ;
connecting_to_point = pos ;
connecting_target_node = graph_node - > get_name ( ) ;
connecting_target_port_index = j ;
2020-07-04 07:00:17 +02:00
return ;
}
2015-07-21 03:15:06 +02:00
}
2024-01-18 16:16:17 +01:00
connecting_target_valid = false ;
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-10-24 16:19:33 +02:00
Control * child = Object : : cast_to < Control > ( graph_node - > get_child ( slot_index , false ) ) ;
2023-07-10 17:26:02 +02:00
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 ) ) {
2024-01-18 16:16:17 +01:00
if ( ! is_node_hover_valid ( connecting_from_node , connecting_from_port_index , graph_node - > get_name ( ) , j ) ) {
2021-08-01 03:19:55 +02:00
continue ;
}
2024-01-18 16:16:17 +01:00
connecting_target_valid = true ;
connecting_to_point = pos ;
connecting_target_node = graph_node - > get_name ( ) ;
connecting_target_port_index = j ;
2020-07-04 07:00:17 +02:00
return ;
}
2015-07-21 03:15:06 +02:00
}
2024-01-18 16:16:17 +01:00
connecting_target_valid = false ;
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 ) {
2024-01-18 16:16:17 +01:00
if ( connecting & & connecting_target_valid ) {
if ( connecting_from_output ) {
emit_signal ( SNAME ( " connection_request " ) , connecting_from_node , connecting_from_port_index , connecting_target_node , connecting_target_port_index ) ;
2022-08-12 13:06:08 +02:00
} else {
2024-01-18 16:16:17 +01:00
emit_signal ( SNAME ( " connection_request " ) , connecting_target_node , connecting_target_port_index , connecting_from_node , connecting_from_port_index ) ;
2020-07-04 07:00:17 +02:00
}
} else if ( ! just_disconnected ) {
2024-01-18 16:16:17 +01:00
if ( connecting_from_output ) {
emit_signal ( SNAME ( " connection_to_empty " ) , connecting_from_node , connecting_from_port_index , mb - > get_position ( ) ) ;
2020-07-04 07:00:17 +02:00
} else {
2024-01-18 16:16:17 +01:00
emit_signal ( SNAME ( " connection_from_empty " ) , connecting_from_node , connecting_from_port_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 ( ) ) {
2023-09-12 15:01:42 +02:00
Ref < Texture2D > resizer = p_graph_node - > theme_cache . resizer ;
2023-07-10 17:26:02 +02:00
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-09-12 15:01:42 +02:00
p_pos . x - ( p_left ? theme_cache . port_hotzone_outer_extent : theme_cache . port_hotzone_inner_extent ) ,
2023-02-23 05:40:27 +01:00
p_pos . y - p_port_size . height / 2.0 ,
2023-09-12 15:01:42 +02:00
theme_cache . port_hotzone_inner_extent + theme_cache . port_hotzone_outer_extent ,
2022-05-30 15:48:58 +02:00
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
}
2024-01-18 16:16:17 +01:00
PackedVector2Array GraphEdit : : get_connection_line ( const Vector2 & p_from , const Vector2 & p_to ) const {
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
2024-01-18 16:16:17 +01:00
Ref < GraphEdit : : Connection > GraphEdit : : get_closest_connection_at_point ( const Vector2 & p_point , float p_max_distance ) const {
Vector2 transformed_point = p_point + get_scroll_offset ( ) ;
Ref < GraphEdit : : Connection > closest_connection ;
float closest_distance = p_max_distance ;
for ( const Ref < Connection > & c : connections ) {
if ( c - > _cache . aabb . distance_to ( transformed_point ) > p_max_distance ) {
continue ;
}
Vector < Vector2 > points = get_connection_line ( c - > _cache . from_pos * zoom , c - > _cache . to_pos * zoom ) ;
for ( int i = 0 ; i < points . size ( ) - 1 ; i + + ) {
float distance = Geometry2D : : get_distance_to_segment ( transformed_point , & points [ i ] ) ;
if ( distance < = lines_thickness * 0.5 + p_max_distance & & distance < closest_distance ) {
closest_connection = c ;
closest_distance = distance ;
}
}
2020-07-19 19:11:02 +02:00
}
2017-07-01 02:30:17 +02:00
2024-01-18 16:16:17 +01:00
return closest_connection ;
2015-01-03 20:52:37 +01:00
}
2024-01-18 16:16:17 +01:00
List < Ref < GraphEdit : : Connection > > GraphEdit : : get_connections_intersecting_with_rect ( const Rect2 & p_rect ) const {
Rect2 transformed_rect = p_rect ;
transformed_rect . position + = get_scroll_offset ( ) ;
2016-08-31 04:44:14 +02:00
2024-01-18 16:16:17 +01:00
List < Ref < Connection > > intersecting_connections ;
for ( const Ref < Connection > & c : connections ) {
if ( ! c - > _cache . aabb . intersects ( transformed_rect ) ) {
2017-08-27 21:07:15 +02:00
continue ;
}
2016-08-31 04:44:14 +02:00
2024-01-18 16:16:17 +01:00
Vector < Vector2 > points = get_connection_line ( c - > _cache . from_pos * zoom , c - > _cache . to_pos * zoom ) ;
for ( int i = 0 ; i < points . size ( ) - 1 ; i + + ) {
if ( Geometry2D : : segment_intersects_rect ( points [ i ] , points [ i + 1 ] , transformed_rect ) ) {
intersecting_connections . push_back ( c ) ;
break ;
}
}
}
return intersecting_connections ;
}
void GraphEdit : : _draw_minimap_connection_line ( CanvasItem * p_where , const Vector2 & p_from , const Vector2 & p_to , const Color & p_from_color , const Color & p_to_color ) {
const Vector < Vector2 > & points = get_connection_line ( p_from , p_to ) ;
LocalVector < Color > colors ;
colors . reserve ( points . size ( ) ) ;
float length_inv = 1.0 / ( p_from ) . distance_to ( p_to ) ;
for ( const Vector2 & point : points ) {
float normalized_curve_position = ( p_from ) . distance_to ( point ) * length_inv ;
colors . push_back ( p_from_color . lerp ( p_to_color , normalized_curve_position ) ) ;
}
p_where - > draw_polyline_colors ( points , colors , 0.5 , lines_antialiased ) ;
}
void GraphEdit : : _update_connections ( ) {
// Collect all dead connections and remove them.
List < List < Ref < Connection > > : : Element * > dead_connections ;
for ( List < Ref < Connection > > : : Element * E = connections . front ( ) ; E ; E = E - > next ( ) ) {
Ref < Connection > & c = E - > get ( ) ;
if ( c - > _cache . dirty ) {
Node * from = get_node_or_null ( NodePath ( c - > from_node ) ) ;
GraphNode * gnode_from = Object : : cast_to < GraphNode > ( from ) ;
if ( ! gnode_from ) {
dead_connections . push_back ( E ) ;
continue ;
}
Node * to = get_node_or_null ( NodePath ( c - > to_node ) ) ;
GraphNode * gnode_to = Object : : cast_to < GraphNode > ( to ) ;
if ( ! gnode_to ) {
dead_connections . push_back ( E ) ;
continue ;
}
2016-08-31 04:44:14 +02:00
2024-01-18 16:16:17 +01:00
const Vector2 from_pos = gnode_from - > get_output_port_position ( c - > from_port ) + gnode_from - > get_position_offset ( ) ;
const Vector2 to_pos = gnode_to - > get_input_port_position ( c - > to_port ) + gnode_to - > get_position_offset ( ) ;
const Color from_color = gnode_from - > get_output_port_color ( c - > from_port ) ;
const Color to_color = gnode_to - > get_input_port_color ( c - > to_port ) ;
const int from_type = gnode_from - > get_output_port_type ( c - > from_port ) ;
const int to_type = gnode_to - > get_input_port_type ( c - > to_port ) ;
c - > _cache . from_pos = from_pos ;
c - > _cache . to_pos = to_pos ;
c - > _cache . from_color = from_color ;
c - > _cache . to_color = to_color ;
PackedVector2Array line_points = get_connection_line ( from_pos * zoom , to_pos * zoom ) ;
c - > _cache . line - > set_points ( line_points ) ;
Ref < ShaderMaterial > line_material = c - > _cache . line - > get_material ( ) ;
if ( line_material . is_null ( ) ) {
line_material . instantiate ( ) ;
c - > _cache . line - > set_material ( line_material ) ;
}
float line_width = _get_shader_line_width ( ) ;
line_material - > set_shader_parameter ( " line_width " , line_width ) ;
line_material - > set_shader_parameter ( " from_type " , from_type ) ;
line_material - > set_shader_parameter ( " to_type " , to_type ) ;
line_material - > set_shader_parameter ( " rim_color " , theme_cache . connection_rim_color ) ;
// Compute bounding box of the line, including the line width.
c - > _cache . aabb = Rect2 ( line_points [ 0 ] , Vector2 ( ) ) ;
for ( int i = 0 ; i < line_points . size ( ) ; i + + ) {
c - > _cache . aabb . expand_to ( line_points [ i ] ) ;
}
c - > _cache . aabb . grow_by ( lines_thickness * 0.5 ) ;
c - > _cache . dirty = false ;
}
// Skip updating/drawing connections that are not visible.
Rect2 viewport_rect = get_viewport_rect ( ) ;
viewport_rect . position + = get_scroll_offset ( ) ;
if ( ! c - > _cache . aabb . intersects ( viewport_rect ) ) {
2017-08-27 21:07:15 +02:00
continue ;
2016-08-31 04:44:14 +02:00
}
2024-01-18 16:16:17 +01:00
Color from_color = c - > _cache . from_color ;
Color to_color = c - > _cache . to_color ;
if ( c - > activity > 0 ) {
from_color = from_color . lerp ( theme_cache . activity_color , c - > activity ) ;
to_color = to_color . lerp ( theme_cache . activity_color , c - > activity ) ;
}
2018-06-19 03:10:48 +02:00
2024-01-18 16:16:17 +01:00
if ( c = = hovered_connection ) {
from_color = from_color . blend ( theme_cache . connection_hover_tint_color ) ;
to_color = to_color . blend ( theme_cache . connection_hover_tint_color ) ;
2018-06-19 03:10:48 +02:00
}
2024-01-18 16:16:17 +01:00
// Update Line2D node.
Ref < Gradient > line_gradient = memnew ( Gradient ) ;
float line_width = _get_shader_line_width ( ) ;
c - > _cache . line - > set_width ( line_width ) ;
line_gradient - > set_color ( 0 , from_color ) ;
line_gradient - > set_color ( 1 , to_color ) ;
c - > _cache . line - > set_gradient ( line_gradient ) ;
2017-08-27 21:07:15 +02:00
}
2024-01-18 16:16:17 +01:00
for ( const List < Ref < Connection > > : : Element * E : dead_connections ) {
List < Ref < Connection > > & connections_from = connection_map [ E - > get ( ) - > from_node ] ;
List < Ref < Connection > > & connections_to = connection_map [ E - > get ( ) - > to_node ] ;
connections_from . erase ( E - > get ( ) ) ;
connections_to . erase ( E - > get ( ) ) ;
E - > get ( ) - > _cache . line - > queue_free ( ) ;
connections . erase ( E - > get ( ) ) ;
2016-08-31 04:44:14 +02:00
}
}
2015-01-03 20:52:37 +01:00
void GraphEdit : : _top_layer_draw ( ) {
2024-01-18 16:16:17 +01:00
if ( ! box_selecting ) {
return ;
}
top_layer - > draw_rect ( box_selecting_rect , theme_cache . selection_fill ) ;
top_layer - > draw_rect ( box_selecting_rect , theme_cache . selection_stroke , false ) ;
}
void GraphEdit : : _update_top_connection_layer ( ) {
2015-07-21 03:15:06 +02:00
_update_scroll ( ) ;
2024-01-18 16:16:17 +01:00
if ( ! connecting ) {
dragged_connection_line - > clear_points ( ) ;
2015-07-21 03:15:06 +02:00
2024-01-18 16:16:17 +01:00
return ;
}
2015-07-21 03:15:06 +02:00
2024-01-18 16:16:17 +01:00
GraphNode * graph_node_from = Object : : cast_to < GraphNode > ( get_node_or_null ( NodePath ( connecting_from_node ) ) ) ;
ERR_FAIL_NULL ( graph_node_from ) ;
2016-08-25 22:45:20 +02:00
2024-01-18 16:16:17 +01:00
Vector2 from_pos = graph_node_from - > get_position ( ) / zoom ;
Vector2 to_pos = connecting_to_point / zoom ;
int from_type ;
int to_type = connecting_type ;
Color from_color ;
Color to_color = connecting_color ;
if ( connecting_from_output ) {
from_pos + = graph_node_from - > get_output_port_position ( connecting_from_port_index ) ;
from_type = graph_node_from - > get_output_port_type ( connecting_from_port_index ) ;
from_color = graph_node_from - > get_output_port_color ( connecting_from_port_index ) ;
} else {
from_pos + = graph_node_from - > get_input_port_position ( connecting_from_port_index ) ;
from_type = graph_node_from - > get_input_port_type ( connecting_from_port_index ) ;
from_color = graph_node_from - > get_input_port_color ( connecting_from_port_index ) ;
}
if ( connecting_target_valid ) {
GraphNode * graph_node_to = Object : : cast_to < GraphNode > ( get_node_or_null ( NodePath ( connecting_target_node ) ) ) ;
ERR_FAIL_NULL ( graph_node_to ) ;
if ( connecting_from_output ) {
to_type = graph_node_to - > get_input_port_type ( connecting_target_port_index ) ;
to_color = graph_node_to - > get_input_port_color ( connecting_target_port_index ) ;
} else {
to_type = graph_node_to - > get_output_port_type ( connecting_target_port_index ) ;
to_color = graph_node_to - > get_output_port_color ( connecting_target_port_index ) ;
2016-08-25 22:45:20 +02:00
}
2024-01-18 16:16:17 +01:00
// Highlight the line to the mouse cursor when it's over a valid target port.
from_color = from_color . blend ( theme_cache . connection_valid_target_tint_color ) ;
to_color = to_color . blend ( theme_cache . connection_valid_target_tint_color ) ;
}
if ( ! connecting_from_output ) {
SWAP ( from_pos , to_pos ) ;
SWAP ( from_type , to_type ) ;
SWAP ( from_color , to_color ) ;
2015-07-21 03:15:06 +02:00
}
2024-01-18 16:16:17 +01:00
PackedVector2Array line_points = get_connection_line ( from_pos * zoom , to_pos * zoom ) ;
dragged_connection_line - > set_points ( line_points ) ;
Ref < ShaderMaterial > line_material = dragged_connection_line - > get_material ( ) ;
if ( line_material . is_null ( ) ) {
line_material . instantiate ( ) ;
line_material - > set_shader ( connections_shader ) ;
dragged_connection_line - > set_material ( line_material ) ;
2019-08-15 22:17:08 +02:00
}
2024-01-18 16:16:17 +01:00
float line_width = _get_shader_line_width ( ) ;
line_material - > set_shader_parameter ( " line_width " , line_width ) ;
line_material - > set_shader_parameter ( " from_type " , from_type ) ;
line_material - > set_shader_parameter ( " to_type " , to_type ) ;
line_material - > set_shader_parameter ( " rim_color " , theme_cache . connection_rim_color ) ;
Ref < Gradient > line_gradient = memnew ( Gradient ) ;
dragged_connection_line - > set_width ( line_width ) ;
line_gradient - > set_color ( 0 , from_color ) ;
line_gradient - > set_color ( 1 , to_color ) ;
dragged_connection_line - > set_gradient ( line_gradient ) ;
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-09-12 15:01:42 +02:00
minimap - > draw_style_box ( minimap - > theme_cache . 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 ) ;
2023-09-12 15:01:42 +02:00
Ref < StyleBoxFlat > sb_minimap = minimap - > theme_cache . node_style - > duplicate ( ) ;
2020-11-06 20:16:45 +01:00
// Override default values with colors provided by the GraphNode's stylebox, if possible.
2023-09-12 15:01:42 +02:00
Ref < StyleBoxFlat > sb_frame = graph_node - > is_selected ( ) ? graph_node - > theme_cache . panel_selected : graph_node - > theme_cache . 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.
2024-01-18 16:16:17 +01:00
for ( const Ref < Connection > & c : connections ) {
Vector2 from_position = minimap - > _convert_from_graph_position ( c - > _cache . from_pos * zoom - graph_offset ) + minimap_offset ;
Vector2 to_position = minimap - > _convert_from_graph_position ( c - > _cache . to_pos * zoom - graph_offset ) + minimap_offset ;
Color from_color = c - > _cache . from_color ;
Color to_color = c - > _cache . to_color ;
if ( c - > activity > 0 ) {
from_color = from_color . lerp ( theme_cache . activity_color , c - > activity ) ;
to_color = to_color . lerp ( theme_cache . activity_color , c - > activity ) ;
2020-11-06 20:16:45 +01:00
}
2024-01-18 16:16:17 +01:00
_draw_minimap_connection_line ( minimap , from_position , to_position , from_color , to_color ) ;
2020-11-06 20:16:45 +01:00
}
// Draw the "camera" viewport.
Rect2 camera_rect = minimap - > get_camera_rect ( ) ;
2023-09-12 15:01:42 +02:00
minimap - > draw_style_box ( minimap - > theme_cache . camera_style , camera_rect ) ;
2020-11-06 20:16:45 +01:00
// Draw the resizer control.
2023-09-12 15:01:42 +02:00
Ref < Texture2D > resizer = minimap - > theme_cache . resizer ;
Color resizer_color = minimap - > theme_cache . resizer_color ;
2020-11-06 20:16:45 +01:00
minimap - > draw_texture ( resizer , Point2 ( ) , resizer_color ) ;
}
2023-10-22 18:01:03 +02:00
void GraphEdit : : _draw_grid ( ) {
Vector2 offset = get_scroll_offset ( ) / zoom ;
Size2 size = get_size ( ) / zoom ;
Point2i from_pos = ( offset / float ( snapping_distance ) ) . floor ( ) ;
Point2i len = ( size / float ( snapping_distance ) ) . floor ( ) + Vector2 ( 1 , 1 ) ;
switch ( grid_pattern ) {
case GRID_PATTERN_LINES : {
for ( int i = from_pos . x ; i < from_pos . x + len . x ; i + + ) {
Color color ;
if ( ABS ( i ) % GRID_MINOR_STEPS_PER_MAJOR_LINE = = 0 ) {
color = theme_cache . grid_major ;
} else {
color = theme_cache . grid_minor ;
}
float base_offset = i * snapping_distance * zoom - offset . x * zoom ;
draw_line ( Vector2 ( base_offset , 0 ) , Vector2 ( base_offset , get_size ( ) . height ) , color ) ;
}
for ( int i = from_pos . y ; i < from_pos . y + len . y ; i + + ) {
Color color ;
if ( ABS ( i ) % GRID_MINOR_STEPS_PER_MAJOR_LINE = = 0 ) {
color = theme_cache . grid_major ;
} else {
color = theme_cache . grid_minor ;
}
float base_offset = i * snapping_distance * zoom - offset . y * zoom ;
draw_line ( Vector2 ( 0 , base_offset ) , Vector2 ( get_size ( ) . width , base_offset ) , color ) ;
}
} break ;
case GRID_PATTERN_DOTS : {
Color transparent_grid_minor = theme_cache . grid_minor ;
transparent_grid_minor . a * = CLAMP ( 2 * ( zoom - 0.4 ) , 0 , 1 ) ;
for ( int i = from_pos . x ; i < from_pos . x + len . x ; i + + ) {
for ( int j = from_pos . y ; j < from_pos . y + len . y ; j + + ) {
Color color = transparent_grid_minor ;
if ( ABS ( i ) % GRID_MINOR_STEPS_PER_MAJOR_LINE = = 0 & & ABS ( j ) % GRID_MINOR_STEPS_PER_MAJOR_LINE = = 0 ) {
color = theme_cache . grid_major ;
}
if ( color . a = = 0 ) {
continue ;
}
float base_offset_x = i * snapping_distance * zoom - offset . x * zoom ;
float base_offset_y = j * snapping_distance * zoom - offset . y * zoom ;
draw_rect ( Rect2 ( base_offset_x - 1 , base_offset_y - 1 , 3 , 3 ) , color ) ;
}
}
} break ;
}
}
2017-03-05 16:44:50 +01: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
2024-01-18 16:16:17 +01:00
// Highlight the connection close to the mouse cursor.
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseMotion > mm = p_ev ;
2024-01-18 16:16:17 +01:00
if ( mm . is_valid ( ) ) {
Ref < Connection > new_highlighted_connection = get_closest_connection_at_point ( mm - > get_position ( ) ) ;
if ( new_highlighted_connection ! = hovered_connection ) {
connections_layer - > queue_redraw ( ) ;
}
hovered_connection = new_highlighted_connection ;
}
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 ;
}
2017-03-05 16:44:50 +01:00
just_selected = true ;
2020-05-04 20:36:52 +02:00
drag_accum + = mm - > get_relative ( ) ;
2017-03-05 16:44:50 +01: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-06-08 23:24:00 +02:00
if ( snapping_enabled ^ Input : : get_singleton ( ) - > is_key_pressed ( Key : : CMD_OR_CTRL ) ) {
2023-07-10 17:26:02 +02:00
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
2024-01-18 16:16:17 +01:00
// Box selection logic.
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
2017-03-05 16:44:50 +01: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 ;
2017-03-05 16:44:50 +01: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
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 ) {
2023-06-08 23:24:00 +02:00
if ( ! just_selected & & drag_accum = = Vector2 ( ) & & Input : : get_singleton ( ) - > is_key_pressed ( Key : : CMD_OR_CTRL ) ) {
2023-07-10 17:26:02 +02:00
// Deselect current node.
2017-03-05 16:44:50 +01: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
2017-03-05 16:44:50 +01: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
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2024-01-18 16:16:17 +01:00
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
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.
2017-03-05 16:44:50 +01: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 ( ) ;
2023-06-08 23:24:00 +02:00
if ( ! graph_element - > is_selected ( ) & & ! Input : : get_singleton ( ) - > is_key_pressed ( Key : : CMD_OR_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 ( ) ;
2023-06-08 23:24:00 +02:00
if ( mb - > is_command_or_control_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 ( ) ;
2017-03-05 16:44:50 +01: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 ( ) ;
2017-03-05 16:44:50 +01: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 ( ) ;
2017-03-05 16:44:50 +01: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-09-26 16:41:39 +02:00
emit_signal ( SNAME ( " delete_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 ) ;
2024-01-18 16:16:17 +01:00
connections_layer - > queue_redraw ( ) ;
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 ) {
2024-01-18 16:16:17 +01:00
// We need to invalidate all connections since we don't know whether
// the user is zooming/panning at the same time.
_invalidate_connection_line_cache ( ) ;
2023-01-19 17:50:51 +01:00
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 ) {
2024-01-18 16:16:17 +01:00
for ( Ref < Connection > & c : connection_map [ p_from ] ) {
if ( c - > from_node = = p_from & & c - > from_port = = p_from_port & & c - > to_node = = p_to & & c - > to_port = = p_to_port ) {
if ( ! Math : : is_equal_approx ( c - > activity , p_activity ) ) {
2023-07-10 17:26:02 +02:00
// Update only if changed.
2022-08-13 23:21:24 +02:00
minimap - > queue_redraw ( ) ;
2024-01-18 16:16:17 +01:00
c - > _cache . dirty = true ;
2022-08-13 23:21:24 +02:00
connections_layer - > queue_redraw ( ) ;
2024-01-18 16:16:17 +01:00
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
2018-06-19 03:10:48 +02:00
}
2024-01-18 16:16:17 +01:00
c - > activity = p_activity ;
2018-06-19 03:10:48 +02:00
return ;
}
}
}
2024-01-18 16:16:17 +01:00
void GraphEdit : : reset_all_connection_activity ( ) {
bool changed = false ;
for ( Ref < Connection > & conn : connections ) {
if ( conn - > activity > 0 ) {
changed = true ;
conn - > _cache . dirty = true ;
}
conn - > activity = 0 ;
}
if ( changed ) {
connections_layer - > queue_redraw ( ) ;
}
}
2015-01-03 20:52:37 +01:00
void GraphEdit : : clear_connections ( ) {
2024-01-18 16:16:17 +01:00
for ( Ref < Connection > & c : connections ) {
c - > _cache . line - > queue_free ( ) ;
}
2015-07-21 03:15:06 +02:00
connections . clear ( ) ;
2024-01-18 16:16:17 +01:00
connection_map . 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
minimap - > queue_redraw ( ) ;
queue_redraw ( ) ;
connections_layer - > queue_redraw ( ) ;
2024-01-18 16:16:17 +01:00
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
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 ;
2024-01-18 16:16:17 +01:00
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
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 ;
}
2015-01-07 05:45:46 +01:00
void GraphEdit : : set_right_disconnects ( bool p_enable ) {
2017-03-05 16:44:50 +01:00
right_disconnects = p_enable ;
2015-01-07 05:45:46 +01:00
}
2017-03-05 16:44:50 +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 ) ;
}
2017-03-05 16:44:50 +01:00
void GraphEdit : : remove_valid_right_disconnect_type ( int p_type ) {
2016-08-03 00:11:05 +02:00
valid_right_disconnect_types . erase ( p_type ) ;
}
2017-03-05 16:44:50 +01:00
void GraphEdit : : add_valid_left_disconnect_type ( int p_type ) {
2016-08-03 00:11:05 +02:00
valid_left_disconnect_types . insert ( p_type ) ;
}
2017-03-05 16:44:50 +01:00
void GraphEdit : : remove_valid_left_disconnect_type ( int p_type ) {
2016-08-03 00:11:05 +02:00
valid_left_disconnect_types . erase ( p_type ) ;
}
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > GraphEdit : : _get_connection_list ( ) const {
2024-01-18 16:16:17 +01:00
List < Ref < Connection > > conns = get_connection_list ( ) ;
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > arr ;
2024-01-18 16:16:17 +01:00
for ( const Ref < Connection > & conn : conns ) {
2015-07-21 03:15:06 +02:00
Dictionary d ;
2024-01-18 16:16:17 +01:00
d [ " from_node " ] = conn - > from_node ;
d [ " from_port " ] = conn - > from_port ;
d [ " to_node " ] = conn - > to_node ;
d [ " to_port " ] = conn - > to_port ;
arr . push_back ( d ) ;
}
return arr ;
}
Dictionary GraphEdit : : _get_closest_connection_at_point ( const Vector2 & p_point , float p_max_distance ) const {
Dictionary ret ;
Ref < Connection > c = get_closest_connection_at_point ( p_point , p_max_distance ) ;
if ( c . is_valid ( ) ) {
ret [ " from_node " ] = c - > from_node ;
ret [ " from_port " ] = c - > from_port ;
ret [ " to_node " ] = c - > to_node ;
ret [ " to_port " ] = c - > to_port ;
}
return ret ;
}
TypedArray < Dictionary > GraphEdit : : _get_connections_intersecting_with_rect ( const Rect2 & p_rect ) const {
List < Ref < Connection > > intersecting_connections = get_connections_intersecting_with_rect ( p_rect ) ;
TypedArray < Dictionary > arr ;
for ( const Ref < Connection > & conn : intersecting_connections ) {
Dictionary d ;
d [ " from_node " ] = conn - > from_node ;
d [ " from_port " ] = conn - > from_port ;
d [ " to_node " ] = conn - > to_node ;
d [ " to_port " ] = conn - > 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
}
2024-01-18 16:16:17 +01:00
void GraphEdit : : _invalidate_connection_line_cache ( ) {
for ( Ref < Connection > & c : connections ) {
c - > _cache . dirty = true ;
}
}
float GraphEdit : : _get_shader_line_width ( ) {
return lines_thickness * theme_cache . base_scale + 4.0 ;
}
2017-03-05 16:44:50 +01: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 ) ;
}
2017-03-05 16:44:50 +01:00
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 ) ;
}
2017-03-05 16:44:50 +01:00
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 ;
2023-10-02 13:43:08 +02:00
toggle_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 ;
}
2023-10-22 18:01:03 +02:00
void GraphEdit : : set_grid_pattern ( GridPattern p_pattern ) {
if ( grid_pattern = = p_pattern ) {
return ;
}
grid_pattern = p_pattern ;
queue_redraw ( ) ;
}
GraphEdit : : GridPattern GraphEdit : : get_grid_pattern ( ) const {
return grid_pattern ;
}
2023-07-10 17:26:02 +02:00
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 ( ) {
2023-10-02 13:43:08 +02:00
show_grid = toggle_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 ( ) ;
}
2023-10-02 13:43:08 +02:00
void GraphEdit : : set_show_menu ( bool p_hidden ) {
show_menu = p_hidden ;
menu_panel - > set_visible ( show_menu ) ;
}
bool GraphEdit : : is_showing_menu ( ) const {
return show_menu ;
}
void GraphEdit : : set_show_zoom_label ( bool p_hidden ) {
show_zoom_label = p_hidden ;
zoom_label - > set_visible ( show_zoom_label ) ;
}
bool GraphEdit : : is_showing_zoom_label ( ) const {
return show_zoom_label ;
}
void GraphEdit : : set_show_zoom_buttons ( bool p_hidden ) {
show_zoom_buttons = p_hidden ;
zoom_minus_button - > set_visible ( show_zoom_buttons ) ;
zoom_reset_button - > set_visible ( show_zoom_buttons ) ;
zoom_plus_button - > set_visible ( show_zoom_buttons ) ;
}
bool GraphEdit : : is_showing_zoom_buttons ( ) const {
return show_zoom_buttons ;
}
void GraphEdit : : set_show_grid_buttons ( bool p_hidden ) {
show_grid_buttons = p_hidden ;
toggle_grid_button - > set_visible ( show_grid_buttons ) ;
toggle_snapping_button - > set_visible ( show_grid_buttons ) ;
snapping_distance_spinbox - > set_visible ( show_grid_buttons ) ;
2022-05-04 07:31:53 +02:00
}
2023-10-02 13:43:08 +02:00
bool GraphEdit : : is_showing_grid_buttons ( ) const {
return show_grid_buttons ;
}
void GraphEdit : : set_show_minimap_button ( bool p_hidden ) {
show_minimap_button = p_hidden ;
minimap_button - > set_visible ( show_minimap_button ) ;
}
bool GraphEdit : : is_showing_minimap_button ( ) const {
return show_minimap_button ;
}
void GraphEdit : : set_show_arrange_button ( bool p_hidden ) {
show_arrange_button = p_hidden ;
arrange_button - > set_visible ( show_arrange_button ) ;
}
bool GraphEdit : : is_showing_arrange_button ( ) const {
return show_arrange_button ;
2022-05-04 07:31:53 +02:00
}
2024-01-18 16:16:17 +01:00
void GraphEdit : : override_connections_shader ( const Ref < Shader > & p_shader ) {
connections_shader = p_shader ;
_invalidate_connection_line_cache ( ) ;
connections_layer - > queue_redraw ( ) ;
minimap - > queue_redraw ( ) ;
callable_mp ( this , & GraphEdit : : _update_top_connection_layer ) . call_deferred ( ) ;
}
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 ;
2024-01-18 16:16:17 +01:00
_invalidate_connection_line_cache ( ) ;
connections_layer - > queue_redraw ( ) ;
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 ) {
2024-01-18 16:16:17 +01:00
ERR_FAIL_COND_MSG ( p_thickness < 0 , " Connection lines thickness must be greater than or equal to 0. " ) ;
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 ;
2024-01-18 16:16:17 +01:00
_invalidate_connection_line_cache ( ) ;
connections_layer - > queue_redraw ( ) ;
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 ;
2024-01-18 16:16:17 +01:00
_invalidate_connection_line_cache ( ) ;
connections_layer - > queue_redraw ( ) ;
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-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_connection_list " ) , & GraphEdit : : _get_connection_list ) ;
2024-01-18 16:16:17 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_closest_connection_at_point " , " point " , " max_distance " ) , & GraphEdit : : _get_closest_connection_at_point , DEFVAL ( 4.0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_connections_intersecting_with_rect " , " rect " ) , & GraphEdit : : _get_connections_intersecting_with_rect ) ;
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-03-05 16:44:50 +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 ) ;
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 ) ;
2023-10-22 18:01:03 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_grid_pattern " , " pattern " ) , & GraphEdit : : set_grid_pattern ) ;
ClassDB : : bind_method ( D_METHOD ( " get_grid_pattern " ) , & GraphEdit : : get_grid_pattern ) ;
2023-07-10 17:26:02 +02:00
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 ) ;
2023-10-02 13:43:08 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_show_menu " , " hidden " ) , & GraphEdit : : set_show_menu ) ;
ClassDB : : bind_method ( D_METHOD ( " is_showing_menu " ) , & GraphEdit : : is_showing_menu ) ;
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 ) ;
ClassDB : : bind_method ( D_METHOD ( " set_show_grid_buttons " , " hidden " ) , & GraphEdit : : set_show_grid_buttons ) ;
ClassDB : : bind_method ( D_METHOD ( " is_showing_grid_buttons " ) , & GraphEdit : : is_showing_grid_buttons ) ;
ClassDB : : bind_method ( D_METHOD ( " set_show_zoom_buttons " , " hidden " ) , & GraphEdit : : set_show_zoom_buttons ) ;
ClassDB : : bind_method ( D_METHOD ( " is_showing_zoom_buttons " ) , & GraphEdit : : is_showing_zoom_buttons ) ;
ClassDB : : bind_method ( D_METHOD ( " set_show_minimap_button " , " hidden " ) , & GraphEdit : : set_show_minimap_button ) ;
ClassDB : : bind_method ( D_METHOD ( " is_showing_minimap_button " ) , & GraphEdit : : is_showing_minimap_button ) ;
ClassDB : : bind_method ( D_METHOD ( " set_show_arrange_button " , " hidden " ) , & GraphEdit : : set_show_arrange_button ) ;
ClassDB : : bind_method ( D_METHOD ( " is_showing_arrange_button " ) , & GraphEdit : : is_showing_arrange_button ) ;
2022-05-04 07:31:53 +02:00
2017-03-05 16:44:50 +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
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 " ) ;
2023-10-22 18:01:03 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " grid_pattern " , PROPERTY_HINT_ENUM , " Lines,Dots " ) , " set_grid_pattern " , " get_grid_pattern " ) ;
2023-07-10 17:26:02 +02:00
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 " ) ;
2023-10-02 13:43:08 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " right_disconnects " ) , " set_right_disconnects " , " is_right_disconnects_enabled " ) ;
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 " ) ;
2024-01-18 16:16:17 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " connection_lines_thickness " , PROPERTY_HINT_RANGE , " 0,100,0.1,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 " ) ;
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
2023-10-02 13:43:08 +02:00
ADD_GROUP ( " Toolbar Menu " , " " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " show_menu " ) , " set_show_menu " , " is_showing_menu " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " show_zoom_label " ) , " set_show_zoom_label " , " is_showing_zoom_label " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " show_zoom_buttons " ) , " set_show_zoom_buttons " , " is_showing_zoom_buttons " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " show_grid_buttons " ) , " set_show_grid_buttons " , " is_showing_grid_buttons " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " show_minimap_button " ) , " set_show_minimap_button " , " is_showing_minimap_button " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " show_arrange_button " ) , " set_show_arrange_button " , " is_showing_arrange_button " ) ;
2022-05-04 07:31:53 +02:00
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 " ) ) ) ;
2023-09-26 16:41:39 +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 " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " connection_drag_started " , PropertyInfo ( Variant : : STRING_NAME , " from_node " ) , PropertyInfo ( Variant : : INT , " from_port " ) , PropertyInfo ( Variant : : BOOL , " is_output " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " connection_drag_ended " ) ) ;
2019-07-12 19:36:33 +02:00
ADD_SIGNAL ( MethodInfo ( " copy_nodes_request " ) ) ;
ADD_SIGNAL ( MethodInfo ( " paste_nodes_request " ) ) ;
2023-09-26 16:41:39 +02:00
ADD_SIGNAL ( MethodInfo ( " duplicate_nodes_request " ) ) ;
ADD_SIGNAL ( MethodInfo ( " delete_nodes_request " , PropertyInfo ( Variant : : ARRAY , " nodes " , PROPERTY_HINT_ARRAY_TYPE , " StringName " ) ) ) ;
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 " ) ) ) ;
2023-09-26 16:41:39 +02:00
ADD_SIGNAL ( MethodInfo ( " popup_request " , PropertyInfo ( Variant : : VECTOR2 , " position " ) ) ) ;
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 " ) ) ) ;
2021-09-28 18:00:16 +02:00
BIND_ENUM_CONSTANT ( SCROLL_ZOOMS ) ;
BIND_ENUM_CONSTANT ( SCROLL_PANS ) ;
2023-09-12 15:01:42 +02:00
2023-10-22 18:01:03 +02:00
BIND_ENUM_CONSTANT ( GRID_PATTERN_LINES ) ;
BIND_ENUM_CONSTANT ( GRID_PATTERN_DOTS ) ;
2023-09-12 15:01:42 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_STYLEBOX , GraphEdit , panel ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , GraphEdit , grid_major ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , GraphEdit , grid_minor ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_COLOR , GraphEdit , activity_color , " activity " ) ;
2024-01-18 16:16:17 +01:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , GraphEdit , connection_hover_tint_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , GraphEdit , connection_valid_target_tint_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , GraphEdit , connection_rim_color ) ;
2023-09-12 15:01:42 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , GraphEdit , selection_fill ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , GraphEdit , selection_stroke ) ;
2023-10-02 13:43:08 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_STYLEBOX , GraphEdit , menu_panel ) ;
2023-09-12 15:01:42 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , GraphEdit , zoom_in ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , GraphEdit , zoom_out ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , GraphEdit , zoom_reset ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , GraphEdit , snapping_toggle ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , GraphEdit , grid_toggle ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , GraphEdit , minimap_toggle ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , GraphEdit , layout ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , GraphEdit , port_hotzone_inner_extent ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , GraphEdit , port_hotzone_outer_extent ) ;
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
2024-01-18 16:16:17 +01:00
top_layer = memnew ( Control ) ;
2021-08-25 15:49:30 +02:00
add_child ( top_layer , false , INTERNAL_MODE_BACK ) ;
2024-01-18 16:16:17 +01:00
top_layer - > set_mouse_filter ( MOUSE_FILTER_IGNORE ) ;
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 ) ) ;
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
2017-03-05 16:44:50 +01:00
connections_layer = memnew ( Control ) ;
2023-11-17 13:29:39 +01:00
add_child ( connections_layer , false , INTERNAL_MODE_FRONT ) ;
2024-01-18 16:16:17 +01:00
connections_layer - > connect ( " draw " , callable_mp ( this , & GraphEdit : : _update_connections ) ) ;
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
2024-01-18 16:16:17 +01:00
top_connection_layer = memnew ( GraphEditFilter ( this ) ) ;
add_child ( top_connection_layer , false , INTERNAL_MODE_BACK ) ;
connections_shader = default_connections_shader ;
top_connection_layer - > set_mouse_filter ( MOUSE_FILTER_PASS ) ;
top_connection_layer - > set_anchors_and_offsets_preset ( Control : : PRESET_FULL_RECT ) ;
top_connection_layer - > connect ( " gui_input " , callable_mp ( this , & GraphEdit : : _top_connection_layer_input ) ) ;
dragged_connection_line = memnew ( Line2D ) ;
dragged_connection_line - > set_texture_mode ( Line2D : : LINE_TEXTURE_STRETCH ) ;
top_connection_layer - > add_child ( dragged_connection_line ) ;
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-10-02 13:43:08 +02:00
// Toolbar menu.
menu_panel = memnew ( PanelContainer ) ;
menu_panel - > set_visible ( show_menu ) ;
top_layer - > add_child ( menu_panel ) ;
menu_panel - > set_position ( Vector2 ( 10 , 10 ) ) ;
2023-07-10 17:26:02 +02:00
menu_hbox = memnew ( HBoxContainer ) ;
2023-10-02 13:43:08 +02:00
menu_panel - > add_child ( menu_hbox ) ;
// Zoom label and controls.
2016-02-08 20:28:12 +01:00
2021-06-16 15:14:25 +02:00
zoom_label = memnew ( Label ) ;
2023-10-02 13:43:08 +02:00
zoom_label - > set_visible ( show_zoom_label ) ;
2021-06-16 15:14:25 +02:00
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 ) ) ;
2023-10-02 13:43:08 +02:00
menu_hbox - > add_child ( zoom_label ) ;
2021-06-16 15:14:25 +02:00
_update_zoom_label ( ) ;
2023-07-10 17:26:02 +02:00
zoom_minus_button = memnew ( Button ) ;
2023-09-19 18:03:10 +02:00
zoom_minus_button - > set_theme_type_variation ( " FlatButton " ) ;
2023-10-02 13:43:08 +02:00
zoom_minus_button - > set_visible ( show_zoom_buttons ) ;
2023-07-10 17:26:02 +02:00
zoom_minus_button - > set_tooltip_text ( RTR ( " Zoom Out " ) ) ;
zoom_minus_button - > set_focus_mode ( FOCUS_NONE ) ;
2023-10-02 13:43:08 +02:00
menu_hbox - > add_child ( zoom_minus_button ) ;
zoom_minus_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : _zoom_minus ) ) ;
2023-07-10 17:26:02 +02:00
zoom_reset_button = memnew ( Button ) ;
2023-09-19 18:03:10 +02:00
zoom_reset_button - > set_theme_type_variation ( " FlatButton " ) ;
2023-10-02 13:43:08 +02:00
zoom_reset_button - > set_visible ( show_zoom_buttons ) ;
2023-07-10 17:26:02 +02:00
zoom_reset_button - > set_tooltip_text ( RTR ( " Zoom Reset " ) ) ;
zoom_reset_button - > set_focus_mode ( FOCUS_NONE ) ;
2023-10-02 13:43:08 +02:00
menu_hbox - > add_child ( zoom_reset_button ) ;
zoom_reset_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : _zoom_reset ) ) ;
2023-07-10 17:26:02 +02:00
zoom_plus_button = memnew ( Button ) ;
2023-09-19 18:03:10 +02:00
zoom_plus_button - > set_theme_type_variation ( " FlatButton " ) ;
2023-10-02 13:43:08 +02:00
zoom_plus_button - > set_visible ( show_zoom_buttons ) ;
2023-07-10 17:26:02 +02:00
zoom_plus_button - > set_tooltip_text ( RTR ( " Zoom In " ) ) ;
zoom_plus_button - > set_focus_mode ( FOCUS_NONE ) ;
2023-10-02 13:43:08 +02:00
menu_hbox - > add_child ( zoom_plus_button ) ;
zoom_plus_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : _zoom_plus ) ) ;
2023-07-10 17:26:02 +02:00
2023-10-02 13:43:08 +02:00
// Grid controls.
toggle_grid_button = memnew ( Button ) ;
toggle_grid_button - > set_theme_type_variation ( " FlatButton " ) ;
toggle_grid_button - > set_visible ( show_grid_buttons ) ;
toggle_grid_button - > set_toggle_mode ( true ) ;
toggle_grid_button - > set_pressed ( true ) ;
toggle_grid_button - > set_tooltip_text ( RTR ( " Toggle the visual grid. " ) ) ;
toggle_grid_button - > set_focus_mode ( FOCUS_NONE ) ;
menu_hbox - > add_child ( toggle_grid_button ) ;
toggle_grid_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : _show_grid_toggled ) ) ;
2023-07-10 17:26:02 +02:00
toggle_snapping_button = memnew ( Button ) ;
2023-09-19 18:03:10 +02:00
toggle_snapping_button - > set_theme_type_variation ( " FlatButton " ) ;
2023-10-02 13:43:08 +02:00
toggle_snapping_button - > set_visible ( show_grid_buttons ) ;
2023-07-10 17:26:02 +02:00
toggle_snapping_button - > set_toggle_mode ( true ) ;
toggle_snapping_button - > set_tooltip_text ( RTR ( " Toggle snapping to the grid. " ) ) ;
toggle_snapping_button - > set_pressed ( snapping_enabled ) ;
toggle_snapping_button - > set_focus_mode ( FOCUS_NONE ) ;
menu_hbox - > add_child ( toggle_snapping_button ) ;
2023-10-02 13:43:08 +02:00
toggle_snapping_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : _snapping_toggled ) ) ;
2023-07-10 17:26:02 +02:00
snapping_distance_spinbox = memnew ( SpinBox ) ;
2023-10-02 13:43:08 +02:00
snapping_distance_spinbox - > set_visible ( show_grid_buttons ) ;
2023-07-10 17:26:02 +02:00
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. " ) ) ;
menu_hbox - > add_child ( snapping_distance_spinbox ) ;
2023-10-02 13:43:08 +02:00
snapping_distance_spinbox - > connect ( " value_changed " , callable_mp ( this , & GraphEdit : : _snapping_distance_changed ) ) ;
// Extra controls.
2016-08-04 05:05:35 +02:00
2020-11-06 20:16:45 +01:00
minimap_button = memnew ( Button ) ;
2023-09-19 18:03:10 +02:00
minimap_button - > set_theme_type_variation ( " FlatButton " ) ;
2023-10-02 13:43:08 +02:00
minimap_button - > set_visible ( show_minimap_button ) ;
2020-11-06 20:16:45 +01:00
minimap_button - > set_toggle_mode ( true ) ;
2023-07-10 17:26:02 +02:00
minimap_button - > set_tooltip_text ( RTR ( " Toggle the graph minimap. " ) ) ;
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 ) ;
2023-10-02 13:43:08 +02:00
minimap_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : _minimap_toggled ) ) ;
arrange_button = memnew ( Button ) ;
arrange_button - > set_theme_type_variation ( " FlatButton " ) ;
arrange_button - > set_visible ( show_arrange_button ) ;
arrange_button - > connect ( " pressed " , callable_mp ( this , & GraphEdit : : arrange_nodes ) ) ;
arrange_button - > set_focus_mode ( FOCUS_NONE ) ;
menu_hbox - > add_child ( arrange_button ) ;
arrange_button - > set_tooltip_text ( RTR ( " Automatically arrange selected nodes. " ) ) ;
2020-11-06 20:16:45 +01:00
2023-10-02 13:43:08 +02:00
// Minimap.
2021-08-10 21:14:19 +02:00
2023-10-02 13:43:08 +02:00
const Vector2 minimap_size = Vector2 ( 240 , 160 ) ;
const 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
}