Clean up/refactor GraphNode and make it more flexible

Split GraphNode into GraphElement and GraphNode, add custom
titlebar, and adjust theming.
This commit is contained in:
Hendrik Brucker 2023-08-09 18:31:15 +02:00
parent 08c578c54c
commit 5afe78bd9c
26 changed files with 1600 additions and 1537 deletions

View file

@ -26,12 +26,12 @@
<param index="2" name="mouse_position" type="Vector2" />
<description>
Returns whether the [param mouse_position] is in the input hot zone.
By default, a hot zone is a [Rect2] positioned such that its center is at [param in_node].[method GraphNode.get_connection_input_position]([param in_port]) (For output's case, call [method GraphNode.get_connection_output_position] instead). The hot zone's width is twice the Theme Property [code]port_grab_distance_horizontal[/code], and its height is twice the [code]port_grab_distance_vertical[/code].
By default, a hot zone is a [Rect2] positioned such that its center is at [param in_node].[method GraphNode.get_input_port_position]([param in_port]) (For output's case, call [method GraphNode.get_output_port_position] instead). The hot zone's width is twice the Theme Property [code]port_grab_distance_horizontal[/code], and its height is twice the [code]port_grab_distance_vertical[/code].
Below is a sample code to help get started:
[codeblock]
func _is_in_input_hotzone(in_node, in_port, mouse_position):
var port_size: Vector2 = Vector2(get_theme_constant("port_grab_distance_horizontal"), get_theme_constant("port_grab_distance_vertical"))
var port_pos: Vector2 = in_node.get_position() + in_node.get_connection_input_position(in_port) - port_size / 2
var port_pos: Vector2 = in_node.get_position() + in_node.get_input_port_position(in_port) - port_size / 2
var rect = Rect2(port_pos, port_size)
return rect.has_point(mouse_position)
@ -49,7 +49,7 @@
[codeblock]
func _is_in_output_hotzone(in_node, in_port, mouse_position):
var port_size: Vector2 = Vector2(get_theme_constant("port_grab_distance_horizontal"), get_theme_constant("port_grab_distance_vertical"))
var port_pos: Vector2 = in_node.get_position() + in_node.get_connection_output_position(in_port) - port_size / 2
var port_pos: Vector2 = in_node.get_position() + in_node.get_output_port_position(in_port) - port_size / 2
var rect = Rect2(port_pos, port_size)
return rect.has_point(mouse_position)
@ -289,6 +289,12 @@
Emitted at the beginning of a GraphNode movement.
</description>
</signal>
<signal name="close_nodes_request">
<param index="0" name="nodes" type="StringName[]" />
<description>
Emitted when attempting to remove a GraphNode from the GraphEdit. Provides a list of node names to be removed (all selected nodes, excluding nodes without closing button).
</description>
</signal>
<signal name="connection_drag_ended">
<description>
Emitted at the end of a connection drag.
@ -332,12 +338,6 @@
Emitted when the user presses [kbd]Ctrl + C[/kbd].
</description>
</signal>
<signal name="delete_nodes_request">
<param index="0" name="nodes" type="StringName[]" />
<description>
Emitted when a GraphNode is attempted to be removed from the GraphEdit. Provides a list of node names to be removed (all selected nodes, excluding nodes without closing button).
</description>
</signal>
<signal name="disconnection_request">
<param index="0" name="from_node" type="StringName" />
<param index="1" name="from_port" type="int" />

View file

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GraphElement" inherits="Container" is_experimental="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A container that represents a basic element that can be placed inside a [GraphEdit] control.
</brief_description>
<description>
[GraphElement] allows to create custom elements for a [GraphEdit] graph. By default such elements can be selected, resized, and repositioned, but they cannot be connected. For a graph element that allows for connections see [GraphNode].
</description>
<tutorials>
</tutorials>
<members>
<member name="draggable" type="bool" setter="set_draggable" getter="is_draggable" default="true">
If [code]true[/code], the user can drag the GraphElement.
</member>
<member name="position_offset" type="Vector2" setter="set_position_offset" getter="get_position_offset" default="Vector2(0, 0)">
The offset of the GraphElement, relative to the scroll offset of the [GraphEdit].
</member>
<member name="resizable" type="bool" setter="set_resizable" getter="is_resizable" default="false">
If [code]true[/code], the user can resize the GraphElement.
[b]Note:[/b] Dragging the handle will only emit the [signal resize_request] signal, the GraphElement needs to be resized manually.
</member>
<member name="selectable" type="bool" setter="set_selectable" getter="is_selectable" default="true">
If [code]true[/code], the user can select the GraphElement.
</member>
<member name="selected" type="bool" setter="set_selected" getter="is_selected" default="false">
If [code]true[/code], the GraphElement is selected.
</member>
</members>
<signals>
<signal name="close_request">
<description>
Emitted when closing the GraphElement is requested.
</description>
</signal>
<signal name="dragged">
<param index="0" name="from" type="Vector2" />
<param index="1" name="to" type="Vector2" />
<description>
Emitted when the GraphElement is dragged.
</description>
</signal>
<signal name="node_deselected">
<description>
Emitted when the GraphElement is deselected.
</description>
</signal>
<signal name="node_selected">
<description>
Emitted when the GraphElement is selected.
</description>
</signal>
<signal name="position_offset_changed">
<description>
Emitted when the GraphElement is moved.
</description>
</signal>
<signal name="raise_request">
<description>
Emitted when displaying the GraphElement over other ones is requested. Happens on focusing (clicking into) the GraphElement.
</description>
</signal>
<signal name="resize_request">
<param index="0" name="new_minsize" type="Vector2" />
<description>
Emitted when resizing the GraphElement is requested. Happens on dragging the resizer handle (see [member resizable]).
</description>
</signal>
</signals>
</class>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GraphNode" inherits="Container" is_experimental="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<class name="GraphNode" inherits="GraphElement" is_experimental="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A container with connection ports, representing a node in a [GraphEdit].
</brief_description>
@ -7,153 +7,154 @@
[GraphNode] allows to create nodes for a [GraphEdit] graph with customizable content based on its child controls. [GraphNode] is derived from [Container] and it is responsible for placing its children on screen. This works similar to [VBoxContainer]. Children, in turn, provide [GraphNode] with so-called slots, each of which can have a connection port on either side.
Each [GraphNode] slot is defined by its index and can provide the node with up to two ports: one on the left, and one on the right. By convention the left port is also referred to as the [b]input port[/b] and the right port is referred to as the [b]output port[/b]. Each port can be enabled and configured individually, using different type and color. The type is an arbitrary value that you can define using your own considerations. The parent [GraphEdit] will receive this information on each connect and disconnect request.
Slots can be configured in the Inspector dock once you add at least one child [Control]. The properties are grouped by each slot's index in the "Slot" section.
[b]Note:[/b] While GraphNode is set up using slots and slot indices, connections are made between the ports which are enabled. Because of that, [GraphEdit] uses the port's index and not the slot's index. You can use [method get_connection_input_slot] and [method get_connection_output_slot] to get the slot index from the port index.
[b]Note:[/b] While GraphNode is set up using slots and slot indices, connections are made between the ports which are enabled. Because of that [GraphEdit] uses the port's index and not the slot's index. You can use [method get_input_port_slot] and [method get_output_port_slot] to get the slot index from the port index.
</description>
<tutorials>
</tutorials>
<methods>
<method name="_draw_port" qualifiers="virtual">
<return type="void" />
<param index="0" name="slot_index" type="int" />
<param index="1" name="position" type="Vector2i" />
<param index="2" name="left" type="bool" />
<param index="3" name="color" type="Color" />
<description>
</description>
</method>
<method name="clear_all_slots">
<return type="void" />
<description>
Disables all input and output slots of the GraphNode.
Disables all slots of the GraphNode. This will remove all input/output ports from the GraphNode.
</description>
</method>
<method name="clear_slot">
<return type="void" />
<param index="0" name="slot_index" type="int" />
<description>
Disables input and output slot whose index is [param slot_index].
Disables the slot with the given [param slot_index]. This will remove the corresponding input and output port from the GraphNode.
</description>
</method>
<method name="get_connection_input_color">
<method name="get_input_port_color">
<return type="Color" />
<param index="0" name="port" type="int" />
<param index="0" name="port_idx" type="int" />
<description>
Returns the [Color] of the input connection [param port].
Returns the [Color] of the input port with the given [param port_idx].
</description>
</method>
<method name="get_connection_input_count">
<method name="get_input_port_count">
<return type="int" />
<description>
Returns the number of enabled input slots (connections) to the GraphNode.
Returns the number of slots with an enabled input port.
</description>
</method>
<method name="get_connection_input_height">
<return type="int" />
<param index="0" name="port" type="int" />
<description>
Returns the height of the input connection [param port].
</description>
</method>
<method name="get_connection_input_position">
<method name="get_input_port_position">
<return type="Vector2" />
<param index="0" name="port" type="int" />
<param index="0" name="port_idx" type="int" />
<description>
Returns the position of the input connection [param port].
Returns the position of the input port with the given [param port_idx].
</description>
</method>
<method name="get_connection_input_slot">
<method name="get_input_port_slot">
<return type="int" />
<param index="0" name="port" type="int" />
<param index="0" name="port_idx" type="int" />
<description>
Returns the corresponding slot index of the input connection [param port].
Returns the corresponding slot index of the input port with the given [param port_idx].
</description>
</method>
<method name="get_connection_input_type">
<method name="get_input_port_type">
<return type="int" />
<param index="0" name="port" type="int" />
<param index="0" name="port_idx" type="int" />
<description>
Returns the type of the input connection [param port].
Returns the type of the input port with the given [param port_idx].
</description>
</method>
<method name="get_connection_output_color">
<method name="get_output_port_color">
<return type="Color" />
<param index="0" name="port" type="int" />
<param index="0" name="port_idx" type="int" />
<description>
Returns the [Color] of the output connection [param port].
Returns the [Color] of the output port with the given [param port_idx].
</description>
</method>
<method name="get_connection_output_count">
<method name="get_output_port_count">
<return type="int" />
<description>
Returns the number of enabled output slots (connections) of the GraphNode.
Returns the number of slots with an enabled output port.
</description>
</method>
<method name="get_connection_output_height">
<return type="int" />
<param index="0" name="port" type="int" />
<description>
Returns the height of the output connection [param port].
</description>
</method>
<method name="get_connection_output_position">
<method name="get_output_port_position">
<return type="Vector2" />
<param index="0" name="port" type="int" />
<param index="0" name="port_idx" type="int" />
<description>
Returns the position of the output connection [param port].
Returns the position of the output port with the given [param port_idx].
</description>
</method>
<method name="get_connection_output_slot">
<method name="get_output_port_slot">
<return type="int" />
<param index="0" name="port" type="int" />
<param index="0" name="port_idx" type="int" />
<description>
Returns the corresponding slot index of the output connection [param port].
Returns the corresponding slot index of the output port with the given [param port_idx].
</description>
</method>
<method name="get_connection_output_type">
<method name="get_output_port_type">
<return type="int" />
<param index="0" name="port" type="int" />
<param index="0" name="port_idx" type="int" />
<description>
Returns the type of the output connection [param port].
Returns the type of the output port with the given [param port_idx].
</description>
</method>
<method name="get_slot_color_left" qualifiers="const">
<return type="Color" />
<param index="0" name="slot_index" type="int" />
<description>
Returns the left (input) [Color] of the slot [param slot_index].
Returns the left (input) [Color] of the slot with the given [param slot_index].
</description>
</method>
<method name="get_slot_color_right" qualifiers="const">
<return type="Color" />
<param index="0" name="slot_index" type="int" />
<description>
Returns the right (output) [Color] of the slot [param slot_index].
Returns the right (output) [Color] of the slot with the given [param slot_index].
</description>
</method>
<method name="get_slot_type_left" qualifiers="const">
<return type="int" />
<param index="0" name="slot_index" type="int" />
<description>
Returns the left (input) type of the slot [param slot_index].
Returns the left (input) type of the slot with the given [param slot_index].
</description>
</method>
<method name="get_slot_type_right" qualifiers="const">
<return type="int" />
<param index="0" name="slot_index" type="int" />
<description>
Returns the right (output) type of the slot [param slot_index].
Returns the right (output) type of the slot with the given [param slot_index].
</description>
</method>
<method name="get_titlebar_hbox">
<return type="HBoxContainer" />
<description>
Returns the [HBoxContainer] used for the title bar, only containing a [Label] for displaying the title by default. This can be used to add custom controls to the title bar such as option or close buttons.
</description>
</method>
<method name="is_slot_draw_stylebox" qualifiers="const">
<return type="bool" />
<param index="0" name="slot_index" type="int" />
<description>
Returns true if the background [StyleBox] of the slot [param slot_index] is drawn.
Returns true if the background [StyleBox] of the slot with the given [param slot_index] is drawn.
</description>
</method>
<method name="is_slot_enabled_left" qualifiers="const">
<return type="bool" />
<param index="0" name="slot_index" type="int" />
<description>
Returns [code]true[/code] if left (input) side of the slot [param slot_index] is enabled.
Returns [code]true[/code] if left (input) side of the slot with the given [param slot_index] is enabled.
</description>
</method>
<method name="is_slot_enabled_right" qualifiers="const">
<return type="bool" />
<param index="0" name="slot_index" type="int" />
<description>
Returns [code]true[/code] if right (output) side of the slot [param slot_index] is enabled.
Returns [code]true[/code] if right (output) side of the slot with the given [param slot_index] is enabled.
</description>
</method>
<method name="set_slot">
@ -169,7 +170,7 @@
<param index="8" name="custom_icon_right" type="Texture2D" default="null" />
<param index="9" name="draw_stylebox" type="bool" default="true" />
<description>
Sets properties of the slot with the [param slot_index] index.
Sets properties of the slot with the given [param slot_index].
If [param enable_left_port]/[param enable_right_port] is [code]true[/code], a port will appear and the slot will be able to be connected from this side.
With [param type_left]/[param type_right] an arbitrary type can be assigned to each port. Two ports can be connected if they share the same type, or if the connection between their types is allowed in the parent [GraphEdit] (see [method GraphEdit.add_valid_connection_type]). Keep in mind that the [GraphEdit] has the final say in accepting the connection. Type compatibility simply allows the [signal GraphEdit.connection_request] signal to be emitted.
Ports can be further customized using [param color_left]/[param color_right] and [param custom_icon_left]/[param custom_icon_right]. The color parameter adds a tint to the icon. The custom icon can be used to override the default port dot.
@ -183,7 +184,7 @@
<param index="0" name="slot_index" type="int" />
<param index="1" name="color" type="Color" />
<description>
Sets the [Color] of the left (input) side of the slot [param slot_index] to [param color].
Sets the [Color] of the left (input) side of the slot with the given [param slot_index] to [param color].
</description>
</method>
<method name="set_slot_color_right">
@ -191,7 +192,7 @@
<param index="0" name="slot_index" type="int" />
<param index="1" name="color" type="Color" />
<description>
Sets the [Color] of the right (output) side of the slot [param slot_index] to [param color].
Sets the [Color] of the right (output) side of the slot with the given [param slot_index] to [param color].
</description>
</method>
<method name="set_slot_draw_stylebox">
@ -199,7 +200,7 @@
<param index="0" name="slot_index" type="int" />
<param index="1" name="enable" type="bool" />
<description>
Toggles the background [StyleBox] of the slot [param slot_index].
Toggles the background [StyleBox] of the slot with the given [param slot_index].
</description>
</method>
<method name="set_slot_enabled_left">
@ -207,7 +208,7 @@
<param index="0" name="slot_index" type="int" />
<param index="1" name="enable" type="bool" />
<description>
Toggles the left (input) side of the slot [param slot_index]. If [param enable] is [code]true[/code], a port will appear on the left side and the slot will be able to be connected from this side.
Toggles the left (input) side of the slot with the given [param slot_index]. If [param enable] is [code]true[/code], a port will appear on the left side and the slot will be able to be connected from this side.
</description>
</method>
<method name="set_slot_enabled_right">
@ -215,7 +216,7 @@
<param index="0" name="slot_index" type="int" />
<param index="1" name="enable" type="bool" />
<description>
Toggles the right (output) side of the slot [param slot_index]. If [param enable] is [code]true[/code], a port will appear on the right side and the slot will be able to be connected from this side.
Toggles the right (output) side of the slot with the given [param slot_index]. If [param enable] is [code]true[/code], a port will appear on the right side and the slot will be able to be connected from this side.
</description>
</method>
<method name="set_slot_type_left">
@ -223,7 +224,7 @@
<param index="0" name="slot_index" type="int" />
<param index="1" name="type" type="int" />
<description>
Sets the left (input) type of the slot [param slot_index] to [param type]. If the value is negative, all connections will be disallowed to be created via user inputs.
Sets the left (input) type of the slot with the given [param slot_index] to [param type]. If the value is negative, all connections will be disallowed to be created via user inputs.
</description>
</method>
<method name="set_slot_type_right">
@ -231,156 +232,54 @@
<param index="0" name="slot_index" type="int" />
<param index="1" name="type" type="int" />
<description>
Sets the right (output) type of the slot [param slot_index] to [param type]. If the value is negative, all connections will be disallowed to be created via user inputs.
Sets the right (output) type of the slot with the given [param slot_index] to [param type]. If the value is negative, all connections will be disallowed to be created via user inputs.
</description>
</method>
</methods>
<members>
<member name="draggable" type="bool" setter="set_draggable" getter="is_draggable" default="true">
If [code]true[/code], the user can drag the GraphNode.
</member>
<member name="language" type="String" setter="set_language" getter="get_language" default="&quot;&quot;">
Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead.
</member>
<member name="mouse_filter" type="int" setter="set_mouse_filter" getter="get_mouse_filter" overrides="Control" enum="Control.MouseFilter" default="0" />
<member name="overlay" type="int" setter="set_overlay" getter="get_overlay" enum="GraphNode.Overlay" default="0">
Sets the overlay shown above the GraphNode. See [enum Overlay].
</member>
<member name="position_offset" type="Vector2" setter="set_position_offset" getter="get_position_offset" default="Vector2(0, 0)">
The offset of the GraphNode, relative to the scroll offset of the [GraphEdit].
[b]Note:[/b] You cannot use position offset directly, as [GraphEdit] is a [Container].
</member>
<member name="resizable" type="bool" setter="set_resizable" getter="is_resizable" default="false">
If [code]true[/code], the user can resize the GraphNode.
[b]Note:[/b] Dragging the handle will only emit the [signal resize_request] signal, the GraphNode needs to be resized manually.
</member>
<member name="selectable" type="bool" setter="set_selectable" getter="is_selectable" default="true">
If [code]true[/code], the user can select the GraphNode.
</member>
<member name="selected" type="bool" setter="set_selected" getter="is_selected" default="false">
If [code]true[/code], the GraphNode is selected.
</member>
<member name="show_close" type="bool" setter="set_show_close_button" getter="is_close_button_visible" default="false">
If [code]true[/code], the close button will be visible.
[b]Note:[/b] Pressing it will only emit the [signal close_request] signal, the GraphNode needs to be removed manually.
</member>
<member name="text_direction" type="int" setter="set_text_direction" getter="get_text_direction" enum="Control.TextDirection" default="0">
Base text writing direction.
</member>
<member name="title" type="String" setter="set_title" getter="get_title" default="&quot;&quot;">
The text displayed in the GraphNode's title bar.
</member>
</members>
<signals>
<signal name="close_request">
<description>
Emitted when the GraphNode is requested to be closed. Happens on clicking the close button (see [member show_close]).
</description>
</signal>
<signal name="dragged">
<param index="0" name="from" type="Vector2" />
<param index="1" name="to" type="Vector2" />
<description>
Emitted when the GraphNode is dragged.
</description>
</signal>
<signal name="node_deselected">
<description>
Emitted when the GraphNode is deselected.
</description>
</signal>
<signal name="node_selected">
<description>
Emitted when the GraphNode is selected.
</description>
</signal>
<signal name="position_offset_changed">
<description>
Emitted when the GraphNode is moved.
</description>
</signal>
<signal name="raise_request">
<description>
Emitted when the GraphNode is requested to be displayed over other ones. Happens on focusing (clicking into) the GraphNode.
</description>
</signal>
<signal name="resize_request">
<param index="0" name="new_minsize" type="Vector2" />
<description>
Emitted when the GraphNode is requested to be resized. Happens on dragging the resizer handle (see [member resizable]).
</description>
</signal>
<signal name="slot_updated">
<param index="0" name="idx" type="int" />
<param index="0" name="slot_index" type="int" />
<description>
Emitted when any GraphNode's slot is updated.
</description>
</signal>
</signals>
<constants>
<constant name="OVERLAY_DISABLED" value="0" enum="Overlay">
No overlay is shown.
</constant>
<constant name="OVERLAY_BREAKPOINT" value="1" enum="Overlay">
Show overlay set in the [theme_item breakpoint] theme property.
</constant>
<constant name="OVERLAY_POSITION" value="2" enum="Overlay">
Show overlay set in the [theme_item position] theme property.
</constant>
</constants>
<theme_items>
<theme_item name="close_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
The color modulation applied to the close button icon.
</theme_item>
<theme_item name="resizer_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
The color modulation applied to the resizer icon.
</theme_item>
<theme_item name="title_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
Color of the title text.
</theme_item>
<theme_item name="close_h_offset" data_type="constant" type="int" default="12">
</theme_item>
<theme_item name="close_offset" data_type="constant" type="int" default="22">
The vertical offset of the close button.
</theme_item>
<theme_item name="port_offset" data_type="constant" type="int" default="0">
<theme_item name="port_h_offset" data_type="constant" type="int" default="0">
Horizontal offset for the ports.
</theme_item>
<theme_item name="separation" data_type="constant" type="int" default="2">
The vertical distance between ports.
</theme_item>
<theme_item name="title_h_offset" data_type="constant" type="int" default="0">
Horizontal offset of the title text.
</theme_item>
<theme_item name="title_offset" data_type="constant" type="int" default="26">
Vertical offset of the title text.
</theme_item>
<theme_item name="title_font" data_type="font" type="Font">
Font used for the title text.
</theme_item>
<theme_item name="close" data_type="icon" type="Texture2D">
The icon for the close button, visible when [member show_close] is enabled.
</theme_item>
<theme_item name="port" data_type="icon" type="Texture2D">
The icon used for representing ports.
</theme_item>
<theme_item name="resizer" data_type="icon" type="Texture2D">
The icon used for resizer, visible when [member resizable] is enabled.
The icon used for the resizer, visible when [member GraphElement.resizable] is enabled.
</theme_item>
<theme_item name="breakpoint" data_type="style" type="StyleBox">
The background used when [member overlay] is set to [constant OVERLAY_BREAKPOINT].
<theme_item name="panel" data_type="style" type="StyleBox">
The default background for the slot area of the [GraphNode].
</theme_item>
<theme_item name="frame" data_type="style" type="StyleBox">
The default background for [GraphNode].
</theme_item>
<theme_item name="position" data_type="style" type="StyleBox">
The background used when [member overlay] is set to [constant OVERLAY_POSITION].
</theme_item>
<theme_item name="selected_frame" data_type="style" type="StyleBox">
The background used when the [GraphNode] is selected.
<theme_item name="panel_selected" data_type="style" type="StyleBox">
The [StyleBox] used for the slot area when selected.
</theme_item>
<theme_item name="slot" data_type="style" type="StyleBox">
The [StyleBox] used for each slot of the [GraphNode].
</theme_item>
<theme_item name="titlebar" data_type="style" type="StyleBox">
The [StyleBox] used for the title bar of the [GraphNode].
</theme_item>
<theme_item name="titlebar_selected" data_type="style" type="StyleBox">
The [StyleBox] used for the title bar of the [GraphNode] when it is selected.
</theme_item>
</theme_items>
</class>

View file

@ -1830,8 +1830,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// GraphEdit
theme->set_stylebox("panel", "GraphEdit", style_tree_bg);
if (dark_theme) {
theme->set_color("grid_major", "GraphEdit", Color(1.0, 1.0, 1.0, 0.15));
theme->set_color("grid_minor", "GraphEdit", Color(1.0, 1.0, 1.0, 0.07));
theme->set_color("grid_major", "GraphEdit", Color(1.0, 1.0, 1.0, 0.1));
theme->set_color("grid_minor", "GraphEdit", Color(1.0, 1.0, 1.0, 0.05));
} else {
theme->set_color("grid_major", "GraphEdit", Color(0.0, 0.0, 0.0, 0.15));
theme->set_color("grid_minor", "GraphEdit", Color(0.0, 0.0, 0.0, 0.07));
@ -1866,6 +1866,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_minimap_node = make_flat_stylebox(Color(0, 0, 0), 0, 0, 0, 0);
}
style_minimap_camera->set_border_width_all(1);
style_minimap_node->set_anti_aliased(false);
theme->set_stylebox("camera", "GraphEditMinimap", style_minimap_camera);
theme->set_stylebox("node", "GraphEditMinimap", style_minimap_node);
@ -1879,38 +1880,43 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("resizer_color", "GraphEditMinimap", minimap_resizer_color);
// GraphNode
const int gn_margin_top = 2;
const int gn_margin_side = 2;
const int gn_margin_bottom = 2;
// StateMachine
const int sm_margin_side = 10;
Color graphnode_bg = dark_color_3;
if (!dark_theme) {
graphnode_bg = prop_section_color;
}
const Color graph_node_selected_border_color = graphnode_bg.lerp(accent_color, 0.275);
Ref<StyleBoxFlat> graphsb = make_flat_stylebox(graphnode_bg.lerp(style_tree_bg->get_bg_color(), 0.3), gn_margin_side, 24, gn_margin_side, gn_margin_bottom, corner_width);
graphsb->set_border_width_all(border_width);
graphsb->set_border_color(graphnode_bg);
Ref<StyleBoxFlat> graphsbselected = make_flat_stylebox(graphnode_bg * Color(1, 1, 1, 1), gn_margin_side, 24, gn_margin_side, gn_margin_bottom, corner_width);
graphsbselected->set_border_width_all(2 * EDSCALE + border_width);
graphsbselected->set_border_color(Color(accent_color.r, accent_color.g, accent_color.b, 0.6));
Ref<StyleBoxFlat> graphsbcomment = make_flat_stylebox(graphnode_bg * Color(1, 1, 1, 0.3), gn_margin_side, 24, gn_margin_side, gn_margin_bottom, corner_width);
graphsbcomment->set_border_width_all(border_width);
graphsbcomment->set_border_color(graphnode_bg);
Ref<StyleBoxFlat> graphsbcommentselected = make_flat_stylebox(graphnode_bg * Color(1, 1, 1, 0.4), gn_margin_side, 24, gn_margin_side, gn_margin_bottom, corner_width);
graphsbcommentselected->set_border_width_all(border_width);
graphsbcommentselected->set_border_color(graphnode_bg);
Ref<StyleBoxFlat> graphsbbreakpoint = graphsbselected->duplicate();
graphsbbreakpoint->set_draw_center(false);
graphsbbreakpoint->set_border_color(warning_color);
graphsbbreakpoint->set_shadow_color(warning_color * Color(1.0, 1.0, 1.0, 0.1));
Ref<StyleBoxFlat> graphsbposition = graphsbselected->duplicate();
graphsbposition->set_draw_center(false);
graphsbposition->set_border_color(error_color);
graphsbposition->set_shadow_color(error_color * Color(1.0, 1.0, 1.0, 0.2));
Ref<StyleBoxEmpty> graphsbslot = make_empty_stylebox(12, 0, 12, 0);
const Color graphnode_frame_bg = graphnode_bg.lerp(style_tree_bg->get_bg_color(), 0.3);
Ref<StyleBoxFlat> graphn_sb_panel = make_flat_stylebox(graphnode_frame_bg, gn_margin_side, gn_margin_top, gn_margin_side, gn_margin_bottom, corner_width);
graphn_sb_panel->set_border_width_all(border_width);
graphn_sb_panel->set_border_color(graphnode_bg);
graphn_sb_panel->set_corner_radius_individual(0, 0, corner_radius * EDSCALE, corner_radius * EDSCALE);
graphn_sb_panel->set_expand_margin(SIDE_TOP, 17 * EDSCALE);
Ref<StyleBoxFlat> graphn_sb_panel_selected = make_flat_stylebox(graphnode_frame_bg, gn_margin_side, gn_margin_top, gn_margin_side, gn_margin_bottom, corner_width);
graphn_sb_panel_selected->set_border_width_all(2 * EDSCALE + border_width);
graphn_sb_panel_selected->set_border_color(graph_node_selected_border_color);
graphn_sb_panel_selected->set_corner_radius_individual(0, 0, corner_radius * EDSCALE, corner_radius * EDSCALE);
graphn_sb_panel_selected->set_expand_margin(SIDE_TOP, 17 * EDSCALE);
const int gn_titlebar_margin_side = 12;
Ref<StyleBoxFlat> graphn_sb_titlebar = make_flat_stylebox(graphnode_bg, gn_titlebar_margin_side, gn_margin_top, gn_titlebar_margin_side, 0, corner_width);
graphn_sb_titlebar->set_expand_margin(SIDE_TOP, 2 * EDSCALE);
graphn_sb_titlebar->set_corner_radius_individual(corner_radius * EDSCALE, corner_radius * EDSCALE, 0, 0);
Ref<StyleBoxFlat> graphn_sb_titlebar_selected = make_flat_stylebox(graph_node_selected_border_color, gn_titlebar_margin_side, gn_margin_top, gn_titlebar_margin_side, 0, corner_width);
graphn_sb_titlebar_selected->set_corner_radius_individual(corner_radius * EDSCALE, corner_radius * EDSCALE, 0, 0);
graphn_sb_titlebar_selected->set_expand_margin(SIDE_TOP, 2 * EDSCALE);
Ref<StyleBoxEmpty> graphn_sb_slot = make_empty_stylebox(12, 0, 12, 0);
// StateMachine.
const int sm_margin_side = 10;
Ref<StyleBoxFlat> smgraphsb = make_flat_stylebox(dark_color_3 * Color(1, 1, 1, 0.7), sm_margin_side, 24, sm_margin_side, gn_margin_bottom, corner_width);
smgraphsb->set_border_width_all(border_width);
smgraphsb->set_border_color(graphnode_bg);
@ -1920,45 +1926,42 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
smgraphsbselected->set_shadow_size(8 * EDSCALE);
smgraphsbselected->set_shadow_color(shadow_color);
graphsb->set_border_width(SIDE_TOP, 24 * EDSCALE);
graphsbselected->set_border_width(SIDE_TOP, 24 * EDSCALE);
graphsbcomment->set_border_width(SIDE_TOP, 24 * EDSCALE);
graphsbcommentselected->set_border_width(SIDE_TOP, 24 * EDSCALE);
theme->set_stylebox("panel", "GraphElement", graphn_sb_panel);
theme->set_stylebox("panel_selected", "GraphElement", graphn_sb_panel_selected);
theme->set_stylebox("titlebar", "GraphElement", graphn_sb_titlebar);
theme->set_stylebox("titlebar_selected", "GraphElement", graphn_sb_titlebar_selected);
graphsb->set_corner_detail(corner_radius * EDSCALE);
graphsbselected->set_corner_detail(corner_radius * EDSCALE);
graphsbcomment->set_corner_detail(corner_radius * EDSCALE);
graphsbcommentselected->set_corner_detail(corner_radius * EDSCALE);
// GraphNode's title Label.
theme->set_type_variation("GraphNodeTitleLabel", "Label");
theme->set_stylebox("frame", "GraphNode", graphsb);
theme->set_stylebox("selected_frame", "GraphNode", graphsbselected);
theme->set_stylebox("breakpoint", "GraphNode", graphsbbreakpoint);
theme->set_stylebox("position", "GraphNode", graphsbposition);
theme->set_stylebox("slot", "GraphNode", graphsbslot);
theme->set_stylebox("state_machine_frame", "GraphNode", smgraphsb);
theme->set_stylebox("state_machine_selected_frame", "GraphNode", smgraphsbselected);
theme->set_stylebox("normal", "GraphNodeTitleLabel", make_empty_stylebox(0, 0, 0, 0));
theme->set_color("font_color", "GraphNodeTitleLabel", font_color);
theme->set_constant("line_spacing", "GraphNodeTitleLabel", 3 * EDSCALE);
Color node_decoration_color = dark_color_1.inverted();
theme->set_color("title_color", "GraphNode", node_decoration_color);
node_decoration_color.a = 0.7;
theme->set_color("close_color", "GraphNode", node_decoration_color);
theme->set_color("resizer_color", "GraphNode", node_decoration_color);
Color graphnode_decoration_color = dark_color_1.inverted();
theme->set_constant("port_offset", "GraphNode", 0);
theme->set_constant("title_h_offset", "GraphNode", 12 * EDSCALE);
theme->set_constant("title_offset", "GraphNode", 21 * EDSCALE);
theme->set_constant("close_h_offset", "GraphNode", -2 * EDSCALE);
theme->set_constant("close_offset", "GraphNode", 20 * EDSCALE);
theme->set_color("resizer_color", "GraphElement", graphnode_decoration_color);
theme->set_icon("resizer", "GraphElement", theme->get_icon(SNAME("GuiResizer"), EditorStringName(EditorIcons)));
// GraphNode.
theme->set_stylebox("panel", "GraphNode", graphn_sb_panel);
theme->set_stylebox("panel_selected", "GraphNode", graphn_sb_panel_selected);
theme->set_stylebox("titlebar", "GraphNode", graphn_sb_titlebar);
theme->set_stylebox("titlebar_selected", "GraphNode", graphn_sb_titlebar_selected);
theme->set_stylebox("slot", "GraphNode", graphn_sb_slot);
theme->set_color("resizer_color", "GraphNode", graphnode_decoration_color);
theme->set_constant("port_h_offset", "GraphNode", 0);
theme->set_constant("separation", "GraphNode", 1 * EDSCALE);
theme->set_icon("close", "GraphNode", theme->get_icon(SNAME("GuiCloseCustomizable"), EditorStringName(EditorIcons)));
theme->set_icon("resizer", "GraphNode", theme->get_icon(SNAME("GuiResizer"), EditorStringName(EditorIcons)));
Ref<ImageTexture> port_icon = theme->get_icon(SNAME("GuiGraphNodePort"), EditorStringName(EditorIcons));
// The true size is 24x24 This is necessary for sharp port icons at high zoom levels in GraphEdit (up to ~200%).
port_icon->set_size_override(Size2(12, 12));
theme->set_icon("port", "GraphNode", port_icon);
theme->set_font("title_font", "GraphNode", theme->get_font(SNAME("main_bold_msdf"), EditorStringName(EditorFonts)));
theme->set_stylebox("state_machine_frame", "GraphNode", smgraphsb);
theme->set_stylebox("state_machine_selected_frame", "GraphNode", smgraphsbselected);
// GridContainer
theme->set_constant("v_separation", "GridContainer", Math::round(widget_default_margin.y - 2 * EDSCALE));

View file

@ -1 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 1a2 2 0 0 0-1 3.73v6.541a2 2 0 1 0 2 0V5.414l4.793 4.793 1.414-1.414L5.414 4h5.857a2 2 0 1 0 0-2H4.729A2 2 0 0 0 3 1zm10.656 6.93-.707.707 1.414 1.414.707-.707zm-1.414 1.414-3.889 3.889L8 15l1.767-.353 3.889-3.89-1.414-1.413z" fill="#8eef97"/></svg>
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M11 1c-.554 0-1 .446-1 1v3c0 .554.446 1 1 1h3c.554 0 1-.446 1-1V2c0-.554-.446-1-1-1h-3zM9 3.883L6.732 5.016c.17.292.268.628.268.984v1.117l2.268-1.133A1.964 1.964 0 019 5V3.883zM2 5c-.554 0-1 .446-1 1v4c0 .554.446 1 1 1h3c.554 0 1-.446 1-1V6c0-.554-.446-1-1-1H2zm5 3.883V10c0 .356-.099.692-.268.984L9 12.117V11c0-.356.099-.692.268-.984L7 8.883zM11 10c-.554 0-1 .446-1 1v3c0 .554.446 1 1 1h3c.554 0 1-.446 1-1v-3c0-.554-.446-1-1-1h-3z" color="#000" fill="#8eef97"/><path d="M5 3h6" fill="none"/><rect width="3" height="1" x="-5" y="6.5" ry=".5" fill="#4a4a4a"/></svg>

Before

Width:  |  Height:  |  Size: 345 B

After

Width:  |  Height:  |  Size: 658 B

View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 2a6 6 0 00-6 6 6 6 0 006 6 6 6 0 006-6 6 6 0 00-6-6zm0 2a4 4 0 014 4 4 4 0 01-4 4 4 4 0 01-4-4 4 4 0 014-4zm0 2a2 2 0 100 4 2 2 0 000-4z" fill="#8eef97"/></svg>

After

Width:  |  Height:  |  Size: 256 B

View file

@ -1 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 1a2 2 0 0 0-1 3.73v6.541a2 2 0 1 0 2 0V5.414L8.086 9.5 9.5 8.086 5.414 4h5.857a2 2 0 1 0 0-2H4.729A2 2 0 0 0 3 1zm9.5 9a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5z" fill="#8eef97"/></svg>
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.846 2A1.841 1.841 0 003 3.846V4h10v-.154A1.841 1.841 0 0011.154 2H4.846zM3 5v1a3 3 0 013 3 3 3 0 01-3 3v.154C3 13.177 3.823 14 4.846 14h6.308A1.841 1.841 0 0013 12.154V12a3 3 0 01-3-3 3 3 0 013-3V5H3zm0 2a2 2 0 00-2 2 2 2 0 002 2 2 2 0 002-2 2 2 0 00-2-2zm10 0a2 2 0 00-2 2 2 2 0 002 2 2 2 0 002-2 2 2 0 00-2-2z" fill="#8eef97"/></svg>

Before

Width:  |  Height:  |  Size: 276 B

After

Width:  |  Height:  |  Size: 431 B

View file

@ -1 +0,0 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 3 10 10M3 13 13 3" fill="none" stroke="#fff" stroke-width="2" stroke-opacity=".898"/></svg>

Before

Width:  |  Height:  |  Size: 187 B

View file

@ -169,8 +169,8 @@ void AnimationNodeBlendTreeEditor::update_graph() {
name->connect("focus_exited", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_renamed_focus_out).bind(agnode), CONNECT_DEFERRED);
name->connect("text_changed", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_rename_lineedit_changed), CONNECT_DEFERRED);
base = 1;
node->set_show_close_button(true);
node->connect("close_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_delete_request).bind(E), CONNECT_DEFERRED);
agnode->set_closable(true);
node->connect("close_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_close_request).bind(E), CONNECT_DEFERRED);
}
for (int i = 0; i < agnode->get_input_count(); i++) {
@ -263,7 +263,7 @@ void AnimationNodeBlendTreeEditor::update_graph() {
mb->get_popup()->connect("index_pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_anim_selected).bind(options, E), CONNECT_DEFERRED);
}
Ref<StyleBoxFlat> sb = node->get_theme_stylebox(SNAME("frame"), SNAME("GraphNode"));
Ref<StyleBoxFlat> sb = node->get_theme_stylebox(SNAME("panel"), SNAME("GraphNode"));
Color c = sb->get_border_color();
Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0);
mono_color.a = 0.85;
@ -498,7 +498,7 @@ void AnimationNodeBlendTreeEditor::_anim_selected(int p_index, Array p_options,
undo_redo->commit_action();
}
void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) {
void AnimationNodeBlendTreeEditor::_close_request(const String &p_which) {
if (read_only) {
return;
}
@ -522,7 +522,7 @@ void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) {
undo_redo->commit_action();
}
void AnimationNodeBlendTreeEditor::_delete_nodes_request(const TypedArray<StringName> &p_nodes) {
void AnimationNodeBlendTreeEditor::_close_nodes_request(const TypedArray<StringName> &p_nodes) {
if (read_only) {
return;
}
@ -532,15 +532,19 @@ void AnimationNodeBlendTreeEditor::_delete_nodes_request(const TypedArray<String
if (p_nodes.is_empty()) {
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
if (gn) {
if (gn->is_selected() && gn->is_close_button_visible()) {
if (gn && gn->is_selected()) {
Ref<AnimationNode> anode = blend_tree->get_node(gn->get_name());
if (anode->is_closable()) {
to_erase.push_back(gn->get_name());
}
}
}
} else {
for (int i = 0; i < p_nodes.size(); i++) {
to_erase.push_back(p_nodes[i]);
Ref<AnimationNode> anode = blend_tree->get_node(p_nodes[i]);
if (anode->is_closable()) {
to_erase.push_back(p_nodes[i]);
}
}
}
@ -552,7 +556,7 @@ void AnimationNodeBlendTreeEditor::_delete_nodes_request(const TypedArray<String
undo_redo->create_action(TTR("Delete Node(s)"));
for (const StringName &F : to_erase) {
_delete_request(F);
_close_request(F);
}
undo_redo->commit_action();
@ -1083,7 +1087,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
graph->connect("disconnection_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_disconnection_request), CONNECT_DEFERRED);
graph->connect("node_selected", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_selected));
graph->connect("scroll_offset_changed", callable_mp(this, &AnimationNodeBlendTreeEditor::_scroll_changed));
graph->connect("delete_nodes_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_delete_nodes_request));
graph->connect("close_nodes_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_close_nodes_request));
graph->connect("popup_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_popup_request));
graph->connect("connection_to_empty", callable_mp(this, &AnimationNodeBlendTreeEditor::_connection_to_empty));
graph->connect("connection_from_empty", callable_mp(this, &AnimationNodeBlendTreeEditor::_connection_from_empty));

View file

@ -109,8 +109,8 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
void _node_selected(Object *p_node);
void _open_in_editor(const String &p_which);
void _anim_selected(int p_index, Array p_options, const String &p_node);
void _delete_request(const String &p_which);
void _delete_nodes_request(const TypedArray<StringName> &p_nodes);
void _close_request(const String &p_which);
void _close_nodes_request(const TypedArray<StringName> &p_nodes);
bool _update_filters(const Ref<AnimationNode> &anode);
void _inspect_filters(const String &p_which);

View file

@ -144,22 +144,26 @@ void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p
bool is_dirty = link.preview_pos < 0;
if (!is_dirty && link.preview_visible && link.preview_box != nullptr) {
link.graph_node->remove_child(link.preview_box);
link.graph_element->remove_child(link.preview_box);
memdelete(link.preview_box);
link.preview_box = nullptr;
link.graph_node->reset_size();
link.graph_element->reset_size();
link.preview_visible = false;
}
if (p_port_id != -1 && link.output_ports[p_port_id].preview_button != nullptr) {
if (is_dirty) {
link.preview_pos = link.graph_node->get_child_count();
link.preview_pos = link.graph_element->get_child_count();
}
VBoxContainer *vbox = memnew(VBoxContainer);
link.graph_node->add_child(vbox);
link.graph_node->move_child(vbox, link.preview_pos);
link.graph_node->set_slot_draw_stylebox(vbox->get_index(), false);
link.graph_element->add_child(vbox);
link.graph_element->move_child(vbox, link.preview_pos);
GraphNode *graph_node = Object::cast_to<GraphNode>(link.graph_element);
if (graph_node) {
graph_node->set_slot_draw_stylebox(vbox->get_index(false), false);
}
Control *offset = memnew(Control);
offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE));
@ -293,7 +297,7 @@ void VisualShaderGraphPlugin::update_node_size(int p_node_id) {
if (!links.has(p_node_id)) {
return;
}
links[p_node_id].graph_node->reset_size();
links[p_node_id].graph_element->reset_size();
}
void VisualShaderGraphPlugin::register_default_input_button(int p_node_id, int p_port_id, Button *p_button) {
@ -324,7 +328,7 @@ VisualShader::Type VisualShaderGraphPlugin::get_shader_type() const {
void VisualShaderGraphPlugin::set_node_position(VisualShader::Type p_type, int p_id, const Vector2 &p_position) {
if (visual_shader->get_shader_type() == p_type && links.has(p_id)) {
links[p_id].graph_node->set_position_offset(p_position);
links[p_id].graph_element->set_position_offset(p_position);
}
}
@ -336,8 +340,8 @@ void VisualShaderGraphPlugin::clear_links() {
links.clear();
}
void VisualShaderGraphPlugin::register_link(VisualShader::Type p_type, int p_id, VisualShaderNode *p_visual_node, GraphNode *p_graph_node) {
links.insert(p_id, { p_type, p_visual_node, p_graph_node, p_visual_node->get_output_port_for_preview() != -1, -1, HashMap<int, InputPort>(), HashMap<int, Port>(), nullptr, nullptr, nullptr, { nullptr, nullptr, nullptr } });
void VisualShaderGraphPlugin::register_link(VisualShader::Type p_type, int p_id, VisualShaderNode *p_visual_node, GraphElement *p_graph_element) {
links.insert(p_id, { p_type, p_visual_node, p_graph_element, p_visual_node->get_output_port_for_preview() != -1, -1, HashMap<int, InputPort>(), HashMap<int, Port>(), nullptr, nullptr, nullptr, { nullptr, nullptr, nullptr } });
}
void VisualShaderGraphPlugin::register_output_port(int p_node_id, int p_port, TextureButton *p_button) {
@ -415,42 +419,49 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
// Visual shader specific theme for MSDF font.
Ref<Theme> vstheme;
vstheme.instantiate();
Ref<Font> label_font = EditorNode::get_singleton()->get_editor_theme()->get_font("main_msdf", EditorStringName(EditorFonts));
Ref<Font> label_font = EditorNode::get_singleton()->get_editor_theme()->get_font("main_msdf", EditorStringName(EditorIcons));
Ref<Font> label_bold_font = EditorNode::get_singleton()->get_editor_theme()->get_font("main_bold_msdf", EditorStringName(EditorIcons));
vstheme->set_font("font", "Label", label_font);
vstheme->set_font("font", "GraphNodeTitleLabel", label_bold_font);
vstheme->set_font("font", "LineEdit", label_font);
vstheme->set_font("font", "Button", label_font);
Ref<VisualShaderNode> vsnode = visual_shader->get_node(p_type, p_id);
Ref<VisualShaderNodeResizableBase> resizable_node = Object::cast_to<VisualShaderNodeResizableBase>(vsnode.ptr());
bool is_resizable = !resizable_node.is_null();
Ref<VisualShaderNodeResizableBase> resizable_node = vsnode;
bool is_resizable = resizable_node.is_valid();
Size2 size = Size2(0, 0);
Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(vsnode.ptr());
bool is_group = !group_node.is_null();
Ref<VisualShaderNodeGroupBase> group_node = vsnode;
bool is_group = group_node.is_valid();
Ref<VisualShaderNodeComment> comment_node = Object::cast_to<VisualShaderNodeComment>(vsnode.ptr());
Ref<VisualShaderNodeComment> comment_node = vsnode;
bool is_comment = comment_node.is_valid();
Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(group_node.ptr());
bool is_expression = !expression_node.is_null();
Ref<VisualShaderNodeExpression> expression_node = group_node;
bool is_expression = expression_node.is_valid();
String expression = "";
VisualShaderNodeCustom *custom_node = Object::cast_to<VisualShaderNodeCustom>(vsnode.ptr());
if (custom_node) {
Ref<VisualShaderNodeCustom> custom_node = vsnode;
if (custom_node.is_valid()) {
custom_node->_set_initialized(true);
}
// Create graph node.
GraphNode *node = memnew(GraphNode);
node->set_title(vsnode->get_caption());
// All nodes are closable except the output node.
if (p_id >= 2) {
vsnode->set_closable(true);
node->connect("close_request", callable_mp(editor, &VisualShaderEditor::_close_node_request).bind(p_type, p_id), CONNECT_DEFERRED);
}
graph->add_child(node);
node->set_theme(vstheme);
editor->_update_created_node(node);
if (p_just_update) {
Link &link = links[p_id];
link.graph_node = node;
link.graph_element = node;
link.preview_box = nullptr;
link.preview_pos = -1;
link.output_ports.clear();
@ -474,19 +485,18 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
}
node->set_position_offset(visual_shader->get_node_position(p_type, p_id));
node->set_title(vsnode->get_caption());
node->set_name(itos(p_id));
if (p_id >= 2) {
node->set_show_close_button(true);
node->connect("close_request", callable_mp(editor, &VisualShaderEditor::_delete_node_request).bind(p_type, p_id), CONNECT_DEFERRED);
}
node->set_name(itos(p_id));
node->connect("dragged", callable_mp(editor, &VisualShaderEditor::_node_dragged).bind(p_id));
Control *custom_editor = nullptr;
int port_offset = 1;
if (is_resizable) {
editor->call_deferred(SNAME("_set_node_size"), (int)p_type, p_id, size);
}
Control *content_offset = memnew(Control);
content_offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE));
node->add_child(content_offset);
@ -495,10 +505,6 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
port_offset += 1;
}
if (is_resizable) {
editor->call_deferred(SNAME("_set_node_size"), (int)p_type, p_id, size);
}
Ref<VisualShaderNodeParticleEmit> emit = vsnode;
if (emit.is_valid()) {
node->set_custom_minimum_size(Size2(200 * EDSCALE, 0));
@ -608,7 +614,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
if (custom_editor) {
if (is_curve || (hb == nullptr && !vsnode->is_use_prop_slots() && (vsnode->get_output_port_count() == 0 || vsnode->get_output_port_name(0) == "") && (vsnode->get_input_port_count() == 0 || vsnode->get_input_port_name(0) == ""))) {
//will be embedded in first port
// Will be embedded in first port.
} else {
port_offset++;
node->add_child(custom_editor);
@ -896,93 +902,97 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
if (!is_first_hbox) {
idx = i + port_offset;
}
node->set_slot(idx, valid_left, port_left, type_color[port_left], valid_right, port_right, type_color[port_right]);
if (!is_comment) {
GraphNode *graph_node = Object::cast_to<GraphNode>(node);
if (vsnode->_is_output_port_expanded(i)) {
switch (vsnode->get_output_port_type(i)) {
case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
port_offset++;
valid_left = (i + 1) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 1);
}
node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[0]);
port_offset++;
graph_node->set_slot(idx, valid_left, port_left, type_color[port_left], valid_right, port_right, type_color[port_right]);
valid_left = (i + 2) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 2);
}
node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[1]);
if (vsnode->_is_output_port_expanded(i)) {
switch (vsnode->get_output_port_type(i)) {
case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
port_offset++;
valid_left = (i + 1) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 1);
}
graph_node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[0]);
port_offset++;
expanded_type = VisualShaderNode::PORT_TYPE_VECTOR_2D;
} break;
case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
port_offset++;
valid_left = (i + 1) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 1);
}
node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[0]);
port_offset++;
valid_left = (i + 2) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 2);
}
graph_node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[1]);
valid_left = (i + 2) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 2);
}
node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[1]);
port_offset++;
expanded_type = VisualShaderNode::PORT_TYPE_VECTOR_2D;
} break;
case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
port_offset++;
valid_left = (i + 1) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 1);
}
graph_node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[0]);
port_offset++;
valid_left = (i + 3) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 3);
}
node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[2]);
valid_left = (i + 2) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 2);
}
graph_node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[1]);
port_offset++;
expanded_type = VisualShaderNode::PORT_TYPE_VECTOR_3D;
} break;
case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
port_offset++;
valid_left = (i + 1) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 1);
}
node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[0]);
port_offset++;
valid_left = (i + 3) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 3);
}
graph_node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[2]);
valid_left = (i + 2) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 2);
}
node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[1]);
port_offset++;
expanded_type = VisualShaderNode::PORT_TYPE_VECTOR_3D;
} break;
case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
port_offset++;
valid_left = (i + 1) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 1);
}
graph_node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[0]);
port_offset++;
valid_left = (i + 3) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 3);
}
node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[2]);
port_offset++;
valid_left = (i + 2) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 2);
}
graph_node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[1]);
port_offset++;
valid_left = (i + 4) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 4);
}
node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[3]);
valid_left = (i + 3) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 3);
}
graph_node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[2]);
port_offset++;
expanded_type = VisualShaderNode::PORT_TYPE_VECTOR_4D;
} break;
default:
break;
valid_left = (i + 4) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
if (valid_left) {
port_left = vsnode->get_input_port_type(i + 4);
}
graph_node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[3]);
expanded_type = VisualShaderNode::PORT_TYPE_VECTOR_4D;
} break;
default:
break;
}
}
}
}
@ -1005,6 +1015,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
Label *error_label = memnew(Label);
error_label->add_theme_color_override("font_color", editor->get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
error_label->set_text(error);
error_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD);
node->add_child(error_label);
}
@ -1061,16 +1072,13 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
expression_box->connect("focus_exited", callable_mp(editor, &VisualShaderEditor::_expression_focus_out).bind(expression_box, p_id));
}
if (is_comment) {
graph->move_child(node, 0); // to prevents a bug where comment node overlaps its content
}
}
void VisualShaderGraphPlugin::remove_node(VisualShader::Type p_type, int p_id, bool p_just_update) {
if (visual_shader->get_shader_type() == p_type && links.has(p_id)) {
links[p_id].graph_node->get_parent()->remove_child(links[p_id].graph_node);
memdelete(links[p_id].graph_node);
Node *graph_edit_node = links[p_id].graph_element->get_parent();
graph_edit_node->remove_child(links[p_id].graph_element);
memdelete(links[p_id].graph_element);
if (!p_just_update) {
links.erase(p_id);
}
@ -1374,7 +1382,7 @@ void VisualShaderEditor::_update_custom_script(const Ref<Script> &p_script) {
if (vsnode.is_null()) {
continue;
}
Ref<VisualShaderNodeCustom> custom_node = Ref<VisualShaderNodeCustom>(vsnode.ptr());
Ref<VisualShaderNodeCustom> custom_node = vsnode;
if (custom_node.is_null() || custom_node->get_script() != p_script) {
continue;
}
@ -1486,7 +1494,7 @@ void VisualShaderEditor::_resources_removed() {
if (vsnode.is_null()) {
continue;
}
Ref<VisualShaderNodeCustom> custom_node = Ref<VisualShaderNodeCustom>(vsnode.ptr());
Ref<VisualShaderNodeCustom> custom_node = vsnode;
if (custom_node.is_null() || custom_node->get_script() != scr) {
continue;
}
@ -1916,8 +1924,8 @@ Size2 VisualShaderEditor::get_minimum_size() const {
return Size2(10, 200);
}
void VisualShaderEditor::_draw_color_over_button(Object *obj, Color p_color) {
Button *button = Object::cast_to<Button>(obj);
void VisualShaderEditor::_draw_color_over_button(Object *p_obj, Color p_color) {
Button *button = Object::cast_to<Button>(p_obj);
if (!button) {
return;
}
@ -1926,18 +1934,6 @@ void VisualShaderEditor::_draw_color_over_button(Object *obj, Color p_color) {
button->draw_rect(Rect2(normal->get_offset(), button->get_size() - normal->get_minimum_size()), p_color);
}
void VisualShaderEditor::_update_created_node(GraphNode *node) {
const Ref<StyleBoxFlat> sb = node->get_theme_stylebox(SNAME("frame"), SNAME("GraphNode"));
Color c = sb->get_border_color();
const Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0, 0.85) : Color(0.0, 0.0, 0.0, 0.85);
c = mono_color;
node->add_theme_color_override("title_color", c);
c.a = 0.7;
node->add_theme_color_override("close_color", c);
node->add_theme_color_override("resizer_color", c);
}
void VisualShaderEditor::_update_parameters(bool p_update_refs) {
VisualShaderNodeParameterRef::clear_parameters(visual_shader->get_rid());
@ -2025,9 +2021,9 @@ void VisualShaderEditor::_update_graph() {
VisualShader::Type type = get_current_shader_type();
graph->clear_connections();
//erase all nodes
// Remove all nodes.
for (int i = 0; i < graph->get_child_count(); i++) {
if (Object::cast_to<GraphNode>(graph->get_child(i))) {
if (Object::cast_to<GraphElement>(graph->get_child(i))) {
Node *node = graph->get_child(i);
graph->remove_child(node);
memdelete(node);
@ -2406,14 +2402,14 @@ void VisualShaderEditor::_remove_output_port(int p_node, int p_port) {
undo_redo->commit_action();
}
void VisualShaderEditor::_expression_focus_out(Object *code_edit, int p_node) {
void VisualShaderEditor::_expression_focus_out(Object *p_code_edit, int p_node) {
VisualShader::Type type = get_current_shader_type();
Ref<VisualShaderNodeExpression> node = visual_shader->get_node(type, p_node);
if (node.is_null()) {
return;
}
CodeEdit *expression_box = Object::cast_to<CodeEdit>(code_edit);
CodeEdit *expression_box = Object::cast_to<CodeEdit>(p_code_edit);
if (node->get_expression() == expression_box->get_text()) {
return;
@ -2452,20 +2448,20 @@ void VisualShaderEditor::_set_node_size(int p_type, int p_node, const Vector2 &p
}
}
GraphNode *gn = nullptr;
GraphElement *graph_element = nullptr;
Node *node2 = graph->get_node(itos(p_node));
gn = Object::cast_to<GraphNode>(node2);
if (!gn) {
graph_element = Object::cast_to<GraphElement>(node2);
if (!graph_element) {
return;
}
gn->set_custom_minimum_size(size);
gn->reset_size();
graph_element->set_custom_minimum_size(size);
graph_element->reset_size();
if (!expression_node.is_null() && text_box) {
Size2 box_size = size;
if (box_size.x < 150 * EDSCALE || box_size.y < 0) {
box_size.x = gn->get_size().x;
box_size.x = graph_element->get_size().x;
}
box_size.x -= text_box->get_offset(SIDE_LEFT);
box_size.x -= 28 * EDSCALE;
@ -2483,9 +2479,14 @@ void VisualShaderEditor::_node_resized(const Vector2 &p_new_size, int p_type, in
return;
}
Vector2 new_size = p_new_size;
if (graph->is_snapping_enabled() ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) {
new_size = new_size.snapped(Vector2(graph->get_snapping_distance(), graph->get_snapping_distance()));
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Resize VisualShader Node"), UndoRedo::MERGE_ENDS);
undo_redo->add_do_method(this, "_set_node_size", p_type, p_node, p_new_size);
undo_redo->add_do_method(this, "_set_node_size", p_type, p_node, new_size);
undo_redo->add_undo_method(this, "_set_node_size", p_type, p_node, node->get_size());
undo_redo->commit_action();
}
@ -2547,7 +2548,7 @@ void VisualShaderEditor::_comment_title_popup_hide() {
return; // nothing changed - ignored
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Comment Node Title"));
undo_redo->create_action(TTR("Set Comment Title"));
undo_redo->add_do_method(node.ptr(), "set_title", comment_title_change_edit->get_text());
undo_redo->add_undo_method(node.ptr(), "set_title", node->get_title());
undo_redo->add_do_method(graph_plugin.ptr(), "update_node", (int)type, node_id);
@ -2590,7 +2591,7 @@ void VisualShaderEditor::_comment_desc_popup_hide() {
return; // nothing changed - ignored
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Comment Node Description"));
undo_redo->create_action(TTR("Set Comment Description"));
undo_redo->add_do_method(node.ptr(), "set_description", comment_desc_change_edit->get_text());
undo_redo->add_undo_method(node.ptr(), "set_description", node->get_title());
undo_redo->add_do_method(graph_plugin.ptr(), "update_node", (int)type, node_id);
@ -3544,12 +3545,12 @@ void VisualShaderEditor::_delete_nodes(int p_type, const List<int> &p_nodes) {
}
}
// delete nodes from the graph
// Delete nodes from the graph.
for (const int &F : p_nodes) {
undo_redo->add_do_method(graph_plugin.ptr(), "remove_node", type, F, false);
}
// update parameter refs if any parameter has been deleted
// Update parameter refs if any parameter has been deleted.
if (parameter_names.size() > 0) {
undo_redo->add_do_method(this, "_update_parameters", true);
undo_redo->add_undo_method(this, "_update_parameters", true);
@ -3789,7 +3790,12 @@ void VisualShaderEditor::_convert_constants_to_parameters(bool p_vice_versa) {
undo_redo->commit_action();
}
void VisualShaderEditor::_delete_node_request(int p_type, int p_node) {
void VisualShaderEditor::_close_node_request(int p_type, int p_node) {
Ref<VisualShaderNode> node = visual_shader->get_node((VisualShader::Type)p_type, p_node);
if (!node->is_closable()) {
return;
}
List<int> to_erase;
to_erase.push_back(p_node);
@ -3799,22 +3805,30 @@ void VisualShaderEditor::_delete_node_request(int p_type, int p_node) {
undo_redo->commit_action();
}
void VisualShaderEditor::_delete_nodes_request(const TypedArray<StringName> &p_nodes) {
void VisualShaderEditor::_close_nodes_request(const TypedArray<StringName> &p_nodes) {
List<int> to_erase;
if (p_nodes.is_empty()) {
// Called from context menu.
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
if (gn) {
if (gn->is_selected() && gn->is_close_button_visible()) {
to_erase.push_back(gn->get_name().operator String().to_int());
GraphElement *graph_element = Object::cast_to<GraphElement>(graph->get_child(i));
if (graph_element && graph_element->is_selected()) {
VisualShader::Type type = get_current_shader_type();
int id = String(graph_element->get_name()).to_int();
Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, id);
if (vsnode->is_closable()) {
to_erase.push_back(graph_element->get_name().operator String().to_int());
}
}
}
} else {
VisualShader::Type type = get_current_shader_type();
for (int i = 0; i < p_nodes.size(); i++) {
to_erase.push_back(p_nodes[i].operator String().to_int());
int id = p_nodes[i].operator String().to_int();
Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, id);
if (vsnode->is_closable()) {
to_erase.push_back(id);
}
}
}
@ -3831,59 +3845,62 @@ void VisualShaderEditor::_delete_nodes_request(const TypedArray<StringName> &p_n
void VisualShaderEditor::_node_selected(Object *p_node) {
VisualShader::Type type = get_current_shader_type();
GraphNode *gn = Object::cast_to<GraphNode>(p_node);
ERR_FAIL_COND(!gn);
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
ERR_FAIL_COND(!graph_element);
int id = String(gn->get_name()).to_int();
int id = String(graph_element->get_name()).to_int();
Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, id);
ERR_FAIL_COND(!vsnode.is_valid());
//do not rely on this, makes editor more complex
//EditorNode::get_singleton()->push_item(vsnode.ptr(), "", true);
}
void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
VisualShader::Type type = get_current_shader_type();
Ref<VisualShaderNode> selected_vsnode;
// Right click actions.
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) {
selected_constants.clear();
selected_parameters.clear();
selected_comment = -1;
selected_float_constant = -1;
List<int> to_change;
List<int> selected_closable_graph_elements;
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
if (gn) {
if (gn->is_selected() && gn->is_close_button_visible()) {
int id = gn->get_name().operator String().to_int();
to_change.push_back(id);
GraphElement *graph_element = Object::cast_to<GraphElement>(graph->get_child(i));
if (graph_element && graph_element->is_selected()) {
int id = String(graph_element->get_name()).to_int();
Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, id);
if (!vsnode->is_closable()) {
continue;
}
Ref<VisualShaderNode> node = visual_shader->get_node(type, id);
selected_closable_graph_elements.push_back(id);
VisualShaderNodeComment *comment_node = Object::cast_to<VisualShaderNodeComment>(node.ptr());
if (comment_node != nullptr) {
selected_comment = id;
}
VisualShaderNodeConstant *constant_node = Object::cast_to<VisualShaderNodeConstant>(node.ptr());
if (constant_node != nullptr) {
selected_constants.insert(id);
}
VisualShaderNodeFloatConstant *float_constant_node = Object::cast_to<VisualShaderNodeFloatConstant>(node.ptr());
if (float_constant_node != nullptr) {
selected_float_constant = id;
}
VisualShaderNodeParameter *parameter_node = Object::cast_to<VisualShaderNodeParameter>(node.ptr());
if (parameter_node != nullptr && parameter_node->is_convertible_to_constant()) {
selected_parameters.insert(id);
}
Ref<VisualShaderNode> node = visual_shader->get_node(type, id);
selected_vsnode = node;
VisualShaderNodeComment *frame_node = Object::cast_to<VisualShaderNodeComment>(node.ptr());
if (frame_node != nullptr) {
selected_comment = id;
}
VisualShaderNodeConstant *constant_node = Object::cast_to<VisualShaderNodeConstant>(node.ptr());
if (constant_node != nullptr) {
selected_constants.insert(id);
}
VisualShaderNodeFloatConstant *float_constant_node = Object::cast_to<VisualShaderNodeFloatConstant>(node.ptr());
if (float_constant_node != nullptr) {
selected_float_constant = id;
}
VisualShaderNodeParameter *parameter_node = Object::cast_to<VisualShaderNodeParameter>(node.ptr());
if (parameter_node != nullptr && parameter_node->is_convertible_to_constant()) {
selected_parameters.insert(id);
}
}
}
if (to_change.size() > 1) {
if (selected_closable_graph_elements.size() > 1) {
selected_comment = -1;
selected_float_constant = -1;
}
@ -3896,14 +3913,14 @@ void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
}
}
if (to_change.is_empty() && copy_buffer_empty) {
if (selected_closable_graph_elements.is_empty() && copy_buffer_empty) {
_show_members_dialog(true);
} else {
popup_menu->set_item_disabled(NodeMenuOptions::CUT, to_change.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::COPY, to_change.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::CUT, selected_closable_graph_elements.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::COPY, selected_closable_graph_elements.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::PASTE, copy_buffer_empty);
popup_menu->set_item_disabled(NodeMenuOptions::DELETE, to_change.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::DUPLICATE, to_change.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::DELETE, selected_closable_graph_elements.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::DUPLICATE, selected_closable_graph_elements.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::CLEAR_COPY_BUFFER, copy_buffer_empty);
int temp = popup_menu->get_item_index(NodeMenuOptions::SEPARATOR2);
@ -4184,9 +4201,9 @@ void VisualShaderEditor::_dup_copy_nodes(int p_type, List<CopyItem> &r_items, Li
HashSet<int> nodes;
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
if (gn) {
int id = String(gn->get_name()).to_int();
GraphElement *graph_element = Object::cast_to<GraphElement>(graph->get_child(i));
if (graph_element) {
int id = String(graph_element->get_name()).to_int();
Ref<VisualShaderNode> node = visual_shader->get_node(type, id);
Ref<VisualShaderNodeOutput> output = node;
@ -4194,7 +4211,7 @@ void VisualShaderEditor::_dup_copy_nodes(int p_type, List<CopyItem> &r_items, Li
continue;
}
if (node.is_valid() && gn->is_selected()) {
if (node.is_valid() && graph_element->is_selected()) {
Vector2 pos = visual_shader->get_node_position(type, id);
selection_center += pos;
@ -4320,13 +4337,13 @@ void VisualShaderEditor::_dup_paste_nodes(int p_type, List<CopyItem> &r_items, c
// reselect nodes by excluding the other ones
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
if (gn) {
int id = String(gn->get_name()).to_int();
GraphElement *graph_element = Object::cast_to<GraphElement>(graph->get_child(i));
if (graph_element) {
int id = String(graph_element->get_name()).to_int();
if (added_set.has(id)) {
gn->set_selected(true);
graph_element->set_selected(true);
} else {
gn->set_selected(false);
graph_element->set_selected(false);
}
}
}
@ -4819,7 +4836,7 @@ void VisualShaderEditor::_node_menu_id_pressed(int p_idx) {
_paste_nodes(true, menu_point);
break;
case NodeMenuOptions::DELETE:
_delete_nodes_request(TypedArray<StringName>());
_close_nodes_request(TypedArray<StringName>());
break;
case NodeMenuOptions::DUPLICATE:
_duplicate_nodes();
@ -5119,7 +5136,7 @@ VisualShaderEditor::VisualShaderEditor() {
graph->connect("duplicate_nodes_request", callable_mp(this, &VisualShaderEditor::_duplicate_nodes));
graph->connect("copy_nodes_request", callable_mp(this, &VisualShaderEditor::_copy_nodes).bind(false));
graph->connect("paste_nodes_request", callable_mp(this, &VisualShaderEditor::_paste_nodes).bind(false, Point2()));
graph->connect("delete_nodes_request", callable_mp(this, &VisualShaderEditor::_delete_nodes_request));
graph->connect("close_nodes_request", callable_mp(this, &VisualShaderEditor::_close_nodes_request));
graph->connect("gui_input", callable_mp(this, &VisualShaderEditor::_graph_gui_input));
graph->connect("connection_to_empty", callable_mp(this, &VisualShaderEditor::_connection_to_empty));
graph->connect("connection_from_empty", callable_mp(this, &VisualShaderEditor::_connection_from_empty));

View file

@ -38,9 +38,10 @@
#include "scene/resources/visual_shader.h"
class CodeEdit;
class ColorPicker;
class CurveEditor;
class GraphEdit;
class GraphNode;
class GraphElement;
class MenuButton;
class PopupPanel;
class RichTextLabel;
@ -81,7 +82,7 @@ private:
struct Link {
VisualShader::Type type = VisualShader::Type::TYPE_MAX;
VisualShaderNode *visual_node = nullptr;
GraphNode *graph_node = nullptr;
GraphElement *graph_element = nullptr;
bool preview_visible = false;
int preview_pos = 0;
HashMap<int, InputPort> input_ports;
@ -105,7 +106,7 @@ public:
void set_editor(VisualShaderEditor *p_editor);
void register_shader(VisualShader *p_visual_shader);
void set_connections(const List<VisualShader::Connection> &p_connections);
void register_link(VisualShader::Type p_type, int p_id, VisualShaderNode *p_visual_node, GraphNode *p_graph_node);
void register_link(VisualShader::Type p_type, int p_id, VisualShaderNode *p_visual_node, GraphElement *p_graph_element);
void register_output_port(int p_id, int p_port, TextureButton *p_button);
void register_parameter_name(int p_id, LineEdit *p_parameter_name);
void register_default_input_button(int p_node_id, int p_port_id, Button *p_button);
@ -343,7 +344,7 @@ class VisualShaderEditor : public VBoxContainer {
List<VisualShaderNodeParameterRef> uniform_refs;
void _draw_color_over_button(Object *obj, Color p_color);
void _draw_color_over_button(Object *p_obj, Color p_color);
void _setup_node(VisualShaderNode *p_node, const Vector<Variant> &p_ops);
void _add_node(int p_idx, const Vector<Variant> &p_ops, String p_resource_path = "", int p_node_idx = -1);
@ -379,8 +380,8 @@ class VisualShaderEditor : public VBoxContainer {
void _node_selected(Object *p_node);
void _delete_nodes(int p_type, const List<int> &p_nodes);
void _delete_node_request(int p_type, int p_node);
void _delete_nodes_request(const TypedArray<StringName> &p_nodes);
void _close_node_request(int p_type, int p_node);
void _close_nodes_request(const TypedArray<StringName> &p_nodes);
void _node_changed(int p_id);
@ -417,9 +418,9 @@ class VisualShaderEditor : public VBoxContainer {
void _comment_desc_text_changed();
void _parameter_line_edit_changed(const String &p_text, int p_node_id);
void _parameter_line_edit_focus_out(Object *line_edit, int p_node_id);
void _parameter_line_edit_focus_out(Object *p_line_edit, int p_node_id);
void _port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output);
void _port_name_focus_out(Object *p_line_edit, int p_node_id, int p_port_id, bool p_output);
struct CopyItem {
int id;
@ -451,7 +452,7 @@ class VisualShaderEditor : public VBoxContainer {
void _mode_selected(int p_id);
void _custom_mode_toggled(bool p_enabled);
void _input_select_item(Ref<VisualShaderNodeInput> input, String name);
void _input_select_item(Ref<VisualShaderNodeInput> p_input, String p_name);
void _parameter_ref_select_item(Ref<VisualShaderNodeParameterRef> p_parameter_ref, String p_name);
void _varying_select_item(Ref<VisualShaderNodeVarying> p_varying, String p_name);
@ -470,7 +471,7 @@ class VisualShaderEditor : public VBoxContainer {
void _change_output_port_name(const String &p_text, Object *p_line_edit, int p_node, int p_port);
void _expand_output_port(int p_node, int p_port, bool p_expand);
void _expression_focus_out(Object *code_edit, int p_node);
void _expression_focus_out(Object *p_code_edit, int p_node);
void _set_node_size(int p_type, int p_node, const Size2 &p_size);
void _node_resized(const Vector2 &p_new_size, int p_type, int p_node);
@ -500,7 +501,6 @@ class VisualShaderEditor : public VBoxContainer {
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
bool _is_available(int p_mode);
void _update_created_node(GraphNode *node);
void _update_parameters(bool p_update_refs);
void _update_parameter_refs(HashSet<String> &p_names);
void _update_varyings();

View file

@ -116,3 +116,56 @@ Validate extension JSON: API was removed: classes/GLTFDocumentExtensionPhysics
Validate extension JSON: API was removed: classes/GLTFDocumentExtensionTextureWebP
Excluded unexposed classes from extension_api.json.
GH-79311
--------
Validate extension JSON: API was removed: classes/GraphEdit/signals/delete_nodes_request
Validate extension JSON: API was removed: classes/GraphNode/methods/get_connection_input_color
Validate extension JSON: API was removed: classes/GraphNode/methods/get_connection_input_count
Validate extension JSON: API was removed: classes/GraphNode/methods/get_connection_input_height
Validate extension JSON: API was removed: classes/GraphNode/methods/get_connection_input_position
Validate extension JSON: API was removed: classes/GraphNode/methods/get_connection_input_slot
Validate extension JSON: API was removed: classes/GraphNode/methods/get_connection_input_type
Validate extension JSON: API was removed: classes/GraphNode/methods/get_connection_output_color
Validate extension JSON: API was removed: classes/GraphNode/methods/get_connection_output_count
Validate extension JSON: API was removed: classes/GraphNode/methods/get_connection_output_height
Validate extension JSON: API was removed: classes/GraphNode/methods/get_connection_output_position
Validate extension JSON: API was removed: classes/GraphNode/methods/get_connection_output_slot
Validate extension JSON: API was removed: classes/GraphNode/methods/get_connection_output_type
Validate extension JSON: API was removed: classes/GraphNode/methods/get_language
Validate extension JSON: API was removed: classes/GraphNode/methods/get_overlay
Validate extension JSON: API was removed: classes/GraphNode/methods/get_position_offset
Validate extension JSON: API was removed: classes/GraphNode/methods/get_text_direction
Validate extension JSON: API was removed: classes/GraphNode/methods/is_close_button_visible
Validate extension JSON: API was removed: classes/GraphNode/methods/is_draggable
Validate extension JSON: API was removed: classes/GraphNode/methods/is_resizable
Validate extension JSON: API was removed: classes/GraphNode/methods/is_selectable
Validate extension JSON: API was removed: classes/GraphNode/methods/is_selected
Validate extension JSON: API was removed: classes/GraphNode/methods/set_draggable
Validate extension JSON: API was removed: classes/GraphNode/methods/set_language
Validate extension JSON: API was removed: classes/GraphNode/methods/set_overlay
Validate extension JSON: API was removed: classes/GraphNode/methods/set_position_offset
Validate extension JSON: API was removed: classes/GraphNode/methods/set_resizable
Validate extension JSON: API was removed: classes/GraphNode/methods/set_selectable
Validate extension JSON: API was removed: classes/GraphNode/methods/set_selected
Validate extension JSON: API was removed: classes/GraphNode/methods/set_show_close_button
Validate extension JSON: API was removed: classes/GraphNode/methods/set_text_direction
Validate extension JSON: API was removed: classes/GraphNode/properties/draggable
Validate extension JSON: API was removed: classes/GraphNode/properties/language
Validate extension JSON: API was removed: classes/GraphNode/properties/overlay
Validate extension JSON: API was removed: classes/GraphNode/properties/position_offset
Validate extension JSON: API was removed: classes/GraphNode/properties/resizable
Validate extension JSON: API was removed: classes/GraphNode/properties/selectable
Validate extension JSON: API was removed: classes/GraphNode/properties/selected
Validate extension JSON: API was removed: classes/GraphNode/properties/show_close
Validate extension JSON: API was removed: classes/GraphNode/properties/text_direction
Validate extension JSON: API was removed: classes/GraphNode/signals/close_request
Validate extension JSON: API was removed: classes/GraphNode/signals/dragged
Validate extension JSON: API was removed: classes/GraphNode/signals/node_deselected
Validate extension JSON: API was removed: classes/GraphNode/signals/node_selected
Validate extension JSON: API was removed: classes/GraphNode/signals/position_offset_changed
Validate extension JSON: API was removed: classes/GraphNode/signals/raise_request
Validate extension JSON: API was removed: classes/GraphNode/signals/resize_request
Refactor GraphNode (splitup in GraphElement and GraphNode)

View file

@ -384,6 +384,14 @@ bool AnimationNode::is_filter_enabled() const {
return filter_enabled;
}
void AnimationNode::set_closable(bool p_closable) {
closable = p_closable;
}
bool AnimationNode::is_closable() const {
return closable;
}
bool AnimationNode::is_path_filtered(const NodePath &p_path) const {
return filter.has(p_path);
}

View file

@ -101,6 +101,8 @@ public:
HashMap<NodePath, bool> filter;
bool filter_enabled = false;
bool closable = false;
Array _get_filters() const;
void _set_filters(const Array &p_filters);
friend class AnimationNodeBlendTree;
@ -160,6 +162,9 @@ public:
void set_filter_enabled(bool p_enable);
bool is_filter_enabled() const;
void set_closable(bool p_closable);
bool is_closable() const;
virtual bool has_filter() const;
virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) const;

View file

@ -295,16 +295,16 @@ void GraphEdit::_update_scroll_offset() {
set_block_minimum_size_adjust(true);
for (int i = 0; i < get_child_count(); i++) {
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
if (!graph_node) {
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
if (!graph_element) {
continue;
}
Point2 pos = graph_node->get_position_offset() * zoom;
Point2 pos = graph_element->get_position_offset() * zoom;
pos -= Point2(h_scrollbar->get_value(), v_scrollbar->get_value());
graph_node->set_position(pos);
if (graph_node->get_scale() != Vector2(zoom, zoom)) {
graph_node->set_scale(Vector2(zoom, zoom));
graph_element->set_position(pos);
if (graph_element->get_scale() != Vector2(zoom, zoom)) {
graph_element->set_scale(Vector2(zoom, zoom));
}
}
@ -328,14 +328,14 @@ void GraphEdit::_update_scroll() {
Rect2 screen_rect;
for (int i = 0; i < get_child_count(); i++) {
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
if (!graph_node) {
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
if (!graph_element) {
continue;
}
Rect2 node_rect;
node_rect.position = graph_node->get_position_offset() * zoom;
node_rect.size = graph_node->get_size() * zoom;
node_rect.position = graph_element->get_position_offset() * zoom;
node_rect.size = graph_element->get_size() * zoom;
screen_rect = screen_rect.merge(node_rect);
}
@ -378,46 +378,48 @@ void GraphEdit::_update_scroll() {
updating = false;
}
void GraphEdit::_graph_node_moved_to_front(Node *p_gn) {
GraphNode *graph_node = Object::cast_to<GraphNode>(p_gn);
ERR_FAIL_NULL(graph_node);
void GraphEdit::_graph_element_moved_to_front(Node *p_node) {
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
ERR_FAIL_NULL(graph_element);
graph_node->move_to_front();
graph_element->move_to_front();
}
void GraphEdit::_graph_node_selected(Node *p_gn) {
GraphNode *graph_node = Object::cast_to<GraphNode>(p_gn);
ERR_FAIL_NULL(graph_node);
void GraphEdit::_graph_element_selected(Node *p_node) {
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
ERR_FAIL_NULL(graph_element);
emit_signal(SNAME("node_selected"), graph_node);
emit_signal(SNAME("node_selected"), graph_element);
}
void GraphEdit::_graph_node_deselected(Node *p_gn) {
GraphNode *graph_node = Object::cast_to<GraphNode>(p_gn);
ERR_FAIL_NULL(graph_node);
void GraphEdit::_graph_element_deselected(Node *p_node) {
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
ERR_FAIL_NULL(graph_element);
emit_signal(SNAME("node_deselected"), graph_node);
emit_signal(SNAME("node_deselected"), graph_element);
}
void GraphEdit::_graph_node_resized(Vector2 p_new_minsize, Node *p_gn) {
GraphNode *graph_node = Object::cast_to<GraphNode>(p_gn);
ERR_FAIL_NULL(graph_node);
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);
graph_node->set_custom_minimum_size(p_new_minsize);
graph_element->set_size(p_new_minsize);
}
void GraphEdit::_graph_node_moved(Node *p_gn) {
GraphNode *graph_node = Object::cast_to<GraphNode>(p_gn);
ERR_FAIL_NULL(graph_node);
void GraphEdit::_graph_element_moved(Node *p_node) {
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
ERR_FAIL_NULL(graph_element);
top_layer->queue_redraw();
minimap->queue_redraw();
queue_redraw();
connections_layer->queue_redraw();
}
void GraphEdit::_graph_node_slot_updated(int p_index, Node *p_gn) {
GraphNode *graph_node = Object::cast_to<GraphNode>(p_gn);
void GraphEdit::_graph_node_slot_updated(int p_index, Node *p_node) {
GraphNode *graph_node = Object::cast_to<GraphNode>(p_node);
ERR_FAIL_NULL(graph_node);
top_layer->queue_redraw();
minimap->queue_redraw();
queue_redraw();
@ -430,19 +432,25 @@ void GraphEdit::add_child_notify(Node *p_child) {
// Keep the top layer always on top!
callable_mp((CanvasItem *)top_layer, &CanvasItem::move_to_front).call_deferred();
GraphNode *graph_node = Object::cast_to<GraphNode>(p_child);
if (graph_node) {
graph_node->set_scale(Vector2(zoom, zoom));
graph_node->connect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved).bind(graph_node));
graph_node->connect("node_selected", callable_mp(this, &GraphEdit::_graph_node_selected).bind(graph_node));
graph_node->connect("node_deselected", callable_mp(this, &GraphEdit::_graph_node_deselected).bind(graph_node));
graph_node->connect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated).bind(graph_node));
graph_node->connect("raise_request", callable_mp(this, &GraphEdit::_graph_node_moved_to_front).bind(graph_node));
graph_node->connect("resize_request", callable_mp(this, &GraphEdit::_graph_node_resized).bind(graph_node));
graph_node->connect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
graph_node->connect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw));
_graph_node_moved(graph_node);
graph_node->set_mouse_filter(MOUSE_FILTER_PASS);
GraphElement *graph_element = Object::cast_to<GraphElement>(p_child);
if (graph_element) {
graph_element->connect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_element_moved).bind(graph_element));
graph_element->connect("node_selected", callable_mp(this, &GraphEdit::_graph_element_selected).bind(graph_element));
graph_element->connect("node_deselected", callable_mp(this, &GraphEdit::_graph_element_deselected).bind(graph_element));
GraphNode *graph_node = Object::cast_to<GraphNode>(graph_element);
if (graph_node) {
graph_element->connect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated).bind(graph_element));
}
graph_element->connect("raise_request", callable_mp(this, &GraphEdit::_graph_element_moved_to_front).bind(graph_element));
graph_element->connect("resize_request", callable_mp(this, &GraphEdit::_graph_element_resized).bind(graph_element));
graph_element->connect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
graph_element->connect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw));
graph_element->set_scale(Vector2(zoom, zoom));
_graph_element_moved(graph_element);
graph_element->set_mouse_filter(MOUSE_FILTER_PASS);
}
}
@ -461,20 +469,26 @@ void GraphEdit::remove_child_notify(Node *p_child) {
callable_mp((CanvasItem *)top_layer, &CanvasItem::move_to_front).call_deferred();
}
GraphNode *graph_node = Object::cast_to<GraphNode>(p_child);
if (graph_node) {
graph_node->disconnect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved));
graph_node->disconnect("node_selected", callable_mp(this, &GraphEdit::_graph_node_selected));
graph_node->disconnect("node_deselected", callable_mp(this, &GraphEdit::_graph_node_deselected));
graph_node->disconnect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated));
graph_node->disconnect("raise_request", callable_mp(this, &GraphEdit::_graph_node_moved_to_front));
GraphElement *graph_element = Object::cast_to<GraphElement>(p_child);
if (graph_element) {
graph_element->disconnect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_element_moved));
graph_element->disconnect("node_selected", callable_mp(this, &GraphEdit::_graph_element_selected));
graph_element->disconnect("node_deselected", callable_mp(this, &GraphEdit::_graph_element_deselected));
GraphNode *graph_node = Object::cast_to<GraphNode>(graph_element);
if (graph_node) {
graph_element->disconnect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated));
}
graph_element->disconnect("raise_request", callable_mp(this, &GraphEdit::_graph_element_moved_to_front));
graph_element->disconnect("resize_request", callable_mp(this, &GraphEdit::_graph_element_resized));
// In case of the whole GraphEdit being destroyed these references can already be freed.
if (connections_layer != nullptr && connections_layer->is_inside_tree()) {
graph_node->disconnect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
graph_element->disconnect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
}
if (minimap != nullptr && minimap->is_inside_tree()) {
graph_node->disconnect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw));
graph_element->disconnect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw));
}
}
}
@ -572,11 +586,11 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
continue;
}
for (int j = 0; j < graph_node->get_connection_input_count(); j++) {
for (int j = 0; j < graph_node->get_input_port_count(); j++) {
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
// Determine slot height.
int slot_index = graph_node->get_connection_input_slot(j);
int slot_index = graph_node->get_input_port_slot(j);
Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index));
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
@ -586,11 +600,11 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
}
}
for (int j = 0; j < graph_node->get_connection_output_count(); j++) {
for (int j = 0; j < graph_node->get_output_port_count(); j++) {
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
// Determine slot height.
int slot_index = graph_node->get_connection_output_slot(j);
int slot_index = graph_node->get_output_port_slot(j);
Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index));
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
@ -616,17 +630,17 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
continue;
}
for (int j = 0; j < graph_node->get_connection_output_count(); j++) {
Vector2 pos = graph_node->get_connection_output_position(j) * zoom + graph_node->get_position();
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();
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
// Determine slot height.
int slot_index = graph_node->get_connection_output_slot(j);
int slot_index = graph_node->get_output_port_slot(j);
Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index));
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
if (is_in_output_hotzone(graph_node, j, click_pos, port_size)) {
if (valid_left_disconnect_types.has(graph_node->get_connection_output_type(j))) {
if (valid_left_disconnect_types.has(graph_node->get_output_port_type(j))) {
// Check disconnect.
for (const Connection &E : connections) {
if (E.from_node == graph_node->get_name() && E.from_port == j) {
@ -635,8 +649,8 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_from = E.to_node;
connecting_index = E.to_port;
connecting_out = false;
connecting_type = Object::cast_to<GraphNode>(to)->get_connection_input_type(E.to_port);
connecting_color = Object::cast_to<GraphNode>(to)->get_connection_input_color(E.to_port);
connecting_type = Object::cast_to<GraphNode>(to)->get_input_port_type(E.to_port);
connecting_color = Object::cast_to<GraphNode>(to)->get_input_port_color(E.to_port);
connecting_target = false;
connecting_to = pos;
@ -659,8 +673,8 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_from = graph_node->get_name();
connecting_index = j;
connecting_out = true;
connecting_type = graph_node->get_connection_output_type(j);
connecting_color = graph_node->get_connection_output_color(j);
connecting_type = graph_node->get_output_port_type(j);
connecting_color = graph_node->get_output_port_color(j);
connecting_target = false;
connecting_to = pos;
if (connecting_type >= 0) {
@ -672,18 +686,18 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
}
for (int j = 0; j < graph_node->get_connection_input_count(); j++) {
Vector2 pos = graph_node->get_connection_input_position(j) + graph_node->get_position();
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();
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
// Determine slot height.
int slot_index = graph_node->get_connection_input_slot(j);
int slot_index = graph_node->get_input_port_slot(j);
Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index));
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
if (is_in_input_hotzone(graph_node, j, click_pos, port_size)) {
if (right_disconnects || valid_right_disconnect_types.has(graph_node->get_connection_input_type(j))) {
if (right_disconnects || valid_right_disconnect_types.has(graph_node->get_input_port_type(j))) {
// Check disconnect.
for (const Connection &E : connections) {
if (E.to_node == graph_node->get_name() && E.to_port == j) {
@ -692,8 +706,8 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_from = E.from_node;
connecting_index = E.from_port;
connecting_out = true;
connecting_type = Object::cast_to<GraphNode>(fr)->get_connection_output_type(E.from_port);
connecting_color = Object::cast_to<GraphNode>(fr)->get_connection_output_color(E.from_port);
connecting_type = Object::cast_to<GraphNode>(fr)->get_output_port_type(E.from_port);
connecting_color = Object::cast_to<GraphNode>(fr)->get_output_port_color(E.from_port);
connecting_target = false;
connecting_to = pos;
just_disconnected = true;
@ -715,8 +729,8 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_from = graph_node->get_name();
connecting_index = j;
connecting_out = false;
connecting_type = graph_node->get_connection_input_type(j);
connecting_color = graph_node->get_connection_input_color(j);
connecting_type = graph_node->get_input_port_type(j);
connecting_color = graph_node->get_input_port_color(j);
connecting_target = false;
connecting_to = pos;
if (connecting_type >= 0) {
@ -749,16 +763,16 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
if (!connecting_out) {
for (int j = 0; j < graph_node->get_connection_output_count(); j++) {
Vector2 pos = graph_node->get_connection_output_position(j) + graph_node->get_position();
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();
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
// Determine slot height.
int slot_index = graph_node->get_connection_output_slot(j);
int slot_index = graph_node->get_output_port_slot(j);
Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index));
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
int type = graph_node->get_connection_output_type(j);
int type = graph_node->get_output_port_type(j);
if ((type == connecting_type ||
valid_connection_types.has(ConnectionType(connecting_type, type))) &&
is_in_output_hotzone(graph_node, j, mpos, port_size)) {
@ -773,16 +787,16 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
}
} else {
for (int j = 0; j < graph_node->get_connection_input_count(); j++) {
Vector2 pos = graph_node->get_connection_input_position(j) + graph_node->get_position();
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();
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
// Determine slot height.
int slot_index = graph_node->get_connection_input_slot(j);
int slot_index = graph_node->get_input_port_slot(j);
Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index));
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
int type = graph_node->get_connection_input_type(j);
int type = graph_node->get_input_port_type(j);
if ((type == connecting_type || valid_connection_types.has(ConnectionType(connecting_type, type))) &&
is_in_input_hotzone(graph_node, j, mpos, port_size)) {
if (!is_node_hover_valid(connecting_from, connecting_index, graph_node->get_name(), j)) {
@ -851,17 +865,17 @@ bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &mpos
}
}
bool GraphEdit::is_in_input_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
bool GraphEdit::is_in_input_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
bool success;
if (GDVIRTUAL_CALL(_is_in_input_hotzone, p_graph_node, p_port, p_mouse_pos, success)) {
if (GDVIRTUAL_CALL(_is_in_input_hotzone, p_graph_node, p_port_idx, p_mouse_pos, success)) {
return success;
} else {
Vector2 pos = p_graph_node->get_connection_input_position(p_port) + p_graph_node->get_position();
Vector2 pos = p_graph_node->get_input_port_position(p_port_idx) * zoom + p_graph_node->get_position();
return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, true);
}
}
bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
if (p_graph_node->is_resizable()) {
Ref<Texture2D> resizer = p_graph_node->get_theme_icon(SNAME("resizer"));
Rect2 resizer_rect = Rect2(p_graph_node->get_position() / zoom + p_graph_node->get_size() - resizer->get_size(), resizer->get_size());
@ -871,10 +885,10 @@ bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_port, const
}
bool success;
if (GDVIRTUAL_CALL(_is_in_output_hotzone, p_graph_node, p_port, p_mouse_pos, success)) {
if (GDVIRTUAL_CALL(_is_in_output_hotzone, p_graph_node, p_port_idx, p_mouse_pos, success)) {
return success;
} else {
Vector2 pos = p_graph_node->get_connection_output_position(p_port) + p_graph_node->get_position();
Vector2 pos = p_graph_node->get_output_port_position(p_port_idx) * zoom + p_graph_node->get_position();
return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, false);
}
}
@ -978,10 +992,10 @@ void GraphEdit::_connections_layer_draw() {
continue;
}
Vector2 frompos = gnode_from->get_connection_output_position(c.from_port) + gnode_from->get_position_offset() * zoom;
Color color = gnode_from->get_connection_output_color(c.from_port);
Vector2 topos = gnode_to->get_connection_input_position(c.to_port) + gnode_to->get_position_offset() * zoom;
Color tocolor = gnode_to->get_connection_input_color(c.to_port);
Vector2 frompos = gnode_from->get_output_port_position(c.from_port) * zoom + gnode_from->get_position_offset() * zoom;
Color color = gnode_from->get_output_port_color(c.from_port);
Vector2 topos = gnode_to->get_input_port_position(c.to_port) * zoom + gnode_to->get_position_offset() * zoom;
Color tocolor = gnode_to->get_input_port_color(c.to_port);
if (c.activity > 0) {
color = color.lerp(activity_color, c.activity);
@ -1005,9 +1019,9 @@ void GraphEdit::_top_layer_draw() {
ERR_FAIL_NULL(graph_node_from);
Vector2 pos;
if (connecting_out) {
pos = graph_node_from->get_connection_output_position(connecting_index);
pos = graph_node_from->get_output_port_position(connecting_index) * zoom;
} else {
pos = graph_node_from->get_connection_input_position(connecting_index);
pos = graph_node_from->get_input_port_position(connecting_index) * zoom;
}
pos += graph_node_from->get_position();
@ -1061,7 +1075,7 @@ void GraphEdit::_minimap_draw() {
Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox(SNAME("node"))->duplicate();
// Override default values with colors provided by the GraphNode's stylebox, if possible.
Ref<StyleBoxFlat> sb_frame = graph_node->get_theme_stylebox(graph_node->is_selected() ? "selected_frame" : "frame");
Ref<StyleBoxFlat> sb_frame = graph_node->get_theme_stylebox(graph_node->is_selected() ? "panel_selected" : "panel");
if (sb_frame.is_valid()) {
Color node_color = sb_frame->get_bg_color();
sb_minimap->set_bg_color(node_color);
@ -1085,12 +1099,12 @@ void GraphEdit::_minimap_draw() {
continue;
}
Vector2 from_port_position = graph_node_from->get_position_offset() + graph_node_from->get_connection_output_position(E.from_port);
Vector2 from_port_position = graph_node_from->get_position_offset() * zoom + graph_node_from->get_output_port_position(E.from_port) * zoom;
Vector2 from_position = minimap->_convert_from_graph_position(from_port_position - graph_offset) + minimap_offset;
Color from_color = graph_node_from->get_connection_output_color(E.from_port);
Vector2 to_port_position = graph_node_to->get_position_offset() + graph_node_to->get_connection_input_position(E.to_port);
Color from_color = graph_node_from->get_output_port_color(E.from_port);
Vector2 to_port_position = graph_node_to->get_position_offset() * zoom + graph_node_to->get_input_port_position(E.to_port) * zoom;
Vector2 to_position = minimap->_convert_from_graph_position(to_port_position - graph_offset) + minimap_offset;
Color to_color = graph_node_to->get_connection_input_color(E.to_port);
Color to_color = graph_node_to->get_input_port_color(E.to_port);
if (E.activity > 0) {
from_color = from_color.lerp(activity_color, E.activity);
@ -1137,9 +1151,9 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
just_selected = true;
drag_accum += mm->get_relative();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
if (graph_node && graph_node->is_selected() && graph_node->is_draggable()) {
Vector2 pos = (graph_node->get_drag_from() * zoom + drag_accum) / zoom;
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;
// Snapping can be toggled temporarily by holding down Ctrl.
// This is done here as to not toggle the grid when holding down Ctrl.
@ -1147,7 +1161,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
pos = pos.snapped(Vector2(snapping_distance, snapping_distance));
}
graph_node->set_position_offset(pos);
graph_element->set_position_offset(pos);
}
}
}
@ -1158,18 +1172,18 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
box_selecting_rect = Rect2(box_selecting_from.min(box_selecting_to), (box_selecting_from - box_selecting_to).abs());
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
if (!graph_node) {
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
if (!graph_element) {
continue;
}
Rect2 r = graph_node->get_rect();
Rect2 r = graph_element->get_rect();
bool in_box = r.intersects(box_selecting_rect);
if (in_box) {
graph_node->set_selected(box_selection_mode_additive);
graph_element->set_selected(box_selection_mode_additive);
} else {
graph_node->set_selected(prev_selected.find(graph_node) != nullptr);
graph_element->set_selected(prev_selected.find(graph_element) != nullptr);
}
}
@ -1183,12 +1197,12 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
if (box_selecting) {
box_selecting = false;
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
if (!graph_node) {
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
if (!graph_element) {
continue;
}
graph_node->set_selected(prev_selected.find(graph_node) != nullptr);
graph_element->set_selected(prev_selected.find(graph_element) != nullptr);
}
top_layer->queue_redraw();
minimap->queue_redraw();
@ -1205,12 +1219,12 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
if (!just_selected && drag_accum == Vector2() && Input::get_singleton()->is_key_pressed(Key::CTRL)) {
// Deselect current node.
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
if (graph_node) {
Rect2 r = graph_node->get_rect();
if (graph_element) {
Rect2 r = graph_element->get_rect();
if (r.has_point(mb->get_position())) {
graph_node->set_selected(false);
graph_element->set_selected(false);
}
}
}
@ -1218,9 +1232,9 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
if (drag_accum != Vector2()) {
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
if (graph_node && graph_node->is_selected()) {
graph_node->set_drag(false);
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
if (graph_element && graph_element->is_selected()) {
graph_element->set_drag(false);
}
}
}
@ -1240,27 +1254,27 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
// Node selection logic.
if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
GraphNode *graph_node = nullptr;
GraphElement *graph_element = nullptr;
// Find node which was clicked on.
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *selected_gcontrol = Object::cast_to<GraphNode>(get_child(i));
GraphElement *selected_element = Object::cast_to<GraphElement>(get_child(i));
if (!selected_gcontrol) {
if (!selected_element) {
continue;
}
if (selected_gcontrol->is_resizing()) {
if (selected_element->is_resizing()) {
continue;
}
if (selected_gcontrol->has_point((mb->get_position() - selected_gcontrol->get_position()) / zoom)) {
graph_node = selected_gcontrol;
if (selected_element->has_point((mb->get_position() - selected_element->get_position()) / zoom)) {
graph_element = selected_element;
break;
}
}
if (graph_node) {
if (graph_element) {
if (_filter_input(mb->get_position())) {
return;
}
@ -1268,26 +1282,26 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
// Left-clicked on a node, select it.
dragging = true;
drag_accum = Vector2();
just_selected = !graph_node->is_selected();
if (!graph_node->is_selected() && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
just_selected = !graph_element->is_selected();
if (!graph_element->is_selected() && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
for (int i = 0; i < get_child_count(); i++) {
GraphNode *o_gn = Object::cast_to<GraphNode>(get_child(i));
if (!o_gn) {
GraphElement *child_element = Object::cast_to<GraphElement>(get_child(i));
if (!child_element) {
continue;
}
o_gn->set_selected(o_gn == graph_node);
child_element->set_selected(child_element == graph_element);
}
}
graph_node->set_selected(true);
graph_element->set_selected(true);
for (int i = 0; i < get_child_count(); i++) {
GraphNode *o_gn = Object::cast_to<GraphNode>(get_child(i));
if (!o_gn) {
GraphElement *child_element = Object::cast_to<GraphElement>(get_child(i));
if (!child_element) {
continue;
}
if (o_gn->is_selected()) {
o_gn->set_drag(true);
if (child_element->is_selected()) {
child_element->set_drag(true);
}
}
@ -1306,34 +1320,34 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
box_selection_mode_additive = true;
prev_selected.clear();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i));
if (!gn2 || !gn2->is_selected()) {
GraphElement *child_element = Object::cast_to<GraphElement>(get_child(i));
if (!child_element || !child_element->is_selected()) {
continue;
}
prev_selected.push_back(gn2);
prev_selected.push_back(child_element);
}
} else if (mb->is_shift_pressed()) {
box_selection_mode_additive = false;
prev_selected.clear();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i));
if (!gn2 || !gn2->is_selected()) {
GraphElement *child_element = Object::cast_to<GraphElement>(get_child(i));
if (!child_element || !child_element->is_selected()) {
continue;
}
prev_selected.push_back(gn2);
prev_selected.push_back(child_element);
}
} else {
box_selection_mode_additive = true;
prev_selected.clear();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i));
if (!gn2) {
GraphElement *child_element = Object::cast_to<GraphElement>(get_child(i));
if (!child_element) {
continue;
}
gn2->set_selected(false);
child_element->set_selected(false);
}
}
}
@ -1372,7 +1386,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
}
}
emit_signal(SNAME("delete_nodes_request"), nodes);
emit_signal(SNAME("close_nodes_request"), nodes);
accept_event();
}
}
@ -1886,7 +1900,7 @@ void GraphEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("node_deselected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
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("delete_nodes_request", PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_ARRAY_TYPE, "StringName")));
ADD_SIGNAL(MethodInfo("close_nodes_request", PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_ARRAY_TYPE, "StringName")));
ADD_SIGNAL(MethodInfo("begin_node_move"));
ADD_SIGNAL(MethodInfo("end_node_move"));
ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "offset")));

View file

@ -195,7 +195,7 @@ private:
Point2 box_selecting_from;
Point2 box_selecting_to;
Rect2 box_selecting_rect;
List<GraphNode *> prev_selected;
List<GraphElement *> prev_selected;
bool setting_scroll_offset = false;
bool right_disconnects = false;
@ -229,12 +229,12 @@ private:
void _draw_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_zoom);
void _graph_node_selected(Node *p_gn);
void _graph_node_deselected(Node *p_gn);
void _graph_node_moved_to_front(Node *p_gn);
void _graph_node_resized(Vector2 p_new_minsize, Node *p_gn);
void _graph_node_moved(Node *p_gn);
void _graph_node_slot_updated(int p_index, Node *p_gn);
void _graph_element_selected(Node *p_node);
void _graph_element_deselected(Node *p_node);
void _graph_element_moved_to_front(Node *p_node);
void _graph_element_resized(Vector2 p_new_minsize, Node *p_node);
void _graph_element_moved(Node *p_node);
void _graph_node_slot_updated(int p_index, Node *p_node);
void _update_scroll();
void _update_scroll_offset();
@ -269,8 +269,8 @@ protected:
void _notification(int p_what);
virtual bool is_in_input_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
virtual bool is_in_output_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
virtual bool is_in_input_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
virtual bool is_in_output_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
GDVIRTUAL2RC(Vector<Vector2>, _get_connection_line, Vector2, Vector2)
GDVIRTUAL3R(bool, _is_in_input_hotzone, Object *, int, Vector2)

View file

@ -408,8 +408,8 @@ void GraphEditArranger::_calculate_inner_shifts(Dictionary &r_inner_shifts, cons
int port_from = ports.first;
int port_to = ports.second;
Vector2 pos_from = gnode_from->get_connection_output_position(port_from) * graph_edit->get_zoom();
Vector2 pos_to = gnode_to->get_connection_input_position(port_to) * graph_edit->get_zoom();
Vector2 pos_from = gnode_from->get_output_port_position(port_from) * graph_edit->get_zoom();
Vector2 pos_to = gnode_to->get_input_port_position(port_to) * graph_edit->get_zoom();
real_t s = (real_t)r_inner_shifts[u] + (pos_from.y - pos_to.y) / graph_edit->get_zoom();
r_inner_shifts[v] = s;
@ -459,8 +459,8 @@ float GraphEditArranger::_calculate_threshold(StringName p_v, StringName p_w, co
if (incoming.from_node != StringName()) {
GraphNode *gnode_from = Object::cast_to<GraphNode>(r_node_names[incoming.from_node]);
GraphNode *gnode_to = Object::cast_to<GraphNode>(r_node_names[p_w]);
Vector2 pos_from = gnode_from->get_connection_output_position(incoming.from_port) * graph_edit->get_zoom();
Vector2 pos_to = gnode_to->get_connection_input_position(incoming.to_port) * graph_edit->get_zoom();
Vector2 pos_from = gnode_from->get_output_port_position(incoming.from_port) * graph_edit->get_zoom();
Vector2 pos_to = gnode_to->get_input_port_position(incoming.to_port) * graph_edit->get_zoom();
// If connected block node is selected, calculate thershold or add current block to list.
if (gnode_from->is_selected()) {
@ -491,8 +491,8 @@ float GraphEditArranger::_calculate_threshold(StringName p_v, StringName p_w, co
if (outgoing.to_node != StringName()) {
GraphNode *gnode_from = Object::cast_to<GraphNode>(r_node_names[p_w]);
GraphNode *gnode_to = Object::cast_to<GraphNode>(r_node_names[outgoing.to_node]);
Vector2 pos_from = gnode_from->get_connection_output_position(outgoing.from_port) * graph_edit->get_zoom();
Vector2 pos_to = gnode_to->get_connection_input_position(outgoing.to_port) * graph_edit->get_zoom();
Vector2 pos_from = gnode_from->get_output_port_position(outgoing.from_port) * graph_edit->get_zoom();
Vector2 pos_to = gnode_to->get_input_port_position(outgoing.to_port) * graph_edit->get_zoom();
// If connected block node is selected, calculate thershold or add current block to list.
if (gnode_to->is_selected()) {

244
scene/gui/graph_element.cpp Normal file
View file

@ -0,0 +1,244 @@
/**************************************************************************/
/* graph_element.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. */
/**************************************************************************/
#include "graph_element.h"
#include "core/string/translation.h"
#include "scene/gui/graph_edit.h"
#ifdef TOOLS_ENABLED
void GraphElement::_edit_set_position(const Point2 &p_position) {
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
if (graph) {
Point2 offset = (p_position + graph->get_scroll_offset()) * graph->get_zoom();
set_position_offset(offset);
}
set_position(p_position);
}
#endif
void GraphElement::_resort() {
Size2 size = get_size();
for (int i = 0; i < get_child_count(); i++) {
Control *child = Object::cast_to<Control>(get_child(i));
if (!child || !child->is_visible_in_tree()) {
continue;
}
if (child->is_set_as_top_level()) {
continue;
}
fit_child_in_rect(child, Rect2(Point2(), size));
}
}
Size2 GraphElement::get_minimum_size() const {
Size2 minsize;
for (int i = 0; i < get_child_count(); i++) {
Control *child = Object::cast_to<Control>(get_child(i));
if (!child) {
continue;
}
if (child->is_set_as_top_level()) {
continue;
}
Size2i size = child->get_combined_minimum_size();
minsize.width = MAX(minsize.width, size.width);
minsize.height = MAX(minsize.height, size.height);
}
return minsize;
}
void GraphElement::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_SORT_CHILDREN: {
_resort();
} break;
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
update_minimum_size();
queue_redraw();
} break;
}
}
void GraphElement::_validate_property(PropertyInfo &p_property) const {
Control::_validate_property(p_property);
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
if (graph) {
if (p_property.name == "position") {
p_property.usage |= PROPERTY_USAGE_READ_ONLY;
}
}
}
void GraphElement::set_position_offset(const Vector2 &p_offset) {
if (position_offset == p_offset) {
return;
}
position_offset = p_offset;
emit_signal(SNAME("position_offset_changed"));
queue_redraw();
}
Vector2 GraphElement::get_position_offset() const {
return position_offset;
}
void GraphElement::set_selected(bool p_selected) {
if (!is_selectable() || selected == p_selected) {
return;
}
selected = p_selected;
emit_signal(p_selected ? SNAME("node_selected") : SNAME("node_deselected"));
queue_redraw();
}
bool GraphElement::is_selected() {
return selected;
}
void GraphElement::set_drag(bool p_drag) {
if (p_drag) {
drag_from = get_position_offset();
} else {
emit_signal(SNAME("dragged"), drag_from, get_position_offset()); // Required for undo/redo.
}
}
Vector2 GraphElement::get_drag_from() {
return drag_from;
}
void GraphElement::gui_input(const Ref<InputEvent> &p_ev) {
ERR_FAIL_COND(p_ev.is_null());
Ref<InputEventMouseButton> mb = p_ev;
if (mb.is_valid()) {
ERR_FAIL_COND_MSG(get_parent_control() == nullptr, "GraphElement must be the child of a GraphEdit node.");
if (mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
Vector2 mpos = mb->get_position();
Ref<Texture2D> resizer = get_theme_icon(SNAME("resizer"));
if (resizable && mpos.x > get_size().x - resizer->get_width() && mpos.y > get_size().y - resizer->get_height()) {
resizing = true;
resizing_from = mpos;
resizing_from_size = get_size();
accept_event();
return;
}
emit_signal(SNAME("raise_request"));
}
if (!mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
resizing = false;
}
}
Ref<InputEventMouseMotion> mm = p_ev;
if (resizing && mm.is_valid()) {
Vector2 mpos = mm->get_position();
Vector2 diff = mpos - resizing_from;
emit_signal(SNAME("resize_request"), resizing_from_size + diff);
}
}
void GraphElement::set_resizable(bool p_enable) {
if (resizable == p_enable) {
return;
}
resizable = p_enable;
queue_redraw();
}
bool GraphElement::is_resizable() const {
return resizable;
}
void GraphElement::set_draggable(bool p_draggable) {
draggable = p_draggable;
}
bool GraphElement::is_draggable() {
return draggable;
}
void GraphElement::set_selectable(bool p_selectable) {
if (!p_selectable) {
set_selected(false);
}
selectable = p_selectable;
}
bool GraphElement::is_selectable() {
return selectable;
}
void GraphElement::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_resizable", "resizable"), &GraphElement::set_resizable);
ClassDB::bind_method(D_METHOD("is_resizable"), &GraphElement::is_resizable);
ClassDB::bind_method(D_METHOD("set_draggable", "draggable"), &GraphElement::set_draggable);
ClassDB::bind_method(D_METHOD("is_draggable"), &GraphElement::is_draggable);
ClassDB::bind_method(D_METHOD("set_selectable", "selectable"), &GraphElement::set_selectable);
ClassDB::bind_method(D_METHOD("is_selectable"), &GraphElement::is_selectable);
ClassDB::bind_method(D_METHOD("set_selected", "selected"), &GraphElement::set_selected);
ClassDB::bind_method(D_METHOD("is_selected"), &GraphElement::is_selected);
ClassDB::bind_method(D_METHOD("set_position_offset", "offset"), &GraphElement::set_position_offset);
ClassDB::bind_method(D_METHOD("get_position_offset"), &GraphElement::get_position_offset);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position_offset"), "set_position_offset", "get_position_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resizable"), "set_resizable", "is_resizable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draggable"), "set_draggable", "is_draggable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selectable"), "set_selectable", "is_selectable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selected"), "set_selected", "is_selected");
ADD_SIGNAL(MethodInfo("position_offset_changed"));
ADD_SIGNAL(MethodInfo("node_selected"));
ADD_SIGNAL(MethodInfo("node_deselected"));
ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to")));
ADD_SIGNAL(MethodInfo("raise_request"));
ADD_SIGNAL(MethodInfo("close_request"));
ADD_SIGNAL(MethodInfo("resize_request", PropertyInfo(Variant::VECTOR2, "new_minsize")));
}

93
scene/gui/graph_element.h Normal file
View file

@ -0,0 +1,93 @@
/**************************************************************************/
/* graph_element.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef GRAPH_ELEMENT_H
#define GRAPH_ELEMENT_H
#include "scene/gui/container.h"
class GraphElement : public Container {
GDCLASS(GraphElement, Container);
protected:
bool selected = false;
bool resizable = false;
bool resizing = false;
bool draggable = true;
bool selectable = true;
Vector2 drag_from;
Vector2 resizing_from;
Vector2 resizing_from_size;
Vector2 position_offset;
#ifdef TOOLS_ENABLED
void _edit_set_position(const Point2 &p_position) override;
#endif
protected:
virtual void gui_input(const Ref<InputEvent> &p_ev) override;
void _notification(int p_what);
static void _bind_methods();
virtual void _resort();
void _validate_property(PropertyInfo &p_property) const;
public:
void set_position_offset(const Vector2 &p_offset);
Vector2 get_position_offset() const;
void set_selected(bool p_selected);
bool is_selected();
void set_drag(bool p_drag);
Vector2 get_drag_from();
void set_resizable(bool p_enable);
bool is_resizable() const;
void set_draggable(bool p_draggable);
bool is_draggable();
void set_selectable(bool p_selectable);
bool is_selectable();
virtual Size2 get_minimum_size() const override;
bool is_resizing() const {
return resizing;
}
GraphElement() {}
};
#endif // GRAPH_ELEMENT_H

File diff suppressed because it is too large Load diff

View file

@ -31,11 +31,33 @@
#ifndef GRAPH_NODE_H
#define GRAPH_NODE_H
#include "scene/gui/container.h"
#include "scene/resources/text_line.h"
#include "scene/gui/graph_element.h"
class GraphNode : public Container {
GDCLASS(GraphNode, Container);
class HBoxContainer;
class GraphNode : public GraphElement {
GDCLASS(GraphNode, GraphElement);
struct Slot {
bool enable_left = false;
int type_left = 0;
Color color_left = Color(1, 1, 1, 1);
Ref<Texture2D> custom_port_icon_left;
bool enable_right = false;
int type_right = 0;
Color color_right = Color(1, 1, 1, 1);
Ref<Texture2D> custom_port_icon_right;
bool draw_stylebox = true;
};
struct PortCache {
Vector2 pos;
int slot_index;
int type = 0;
Color color;
};
struct _MinSizeCache {
int min_size;
@ -43,158 +65,77 @@ class GraphNode : public Container {
int final_size;
};
public:
enum Overlay {
OVERLAY_DISABLED,
OVERLAY_BREAKPOINT,
OVERLAY_POSITION
};
private:
struct Slot {
bool enable_left = false;
int type_left = 0;
Color color_left = Color(1, 1, 1, 1);
bool enable_right = false;
int type_right = 0;
Color color_right = Color(1, 1, 1, 1);
Ref<Texture2D> custom_slot_left;
Ref<Texture2D> custom_slot_right;
bool draw_stylebox = true;
};
HBoxContainer *titlebar_hbox = nullptr;
Label *title_label = nullptr;
String title;
Ref<TextLine> title_buf;
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
bool show_close = false;
Vector2 position_offset;
bool comment = false;
bool resizable = false;
bool draggable = true;
bool selectable = true;
bool resizing = false;
Vector2 resizing_from;
Vector2 resizing_from_size;
Rect2 close_rect;
Vector<int> cache_y;
struct PortCache {
Vector2 position;
int height;
int slot_idx;
int type = 0;
Color color;
};
Vector<PortCache> left_port_cache;
Vector<PortCache> right_port_cache;
HashMap<int, Slot> slot_info;
HashMap<int, Slot> slot_table;
bool connpos_dirty = true;
Vector<int> slot_y_cache;
void _connpos_update();
void _resort();
void _shape();
bool port_pos_dirty = true;
Vector2 drag_from;
bool selected = false;
Overlay overlay = OVERLAY_DISABLED;
#ifdef TOOLS_ENABLED
void _edit_set_position(const Point2 &p_position) override;
#endif
void _port_pos_update();
protected:
virtual void gui_input(const Ref<InputEvent> &p_ev) override;
void _notification(int p_what);
static void _bind_methods();
virtual void _resort() override;
virtual void draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Color &p_color);
GDVIRTUAL4(_draw_port, int, Point2i, bool, const Color &);
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
void _validate_property(PropertyInfo &p_property) const;
public:
void set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left = Ref<Texture2D>(), const Ref<Texture2D> &p_custom_right = Ref<Texture2D>(), bool p_draw_stylebox = true);
void clear_slot(int p_idx);
void clear_all_slots();
bool is_slot_enabled_left(int p_idx) const;
void set_slot_enabled_left(int p_idx, bool p_enable_left);
void set_slot_type_left(int p_idx, int p_type_left);
int get_slot_type_left(int p_idx) const;
void set_slot_color_left(int p_idx, const Color &p_color_left);
Color get_slot_color_left(int p_idx) const;
bool is_slot_enabled_right(int p_idx) const;
void set_slot_enabled_right(int p_idx, bool p_enable_right);
void set_slot_type_right(int p_idx, int p_type_right);
int get_slot_type_right(int p_idx) const;
void set_slot_color_right(int p_idx, const Color &p_color_right);
Color get_slot_color_right(int p_idx) const;
bool is_slot_draw_stylebox(int p_idx) const;
void set_slot_draw_stylebox(int p_idx, bool p_enable);
void set_title(const String &p_title);
String get_title() const;
void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const;
HBoxContainer *get_titlebar_hbox();
void set_language(const String &p_language);
String get_language() const;
void set_slot(int p_slot_index, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left = Ref<Texture2D>(), const Ref<Texture2D> &p_custom_right = Ref<Texture2D>(), bool p_draw_stylebox = true);
void clear_slot(int p_slot_index);
void clear_all_slots();
void set_position_offset(const Vector2 &p_offset);
Vector2 get_position_offset() const;
bool is_slot_enabled_left(int p_slot_index) const;
void set_slot_enabled_left(int p_slot_index, bool p_enable);
void set_selected(bool p_selected);
bool is_selected();
void set_slot_type_left(int p_slot_index, int p_type);
int get_slot_type_left(int p_slot_index) const;
void set_drag(bool p_drag);
Vector2 get_drag_from();
void set_slot_color_left(int p_slot_index, const Color &p_color);
Color get_slot_color_left(int p_slot_index) const;
void set_show_close_button(bool p_enable);
bool is_close_button_visible() const;
bool is_slot_enabled_right(int p_slot_index) const;
void set_slot_enabled_right(int p_slot_index, bool p_enable);
int get_connection_input_count();
int get_connection_input_height(int p_port);
Vector2 get_connection_input_position(int p_port);
int get_connection_input_type(int p_port);
Color get_connection_input_color(int p_port);
int get_connection_input_slot(int p_port);
void set_slot_type_right(int p_slot_index, int p_type);
int get_slot_type_right(int p_slot_index) const;
int get_connection_output_count();
int get_connection_output_height(int p_port);
Vector2 get_connection_output_position(int p_port);
int get_connection_output_type(int p_port);
Color get_connection_output_color(int p_port);
int get_connection_output_slot(int p_port);
void set_slot_color_right(int p_slot_index, const Color &p_color);
Color get_slot_color_right(int p_slot_index) const;
void set_overlay(Overlay p_overlay);
Overlay get_overlay() const;
bool is_slot_draw_stylebox(int p_slot_index) const;
void set_slot_draw_stylebox(int p_slot_index, bool p_enable);
void set_resizable(bool p_enable);
bool is_resizable() const;
int get_input_port_count();
Vector2 get_input_port_position(int p_port_idx);
int get_input_port_type(int p_port_idx);
Color get_input_port_color(int p_port_idx);
int get_input_port_slot(int p_port_idx);
void set_draggable(bool p_draggable);
bool is_draggable();
void set_selectable(bool p_selectable);
bool is_selectable();
int get_output_port_count();
Vector2 get_output_port_position(int p_port_idx);
int get_output_port_type(int p_port_idx);
Color get_output_port_color(int p_port_idx);
int get_output_port_slot(int p_port_idx);
virtual Size2 get_minimum_size() const override;
@ -203,13 +144,7 @@ public:
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
bool is_resizing() const {
return resizing;
}
GraphNode();
};
VARIANT_ENUM_CAST(GraphNode::Overlay)
#endif // GRAPH_NODE_H

View file

@ -439,6 +439,7 @@ void register_scene_types() {
GDREGISTER_CLASS(HSplitContainer);
GDREGISTER_CLASS(VSplitContainer);
GDREGISTER_CLASS(GraphElement);
GDREGISTER_CLASS(GraphNode);
GDREGISTER_CLASS(GraphEdit);

View file

@ -330,6 +330,14 @@ void VisualShaderNode::set_disabled(bool p_disabled) {
disabled = p_disabled;
}
bool VisualShaderNode::is_closable() const {
return closable;
}
void VisualShaderNode::set_closable(bool p_closable) {
closable = p_closable;
}
Vector<VisualShader::DefaultTextureParam> VisualShaderNode::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
return Vector<VisualShader::DefaultTextureParam>();
}

View file

@ -265,6 +265,7 @@ class VisualShaderNode : public Resource {
protected:
bool simple_decl = true;
bool disabled = false;
bool closable = false;
static void _bind_methods();
@ -331,6 +332,9 @@ public:
bool is_disabled() const;
void set_disabled(bool p_disabled = true);
bool is_closable() const;
void set_closable(bool p_closable = true);
virtual Vector<StringName> get_editable_properties() const;
virtual HashMap<StringName, String> get_editable_properties_names() const;

View file

@ -699,40 +699,43 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("icon_max_width", "PopupMenu", 0);
// GraphNode
Ref<StyleBoxFlat> graphnode_normal = make_flat_stylebox(style_normal_color, 18, 42, 18, 12);
graphnode_normal->set_border_width(SIDE_TOP, 30);
Ref<StyleBoxFlat> graphnode_normal = make_flat_stylebox(style_normal_color, 18, 12, 18, 12);
graphnode_normal->set_border_color(Color(0.325, 0.325, 0.325, 0.6));
Ref<StyleBoxFlat> graphnode_selected = graphnode_normal->duplicate();
graphnode_selected->set_border_color(Color(0.625, 0.625, 0.625, 0.6));
Ref<StyleBoxFlat> graphnode_comment_normal = make_flat_stylebox(style_pressed_color, 18, 42, 18, 12, 3, true, 2);
graphnode_comment_normal->set_border_color(style_pressed_color);
Ref<StyleBoxFlat> graphnode_comment_selected = graphnode_comment_normal->duplicate();
graphnode_comment_selected->set_border_color(style_hover_color);
Ref<StyleBoxFlat> graphnode_breakpoint = make_flat_stylebox(style_pressed_color, 18, 42, 18, 12, 6, true, 4);
graphnode_breakpoint->set_border_color(Color(0.9, 0.29, 0.3));
Ref<StyleBoxFlat> graphnode_position = make_flat_stylebox(style_pressed_color, 18, 42, 18, 12, 6, true, 4);
graphnode_position->set_border_color(Color(0.98, 0.89, 0.27));
Ref<StyleBoxFlat> graphn_sb_titlebar = make_flat_stylebox(style_normal_color.lightened(0.3), 4, 4, 4, 4);
Ref<StyleBoxFlat> graphn_sb_titlebar_selected = graphnode_normal->duplicate();
graphn_sb_titlebar_selected->set_bg_color(Color(1.0, 0.625, 0.625, 0.6));
Ref<StyleBoxEmpty> graphnode_slot = make_empty_stylebox(0, 0, 0, 0);
theme->set_stylebox("frame", "GraphNode", graphnode_normal);
theme->set_stylebox("selected_frame", "GraphNode", graphnode_selected);
theme->set_stylebox("breakpoint", "GraphNode", graphnode_breakpoint);
theme->set_stylebox("position", "GraphNode", graphnode_position);
theme->set_stylebox("panel", "GraphNode", graphnode_normal);
theme->set_stylebox("panel_selected", "GraphNode", graphnode_selected);
theme->set_stylebox("titlebar", "GraphNode", graphn_sb_titlebar);
theme->set_stylebox("titlebar_selected", "GraphNode", graphn_sb_titlebar_selected);
theme->set_stylebox("slot", "GraphNode", graphnode_slot);
theme->set_icon("port", "GraphNode", icons["graph_port"]);
theme->set_icon("close", "GraphNode", icons["close"]);
theme->set_icon("resizer", "GraphNode", icons["resizer_se"]);
theme->set_font("title_font", "GraphNode", Ref<Font>());
theme->set_color("title_color", "GraphNode", control_font_color);
theme->set_color("close_color", "GraphNode", control_font_color);
theme->set_color("resizer_color", "GraphNode", control_font_color);
theme->set_constant("separation", "GraphNode", Math::round(2 * scale));
theme->set_constant("title_offset", "GraphNode", Math::round(26 * scale));
theme->set_constant("title_h_offset", "GraphNode", 0);
theme->set_constant("close_offset", "GraphNode", Math::round(22 * scale));
theme->set_constant("close_h_offset", "GraphNode", Math::round(12 * scale));
theme->set_constant("port_offset", "GraphNode", 0);
theme->set_constant("port_h_offset", "GraphNode", 0);
// GraphNodes's title Label.
theme->set_type_variation("GraphNodeTitleLabel", "Label");
theme->set_stylebox("normal", "GraphNodeTitleLabel", make_empty_stylebox(0, 0, 0, 0));
theme->set_font("font", "GraphNodeTitleLabel", Ref<Font>());
theme->set_font_size("font_size", "GraphNodeTitleLabel", -1);
theme->set_color("font_color", "GraphNodeTitleLabel", control_font_color);
theme->set_color("font_shadow_color", "GraphNodeTitleLabel", Color(0, 0, 0, 0));
theme->set_color("font_outline_color", "GraphNodeTitleLabel", control_font_color);
theme->set_constant("shadow_offset_x", "GraphNodeTitleLabel", Math::round(1 * scale));
theme->set_constant("shadow_offset_y", "GraphNodeTitleLabel", Math::round(1 * scale));
theme->set_constant("outline_size", "GraphNodeTitleLabel", 0);
theme->set_constant("shadow_outline_size", "GraphNodeTitleLabel", Math::round(1 * scale));
theme->set_constant("line_spacing", "GraphNodeTitleLabel", Math::round(3 * scale));
// Tree