mirror of
https://github.com/godotengine/godot.git
synced 2024-11-21 19:42:43 +00:00
Add GraphFrame and integrate it in VisualShader
This commit is contained in:
parent
7a42afbba0
commit
a81561cbd9
@ -109,6 +109,14 @@
|
||||
Rearranges selected nodes in a layout with minimum crossings between connections and uniform horizontal and vertical gap between nodes.
|
||||
</description>
|
||||
</method>
|
||||
<method name="attach_graph_element_to_frame">
|
||||
<return type="void" />
|
||||
<param index="0" name="element" type="StringName" />
|
||||
<param index="1" name="frame" type="StringName" />
|
||||
<description>
|
||||
Attaches the [param element] [GraphElement] to the [param frame] [GraphFrame].
|
||||
</description>
|
||||
</method>
|
||||
<method name="clear_connections">
|
||||
<return type="void" />
|
||||
<description>
|
||||
@ -125,6 +133,13 @@
|
||||
Create a connection between the [param from_port] of the [param from_node] [GraphNode] and the [param to_port] of the [param to_node] [GraphNode]. If the connection already exists, no connection is created.
|
||||
</description>
|
||||
</method>
|
||||
<method name="detach_graph_element_from_frame">
|
||||
<return type="void" />
|
||||
<param index="0" name="element" type="StringName" />
|
||||
<description>
|
||||
Detaches the [param element] [GraphElement] from the [GraphFrame] it is currently attached to.
|
||||
</description>
|
||||
</method>
|
||||
<method name="disconnect_node">
|
||||
<return type="void" />
|
||||
<param index="0" name="from_node" type="StringName" />
|
||||
@ -143,6 +158,13 @@
|
||||
[b]Note:[/b] This method suppresses any other connection request signals apart from [signal connection_drag_ended].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_attached_nodes_of_frame">
|
||||
<return type="StringName[]" />
|
||||
<param index="0" name="frame" type="StringName" />
|
||||
<description>
|
||||
Returns an array of node names that are attached to the [GraphFrame] with the given name.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_closest_connection_at_point" qualifiers="const">
|
||||
<return type="Dictionary" />
|
||||
<param index="0" name="point" type="Vector2" />
|
||||
@ -179,6 +201,13 @@
|
||||
Returns an [Array] containing the list of connections that intersect with the given [Rect2]. A connection consists in a structure of the form [code]{ from_port: 0, from_node: "GraphNode name 0", to_port: 1, to_node: "GraphNode name 1" }[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_element_frame">
|
||||
<return type="GraphFrame" />
|
||||
<param index="0" name="element" type="StringName" />
|
||||
<description>
|
||||
Returns the [GraphFrame] that contains the [GraphElement] with the given name.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_menu_hbox">
|
||||
<return type="HBoxContainer" />
|
||||
<description>
|
||||
@ -395,6 +424,21 @@
|
||||
Emitted at the end of a [GraphElement]'s movement.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="frame_rect_changed">
|
||||
<param index="0" name="frame" type="GraphFrame" />
|
||||
<param index="1" name="new_rect" type="Vector2" />
|
||||
<description>
|
||||
Emitted when the [GraphFrame] [param frame] is resized to [param new_rect].
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="graph_elements_linked_to_frame_request">
|
||||
<param index="0" name="elements" type="Array" />
|
||||
<param index="1" name="frame" type="StringName" />
|
||||
<description>
|
||||
Emitted when one or more [GraphElement]s are dropped onto the [GraphFrame] named [param frame], when they were not previously attached to any other one.
|
||||
[param elements] is an array of [GraphElement]s to be attached.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="node_deselected">
|
||||
<param index="0" name="node" type="Node" />
|
||||
<description>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</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.
|
||||
[b]Note:[/b] Dragging the handle will only emit the [signal resize_request] and [signal resize_end] signals, 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.
|
||||
@ -59,8 +59,14 @@
|
||||
Emitted when displaying the GraphElement over other ones is requested. Happens on focusing (clicking into) the GraphElement.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="resize_end">
|
||||
<param index="0" name="new_size" type="Vector2" />
|
||||
<description>
|
||||
Emitted when releasing the mouse button after dragging the resizer handle (see [member resizable]).
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="resize_request">
|
||||
<param index="0" name="new_minsize" type="Vector2" />
|
||||
<param index="0" name="new_size" type="Vector2" />
|
||||
<description>
|
||||
Emitted when resizing the GraphElement is requested. Happens on dragging the resizer handle (see [member resizable]).
|
||||
</description>
|
||||
|
66
doc/classes/GraphFrame.xml
Normal file
66
doc/classes/GraphFrame.xml
Normal file
@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="GraphFrame" inherits="GraphElement" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
GraphFrame is a special [GraphElement] that can be used to organize other [GraphElement]s inside a [GraphEdit].
|
||||
</brief_description>
|
||||
<description>
|
||||
GraphFrame is a special [GraphElement] to which other [GraphElement]s can be attached. It can be configured to automatically resize to enclose all attached [GraphElement]s. If the frame is moved, all the attached [GraphElement]s inside it will be moved as well.
|
||||
A GraphFrame is always kept behind the connection layer and other [GraphElement]s inside a [GraphEdit].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<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>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="autoshrink_enabled" type="bool" setter="set_autoshrink_enabled" getter="is_autoshrink_enabled" default="true">
|
||||
If [code]true[/code], the frame's rect will be adjusted automatically to enclose all attached [GraphElement]s.
|
||||
</member>
|
||||
<member name="autoshrink_margin" type="int" setter="set_autoshrink_margin" getter="get_autoshrink_margin" default="40">
|
||||
The margin around the attached nodes that is used to calculate the size of the frame when [member autoshrink_enabled] is [code]true[/code].
|
||||
</member>
|
||||
<member name="drag_margin" type="int" setter="set_drag_margin" getter="get_drag_margin" default="16">
|
||||
The margin inside the frame that can be used to drag the frame.
|
||||
</member>
|
||||
<member name="mouse_filter" type="int" setter="set_mouse_filter" getter="get_mouse_filter" overrides="Control" enum="Control.MouseFilter" default="0" />
|
||||
<member name="tint_color" type="Color" setter="set_tint_color" getter="get_tint_color" default="Color(0.3, 0.3, 0.3, 0.75)">
|
||||
The color of the frame when [member tint_color_enabled] is [code]true[/code].
|
||||
</member>
|
||||
<member name="tint_color_enabled" type="bool" setter="set_tint_color_enabled" getter="is_tint_color_enabled" default="false">
|
||||
If [code]true[/code], the tint color will be used to tint the frame.
|
||||
</member>
|
||||
<member name="title" type="String" setter="set_title" getter="get_title" default="""">
|
||||
Title of the frame.
|
||||
</member>
|
||||
</members>
|
||||
<signals>
|
||||
<signal name="autoshrink_changed">
|
||||
<description>
|
||||
Emitted when [member autoshrink_enabled] or [member autoshrink_margin] changes.
|
||||
</description>
|
||||
</signal>
|
||||
</signals>
|
||||
<theme_items>
|
||||
<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="panel" data_type="style" type="StyleBox">
|
||||
The default [StyleBox] used for the background of the [GraphFrame].
|
||||
</theme_item>
|
||||
<theme_item name="panel_selected" data_type="style" type="StyleBox">
|
||||
The [StyleBox] used for the background of the [GraphFrame] when it is selected.
|
||||
</theme_item>
|
||||
<theme_item name="titlebar" data_type="style" type="StyleBox">
|
||||
The [StyleBox] used for the title bar of the [GraphFrame].
|
||||
</theme_item>
|
||||
<theme_item name="titlebar_selected" data_type="style" type="StyleBox">
|
||||
The [StyleBox] used for the title bar of the [GraphFrame] when it is selected.
|
||||
</theme_item>
|
||||
</theme_items>
|
||||
</class>
|
@ -29,6 +29,15 @@
|
||||
Adds a new varying value node to the shader.
|
||||
</description>
|
||||
</method>
|
||||
<method name="attach_node_to_frame">
|
||||
<return type="void" />
|
||||
<param index="0" name="type" type="int" enum="VisualShader.Type" />
|
||||
<param index="1" name="id" type="int" />
|
||||
<param index="2" name="frame" type="int" />
|
||||
<description>
|
||||
Attaches the given node to the given frame.
|
||||
</description>
|
||||
</method>
|
||||
<method name="can_connect_nodes" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="type" type="int" enum="VisualShader.Type" />
|
||||
@ -62,6 +71,14 @@
|
||||
Connects the specified nodes and ports, even if they can't be connected. Such connection is invalid and will not function properly.
|
||||
</description>
|
||||
</method>
|
||||
<method name="detach_node_from_frame">
|
||||
<return type="void" />
|
||||
<param index="0" name="type" type="int" enum="VisualShader.Type" />
|
||||
<param index="1" name="id" type="int" />
|
||||
<description>
|
||||
Detaches the given node from the frame it is attached to.
|
||||
</description>
|
||||
</method>
|
||||
<method name="disconnect_nodes">
|
||||
<return type="void" />
|
||||
<param index="0" name="type" type="int" enum="VisualShader.Type" />
|
||||
|
@ -61,6 +61,9 @@
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="linked_parent_graph_frame" type="int" setter="set_frame" getter="get_frame" default="-1">
|
||||
Represents the index of the frame this node is linked to. If set to [code]-1[/code] the node is not linked to any frame.
|
||||
</member>
|
||||
<member name="output_port_for_preview" type="int" setter="set_output_port_for_preview" getter="get_output_port_for_preview" default="-1">
|
||||
Sets the output port index which will be showed for preview. If set to [code]-1[/code] no port will be open for preview.
|
||||
</member>
|
||||
|
@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="VisualShaderNodeComment" inherits="VisualShaderNodeResizableBase" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
A comment node to be placed on visual shader graph.
|
||||
</brief_description>
|
||||
<description>
|
||||
A resizable rectangular area with changeable [member title] and [member description] used for better organizing of other visual shader nodes.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<members>
|
||||
<member name="description" type="String" setter="set_description" getter="get_description" default="""">
|
||||
An additional description which placed below the title.
|
||||
</member>
|
||||
<member name="title" type="String" setter="set_title" getter="get_title" default=""Comment"">
|
||||
A title of the node.
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
46
doc/classes/VisualShaderNodeFrame.xml
Normal file
46
doc/classes/VisualShaderNodeFrame.xml
Normal file
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="VisualShaderNodeFrame" inherits="VisualShaderNodeResizableBase" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
A frame other visual shader nodes can be attached to for better organization.
|
||||
</brief_description>
|
||||
<description>
|
||||
A rectangular frame that can be used to group visual shader nodes together to improve organization.
|
||||
Nodes attached to the frame will move with it when it is dragged and it can automatically resize to enclose all attached nodes.
|
||||
Its title, description and color can be customized.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="add_attached_node">
|
||||
<return type="void" />
|
||||
<param index="0" name="node" type="int" />
|
||||
<description>
|
||||
Adds a node to the list of nodes attached to the frame. Should not be called directly, use the [method VisualShader.attach_node_to_frame] method instead.
|
||||
</description>
|
||||
</method>
|
||||
<method name="remove_attached_node">
|
||||
<return type="void" />
|
||||
<param index="0" name="node" type="int" />
|
||||
<description>
|
||||
Removes a node from the list of nodes attached to the frame. Should not be called directly, use the [method VisualShader.detach_node_from_frame] method instead.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="attached_nodes" type="PackedInt32Array" setter="set_attached_nodes" getter="get_attached_nodes" default="PackedInt32Array()">
|
||||
The list of nodes attached to the frame.
|
||||
</member>
|
||||
<member name="autoshrink" type="bool" setter="set_autoshrink_enabled" getter="is_autoshrink_enabled" default="true">
|
||||
If [code]true[/code], the frame will automatically resize to enclose all attached nodes.
|
||||
</member>
|
||||
<member name="tint_color" type="Color" setter="set_tint_color" getter="get_tint_color" default="Color(0.3, 0.3, 0.3, 0.75)">
|
||||
The color of the frame when [member tint_color_enabled] is [code]true[/code].
|
||||
</member>
|
||||
<member name="tint_color_enabled" type="bool" setter="set_tint_color_enabled" getter="is_tint_color_enabled" default="false">
|
||||
If [code]true[/code], the frame will be tinted with the color specified in [member tint_color].
|
||||
</member>
|
||||
<member name="title" type="String" setter="set_title" getter="get_title" default=""Title"">
|
||||
The title of the node.
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
1
editor/icons/GraphFrame.svg
Normal file
1
editor/icons/GraphFrame.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 1c-1.108 0-2 .892-2 2v10c0 1.108.892 2 2 2h10c1.108 0 2-.892 2-2V3c0-1.108-.892-2-2-2zm1.25 2h6.5c.692 0 1.25.558 1.25 1.25V5c-1.645 0-3 1.355-3 3s1.355 3 3 3v.75c0 .692-.558 1.25-1.25 1.25h-6.5C3.558 13 3 12.442 3 11.75v-7.5C3 3.558 3.558 3 4.25 3zM12 6a2 2 0 110 4 2 2 0 010-4z" fill="#8eef97" paint-order="stroke markers fill"/></svg>
|
After Width: | Height: | Size: 413 B |
@ -170,7 +170,7 @@ 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;
|
||||
agnode->set_closable(true);
|
||||
agnode->set_deletable(true);
|
||||
|
||||
if (!read_only) {
|
||||
Button *delete_button = memnew(Button);
|
||||
@ -541,7 +541,7 @@ void AnimationNodeBlendTreeEditor::_delete_nodes_request(const TypedArray<String
|
||||
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
|
||||
if (gn && gn->is_selected()) {
|
||||
Ref<AnimationNode> anode = blend_tree->get_node(gn->get_name());
|
||||
if (anode->is_closable()) {
|
||||
if (anode->is_deletable()) {
|
||||
to_erase.push_back(gn->get_name());
|
||||
}
|
||||
}
|
||||
@ -549,7 +549,7 @@ void AnimationNodeBlendTreeEditor::_delete_nodes_request(const TypedArray<String
|
||||
} else {
|
||||
for (int i = 0; i < p_nodes.size(); i++) {
|
||||
Ref<AnimationNode> anode = blend_tree->get_node(p_nodes[i]);
|
||||
if (anode->is_closable()) {
|
||||
if (anode->is_deletable()) {
|
||||
to_erase.push_back(p_nodes[i]);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -42,6 +42,7 @@ class CodeEdit;
|
||||
class ColorPicker;
|
||||
class CurveEditor;
|
||||
class GraphElement;
|
||||
class GraphFrame;
|
||||
class MenuButton;
|
||||
class PopupPanel;
|
||||
class RichTextLabel;
|
||||
@ -120,11 +121,12 @@ public:
|
||||
bool is_preview_visible(int p_id) const;
|
||||
void update_node(VisualShader::Type p_type, int p_id);
|
||||
void update_node_deferred(VisualShader::Type p_type, int p_node_id);
|
||||
void add_node(VisualShader::Type p_type, int p_id, bool p_just_update);
|
||||
void add_node(VisualShader::Type p_type, int p_id, bool p_just_update, bool p_update_frames);
|
||||
void remove_node(VisualShader::Type p_type, int p_id, bool p_just_update);
|
||||
void connect_nodes(VisualShader::Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
|
||||
void disconnect_nodes(VisualShader::Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
|
||||
void show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id, bool p_is_valid);
|
||||
void update_frames(VisualShader::Type p_type, int p_node);
|
||||
void set_node_position(VisualShader::Type p_type, int p_id, const Vector2 &p_position);
|
||||
void refresh_node_ports(VisualShader::Type p_type, int p_node);
|
||||
void set_input_port_default_value(VisualShader::Type p_type, int p_node_id, int p_port_id, const Variant &p_value);
|
||||
@ -133,9 +135,13 @@ public:
|
||||
void update_curve(int p_node_id);
|
||||
void update_curve_xyz(int p_node_id);
|
||||
void set_expression(VisualShader::Type p_type, int p_node_id, const String &p_expression);
|
||||
void attach_node_to_frame(VisualShader::Type p_type, int p_node_id, int p_frame_id);
|
||||
void detach_node_from_frame(VisualShader::Type p_type, int p_node_id);
|
||||
void set_frame_color_enabled(VisualShader::Type p_type, int p_node_id, bool p_enable);
|
||||
void set_frame_color(VisualShader::Type p_type, int p_node_id, const Color &p_color);
|
||||
void set_frame_autoshrink_enabled(VisualShader::Type p_type, int p_node_id, bool p_enable);
|
||||
int get_constant_index(float p_constant) const;
|
||||
Ref<Script> get_node_script(int p_node_id) const;
|
||||
void update_node_size(int p_node_id);
|
||||
void update_theme();
|
||||
bool is_node_has_parameter_instances_relatively(VisualShader::Type p_type, int p_node) const;
|
||||
VisualShader::Type get_shader_type() const;
|
||||
@ -219,11 +225,11 @@ class VisualShaderEditor : public VBoxContainer {
|
||||
ConfirmationDialog *remove_varying_dialog = nullptr;
|
||||
Tree *varyings = nullptr;
|
||||
|
||||
PopupPanel *comment_title_change_popup = nullptr;
|
||||
LineEdit *comment_title_change_edit = nullptr;
|
||||
PopupPanel *frame_title_change_popup = nullptr;
|
||||
LineEdit *frame_title_change_edit = nullptr;
|
||||
|
||||
PopupPanel *comment_desc_change_popup = nullptr;
|
||||
TextEdit *comment_desc_change_edit = nullptr;
|
||||
PopupPanel *frame_tint_color_pick_popup = nullptr;
|
||||
ColorPicker *frame_tint_color_picker = nullptr;
|
||||
|
||||
bool preview_first = true;
|
||||
bool preview_showed = false;
|
||||
@ -281,9 +287,12 @@ class VisualShaderEditor : public VBoxContainer {
|
||||
FLOAT_CONSTANTS,
|
||||
CONVERT_CONSTANTS_TO_PARAMETERS,
|
||||
CONVERT_PARAMETERS_TO_CONSTANTS,
|
||||
UNLINK_FROM_PARENT_FRAME,
|
||||
SEPARATOR3, // ignore
|
||||
SET_COMMENT_TITLE,
|
||||
SET_COMMENT_DESCRIPTION,
|
||||
SET_FRAME_TITLE,
|
||||
ENABLE_FRAME_COLOR,
|
||||
SET_FRAME_COLOR,
|
||||
ENABLE_FRAME_AUTOSHRINK,
|
||||
};
|
||||
|
||||
enum ConnectionMenuOptions {
|
||||
@ -374,6 +383,9 @@ class VisualShaderEditor : public VBoxContainer {
|
||||
void _get_next_nodes_recursively(VisualShader::Type p_type, int p_node_id, LocalVector<int> &r_nodes) const;
|
||||
String _get_description(int p_idx);
|
||||
|
||||
Vector<int> nodes_link_to_frame_buffer; // Contains the nodes that are requested to be linked to a frame. This is used to perform one Undo/Redo operation for dragging nodes.
|
||||
int frame_node_id_to_link_to = -1;
|
||||
|
||||
struct DragOp {
|
||||
VisualShader::Type type = VisualShader::Type::TYPE_MAX;
|
||||
int node = 0;
|
||||
@ -381,6 +393,7 @@ class VisualShaderEditor : public VBoxContainer {
|
||||
Vector2 to;
|
||||
};
|
||||
List<DragOp> drag_buffer;
|
||||
|
||||
bool drag_dirty = false;
|
||||
void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, int p_node);
|
||||
void _nodes_dragged();
|
||||
@ -398,6 +411,9 @@ class VisualShaderEditor : public VBoxContainer {
|
||||
|
||||
void _node_changed(int p_id);
|
||||
|
||||
void _nodes_linked_to_frame_request(const TypedArray<StringName> &p_nodes, const StringName &p_frame);
|
||||
void _frame_rect_changed(const GraphFrame *p_frame, const Rect2 &p_new_rect);
|
||||
|
||||
void _edit_port_default_input(Object *p_button, int p_node, int p_port);
|
||||
void _port_edited(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing);
|
||||
|
||||
@ -411,29 +427,36 @@ class VisualShaderEditor : public VBoxContainer {
|
||||
|
||||
HashSet<int> selected_constants;
|
||||
HashSet<int> selected_parameters;
|
||||
int selected_comment = -1;
|
||||
int selected_frame = -1;
|
||||
int selected_float_constant = -1;
|
||||
|
||||
void _convert_constants_to_parameters(bool p_vice_versa);
|
||||
void _detach_nodes_from_frame_request();
|
||||
void _detach_nodes_from_frame(int p_type, const List<int> &p_nodes);
|
||||
void _replace_node(VisualShader::Type p_type_id, int p_node_id, const StringName &p_from, const StringName &p_to);
|
||||
void _update_constant(VisualShader::Type p_type_id, int p_node_id, const Variant &p_var, int p_preview_port);
|
||||
void _update_parameter(VisualShader::Type p_type_id, int p_node_id, const Variant &p_var, int p_preview_port);
|
||||
|
||||
void _unlink_node_from_parent_frame(int p_node_id);
|
||||
|
||||
void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position);
|
||||
void _connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position);
|
||||
bool _check_node_drop_on_connection(const Vector2 &p_position, Ref<GraphEdit::Connection> *r_closest_connection, int *r_node_id = nullptr, int *r_to_port = nullptr);
|
||||
void _handle_node_drop_on_connection();
|
||||
|
||||
void _comment_title_popup_show(const Point2 &p_position, int p_node_id);
|
||||
void _comment_title_popup_hide();
|
||||
void _comment_title_popup_focus_out();
|
||||
void _comment_title_text_changed(const String &p_new_text);
|
||||
void _comment_title_text_submitted(const String &p_new_text);
|
||||
void _frame_title_popup_show(const Point2 &p_position, int p_node_id);
|
||||
void _frame_title_popup_hide();
|
||||
void _frame_title_popup_focus_out();
|
||||
void _frame_title_text_changed(const String &p_new_text);
|
||||
void _frame_title_text_submitted(const String &p_new_text);
|
||||
|
||||
void _comment_desc_popup_show(const Point2 &p_position, int p_node_id);
|
||||
void _comment_desc_popup_hide();
|
||||
void _comment_desc_confirm();
|
||||
void _comment_desc_text_changed();
|
||||
void _frame_color_enabled_changed(int p_node_id);
|
||||
void _frame_color_popup_show(const Point2 &p_position, int p_node_id);
|
||||
void _frame_color_popup_hide();
|
||||
void _frame_color_confirm();
|
||||
void _frame_color_changed(const Color &p_color);
|
||||
|
||||
void _frame_autoshrink_enabled_changed(int p_node_id);
|
||||
|
||||
void _parameter_line_edit_changed(const String &p_text, int p_node_id);
|
||||
void _parameter_line_edit_focus_out(Object *p_line_edit, int p_node_id);
|
||||
|
@ -1528,7 +1528,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
|
||||
p_theme->set_color("resizer_color", "GraphEditMinimap", minimap_resizer_color);
|
||||
}
|
||||
|
||||
// GraphElement & GraphNode.
|
||||
// GraphElement, GraphNode & GraphFrame.
|
||||
{
|
||||
const int gn_margin_top = 2;
|
||||
const int gn_margin_side = 2;
|
||||
@ -1619,6 +1619,41 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
|
||||
p_theme->set_constant("shadow_offset_x", "GraphNodeTitleLabel", 0);
|
||||
p_theme->set_constant("shadow_offset_y", "GraphNodeTitleLabel", 1);
|
||||
p_theme->set_constant("line_spacing", "GraphNodeTitleLabel", 3 * EDSCALE);
|
||||
|
||||
// GraphFrame.
|
||||
|
||||
const int gf_corner_width = 7 * EDSCALE;
|
||||
const int gf_border_width = 2 * MAX(1, EDSCALE);
|
||||
|
||||
Ref<StyleBoxFlat> graphframe_sb = make_flat_stylebox(Color(0.0, 0.0, 0.0, 0.2), gn_margin_side, gn_margin_side, gn_margin_side, gn_margin_bottom, gf_corner_width);
|
||||
graphframe_sb->set_expand_margin(SIDE_TOP, 38 * EDSCALE);
|
||||
graphframe_sb->set_border_width_all(gf_border_width);
|
||||
graphframe_sb->set_border_color(high_contrast_borders ? gn_bg_color.lightened(0.2) : gn_bg_color.darkened(0.3));
|
||||
graphframe_sb->set_shadow_size(8 * EDSCALE);
|
||||
graphframe_sb->set_shadow_color(Color(p_config.shadow_color, p_config.shadow_color.a * 0.25));
|
||||
graphframe_sb->set_anti_aliased(true);
|
||||
|
||||
Ref<StyleBoxFlat> graphframe_sb_selected = graphframe_sb->duplicate();
|
||||
graphframe_sb_selected->set_border_color(gn_selected_border_color);
|
||||
|
||||
p_theme->set_stylebox("panel", "GraphFrame", graphframe_sb);
|
||||
p_theme->set_stylebox("panel_selected", "GraphFrame", graphframe_sb_selected);
|
||||
p_theme->set_stylebox("titlebar", "GraphFrame", make_empty_stylebox(4, 4, 4, 4));
|
||||
p_theme->set_stylebox("titlebar_selected", "GraphFrame", make_empty_stylebox(4, 4, 4, 4));
|
||||
p_theme->set_color("resizer_color", "GraphFrame", gn_decoration_color);
|
||||
|
||||
// GraphFrame's title Label
|
||||
p_theme->set_type_variation("GraphFrameTitleLabel", "Label");
|
||||
p_theme->set_stylebox("normal", "GraphFrameTitleLabel", memnew(StyleBoxEmpty));
|
||||
p_theme->set_font_size("font_size", "GraphFrameTitleLabel", 22);
|
||||
p_theme->set_color("font_color", "GraphFrameTitleLabel", Color(1, 1, 1));
|
||||
p_theme->set_color("font_shadow_color", "GraphFrameTitleLabel", Color(0, 0, 0, 0));
|
||||
p_theme->set_color("font_outline_color", "GraphFrameTitleLabel", Color(1, 1, 1));
|
||||
p_theme->set_constant("shadow_offset_x", "GraphFrameTitleLabel", 1 * EDSCALE);
|
||||
p_theme->set_constant("shadow_offset_y", "GraphFrameTitleLabel", 1 * EDSCALE);
|
||||
p_theme->set_constant("outline_size", "GraphFrameTitleLabel", 0);
|
||||
p_theme->set_constant("shadow_outline_size", "GraphFrameTitleLabel", 1 * EDSCALE);
|
||||
p_theme->set_constant("line_spacing", "GraphFrameTitleLabel", 3 * EDSCALE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,3 +259,10 @@ Validate extension JSON: Error: Field 'classes/AStarGrid2D/methods/get_point_pat
|
||||
|
||||
Added optional "allow_partial_path" argument to get_id_path and get_point_path methods in AStar classes.
|
||||
Compatibility methods registered.
|
||||
|
||||
|
||||
GH-88014
|
||||
--------
|
||||
Validate extension JSON: API was removed: classes/VisualShaderNodeComment
|
||||
|
||||
Removed VisualShaderNodeComment, which is replaced by VisualShaderNodeFrame.
|
||||
|
@ -395,11 +395,11 @@ bool AnimationNode::is_filter_enabled() const {
|
||||
return filter_enabled;
|
||||
}
|
||||
|
||||
void AnimationNode::set_closable(bool p_closable) {
|
||||
void AnimationNode::set_deletable(bool p_closable) {
|
||||
closable = p_closable;
|
||||
}
|
||||
|
||||
bool AnimationNode::is_closable() const {
|
||||
bool AnimationNode::is_deletable() const {
|
||||
return closable;
|
||||
}
|
||||
|
||||
|
@ -184,8 +184,8 @@ public:
|
||||
void set_filter_enabled(bool p_enable);
|
||||
bool is_filter_enabled() const;
|
||||
|
||||
void set_closable(bool p_closable);
|
||||
bool is_closable() const;
|
||||
void set_deletable(bool p_closable);
|
||||
bool is_deletable() const;
|
||||
|
||||
virtual bool has_filter() const;
|
||||
|
||||
|
@ -442,11 +442,42 @@ void GraphEdit::_update_scroll() {
|
||||
updating = false;
|
||||
}
|
||||
|
||||
void GraphEdit::_graph_element_moved_to_front(Node *p_node) {
|
||||
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
|
||||
ERR_FAIL_NULL(graph_element);
|
||||
void GraphEdit::_ensure_node_order_from(Node *p_node) {
|
||||
GraphElement *graph_node = Object::cast_to<GraphElement>(p_node);
|
||||
ERR_FAIL_NULL(graph_node);
|
||||
GraphFrame *frame = Object::cast_to<GraphFrame>(p_node);
|
||||
|
||||
graph_element->move_to_front();
|
||||
// Move a non-frame node directly to the front.
|
||||
if (!frame) {
|
||||
graph_node->move_to_front();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reorder the frames behind the connection layer.
|
||||
List<GraphFrame *> attached_nodes_to_move;
|
||||
attached_nodes_to_move.push_back(frame);
|
||||
|
||||
while (!attached_nodes_to_move.is_empty()) {
|
||||
GraphFrame *attached_frame = attached_nodes_to_move.front()->get();
|
||||
attached_nodes_to_move.pop_front();
|
||||
|
||||
// Move the frame to the front of the background node index range.
|
||||
attached_frame->get_parent()->call_deferred("move_child", attached_frame, background_nodes_separator_idx - 1);
|
||||
|
||||
if (!frame_attached_nodes.has(attached_frame->get_name())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const StringName &attached_node_name : frame_attached_nodes.get(attached_frame->get_name())) {
|
||||
GraphElement *attached_node = Object::cast_to<GraphElement>(get_node(NodePath(attached_node_name)));
|
||||
|
||||
GraphFrame *attached_child_frame_node = Object::cast_to<GraphFrame>(attached_node);
|
||||
|
||||
if (attached_child_frame_node && (attached_child_frame_node != frame)) {
|
||||
attached_nodes_to_move.push_back(attached_child_frame_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEdit::_graph_element_selected(Node *p_node) {
|
||||
@ -463,11 +494,42 @@ void GraphEdit::_graph_element_deselected(Node *p_node) {
|
||||
emit_signal(SNAME("node_deselected"), graph_element);
|
||||
}
|
||||
|
||||
void GraphEdit::_graph_element_resized(Vector2 p_new_minsize, Node *p_node) {
|
||||
void GraphEdit::_graph_element_resize_request(const Vector2 &p_new_minsize, Node *p_node) {
|
||||
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
|
||||
ERR_FAIL_NULL(graph_element);
|
||||
|
||||
graph_element->set_size(p_new_minsize);
|
||||
// Snap the new size to the grid if snapping is enabled.
|
||||
Vector2 new_size = p_new_minsize;
|
||||
if (snapping_enabled ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) {
|
||||
new_size = new_size.snapped(Vector2(snapping_distance, snapping_distance));
|
||||
}
|
||||
|
||||
// Disallow resizing the frame to a size smaller than the minimum size of the attached nodes.
|
||||
GraphFrame *frame = Object::cast_to<GraphFrame>(graph_element);
|
||||
if (frame && !frame->is_autoshrink_enabled()) {
|
||||
Rect2 frame_rect = _compute_shrinked_frame_rect(frame);
|
||||
Vector2 computed_min_size = (frame_rect.position + frame_rect.size) - frame->get_position_offset();
|
||||
frame->set_size(new_size.max(computed_min_size));
|
||||
} else {
|
||||
graph_element->set_size(new_size);
|
||||
}
|
||||
|
||||
// Update all parent frames recursively bottom-up.
|
||||
if (linked_parent_map.has(graph_element->get_name())) {
|
||||
GraphFrame *parent_frame = Object::cast_to<GraphFrame>(get_node_or_null(NodePath(linked_parent_map[graph_element->get_name()])));
|
||||
if (parent_frame) {
|
||||
_update_graph_frame(parent_frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEdit::_graph_frame_autoshrink_changed(const Vector2 &p_new_minsize, GraphFrame *p_frame) {
|
||||
_update_graph_frame(p_frame);
|
||||
|
||||
minimap->queue_redraw();
|
||||
queue_redraw();
|
||||
connections_layer->queue_redraw();
|
||||
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
|
||||
}
|
||||
|
||||
void GraphEdit::_graph_element_moved(Node *p_node) {
|
||||
@ -502,6 +564,26 @@ void GraphEdit::_graph_node_rect_changed(GraphNode *p_node) {
|
||||
|
||||
connections_layer->queue_redraw();
|
||||
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
|
||||
|
||||
// Update all parent frames recursively bottom-up.
|
||||
if (linked_parent_map.has(p_node->get_name())) {
|
||||
GraphFrame *parent_frame = Object::cast_to<GraphFrame>(get_node(NodePath(linked_parent_map[p_node->get_name()])));
|
||||
if (parent_frame) {
|
||||
_update_graph_frame(parent_frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEdit::_ensure_node_order_from_root(const StringName &p_node) {
|
||||
// Find the root frame node of the frame tree starting from p_node.
|
||||
GraphElement *root_frame = Object::cast_to<GraphElement>(get_node(NodePath(p_node)));
|
||||
ERR_FAIL_NULL(root_frame);
|
||||
|
||||
while (linked_parent_map.has(root_frame->get_name())) {
|
||||
root_frame = Object::cast_to<GraphElement>(get_node(NodePath(linked_parent_map[root_frame->get_name()])));
|
||||
}
|
||||
|
||||
_ensure_node_order_from(root_frame);
|
||||
}
|
||||
|
||||
void GraphEdit::add_child_notify(Node *p_child) {
|
||||
@ -520,10 +602,23 @@ void GraphEdit::add_child_notify(Node *p_child) {
|
||||
if (graph_node) {
|
||||
graph_node->connect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated).bind(graph_element));
|
||||
graph_node->connect("item_rect_changed", callable_mp(this, &GraphEdit::_graph_node_rect_changed).bind(graph_node));
|
||||
_ensure_node_order_from(graph_node);
|
||||
}
|
||||
|
||||
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));
|
||||
GraphFrame *graph_frame = Object::cast_to<GraphFrame>(graph_element);
|
||||
if (graph_frame) {
|
||||
background_nodes_separator_idx++;
|
||||
|
||||
callable_mp((Node *)this, &Node::move_child).call_deferred(graph_frame, 0);
|
||||
callable_mp((Node *)this, &Node::move_child).call_deferred(connections_layer, background_nodes_separator_idx);
|
||||
|
||||
_update_graph_frame(graph_frame);
|
||||
|
||||
graph_frame->connect("autoshrink_changed", callable_mp(this, &GraphEdit::_graph_frame_autoshrink_changed).bind(graph_element));
|
||||
}
|
||||
graph_element->connect("raise_request", callable_mp(this, &GraphEdit::_ensure_node_order_from).bind(graph_element));
|
||||
graph_element->connect("resize_request", callable_mp(this, &GraphEdit::_graph_element_resize_request).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));
|
||||
@ -565,8 +660,35 @@ void GraphEdit::remove_child_notify(Node *p_child) {
|
||||
connections_layer->queue_redraw();
|
||||
}
|
||||
|
||||
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));
|
||||
GraphFrame *frame = Object::cast_to<GraphFrame>(graph_element);
|
||||
if (frame) {
|
||||
background_nodes_separator_idx--;
|
||||
graph_element->disconnect("autoshrink_changed", callable_mp(this, &GraphEdit::_graph_frame_autoshrink_changed));
|
||||
}
|
||||
|
||||
if (linked_parent_map.has(graph_element->get_name())) {
|
||||
GraphFrame *parent_frame = Object::cast_to<GraphFrame>(get_node(NodePath(linked_parent_map[graph_element->get_name()])));
|
||||
if (parent_frame) {
|
||||
if (frame_attached_nodes.has(parent_frame->get_name())) {
|
||||
frame_attached_nodes.get(parent_frame->get_name()).erase(graph_element->get_name());
|
||||
}
|
||||
linked_parent_map.erase(graph_element->get_name());
|
||||
_update_graph_frame(parent_frame);
|
||||
}
|
||||
}
|
||||
|
||||
if (frame_attached_nodes.has(graph_element->get_name())) {
|
||||
for (const StringName &attached_node_name : frame_attached_nodes.get(graph_element->get_name())) {
|
||||
GraphElement *attached_node = Object::cast_to<GraphElement>(get_node(NodePath(attached_node_name)));
|
||||
if (attached_node) {
|
||||
linked_parent_map.erase(attached_node->get_name());
|
||||
}
|
||||
}
|
||||
frame_attached_nodes.erase(graph_element->get_name());
|
||||
}
|
||||
|
||||
graph_element->disconnect("raise_request", callable_mp(this, &GraphEdit::_ensure_node_order_from));
|
||||
graph_element->disconnect("resize_request", callable_mp(this, &GraphEdit::_graph_element_resize_request));
|
||||
|
||||
// In case of the whole GraphEdit being destroyed these references can already be freed.
|
||||
if (minimap != nullptr && minimap->is_inside_tree()) {
|
||||
@ -620,6 +742,7 @@ void GraphEdit::_notification(int p_what) {
|
||||
_draw_grid();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_RESIZED: {
|
||||
_update_scroll();
|
||||
minimap->queue_redraw();
|
||||
@ -628,6 +751,118 @@ void GraphEdit::_notification(int p_what) {
|
||||
}
|
||||
}
|
||||
|
||||
Rect2 GraphEdit::_compute_shrinked_frame_rect(const GraphFrame *p_frame) {
|
||||
Vector2 min_point{ FLT_MAX, FLT_MAX };
|
||||
Vector2 max_point{ -FLT_MAX, -FLT_MAX };
|
||||
|
||||
if (!frame_attached_nodes.has(p_frame->get_name())) {
|
||||
return Rect2(p_frame->get_position_offset(), Size2());
|
||||
}
|
||||
|
||||
int autoshrink_margin = p_frame->get_autoshrink_margin();
|
||||
|
||||
for (const StringName &attached_node_name : frame_attached_nodes.get(p_frame->get_name())) {
|
||||
GraphElement *attached_node = Object::cast_to<GraphElement>(get_node_or_null(NodePath(attached_node_name)));
|
||||
|
||||
if (!attached_node || attached_node == p_frame) {
|
||||
if (!attached_node) {
|
||||
frame_attached_nodes.get(p_frame->get_name()).erase(attached_node_name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 node_pos = attached_node->get_position_offset();
|
||||
Vector2 size = attached_node->get_size();
|
||||
min_point = min_point.min(node_pos);
|
||||
max_point = max_point.max(node_pos + size);
|
||||
}
|
||||
|
||||
// It's sufficient to check only one value here.
|
||||
if (min_point.x == FLT_MAX) {
|
||||
return Rect2(p_frame->get_position_offset(), Size2());
|
||||
}
|
||||
|
||||
min_point -= Size2(autoshrink_margin, autoshrink_margin);
|
||||
max_point += Size2(autoshrink_margin, autoshrink_margin);
|
||||
|
||||
return Rect2(min_point, max_point - min_point);
|
||||
}
|
||||
|
||||
void GraphEdit::_update_graph_frame(GraphFrame *p_frame) {
|
||||
Rect2 frame_rect = _compute_shrinked_frame_rect(p_frame);
|
||||
|
||||
Vector2 min_point = frame_rect.position;
|
||||
Vector2 max_point = frame_rect.position + frame_rect.size;
|
||||
|
||||
// Only update the size if there are attached nodes.
|
||||
if (frame_attached_nodes.has(p_frame->get_name()) && frame_attached_nodes.get(p_frame->get_name()).size() > 0) {
|
||||
if (!p_frame->is_autoshrink_enabled()) {
|
||||
Vector2 old_offset = p_frame->get_position_offset();
|
||||
min_point = min_point.min(old_offset);
|
||||
max_point = max_point.max(old_offset + p_frame->get_size());
|
||||
}
|
||||
|
||||
Rect2 old_rect = p_frame->get_rect();
|
||||
|
||||
p_frame->set_position_offset(min_point);
|
||||
p_frame->set_size(max_point - min_point);
|
||||
|
||||
// Emit the signal only if the frame rect has changed.
|
||||
if (old_rect != p_frame->get_rect()) {
|
||||
emit_signal(SNAME("frame_rect_changed"), p_frame, p_frame->get_rect());
|
||||
}
|
||||
}
|
||||
|
||||
// Update all parent frames recursively bottom-up.
|
||||
if (linked_parent_map.has(p_frame->get_name())) {
|
||||
GraphFrame *parent_frame = Object::cast_to<GraphFrame>(get_node_or_null(NodePath(linked_parent_map[p_frame->get_name()])));
|
||||
if (parent_frame) {
|
||||
_update_graph_frame(parent_frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEdit::_set_drag_frame_attached_nodes(GraphFrame *p_frame, bool p_drag) {
|
||||
if (!frame_attached_nodes.has(p_frame->get_name())) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const StringName &attached_node_name : frame_attached_nodes.get(p_frame->get_name())) {
|
||||
GraphElement *attached_node = Object::cast_to<GraphElement>(get_node(NodePath(attached_node_name)));
|
||||
|
||||
attached_node->set_drag(p_drag);
|
||||
GraphFrame *graph_frame = Object::cast_to<GraphFrame>(attached_node);
|
||||
if (graph_frame) {
|
||||
_set_drag_frame_attached_nodes(graph_frame, p_drag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEdit::_set_position_of_frame_attached_nodes(GraphFrame *p_frame, const Vector2 &p_pos) {
|
||||
if (!frame_attached_nodes.has(p_frame->get_name())) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const StringName &attached_node_name : frame_attached_nodes.get(p_frame->get_name())) {
|
||||
GraphElement *attached_node = Object::cast_to<GraphElement>(get_node_or_null(NodePath(attached_node_name)));
|
||||
if (!attached_node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 pos = (attached_node->get_drag_from() * zoom + drag_accum) / zoom;
|
||||
if (snapping_enabled ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) {
|
||||
pos = pos.snapped(Vector2(snapping_distance, snapping_distance));
|
||||
}
|
||||
|
||||
// Recursively move graph frames.
|
||||
attached_node->set_position_offset(pos);
|
||||
GraphFrame *graph_frame = Object::cast_to<GraphFrame>(attached_node);
|
||||
if (graph_frame) {
|
||||
_set_position_of_frame_attached_nodes(graph_frame, p_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GraphEdit::_filter_input(const Point2 &p_point) {
|
||||
for (int i = get_child_count() - 1; i >= 0; i--) {
|
||||
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
|
||||
@ -1265,7 +1500,33 @@ void GraphEdit::_minimap_draw() {
|
||||
Vector2 graph_offset = minimap->_get_graph_offset();
|
||||
Vector2 minimap_offset = minimap->minimap_offset;
|
||||
|
||||
// Draw graph nodes.
|
||||
// Draw frame nodes.
|
||||
for (int i = get_child_count() - 1; i >= 0; i--) {
|
||||
GraphFrame *graph_frame = Object::cast_to<GraphFrame>(get_child(i));
|
||||
if (!graph_frame || !graph_frame->is_visible()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 node_position = minimap->_convert_from_graph_position(graph_frame->get_position_offset() * zoom - graph_offset) + minimap_offset;
|
||||
Vector2 node_size = minimap->_convert_from_graph_position(graph_frame->get_size() * zoom);
|
||||
Rect2 node_rect = Rect2(node_position, node_size);
|
||||
|
||||
Ref<StyleBoxFlat> sb_minimap = minimap->theme_cache.node_style->duplicate();
|
||||
|
||||
// Override default values with colors provided by the GraphNode's stylebox, if possible.
|
||||
Ref<StyleBoxFlat> sb_frame = graph_frame->get_theme_stylebox(graph_frame->is_selected() ? SNAME("panel_selected") : SNAME("panel"));
|
||||
if (sb_frame.is_valid()) {
|
||||
Color node_color = sb_frame->get_bg_color();
|
||||
if (graph_frame->is_tint_color_enabled()) {
|
||||
node_color = graph_frame->get_tint_color();
|
||||
}
|
||||
sb_minimap->set_bg_color(node_color);
|
||||
}
|
||||
|
||||
minimap->draw_style_box(sb_minimap, node_rect);
|
||||
}
|
||||
|
||||
// Draw regular graph nodes.
|
||||
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_visible()) {
|
||||
@ -1400,6 +1661,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
|
||||
hovered_connection = new_highlighted_connection;
|
||||
}
|
||||
|
||||
// Logic for moving graph controls via mouse drag.
|
||||
if (mm.is_valid() && dragging) {
|
||||
if (!moving_selection) {
|
||||
emit_signal(SNAME("begin_node_move"));
|
||||
@ -1420,6 +1682,19 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
|
||||
}
|
||||
|
||||
graph_element->set_position_offset(pos);
|
||||
|
||||
if (linked_parent_map.has(graph_element->get_name())) {
|
||||
GraphFrame *parent_frame = Object::cast_to<GraphFrame>(get_node_or_null(NodePath(linked_parent_map[graph_element->get_name()])));
|
||||
if (parent_frame) {
|
||||
_update_graph_frame(parent_frame);
|
||||
}
|
||||
}
|
||||
|
||||
// Update all frame transforms recursively.
|
||||
GraphFrame *graph_frame = Object::cast_to<GraphFrame>(get_child(i));
|
||||
if (graph_frame) {
|
||||
_set_position_of_frame_attached_nodes(graph_frame, drag_accum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1436,10 +1711,12 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only select frames when the box selection is fully enclosing them.
|
||||
bool is_frame = Object::cast_to<GraphFrame>(graph_element);
|
||||
Rect2 r = graph_element->get_rect();
|
||||
bool in_box = r.intersects(box_selecting_rect);
|
||||
bool should_be_selected = is_frame ? box_selecting_rect.encloses(r) : box_selecting_rect.intersects(r);
|
||||
|
||||
if (in_box) {
|
||||
if (should_be_selected) {
|
||||
graph_element->set_selected(box_selection_mode_additive);
|
||||
} else {
|
||||
graph_element->set_selected(prev_selected.find(graph_element) != nullptr);
|
||||
@ -1494,6 +1771,10 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
|
||||
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
|
||||
if (graph_element && graph_element->is_selected()) {
|
||||
graph_element->set_drag(false);
|
||||
GraphFrame *frame = Object::cast_to<GraphFrame>(get_child(i));
|
||||
if (frame) {
|
||||
_set_drag_frame_attached_nodes(frame, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1501,6 +1782,46 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
|
||||
if (moving_selection) {
|
||||
emit_signal(SNAME("end_node_move"));
|
||||
moving_selection = false;
|
||||
|
||||
Vector<GraphElement *> dragged_nodes;
|
||||
for (int i = get_child_count() - 1; i >= 0; i--) {
|
||||
GraphElement *moved_node = Object::cast_to<GraphElement>(get_child(i));
|
||||
if (moved_node && moved_node->is_selected() && moved_node->is_draggable()) {
|
||||
dragged_nodes.push_back(moved_node);
|
||||
}
|
||||
}
|
||||
|
||||
GraphFrame *frame_dropped_on = nullptr;
|
||||
|
||||
// Find frame on which the node(s) is/were dropped.
|
||||
// Count down to find the topmost frame.
|
||||
for (int i = get_child_count() - 1; i >= 0; i--) {
|
||||
GraphFrame *frame = Object::cast_to<GraphFrame>(get_child(i));
|
||||
|
||||
if (!frame || frame->is_resizing()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Rect2 frame_rect = frame->get_rect();
|
||||
if (frame_rect.has_point(mb->get_position()) && !dragged_nodes.has(frame)) {
|
||||
frame_dropped_on = frame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (frame_dropped_on) {
|
||||
dragged_nodes.erase(frame_dropped_on);
|
||||
|
||||
TypedArray<StringName> dragged_node_names;
|
||||
for (GraphElement *moved_node : dragged_nodes) {
|
||||
if (!linked_parent_map.has(moved_node->get_name())) {
|
||||
dragged_node_names.push_back(moved_node->get_name());
|
||||
}
|
||||
}
|
||||
if (dragged_node_names.size() > 0) {
|
||||
emit_signal(SNAME("graph_elements_linked_to_frame_request"), dragged_node_names, frame_dropped_on->get_name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dragging = false;
|
||||
@ -1561,6 +1882,11 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
|
||||
}
|
||||
if (child_element->is_selected()) {
|
||||
child_element->set_drag(true);
|
||||
GraphFrame *frame_node = Object::cast_to<GraphFrame>(get_child(i));
|
||||
if (frame_node) {
|
||||
_ensure_node_order_from(frame_node);
|
||||
_set_drag_frame_attached_nodes(frame_node, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1636,12 +1962,12 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
|
||||
TypedArray<StringName> nodes;
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
|
||||
if (!gn) {
|
||||
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
|
||||
if (!graph_element) {
|
||||
continue;
|
||||
}
|
||||
if (gn->is_selected()) {
|
||||
nodes.push_back(gn->get_name());
|
||||
if (graph_element->is_selected()) {
|
||||
nodes.push_back(graph_element->get_name());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1926,6 +2252,58 @@ bool GraphEdit::is_valid_connection_type(int p_type, int p_with_type) const {
|
||||
return valid_connection_types.has(ct);
|
||||
}
|
||||
|
||||
void GraphEdit::attach_graph_element_to_frame(const StringName &p_graph_element, const StringName &p_parent_frame) {
|
||||
GraphFrame *frame = Object::cast_to<GraphFrame>(get_node(NodePath(p_parent_frame)));
|
||||
ERR_FAIL_NULL_MSG(frame, "Frame does not exist or is not of type GraphFrame.");
|
||||
GraphElement *graph_element = Object::cast_to<GraphElement>(get_node(NodePath(p_graph_element)));
|
||||
ERR_FAIL_NULL_MSG(graph_element, "Graph element to attach does not exist or is not of type GraphElement.");
|
||||
ERR_FAIL_COND_MSG(frame == graph_element, "Cannot attach a frame to itself.");
|
||||
|
||||
linked_parent_map.insert(p_graph_element, p_parent_frame);
|
||||
frame_attached_nodes[p_parent_frame].insert(p_graph_element);
|
||||
|
||||
_ensure_node_order_from_root(p_graph_element);
|
||||
_update_graph_frame(frame);
|
||||
}
|
||||
|
||||
void GraphEdit::detach_graph_element_from_frame(const StringName &p_graph_element) {
|
||||
if (!linked_parent_map.has(p_graph_element)) {
|
||||
return;
|
||||
}
|
||||
GraphFrame *frame = Object::cast_to<GraphFrame>(get_node(NodePath(linked_parent_map[p_graph_element])));
|
||||
ERR_FAIL_NULL_MSG(frame, "Frame does not exist or is not of type GraphFrame.");
|
||||
GraphElement *graph_element = Object::cast_to<GraphElement>(get_node(NodePath(p_graph_element)));
|
||||
ERR_FAIL_NULL_MSG(graph_element, "Graph element to detach does not exist or is not of type GraphElement.");
|
||||
|
||||
frame_attached_nodes.get(frame->get_name()).erase(p_graph_element);
|
||||
linked_parent_map.erase(p_graph_element);
|
||||
|
||||
_update_graph_frame(frame);
|
||||
}
|
||||
|
||||
GraphFrame *GraphEdit::get_element_frame(const StringName &p_attached_graph_element) {
|
||||
if (!linked_parent_map.has(p_attached_graph_element)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Node *parent = get_node_or_null(NodePath(linked_parent_map[p_attached_graph_element]));
|
||||
|
||||
return Object::cast_to<GraphFrame>(parent);
|
||||
}
|
||||
|
||||
TypedArray<StringName> GraphEdit::get_attached_nodes_of_frame(const StringName &p_graph_frame) {
|
||||
if (!frame_attached_nodes.has(p_graph_frame)) {
|
||||
return TypedArray<StringName>();
|
||||
}
|
||||
|
||||
TypedArray<StringName> attached_nodes;
|
||||
for (const StringName &node : frame_attached_nodes.get(p_graph_frame)) {
|
||||
attached_nodes.push_back(node);
|
||||
}
|
||||
|
||||
return attached_nodes;
|
||||
}
|
||||
|
||||
void GraphEdit::set_snapping_enabled(bool p_enable) {
|
||||
if (snapping_enabled == p_enable) {
|
||||
return;
|
||||
@ -2191,6 +2569,11 @@ void GraphEdit::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("is_valid_connection_type", "from_type", "to_type"), &GraphEdit::is_valid_connection_type);
|
||||
ClassDB::bind_method(D_METHOD("get_connection_line", "from_node", "to_node"), &GraphEdit::get_connection_line);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("attach_graph_element_to_frame", "element", "frame"), &GraphEdit::attach_graph_element_to_frame);
|
||||
ClassDB::bind_method(D_METHOD("detach_graph_element_from_frame", "element"), &GraphEdit::detach_graph_element_from_frame);
|
||||
ClassDB::bind_method(D_METHOD("get_element_frame", "element"), &GraphEdit::get_element_frame);
|
||||
ClassDB::bind_method(D_METHOD("get_attached_nodes_of_frame", "frame"), &GraphEdit::get_attached_nodes_of_frame);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_panning_scheme", "scheme"), &GraphEdit::set_panning_scheme);
|
||||
ClassDB::bind_method(D_METHOD("get_panning_scheme"), &GraphEdit::get_panning_scheme);
|
||||
|
||||
@ -2314,11 +2697,13 @@ void GraphEdit::_bind_methods() {
|
||||
|
||||
ADD_SIGNAL(MethodInfo("node_selected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
|
||||
ADD_SIGNAL(MethodInfo("node_deselected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
|
||||
ADD_SIGNAL(MethodInfo("frame_rect_changed", PropertyInfo(Variant::OBJECT, "frame", PROPERTY_HINT_RESOURCE_TYPE, "GraphFrame"), PropertyInfo(Variant::VECTOR2, "new_rect")));
|
||||
|
||||
ADD_SIGNAL(MethodInfo("popup_request", PropertyInfo(Variant::VECTOR2, "at_position")));
|
||||
|
||||
ADD_SIGNAL(MethodInfo("begin_node_move"));
|
||||
ADD_SIGNAL(MethodInfo("end_node_move"));
|
||||
ADD_SIGNAL(MethodInfo("graph_elements_linked_to_frame_request", PropertyInfo(Variant::ARRAY, "elements"), PropertyInfo(Variant::STRING_NAME, "frame")));
|
||||
ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "offset")));
|
||||
|
||||
BIND_ENUM_CONSTANT(SCROLL_ZOOMS);
|
||||
@ -2374,7 +2759,7 @@ GraphEdit::GraphEdit() {
|
||||
top_layer->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
|
||||
|
||||
connections_layer = memnew(Control);
|
||||
add_child(connections_layer, false, INTERNAL_MODE_FRONT);
|
||||
add_child(connections_layer, false);
|
||||
connections_layer->connect("draw", callable_mp(this, &GraphEdit::_update_connections));
|
||||
connections_layer->set_name("_connection_layer");
|
||||
connections_layer->set_disable_visibility_clip(true); // Necessary, so it can draw freely and be offset.
|
||||
|
@ -32,6 +32,7 @@
|
||||
#define GRAPH_EDIT_H
|
||||
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/graph_frame.h"
|
||||
#include "scene/gui/graph_node.h"
|
||||
|
||||
class Button;
|
||||
@ -294,6 +295,13 @@ private:
|
||||
float port_hotzone_outer_extent = 0.0;
|
||||
} theme_cache;
|
||||
|
||||
// This separates the children in two layers to ensure the order
|
||||
// of both background nodes (e.g frame nodes) and foreground nodes (connectable nodes).
|
||||
int background_nodes_separator_idx = 0;
|
||||
|
||||
HashMap<StringName, HashSet<StringName>> frame_attached_nodes;
|
||||
HashMap<StringName, StringName> linked_parent_map;
|
||||
|
||||
void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event);
|
||||
void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event);
|
||||
|
||||
@ -304,12 +312,15 @@ private:
|
||||
|
||||
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_resize_request(const Vector2 &p_new_minsize, Node *p_node);
|
||||
void _graph_frame_autoshrink_changed(const Vector2 &p_new_minsize, GraphFrame *p_frame);
|
||||
void _graph_element_moved(Node *p_node);
|
||||
void _graph_node_slot_updated(int p_index, Node *p_node);
|
||||
void _graph_node_rect_changed(GraphNode *p_node);
|
||||
|
||||
void _ensure_node_order_from_root(const StringName &p_node);
|
||||
void _ensure_node_order_from(Node *p_node);
|
||||
|
||||
void _update_scroll();
|
||||
void _update_scroll_offset();
|
||||
void _scroll_moved(double);
|
||||
@ -332,6 +343,10 @@ private:
|
||||
Dictionary _get_closest_connection_at_point(const Vector2 &p_point, float p_max_distance = 4.0) const;
|
||||
TypedArray<Dictionary> _get_connections_intersecting_with_rect(const Rect2 &p_rect) const;
|
||||
|
||||
Rect2 _compute_shrinked_frame_rect(const GraphFrame *p_frame);
|
||||
void _set_drag_frame_attached_nodes(GraphFrame *p_frame, bool p_drag);
|
||||
void _set_position_of_frame_attached_nodes(GraphFrame *p_frame, const Vector2 &p_pos);
|
||||
|
||||
friend class GraphEditFilter;
|
||||
bool _filter_input(const Point2 &p_point);
|
||||
void _snapping_toggled();
|
||||
@ -377,6 +392,11 @@ public:
|
||||
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
// This method has to be public (for undo redo).
|
||||
// TODO: Find a better way to do this.
|
||||
void _update_graph_frame(GraphFrame *p_frame);
|
||||
|
||||
// Connection related methods.
|
||||
Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
|
||||
bool is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
|
||||
void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
|
||||
@ -397,6 +417,12 @@ public:
|
||||
void remove_valid_connection_type(int p_type, int p_with_type);
|
||||
bool is_valid_connection_type(int p_type, int p_with_type) const;
|
||||
|
||||
// GraphFrame related methods.
|
||||
void attach_graph_element_to_frame(const StringName &p_graph_element, const StringName &p_parent_frame);
|
||||
void detach_graph_element_from_frame(const StringName &p_graph_element);
|
||||
GraphFrame *get_element_frame(const StringName &p_attached_graph_element);
|
||||
TypedArray<StringName> get_attached_nodes_of_frame(const StringName &p_graph_frame);
|
||||
|
||||
void set_panning_scheme(PanningScheme p_scheme);
|
||||
PanningScheme get_panning_scheme() const;
|
||||
|
||||
|
@ -166,7 +166,11 @@ void GraphElement::gui_input(const Ref<InputEvent> &p_ev) {
|
||||
}
|
||||
|
||||
if (!mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
|
||||
resizing = false;
|
||||
if (resizing) {
|
||||
resizing = false;
|
||||
emit_signal(SNAME("resize_end"), get_size());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,7 +241,8 @@ void GraphElement::_bind_methods() {
|
||||
|
||||
ADD_SIGNAL(MethodInfo("raise_request"));
|
||||
ADD_SIGNAL(MethodInfo("delete_request"));
|
||||
ADD_SIGNAL(MethodInfo("resize_request", PropertyInfo(Variant::VECTOR2, "new_minsize")));
|
||||
ADD_SIGNAL(MethodInfo("resize_request", PropertyInfo(Variant::VECTOR2, "new_size")));
|
||||
ADD_SIGNAL(MethodInfo("resize_end", PropertyInfo(Variant::VECTOR2, "new_size")));
|
||||
|
||||
ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to")));
|
||||
ADD_SIGNAL(MethodInfo("position_offset_changed"));
|
||||
|
357
scene/gui/graph_frame.cpp
Normal file
357
scene/gui/graph_frame.cpp
Normal file
@ -0,0 +1,357 @@
|
||||
/**************************************************************************/
|
||||
/* graph_frame.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_frame.h"
|
||||
|
||||
#include "core/string/translation.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/resources/style_box_flat.h"
|
||||
#include "scene/resources/style_box_texture.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
void GraphFrame::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_NULL_MSG(get_parent_control(), "GraphFrame 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 = theme_cache.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) {
|
||||
if (resizing) {
|
||||
resizing = false;
|
||||
emit_signal(SNAME("resize_end"), get_size());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_ev;
|
||||
|
||||
// Only resize if the frame is not auto-resizing based on linked nodes.
|
||||
if (resizing && !autoshrink_enabled && mm.is_valid()) {
|
||||
Vector2 mpos = mm->get_position();
|
||||
|
||||
Vector2 diff = mpos - resizing_from;
|
||||
|
||||
emit_signal(SNAME("resize_request"), resizing_from_size + diff);
|
||||
}
|
||||
}
|
||||
|
||||
Control::CursorShape GraphFrame::get_cursor_shape(const Point2 &p_pos) const {
|
||||
if (resizable && !autoshrink_enabled) {
|
||||
if (resizing || (p_pos.x > get_size().x - theme_cache.resizer->get_width() && p_pos.y > get_size().y - theme_cache.resizer->get_height())) {
|
||||
return CURSOR_FDIAGSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return Control::get_cursor_shape(p_pos);
|
||||
}
|
||||
|
||||
void GraphFrame::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_DRAW: {
|
||||
// Used for layout calculations.
|
||||
Ref<StyleBox> sb_panel = theme_cache.panel;
|
||||
Ref<StyleBox> sb_titlebar = theme_cache.titlebar;
|
||||
|
||||
// Used for drawing.
|
||||
Ref<StyleBox> sb_to_draw_panel = selected ? theme_cache.panel_selected : sb_panel;
|
||||
Ref<StyleBox> sb_to_draw_titlebar = selected ? theme_cache.titlebar_selected : sb_titlebar;
|
||||
Ref<StyleBoxFlat> sb_panel_flat = sb_to_draw_panel;
|
||||
Ref<StyleBoxTexture> sb_panel_texture = sb_to_draw_panel;
|
||||
|
||||
Rect2 titlebar_rect(Point2(), titlebar_hbox->get_size() + sb_titlebar->get_minimum_size());
|
||||
Size2 body_size = get_size();
|
||||
body_size.y -= titlebar_rect.size.height;
|
||||
Rect2 body_rect(Point2(0, titlebar_rect.size.height), body_size);
|
||||
|
||||
// Draw body stylebox.
|
||||
if (tint_color_enabled) {
|
||||
if (sb_panel_flat.is_valid()) {
|
||||
Color original_border_color = sb_panel_flat->get_border_color();
|
||||
sb_panel_flat = sb_panel_flat->duplicate();
|
||||
sb_panel_flat->set_bg_color(tint_color);
|
||||
sb_panel_flat->set_border_color(selected ? original_border_color : tint_color.lightened(0.3));
|
||||
draw_style_box(sb_panel_flat, body_rect);
|
||||
} else if (sb_panel_texture.is_valid()) {
|
||||
sb_panel_texture = sb_panel_flat->duplicate();
|
||||
sb_panel_texture->set_modulate(tint_color);
|
||||
draw_style_box(sb_panel_texture, body_rect);
|
||||
}
|
||||
} else {
|
||||
draw_style_box(sb_panel_flat, body_rect);
|
||||
}
|
||||
|
||||
// Draw title bar stylebox above.
|
||||
draw_style_box(sb_to_draw_titlebar, titlebar_rect);
|
||||
|
||||
// Only draw the resize handle if the frame is not auto-resizing.
|
||||
if (resizable && !autoshrink_enabled) {
|
||||
Ref<Texture2D> resizer = theme_cache.resizer;
|
||||
Color resizer_color = theme_cache.resizer_color;
|
||||
if (resizable) {
|
||||
draw_texture(resizer, get_size() - resizer->get_size(), resizer_color);
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void GraphFrame::_resort() {
|
||||
Ref<StyleBox> sb_panel = theme_cache.panel;
|
||||
Ref<StyleBox> sb_titlebar = theme_cache.titlebar;
|
||||
|
||||
// Resort titlebar first.
|
||||
Size2 titlebar_size = Size2(get_size().width, titlebar_hbox->get_size().height);
|
||||
titlebar_size -= sb_titlebar->get_minimum_size();
|
||||
Rect2 titlebar_rect = Rect2(sb_titlebar->get_offset(), titlebar_size);
|
||||
|
||||
fit_child_in_rect(titlebar_hbox, titlebar_rect);
|
||||
|
||||
// After resort the children of the titlebar container may have changed their height (e.g. Label autowrap).
|
||||
Size2i titlebar_min_size = titlebar_hbox->get_combined_minimum_size();
|
||||
|
||||
Size2 size = get_size() - sb_panel->get_minimum_size() - Size2(0, titlebar_min_size.height + sb_titlebar->get_minimum_size().height);
|
||||
Point2 offset = Point2(sb_panel->get_margin(SIDE_LEFT), sb_panel->get_margin(SIDE_TOP) + titlebar_min_size.height + sb_titlebar->get_minimum_size().height);
|
||||
|
||||
for (int i = 0; i < get_child_count(false); i++) {
|
||||
Control *child = Object::cast_to<Control>(get_child(i, false));
|
||||
if (!child || !child->is_visible_in_tree()) {
|
||||
continue;
|
||||
}
|
||||
if (child->is_set_as_top_level()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fit_child_in_rect(child, Rect2(offset, size));
|
||||
}
|
||||
}
|
||||
|
||||
void GraphFrame::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_title", "title"), &GraphFrame::set_title);
|
||||
ClassDB::bind_method(D_METHOD("get_title"), &GraphFrame::get_title);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_titlebar_hbox"), &GraphFrame::get_titlebar_hbox);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_autoshrink_enabled", "shrink"), &GraphFrame::set_autoshrink_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_autoshrink_enabled"), &GraphFrame::is_autoshrink_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_autoshrink_margin", "autoshrink_margin"), &GraphFrame::set_autoshrink_margin);
|
||||
ClassDB::bind_method(D_METHOD("get_autoshrink_margin"), &GraphFrame::get_autoshrink_margin);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_drag_margin", "drag_margin"), &GraphFrame::set_drag_margin);
|
||||
ClassDB::bind_method(D_METHOD("get_drag_margin"), &GraphFrame::get_drag_margin);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_tint_color_enabled", "enable"), &GraphFrame::set_tint_color_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_tint_color_enabled"), &GraphFrame::is_tint_color_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_tint_color", "color"), &GraphFrame::set_tint_color);
|
||||
ClassDB::bind_method(D_METHOD("get_tint_color"), &GraphFrame::get_tint_color);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoshrink_enabled"), "set_autoshrink_enabled", "is_autoshrink_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "autoshrink_margin", PROPERTY_HINT_RANGE, "0,128,1"), "set_autoshrink_margin", "get_autoshrink_margin");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "drag_margin", PROPERTY_HINT_RANGE, "0,128,1"), "set_drag_margin", "get_drag_margin");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tint_color_enabled"), "set_tint_color_enabled", "is_tint_color_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_color"), "set_tint_color", "get_tint_color");
|
||||
|
||||
ADD_SIGNAL(MethodInfo(SNAME("autoshrink_changed")));
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphFrame, panel);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphFrame, panel_selected);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphFrame, titlebar);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphFrame, titlebar_selected);
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphFrame, resizer);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphFrame, resizer_color);
|
||||
}
|
||||
|
||||
void GraphFrame::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name == "resizable") {
|
||||
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
}
|
||||
}
|
||||
|
||||
void GraphFrame::set_title(const String &p_title) {
|
||||
if (title == p_title) {
|
||||
return;
|
||||
}
|
||||
title = p_title;
|
||||
if (title_label) {
|
||||
title_label->set_text(title);
|
||||
}
|
||||
update_minimum_size();
|
||||
}
|
||||
|
||||
String GraphFrame::get_title() const {
|
||||
return title;
|
||||
}
|
||||
|
||||
void GraphFrame::set_autoshrink_enabled(bool p_shrink) {
|
||||
if (autoshrink_enabled == p_shrink) {
|
||||
return;
|
||||
}
|
||||
|
||||
autoshrink_enabled = p_shrink;
|
||||
|
||||
emit_signal("autoshrink_changed", get_size());
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
bool GraphFrame::is_autoshrink_enabled() const {
|
||||
return autoshrink_enabled;
|
||||
}
|
||||
|
||||
void GraphFrame::set_autoshrink_margin(const int &p_margin) {
|
||||
if (autoshrink_margin == p_margin) {
|
||||
return;
|
||||
}
|
||||
|
||||
autoshrink_margin = p_margin;
|
||||
|
||||
emit_signal("autoshrink_changed", get_size());
|
||||
}
|
||||
|
||||
int GraphFrame::get_autoshrink_margin() const {
|
||||
return autoshrink_margin;
|
||||
}
|
||||
|
||||
HBoxContainer *GraphFrame::get_titlebar_hbox() {
|
||||
return titlebar_hbox;
|
||||
}
|
||||
|
||||
void GraphFrame::set_drag_margin(int p_margin) {
|
||||
drag_margin = p_margin;
|
||||
}
|
||||
|
||||
int GraphFrame::get_drag_margin() const {
|
||||
return drag_margin;
|
||||
}
|
||||
|
||||
void GraphFrame::set_tint_color_enabled(bool p_enable) {
|
||||
tint_color_enabled = p_enable;
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
bool GraphFrame::is_tint_color_enabled() const {
|
||||
return tint_color_enabled;
|
||||
}
|
||||
|
||||
void GraphFrame::set_tint_color(const Color &p_color) {
|
||||
tint_color = p_color;
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
Color GraphFrame::get_tint_color() const {
|
||||
return tint_color;
|
||||
}
|
||||
|
||||
bool GraphFrame::has_point(const Point2 &p_point) const {
|
||||
Ref<StyleBox> sb_panel = theme_cache.panel;
|
||||
Ref<StyleBox> sb_titlebar = theme_cache.titlebar;
|
||||
Ref<Texture2D> resizer = theme_cache.resizer;
|
||||
|
||||
if (Rect2(get_size() - resizer->get_size(), resizer->get_size()).has_point(p_point)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For grabbing on the titlebar.
|
||||
int titlebar_height = titlebar_hbox->get_size().height + sb_titlebar->get_minimum_size().height;
|
||||
if (Rect2(0, 0, get_size().width, titlebar_height).has_point(p_point)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow grabbing on all sides of the frame.
|
||||
Rect2 frame_rect = Rect2(0, 0, get_size().width, get_size().height);
|
||||
Rect2 no_drag_rect = frame_rect.grow(-drag_margin);
|
||||
|
||||
if (frame_rect.has_point(p_point) && !no_drag_rect.has_point(p_point)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Size2 GraphFrame::get_minimum_size() const {
|
||||
Ref<StyleBox> sb_panel = theme_cache.panel;
|
||||
Ref<StyleBox> sb_titlebar = theme_cache.titlebar;
|
||||
|
||||
Size2 minsize = titlebar_hbox->get_minimum_size() + sb_titlebar->get_minimum_size();
|
||||
|
||||
for (int i = 0; i < get_child_count(false); i++) {
|
||||
Control *child = Object::cast_to<Control>(get_child(i, false));
|
||||
if (!child || !child->is_visible() || child->is_set_as_top_level()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Size2i size = child->get_combined_minimum_size();
|
||||
size.width += sb_panel->get_minimum_size().width;
|
||||
|
||||
minsize.x = MAX(minsize.x, size.x);
|
||||
minsize.y += MAX(minsize.y, size.y);
|
||||
}
|
||||
|
||||
minsize.height += sb_panel->get_minimum_size().height;
|
||||
|
||||
return minsize;
|
||||
}
|
||||
|
||||
GraphFrame::GraphFrame() {
|
||||
titlebar_hbox = memnew(HBoxContainer);
|
||||
titlebar_hbox->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
add_child(titlebar_hbox, false, INTERNAL_MODE_FRONT);
|
||||
|
||||
title_label = memnew(Label);
|
||||
title_label->set_theme_type_variation("GraphFrameTitleLabel");
|
||||
title_label->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
title_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
titlebar_hbox->add_child(title_label);
|
||||
|
||||
set_mouse_filter(MOUSE_FILTER_STOP);
|
||||
}
|
108
scene/gui/graph_frame.h
Normal file
108
scene/gui/graph_frame.h
Normal file
@ -0,0 +1,108 @@
|
||||
/**************************************************************************/
|
||||
/* graph_frame.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_FRAME_H
|
||||
#define GRAPH_FRAME_H
|
||||
|
||||
#include "scene/gui/graph_element.h"
|
||||
|
||||
class HBoxContainer;
|
||||
|
||||
class GraphFrame : public GraphElement {
|
||||
GDCLASS(GraphFrame, GraphElement);
|
||||
|
||||
struct _MinSizeCache {
|
||||
int min_size = 0;
|
||||
bool will_stretch = false;
|
||||
int final_size = 0;
|
||||
};
|
||||
|
||||
struct ThemeCache {
|
||||
Ref<StyleBox> panel;
|
||||
Ref<StyleBox> panel_selected;
|
||||
Ref<StyleBox> titlebar;
|
||||
Ref<StyleBox> titlebar_selected;
|
||||
|
||||
Ref<Texture2D> resizer;
|
||||
Color resizer_color;
|
||||
} theme_cache;
|
||||
|
||||
private:
|
||||
String title;
|
||||
|
||||
HBoxContainer *titlebar_hbox = nullptr;
|
||||
Label *title_label = nullptr;
|
||||
|
||||
bool autoshrink_enabled = true;
|
||||
int autoshrink_margin = 40;
|
||||
int drag_margin = 16;
|
||||
|
||||
bool tint_color_enabled = false;
|
||||
Color tint_color = Color(0.3, 0.3, 0.3, 0.75);
|
||||
|
||||
protected:
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
virtual void _resort() override;
|
||||
|
||||
public:
|
||||
void set_title(const String &p_title);
|
||||
String get_title() const;
|
||||
|
||||
void set_autoshrink_enabled(bool p_enable);
|
||||
bool is_autoshrink_enabled() const;
|
||||
|
||||
void set_autoshrink_margin(const int &p_margin);
|
||||
int get_autoshrink_margin() const;
|
||||
|
||||
HBoxContainer *get_titlebar_hbox();
|
||||
|
||||
void set_drag_margin(int p_margin);
|
||||
int get_drag_margin() const;
|
||||
|
||||
void set_tint_color_enabled(bool p_enable);
|
||||
bool is_tint_color_enabled() const;
|
||||
|
||||
void set_tint_color(const Color &p_tint_color);
|
||||
Color get_tint_color() const;
|
||||
|
||||
virtual bool has_point(const Point2 &p_point) const override;
|
||||
virtual Size2 get_minimum_size() const override;
|
||||
|
||||
GraphFrame();
|
||||
};
|
||||
|
||||
#endif // GRAPH_FRAME_H
|
@ -62,9 +62,9 @@ class GraphNode : public GraphElement {
|
||||
};
|
||||
|
||||
struct _MinSizeCache {
|
||||
int min_size;
|
||||
bool will_stretch;
|
||||
int final_size;
|
||||
int min_size = 0;
|
||||
bool will_stretch = false;
|
||||
int final_size = 0;
|
||||
};
|
||||
|
||||
HBoxContainer *titlebar_hbox = nullptr;
|
||||
|
@ -104,6 +104,7 @@
|
||||
#include "scene/gui/file_dialog.h"
|
||||
#include "scene/gui/flow_container.h"
|
||||
#include "scene/gui/graph_edit.h"
|
||||
#include "scene/gui/graph_frame.h"
|
||||
#include "scene/gui/graph_node.h"
|
||||
#include "scene/gui/grid_container.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
@ -473,6 +474,7 @@ void register_scene_types() {
|
||||
|
||||
GDREGISTER_CLASS(GraphElement);
|
||||
GDREGISTER_CLASS(GraphNode);
|
||||
GDREGISTER_CLASS(GraphFrame);
|
||||
GDREGISTER_CLASS(GraphEdit);
|
||||
|
||||
OS::get_singleton()->yield(); // may take time to init
|
||||
@ -649,7 +651,7 @@ void register_scene_types() {
|
||||
GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeGroupBase);
|
||||
GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeConstant);
|
||||
GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeVectorBase);
|
||||
GDREGISTER_CLASS(VisualShaderNodeComment);
|
||||
GDREGISTER_CLASS(VisualShaderNodeFrame);
|
||||
GDREGISTER_CLASS(VisualShaderNodeFloatConstant);
|
||||
GDREGISTER_CLASS(VisualShaderNodeIntConstant);
|
||||
GDREGISTER_CLASS(VisualShaderNodeUIntConstant);
|
||||
|
@ -348,14 +348,22 @@ void VisualShaderNode::set_disabled(bool p_disabled) {
|
||||
disabled = p_disabled;
|
||||
}
|
||||
|
||||
bool VisualShaderNode::is_closable() const {
|
||||
bool VisualShaderNode::is_deletable() const {
|
||||
return closable;
|
||||
}
|
||||
|
||||
void VisualShaderNode::set_closable(bool p_closable) {
|
||||
void VisualShaderNode::set_deletable(bool p_closable) {
|
||||
closable = p_closable;
|
||||
}
|
||||
|
||||
void VisualShaderNode::set_frame(int p_node) {
|
||||
linked_parent_graph_frame = p_node;
|
||||
}
|
||||
|
||||
int VisualShaderNode::get_frame() const {
|
||||
return linked_parent_graph_frame;
|
||||
}
|
||||
|
||||
Vector<VisualShader::DefaultTextureParam> VisualShaderNode::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
|
||||
return Vector<VisualShader::DefaultTextureParam>();
|
||||
}
|
||||
@ -433,9 +441,13 @@ void VisualShaderNode::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_default_input_values", "values"), &VisualShaderNode::set_default_input_values);
|
||||
ClassDB::bind_method(D_METHOD("get_default_input_values"), &VisualShaderNode::get_default_input_values);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_frame", "frame"), &VisualShaderNode::set_frame);
|
||||
ClassDB::bind_method(D_METHOD("get_frame"), &VisualShaderNode::get_frame);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_default_input_values", "get_default_input_values");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "expanded_output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_output_ports_expanded", "_get_output_ports_expanded");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "linked_parent_graph_frame", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_frame", "get_frame");
|
||||
|
||||
BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR);
|
||||
BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR_INT);
|
||||
@ -937,6 +949,9 @@ Vector2 VisualShader::get_node_position(Type p_type, int p_id) const {
|
||||
Ref<VisualShaderNode> VisualShader::get_node(Type p_type, int p_id) const {
|
||||
ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Ref<VisualShaderNode>());
|
||||
const Graph *g = &graph[p_type];
|
||||
if (!g->nodes.has(p_id)) {
|
||||
return Ref<VisualShaderNode>();
|
||||
}
|
||||
ERR_FAIL_COND_V(!g->nodes.has(p_id), Ref<VisualShaderNode>());
|
||||
return g->nodes[p_id].node;
|
||||
}
|
||||
@ -1134,6 +1149,36 @@ bool VisualShader::is_port_types_compatible(int p_a, int p_b) const {
|
||||
return MAX(0, p_a - (int)VisualShaderNode::PORT_TYPE_BOOLEAN) == (MAX(0, p_b - (int)VisualShaderNode::PORT_TYPE_BOOLEAN));
|
||||
}
|
||||
|
||||
void VisualShader::attach_node_to_frame(Type p_type, int p_node, int p_frame) {
|
||||
ERR_FAIL_INDEX(p_type, TYPE_MAX);
|
||||
ERR_FAIL_COND(p_frame < 0);
|
||||
Graph *g = &graph[p_type];
|
||||
|
||||
ERR_FAIL_COND(!g->nodes.has(p_node));
|
||||
|
||||
g->nodes[p_node].node->set_frame(p_frame);
|
||||
|
||||
Ref<VisualShaderNodeFrame> vsnode_frame = g->nodes[p_frame].node;
|
||||
if (vsnode_frame.is_valid()) {
|
||||
vsnode_frame->add_attached_node(p_node);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualShader::detach_node_from_frame(Type p_type, int p_node) {
|
||||
ERR_FAIL_INDEX(p_type, TYPE_MAX);
|
||||
Graph *g = &graph[p_type];
|
||||
|
||||
ERR_FAIL_COND(!g->nodes.has(p_node));
|
||||
|
||||
int parent_frame_id = g->nodes[p_node].node->get_frame();
|
||||
Ref<VisualShaderNodeFrame> vsnode_frame = g->nodes[parent_frame_id].node;
|
||||
if (vsnode_frame.is_valid()) {
|
||||
vsnode_frame->remove_attached_node(p_node);
|
||||
}
|
||||
|
||||
g->nodes[p_node].node->set_frame(-1);
|
||||
}
|
||||
|
||||
void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
|
||||
ERR_FAIL_INDEX(p_type, TYPE_MAX);
|
||||
Graph *g = &graph[p_type];
|
||||
@ -2797,6 +2842,9 @@ void VisualShader::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &VisualShader::set_graph_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_graph_offset"), &VisualShader::get_graph_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("attach_node_to_frame", "type", "id", "frame"), &VisualShader::attach_node_to_frame);
|
||||
ClassDB::bind_method(D_METHOD("detach_node_from_frame", "type", "id"), &VisualShader::detach_node_from_frame);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_varying", "name", "mode", "type"), &VisualShader::add_varying);
|
||||
ClassDB::bind_method(D_METHOD("remove_varying", "name"), &VisualShader::remove_varying);
|
||||
ClassDB::bind_method(D_METHOD("has_varying", "name"), &VisualShader::has_varying);
|
||||
@ -4160,66 +4208,119 @@ VisualShaderNodeResizableBase::VisualShaderNodeResizableBase() {
|
||||
|
||||
////////////// Comment
|
||||
|
||||
String VisualShaderNodeComment::get_caption() const {
|
||||
String VisualShaderNodeFrame::get_caption() const {
|
||||
return title;
|
||||
}
|
||||
|
||||
int VisualShaderNodeComment::get_input_port_count() const {
|
||||
int VisualShaderNodeFrame::get_input_port_count() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
VisualShaderNodeComment::PortType VisualShaderNodeComment::get_input_port_type(int p_port) const {
|
||||
VisualShaderNodeFrame::PortType VisualShaderNodeFrame::get_input_port_type(int p_port) const {
|
||||
return PortType::PORT_TYPE_SCALAR;
|
||||
}
|
||||
|
||||
String VisualShaderNodeComment::get_input_port_name(int p_port) const {
|
||||
String VisualShaderNodeFrame::get_input_port_name(int p_port) const {
|
||||
return String();
|
||||
}
|
||||
|
||||
int VisualShaderNodeComment::get_output_port_count() const {
|
||||
int VisualShaderNodeFrame::get_output_port_count() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
VisualShaderNodeComment::PortType VisualShaderNodeComment::get_output_port_type(int p_port) const {
|
||||
VisualShaderNodeFrame::PortType VisualShaderNodeFrame::get_output_port_type(int p_port) const {
|
||||
return PortType::PORT_TYPE_SCALAR;
|
||||
}
|
||||
|
||||
String VisualShaderNodeComment::get_output_port_name(int p_port) const {
|
||||
String VisualShaderNodeFrame::get_output_port_name(int p_port) const {
|
||||
return String();
|
||||
}
|
||||
|
||||
void VisualShaderNodeComment::set_title(const String &p_title) {
|
||||
void VisualShaderNodeFrame::set_title(const String &p_title) {
|
||||
title = p_title;
|
||||
}
|
||||
|
||||
String VisualShaderNodeComment::get_title() const {
|
||||
String VisualShaderNodeFrame::get_title() const {
|
||||
return title;
|
||||
}
|
||||
|
||||
void VisualShaderNodeComment::set_description(const String &p_description) {
|
||||
description = p_description;
|
||||
void VisualShaderNodeFrame::set_tint_color_enabled(bool p_enabled) {
|
||||
tint_color_enabled = p_enabled;
|
||||
}
|
||||
|
||||
String VisualShaderNodeComment::get_description() const {
|
||||
return description;
|
||||
bool VisualShaderNodeFrame::is_tint_color_enabled() const {
|
||||
return tint_color_enabled;
|
||||
}
|
||||
|
||||
String VisualShaderNodeComment::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
|
||||
void VisualShaderNodeFrame::set_tint_color(const Color &p_color) {
|
||||
tint_color = p_color;
|
||||
}
|
||||
|
||||
Color VisualShaderNodeFrame::get_tint_color() const {
|
||||
return tint_color;
|
||||
}
|
||||
|
||||
void VisualShaderNodeFrame::set_autoshrink_enabled(bool p_enable) {
|
||||
autoshrink = p_enable;
|
||||
}
|
||||
|
||||
bool VisualShaderNodeFrame::is_autoshrink_enabled() const {
|
||||
return autoshrink;
|
||||
}
|
||||
|
||||
String VisualShaderNodeFrame::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
|
||||
return String();
|
||||
}
|
||||
|
||||
void VisualShaderNodeComment::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_title", "title"), &VisualShaderNodeComment::set_title);
|
||||
ClassDB::bind_method(D_METHOD("get_title"), &VisualShaderNodeComment::get_title);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_description", "description"), &VisualShaderNodeComment::set_description);
|
||||
ClassDB::bind_method(D_METHOD("get_description"), &VisualShaderNodeComment::get_description);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "description"), "set_description", "get_description");
|
||||
void VisualShaderNodeFrame::add_attached_node(int p_node) {
|
||||
attached_nodes.insert(p_node);
|
||||
}
|
||||
|
||||
VisualShaderNodeComment::VisualShaderNodeComment() {
|
||||
void VisualShaderNodeFrame::remove_attached_node(int p_node) {
|
||||
attached_nodes.erase(p_node);
|
||||
}
|
||||
|
||||
void VisualShaderNodeFrame::set_attached_nodes(const PackedInt32Array &p_attached_nodes) {
|
||||
attached_nodes.clear();
|
||||
for (const int &node_id : p_attached_nodes) {
|
||||
attached_nodes.insert(node_id);
|
||||
}
|
||||
}
|
||||
|
||||
PackedInt32Array VisualShaderNodeFrame::get_attached_nodes() const {
|
||||
PackedInt32Array attached_nodes_arr;
|
||||
for (const int &node_id : attached_nodes) {
|
||||
attached_nodes_arr.push_back(node_id);
|
||||
}
|
||||
return attached_nodes_arr;
|
||||
}
|
||||
|
||||
void VisualShaderNodeFrame::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_title", "title"), &VisualShaderNodeFrame::set_title);
|
||||
ClassDB::bind_method(D_METHOD("get_title"), &VisualShaderNodeFrame::get_title);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_tint_color_enabled", "enable"), &VisualShaderNodeFrame::set_tint_color_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_tint_color_enabled"), &VisualShaderNodeFrame::is_tint_color_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_tint_color", "color"), &VisualShaderNodeFrame::set_tint_color);
|
||||
ClassDB::bind_method(D_METHOD("get_tint_color"), &VisualShaderNodeFrame::get_tint_color);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_autoshrink_enabled", "enable"), &VisualShaderNodeFrame::set_autoshrink_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_autoshrink_enabled"), &VisualShaderNodeFrame::is_autoshrink_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_attached_node", "node"), &VisualShaderNodeFrame::add_attached_node);
|
||||
ClassDB::bind_method(D_METHOD("remove_attached_node", "node"), &VisualShaderNodeFrame::remove_attached_node);
|
||||
ClassDB::bind_method(D_METHOD("set_attached_nodes", "attached_nodes"), &VisualShaderNodeFrame::set_attached_nodes);
|
||||
ClassDB::bind_method(D_METHOD("get_attached_nodes"), &VisualShaderNodeFrame::get_attached_nodes);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tint_color_enabled"), "set_tint_color_enabled", "is_tint_color_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_color"), "set_tint_color", "get_tint_color");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoshrink"), "set_autoshrink_enabled", "is_autoshrink_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "attached_nodes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_attached_nodes", "get_attached_nodes");
|
||||
}
|
||||
|
||||
VisualShaderNodeFrame::VisualShaderNodeFrame() {
|
||||
}
|
||||
|
||||
////////////// GroupBase
|
||||
|
@ -226,6 +226,9 @@ public: // internal methods
|
||||
void connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
|
||||
bool is_port_types_compatible(int p_a, int p_b) const;
|
||||
|
||||
void attach_node_to_frame(Type p_type, int p_node, int p_frame);
|
||||
void detach_node_from_frame(Type p_type, int p_node);
|
||||
|
||||
void rebuild();
|
||||
void get_node_connections(Type p_type, List<Connection> *r_connections) const;
|
||||
|
||||
@ -287,6 +290,7 @@ public:
|
||||
|
||||
private:
|
||||
int port_preview = -1;
|
||||
int linked_parent_graph_frame = -1;
|
||||
|
||||
HashMap<int, bool> connected_input_ports;
|
||||
HashMap<int, int> connected_output_ports;
|
||||
@ -351,8 +355,11 @@ public:
|
||||
bool is_disabled() const;
|
||||
void set_disabled(bool p_disabled = true);
|
||||
|
||||
bool is_closable() const;
|
||||
void set_closable(bool p_closable = true);
|
||||
bool is_deletable() const;
|
||||
void set_deletable(bool p_closable = true);
|
||||
|
||||
void set_frame(int p_node);
|
||||
int get_frame() const;
|
||||
|
||||
virtual Vector<StringName> get_editable_properties() const;
|
||||
virtual HashMap<StringName, String> get_editable_properties_names() const;
|
||||
@ -712,12 +719,15 @@ public:
|
||||
VisualShaderNodeResizableBase();
|
||||
};
|
||||
|
||||
class VisualShaderNodeComment : public VisualShaderNodeResizableBase {
|
||||
GDCLASS(VisualShaderNodeComment, VisualShaderNodeResizableBase);
|
||||
class VisualShaderNodeFrame : public VisualShaderNodeResizableBase {
|
||||
GDCLASS(VisualShaderNodeFrame, VisualShaderNodeResizableBase);
|
||||
|
||||
protected:
|
||||
String title = "Comment";
|
||||
String description = "";
|
||||
String title = "Title";
|
||||
bool tint_color_enabled = false;
|
||||
Color tint_color = Color(0.3, 0.3, 0.3, 0.75);
|
||||
bool autoshrink = true;
|
||||
HashSet<int> attached_nodes;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
@ -738,12 +748,23 @@ public:
|
||||
void set_title(const String &p_title);
|
||||
String get_title() const;
|
||||
|
||||
void set_description(const String &p_description);
|
||||
String get_description() const;
|
||||
void set_tint_color_enabled(bool p_enable);
|
||||
bool is_tint_color_enabled() const;
|
||||
|
||||
virtual Category get_category() const override { return CATEGORY_SPECIAL; }
|
||||
void set_tint_color(const Color &p_color);
|
||||
Color get_tint_color() const;
|
||||
|
||||
VisualShaderNodeComment();
|
||||
void set_autoshrink_enabled(bool p_enable);
|
||||
bool is_autoshrink_enabled() const;
|
||||
|
||||
void add_attached_node(int p_node);
|
||||
void remove_attached_node(int p_node);
|
||||
void set_attached_nodes(const PackedInt32Array &p_nodes);
|
||||
PackedInt32Array get_attached_nodes() const;
|
||||
|
||||
virtual Category get_category() const override { return CATEGORY_NONE; }
|
||||
|
||||
VisualShaderNodeFrame();
|
||||
};
|
||||
|
||||
class VisualShaderNodeGroupBase : public VisualShaderNodeResizableBase {
|
||||
|
@ -757,6 +757,36 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
|
||||
theme->set_constant("shadow_outline_size", "GraphNodeTitleLabel", Math::round(1 * scale));
|
||||
theme->set_constant("line_spacing", "GraphNodeTitleLabel", Math::round(3 * scale));
|
||||
|
||||
// GraphFrame
|
||||
|
||||
Ref<StyleBoxFlat> graphframe_sb = make_flat_stylebox(style_pressed_color, 18, 12, 18, 12, 3, true, 2);
|
||||
graphframe_sb->set_expand_margin(SIDE_TOP, 38 * scale);
|
||||
graphframe_sb->set_border_color(style_pressed_color);
|
||||
Ref<StyleBoxFlat> graphframe_sb_selected = graphframe_sb->duplicate();
|
||||
graphframe_sb_selected->set_border_color(style_hover_color);
|
||||
|
||||
theme->set_stylebox("panel", "GraphFrame", graphframe_sb);
|
||||
theme->set_stylebox("panel_selected", "GraphFrame", graphframe_sb_selected);
|
||||
theme->set_stylebox("titlebar", "GraphFrame", make_empty_stylebox(4, 4, 4, 4));
|
||||
theme->set_stylebox("titlebar_selected", "GraphFrame", make_empty_stylebox(4, 4, 4, 4));
|
||||
theme->set_icon("resizer", "GraphFrame", icons["resizer_se"]);
|
||||
theme->set_color("resizer_color", "GraphFrame", control_font_color);
|
||||
|
||||
// GraphFrame's title Label
|
||||
|
||||
theme->set_type_variation("GraphFrameTitleLabel", "Label");
|
||||
|
||||
theme->set_stylebox("normal", "GraphFrameTitleLabel", memnew(StyleBoxEmpty));
|
||||
theme->set_font_size("font_size", "GraphFrameTitleLabel", 22);
|
||||
theme->set_color("font_color", "GraphFrameTitleLabel", Color(1, 1, 1));
|
||||
theme->set_color("font_shadow_color", "GraphFrameTitleLabel", Color(0, 0, 0, 0));
|
||||
theme->set_color("font_outline_color", "GraphFrameTitleLabel", Color(1, 1, 1));
|
||||
theme->set_constant("shadow_offset_x", "GraphFrameTitleLabel", 1 * scale);
|
||||
theme->set_constant("shadow_offset_y", "GraphFrameTitleLabel", 1 * scale);
|
||||
theme->set_constant("outline_size", "GraphFrameTitleLabel", 0);
|
||||
theme->set_constant("shadow_outline_size", "GraphFrameTitleLabel", 1 * scale);
|
||||
theme->set_constant("line_spacing", "GraphFrameTitleLabel", 3 * scale);
|
||||
|
||||
// Tree
|
||||
|
||||
theme->set_stylebox("panel", "Tree", make_flat_stylebox(style_normal_color, 4, 4, 4, 5));
|
||||
|
Loading…
Reference in New Issue
Block a user