mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 21:51:22 +00:00 
			
		
		
		
	Standardize dialog input validation as a new class
This commit is contained in:
		
							parent
							
								
									eca6f0eb54
								
							
						
					
					
						commit
						7f41403a6f
					
				
					 13 changed files with 382 additions and 341 deletions
				
			
		|  | @ -33,10 +33,10 @@ | ||||||
| #include "core/io/dir_access.h" | #include "core/io/dir_access.h" | ||||||
| #include "editor/editor_node.h" | #include "editor/editor_node.h" | ||||||
| #include "editor/editor_scale.h" | #include "editor/editor_scale.h" | ||||||
|  | #include "editor/gui/editor_validation_panel.h" | ||||||
| #include "scene/gui/box_container.h" | #include "scene/gui/box_container.h" | ||||||
| #include "scene/gui/label.h" | #include "scene/gui/label.h" | ||||||
| #include "scene/gui/line_edit.h" | #include "scene/gui/line_edit.h" | ||||||
| #include "scene/gui/panel_container.h" |  | ||||||
| 
 | 
 | ||||||
| static String sanitize_input(const String &p_path) { | static String sanitize_input(const String &p_path) { | ||||||
| 	String path = p_path.strip_edges(); | 	String path = p_path.strip_edges(); | ||||||
|  | @ -73,24 +73,17 @@ String DirectoryCreateDialog::_validate_path(const String &p_path) const { | ||||||
| 	return String(); | 	return String(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DirectoryCreateDialog::_on_dir_path_changed(const String &p_text) { | void DirectoryCreateDialog::_on_dir_path_changed() { | ||||||
| 	const String path = sanitize_input(p_text); | 	const String path = sanitize_input(dir_path->get_text()); | ||||||
| 	const String error = _validate_path(path); | 	const String error = _validate_path(path); | ||||||
| 
 | 
 | ||||||
| 	if (error.is_empty()) { | 	if (error.is_empty()) { | ||||||
| 		status_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); |  | ||||||
| 
 |  | ||||||
| 		if (path.contains("/")) { | 		if (path.contains("/")) { | ||||||
| 			status_label->set_text(TTR("Using slashes in folder names will create subfolders recursively.")); | 			validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Using slashes in folder names will create subfolders recursively."), EditorValidationPanel::MSG_OK); | ||||||
| 		} else { |  | ||||||
| 			status_label->set_text(TTR("Folder name is valid.")); |  | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		status_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); | 		validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, error, EditorValidationPanel::MSG_ERROR); | ||||||
| 		status_label->set_text(error); |  | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	get_ok_button()->set_disabled(!error.is_empty()); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DirectoryCreateDialog::ok_pressed() { | void DirectoryCreateDialog::ok_pressed() { | ||||||
|  | @ -127,21 +120,13 @@ void DirectoryCreateDialog::config(const String &p_base_dir) { | ||||||
| 	label->set_text(vformat(TTR("Create new folder in %s:"), base_dir)); | 	label->set_text(vformat(TTR("Create new folder in %s:"), base_dir)); | ||||||
| 	dir_path->set_text("new folder"); | 	dir_path->set_text("new folder"); | ||||||
| 	dir_path->select_all(); | 	dir_path->select_all(); | ||||||
| 	_on_dir_path_changed(dir_path->get_text()); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DirectoryCreateDialog::_bind_methods() { | void DirectoryCreateDialog::_bind_methods() { | ||||||
| 	ADD_SIGNAL(MethodInfo("dir_created")); | 	ADD_SIGNAL(MethodInfo("dir_created")); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DirectoryCreateDialog::_notification(int p_what) { |  | ||||||
| 	switch (p_what) { |  | ||||||
| 		case NOTIFICATION_THEME_CHANGED: { |  | ||||||
| 			status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); |  | ||||||
| 		} break; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| DirectoryCreateDialog::DirectoryCreateDialog() { | DirectoryCreateDialog::DirectoryCreateDialog() { | ||||||
| 	set_title(TTR("Create Folder")); | 	set_title(TTR("Create Folder")); | ||||||
| 	set_min_size(Size2i(480, 0) * EDSCALE); | 	set_min_size(Size2i(480, 0) * EDSCALE); | ||||||
|  | @ -154,7 +139,6 @@ DirectoryCreateDialog::DirectoryCreateDialog() { | ||||||
| 	vb->add_child(label); | 	vb->add_child(label); | ||||||
| 
 | 
 | ||||||
| 	dir_path = memnew(LineEdit); | 	dir_path = memnew(LineEdit); | ||||||
| 	dir_path->connect("text_changed", callable_mp(this, &DirectoryCreateDialog::_on_dir_path_changed)); |  | ||||||
| 	vb->add_child(dir_path); | 	vb->add_child(dir_path); | ||||||
| 	register_text_enter(dir_path); | 	register_text_enter(dir_path); | ||||||
| 
 | 
 | ||||||
|  | @ -162,11 +146,11 @@ DirectoryCreateDialog::DirectoryCreateDialog() { | ||||||
| 	spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); | 	spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); | ||||||
| 	vb->add_child(spacing); | 	vb->add_child(spacing); | ||||||
| 
 | 
 | ||||||
| 	status_panel = memnew(PanelContainer); | 	validation_panel = memnew(EditorValidationPanel); | ||||||
| 	status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL); | 	vb->add_child(validation_panel); | ||||||
| 	vb->add_child(status_panel); | 	validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Folder name is valid.")); | ||||||
|  | 	validation_panel->set_update_callback(callable_mp(this, &DirectoryCreateDialog::_on_dir_path_changed)); | ||||||
|  | 	validation_panel->set_accept_button(get_ok_button()); | ||||||
| 
 | 
 | ||||||
| 	status_label = memnew(Label); | 	dir_path->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); | ||||||
| 	status_label->set_v_size_flags(Control::SIZE_EXPAND_FILL); |  | ||||||
| 	status_panel->add_child(status_label); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -33,9 +33,9 @@ | ||||||
| 
 | 
 | ||||||
| #include "scene/gui/dialogs.h" | #include "scene/gui/dialogs.h" | ||||||
| 
 | 
 | ||||||
|  | class EditorValidationPanel; | ||||||
| class Label; | class Label; | ||||||
| class LineEdit; | class LineEdit; | ||||||
| class PanelContainer; |  | ||||||
| 
 | 
 | ||||||
| class DirectoryCreateDialog : public ConfirmationDialog { | class DirectoryCreateDialog : public ConfirmationDialog { | ||||||
| 	GDCLASS(DirectoryCreateDialog, ConfirmationDialog); | 	GDCLASS(DirectoryCreateDialog, ConfirmationDialog); | ||||||
|  | @ -44,17 +44,13 @@ class DirectoryCreateDialog : public ConfirmationDialog { | ||||||
| 
 | 
 | ||||||
| 	Label *label = nullptr; | 	Label *label = nullptr; | ||||||
| 	LineEdit *dir_path = nullptr; | 	LineEdit *dir_path = nullptr; | ||||||
| 
 | 	EditorValidationPanel *validation_panel = nullptr; | ||||||
| 	PanelContainer *status_panel = nullptr; |  | ||||||
| 	Label *status_label = nullptr; |  | ||||||
| 
 | 
 | ||||||
| 	String _validate_path(const String &p_path) const; | 	String _validate_path(const String &p_path) const; | ||||||
| 
 | 	void _on_dir_path_changed(); | ||||||
| 	void _on_dir_path_changed(const String &p_text); |  | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
| 	static void _bind_methods(); | 	static void _bind_methods(); | ||||||
| 	void _notification(int p_what); |  | ||||||
| 
 | 
 | ||||||
| 	virtual void ok_pressed() override; | 	virtual void ok_pressed() override; | ||||||
| 	virtual void _post_popup() override; | 	virtual void _post_popup() override; | ||||||
|  |  | ||||||
|  | @ -38,6 +38,7 @@ | ||||||
| #include "editor/editor_scale.h" | #include "editor/editor_scale.h" | ||||||
| #include "editor/editor_settings.h" | #include "editor/editor_settings.h" | ||||||
| #include "editor/editor_undo_redo_manager.h" | #include "editor/editor_undo_redo_manager.h" | ||||||
|  | #include "editor/gui/editor_validation_panel.h" | ||||||
| #include "editor/inspector_dock.h" | #include "editor/inspector_dock.h" | ||||||
| #include "editor/plugins/script_editor_plugin.h" | #include "editor/plugins/script_editor_plugin.h" | ||||||
| #include "multi_node_edit.h" | #include "multi_node_edit.h" | ||||||
|  | @ -3927,12 +3928,6 @@ void EditorInspector::_notification(int p_what) { | ||||||
| 			} | 			} | ||||||
| 		} break; | 		} break; | ||||||
| 
 | 
 | ||||||
| 		case NOTIFICATION_THEME_CHANGED: { |  | ||||||
| 			if (add_meta_error_panel) { |  | ||||||
| 				add_meta_error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); |  | ||||||
| 			} |  | ||||||
| 		} break; |  | ||||||
| 
 |  | ||||||
| 		case NOTIFICATION_PREDELETE: { | 		case NOTIFICATION_PREDELETE: { | ||||||
| 			edit(nullptr); //just in case
 | 			edit(nullptr); //just in case
 | ||||||
| 		} break; | 		} break; | ||||||
|  | @ -4083,27 +4078,17 @@ void EditorInspector::_add_meta_confirm() { | ||||||
| 	undo_redo->commit_action(); | 	undo_redo->commit_action(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EditorInspector::_check_meta_name(const String &p_name) { | void EditorInspector::_check_meta_name() { | ||||||
| 	String error; | 	const String meta_name = add_meta_name->get_text(); | ||||||
| 
 | 
 | ||||||
| 	if (p_name == "") { | 	if (meta_name.is_empty()) { | ||||||
| 		error = TTR("Metadata name can't be empty."); | 		validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name can't be empty."), EditorValidationPanel::MSG_ERROR); | ||||||
| 	} else if (!p_name.is_valid_identifier()) { | 	} else if (!meta_name.is_valid_identifier()) { | ||||||
| 		error = TTR("Metadata name must be a valid identifier."); | 		validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name must be a valid identifier."), EditorValidationPanel::MSG_ERROR); | ||||||
| 	} else if (object->has_meta(p_name)) { | 	} else if (object->has_meta(meta_name)) { | ||||||
| 		error = vformat(TTR("Metadata with name \"%s\" already exists."), p_name); | 		validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, vformat(TTR("Metadata with name \"%s\" already exists."), meta_name), EditorValidationPanel::MSG_ERROR); | ||||||
| 	} else if (p_name[0] == '_') { | 	} else if (meta_name[0] == '_') { | ||||||
| 		error = TTR("Names starting with _ are reserved for editor-only metadata."); | 		validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Names starting with _ are reserved for editor-only metadata."), EditorValidationPanel::MSG_ERROR); | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (error != "") { |  | ||||||
| 		add_meta_error->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); |  | ||||||
| 		add_meta_error->set_text(error); |  | ||||||
| 		add_meta_dialog->get_ok_button()->set_disabled(true); |  | ||||||
| 	} else { |  | ||||||
| 		add_meta_error->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); |  | ||||||
| 		add_meta_error->set_text(TTR("Metadata name is valid.")); |  | ||||||
| 		add_meta_dialog->get_ok_button()->set_disabled(false); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -4143,16 +4128,13 @@ void EditorInspector::_show_add_meta_dialog() { | ||||||
| 		add_meta_dialog->register_text_enter(add_meta_name); | 		add_meta_dialog->register_text_enter(add_meta_name); | ||||||
| 		add_meta_dialog->connect("confirmed", callable_mp(this, &EditorInspector::_add_meta_confirm)); | 		add_meta_dialog->connect("confirmed", callable_mp(this, &EditorInspector::_add_meta_confirm)); | ||||||
| 
 | 
 | ||||||
| 		add_meta_error_panel = memnew(PanelContainer); | 		validation_panel = memnew(EditorValidationPanel); | ||||||
| 		vbc->add_child(add_meta_error_panel); | 		vbc->add_child(validation_panel); | ||||||
| 		if (is_inside_tree()) { | 		validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name is valid.")); | ||||||
| 			add_meta_error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); | 		validation_panel->set_update_callback(callable_mp(this, &EditorInspector::_check_meta_name)); | ||||||
| 		} | 		validation_panel->set_accept_button(add_meta_dialog->get_ok_button()); | ||||||
| 
 | 
 | ||||||
| 		add_meta_error = memnew(Label); | 		add_meta_name->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); | ||||||
| 		add_meta_error_panel->add_child(add_meta_error); |  | ||||||
| 
 |  | ||||||
| 		add_meta_name->connect("text_changed", callable_mp(this, &EditorInspector::_check_meta_name)); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	Node *node = Object::cast_to<Node>(object); | 	Node *node = Object::cast_to<Node>(object); | ||||||
|  | @ -4164,9 +4146,9 @@ void EditorInspector::_show_add_meta_dialog() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	add_meta_dialog->popup_centered(); | 	add_meta_dialog->popup_centered(); | ||||||
| 	add_meta_name->set_text(""); |  | ||||||
| 	_check_meta_name(""); |  | ||||||
| 	add_meta_name->grab_focus(); | 	add_meta_name->grab_focus(); | ||||||
|  | 	add_meta_name->set_text(""); | ||||||
|  | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EditorInspector::_bind_methods() { | void EditorInspector::_bind_methods() { | ||||||
|  |  | ||||||
|  | @ -39,6 +39,7 @@ class AcceptDialog; | ||||||
| class Button; | class Button; | ||||||
| class ConfirmationDialog; | class ConfirmationDialog; | ||||||
| class EditorInspector; | class EditorInspector; | ||||||
|  | class EditorValidationPanel; | ||||||
| class LineEdit; | class LineEdit; | ||||||
| class OptionButton; | class OptionButton; | ||||||
| class PanelContainer; | class PanelContainer; | ||||||
|  | @ -543,12 +544,11 @@ class EditorInspector : public ScrollContainer { | ||||||
| 	ConfirmationDialog *add_meta_dialog = nullptr; | 	ConfirmationDialog *add_meta_dialog = nullptr; | ||||||
| 	LineEdit *add_meta_name = nullptr; | 	LineEdit *add_meta_name = nullptr; | ||||||
| 	OptionButton *add_meta_type = nullptr; | 	OptionButton *add_meta_type = nullptr; | ||||||
| 	PanelContainer *add_meta_error_panel = nullptr; | 	EditorValidationPanel *validation_panel = nullptr; | ||||||
| 	Label *add_meta_error = nullptr; |  | ||||||
| 
 | 
 | ||||||
| 	void _add_meta_confirm(); | 	void _add_meta_confirm(); | ||||||
| 	void _show_add_meta_dialog(); | 	void _show_add_meta_dialog(); | ||||||
| 	void _check_meta_name(const String &p_name); | 	void _check_meta_name(); | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
| 	static void _bind_methods(); | 	static void _bind_methods(); | ||||||
|  |  | ||||||
|  | @ -1280,6 +1280,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	theme->set_stylebox("panel", "Tree", style_tree_bg); | 	theme->set_stylebox("panel", "Tree", style_tree_bg); | ||||||
|  | 	theme->set_stylebox("panel", "EditorValidationPanel", style_tree_bg); | ||||||
| 
 | 
 | ||||||
| 	// Tree
 | 	// Tree
 | ||||||
| 	theme->set_icon("checked", "Tree", theme->get_icon(SNAME("GuiChecked"), SNAME("EditorIcons"))); | 	theme->set_icon("checked", "Tree", theme->get_icon(SNAME("GuiChecked"), SNAME("EditorIcons"))); | ||||||
|  |  | ||||||
							
								
								
									
										134
									
								
								editor/gui/editor_validation_panel.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								editor/gui/editor_validation_panel.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  editor_validation_panel.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 "editor_validation_panel.h" | ||||||
|  | 
 | ||||||
|  | #include "editor/editor_scale.h" | ||||||
|  | #include "scene/gui/box_container.h" | ||||||
|  | #include "scene/gui/button.h" | ||||||
|  | #include "scene/gui/label.h" | ||||||
|  | 
 | ||||||
|  | void EditorValidationPanel::_update() { | ||||||
|  | 	for (const KeyValue<int, String> &E : valid_messages) { | ||||||
|  | 		set_message(E.key, E.value, MSG_OK); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	valid = true; | ||||||
|  | 	update_callback.callv(Array()); | ||||||
|  | 
 | ||||||
|  | 	if (accept_button) { | ||||||
|  | 		accept_button->set_disabled(!valid); | ||||||
|  | 	} | ||||||
|  | 	pending_update = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditorValidationPanel::_notification(int p_what) { | ||||||
|  | 	switch (p_what) { | ||||||
|  | 		case NOTIFICATION_THEME_CHANGED: { | ||||||
|  | 			theme_cache.valid_color = get_theme_color(SNAME("success_color"), SNAME("Editor")); | ||||||
|  | 			theme_cache.warning_color = get_theme_color(SNAME("warning_color"), SNAME("Editor")); | ||||||
|  | 			theme_cache.error_color = get_theme_color(SNAME("error_color"), SNAME("Editor")); | ||||||
|  | 		} break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditorValidationPanel::add_line(int p_id, const String &p_valid_message) { | ||||||
|  | 	ERR_FAIL_COND(valid_messages.has(p_id)); | ||||||
|  | 
 | ||||||
|  | 	Label *label = memnew(Label); | ||||||
|  | 	message_container->add_child(label); | ||||||
|  | 	label->set_custom_minimum_size(Size2(200 * EDSCALE, 0)); | ||||||
|  | 	label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); | ||||||
|  | 
 | ||||||
|  | 	valid_messages[p_id] = p_valid_message; | ||||||
|  | 	labels[p_id] = label; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditorValidationPanel::set_accept_button(Button *p_button) { | ||||||
|  | 	accept_button = p_button; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditorValidationPanel::set_update_callback(const Callable &p_callback) { | ||||||
|  | 	update_callback = p_callback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditorValidationPanel::update() { | ||||||
|  | 	ERR_FAIL_COND(update_callback.is_null()); | ||||||
|  | 
 | ||||||
|  | 	if (pending_update) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	pending_update = true; | ||||||
|  | 	callable_mp(this, &EditorValidationPanel::_update).call_deferred(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditorValidationPanel::set_message(int p_id, const String &p_text, MessageType p_type, bool p_auto_prefix) { | ||||||
|  | 	ERR_FAIL_COND(!valid_messages.has(p_id)); | ||||||
|  | 
 | ||||||
|  | 	Label *label = labels[p_id]; | ||||||
|  | 	if (p_text.is_empty()) { | ||||||
|  | 		label->hide(); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	label->show(); | ||||||
|  | 
 | ||||||
|  | 	if (p_auto_prefix) { | ||||||
|  | 		label->set_text(String(U"•  ") + p_text); | ||||||
|  | 	} else { | ||||||
|  | 		label->set_text(p_text); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	switch (p_type) { | ||||||
|  | 		case MSG_OK: | ||||||
|  | 			label->add_theme_color_override(SNAME("font_color"), theme_cache.valid_color); | ||||||
|  | 			break; | ||||||
|  | 		case MSG_WARNING: | ||||||
|  | 			label->add_theme_color_override(SNAME("font_color"), theme_cache.warning_color); | ||||||
|  | 			break; | ||||||
|  | 		case MSG_ERROR: | ||||||
|  | 			label->add_theme_color_override(SNAME("font_color"), theme_cache.error_color); | ||||||
|  | 			valid = false; | ||||||
|  | 			break; | ||||||
|  | 		case MSG_INFO: | ||||||
|  | 			label->remove_theme_color_override(SNAME("font_color")); | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool EditorValidationPanel::is_valid() const { | ||||||
|  | 	return valid; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | EditorValidationPanel::EditorValidationPanel() { | ||||||
|  | 	set_v_size_flags(SIZE_EXPAND_FILL); | ||||||
|  | 
 | ||||||
|  | 	message_container = memnew(VBoxContainer); | ||||||
|  | 	add_child(message_container); | ||||||
|  | } | ||||||
							
								
								
									
										88
									
								
								editor/gui/editor_validation_panel.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								editor/gui/editor_validation_panel.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,88 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  editor_validation_panel.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 EDITOR_VALIDATION_PANEL_H | ||||||
|  | #define EDITOR_VALIDATION_PANEL_H | ||||||
|  | 
 | ||||||
|  | #include "scene/gui/panel_container.h" | ||||||
|  | 
 | ||||||
|  | class Button; | ||||||
|  | class Label; | ||||||
|  | class VBoxContainer; | ||||||
|  | 
 | ||||||
|  | class EditorValidationPanel : public PanelContainer { | ||||||
|  | 	GDCLASS(EditorValidationPanel, PanelContainer); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	enum MessageType { | ||||||
|  | 		MSG_OK, | ||||||
|  | 		MSG_WARNING, | ||||||
|  | 		MSG_ERROR, | ||||||
|  | 		MSG_INFO, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	static const int MSG_ID_DEFAULT = 0; // Avoids hard-coding ID in dialogs with single-line validation.
 | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	VBoxContainer *message_container = nullptr; | ||||||
|  | 
 | ||||||
|  | 	HashMap<int, String> valid_messages; | ||||||
|  | 	HashMap<int, Label *> labels; | ||||||
|  | 
 | ||||||
|  | 	bool valid = false; | ||||||
|  | 	bool pending_update = false; | ||||||
|  | 
 | ||||||
|  | 	struct ThemeCache { | ||||||
|  | 		Color valid_color; | ||||||
|  | 		Color warning_color; | ||||||
|  | 		Color error_color; | ||||||
|  | 	} theme_cache; | ||||||
|  | 
 | ||||||
|  | 	void _update(); | ||||||
|  | 
 | ||||||
|  | 	Callable update_callback; | ||||||
|  | 	Button *accept_button = nullptr; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	void _notification(int p_what); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void add_line(int p_id, const String &p_valid_message = ""); | ||||||
|  | 	void set_accept_button(Button *p_button); | ||||||
|  | 	void set_update_callback(const Callable &p_callback); | ||||||
|  | 
 | ||||||
|  | 	void update(); | ||||||
|  | 	void set_message(int p_id, const String &p_text, MessageType p_type, bool p_auto_prefix = true); | ||||||
|  | 	bool is_valid() const; | ||||||
|  | 
 | ||||||
|  | 	EditorValidationPanel(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif // EDITOR_VALIDATION_PANEL_H
 | ||||||
|  | @ -34,6 +34,7 @@ | ||||||
| #include "editor/create_dialog.h" | #include "editor/create_dialog.h" | ||||||
| #include "editor/editor_node.h" | #include "editor/editor_node.h" | ||||||
| #include "editor/editor_scale.h" | #include "editor/editor_scale.h" | ||||||
|  | #include "editor/gui/editor_validation_panel.h" | ||||||
| #include "scene/2d/node_2d.h" | #include "scene/2d/node_2d.h" | ||||||
| #include "scene/3d/node_3d.h" | #include "scene/3d/node_3d.h" | ||||||
| #include "scene/gui/box_container.h" | #include "scene/gui/box_container.h" | ||||||
|  | @ -41,7 +42,6 @@ | ||||||
| #include "scene/gui/grid_container.h" | #include "scene/gui/grid_container.h" | ||||||
| #include "scene/gui/line_edit.h" | #include "scene/gui/line_edit.h" | ||||||
| #include "scene/gui/option_button.h" | #include "scene/gui/option_button.h" | ||||||
| #include "scene/gui/panel_container.h" |  | ||||||
| #include "scene/resources/packed_scene.h" | #include "scene/resources/packed_scene.h" | ||||||
| 
 | 
 | ||||||
| void SceneCreateDialog::_notification(int p_what) { | void SceneCreateDialog::_notification(int p_what) { | ||||||
|  | @ -53,7 +53,6 @@ void SceneCreateDialog::_notification(int p_what) { | ||||||
| 			node_type_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons"))); | 			node_type_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons"))); | ||||||
| 			node_type_gui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons"))); | 			node_type_gui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons"))); | ||||||
| 			node_type_other->add_theme_icon_override(SNAME("icon"), get_theme_icon(SNAME("Node"), SNAME("EditorIcons"))); | 			node_type_other->add_theme_icon_override(SNAME("icon"), get_theme_icon(SNAME("Node"), SNAME("EditorIcons"))); | ||||||
| 			status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); |  | ||||||
| 		} break; | 		} break; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -63,7 +62,7 @@ void SceneCreateDialog::config(const String &p_dir) { | ||||||
| 	root_name_edit->set_text(""); | 	root_name_edit->set_text(""); | ||||||
| 	scene_name_edit->set_text(""); | 	scene_name_edit->set_text(""); | ||||||
| 	scene_name_edit->call_deferred(SNAME("grab_focus")); | 	scene_name_edit->call_deferred(SNAME("grab_focus")); | ||||||
| 	update_dialog(); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SceneCreateDialog::accept_create() { | void SceneCreateDialog::accept_create() { | ||||||
|  | @ -82,40 +81,35 @@ void SceneCreateDialog::browse_types() { | ||||||
| void SceneCreateDialog::on_type_picked() { | void SceneCreateDialog::on_type_picked() { | ||||||
| 	other_type_display->set_text(select_node_dialog->get_selected_type().get_slice(" ", 0)); | 	other_type_display->set_text(select_node_dialog->get_selected_type().get_slice(" ", 0)); | ||||||
| 	if (node_type_other->is_pressed()) { | 	if (node_type_other->is_pressed()) { | ||||||
| 		update_dialog(); | 		validation_panel->update(); | ||||||
| 	} else { | 	} else { | ||||||
| 		node_type_other->set_pressed(true); // Calls update_dialog() via group.
 | 		node_type_other->set_pressed(true); // Calls validation_panel->update() via group.
 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SceneCreateDialog::update_dialog() { | void SceneCreateDialog::update_dialog() { | ||||||
| 	scene_name = scene_name_edit->get_text().strip_edges(); | 	scene_name = scene_name_edit->get_text().strip_edges(); | ||||||
| 	update_error(file_error_label, MSG_OK, TTR("Scene name is valid.")); |  | ||||||
| 
 | 
 | ||||||
| 	bool is_valid = true; |  | ||||||
| 	if (scene_name.is_empty()) { | 	if (scene_name.is_empty()) { | ||||||
| 		update_error(file_error_label, MSG_ERROR, TTR("Scene name is empty.")); | 		validation_panel->set_message(MSG_ID_PATH, TTR("Scene name is empty."), EditorValidationPanel::MSG_ERROR); | ||||||
| 		is_valid = false; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (is_valid) { | 	if (validation_panel->is_valid()) { | ||||||
| 		if (!scene_name.ends_with(".")) { | 		if (!scene_name.ends_with(".")) { | ||||||
| 			scene_name += "."; | 			scene_name += "."; | ||||||
| 		} | 		} | ||||||
| 		scene_name += scene_extension_picker->get_selected_metadata().operator String(); | 		scene_name += scene_extension_picker->get_selected_metadata().operator String(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (is_valid && !scene_name.is_valid_filename()) { | 	if (validation_panel->is_valid() && !scene_name.is_valid_filename()) { | ||||||
| 		update_error(file_error_label, MSG_ERROR, TTR("File name invalid.")); | 		validation_panel->set_message(MSG_ID_PATH, TTR("File name invalid."), EditorValidationPanel::MSG_ERROR); | ||||||
| 		is_valid = false; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (is_valid) { | 	if (validation_panel->is_valid()) { | ||||||
| 		scene_name = directory.path_join(scene_name); | 		scene_name = directory.path_join(scene_name); | ||||||
| 		Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); | 		Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); | ||||||
| 		if (da->file_exists(scene_name)) { | 		if (da->file_exists(scene_name)) { | ||||||
| 			update_error(file_error_label, MSG_ERROR, TTR("File already exists.")); | 			validation_panel->set_message(MSG_ID_PATH, TTR("File already exists."), EditorValidationPanel::MSG_ERROR); | ||||||
| 			is_valid = false; |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -126,8 +120,6 @@ void SceneCreateDialog::update_dialog() { | ||||||
| 		node_type_other->set_icon(nullptr); | 		node_type_other->set_icon(nullptr); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	update_error(node_error_label, MSG_OK, TTR("Root node valid.")); |  | ||||||
| 
 |  | ||||||
| 	root_name = root_name_edit->get_text().strip_edges(); | 	root_name = root_name_edit->get_text().strip_edges(); | ||||||
| 	if (root_name.is_empty()) { | 	if (root_name.is_empty()) { | ||||||
| 		root_name = scene_name_edit->get_text().strip_edges(); | 		root_name = scene_name_edit->get_text().strip_edges(); | ||||||
|  | @ -135,39 +127,16 @@ void SceneCreateDialog::update_dialog() { | ||||||
| 		if (root_name.is_empty()) { | 		if (root_name.is_empty()) { | ||||||
| 			root_name_edit->set_placeholder(TTR("Leave empty to derive from scene name")); | 			root_name_edit->set_placeholder(TTR("Leave empty to derive from scene name")); | ||||||
| 		} else { | 		} else { | ||||||
| 			// Respect the desired root node casing from ProjectSettings and ensure it's a valid node name.
 | 			// Respect the desired root node casing from ProjectSettings.
 | ||||||
| 			String adjusted_root_name = Node::adjust_name_casing(root_name); | 			root_name = Node::adjust_name_casing(root_name); | ||||||
| 			root_name = adjusted_root_name.validate_node_name(); | 			root_name_edit->set_placeholder(root_name.validate_node_name()); | ||||||
| 
 |  | ||||||
| 			bool has_invalid_characters = root_name != adjusted_root_name; |  | ||||||
| 			if (has_invalid_characters) { |  | ||||||
| 				update_error(node_error_label, MSG_WARNING, TTR("Invalid root node name characters have been replaced.")); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			root_name_edit->set_placeholder(root_name); |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (root_name.is_empty() || root_name.validate_node_name() != root_name) { | 	if (root_name.is_empty()) { | ||||||
| 		update_error(node_error_label, MSG_ERROR, TTR("Invalid root node name.")); | 		validation_panel->set_message(MSG_ID_ROOT, TTR("Invalid root node name."), EditorValidationPanel::MSG_ERROR); | ||||||
| 		is_valid = false; | 	} else if (root_name != root_name.validate_node_name()) { | ||||||
| 	} | 		validation_panel->set_message(MSG_ID_ROOT, TTR("Invalid root node name characters have been replaced."), EditorValidationPanel::MSG_WARNING); | ||||||
| 
 |  | ||||||
| 	get_ok_button()->set_disabled(!is_valid); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SceneCreateDialog::update_error(Label *p_label, MsgType p_type, const String &p_msg) { |  | ||||||
| 	p_label->set_text(String::utf8("•  ") + p_msg); |  | ||||||
| 	switch (p_type) { |  | ||||||
| 		case MSG_OK: |  | ||||||
| 			p_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); |  | ||||||
| 			break; |  | ||||||
| 		case MSG_ERROR: |  | ||||||
| 			p_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); |  | ||||||
| 			break; |  | ||||||
| 		case MSG_WARNING: |  | ||||||
| 			p_label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); |  | ||||||
| 			break; |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -268,8 +237,6 @@ SceneCreateDialog::SceneCreateDialog() { | ||||||
| 		select_node_button = memnew(Button); | 		select_node_button = memnew(Button); | ||||||
| 		hb->add_child(select_node_button); | 		hb->add_child(select_node_button); | ||||||
| 		select_node_button->connect("pressed", callable_mp(this, &SceneCreateDialog::browse_types)); | 		select_node_button->connect("pressed", callable_mp(this, &SceneCreateDialog::browse_types)); | ||||||
| 
 |  | ||||||
| 		node_type_group->connect("pressed", callable_mp(this, &SceneCreateDialog::update_dialog).unbind(1)); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	{ | 	{ | ||||||
|  | @ -282,7 +249,6 @@ SceneCreateDialog::SceneCreateDialog() { | ||||||
| 		scene_name_edit = memnew(LineEdit); | 		scene_name_edit = memnew(LineEdit); | ||||||
| 		hb->add_child(scene_name_edit); | 		hb->add_child(scene_name_edit); | ||||||
| 		scene_name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); | 		scene_name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); | ||||||
| 		scene_name_edit->connect("text_changed", callable_mp(this, &SceneCreateDialog::update_dialog).unbind(1)); |  | ||||||
| 		scene_name_edit->connect("text_submitted", callable_mp(this, &SceneCreateDialog::accept_create).unbind(1)); | 		scene_name_edit->connect("text_submitted", callable_mp(this, &SceneCreateDialog::accept_create).unbind(1)); | ||||||
| 
 | 
 | ||||||
| 		List<String> extensions; | 		List<String> extensions; | ||||||
|  | @ -305,7 +271,6 @@ SceneCreateDialog::SceneCreateDialog() { | ||||||
| 		gc->add_child(root_name_edit); | 		gc->add_child(root_name_edit); | ||||||
| 		root_name_edit->set_tooltip_text(TTR("When empty, the root node name is derived from the scene name based on the \"editor/naming/node_name_casing\" project setting.")); | 		root_name_edit->set_tooltip_text(TTR("When empty, the root node name is derived from the scene name based on the \"editor/naming/node_name_casing\" project setting.")); | ||||||
| 		root_name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); | 		root_name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); | ||||||
| 		root_name_edit->connect("text_changed", callable_mp(this, &SceneCreateDialog::update_dialog).unbind(1)); |  | ||||||
| 		root_name_edit->connect("text_submitted", callable_mp(this, &SceneCreateDialog::accept_create).unbind(1)); | 		root_name_edit->connect("text_submitted", callable_mp(this, &SceneCreateDialog::accept_create).unbind(1)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -313,19 +278,16 @@ SceneCreateDialog::SceneCreateDialog() { | ||||||
| 	main_vb->add_child(spacing); | 	main_vb->add_child(spacing); | ||||||
| 	spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); | 	spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); | ||||||
| 
 | 
 | ||||||
| 	status_panel = memnew(PanelContainer); | 	validation_panel = memnew(EditorValidationPanel); | ||||||
| 	main_vb->add_child(status_panel); | 	main_vb->add_child(validation_panel); | ||||||
| 	status_panel->set_h_size_flags(Control::SIZE_FILL); | 	validation_panel->add_line(MSG_ID_PATH, TTR("Scene name is valid.")); | ||||||
| 	status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL); | 	validation_panel->add_line(MSG_ID_ROOT, TTR("Root node valid.")); | ||||||
|  | 	validation_panel->set_update_callback(callable_mp(this, &SceneCreateDialog::update_dialog)); | ||||||
|  | 	validation_panel->set_accept_button(get_ok_button()); | ||||||
| 
 | 
 | ||||||
| 	VBoxContainer *status_vb = memnew(VBoxContainer); | 	node_type_group->connect("pressed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); | ||||||
| 	status_panel->add_child(status_vb); | 	scene_name_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); | ||||||
| 
 | 	root_name_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); | ||||||
| 	file_error_label = memnew(Label); |  | ||||||
| 	status_vb->add_child(file_error_label); |  | ||||||
| 
 |  | ||||||
| 	node_error_label = memnew(Label); |  | ||||||
| 	status_vb->add_child(node_error_label); |  | ||||||
| 
 | 
 | ||||||
| 	set_title(TTR("Create New Scene")); | 	set_title(TTR("Create New Scene")); | ||||||
| 	set_min_size(Size2i(400 * EDSCALE, 0)); | 	set_min_size(Size2i(400 * EDSCALE, 0)); | ||||||
|  |  | ||||||
|  | @ -37,18 +37,17 @@ class ButtonGroup; | ||||||
| class CheckBox; | class CheckBox; | ||||||
| class CreateDialog; | class CreateDialog; | ||||||
| class EditorFileDialog; | class EditorFileDialog; | ||||||
|  | class EditorValidationPanel; | ||||||
| class Label; | class Label; | ||||||
| class LineEdit; | class LineEdit; | ||||||
| class OptionButton; | class OptionButton; | ||||||
| class PanelContainer; |  | ||||||
| 
 | 
 | ||||||
| class SceneCreateDialog : public ConfirmationDialog { | class SceneCreateDialog : public ConfirmationDialog { | ||||||
| 	GDCLASS(SceneCreateDialog, ConfirmationDialog); | 	GDCLASS(SceneCreateDialog, ConfirmationDialog); | ||||||
| 
 | 
 | ||||||
| 	enum MsgType { | 	enum { | ||||||
| 		MSG_OK, | 		MSG_ID_PATH, | ||||||
| 		MSG_ERROR, | 		MSG_ID_ROOT, | ||||||
| 		MSG_WARNING, |  | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const StringName type_meta = StringName("type"); | 	const StringName type_meta = StringName("type"); | ||||||
|  | @ -80,15 +79,12 @@ private: | ||||||
| 	OptionButton *scene_extension_picker = nullptr; | 	OptionButton *scene_extension_picker = nullptr; | ||||||
| 	LineEdit *root_name_edit = nullptr; | 	LineEdit *root_name_edit = nullptr; | ||||||
| 
 | 
 | ||||||
| 	PanelContainer *status_panel = nullptr; | 	EditorValidationPanel *validation_panel = nullptr; | ||||||
| 	Label *file_error_label = nullptr; |  | ||||||
| 	Label *node_error_label = nullptr; |  | ||||||
| 
 | 
 | ||||||
| 	void accept_create(); | 	void accept_create(); | ||||||
| 	void browse_types(); | 	void browse_types(); | ||||||
| 	void on_type_picked(); | 	void on_type_picked(); | ||||||
| 	void update_dialog(); | 	void update_dialog(); | ||||||
| 	void update_error(Label *p_label, MsgType p_type, const String &p_msg); |  | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
| 	void _notification(int p_what); | 	void _notification(int p_what); | ||||||
|  |  | ||||||
|  | @ -41,6 +41,7 @@ | ||||||
| #include "editor/editor_scale.h" | #include "editor/editor_scale.h" | ||||||
| #include "editor/editor_settings.h" | #include "editor/editor_settings.h" | ||||||
| #include "editor/gui/editor_file_dialog.h" | #include "editor/gui/editor_file_dialog.h" | ||||||
|  | #include "editor/gui/editor_validation_panel.h" | ||||||
| 
 | 
 | ||||||
| static String _get_parent_class_of_script(String p_path) { | static String _get_parent_class_of_script(String p_path) { | ||||||
| 	if (!ResourceLoader::exists(p_path, "Script")) { | 	if (!ResourceLoader::exists(p_path, "Script")) { | ||||||
|  | @ -136,7 +137,6 @@ void ScriptCreateDialog::_notification(int p_what) { | ||||||
| 			path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); | 			path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); | ||||||
| 			parent_browse_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); | 			parent_browse_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); | ||||||
| 			parent_search_button->set_icon(get_theme_icon(SNAME("ClassList"), SNAME("EditorIcons"))); | 			parent_search_button->set_icon(get_theme_icon(SNAME("ClassList"), SNAME("EditorIcons"))); | ||||||
| 			status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); |  | ||||||
| 		} break; | 		} break; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -295,13 +295,7 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Let ScriptLanguage do custom validation.
 | 	// Let ScriptLanguage do custom validation.
 | ||||||
| 	String path_error = ScriptServer::get_language(language_menu->get_selected())->validate_path(p); | 	return ScriptServer::get_language(language_menu->get_selected())->validate_path(p); | ||||||
| 	if (!path_error.is_empty()) { |  | ||||||
| 		return path_error; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// All checks passed.
 |  | ||||||
| 	return ""; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| String ScriptCreateDialog::_get_class_name() const { | String ScriptCreateDialog::_get_class_name() const { | ||||||
|  | @ -314,12 +308,12 @@ String ScriptCreateDialog::_get_class_name() const { | ||||||
| 
 | 
 | ||||||
| void ScriptCreateDialog::_class_name_changed(const String &p_name) { | void ScriptCreateDialog::_class_name_changed(const String &p_name) { | ||||||
| 	is_class_name_valid = _validate_class(class_name->get_text()); | 	is_class_name_valid = _validate_class(class_name->get_text()); | ||||||
| 	_update_dialog(); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ScriptCreateDialog::_parent_name_changed(const String &p_parent) { | void ScriptCreateDialog::_parent_name_changed(const String &p_parent) { | ||||||
| 	is_parent_name_valid = _validate_parent(parent_name->get_text()); | 	is_parent_name_valid = _validate_parent(parent_name->get_text()); | ||||||
| 	_update_dialog(); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ScriptCreateDialog::_template_changed(int p_template) { | void ScriptCreateDialog::_template_changed(int p_template) { | ||||||
|  | @ -347,6 +341,7 @@ void ScriptCreateDialog::_template_changed(int p_template) { | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	// Update template label information.
 | 	// Update template label information.
 | ||||||
| 	String template_info = U"•  "; | 	String template_info = U"•  "; | ||||||
| 	template_info += TTR("Template:"); | 	template_info += TTR("Template:"); | ||||||
|  | @ -354,8 +349,7 @@ void ScriptCreateDialog::_template_changed(int p_template) { | ||||||
| 	if (!sinfo.description.is_empty()) { | 	if (!sinfo.description.is_empty()) { | ||||||
| 		template_info += " - " + sinfo.description; | 		template_info += " - " + sinfo.description; | ||||||
| 	} | 	} | ||||||
| 	template_info_label->set_text(template_info); | 	validation_panel->set_message(MSG_ID_TEMPLATE, template_info, EditorValidationPanel::MSG_INFO, false); | ||||||
| 	template_info_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ScriptCreateDialog::ok_pressed() { | void ScriptCreateDialog::ok_pressed() { | ||||||
|  | @ -367,7 +361,7 @@ void ScriptCreateDialog::ok_pressed() { | ||||||
| 
 | 
 | ||||||
| 	EditorSettings::get_singleton()->save(); | 	EditorSettings::get_singleton()->save(); | ||||||
| 	is_new_script_created = true; | 	is_new_script_created = true; | ||||||
| 	_update_dialog(); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ScriptCreateDialog::_create_new() { | void ScriptCreateDialog::_create_new() { | ||||||
|  | @ -471,7 +465,7 @@ void ScriptCreateDialog::_language_changed(int l) { | ||||||
| 	EditorSettings::get_singleton()->set_project_metadata("script_setup", "last_selected_language", language_menu->get_item_text(language_menu->get_selected())); | 	EditorSettings::get_singleton()->set_project_metadata("script_setup", "last_selected_language", language_menu->get_item_text(language_menu->get_selected())); | ||||||
| 
 | 
 | ||||||
| 	_parent_name_changed(parent_name->get_text()); | 	_parent_name_changed(parent_name->get_text()); | ||||||
| 	_update_dialog(); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ScriptCreateDialog::_built_in_pressed() { | void ScriptCreateDialog::_built_in_pressed() { | ||||||
|  | @ -482,13 +476,13 @@ void ScriptCreateDialog::_built_in_pressed() { | ||||||
| 		is_built_in = false; | 		is_built_in = false; | ||||||
| 		_path_changed(file_path->get_text()); | 		_path_changed(file_path->get_text()); | ||||||
| 	} | 	} | ||||||
| 	_update_dialog(); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ScriptCreateDialog::_use_template_pressed() { | void ScriptCreateDialog::_use_template_pressed() { | ||||||
| 	is_using_templates = use_templates->is_pressed(); | 	is_using_templates = use_templates->is_pressed(); | ||||||
| 	EditorSettings::get_singleton()->set_meta("script_setup_use_script_templates", is_using_templates); | 	EditorSettings::get_singleton()->set_meta("script_setup_use_script_templates", is_using_templates); | ||||||
| 	_update_dialog(); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ScriptCreateDialog::_browse_path(bool browse_parent, bool p_save) { | void ScriptCreateDialog::_browse_path(bool browse_parent, bool p_save) { | ||||||
|  | @ -555,10 +549,9 @@ void ScriptCreateDialog::_path_changed(const String &p_path) { | ||||||
| 	is_path_valid = false; | 	is_path_valid = false; | ||||||
| 	is_new_script_created = true; | 	is_new_script_created = true; | ||||||
| 
 | 
 | ||||||
| 	String path_error = _validate_path(p_path, false); | 	path_error = _validate_path(p_path, false); | ||||||
| 	if (!path_error.is_empty()) { | 	if (!path_error.is_empty()) { | ||||||
| 		_msg_path_valid(false, path_error); | 		validation_panel->update(); | ||||||
| 		_update_dialog(); |  | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -567,33 +560,16 @@ void ScriptCreateDialog::_path_changed(const String &p_path) { | ||||||
| 	String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges()); | 	String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges()); | ||||||
| 	if (da->file_exists(p)) { | 	if (da->file_exists(p)) { | ||||||
| 		is_new_script_created = false; | 		is_new_script_created = false; | ||||||
| 		_msg_path_valid(true, TTR("File exists, it will be reused.")); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	is_path_valid = true; | 	is_path_valid = true; | ||||||
| 	_update_dialog(); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ScriptCreateDialog::_path_submitted(const String &p_path) { | void ScriptCreateDialog::_path_submitted(const String &p_path) { | ||||||
|  | 	if (!get_ok_button()->is_disabled()) { | ||||||
| 		ok_pressed(); | 		ok_pressed(); | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| void ScriptCreateDialog::_msg_script_valid(bool valid, const String &p_msg) { |  | ||||||
| 	error_label->set_text(String::utf8("•  ") + p_msg); |  | ||||||
| 	if (valid) { |  | ||||||
| 		error_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); |  | ||||||
| 	} else { |  | ||||||
| 		error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ScriptCreateDialog::_msg_path_valid(bool valid, const String &p_msg) { |  | ||||||
| 	path_error_label->set_text(String::utf8("•  ") + p_msg); |  | ||||||
| 	if (valid) { |  | ||||||
| 		path_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); |  | ||||||
| 	} else { |  | ||||||
| 		path_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ScriptCreateDialog::_update_template_menu() { | void ScriptCreateDialog::_update_template_menu() { | ||||||
|  | @ -688,25 +664,25 @@ void ScriptCreateDialog::_update_template_menu() { | ||||||
| void ScriptCreateDialog::_update_dialog() { | void ScriptCreateDialog::_update_dialog() { | ||||||
| 	// "Add Script Dialog" GUI logic and script checks.
 | 	// "Add Script Dialog" GUI logic and script checks.
 | ||||||
| 	_update_template_menu(); | 	_update_template_menu(); | ||||||
| 	bool script_ok = true; |  | ||||||
| 
 | 
 | ||||||
| 	// Is script path/name valid (order from top to bottom)?
 | 	// Is script path/name valid (order from top to bottom)?
 | ||||||
| 
 | 
 | ||||||
| 	if (!is_built_in && !is_path_valid) { | 	if (!is_built_in && !is_path_valid) { | ||||||
| 		_msg_script_valid(false, TTR("Invalid path.")); | 		validation_panel->set_message(MSG_ID_SCRIPT, TTR("Invalid path."), EditorValidationPanel::MSG_ERROR); | ||||||
| 		script_ok = false; |  | ||||||
| 	} | 	} | ||||||
| 	if (has_named_classes && (is_new_script_created && !is_class_name_valid)) { | 	if (has_named_classes && (is_new_script_created && !is_class_name_valid)) { | ||||||
| 		_msg_script_valid(false, TTR("Invalid class name.")); | 		validation_panel->set_message(MSG_ID_SCRIPT, TTR("Invalid class name."), EditorValidationPanel::MSG_ERROR); | ||||||
| 		script_ok = false; |  | ||||||
| 	} | 	} | ||||||
| 	if (!is_parent_name_valid && is_new_script_created) { | 	if (!is_parent_name_valid && is_new_script_created) { | ||||||
| 		_msg_script_valid(false, TTR("Invalid inherited parent name or path.")); | 		validation_panel->set_message(MSG_ID_SCRIPT, TTR("Invalid inherited parent name or path."), EditorValidationPanel::MSG_ERROR); | ||||||
| 		script_ok = false; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (script_ok) { | 	if (validation_panel->is_valid() && !is_new_script_created) { | ||||||
| 		_msg_script_valid(true, TTR("Script path/name is valid.")); | 		validation_panel->set_message(MSG_ID_SCRIPT, TTR("File exists, it will be reused."), EditorValidationPanel::MSG_OK); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!path_error.is_empty()) { | ||||||
|  | 		validation_panel->set_message(MSG_ID_PATH, path_error, EditorValidationPanel::MSG_ERROR); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Does script have named classes?
 | 	// Does script have named classes?
 | ||||||
|  | @ -752,7 +728,11 @@ void ScriptCreateDialog::_update_dialog() { | ||||||
| 
 | 
 | ||||||
| 	// Is Script created or loaded from existing file?
 | 	// Is Script created or loaded from existing file?
 | ||||||
| 
 | 
 | ||||||
| 	builtin_warning_label->set_visible(is_built_in); | 	if (is_built_in) { | ||||||
|  | 		validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Note: Built-in scripts have some limitations and can't be edited using an external editor."), EditorValidationPanel::MSG_INFO, false); | ||||||
|  | 	} else if (_get_class_name() == parent_name->get_text()) { | ||||||
|  | 		validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Warning: Having the script name be the same as a built-in type is usually not desired."), EditorValidationPanel::MSG_WARNING, false); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	path_controls[0]->set_visible(!is_built_in); | 	path_controls[0]->set_visible(!is_built_in); | ||||||
| 	path_controls[1]->set_visible(!is_built_in); | 	path_controls[1]->set_visible(!is_built_in); | ||||||
|  | @ -761,7 +741,6 @@ void ScriptCreateDialog::_update_dialog() { | ||||||
| 
 | 
 | ||||||
| 	// Check if the script name is the same as the parent class.
 | 	// Check if the script name is the same as the parent class.
 | ||||||
| 	// This warning isn't relevant if the script is built-in.
 | 	// This warning isn't relevant if the script is built-in.
 | ||||||
| 	script_name_warning_label->set_visible(!is_built_in && _get_class_name() == parent_name->get_text()); |  | ||||||
| 
 | 
 | ||||||
| 	bool is_new_file = is_built_in || is_new_script_created; | 	bool is_new_file = is_built_in || is_new_script_created; | ||||||
| 
 | 
 | ||||||
|  | @ -774,21 +753,16 @@ void ScriptCreateDialog::_update_dialog() { | ||||||
| 
 | 
 | ||||||
| 	if (is_new_file) { | 	if (is_new_file) { | ||||||
| 		if (is_built_in) { | 		if (is_built_in) { | ||||||
| 			_msg_path_valid(true, TTR("Built-in script (into scene file).")); | 			validation_panel->set_message(MSG_ID_PATH, TTR("Built-in script (into scene file)."), EditorValidationPanel::MSG_OK); | ||||||
| 		} |  | ||||||
| 		if (is_new_script_created && is_path_valid) { |  | ||||||
| 			_msg_path_valid(true, TTR("Will create a new script file.")); |  | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
|  | 		template_inactive_message = TTR("Using existing script file."); | ||||||
| 		if (load_enabled) { | 		if (load_enabled) { | ||||||
| 			template_inactive_message = TTR("Using existing script file."); |  | ||||||
| 			if (is_path_valid) { | 			if (is_path_valid) { | ||||||
| 				_msg_path_valid(true, TTR("Will load an existing script file.")); | 				validation_panel->set_message(MSG_ID_PATH, TTR("Will load an existing script file."), EditorValidationPanel::MSG_OK); | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			template_inactive_message = TTR("Using existing script file."); | 			validation_panel->set_message(MSG_ID_PATH, TTR("Script file already exists."), EditorValidationPanel::MSG_ERROR); | ||||||
| 			_msg_path_valid(false, TTR("Script file already exists.")); |  | ||||||
| 			script_ok = false; |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -806,18 +780,7 @@ void ScriptCreateDialog::_update_dialog() { | ||||||
| 		template_menu->set_disabled(true); | 		template_menu->set_disabled(true); | ||||||
| 		template_menu->clear(); | 		template_menu->clear(); | ||||||
| 		template_menu->add_item(template_inactive_message); | 		template_menu->add_item(template_inactive_message); | ||||||
| 	} | 		validation_panel->set_message(MSG_ID_TEMPLATE, "", EditorValidationPanel::MSG_INFO); | ||||||
| 	template_info_label->set_visible(!template_menu->is_disabled()); |  | ||||||
| 
 |  | ||||||
| 	get_ok_button()->set_disabled(!script_ok); |  | ||||||
| 
 |  | ||||||
| 	Callable entered_call = callable_mp(this, &ScriptCreateDialog::_path_submitted); |  | ||||||
| 	if (script_ok) { |  | ||||||
| 		if (!file_path->is_connected("text_submitted", entered_call)) { |  | ||||||
| 			file_path->connect("text_submitted", entered_call); |  | ||||||
| 		} |  | ||||||
| 	} else if (file_path->is_connected("text_submitted", entered_call)) { |  | ||||||
| 		file_path->disconnect("text_submitted", entered_call); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -967,47 +930,23 @@ ScriptCreateDialog::ScriptCreateDialog() { | ||||||
| 
 | 
 | ||||||
| 	/* Information Messages Field */ | 	/* Information Messages Field */ | ||||||
| 
 | 
 | ||||||
| 	VBoxContainer *vb = memnew(VBoxContainer); | 	validation_panel = memnew(EditorValidationPanel); | ||||||
| 
 | 	validation_panel->add_line(MSG_ID_SCRIPT, TTR("Script path/name is valid.")); | ||||||
| 	error_label = memnew(Label); | 	validation_panel->add_line(MSG_ID_PATH, TTR("Will create a new script file.")); | ||||||
| 	vb->add_child(error_label); | 	validation_panel->add_line(MSG_ID_BUILT_IN); | ||||||
| 
 | 	validation_panel->add_line(MSG_ID_TEMPLATE); | ||||||
| 	path_error_label = memnew(Label); | 	validation_panel->set_update_callback(callable_mp(this, &ScriptCreateDialog::_update_dialog)); | ||||||
| 	vb->add_child(path_error_label); | 	validation_panel->set_accept_button(get_ok_button()); | ||||||
| 
 |  | ||||||
| 	builtin_warning_label = memnew(Label); |  | ||||||
| 	builtin_warning_label->set_text( |  | ||||||
| 			TTR("Note: Built-in scripts have some limitations and can't be edited using an external editor.")); |  | ||||||
| 	vb->add_child(builtin_warning_label); |  | ||||||
| 	builtin_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); |  | ||||||
| 	builtin_warning_label->hide(); |  | ||||||
| 
 |  | ||||||
| 	script_name_warning_label = memnew(Label); |  | ||||||
| 	script_name_warning_label->set_text( |  | ||||||
| 			TTR("Warning: Having the script name be the same as a built-in type is usually not desired.")); |  | ||||||
| 	vb->add_child(script_name_warning_label); |  | ||||||
| 	script_name_warning_label->add_theme_color_override("font_color", Color(1, 0.85, 0.4)); |  | ||||||
| 	script_name_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); |  | ||||||
| 	script_name_warning_label->hide(); |  | ||||||
| 
 |  | ||||||
| 	template_info_label = memnew(Label); |  | ||||||
| 	vb->add_child(template_info_label); |  | ||||||
| 	template_info_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); |  | ||||||
| 
 |  | ||||||
| 	status_panel = memnew(PanelContainer); |  | ||||||
| 	status_panel->set_h_size_flags(Control::SIZE_FILL); |  | ||||||
| 	status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL); |  | ||||||
| 	status_panel->add_child(vb); |  | ||||||
| 
 | 
 | ||||||
| 	/* Spacing */ | 	/* Spacing */ | ||||||
| 
 | 
 | ||||||
| 	Control *spacing = memnew(Control); | 	Control *spacing = memnew(Control); | ||||||
| 	spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); | 	spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); | ||||||
| 
 | 
 | ||||||
| 	vb = memnew(VBoxContainer); | 	VBoxContainer *vb = memnew(VBoxContainer); | ||||||
| 	vb->add_child(gc); | 	vb->add_child(gc); | ||||||
| 	vb->add_child(spacing); | 	vb->add_child(spacing); | ||||||
| 	vb->add_child(status_panel); | 	vb->add_child(validation_panel); | ||||||
| 	add_child(vb); | 	add_child(vb); | ||||||
| 
 | 
 | ||||||
| 	/* Language */ | 	/* Language */ | ||||||
|  |  | ||||||
|  | @ -41,17 +41,20 @@ | ||||||
| 
 | 
 | ||||||
| class CreateDialog; | class CreateDialog; | ||||||
| class EditorFileDialog; | class EditorFileDialog; | ||||||
|  | class EditorValidationPanel; | ||||||
| 
 | 
 | ||||||
| class ScriptCreateDialog : public ConfirmationDialog { | class ScriptCreateDialog : public ConfirmationDialog { | ||||||
| 	GDCLASS(ScriptCreateDialog, ConfirmationDialog); | 	GDCLASS(ScriptCreateDialog, ConfirmationDialog); | ||||||
| 
 | 
 | ||||||
|  | 	enum { | ||||||
|  | 		MSG_ID_SCRIPT, | ||||||
|  | 		MSG_ID_PATH, | ||||||
|  | 		MSG_ID_BUILT_IN, | ||||||
|  | 		MSG_ID_TEMPLATE, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	LineEdit *class_name = nullptr; | 	LineEdit *class_name = nullptr; | ||||||
| 	Label *error_label = nullptr; | 	EditorValidationPanel *validation_panel = nullptr; | ||||||
| 	Label *path_error_label = nullptr; |  | ||||||
| 	Label *builtin_warning_label = nullptr; |  | ||||||
| 	Label *script_name_warning_label = nullptr; |  | ||||||
| 	Label *template_info_label = nullptr; |  | ||||||
| 	PanelContainer *status_panel = nullptr; |  | ||||||
| 	LineEdit *parent_name = nullptr; | 	LineEdit *parent_name = nullptr; | ||||||
| 	Button *parent_browse_button = nullptr; | 	Button *parent_browse_button = nullptr; | ||||||
| 	Button *parent_search_button = nullptr; | 	Button *parent_search_button = nullptr; | ||||||
|  | @ -67,6 +70,7 @@ class ScriptCreateDialog : public ConfirmationDialog { | ||||||
| 	AcceptDialog *alert = nullptr; | 	AcceptDialog *alert = nullptr; | ||||||
| 	CreateDialog *select_class = nullptr; | 	CreateDialog *select_class = nullptr; | ||||||
| 	bool is_browsing_parent = false; | 	bool is_browsing_parent = false; | ||||||
|  | 	String path_error; | ||||||
| 	String template_inactive_message; | 	String template_inactive_message; | ||||||
| 	String initial_bp; | 	String initial_bp; | ||||||
| 	bool is_new_script_created = true; | 	bool is_new_script_created = true; | ||||||
|  | @ -113,8 +117,6 @@ class ScriptCreateDialog : public ConfirmationDialog { | ||||||
| 	virtual void ok_pressed() override; | 	virtual void ok_pressed() override; | ||||||
| 	void _create_new(); | 	void _create_new(); | ||||||
| 	void _load_exist(); | 	void _load_exist(); | ||||||
| 	void _msg_script_valid(bool valid, const String &p_msg = String()); |  | ||||||
| 	void _msg_path_valid(bool valid, const String &p_msg = String()); |  | ||||||
| 	void _update_template_menu(); | 	void _update_template_menu(); | ||||||
| 	void _update_dialog(); | 	void _update_dialog(); | ||||||
| 	ScriptLanguage::ScriptTemplate _get_current_template() const; | 	ScriptLanguage::ScriptTemplate _get_current_template() const; | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ | ||||||
| #include "core/config/project_settings.h" | #include "core/config/project_settings.h" | ||||||
| #include "editor/editor_scale.h" | #include "editor/editor_scale.h" | ||||||
| #include "editor/gui/editor_file_dialog.h" | #include "editor/gui/editor_file_dialog.h" | ||||||
|  | #include "editor/gui/editor_validation_panel.h" | ||||||
| #include "scene/resources/shader_include.h" | #include "scene/resources/shader_include.h" | ||||||
| #include "scene/resources/visual_shader.h" | #include "scene/resources/visual_shader.h" | ||||||
| #include "servers/rendering/shader_types.h" | #include "servers/rendering/shader_types.h" | ||||||
|  | @ -89,7 +90,6 @@ void ShaderCreateDialog::_update_theme() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); | 	path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); | ||||||
| 	status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ShaderCreateDialog::_update_language_info() { | void ShaderCreateDialog::_update_language_info() { | ||||||
|  | @ -147,7 +147,7 @@ void ShaderCreateDialog::ok_pressed() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	is_new_shader_created = true; | 	is_new_shader_created = true; | ||||||
| 	_update_dialog(); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ShaderCreateDialog::_create_new() { | void ShaderCreateDialog::_create_new() { | ||||||
|  | @ -327,7 +327,7 @@ void ShaderCreateDialog::_type_changed(int p_language) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	EditorSettings::get_singleton()->set_project_metadata("shader_setup", "last_selected_language", type_menu->get_item_text(type_menu->get_selected())); | 	EditorSettings::get_singleton()->set_project_metadata("shader_setup", "last_selected_language", type_menu->get_item_text(type_menu->get_selected())); | ||||||
| 	_update_dialog(); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ShaderCreateDialog::_built_in_toggled(bool p_enabled) { | void ShaderCreateDialog::_built_in_toggled(bool p_enabled) { | ||||||
|  | @ -337,7 +337,7 @@ void ShaderCreateDialog::_built_in_toggled(bool p_enabled) { | ||||||
| 	} else { | 	} else { | ||||||
| 		_path_changed(file_path->get_text()); | 		_path_changed(file_path->get_text()); | ||||||
| 	} | 	} | ||||||
| 	_update_dialog(); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ShaderCreateDialog::_browse_path() { | void ShaderCreateDialog::_browse_path() { | ||||||
|  | @ -378,10 +378,9 @@ void ShaderCreateDialog::_path_changed(const String &p_path) { | ||||||
| 	is_path_valid = false; | 	is_path_valid = false; | ||||||
| 	is_new_shader_created = true; | 	is_new_shader_created = true; | ||||||
| 
 | 
 | ||||||
| 	String path_error = _validate_path(p_path); | 	path_error = _validate_path(p_path); | ||||||
| 	if (!path_error.is_empty()) { | 	if (!path_error.is_empty()) { | ||||||
| 		_msg_path_valid(false, path_error); | 		validation_panel->update(); | ||||||
| 		_update_dialog(); |  | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -389,16 +388,17 @@ void ShaderCreateDialog::_path_changed(const String &p_path) { | ||||||
| 	String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges()); | 	String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges()); | ||||||
| 	if (f->file_exists(p)) { | 	if (f->file_exists(p)) { | ||||||
| 		is_new_shader_created = false; | 		is_new_shader_created = false; | ||||||
| 		_msg_path_valid(true, TTR("File exists, it will be reused.")); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	is_path_valid = true; | 	is_path_valid = true; | ||||||
| 	_update_dialog(); | 	validation_panel->update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ShaderCreateDialog::_path_submitted(const String &p_path) { | void ShaderCreateDialog::_path_submitted(const String &p_path) { | ||||||
|  | 	if (!get_ok_button()->is_disabled()) { | ||||||
| 		ok_pressed(); | 		ok_pressed(); | ||||||
| 	} | 	} | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void ShaderCreateDialog::config(const String &p_base_path, bool p_built_in_enabled, bool p_load_enabled, int p_preferred_type, int p_preferred_mode) { | void ShaderCreateDialog::config(const String &p_base_path, bool p_built_in_enabled, bool p_load_enabled, int p_preferred_type, int p_preferred_mode) { | ||||||
| 	if (!p_base_path.is_empty()) { | 	if (!p_base_path.is_empty()) { | ||||||
|  | @ -490,33 +490,14 @@ String ShaderCreateDialog::_validate_path(const String &p_path) { | ||||||
| 	return ""; | 	return ""; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ShaderCreateDialog::_msg_script_valid(bool valid, const String &p_msg) { |  | ||||||
| 	error_label->set_text(String::utf8("•  ") + p_msg); |  | ||||||
| 	if (valid) { |  | ||||||
| 		error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("success_color"), SNAME("Editor"))); |  | ||||||
| 	} else { |  | ||||||
| 		error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("error_color"), SNAME("Editor"))); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ShaderCreateDialog::_msg_path_valid(bool valid, const String &p_msg) { |  | ||||||
| 	path_error_label->set_text(String::utf8("•  ") + p_msg); |  | ||||||
| 	if (valid) { |  | ||||||
| 		path_error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("success_color"), SNAME("Editor"))); |  | ||||||
| 	} else { |  | ||||||
| 		path_error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("error_color"), SNAME("Editor"))); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ShaderCreateDialog::_update_dialog() { | void ShaderCreateDialog::_update_dialog() { | ||||||
| 	bool shader_ok = true; |  | ||||||
| 
 |  | ||||||
| 	if (!is_built_in && !is_path_valid) { | 	if (!is_built_in && !is_path_valid) { | ||||||
| 		_msg_script_valid(false, TTR("Invalid path.")); | 		validation_panel->set_message(MSG_ID_SHADER, TTR("Invalid path."), EditorValidationPanel::MSG_ERROR); | ||||||
| 		shader_ok = false; |  | ||||||
| 	} | 	} | ||||||
| 	if (shader_ok) { | 	if (!path_error.is_empty()) { | ||||||
| 		_msg_script_valid(true, TTR("Shader path/name is valid.")); | 		validation_panel->set_message(MSG_ID_PATH, path_error, EditorValidationPanel::MSG_ERROR); | ||||||
|  | 	} else if (validation_panel->is_valid() && !is_new_shader_created) { | ||||||
|  | 		validation_panel->set_message(MSG_ID_SHADER, TTR("File exists, it will be reused."), EditorValidationPanel::MSG_OK); | ||||||
| 	} | 	} | ||||||
| 	if (!built_in_enabled) { | 	if (!built_in_enabled) { | ||||||
| 		internal->set_pressed(false); | 		internal->set_pressed(false); | ||||||
|  | @ -537,37 +518,23 @@ void ShaderCreateDialog::_update_dialog() { | ||||||
| 
 | 
 | ||||||
| 	internal->set_disabled(!built_in_enabled); | 	internal->set_disabled(!built_in_enabled); | ||||||
| 
 | 
 | ||||||
| 	builtin_warning_label->set_visible(is_built_in); | 	if (is_built_in) { | ||||||
|  | 		validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Note: Built-in shaders can't be edited using an external editor."), EditorValidationPanel::MSG_INFO, false); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if (is_built_in) { | 	if (is_built_in) { | ||||||
| 		set_ok_button_text(TTR("Create")); | 		set_ok_button_text(TTR("Create")); | ||||||
| 		_msg_path_valid(true, TTR("Built-in shader (into scene file).")); | 		validation_panel->set_message(MSG_ID_PATH, TTR("Built-in shader (into scene file)."), EditorValidationPanel::MSG_OK); | ||||||
| 	} else if (is_new_shader_created) { | 	} else if (is_new_shader_created) { | ||||||
| 		set_ok_button_text(TTR("Create")); | 		set_ok_button_text(TTR("Create")); | ||||||
| 		if (is_path_valid) { |  | ||||||
| 			_msg_path_valid(true, TTR("Will create a new shader file.")); |  | ||||||
| 		} |  | ||||||
| 	} else if (load_enabled) { | 	} else if (load_enabled) { | ||||||
| 		set_ok_button_text(TTR("Load")); | 		set_ok_button_text(TTR("Load")); | ||||||
| 		if (is_path_valid) { | 		if (is_path_valid) { | ||||||
| 			_msg_path_valid(true, TTR("Will load an existing shader file.")); | 			validation_panel->set_message(MSG_ID_PATH, TTR("Will load an existing shader file."), EditorValidationPanel::MSG_OK); | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		set_ok_button_text(TTR("Create")); | 		set_ok_button_text(TTR("Create")); | ||||||
| 		_msg_path_valid(false, TTR("Shader file already exists.")); | 		validation_panel->set_message(MSG_ID_PATH, TTR("Shader file already exists."), EditorValidationPanel::MSG_ERROR); | ||||||
| 
 |  | ||||||
| 		shader_ok = false; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	get_ok_button()->set_disabled(!shader_ok); |  | ||||||
| 
 |  | ||||||
| 	Callable entered_call = callable_mp(this, &ShaderCreateDialog::_path_submitted); |  | ||||||
| 	if (shader_ok) { |  | ||||||
| 		if (!file_path->is_connected("text_submitted", entered_call)) { |  | ||||||
| 			file_path->connect("text_submitted", entered_call); |  | ||||||
| 		} |  | ||||||
| 	} else if (file_path->is_connected("text_submitted", entered_call)) { |  | ||||||
| 		file_path->disconnect("text_submitted", entered_call); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -588,35 +555,22 @@ ShaderCreateDialog::ShaderCreateDialog() { | ||||||
| 
 | 
 | ||||||
| 	// Error Fields.
 | 	// Error Fields.
 | ||||||
| 
 | 
 | ||||||
| 	VBoxContainer *vb = memnew(VBoxContainer); | 	validation_panel = memnew(EditorValidationPanel); | ||||||
| 
 | 	validation_panel->add_line(MSG_ID_SHADER, TTR("Shader path/name is valid.")); | ||||||
| 	error_label = memnew(Label); | 	validation_panel->add_line(MSG_ID_PATH, TTR("Will create a new shader file.")); | ||||||
| 	vb->add_child(error_label); | 	validation_panel->add_line(MSG_ID_BUILT_IN); | ||||||
| 
 | 	validation_panel->set_update_callback(callable_mp(this, &ShaderCreateDialog::_update_dialog)); | ||||||
| 	path_error_label = memnew(Label); | 	validation_panel->set_accept_button(get_ok_button()); | ||||||
| 	vb->add_child(path_error_label); |  | ||||||
| 
 |  | ||||||
| 	builtin_warning_label = memnew(Label); |  | ||||||
| 	builtin_warning_label->set_text( |  | ||||||
| 			TTR("Note: Built-in shaders can't be edited using an external editor.")); |  | ||||||
| 	vb->add_child(builtin_warning_label); |  | ||||||
| 	builtin_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); |  | ||||||
| 	builtin_warning_label->hide(); |  | ||||||
| 
 |  | ||||||
| 	status_panel = memnew(PanelContainer); |  | ||||||
| 	status_panel->set_h_size_flags(Control::SIZE_FILL); |  | ||||||
| 	status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL); |  | ||||||
| 	status_panel->add_child(vb); |  | ||||||
| 
 | 
 | ||||||
| 	// Spacing.
 | 	// Spacing.
 | ||||||
| 
 | 
 | ||||||
| 	Control *spacing = memnew(Control); | 	Control *spacing = memnew(Control); | ||||||
| 	spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); | 	spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); | ||||||
| 
 | 
 | ||||||
| 	vb = memnew(VBoxContainer); | 	VBoxContainer *vb = memnew(VBoxContainer); | ||||||
| 	vb->add_child(gc); | 	vb->add_child(gc); | ||||||
| 	vb->add_child(spacing); | 	vb->add_child(spacing); | ||||||
| 	vb->add_child(status_panel); | 	vb->add_child(validation_panel); | ||||||
| 	add_child(vb); | 	add_child(vb); | ||||||
| 
 | 
 | ||||||
| 	// Type.
 | 	// Type.
 | ||||||
|  |  | ||||||
|  | @ -40,10 +40,17 @@ | ||||||
| #include "scene/gui/panel_container.h" | #include "scene/gui/panel_container.h" | ||||||
| 
 | 
 | ||||||
| class EditorFileDialog; | class EditorFileDialog; | ||||||
|  | class EditorValidationPanel; | ||||||
| 
 | 
 | ||||||
| class ShaderCreateDialog : public ConfirmationDialog { | class ShaderCreateDialog : public ConfirmationDialog { | ||||||
| 	GDCLASS(ShaderCreateDialog, ConfirmationDialog); | 	GDCLASS(ShaderCreateDialog, ConfirmationDialog); | ||||||
| 
 | 
 | ||||||
|  | 	enum { | ||||||
|  | 		MSG_ID_SHADER, | ||||||
|  | 		MSG_ID_PATH, | ||||||
|  | 		MSG_ID_BUILT_IN, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	struct ShaderTypeData { | 	struct ShaderTypeData { | ||||||
| 		List<String> extensions; | 		List<String> extensions; | ||||||
| 		String default_extension; | 		String default_extension; | ||||||
|  | @ -53,10 +60,7 @@ class ShaderCreateDialog : public ConfirmationDialog { | ||||||
| 	List<ShaderTypeData> type_data; | 	List<ShaderTypeData> type_data; | ||||||
| 
 | 
 | ||||||
| 	GridContainer *gc = nullptr; | 	GridContainer *gc = nullptr; | ||||||
| 	Label *error_label = nullptr; | 	EditorValidationPanel *validation_panel = nullptr; | ||||||
| 	Label *path_error_label = nullptr; |  | ||||||
| 	Label *builtin_warning_label = nullptr; |  | ||||||
| 	PanelContainer *status_panel = nullptr; |  | ||||||
| 	OptionButton *type_menu = nullptr; | 	OptionButton *type_menu = nullptr; | ||||||
| 	OptionButton *mode_menu = nullptr; | 	OptionButton *mode_menu = nullptr; | ||||||
| 	OptionButton *template_menu = nullptr; | 	OptionButton *template_menu = nullptr; | ||||||
|  | @ -67,6 +71,7 @@ class ShaderCreateDialog : public ConfirmationDialog { | ||||||
| 	AcceptDialog *alert = nullptr; | 	AcceptDialog *alert = nullptr; | ||||||
| 
 | 
 | ||||||
| 	String initial_base_path; | 	String initial_base_path; | ||||||
|  | 	String path_error; | ||||||
| 	bool is_new_shader_created = true; | 	bool is_new_shader_created = true; | ||||||
| 	bool is_path_valid = false; | 	bool is_path_valid = false; | ||||||
| 	bool is_built_in = false; | 	bool is_built_in = false; | ||||||
|  | @ -93,8 +98,6 @@ class ShaderCreateDialog : public ConfirmationDialog { | ||||||
| 	virtual void ok_pressed() override; | 	virtual void ok_pressed() override; | ||||||
| 	void _create_new(); | 	void _create_new(); | ||||||
| 	void _load_exist(); | 	void _load_exist(); | ||||||
| 	void _msg_script_valid(bool valid, const String &p_msg = String()); |  | ||||||
| 	void _msg_path_valid(bool valid, const String &p_msg = String()); |  | ||||||
| 	void _update_dialog(); | 	void _update_dialog(); | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 kobewi
						kobewi