ソースを参照

[VisualShader] Fix and improve editor state persistence

Hendrik Brucker 9 ヶ月 前
コミット
666d7c030b

+ 0 - 5
doc/classes/VisualShader.xml

@@ -186,11 +186,6 @@
 			</description>
 		</method>
 	</methods>
-	<members>
-		<member name="graph_offset" type="Vector2" setter="set_graph_offset" getter="get_graph_offset" default="Vector2(0, 0)">
-			The offset vector of the whole graph.
-		</member>
-	</members>
 	<constants>
 		<constant name="TYPE_VERTEX" value="0" enum="Type">
 			A vertex shader, operating on vertices.

+ 1 - 0
editor/plugins/shader_editor_plugin.cpp

@@ -454,6 +454,7 @@ void ShaderEditorPlugin::_close_shader(int p_index) {
 	Control *c = shader_tabs->get_tab_control(p_index);
 	VisualShaderEditor *vs_editor = Object::cast_to<VisualShaderEditor>(c);
 	if (vs_editor) {
+		vs_editor->save_editor_layout();
 		file_menu->get_parent()->remove_child(file_menu);
 		menu_hb->add_child(file_menu);
 		menu_hb->move_child(file_menu, 0);

+ 111 - 64
editor/plugins/visual_shader_editor_plugin.cpp

@@ -36,6 +36,7 @@
 #include "core/os/keyboard.h"
 #include "core/version_generated.gen.h"
 #include "editor/editor_node.h"
+#include "editor/editor_paths.h"
 #include "editor/editor_properties.h"
 #include "editor/editor_properties_vector.h"
 #include "editor/editor_settings.h"
@@ -224,7 +225,7 @@ void VisualShaderGraphPlugin::set_connections(const List<VisualShader::Connectio
 }
 
 void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id, bool p_is_valid) {
-	if (visual_shader->get_shader_type() == p_type && links.has(p_node_id) && links[p_node_id].output_ports.has(p_port_id)) {
+	if (editor->get_current_shader_type() == p_type && links.has(p_node_id) && links[p_node_id].output_ports.has(p_port_id)) {
 		Link &link = links[p_node_id];
 
 		for (const KeyValue<int, Port> &E : link.output_ports) {
@@ -261,7 +262,7 @@ void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p
 			vbox->add_child(offset);
 
 			VisualShaderNodePortPreview *port_preview = memnew(VisualShaderNodePortPreview);
-			port_preview->setup(visual_shader, editor->preview_material, visual_shader->get_shader_type(), links[p_node_id].output_ports[p_port_id].type == VisualShaderNode::PORT_TYPE_VECTOR_4D, p_node_id, p_port_id, p_is_valid);
+			port_preview->setup(visual_shader, editor->preview_material, editor->get_current_shader_type(), links[p_node_id].output_ports[p_port_id].type == VisualShaderNode::PORT_TYPE_VECTOR_4D, p_node_id, p_port_id, p_is_valid);
 			port_preview->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
 			vbox->add_child(port_preview);
 			link.preview_visible = true;
@@ -276,7 +277,7 @@ void VisualShaderGraphPlugin::update_node_deferred(VisualShader::Type p_type, in
 }
 
 void VisualShaderGraphPlugin::update_node(VisualShader::Type p_type, int p_node_id) {
-	if (p_type != visual_shader->get_shader_type() || !links.has(p_node_id)) {
+	if (p_type != editor->get_current_shader_type() || !links.has(p_node_id)) {
 		return;
 	}
 	remove_node(p_type, p_node_id, true);
@@ -286,7 +287,7 @@ void VisualShaderGraphPlugin::update_node(VisualShader::Type p_type, int p_node_
 }
 
 void VisualShaderGraphPlugin::set_input_port_default_value(VisualShader::Type p_type, int p_node_id, int p_port_id, const Variant &p_value) {
-	if (p_type != visual_shader->get_shader_type() || !links.has(p_node_id)) {
+	if (p_type != editor->get_current_shader_type() || !links.has(p_node_id)) {
 		return;
 	}
 
@@ -326,7 +327,7 @@ void VisualShaderGraphPlugin::set_input_port_default_value(VisualShader::Type p_
 }
 
 void VisualShaderGraphPlugin::set_parameter_name(VisualShader::Type p_type, int p_node_id, const String &p_name) {
-	if (visual_shader->get_shader_type() == p_type && links.has(p_node_id) && links[p_node_id].parameter_name != nullptr) {
+	if (editor->get_current_shader_type() == p_type && links.has(p_node_id) && links[p_node_id].parameter_name != nullptr) {
 		links[p_node_id].parameter_name->set_text(p_name);
 	}
 }
@@ -367,14 +368,14 @@ int VisualShaderGraphPlugin::get_constant_index(float p_constant) const {
 }
 
 void VisualShaderGraphPlugin::set_expression(VisualShader::Type p_type, int p_node_id, const String &p_expression) {
-	if (p_type != visual_shader->get_shader_type() || !links.has(p_node_id) || !links[p_node_id].expression_edit) {
+	if (p_type != editor->get_current_shader_type() || !links.has(p_node_id) || !links[p_node_id].expression_edit) {
 		return;
 	}
 	links[p_node_id].expression_edit->set_text(p_expression);
 }
 
 void VisualShaderGraphPlugin::attach_node_to_frame(VisualShader::Type p_type, int p_node_id, int p_frame_id) {
-	if (p_type != visual_shader->get_shader_type() || !links.has(p_node_id) || !links.has(p_frame_id)) {
+	if (p_type != editor->get_current_shader_type() || !links.has(p_node_id) || !links.has(p_frame_id)) {
 		return;
 	}
 
@@ -463,7 +464,7 @@ void VisualShaderGraphPlugin::update_reroute_nodes() {
 	for (const KeyValue<int, Link> &E : links) {
 		Ref<VisualShaderNodeReroute> reroute_node = Object::cast_to<VisualShaderNodeReroute>(E.value.visual_node);
 		if (reroute_node.is_valid()) {
-			update_node(visual_shader->get_shader_type(), E.key);
+			update_node(editor->get_current_shader_type(), E.key);
 		}
 	}
 }
@@ -503,10 +504,6 @@ void VisualShaderGraphPlugin::update_parameter_refs() {
 	}
 }
 
-VisualShader::Type VisualShaderGraphPlugin::get_shader_type() const {
-	return visual_shader->get_shader_type();
-}
-
 // Only updates the linked frames of the given node, not the node itself (in case it's a frame node).
 void VisualShaderGraphPlugin::update_frames(VisualShader::Type p_type, int p_node) {
 	GraphEdit *graph = editor->graph;
@@ -540,7 +537,7 @@ void VisualShaderGraphPlugin::update_frames(VisualShader::Type p_type, int p_nod
 }
 
 void VisualShaderGraphPlugin::set_node_position(VisualShader::Type p_type, int p_id, const Vector2 &p_position) {
-	if (visual_shader->get_shader_type() == p_type && links.has(p_id)) {
+	if (editor->get_current_shader_type() == p_type && links.has(p_id)) {
 		links[p_id].graph_element->set_position_offset(p_position);
 	}
 }
@@ -606,7 +603,7 @@ bool VisualShaderGraphPlugin::is_node_has_parameter_instances_relatively(VisualS
 }
 
 void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool p_just_update, bool p_update_frames) {
-	if (visual_shader.is_null() || p_type != visual_shader->get_shader_type()) {
+	if (visual_shader.is_null() || p_type != editor->get_current_shader_type()) {
 		return;
 	}
 	GraphEdit *graph = editor->graph;
@@ -1441,7 +1438,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
 }
 
 void VisualShaderGraphPlugin::remove_node(VisualShader::Type p_type, int p_id, bool p_just_update) {
-	if (visual_shader->get_shader_type() == p_type && links.has(p_id)) {
+	if (editor->get_current_shader_type() == p_type && links.has(p_id)) {
 		GraphEdit *graph_edit = editor->graph;
 		if (!graph_edit) {
 			return;
@@ -1461,7 +1458,7 @@ void VisualShaderGraphPlugin::connect_nodes(VisualShader::Type p_type, int p_fro
 		return;
 	}
 
-	if (visual_shader.is_valid() && visual_shader->get_shader_type() == p_type) {
+	if (visual_shader.is_valid() && editor->get_current_shader_type() == p_type) {
 		// Update reroute nodes since their port type might have changed.
 		Ref<VisualShaderNodeReroute> reroute_to = visual_shader->get_node(p_type, p_to_node);
 		Ref<VisualShaderNodeReroute> reroute_from = visual_shader->get_node(p_type, p_from_node);
@@ -1484,7 +1481,7 @@ void VisualShaderGraphPlugin::disconnect_nodes(VisualShader::Type p_type, int p_
 		return;
 	}
 
-	if (visual_shader.is_valid() && visual_shader->get_shader_type() == p_type) {
+	if (visual_shader.is_valid() && editor->get_current_shader_type() == p_type) {
 		graph->disconnect_node(itos(p_from_node), p_from_port, itos(p_to_node), p_to_port);
 
 		for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) {
@@ -1524,6 +1521,7 @@ List<VisualShaderEditor::CopyItem> VisualShaderEditor::copy_items_buffer;
 List<VisualShader::Connection> VisualShaderEditor::copy_connections_buffer;
 
 void VisualShaderEditor::edit_shader(const Ref<Shader> &p_shader) {
+	shader_fully_loaded = false;
 	bool changed = false;
 	VisualShader *visual_shader_ptr = Object::cast_to<VisualShader>(p_shader.ptr());
 	if (visual_shader_ptr) {
@@ -1538,7 +1536,6 @@ void VisualShaderEditor::edit_shader(const Ref<Shader> &p_shader) {
 		graph_plugin->register_shader(visual_shader.ptr());
 
 		visual_shader->connect_changed(callable_mp(this, &VisualShaderEditor::_update_preview));
-		visual_shader->set_graph_offset(graph->get_scroll_offset() / EDSCALE);
 		_set_mode(visual_shader->get_mode());
 
 		preview_material->set_shader(visual_shader);
@@ -1558,6 +1555,7 @@ void VisualShaderEditor::edit_shader(const Ref<Shader> &p_shader) {
 			_update_options_menu();
 			_update_preview();
 			_update_graph();
+			callable_mp(this, &VisualShaderEditor::_restore_editor_state).call_deferred();
 		}
 	}
 }
@@ -1581,6 +1579,37 @@ void VisualShaderEditor::validate_script() {
 	}
 }
 
+void VisualShaderEditor::save_editor_layout() {
+	const String id_string = _get_cache_id_string();
+
+	const String offset_cache_key = _get_cache_key("offset");
+	const String zoom_cache_key = _get_cache_key("zoom");
+	vs_editor_cache->set_value(id_string, offset_cache_key, graph->get_scroll_offset() / EDSCALE);
+	vs_editor_cache->set_value(id_string, zoom_cache_key, graph->get_zoom());
+	vs_editor_cache->save(EditorPaths::get_singleton()->get_project_settings_dir().path_join("vs_editor_cache.cfg"));
+}
+
+void VisualShaderEditor::set_current_shader_type(VisualShader::Type p_type) {
+	current_type = p_type;
+
+	const String id_string = _get_cache_id_string();
+
+	vs_editor_cache->set_value(id_string, "edited_type", p_type);
+	vs_editor_cache->save(EditorPaths::get_singleton()->get_project_settings_dir().path_join("vs_editor_cache.cfg"));
+
+	const String offset_cache_key = _get_cache_key("offset");
+	const String zoom_cache_key = _get_cache_key("zoom");
+	const Vector2 saved_scroll_offset = vs_editor_cache->get_value(id_string, offset_cache_key, Vector2());
+	const real_t saved_zoom = vs_editor_cache->get_value(id_string, zoom_cache_key, 1.0);
+
+	graph->set_scroll_offset(saved_scroll_offset);
+	graph->set_zoom(saved_zoom);
+}
+
+VisualShader::Type VisualShaderEditor::get_current_shader_type() const {
+	return current_type;
+}
+
 Control *VisualShaderEditor::get_top_bar() {
 	return toolbar;
 }
@@ -1723,7 +1752,7 @@ void VisualShaderEditor::_update_custom_script(const Ref<Script> &p_script) {
 	Ref<VisualShaderNodeCustom> ref;
 	ref.instantiate();
 	ref->set_script(p_script);
-	if (!ref->is_available(visual_shader->get_mode(), visual_shader->get_shader_type())) {
+	if (!ref->is_available(visual_shader->get_mode(), get_current_shader_type())) {
 		for (int i = 0; i < add_options.size(); i++) {
 			if (add_options[i].is_custom && add_options[i].script == p_script) {
 				add_options.remove_at(i);
@@ -2140,7 +2169,7 @@ void VisualShaderEditor::_update_nodes() {
 				Ref<VisualShaderNodeCustom> ref;
 				ref.instantiate();
 				ref->set_script(scr);
-				if (!ref->is_available(visual_shader->get_mode(), visual_shader->get_shader_type())) {
+				if (!ref->is_available(visual_shader->get_mode(), get_current_shader_type())) {
 					continue;
 				}
 				Dictionary dict = get_custom_node_data(ref);
@@ -2164,7 +2193,7 @@ void VisualShaderEditor::_update_nodes() {
 				Object *instance = ClassDB::instantiate(E);
 				Ref<VisualShaderNodeCustom> ref = Object::cast_to<VisualShaderNodeCustom>(instance);
 				ERR_CONTINUE(ref.is_null());
-				if (!ref->is_available(visual_shader->get_mode(), visual_shader->get_shader_type())) {
+				if (!ref->is_available(visual_shader->get_mode(), get_current_shader_type())) {
 					continue;
 				}
 				Dictionary dict = get_custom_node_data(ref);
@@ -2185,7 +2214,7 @@ void VisualShaderEditor::_update_nodes() {
 			Ref<VisualShaderNodeCustom> custom = Object::cast_to<VisualShaderNodeCustom>(item.node.ptr());
 
 			if (custom.is_valid()) {
-				if (!custom->is_available(visual_shader->get_mode(), visual_shader->get_shader_type())) {
+				if (!custom->is_available(visual_shader->get_mode(), get_current_shader_type())) {
 					item.disabled = true;
 				} else {
 					item.disabled = false;
@@ -2276,7 +2305,7 @@ void VisualShaderEditor::_update_options_menu() {
 					Ref<VisualShaderNodeInput> input = Object::cast_to<VisualShaderNodeInput>(vsn.ptr());
 					if (input.is_valid()) {
 						input->set_shader_mode(visual_shader->get_mode());
-						input->set_shader_type(visual_shader->get_shader_type());
+						input->set_shader_type(get_current_shader_type());
 						if (!add_options[i].ops.is_empty() && add_options[i].ops[0].is_string()) {
 							input->set_input_name((String)add_options[i].ops[0]);
 						}
@@ -2468,7 +2497,12 @@ void VisualShaderEditor::_set_mode(int p_which) {
 		varying_button->show();
 		mode = MODE_FLAGS_SPATIAL_CANVASITEM;
 	}
-	visual_shader->set_shader_type(get_current_shader_type());
+
+	const String id_string = _get_cache_id_string();
+
+	int saved_type = vs_editor_cache->get_value(id_string, "edited_type", 0);
+	edit_type->select(saved_type);
+	set_current_shader_type((VisualShader::Type)saved_type);
 }
 
 Size2 VisualShaderEditor::get_minimum_size() const {
@@ -2566,16 +2600,10 @@ void VisualShaderEditor::_update_parameter_refs(HashSet<String> &p_deleted_names
 }
 
 void VisualShaderEditor::_update_graph() {
-	if (updating) {
-		return;
-	}
-
 	if (visual_shader.is_null()) {
 		return;
 	}
 
-	graph->set_scroll_offset(visual_shader->get_graph_offset() * EDSCALE);
-
 	VisualShader::Type type = get_current_shader_type();
 
 	graph->clear_connections();
@@ -2632,18 +2660,33 @@ void VisualShaderEditor::_update_graph() {
 	graph->set_connection_lines_curvature(graph_lines_curvature);
 }
 
-VisualShader::Type VisualShaderEditor::get_current_shader_type() const {
-	VisualShader::Type type;
-	if (mode & MODE_FLAGS_PARTICLES) {
-		type = VisualShader::Type(edit_type->get_selected() + 3 + (custom_mode_enabled ? 3 : 0));
-	} else if (mode & MODE_FLAGS_SKY) {
-		type = VisualShader::Type(edit_type->get_selected() + 8);
-	} else if (mode & MODE_FLAGS_FOG) {
-		type = VisualShader::Type(edit_type->get_selected() + 9);
-	} else {
-		type = VisualShader::Type(edit_type->get_selected());
+void VisualShaderEditor::_restore_editor_state() {
+	const String id_string = _get_cache_id_string();
+
+	const String offset_cache_key = _get_cache_key("offset");
+	const String zoom_cache_key = _get_cache_key("zoom");
+	const Vector2 saved_scroll_offset = vs_editor_cache->get_value(id_string, offset_cache_key, Vector2());
+	const real_t saved_zoom = vs_editor_cache->get_value(id_string, zoom_cache_key, 1.0);
+
+	// Setting the scroll offset needs to be further deferred because it requires min/max scroll offset to be final (as it gets clamped).
+	callable_mp(graph, &GraphEdit::set_zoom).call_deferred(saved_zoom);
+	callable_mp(graph, &GraphEdit::set_scroll_offset).call_deferred(saved_scroll_offset);
+
+	shader_fully_loaded = true;
+}
+
+String VisualShaderEditor::_get_cache_id_string() const {
+	String id_string = visual_shader->get_path();
+	const ResourceUID::ID uid = EditorFileSystem::get_singleton()->get_file_uid(id_string);
+	if (uid != ResourceUID::INVALID_ID) {
+		id_string = ResourceUID::get_singleton()->id_to_text(uid);
 	}
-	return type;
+	return id_string;
+}
+
+String VisualShaderEditor::_get_cache_key(const String &p_prop_name) const {
+	const int type = get_current_shader_type();
+	return "type" + itos(type) + ":" + p_prop_name;
 }
 
 void VisualShaderEditor::_add_input_port(int p_node, int p_port, int p_port_type, const String &p_name) {
@@ -3922,7 +3965,7 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, cons
 				VisualShaderNodeInput *input = Object::cast_to<VisualShaderNodeInput>(vsnode.ptr());
 				if (input) {
 					input->set_shader_mode(visual_shader->get_mode());
-					input->set_shader_type(visual_shader->get_shader_type());
+					input->set_shader_type(get_current_shader_type());
 				}
 
 				// Attempting to connect to the first correct port.
@@ -4123,7 +4166,7 @@ void VisualShaderEditor::_nodes_dragged() {
 	}
 
 	for (const int node_id : nodes_link_to_frame_buffer) {
-		VisualShader::Type type = visual_shader->get_shader_type();
+		VisualShader::Type type = get_current_shader_type();
 		Ref<VisualShaderNode> vs_node = visual_shader->get_node(type, node_id);
 
 		undo_redo->add_do_method(visual_shader.ptr(), "attach_node_to_frame", type, node_id, frame_node_id_to_link_to);
@@ -5295,20 +5338,15 @@ void VisualShaderEditor::_notification(int p_what) {
 	}
 }
 
-void VisualShaderEditor::_scroll_changed(const Vector2 &p_scroll) {
-	if (updating) {
+void VisualShaderEditor::_scroll_offset_changed(const Vector2 &p_scroll) {
+	if (!shader_fully_loaded) {
 		return;
 	}
-	updating = true;
-	visual_shader->set_graph_offset(p_scroll / EDSCALE);
-	updating = false;
+
+	panning_debounce_timer->start();
 }
 
 void VisualShaderEditor::_node_changed(int p_id) {
-	if (updating) {
-		return;
-	}
-
 	if (is_visible_in_tree()) {
 		_update_graph();
 	}
@@ -5329,7 +5367,7 @@ void VisualShaderEditor::_frame_rect_changed(const GraphFrame *p_frame, const Re
 	}
 
 	int node_id = String(p_frame->get_name()).to_int();
-	Ref<VisualShaderNodeResizableBase> vsnode = visual_shader->get_node(visual_shader->get_shader_type(), node_id);
+	Ref<VisualShaderNodeResizableBase> vsnode = visual_shader->get_node(get_current_shader_type(), node_id);
 	if (vsnode.is_null()) {
 		return;
 	}
@@ -5573,7 +5611,7 @@ void VisualShaderEditor::_paste_nodes(bool p_use_custom_position, const Vector2
 	_dup_paste_nodes(type, copy_items_buffer, copy_connections_buffer, graph->get_scroll_offset() / scale + mpos / scale - selection_center, false);
 }
 
-void VisualShaderEditor::_mode_selected(int p_id) {
+void VisualShaderEditor::_type_selected(int p_id) {
 	int offset = 0;
 	if (mode & MODE_FLAGS_PARTICLES) {
 		offset = 3;
@@ -5593,7 +5631,7 @@ void VisualShaderEditor::_mode_selected(int p_id) {
 		offset = 9;
 	}
 
-	visual_shader->set_shader_type(VisualShader::Type(p_id + offset));
+	set_current_shader_type(VisualShader::Type(p_id + offset));
 	_update_nodes();
 	_update_graph();
 
@@ -5607,9 +5645,9 @@ void VisualShaderEditor::_custom_mode_toggled(bool p_enabled) {
 	custom_mode_enabled = p_enabled;
 	int id = edit_type->get_selected() + 3;
 	if (p_enabled) {
-		visual_shader->set_shader_type(VisualShader::Type(id + 3));
+		set_current_shader_type(VisualShader::Type(id + 3));
 	} else {
-		visual_shader->set_shader_type(VisualShader::Type(id));
+		set_current_shader_type(VisualShader::Type(id));
 	}
 	_update_options_menu();
 	_update_graph();
@@ -6242,7 +6280,7 @@ void VisualShaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
 						saved_node_pos_dirty = true;
 						_add_node(cubemap_node_option_idx, {}, arr[i], i);
 					} else if (type == "Mesh" && visual_shader->get_mode() == Shader::MODE_PARTICLES &&
-							(visual_shader->get_shader_type() == VisualShader::TYPE_START || visual_shader->get_shader_type() == VisualShader::TYPE_START_CUSTOM)) {
+							(get_current_shader_type() == VisualShader::TYPE_START || get_current_shader_type() == VisualShader::TYPE_START_CUSTOM)) {
 						saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE);
 						saved_node_pos_dirty = true;
 						_add_node(mesh_emitter_option_idx, {}, arr[i], i);
@@ -6425,6 +6463,9 @@ void VisualShaderEditor::_bind_methods() {
 }
 
 VisualShaderEditor::VisualShaderEditor() {
+	vs_editor_cache.instantiate();
+	vs_editor_cache->load(EditorPaths::get_singleton()->get_project_settings_dir().path_join("vs_editor_cache.cfg"));
+
 	ShaderLanguage::get_keyword_list(&keyword_list);
 	EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &VisualShaderEditor::_resource_saved));
 	FileSystemDock::get_singleton()->get_script_create_dialog()->connect("script_created", callable_mp(this, &VisualShaderEditor::_script_created));
@@ -6462,7 +6503,7 @@ VisualShaderEditor::VisualShaderEditor() {
 	graph->connect("connection_request", callable_mp(this, &VisualShaderEditor::_connection_request), CONNECT_DEFERRED);
 	graph->connect("disconnection_request", callable_mp(this, &VisualShaderEditor::_disconnection_request), CONNECT_DEFERRED);
 	graph->connect("node_selected", callable_mp(this, &VisualShaderEditor::_node_selected));
-	graph->connect("scroll_offset_changed", callable_mp(this, &VisualShaderEditor::_scroll_changed));
+	graph->connect("scroll_offset_changed", callable_mp(this, &VisualShaderEditor::_scroll_offset_changed));
 	graph->connect("duplicate_nodes_request", callable_mp(this, &VisualShaderEditor::_duplicate_nodes));
 	graph->connect("copy_nodes_request", callable_mp(this, &VisualShaderEditor::_copy_nodes).bind(false));
 	graph->connect("cut_nodes_request", callable_mp(this, &VisualShaderEditor::_copy_nodes).bind(true));
@@ -6576,24 +6617,24 @@ VisualShaderEditor::VisualShaderEditor() {
 	edit_type_standard->add_item(TTR("Fragment"));
 	edit_type_standard->add_item(TTR("Light"));
 	edit_type_standard->select(1);
-	edit_type_standard->connect(SceneStringName(item_selected), callable_mp(this, &VisualShaderEditor::_mode_selected));
+	edit_type_standard->connect(SceneStringName(item_selected), callable_mp(this, &VisualShaderEditor::_type_selected));
 
 	edit_type_particles = memnew(OptionButton);
 	edit_type_particles->add_item(TTR("Start"));
 	edit_type_particles->add_item(TTR("Process"));
 	edit_type_particles->add_item(TTR("Collide"));
 	edit_type_particles->select(0);
-	edit_type_particles->connect(SceneStringName(item_selected), callable_mp(this, &VisualShaderEditor::_mode_selected));
+	edit_type_particles->connect(SceneStringName(item_selected), callable_mp(this, &VisualShaderEditor::_type_selected));
 
 	edit_type_sky = memnew(OptionButton);
 	edit_type_sky->add_item(TTR("Sky"));
 	edit_type_sky->select(0);
-	edit_type_sky->connect(SceneStringName(item_selected), callable_mp(this, &VisualShaderEditor::_mode_selected));
+	edit_type_sky->connect(SceneStringName(item_selected), callable_mp(this, &VisualShaderEditor::_type_selected));
 
 	edit_type_fog = memnew(OptionButton);
 	edit_type_fog->add_item(TTR("Fog"));
 	edit_type_fog->select(0);
-	edit_type_fog->connect(SceneStringName(item_selected), callable_mp(this, &VisualShaderEditor::_mode_selected));
+	edit_type_fog->connect(SceneStringName(item_selected), callable_mp(this, &VisualShaderEditor::_type_selected));
 
 	edit_type = edit_type_standard;
 
@@ -7691,6 +7732,12 @@ VisualShaderEditor::VisualShaderEditor() {
 	add_child(property_editor_popup);
 
 	edited_property_holder.instantiate();
+
+	panning_debounce_timer = memnew(Timer);
+	panning_debounce_timer->set_one_shot(true);
+	panning_debounce_timer->set_wait_time(1.0);
+	panning_debounce_timer->connect("timeout", callable_mp(this, &VisualShaderEditor::save_editor_layout));
+	add_child(panning_debounce_timer);
 }
 
 class VisualShaderNodePluginInputEditor : public OptionButton {
@@ -8052,7 +8099,7 @@ Control *VisualShaderNodePluginDefault::create_editor(const Ref<Resource> &p_par
 
 	if (p_shader.is_valid() && (p_node->is_class("VisualShaderNodeVaryingGetter") || p_node->is_class("VisualShaderNodeVaryingSetter"))) {
 		VisualShaderNodePluginVaryingEditor *editor = memnew(VisualShaderNodePluginVaryingEditor);
-		editor->setup(vseditor, p_node, p_shader->get_shader_type());
+		editor->setup(vseditor, p_node, vseditor->get_current_shader_type());
 		return editor;
 	}
 

+ 19 - 6
editor/plugins/visual_shader_editor_plugin.h

@@ -175,7 +175,6 @@ public:
 	Ref<Script> get_node_script(int p_node_id) const;
 	void update_theme();
 	bool is_node_has_parameter_instances_relatively(VisualShader::Type p_type, int p_node) const;
-	VisualShader::Type get_shader_type() const;
 
 	VisualShaderGraphPlugin();
 };
@@ -198,6 +197,8 @@ class VisualShaderEditor : public ShaderEditor {
 	GDCLASS(VisualShaderEditor, ShaderEditor);
 	friend class VisualShaderGraphPlugin;
 
+	Ref<ConfigFile> vs_editor_cache; // Keeps the graph offsets and zoom levels for each VisualShader that has been edited.
+
 	PopupPanel *property_editor_popup = nullptr;
 	EditorProperty *property_editor = nullptr;
 	int editing_node = -1;
@@ -295,6 +296,7 @@ class VisualShaderEditor : public ShaderEditor {
 	};
 
 	int mode = MODE_FLAGS_SPATIAL_CANVASITEM;
+	VisualShader::Type current_type = VisualShader::Type::TYPE_VERTEX; // The type of the currently edited VisualShader.
 
 	enum TypeFlags {
 		TYPE_FLAGS_VERTEX = 1,
@@ -381,6 +383,11 @@ class VisualShaderEditor : public ShaderEditor {
 	void _update_nodes();
 	void _update_graph();
 
+	void _restore_editor_state();
+
+	String _get_cache_id_string() const;
+	String _get_cache_key(const String &p_prop_name) const;
+
 	struct AddOption {
 		String name;
 		String category;
@@ -459,15 +466,17 @@ class VisualShaderEditor : public ShaderEditor {
 	};
 	List<DragOp> drag_buffer;
 
+	Timer *panning_debounce_timer = nullptr;
+	bool shader_fully_loaded = false;
+
 	bool drag_dirty = false;
 	void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, int p_node);
 	void _nodes_dragged();
-	bool updating = false;
 
 	void _connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index);
 	void _disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index);
 
-	void _scroll_changed(const Vector2 &p_scroll);
+	void _scroll_offset_changed(const Vector2 &p_scroll);
 	void _node_selected(Object *p_node);
 
 	void _delete_nodes(int p_type, const List<int> &p_nodes);
@@ -556,7 +565,7 @@ class VisualShaderEditor : public ShaderEditor {
 	Vector<Ref<VisualShaderNodePlugin>> plugins;
 	Ref<VisualShaderGraphPlugin> graph_plugin;
 
-	void _mode_selected(int p_id);
+	void _type_selected(int p_id);
 	void _custom_mode_toggled(bool p_enabled);
 
 	void _input_select_item(Ref<VisualShaderNodeInput> p_input, const String &p_name);
@@ -565,8 +574,6 @@ class VisualShaderEditor : public ShaderEditor {
 
 	void _float_constant_selected(int p_which);
 
-	VisualShader::Type get_current_shader_type() const;
-
 	void _add_input_port(int p_node, int p_port, int p_port_type, const String &p_name);
 	void _remove_input_port(int p_node, int p_port);
 	void _change_input_port_type(int p_type, int p_node, int p_port);
@@ -647,6 +654,12 @@ public:
 	virtual bool is_unsaved() const override;
 	virtual void save_external_data(const String &p_str = "") override;
 	virtual void validate_script() override;
+
+	void save_editor_layout();
+
+	void set_current_shader_type(VisualShader::Type p_type);
+	VisualShader::Type get_current_shader_type() const;
+
 	virtual Control *get_top_bar() override;
 
 	void add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin);

+ 8 - 0
misc/extension_api_validation/4.4-stable.expected

@@ -321,3 +321,11 @@ Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/add_image/a
 Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/update_image/arguments': size changed value in new API, from 11 to 12.
 
 Optional argument added. Compatibility methods registered.
+
+
+GH-98566
+--------
+Validate extension JSON: API was removed: classes/VisualShader/methods/set_graph_offset
+Validate extension JSON: API was removed: classes/VisualShader/methods/get_graph_offset
+
+The graph_offset property was removed from the resource. This information is now stored in `vs_editor_cache.cfg`.

+ 0 - 21
scene/resources/visual_shader.cpp

@@ -822,14 +822,6 @@ VisualShaderNodeCustom::VisualShaderNodeCustom() {
 
 /////////////////////////////////////////////////////////
 
-void VisualShader::set_shader_type(Type p_type) {
-	current_type = p_type;
-}
-
-VisualShader::Type VisualShader::get_shader_type() const {
-	return current_type;
-}
-
 void VisualShader::add_varying(const String &p_name, VaryingMode p_mode, VaryingType p_type) {
 	ERR_FAIL_COND(!p_name.is_valid_ascii_identifier());
 	ERR_FAIL_INDEX((int)p_mode, (int)VARYING_MODE_MAX);
@@ -1466,14 +1458,6 @@ void VisualShader::set_mode(Mode p_mode) {
 	notify_property_list_changed();
 }
 
-void VisualShader::set_graph_offset(const Vector2 &p_offset) {
-	graph_offset = p_offset;
-}
-
-Vector2 VisualShader::get_graph_offset() const {
-	return graph_offset;
-}
-
 Shader::Mode VisualShader::get_mode() const {
 	return shader_mode;
 }
@@ -3157,9 +3141,6 @@ void VisualShader::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("get_node_connections", "type"), &VisualShader::_get_node_connections);
 
-	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);
 
@@ -3173,8 +3154,6 @@ void VisualShader::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader);
 
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_graph_offset", "get_graph_offset");
-
 	ADD_PROPERTY_DEFAULT("code", ""); // Inherited from Shader, prevents showing default code as override in docs.
 
 	BIND_ENUM_CONSTANT(TYPE_VERTEX);

+ 0 - 10
scene/resources/visual_shader.h

@@ -114,8 +114,6 @@ public:
 	};
 
 private:
-	Type current_type;
-
 	struct Node {
 		Ref<VisualShaderNode> node;
 		Vector2 position;
@@ -133,8 +131,6 @@ private:
 
 	TypedArray<Dictionary> _get_node_connections(Type p_type) const;
 
-	Vector2 graph_offset;
-
 	HashMap<String, int> modes;
 	HashSet<StringName> flags;
 
@@ -180,9 +176,6 @@ protected:
 	virtual void reset_state() override;
 
 public: // internal methods
-	void set_shader_type(Type p_type);
-	Type get_shader_type() const;
-
 	enum {
 		NODE_ID_INVALID = -1,
 		NODE_ID_OUTPUT = 0,
@@ -249,9 +242,6 @@ public: // internal methods
 
 	virtual bool is_text_shader() const override;
 
-	void set_graph_offset(const Vector2 &p_offset);
-	Vector2 get_graph_offset() const;
-
 	String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const;
 
 	String validate_port_name(const String &p_port_name, VisualShaderNode *p_node, int p_port_id, bool p_output) const;