Jelajahi Sumber

Redo the shader editor

* Shader editor is permanent (no longer transient).
* Can edit multiple files at the same time.

Likely fixes many usability issues (please lend me a hand Bugsquad team to identify them).
reduz 3 tahun lalu
induk
melakukan
73c102f272

+ 0 - 1
editor/editor_node.cpp

@@ -7047,7 +7047,6 @@ EditorNode::EditorNode() {
 	add_editor_plugin(VersionControlEditorPlugin::get_singleton());
 	add_editor_plugin(memnew(ShaderEditorPlugin));
 	add_editor_plugin(memnew(ShaderFileEditorPlugin));
-	add_editor_plugin(memnew(VisualShaderEditorPlugin));
 
 	add_editor_plugin(memnew(Camera3DEditorPlugin));
 	add_editor_plugin(memnew(ThemeEditorPlugin));

+ 1 - 1
editor/plugins/script_editor_plugin.cpp

@@ -3547,7 +3547,7 @@ void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_numb
 			ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_singleton()->get_editor_data().get_editor("Shader"));
 			shader_editor->edit(res.ptr());
 			shader_editor->make_visible(true);
-			shader_editor->get_shader_editor()->goto_line_selection(line_number - 1, begin, end);
+			shader_editor->get_shader_editor(res)->goto_line_selection(line_number - 1, begin, end);
 			return;
 		} else if (fpath.get_extension() == "tscn") {
 			EditorNode::get_singleton()->load_scene(fpath);

+ 190 - 20
editor/plugins/shader_editor_plugin.cpp

@@ -38,8 +38,12 @@
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
+#include "editor/filesystem_dock.h"
+#include "editor/plugins/visual_shader_editor_plugin.h"
 #include "editor/project_settings_editor.h"
 #include "editor/property_editor.h"
+#include "editor/shader_create_dialog.h"
+#include "scene/gui/split_container.h"
 #include "servers/display_server.h"
 #include "servers/rendering/shader_types.h"
 
@@ -836,50 +840,216 @@ ShaderEditor::ShaderEditor() {
 	_editor_settings_changed();
 }
 
+void ShaderEditorPlugin::_update_shader_list() {
+	shader_list->clear();
+	for (uint32_t i = 0; i < edited_shaders.size(); i++) {
+		String text;
+		String path = edited_shaders[i].shader->get_path();
+		String _class = edited_shaders[i].shader->get_class();
+
+		if (path.is_resource_file()) {
+			text = path.get_file();
+		} else if (edited_shaders[i].shader->get_name() != "") {
+			text = edited_shaders[i].shader->get_name();
+		} else {
+			text = _class + ":" + itos(edited_shaders[i].shader->get_instance_id());
+		}
+
+		if (!shader_list->has_theme_icon(_class, SNAME("EditorIcons"))) {
+			_class = "Resource";
+		}
+		Ref<Texture2D> icon = shader_list->get_theme_icon(_class, SNAME("EditorIcons"));
+
+		shader_list->add_item(text, icon);
+		shader_list->set_item_tooltip(shader_list->get_item_count() - 1, path);
+	}
+
+	if (shader_tabs->get_tab_count()) {
+		shader_list->select(shader_tabs->get_current_tab());
+	}
+
+	for (int i = 1; i < FILE_MAX; i++) {
+		file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), edited_shaders.size() == 0);
+	}
+}
+
 void ShaderEditorPlugin::edit(Object *p_object) {
 	Shader *s = Object::cast_to<Shader>(p_object);
-	shader_editor->edit(s);
+	for (uint32_t i = 0; i < edited_shaders.size(); i++) {
+		if (edited_shaders[i].shader.ptr() == s) {
+			// Exists, select.
+			shader_tabs->set_current_tab(i);
+			shader_list->select(i);
+			return;
+		}
+	}
+	// Add.
+	EditedShader es;
+	es.shader = Ref<Shader>(s);
+	Ref<VisualShader> vs = es.shader;
+	if (vs.is_valid()) {
+		es.visual_shader_editor = memnew(VisualShaderEditor);
+		es.visual_shader_editor->edit(vs.ptr());
+		shader_tabs->add_child(es.visual_shader_editor);
+	} else {
+		es.shader_editor = memnew(ShaderEditor);
+		es.shader_editor->edit(s);
+		shader_tabs->add_child(es.shader_editor);
+	}
+	shader_tabs->set_current_tab(shader_tabs->get_tab_count() - 1);
+	edited_shaders.push_back(es);
+	_update_shader_list();
 }
 
 bool ShaderEditorPlugin::handles(Object *p_object) const {
-	Shader *shader = Object::cast_to<Shader>(p_object);
-	return shader != nullptr && shader->is_text_shader();
+	return Object::cast_to<Shader>(p_object) != nullptr;
 }
 
 void ShaderEditorPlugin::make_visible(bool p_visible) {
 	if (p_visible) {
-		button->show();
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(shader_editor);
-
-	} else {
-		button->hide();
-		if (shader_editor->is_visible_in_tree()) {
-			EditorNode::get_singleton()->hide_bottom_panel();
-		}
-		shader_editor->apply_shaders();
+		EditorNode::get_singleton()->make_bottom_panel_item_visible(main_split);
 	}
 }
 
 void ShaderEditorPlugin::selected_notify() {
-	shader_editor->ensure_select_current();
+}
+
+ShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) {
+	for (uint32_t i = 0; i < edited_shaders.size(); i++) {
+		if (edited_shaders[i].shader == p_for_shader) {
+			return edited_shaders[i].shader_editor;
+		}
+	}
+	return nullptr;
 }
 
 void ShaderEditorPlugin::save_external_data() {
-	shader_editor->save_external_data();
+	for (uint32_t i = 0; i < edited_shaders.size(); i++) {
+		if (edited_shaders[i].shader_editor) {
+			edited_shaders[i].shader_editor->save_external_data();
+		}
+	}
 }
 
 void ShaderEditorPlugin::apply_changes() {
-	shader_editor->apply_shaders();
+	for (uint32_t i = 0; i < edited_shaders.size(); i++) {
+		if (edited_shaders[i].shader_editor) {
+			edited_shaders[i].shader_editor->apply_shaders();
+		}
+	}
+}
+
+void ShaderEditorPlugin::_shader_selected(int p_index) {
+	shader_tabs->set_current_tab(p_index);
+}
+
+void ShaderEditorPlugin::_close_shader(int p_index) {
+	int index = shader_tabs->get_current_tab();
+	ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
+	Control *c = shader_tabs->get_tab_control(index);
+	memdelete(c);
+	edited_shaders.remove_at(index);
+	_update_shader_list();
+}
+
+void ShaderEditorPlugin::_resource_saved(Object *obj) {
+	// May have been renamed on save.
+	for (uint32_t i = 0; i < edited_shaders.size(); i++) {
+		if (edited_shaders[i].shader.ptr() == obj) {
+			_update_shader_list();
+			return;
+		}
+	}
+}
+
+void ShaderEditorPlugin::_menu_item_pressed(int p_index) {
+	switch (p_index) {
+		case FILE_NEW: {
+			String base_path = FileSystemDock::get_singleton()->get_current_path();
+			shader_create_dialog->config(base_path.plus_file("new_shader"), false, false, 0);
+			shader_create_dialog->popup_centered();
+		} break;
+		case FILE_OPEN: {
+			InspectorDock::get_singleton()->open_resource("Shader");
+		} break;
+		case FILE_SAVE: {
+			int index = shader_tabs->get_current_tab();
+			ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
+			EditorNode::get_singleton()->save_resource(edited_shaders[index].shader);
+		} break;
+		case FILE_SAVE_AS: {
+			int index = shader_tabs->get_current_tab();
+			ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
+			String path = edited_shaders[index].shader->get_path();
+			if (!path.is_resource_file()) {
+				path = "";
+			}
+			EditorNode::get_singleton()->save_resource_as(edited_shaders[index].shader, path);
+		} break;
+		case FILE_INSPECT: {
+			int index = shader_tabs->get_current_tab();
+			ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
+			EditorNode::get_singleton()->push_item(edited_shaders[index].shader.ptr());
+		} break;
+		case FILE_CLOSE: {
+			_close_shader(shader_tabs->get_current_tab());
+		} break;
+	}
+}
+
+void ShaderEditorPlugin::_shader_created(Ref<Shader> p_shader) {
+	EditorNode::get_singleton()->push_item(p_shader.ptr());
 }
 
 ShaderEditorPlugin::ShaderEditorPlugin() {
-	shader_editor = memnew(ShaderEditor);
+	main_split = memnew(HSplitContainer);
+
+	VBoxContainer *vb = memnew(VBoxContainer);
+
+	HBoxContainer *file_hb = memnew(HBoxContainer);
+	vb->add_child(file_hb);
+	file_menu = memnew(MenuButton);
+	file_menu->set_text(TTR("File"));
+	file_menu->get_popup()->add_item(TTR("New Shader"), FILE_NEW);
+	file_menu->get_popup()->add_separator();
+	file_menu->get_popup()->add_item(TTR("Load Shader"), FILE_OPEN);
+	file_menu->get_popup()->add_item(TTR("Save Shader"), FILE_SAVE);
+	file_menu->get_popup()->add_item(TTR("Save Shader As"), FILE_SAVE_AS);
+	file_menu->get_popup()->add_separator();
+	file_menu->get_popup()->add_item(TTR("Open Shader in Inspector"), FILE_INSPECT);
+	file_menu->get_popup()->add_separator();
+	file_menu->get_popup()->add_item(TTR("Close Shader"), FILE_CLOSE);
+	file_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditorPlugin::_menu_item_pressed));
+	file_hb->add_child(file_menu);
+
+	for (int i = 1; i < FILE_MAX; i++) {
+		file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), true);
+	}
+
+	shader_list = memnew(ItemList);
+	shader_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+	vb->add_child(shader_list);
+	shader_list->connect("item_selected", callable_mp(this, &ShaderEditorPlugin::_shader_selected));
+
+	main_split->add_child(vb);
+	vb->set_custom_minimum_size(Size2(200, 300) * EDSCALE);
+
+	shader_tabs = memnew(TabContainer);
+	shader_tabs->set_tabs_visible(false);
+	shader_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+	main_split->add_child(shader_tabs);
+	Ref<StyleBoxEmpty> empty;
+	empty.instantiate();
+	shader_tabs->add_theme_style_override("panel", empty);
+
+	button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Shader Editor"), main_split);
 
-	shader_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
-	button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Shader"), shader_editor);
-	button->hide();
+	// Defer connect because Editor class is not in the binding system yet.
+	EditorNode::get_singleton()->call_deferred("connect", "resource_saved", callable_mp(this, &ShaderEditorPlugin::_resource_saved), varray(), CONNECT_DEFERRED);
 
-	_2d = false;
+	shader_create_dialog = memnew(ShaderCreateDialog);
+	vb->add_child(shader_create_dialog);
+	shader_create_dialog->connect("shader_created", callable_mp(this, &ShaderEditorPlugin::_shader_created));
 }
 
 ShaderEditorPlugin::~ShaderEditorPlugin() {

+ 39 - 3
editor/plugins/shader_editor_plugin.h

@@ -42,6 +42,11 @@
 #include "scene/resources/shader.h"
 #include "servers/rendering/shader_warnings.h"
 
+class ItemList;
+class VisualShaderEditor;
+class HSplitContainer;
+class ShaderCreateDialog;
+
 class ShaderTextEditor : public CodeTextEditor {
 	GDCLASS(ShaderTextEditor, CodeTextEditor);
 
@@ -160,9 +165,40 @@ public:
 class ShaderEditorPlugin : public EditorPlugin {
 	GDCLASS(ShaderEditorPlugin, EditorPlugin);
 
-	bool _2d;
-	ShaderEditor *shader_editor = nullptr;
+	struct EditedShader {
+		Ref<Shader> shader;
+		ShaderEditor *shader_editor = nullptr;
+		VisualShaderEditor *visual_shader_editor = nullptr;
+	};
+
+	LocalVector<EditedShader> edited_shaders;
+
+	enum {
+		FILE_NEW,
+		FILE_OPEN,
+		FILE_SAVE,
+		FILE_SAVE_AS,
+		FILE_INSPECT,
+		FILE_CLOSE,
+		FILE_MAX
+	};
+
+	HSplitContainer *main_split = nullptr;
+	ItemList *shader_list = nullptr;
+	TabContainer *shader_tabs = nullptr;
+
 	Button *button = nullptr;
+	MenuButton *file_menu = nullptr;
+
+	ShaderCreateDialog *shader_create_dialog = nullptr;
+
+	void _update_shader_list();
+	void _shader_selected(int p_index);
+	void _menu_item_pressed(int p_index);
+	void _resource_saved(Object *obj);
+	void _close_shader(int p_index);
+
+	void _shader_created(Ref<Shader> p_shader);
 
 public:
 	virtual String get_name() const override { return "Shader"; }
@@ -172,7 +208,7 @@ public:
 	virtual void make_visible(bool p_visible) override;
 	virtual void selected_notify() override;
 
-	ShaderEditor *get_shader_editor() const { return shader_editor; }
+	ShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader);
 
 	virtual void save_external_data() override;
 	virtual void apply_changes() override;

+ 0 - 42
editor/plugins/visual_shader_editor_plugin.cpp

@@ -5659,48 +5659,6 @@ VisualShaderEditor::VisualShaderEditor() {
 	property_editor->connect("variant_changed", callable_mp(this, &VisualShaderEditor::_port_edited));
 }
 
-/////////////////
-
-void VisualShaderEditorPlugin::edit(Object *p_object) {
-	visual_shader_editor->edit(Object::cast_to<VisualShader>(p_object));
-}
-
-bool VisualShaderEditorPlugin::handles(Object *p_object) const {
-	return p_object->is_class("VisualShader");
-}
-
-void VisualShaderEditorPlugin::make_visible(bool p_visible) {
-	if (p_visible) {
-		//editor->hide_animation_player_editors();
-		//editor->animation_panel_make_visible(true);
-		button->show();
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(visual_shader_editor);
-		visual_shader_editor->update_nodes();
-		visual_shader_editor->set_process_input(true);
-		//visual_shader_editor->set_process(true);
-	} else {
-		if (visual_shader_editor->is_visible_in_tree()) {
-			EditorNode::get_singleton()->hide_bottom_panel();
-		}
-		button->hide();
-		visual_shader_editor->set_process_input(false);
-		//visual_shader_editor->set_process(false);
-	}
-}
-
-VisualShaderEditorPlugin::VisualShaderEditorPlugin() {
-	visual_shader_editor = memnew(VisualShaderEditor);
-	visual_shader_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
-
-	button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("VisualShader"), visual_shader_editor);
-	button->hide();
-}
-
-VisualShaderEditorPlugin::~VisualShaderEditorPlugin() {
-}
-
-////////////////
-
 class VisualShaderNodePluginInputEditor : public OptionButton {
 	GDCLASS(VisualShaderNodePluginInputEditor, OptionButton);
 

+ 0 - 17
editor/plugins/visual_shader_editor_plugin.h

@@ -493,23 +493,6 @@ public:
 	VisualShaderEditor();
 };
 
-class VisualShaderEditorPlugin : public EditorPlugin {
-	GDCLASS(VisualShaderEditorPlugin, EditorPlugin);
-
-	VisualShaderEditor *visual_shader_editor = nullptr;
-	Button *button = nullptr;
-
-public:
-	virtual String get_name() const override { return "VisualShader"; }
-	bool has_main_screen() const override { return false; }
-	virtual void edit(Object *p_object) override;
-	virtual bool handles(Object *p_object) const override;
-	virtual void make_visible(bool p_visible) override;
-
-	VisualShaderEditorPlugin();
-	~VisualShaderEditorPlugin();
-};
-
 class VisualShaderNodePluginDefault : public VisualShaderNodePlugin {
 	GDCLASS(VisualShaderNodePluginDefault, VisualShaderNodePlugin);