summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/doc_tools.cpp111
-rw-r--r--editor/editor_builders.py8
-rw-r--r--editor/editor_export.cpp18
-rw-r--r--editor/editor_help.cpp334
-rw-r--r--editor/editor_help.h2
-rw-r--r--editor/editor_help_search.cpp24
-rw-r--r--editor/editor_help_search.h16
-rw-r--r--editor/editor_node.cpp104
-rw-r--r--editor/editor_node.h12
-rw-r--r--editor/editor_properties_array_dict.cpp21
-rw-r--r--editor/editor_resource_preview.cpp217
-rw-r--r--editor/editor_resource_preview.h9
-rw-r--r--editor/editor_run.cpp2
-rw-r--r--editor/editor_spin_slider.cpp14
-rw-r--r--editor/editor_spin_slider.h1
-rw-r--r--editor/editor_toaster.cpp7
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp703
-rw-r--r--editor/plugins/tiles/tile_map_editor.h37
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp10
-rw-r--r--editor/plugins/tiles/tile_set_editor.h6
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp10
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.h5
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp228
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h27
-rw-r--r--editor/project_manager.cpp15
26 files changed, 1342 insertions, 601 deletions
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index beead74c53..61cc6dbd4a 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -57,25 +57,21 @@ void DocTools::merge_from(const DocTools &p_data) {
c.brief_description = cf.brief_description;
c.tutorials = cf.tutorials;
- for (int i = 0; i < c.methods.size(); i++) {
- DocData::MethodDoc &m = c.methods.write[i];
+ for (int i = 0; i < c.constructors.size(); i++) {
+ DocData::MethodDoc &m = c.constructors.write[i];
- for (int j = 0; j < cf.methods.size(); j++) {
- if (cf.methods[j].name != m.name) {
+ for (int j = 0; j < cf.constructors.size(); j++) {
+ if (cf.constructors[j].name != m.name) {
continue;
}
- const char *operator_prefix = "operator "; // Operators use a space at the end, making this prefix an invalid identifier (and differentiating from methods).
-
- if (cf.methods[j].name == c.name || cf.methods[j].name.begins_with(operator_prefix)) {
- // Since constructors and operators can repeat, we need to check the type of
+ {
+ // Since constructors can repeat, we need to check the type of
// the arguments so we make sure they are different.
-
- if (cf.methods[j].arguments.size() != m.arguments.size()) {
+ if (cf.constructors[j].arguments.size() != m.arguments.size()) {
continue;
}
-
- int arg_count = cf.methods[j].arguments.size();
+ int arg_count = cf.constructors[j].arguments.size();
Vector<bool> arg_used;
arg_used.resize(arg_count);
for (int l = 0; l < arg_count; ++l) {
@@ -85,7 +81,7 @@ void DocTools::merge_from(const DocTools &p_data) {
// have to check one by one so we make sure we have an exact match
for (int k = 0; k < arg_count; ++k) {
for (int l = 0; l < arg_count; ++l) {
- if (cf.methods[j].arguments[k].type == m.arguments[l].type && !arg_used[l]) {
+ if (cf.constructors[j].arguments[k].type == m.arguments[l].type && !arg_used[l]) {
arg_used.write[l] = true;
break;
}
@@ -102,6 +98,21 @@ void DocTools::merge_from(const DocTools &p_data) {
}
}
+ const DocData::MethodDoc &mf = cf.constructors[j];
+
+ m.description = mf.description;
+ break;
+ }
+ }
+
+ for (int i = 0; i < c.methods.size(); i++) {
+ DocData::MethodDoc &m = c.methods.write[i];
+
+ for (int j = 0; j < cf.methods.size(); j++) {
+ if (cf.methods[j].name != m.name) {
+ continue;
+ }
+
const DocData::MethodDoc &mf = cf.methods[j];
m.description = mf.description;
@@ -165,6 +176,54 @@ void DocTools::merge_from(const DocTools &p_data) {
}
}
+ for (int i = 0; i < c.operators.size(); i++) {
+ DocData::MethodDoc &m = c.operators.write[i];
+
+ for (int j = 0; j < cf.operators.size(); j++) {
+ if (cf.operators[j].name != m.name) {
+ continue;
+ }
+
+ {
+ // Since operators can repeat, we need to check the type of
+ // the arguments so we make sure they are different.
+ if (cf.operators[j].arguments.size() != m.arguments.size()) {
+ continue;
+ }
+ int arg_count = cf.operators[j].arguments.size();
+ Vector<bool> arg_used;
+ arg_used.resize(arg_count);
+ for (int l = 0; l < arg_count; ++l) {
+ arg_used.write[l] = false;
+ }
+ // also there is no guarantee that argument ordering will match, so we
+ // have to check one by one so we make sure we have an exact match
+ for (int k = 0; k < arg_count; ++k) {
+ for (int l = 0; l < arg_count; ++l) {
+ if (cf.operators[j].arguments[k].type == m.arguments[l].type && !arg_used[l]) {
+ arg_used.write[l] = true;
+ break;
+ }
+ }
+ }
+ bool not_the_same = false;
+ for (int l = 0; l < arg_count; ++l) {
+ if (!arg_used[l]) { // at least one of the arguments was different
+ not_the_same = true;
+ }
+ }
+ if (not_the_same) {
+ continue;
+ }
+ }
+
+ const DocData::MethodDoc &mf = cf.operators[j];
+
+ m.description = mf.description;
+ break;
+ }
+ }
+
#ifndef MODULE_MONO_ENABLED
// The Mono module defines some properties that we want to keep when
// re-generating docs with a non-Mono build, to prevent pointless diffs
@@ -650,11 +709,6 @@ void DocTools::generate(bool p_basic_types) {
DocData::MethodDoc method;
method.name = mi.name;
- if (method.name == cname) {
- method.qualifiers = "constructor";
- } else if (method.name.begins_with("operator")) {
- method.qualifiers = "operator";
- }
for (int j = 0; j < mi.arguments.size(); j++) {
PropertyInfo arginfo = mi.arguments[j];
@@ -694,7 +748,13 @@ void DocTools::generate(bool p_basic_types) {
method.qualifiers += "static";
}
- c.methods.push_back(method);
+ if (method.name == cname) {
+ c.constructors.push_back(method);
+ } else if (method.name.begins_with("operator")) {
+ c.operators.push_back(method);
+ } else {
+ c.methods.push_back(method);
+ }
}
List<PropertyInfo> properties;
@@ -916,7 +976,7 @@ static Error _parse_methods(Ref<XMLParser> &parser, Vector<DocData::MethodDoc> &
methods.push_back(method);
} else {
- ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Invalid tag in doc file: " + parser->get_node_name() + ".");
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Invalid tag in doc file: " + parser->get_node_name() + ", expected " + element + ".");
}
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == section) {
@@ -1044,10 +1104,15 @@ Error DocTools::_load(Ref<XMLParser> parser) {
break; // End of <tutorials>.
}
}
+ } else if (name2 == "constructors") {
+ Error err2 = _parse_methods(parser, c.constructors);
+ ERR_FAIL_COND_V(err2, err2);
} else if (name2 == "methods") {
Error err2 = _parse_methods(parser, c.methods);
ERR_FAIL_COND_V(err2, err2);
-
+ } else if (name2 == "operators") {
+ Error err2 = _parse_methods(parser, c.operators);
+ ERR_FAIL_COND_V(err2, err2);
} else if (name2 == "signals") {
Error err2 = _parse_methods(parser, c.signals);
ERR_FAIL_COND_V(err2, err2);
@@ -1269,6 +1334,8 @@ Error DocTools::save_classes(const String &p_default_path, const Map<String, Str
}
_write_string(f, 1, "</tutorials>");
+ _write_method_doc(f, "constructor", c.constructors);
+
_write_method_doc(f, "method", c.methods);
if (!c.properties.is_empty()) {
@@ -1344,6 +1411,8 @@ Error DocTools::save_classes(const String &p_default_path, const Map<String, Str
_write_string(f, 1, "</theme_items>");
}
+ _write_method_doc(f, "operator", c.operators);
+
_write_string(f, 0, "</class>");
}
diff --git a/editor/editor_builders.py b/editor/editor_builders.py
index ff0daa86ff..67d4b8534f 100644
--- a/editor/editor_builders.py
+++ b/editor/editor_builders.py
@@ -26,7 +26,9 @@ def make_doc_header(target, source, env):
decomp_size = len(buf)
import zlib
- buf = zlib.compress(buf)
+ # Use maximum zlib compression level to further reduce file size
+ # (at the cost of initial build times).
+ buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
g.write("#ifndef _DOC_DATA_RAW_H\n")
@@ -92,7 +94,9 @@ def make_translations_header(target, source, env, category):
with open(sorted_paths[i], "rb") as f:
buf = f.read()
decomp_size = len(buf)
- buf = zlib.compress(buf)
+ # Use maximum zlib compression level to further reduce file size
+ # (at the cost of initial build times).
+ buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
name = os.path.splitext(os.path.basename(sorted_paths[i]))[0]
g.write("static const unsigned char _{}_translation_{}_compressed[] = {{\n".format(category, name))
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 2010ee01db..d1dfc61c19 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -1491,13 +1491,16 @@ void EditorExport::add_export_preset(const Ref<EditorExportPreset> &p_preset, in
}
String EditorExportPlatform::test_etc2() const {
+ // String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
+ // bool etc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc");
+ // bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
bool etc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc");
bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
- if (driver == "GLES2" && !etc_supported) {
- return TTR("Target platform requires 'ETC' texture compression for GLES2. Enable 'Import Etc' in Project Settings.");
- } else if (driver == "Vulkan" && !etc2_supported) {
+ if (driver == "opengl3" && !etc_supported) {
+ return TTR("Target platform requires 'ETC' texture compression for OpenGL. Enable 'Import Etc' in Project Settings.");
+ } else if (driver == "vulkan" && !etc2_supported) {
// FIXME: Review if this is true for Vulkan.
return TTR("Target platform requires 'ETC2' texture compression for Vulkan. Enable 'Import Etc 2' in Project Settings.");
}
@@ -1508,10 +1511,13 @@ String EditorExportPlatform::test_etc2_or_pvrtc() const {
String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
bool pvrtc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc");
+ // String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
+ // bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
+ // bool pvrtc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc");
- if (driver == "GLES2" && !pvrtc_supported) {
- return TTR("Target platform requires 'PVRTC' texture compression for GLES2. Enable 'Import Pvrtc' in Project Settings.");
- } else if (driver == "Vulkan" && !etc2_supported && !pvrtc_supported) {
+ if (driver == "opengl3" && !pvrtc_supported) {
+ return TTR("Target platform requires 'PVRTC' texture compression for OpenGL. Enable 'Import Pvrtc' in Project Settings.");
+ } else if (driver == "vulkan" && !etc2_supported && !pvrtc_supported) {
// FIXME: Review if this is true for Vulkan.
return TTR("Target platform requires 'ETC2' or 'PVRTC' texture compression for Vulkan. Enable 'Import Etc 2' or 'Import Pvrtc' in Project Settings.");
}
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index c049db8ef6..8c3569df07 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -330,6 +330,153 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) {
return OK;
}
+void EditorHelp::_update_method_list(const Vector<DocData::MethodDoc> p_methods, bool &r_method_descrpitons) {
+ Ref<Font> doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts"));
+ class_desc->pop();
+ class_desc->pop();
+
+ class_desc->add_newline();
+ class_desc->push_font(doc_code_font);
+ class_desc->push_indent(1);
+ class_desc->push_table(2);
+ class_desc->set_table_column_expand(1, true);
+
+ bool any_previous = false;
+ for (int pass = 0; pass < 2; pass++) {
+ Vector<DocData::MethodDoc> m;
+
+ for (int i = 0; i < p_methods.size(); i++) {
+ const String &q = p_methods[i].qualifiers;
+ if ((pass == 0 && q.find("virtual") != -1) || (pass == 1 && q.find("virtual") == -1)) {
+ m.push_back(p_methods[i]);
+ }
+ }
+
+ if (any_previous && !m.is_empty()) {
+ class_desc->push_cell();
+ class_desc->pop(); //cell
+ class_desc->push_cell();
+ class_desc->pop(); //cell
+ }
+
+ String group_prefix;
+ for (int i = 0; i < m.size(); i++) {
+ const String new_prefix = m[i].name.substr(0, 3);
+ bool is_new_group = false;
+
+ if (i < m.size() - 1 && new_prefix == m[i + 1].name.substr(0, 3) && new_prefix != group_prefix) {
+ is_new_group = i > 0;
+ group_prefix = new_prefix;
+ } else if (group_prefix != "" && new_prefix != group_prefix) {
+ is_new_group = true;
+ group_prefix = "";
+ }
+
+ if (is_new_group && pass == 1) {
+ class_desc->push_cell();
+ class_desc->pop(); //cell
+ class_desc->push_cell();
+ class_desc->pop(); //cell
+ }
+
+ if (m[i].description != "" || m[i].errors_returned.size() > 0) {
+ r_method_descrpitons = true;
+ }
+
+ _add_method(m[i], true);
+ }
+
+ any_previous = !m.is_empty();
+ }
+
+ class_desc->pop(); //table
+ class_desc->pop();
+ class_desc->pop(); // font
+ class_desc->add_newline();
+ class_desc->add_newline();
+}
+
+void EditorHelp::_update_method_descriptions(const DocData::ClassDoc p_classdoc, const Vector<DocData::MethodDoc> p_methods, const String &p_method_type) {
+ Ref<Font> doc_font = get_theme_font(SNAME("doc"), SNAME("EditorFonts"));
+ Ref<Font> doc_bold_font = get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts"));
+ Ref<Font> doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts"));
+ String link_color_text = title_color.to_html(false);
+ class_desc->pop();
+ class_desc->pop();
+
+ class_desc->add_newline();
+ class_desc->add_newline();
+
+ for (int pass = 0; pass < 2; pass++) {
+ Vector<DocData::MethodDoc> methods_filtered;
+
+ for (int i = 0; i < p_methods.size(); i++) {
+ const String &q = p_methods[i].qualifiers;
+ if ((pass == 0 && q.find("virtual") != -1) || (pass == 1 && q.find("virtual") == -1)) {
+ methods_filtered.push_back(p_methods[i]);
+ }
+ }
+
+ for (int i = 0; i < methods_filtered.size(); i++) {
+ class_desc->push_font(doc_code_font);
+ _add_method(methods_filtered[i], false);
+ class_desc->pop();
+
+ class_desc->add_newline();
+ class_desc->add_newline();
+
+ class_desc->push_color(text_color);
+ class_desc->push_font(doc_font);
+ class_desc->push_indent(1);
+ if (methods_filtered[i].errors_returned.size()) {
+ class_desc->append_text(TTR("Error codes returned:"));
+ class_desc->add_newline();
+ class_desc->push_list(0, RichTextLabel::LIST_DOTS, false);
+ for (int j = 0; j < methods_filtered[i].errors_returned.size(); j++) {
+ if (j > 0) {
+ class_desc->add_newline();
+ }
+ int val = methods_filtered[i].errors_returned[j];
+ String text = itos(val);
+ for (int k = 0; k < CoreConstants::get_global_constant_count(); k++) {
+ if (CoreConstants::get_global_constant_value(k) == val && CoreConstants::get_global_constant_enum(k) == SNAME("Error")) {
+ text = CoreConstants::get_global_constant_name(k);
+ break;
+ }
+ }
+
+ class_desc->push_bold();
+ class_desc->append_text(text);
+ class_desc->pop();
+ }
+ class_desc->pop();
+ class_desc->add_newline();
+ class_desc->add_newline();
+ }
+ if (!methods_filtered[i].description.strip_edges().is_empty()) {
+ _add_text(DTR(methods_filtered[i].description));
+ } else {
+ class_desc->add_image(get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
+ class_desc->add_text(" ");
+ class_desc->push_color(comment_color);
+ if (p_classdoc.is_script_doc) {
+ class_desc->append_text(TTR("There is currently no description for this " + p_method_type + "."));
+ } else {
+ class_desc->append_text(TTR("There is currently no description for this " + p_method_type + ". Please help us by [color=$color][url=$url]contributing one[/url][/color]!").replace("$url", CONTRIBUTE_URL).replace("$color", link_color_text));
+ }
+ class_desc->pop();
+ }
+
+ class_desc->pop();
+ class_desc->pop();
+ class_desc->pop();
+ class_desc->add_newline();
+ class_desc->add_newline();
+ class_desc->add_newline();
+ }
+ }
+}
+
void EditorHelp::_update_doc() {
if (!doc->class_list.has(edited_class)) {
return;
@@ -622,7 +769,9 @@ void EditorHelp::_update_doc() {
}
// Methods overview
- bool method_descr = false;
+ bool constructor_descriptions = false;
+ bool method_descriptions = false;
+ bool operator_descriptions = false;
bool sort_methods = EditorSettings::get_singleton()->get("text_editor/help/sort_functions_alphabetically");
Vector<DocData::MethodDoc> methods;
@@ -640,81 +789,43 @@ void EditorHelp::_update_doc() {
methods.push_back(cd.methods[i]);
}
- if (methods.size()) {
+ if (!cd.constructors.is_empty()) {
if (sort_methods) {
- methods.sort();
+ cd.constructors.sort();
}
+ section_line.push_back(Pair<String, int>(TTR("Constructors"), class_desc->get_line_count() - 2));
+ class_desc->push_color(title_color);
+ class_desc->push_font(doc_title_font);
+ class_desc->add_text(TTR("Constructors"));
+ _update_method_list(cd.constructors, constructor_descriptions);
+ }
+
+ if (!methods.is_empty()) {
+ if (sort_methods) {
+ methods.sort();
+ }
section_line.push_back(Pair<String, int>(TTR("Methods"), class_desc->get_line_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Methods"));
- class_desc->pop();
- class_desc->pop();
-
- class_desc->add_newline();
- class_desc->push_font(doc_code_font);
- class_desc->push_indent(1);
- class_desc->push_table(2);
- class_desc->set_table_column_expand(1, true);
-
- bool any_previous = false;
- for (int pass = 0; pass < 2; pass++) {
- Vector<DocData::MethodDoc> m;
-
- for (int i = 0; i < methods.size(); i++) {
- const String &q = methods[i].qualifiers;
- if ((pass == 0 && q.find("virtual") != -1) || (pass == 1 && q.find("virtual") == -1)) {
- m.push_back(methods[i]);
- }
- }
-
- if (any_previous && !m.is_empty()) {
- class_desc->push_cell();
- class_desc->pop(); //cell
- class_desc->push_cell();
- class_desc->pop(); //cell
- }
-
- String group_prefix;
- for (int i = 0; i < m.size(); i++) {
- const String new_prefix = m[i].name.substr(0, 3);
- bool is_new_group = false;
-
- if (i < m.size() - 1 && new_prefix == m[i + 1].name.substr(0, 3) && new_prefix != group_prefix) {
- is_new_group = i > 0;
- group_prefix = new_prefix;
- } else if (group_prefix != "" && new_prefix != group_prefix) {
- is_new_group = true;
- group_prefix = "";
- }
-
- if (is_new_group && pass == 1) {
- class_desc->push_cell();
- class_desc->pop(); //cell
- class_desc->push_cell();
- class_desc->pop(); //cell
- }
-
- if (m[i].description != "" || m[i].errors_returned.size() > 0) {
- method_descr = true;
- }
-
- _add_method(m[i], true);
- }
+ _update_method_list(methods, method_descriptions);
+ }
- any_previous = !m.is_empty();
+ if (!cd.operators.is_empty()) {
+ if (sort_methods) {
+ cd.operators.sort();
}
- class_desc->pop(); //table
- class_desc->pop();
- class_desc->pop(); // font
- class_desc->add_newline();
- class_desc->add_newline();
+ section_line.push_back(Pair<String, int>(TTR("Operators"), class_desc->get_line_count() - 2));
+ class_desc->push_color(title_color);
+ class_desc->push_font(doc_title_font);
+ class_desc->add_text(TTR("Operators"));
+ _update_method_list(cd.operators, operator_descriptions);
}
// Theme properties
- if (cd.theme_properties.size()) {
+ if (!cd.theme_properties.is_empty()) {
section_line.push_back(Pair<String, int>(TTR("Theme Properties"), class_desc->get_line_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
@@ -775,7 +886,7 @@ void EditorHelp::_update_doc() {
}
// Signals
- if (cd.signals.size()) {
+ if (!cd.signals.is_empty()) {
if (sort_methods) {
cd.signals.sort();
}
@@ -844,7 +955,7 @@ void EditorHelp::_update_doc() {
}
// Constants and enums
- if (cd.constants.size()) {
+ if (!cd.constants.is_empty()) {
Map<String, Vector<DocData::ConstantDoc>> enums;
Vector<DocData::ConstantDoc> constants;
@@ -1195,86 +1306,31 @@ void EditorHelp::_update_doc() {
}
}
+ // Constructor descriptions
+ if (constructor_descriptions) {
+ section_line.push_back(Pair<String, int>(TTR("Constructor Descriptions"), class_desc->get_line_count() - 2));
+ class_desc->push_color(title_color);
+ class_desc->push_font(doc_title_font);
+ class_desc->add_text(TTR("Constructor Descriptions"));
+ _update_method_descriptions(cd, cd.constructors, "constructor");
+ }
+
// Method descriptions
- if (method_descr) {
+ if (method_descriptions) {
section_line.push_back(Pair<String, int>(TTR("Method Descriptions"), class_desc->get_line_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Method Descriptions"));
- class_desc->pop();
- class_desc->pop();
-
- class_desc->add_newline();
- class_desc->add_newline();
-
- for (int pass = 0; pass < 2; pass++) {
- Vector<DocData::MethodDoc> methods_filtered;
-
- for (int i = 0; i < methods.size(); i++) {
- const String &q = methods[i].qualifiers;
- if ((pass == 0 && q.find("virtual") != -1) || (pass == 1 && q.find("virtual") == -1)) {
- methods_filtered.push_back(methods[i]);
- }
- }
-
- for (int i = 0; i < methods_filtered.size(); i++) {
- class_desc->push_font(doc_code_font);
- _add_method(methods_filtered[i], false);
- class_desc->pop();
-
- class_desc->add_newline();
- class_desc->add_newline();
-
- class_desc->push_color(text_color);
- class_desc->push_font(doc_font);
- class_desc->push_indent(1);
- if (methods_filtered[i].errors_returned.size()) {
- class_desc->append_text(TTR("Error codes returned:"));
- class_desc->add_newline();
- class_desc->push_list(0, RichTextLabel::LIST_DOTS, false);
- for (int j = 0; j < methods_filtered[i].errors_returned.size(); j++) {
- if (j > 0) {
- class_desc->add_newline();
- }
- int val = methods_filtered[i].errors_returned[j];
- String text = itos(val);
- for (int k = 0; k < CoreConstants::get_global_constant_count(); k++) {
- if (CoreConstants::get_global_constant_value(k) == val && CoreConstants::get_global_constant_enum(k) == SNAME("Error")) {
- text = CoreConstants::get_global_constant_name(k);
- break;
- }
- }
-
- class_desc->push_bold();
- class_desc->append_text(text);
- class_desc->pop();
- }
- class_desc->pop();
- class_desc->add_newline();
- class_desc->add_newline();
- }
- if (!methods_filtered[i].description.strip_edges().is_empty()) {
- _add_text(DTR(methods_filtered[i].description));
- } else {
- class_desc->add_image(get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
- class_desc->add_text(" ");
- class_desc->push_color(comment_color);
- if (cd.is_script_doc) {
- class_desc->append_text(TTR("There is currently no description for this method."));
- } else {
- class_desc->append_text(TTR("There is currently no description for this method. Please help us by [color=$color][url=$url]contributing one[/url][/color]!").replace("$url", CONTRIBUTE_URL).replace("$color", link_color_text));
- }
- class_desc->pop();
- }
+ _update_method_descriptions(cd, methods, "method");
+ }
- class_desc->pop();
- class_desc->pop();
- class_desc->pop();
- class_desc->add_newline();
- class_desc->add_newline();
- class_desc->add_newline();
- }
- }
+ // Operator descriptions
+ if (operator_descriptions) {
+ section_line.push_back(Pair<String, int>(TTR("Operator Descriptions"), class_desc->get_line_count() - 2));
+ class_desc->push_color(title_color);
+ class_desc->push_font(doc_title_font);
+ class_desc->add_text(TTR("Operator Descriptions"));
+ _update_method_descriptions(cd, cd.operators, "operator");
}
scroll_locked = false;
}
diff --git a/editor/editor_help.h b/editor/editor_help.h
index 46605b6763..c0f3f66505 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -152,6 +152,8 @@ class EditorHelp : public VBoxContainer {
Error _goto_desc(const String &p_class, int p_vscr = -1);
//void _update_history_buttons();
+ void _update_method_list(const Vector<DocData::MethodDoc> p_methods, bool &r_method_descrpitons);
+ void _update_method_descriptions(const DocData::ClassDoc p_classdoc, const Vector<DocData::MethodDoc> p_methods, const String &p_method_type);
void _update_doc();
void _request_help(const String &p_string);
diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp
index e56b10720d..8504745b03 100644
--- a/editor/editor_help_search.cpp
+++ b/editor/editor_help_search.cpp
@@ -226,7 +226,9 @@ EditorHelpSearch::EditorHelpSearch() {
filter_combo->add_item(TTR("Display All"), SEARCH_ALL);
filter_combo->add_separator();
filter_combo->add_item(TTR("Classes Only"), SEARCH_CLASSES);
+ filter_combo->add_item(TTR("Constructors Only"), SEARCH_CONSTRUCTORS);
filter_combo->add_item(TTR("Methods Only"), SEARCH_METHODS);
+ filter_combo->add_item(TTR("Operators Only"), SEARCH_OPERATORS);
filter_combo->add_item(TTR("Signals Only"), SEARCH_SIGNALS);
filter_combo->add_item(TTR("Constants Only"), SEARCH_CONSTANTS);
filter_combo->add_item(TTR("Properties Only"), SEARCH_PROPERTIES);
@@ -334,6 +336,17 @@ bool EditorHelpSearch::Runner::_phase_match_classes() {
// Match members if the term is long enough.
if (term.length() > 1) {
+ if (search_flags & SEARCH_CONSTRUCTORS) {
+ for (int i = 0; i < class_doc.constructors.size(); i++) {
+ String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.constructors[i].name : class_doc.constructors[i].name.to_lower();
+ if (method_name.find(term) > -1 ||
+ (term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
+ (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
+ (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
+ match.constructors.push_back(const_cast<DocData::MethodDoc *>(&class_doc.constructors[i]));
+ }
+ }
+ }
if (search_flags & SEARCH_METHODS) {
for (int i = 0; i < class_doc.methods.size(); i++) {
String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.methods[i].name : class_doc.methods[i].name.to_lower();
@@ -345,6 +358,17 @@ bool EditorHelpSearch::Runner::_phase_match_classes() {
}
}
}
+ if (search_flags & SEARCH_OPERATORS) {
+ for (int i = 0; i < class_doc.operators.size(); i++) {
+ String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.operators[i].name : class_doc.operators[i].name.to_lower();
+ if (method_name.find(term) > -1 ||
+ (term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
+ (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
+ (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
+ match.operators.push_back(const_cast<DocData::MethodDoc *>(&class_doc.operators[i]));
+ }
+ }
+ }
if (search_flags & SEARCH_SIGNALS) {
for (int i = 0; i < class_doc.signals.size(); i++) {
if (_match_string(term, class_doc.signals[i].name)) {
diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h
index bc57c0e3c6..7285f76c01 100644
--- a/editor/editor_help_search.h
+++ b/editor/editor_help_search.h
@@ -43,12 +43,14 @@ class EditorHelpSearch : public ConfirmationDialog {
enum SearchFlags {
SEARCH_CLASSES = 1 << 0,
- SEARCH_METHODS = 1 << 1,
- SEARCH_SIGNALS = 1 << 2,
- SEARCH_CONSTANTS = 1 << 3,
- SEARCH_PROPERTIES = 1 << 4,
- SEARCH_THEME_ITEMS = 1 << 5,
- SEARCH_ALL = SEARCH_CLASSES | SEARCH_METHODS | SEARCH_SIGNALS | SEARCH_CONSTANTS | SEARCH_PROPERTIES | SEARCH_THEME_ITEMS,
+ SEARCH_CONSTRUCTORS = 1 << 1,
+ SEARCH_METHODS = 1 << 2,
+ SEARCH_OPERATORS = 1 << 3,
+ SEARCH_SIGNALS = 1 << 4,
+ SEARCH_CONSTANTS = 1 << 5,
+ SEARCH_PROPERTIES = 1 << 6,
+ SEARCH_THEME_ITEMS = 1 << 7,
+ SEARCH_ALL = SEARCH_CLASSES | SEARCH_CONSTRUCTORS | SEARCH_METHODS | SEARCH_OPERATORS | SEARCH_SIGNALS | SEARCH_CONSTANTS | SEARCH_PROPERTIES | SEARCH_THEME_ITEMS,
SEARCH_CASE_SENSITIVE = 1 << 29,
SEARCH_SHOW_HIERARCHY = 1 << 30
};
@@ -99,7 +101,9 @@ class EditorHelpSearch::Runner : public RefCounted {
struct ClassMatch {
DocData::ClassDoc *doc;
bool name = false;
+ Vector<DocData::MethodDoc *> constructors;
Vector<DocData::MethodDoc *> methods;
+ Vector<DocData::MethodDoc *> operators;
Vector<DocData::MethodDoc *> signals;
Vector<DocData::ConstantDoc *> constants;
Vector<DocData::PropertyDoc *> properties;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 288b6752ee..bb4a05efba 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -589,6 +589,11 @@ void EditorNode::_notification(int p_what) {
settings_changed = false;
emit_signal(SNAME("project_settings_changed"));
}
+
+ ResourceImporterTexture::get_singleton()->update_imports();
+
+ // if using a main thread only renderer, we need to update the resource previews
+ EditorResourcePreview::get_singleton()->update();
} break;
case NOTIFICATION_ENTER_TREE: {
@@ -2903,8 +2908,13 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
OS::get_singleton()->shell_open("https://godotengine.org/donate");
} break;
- case SET_VIDEO_DRIVER_SAVE_AND_RESTART: {
- ProjectSettings::get_singleton()->set("rendering/driver/driver_name", video_driver_request);
+ // case SET_VIDEO_DRIVER_SAVE_AND_RESTART: {
+ // ProjectSettings::get_singleton()->set("rendering/driver/driver_name", video_driver_request);
+ // save_all_scenes();
+ // restart_editor();
+ // } break;
+ case SET_RENDERING_DRIVER_SAVE_AND_RESTART: {
+ ProjectSettings::get_singleton()->set("rendering/driver/driver_name", rendering_driver_request);
ProjectSettings::get_singleton()->save();
save_all_scenes();
@@ -3049,7 +3059,7 @@ void EditorNode::_discard_changes(const String &p_str) {
args.push_back(exec.get_base_dir());
args.push_back("--project-manager");
- Error err = OS::get_singleton()->create_process(exec, args);
+ Error err = OS::get_singleton()->create_instance(args);
ERR_FAIL_COND(err);
} break;
}
@@ -5410,8 +5420,7 @@ void EditorNode::_global_menu_new_window(const Variant &p_tag) {
if (OS::get_singleton()->get_main_loop()) {
List<String> args;
args.push_back("-p");
- String exec = OS::get_singleton()->get_executable_path();
- OS::get_singleton()->create_process(exec, args);
+ OS::get_singleton()->create_instance(args);
}
}
@@ -5585,17 +5594,16 @@ void EditorNode::_bottom_panel_raise_toggled(bool p_pressed) {
top_split->set_visible(!p_pressed);
}
-void EditorNode::_update_video_driver_color() {
- // TODO: Probably should de-hardcode this and add to editor settings.
- if (video_driver->get_text() == "GLES2") {
- video_driver->add_theme_color_override("font_color", Color::hex(0x5586a4ff));
- } else if (video_driver->get_text() == "Vulkan") {
- video_driver->add_theme_color_override("font_color", theme_base->get_theme_color(SNAME("vulkan_color"), SNAME("Editor")));
+void EditorNode::_update_rendering_driver_color() {
+ if (rendering_driver->get_text() == "opengl3") {
+ rendering_driver->add_theme_color_override("font_color", Color::hex(0x5586a4ff));
+ } else if (rendering_driver->get_text() == "vulkan") {
+ rendering_driver->add_theme_color_override("font_color", theme_base->get_theme_color("vulkan_color", "Editor"));
}
}
-void EditorNode::_video_driver_selected(int p_which) {
- String driver = video_driver->get_item_metadata(p_which);
+void EditorNode::_rendering_driver_selected(int p_which) {
+ String driver = rendering_driver->get_item_metadata(p_which);
String current = ""; // OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver());
@@ -5603,10 +5611,10 @@ void EditorNode::_video_driver_selected(int p_which) {
return;
}
- video_driver_request = driver;
+ rendering_driver_request = driver;
video_restart_dialog->popup_centered();
- video_driver->select(video_driver_current);
- _update_video_driver_color();
+ rendering_driver->select(rendering_driver_current);
+ _update_rendering_driver_color();
}
void EditorNode::_resource_saved(RES p_resource, const String &p_path) {
@@ -6610,40 +6618,50 @@ EditorNode::EditorNode() {
HBoxContainer *right_menu_hb = memnew(HBoxContainer);
menu_hb->add_child(right_menu_hb);
- // Toggle for video driver
- video_driver = memnew(OptionButton);
- video_driver->set_focus_mode(Control::FOCUS_NONE);
- video_driver->connect("item_selected", callable_mp(this, &EditorNode::_video_driver_selected));
- video_driver->add_theme_font_override("font", gui_base->get_theme_font(SNAME("bold"), SNAME("EditorFonts")));
- video_driver->add_theme_font_size_override("font_size", gui_base->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")));
- // TODO: Show again when OpenGL is ported.
- video_driver->set_visible(false);
- right_menu_hb->add_child(video_driver);
-
-#ifndef _MSC_VER
-#warning needs to be reimplemented
-#endif
-#if 0
- String video_drivers = ProjectSettings::get_singleton()->get_custom_property_info()["rendering/driver/driver_name"].hint_string;
- String current_video_driver = OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver());
- video_driver_current = 0;
- for (int i = 0; i < video_drivers.get_slice_count(","); i++) {
- String driver = video_drivers.get_slice(",", i);
- video_driver->add_item(driver);
- video_driver->set_item_metadata(i, driver);
+ rendering_driver = memnew(OptionButton);
+
+ // Hide the renderer selection dropdown until OpenGL support is more mature.
+ // The renderer can still be changed in the project settings or using `--rendering-driver opengl3`.
+ rendering_driver->set_visible(false);
+
+ rendering_driver->set_flat(true);
+ rendering_driver->set_focus_mode(Control::FOCUS_NONE);
+ rendering_driver->connect("item_selected", callable_mp(this, &EditorNode::_rendering_driver_selected));
+ rendering_driver->add_theme_font_override("font", gui_base->get_theme_font("bold", "EditorFonts"));
+ rendering_driver->add_theme_font_size_override("font_size", gui_base->get_theme_font_size("bold_size", "EditorFonts"));
+
+ right_menu_hb->add_child(rendering_driver);
- if (current_video_driver == driver) {
- video_driver->select(i);
- video_driver_current = i;
+ // Only display the render drivers that are available for this display driver.
+ int display_driver_idx = OS::get_singleton()->get_display_driver_id();
+ Vector<String> render_drivers = DisplayServer::get_create_function_rendering_drivers(display_driver_idx);
+ String current_rendering_driver = OS::get_singleton()->get_current_rendering_driver_name();
+
+ // As we are doing string comparisons, keep in standard case to prevent problems with capitals
+ // "vulkan" in particular uses lowercase "v" in the code, and uppercase in the UI.
+ current_rendering_driver = current_rendering_driver.to_lower();
+
+ for (int i = 0; i < render_drivers.size(); i++) {
+ String driver = render_drivers[i];
+
+ // Add the driver to the UI.
+ rendering_driver->add_item(driver);
+ rendering_driver->set_item_metadata(i, driver);
+
+ // Lowercase for standard comparison.
+ driver = driver.to_lower();
+
+ if (current_rendering_driver == driver) {
+ rendering_driver->select(i);
+ rendering_driver_current = i;
}
}
+ _update_rendering_driver_color();
- _update_video_driver_color();
-#endif
video_restart_dialog = memnew(ConfirmationDialog);
video_restart_dialog->set_text(TTR("Changing the video driver requires restarting the editor."));
video_restart_dialog->get_ok_button()->set_text(TTR("Save & Restart"));
- video_restart_dialog->connect("confirmed", callable_mp(this, &EditorNode::_menu_option), varray(SET_VIDEO_DRIVER_SAVE_AND_RESTART));
+ video_restart_dialog->connect("confirmed", callable_mp(this, &EditorNode::_menu_option), varray(SET_RENDERING_DRIVER_SAVE_AND_RESTART));
gui_base->add_child(video_restart_dialog);
progress_hb = memnew(BackgroundProgress);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 03e5146d98..98aa4b697c 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -206,7 +206,7 @@ private:
HELP_ABOUT,
HELP_SUPPORT_GODOT_DEVELOPMENT,
- SET_VIDEO_DRIVER_SAVE_AND_RESTART,
+ SET_RENDERING_DRIVER_SAVE_AND_RESTART,
GLOBAL_NEW_WINDOW,
GLOBAL_SCENE,
@@ -222,14 +222,14 @@ private:
Control *theme_base;
Control *gui_base;
VBoxContainer *main_vbox;
- OptionButton *video_driver;
+ OptionButton *rendering_driver;
ConfirmationDialog *video_restart_dialog;
- int video_driver_current;
- String video_driver_request;
- void _video_driver_selected(int);
- void _update_video_driver_color();
+ int rendering_driver_current;
+ String rendering_driver_request;
+ void _rendering_driver_selected(int);
+ void _update_rendering_driver_color();
// Split containers
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 9b5dc8851c..a3b6f6e59b 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -855,6 +855,7 @@ void EditorPropertyDictionary::update_property() {
object->set_dict(dict);
VBoxContainer *add_vbox = nullptr;
+ double default_float_step = EDITOR_GET("interface/inspector/default_float_step");
for (int i = 0; i < amount + 2; i++) {
String prop_name;
@@ -894,7 +895,7 @@ void EditorPropertyDictionary::update_property() {
} break;
case Variant::FLOAT: {
EditorPropertyFloat *editor = memnew(EditorPropertyFloat);
- editor->setup(-100000, 100000, 0.001, true, false, true, true);
+ editor->setup(-100000, 100000, default_float_step, true, false, true, true);
prop = editor;
} break;
case Variant::STRING: {
@@ -905,7 +906,7 @@ void EditorPropertyDictionary::update_property() {
// Math types.
case Variant::VECTOR2: {
EditorPropertyVector2 *editor = memnew(EditorPropertyVector2);
- editor->setup(-100000, 100000, 0.001, true);
+ editor->setup(-100000, 100000, default_float_step, true);
prop = editor;
} break;
@@ -917,7 +918,7 @@ void EditorPropertyDictionary::update_property() {
} break;
case Variant::RECT2: {
EditorPropertyRect2 *editor = memnew(EditorPropertyRect2);
- editor->setup(-100000, 100000, 0.001, true);
+ editor->setup(-100000, 100000, default_float_step, true);
prop = editor;
} break;
@@ -929,7 +930,7 @@ void EditorPropertyDictionary::update_property() {
} break;
case Variant::VECTOR3: {
EditorPropertyVector3 *editor = memnew(EditorPropertyVector3);
- editor->setup(-100000, 100000, 0.001, true);
+ editor->setup(-100000, 100000, default_float_step, true);
prop = editor;
} break;
@@ -941,37 +942,37 @@ void EditorPropertyDictionary::update_property() {
} break;
case Variant::TRANSFORM2D: {
EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D);
- editor->setup(-100000, 100000, 0.001, true);
+ editor->setup(-100000, 100000, default_float_step, true);
prop = editor;
} break;
case Variant::PLANE: {
EditorPropertyPlane *editor = memnew(EditorPropertyPlane);
- editor->setup(-100000, 100000, 0.001, true);
+ editor->setup(-100000, 100000, default_float_step, true);
prop = editor;
} break;
case Variant::QUATERNION: {
EditorPropertyQuaternion *editor = memnew(EditorPropertyQuaternion);
- editor->setup(-100000, 100000, 0.001, true);
+ editor->setup(-100000, 100000, default_float_step, true);
prop = editor;
} break;
case Variant::AABB: {
EditorPropertyAABB *editor = memnew(EditorPropertyAABB);
- editor->setup(-100000, 100000, 0.001, true);
+ editor->setup(-100000, 100000, default_float_step, true);
prop = editor;
} break;
case Variant::BASIS: {
EditorPropertyBasis *editor = memnew(EditorPropertyBasis);
- editor->setup(-100000, 100000, 0.001, true);
+ editor->setup(-100000, 100000, default_float_step, true);
prop = editor;
} break;
case Variant::TRANSFORM3D: {
EditorPropertyTransform3D *editor = memnew(EditorPropertyTransform3D);
- editor->setup(-100000, 100000, 0.001, true);
+ editor->setup(-100000, 100000, default_float_step, true);
prop = editor;
} break;
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index 8783fe4fc0..e9c0b40268 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -210,126 +210,130 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
}
}
-void EditorResourcePreview::_thread() {
- exited.clear();
- while (!exit.is_set()) {
- preview_sem.wait();
- preview_mutex.lock();
-
- if (queue.size()) {
- QueueItem item = queue.front()->get();
- queue.pop_front();
-
- if (cache.has(item.path)) {
- //already has it because someone loaded it, just let it know it's ready
- String path = item.path;
- if (item.resource.is_valid()) {
- path += ":" + itos(cache[item.path].last_hash); //keep last hash (see description of what this is in condition below)
- }
-
- _preview_ready(path, cache[item.path].preview, cache[item.path].small_preview, item.id, item.function, item.userdata);
-
- preview_mutex.unlock();
- } else {
- preview_mutex.unlock();
+void EditorResourcePreview::_iterate() {
+ preview_mutex.lock();
+
+ if (queue.size()) {
+ QueueItem item = queue.front()->get();
+ queue.pop_front();
+
+ if (cache.has(item.path)) {
+ //already has it because someone loaded it, just let it know it's ready
+ String path = item.path;
+ if (item.resource.is_valid()) {
+ path += ":" + itos(cache[item.path].last_hash); //keep last hash (see description of what this is in condition below)
+ }
- Ref<ImageTexture> texture;
- Ref<ImageTexture> small_texture;
+ _preview_ready(path, cache[item.path].preview, cache[item.path].small_preview, item.id, item.function, item.userdata);
- int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
- thumbnail_size *= EDSCALE;
+ preview_mutex.unlock();
+ } else {
+ preview_mutex.unlock();
- if (item.resource.is_valid()) {
- _generate_preview(texture, small_texture, item, String());
+ Ref<ImageTexture> texture;
+ Ref<ImageTexture> small_texture;
- //adding hash to the end of path (should be ID:<objid>:<hash>) because of 5 argument limit to call_deferred
- _preview_ready(item.path + ":" + itos(item.resource->hash_edited_version()), texture, small_texture, item.id, item.function, item.userdata);
+ int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
+ thumbnail_size *= EDSCALE;
- } else {
- String temp_path = EditorPaths::get_singleton()->get_cache_dir();
- String cache_base = ProjectSettings::get_singleton()->globalize_path(item.path).md5_text();
- cache_base = temp_path.plus_file("resthumb-" + cache_base);
+ if (item.resource.is_valid()) {
+ _generate_preview(texture, small_texture, item, String());
- //does not have it, try to load a cached thumbnail
+ //adding hash to the end of path (should be ID:<objid>:<hash>) because of 5 argument limit to call_deferred
+ _preview_ready(item.path + ":" + itos(item.resource->hash_edited_version()), texture, small_texture, item.id, item.function, item.userdata);
- String file = cache_base + ".txt";
- FileAccess *f = FileAccess::open(file, FileAccess::READ);
- if (!f) {
- // No cache found, generate
- _generate_preview(texture, small_texture, item, cache_base);
- } else {
- uint64_t modtime = FileAccess::get_modified_time(item.path);
- int tsize = f->get_line().to_int();
- bool has_small_texture = f->get_line().to_int();
- uint64_t last_modtime = f->get_line().to_int();
+ } else {
+ String temp_path = EditorPaths::get_singleton()->get_cache_dir();
+ String cache_base = ProjectSettings::get_singleton()->globalize_path(item.path).md5_text();
+ cache_base = temp_path.plus_file("resthumb-" + cache_base);
- bool cache_valid = true;
+ //does not have it, try to load a cached thumbnail
- if (tsize != thumbnail_size) {
+ String file = cache_base + ".txt";
+ FileAccess *f = FileAccess::open(file, FileAccess::READ);
+ if (!f) {
+ // No cache found, generate
+ _generate_preview(texture, small_texture, item, cache_base);
+ } else {
+ uint64_t modtime = FileAccess::get_modified_time(item.path);
+ int tsize = f->get_line().to_int();
+ bool has_small_texture = f->get_line().to_int();
+ uint64_t last_modtime = f->get_line().to_int();
+
+ bool cache_valid = true;
+
+ if (tsize != thumbnail_size) {
+ cache_valid = false;
+ memdelete(f);
+ } else if (last_modtime != modtime) {
+ String last_md5 = f->get_line();
+ String md5 = FileAccess::get_md5(item.path);
+ memdelete(f);
+
+ if (last_md5 != md5) {
cache_valid = false;
- memdelete(f);
- } else if (last_modtime != modtime) {
- String last_md5 = f->get_line();
- String md5 = FileAccess::get_md5(item.path);
- memdelete(f);
- if (last_md5 != md5) {
- cache_valid = false;
+ } else {
+ //update modified time
+ f = FileAccess::open(file, FileAccess::WRITE);
+ if (!f) {
+ // Not returning as this would leave the thread hanging and would require
+ // some proper cleanup/disabling of resource preview generation.
+ ERR_PRINT("Cannot create file '" + file + "'. Check user write permissions.");
} else {
- //update modified time
-
- f = FileAccess::open(file, FileAccess::WRITE);
- if (!f) {
- // Not returning as this would leave the thread hanging and would require
- // some proper cleanup/disabling of resource preview generation.
- ERR_PRINT("Cannot create file '" + file + "'. Check user write permissions.");
- } else {
- f->store_line(itos(thumbnail_size));
- f->store_line(itos(has_small_texture));
- f->store_line(itos(modtime));
- f->store_line(md5);
- memdelete(f);
- }
+ f->store_line(itos(thumbnail_size));
+ f->store_line(itos(has_small_texture));
+ f->store_line(itos(modtime));
+ f->store_line(md5);
+ memdelete(f);
}
- } else {
- memdelete(f);
}
+ } else {
+ memdelete(f);
+ }
- if (cache_valid) {
- Ref<Image> img;
- img.instantiate();
- Ref<Image> small_img;
- small_img.instantiate();
+ if (cache_valid) {
+ Ref<Image> img;
+ img.instantiate();
+ Ref<Image> small_img;
+ small_img.instantiate();
- if (img->load(cache_base + ".png") != OK) {
- cache_valid = false;
- } else {
- texture.instantiate();
- texture->create_from_image(img);
-
- if (has_small_texture) {
- if (small_img->load(cache_base + "_small.png") != OK) {
- cache_valid = false;
- } else {
- small_texture.instantiate();
- small_texture->create_from_image(small_img);
- }
+ if (img->load(cache_base + ".png") != OK) {
+ cache_valid = false;
+ } else {
+ texture.instantiate();
+ texture->create_from_image(img);
+
+ if (has_small_texture) {
+ if (small_img->load(cache_base + "_small.png") != OK) {
+ cache_valid = false;
+ } else {
+ small_texture.instantiate();
+ small_texture->create_from_image(small_img);
}
}
}
+ }
- if (!cache_valid) {
- _generate_preview(texture, small_texture, item, cache_base);
- }
+ if (!cache_valid) {
+ _generate_preview(texture, small_texture, item, cache_base);
}
- _preview_ready(item.path, texture, small_texture, item.id, item.function, item.userdata);
}
+ _preview_ready(item.path, texture, small_texture, item.id, item.function, item.userdata);
}
-
- } else {
- preview_mutex.unlock();
}
+
+ } else {
+ preview_mutex.unlock();
+ }
+}
+
+void EditorResourcePreview::_thread() {
+ exited.clear();
+ while (!exit.is_set()) {
+ preview_sem.wait();
+ _iterate();
}
exited.set();
}
@@ -429,8 +433,12 @@ void EditorResourcePreview::check_for_invalidation(const String &p_path) {
}
void EditorResourcePreview::start() {
- ERR_FAIL_COND_MSG(thread.is_started(), "Thread already started.");
- thread.start(_thread_func, this);
+ if (OS::get_singleton()->get_render_main_thread_mode() == OS::RENDER_ANY_THREAD) {
+ ERR_FAIL_COND_MSG(thread.is_started(), "Thread already started.");
+ thread.start(_thread_func, this);
+ } else {
+ _mainthread_only = true;
+ }
}
void EditorResourcePreview::stop() {
@@ -453,3 +461,18 @@ EditorResourcePreview::EditorResourcePreview() {
EditorResourcePreview::~EditorResourcePreview() {
stop();
}
+
+void EditorResourcePreview::update() {
+ if (!_mainthread_only) {
+ return;
+ }
+
+ if (!exit.is_set()) {
+ // no need to even lock the mutex if the size is zero
+ // there is no problem if queue.size() is wrong, even if
+ // there was a race condition.
+ if (queue.size()) {
+ _iterate();
+ }
+ }
+}
diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h
index ea16c8fde0..9d1f269661 100644
--- a/editor/editor_resource_preview.h
+++ b/editor/editor_resource_preview.h
@@ -81,6 +81,11 @@ class EditorResourcePreview : public Node {
SafeFlag exit;
SafeFlag exited;
+ // when running from GLES, we want to run the previews
+ // in the main thread using an update, rather than create
+ // a separate thread
+ bool _mainthread_only = false;
+
struct Item {
Ref<Texture2D> preview;
Ref<Texture2D> small_preview;
@@ -98,6 +103,7 @@ class EditorResourcePreview : public Node {
static void _thread_func(void *ud);
void _thread();
+ void _iterate();
Vector<Ref<EditorResourcePreviewGenerator>> preview_generators;
@@ -119,6 +125,9 @@ public:
void start();
void stop();
+ // for single threaded mode
+ void update();
+
EditorResourcePreview();
~EditorResourcePreview();
};
diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp
index 8a7ec9aa82..d7daa0c750 100644
--- a/editor/editor_run.cpp
+++ b/editor/editor_run.cpp
@@ -236,7 +236,7 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L
int instances = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_instances", 1);
for (int i = 0; i < instances; i++) {
OS::ProcessID pid = 0;
- Error err = OS::get_singleton()->create_process(exec, args, &pid);
+ Error err = OS::get_singleton()->create_instance(args, &pid);
ERR_FAIL_COND_V(err, err);
pids.push_back(pid);
}
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
index ec90af1bcc..82b5ec5ca1 100644
--- a/editor/editor_spin_slider.cpp
+++ b/editor/editor_spin_slider.cpp
@@ -225,7 +225,8 @@ void EditorSpinSlider::_value_input_gui_input(const Ref<InputEvent> &p_event) {
set_value(last_value + real_step);
}
- value_input->set_text(get_text_value());
+ value_input_dirty = true;
+ set_process_internal(true);
} break;
case KEY_DOWN: {
_evaluate_input_text();
@@ -238,7 +239,8 @@ void EditorSpinSlider::_value_input_gui_input(const Ref<InputEvent> &p_event) {
set_value(last_value - real_step);
}
- value_input->set_text(get_text_value());
+ value_input_dirty = true;
+ set_process_internal(true);
} break;
}
}
@@ -424,6 +426,14 @@ void EditorSpinSlider::_notification(int p_what) {
_update_value_input_stylebox();
break;
+ case NOTIFICATION_INTERNAL_PROCESS:
+ if (value_input_dirty) {
+ value_input_dirty = false;
+ value_input->set_text(get_text_value());
+ }
+ set_process_internal(false);
+ break;
+
case NOTIFICATION_DRAW:
_draw_spin_slider();
break;
diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h
index 7e10764491..68448b3240 100644
--- a/editor/editor_spin_slider.h
+++ b/editor/editor_spin_slider.h
@@ -66,6 +66,7 @@ class EditorSpinSlider : public Range {
Popup *value_input_popup = nullptr;
LineEdit *value_input = nullptr;
bool value_input_just_closed = false;
+ bool value_input_dirty = false;
void _grabber_gui_input(const Ref<InputEvent> &p_event);
void _value_input_closed();
diff --git a/editor/editor_toaster.cpp b/editor/editor_toaster.cpp
index 9de0ea40fe..22da12b59b 100644
--- a/editor/editor_toaster.cpp
+++ b/editor/editor_toaster.cpp
@@ -172,10 +172,11 @@ void EditorToaster::_error_handler(void *p_self, const char *p_func, const char
}
}
- if (p_type == ERR_HANDLER_WARNING) {
- EditorToaster::get_singleton()->popup_str(err_str, SEVERITY_WARNING, tooltip_str);
+ Severity severity = (p_type == ERR_HANDLER_WARNING) ? SEVERITY_WARNING : SEVERITY_ERROR;
+ if (Thread::get_caller_id() != Thread::get_main_id()) {
+ EditorToaster::get_singleton()->call_deferred(SNAME("popup_str"), err_str, severity, tooltip_str);
} else {
- EditorToaster::get_singleton()->popup_str(err_str, SEVERITY_ERROR, tooltip_str);
+ EditorToaster::get_singleton()->popup_str(err_str, severity, tooltip_str);
}
}
}
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 84992892a4..b99ccc1012 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -4376,7 +4376,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
const int wireframe_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME);
const int overdraw_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW);
const int shadeless_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS);
- const String unsupported_tooltip = TTR("Not available when using the GLES2 renderer.");
+ const String unsupported_tooltip = TTR("Not available when using the OpenGL renderer.");
view_menu->get_popup()->set_item_disabled(normal_idx, true);
view_menu->get_popup()->set_item_tooltip(normal_idx, unsupported_tooltip);
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 21ef94b999..39fbd86f77 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -99,7 +99,7 @@ void TileMapEditorTilesPlugin::_update_toolbar() {
picker_button->show();
erase_button->show();
tools_settings_vsep_2->show();
- bucket_continuous_checkbox->show();
+ bucket_contiguous_checkbox->show();
random_tile_checkbox->show();
scatter_label->show();
scatter_spinbox->show();
@@ -543,9 +543,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
switch (drag_type) {
case DRAG_TYPE_PAINT: {
- Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_last_mouse_pos, mpos);
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_last_mouse_pos, mpos, drag_erasing);
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
- if (!_is_erasing() && E.value.source_id == TileSet::INVALID_SOURCE) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
Vector2i coords = E.key;
@@ -560,9 +560,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
for (int i = 0; i < line.size(); i++) {
if (!drag_modified.has(line[i])) {
- Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_continuous_checkbox->is_pressed());
+ Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_contiguous_checkbox->is_pressed(), drag_erasing);
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
- if (!_is_erasing() && E.value.source_id == TileSet::INVALID_SOURCE) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
Vector2i coords = E.key;
@@ -592,11 +592,11 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
if (mb->get_button_index() == MOUSE_BUTTON_LEFT || mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
if (mb->is_pressed()) {
- if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
- rmb_erasing = true;
+ // Pressed
+ if (erase_button->is_pressed() || mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ drag_erasing = true;
}
- // Pressed
if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) {
// Do nothing.
} else if (tool_buttons_group->get_pressed_button() == select_tool_button) {
@@ -626,9 +626,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
drag_type = DRAG_TYPE_PAINT;
drag_start_mouse_pos = mpos;
drag_modified.clear();
- Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, mpos, mpos);
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, mpos, mpos, drag_erasing);
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
- if (!_is_erasing() && E.value.source_id == TileSet::INVALID_SOURCE) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
Vector2i coords = E.key;
@@ -653,9 +653,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
for (int i = 0; i < line.size(); i++) {
if (!drag_modified.has(line[i])) {
- Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_continuous_checkbox->is_pressed());
+ Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_contiguous_checkbox->is_pressed(), drag_erasing);
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
- if (!_is_erasing() && E.value.source_id == TileSet::INVALID_SOURCE) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
Vector2i coords = E.key;
@@ -674,9 +674,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
} else {
// Released
_stop_dragging();
- if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
- rmb_erasing = false;
- }
+ drag_erasing = false;
}
CanvasItemEditor::get_singleton()->update_viewport();
@@ -730,7 +728,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
Rect2i drawn_grid_rect;
if (drag_type == DRAG_TYPE_PICK) {
- // Draw the area being picvked.
+ // Draw the area being picked.
Rect2i rect = Rect2i(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos) - tile_map->world_to_map(drag_start_mouse_pos)).abs();
rect.size += Vector2i(1, 1);
for (int x = rect.position.x; x < rect.get_end().x; x++) {
@@ -789,25 +787,25 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
bool expand_grid = false;
if (tool_buttons_group->get_pressed_button() == paint_tool_button && drag_type == DRAG_TYPE_NONE) {
// Preview for a single pattern.
- preview = _draw_line(drag_last_mouse_pos, drag_last_mouse_pos, drag_last_mouse_pos);
+ preview = _draw_line(drag_last_mouse_pos, drag_last_mouse_pos, drag_last_mouse_pos, erase_button->is_pressed());
expand_grid = true;
} else if (tool_buttons_group->get_pressed_button() == line_tool_button || drag_type == DRAG_TYPE_LINE) {
if (drag_type == DRAG_TYPE_NONE) {
// Preview for a single pattern.
- preview = _draw_line(drag_last_mouse_pos, drag_last_mouse_pos, drag_last_mouse_pos);
+ preview = _draw_line(drag_last_mouse_pos, drag_last_mouse_pos, drag_last_mouse_pos, erase_button->is_pressed());
expand_grid = true;
} else if (drag_type == DRAG_TYPE_LINE) {
// Preview for a line pattern.
- preview = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, drag_last_mouse_pos);
+ preview = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, drag_last_mouse_pos, drag_erasing);
expand_grid = true;
}
} else if (drag_type == DRAG_TYPE_RECT) {
// Preview for a rect pattern.
- preview = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos));
+ preview = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos), drag_erasing);
expand_grid = true;
} else if (tool_buttons_group->get_pressed_button() == bucket_tool_button && drag_type == DRAG_TYPE_NONE) {
// Preview for a fill pattern.
- preview = _draw_bucket_fill(tile_map->world_to_map(drag_last_mouse_pos), bucket_continuous_checkbox->is_pressed());
+ preview = _draw_bucket_fill(tile_map->world_to_map(drag_last_mouse_pos), bucket_contiguous_checkbox->is_pressed(), erase_button->is_pressed());
}
// Expand the grid if needed
@@ -855,7 +853,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
Transform2D tile_xform;
tile_xform.set_origin(tile_map->map_to_world(E.key));
tile_xform.set_scale(tile_set->get_tile_size());
- if (!_is_erasing() && random_tile_checkbox->is_pressed()) {
+ if (!(drag_erasing || erase_button->is_pressed()) && random_tile_checkbox->is_pressed()) {
tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
} else {
if (tile_set->has_source(E.value.source_id)) {
@@ -966,7 +964,7 @@ TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(Ref<TileMapPattern> p_pa
return TileMapCell();
}
-Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos) {
+Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return Map<Vector2i, TileMapCell>();
@@ -981,12 +979,12 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_
Ref<TileMapPattern> erase_pattern;
erase_pattern.instantiate();
erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- Ref<TileMapPattern> pattern = _is_erasing() ? erase_pattern : selection_pattern;
+ Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern;
Map<Vector2i, TileMapCell> output;
if (!pattern->is_empty()) {
// Paint the tiles on the tile map.
- if (!_is_erasing() && random_tile_checkbox->is_pressed()) {
+ if (!p_erase && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(p_from_mouse_pos), tile_map->world_to_map(p_to_mouse_pos));
for (int i = 0; i < line.size(); i++) {
@@ -1015,7 +1013,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_
return output;
}
-Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell) {
+Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return Map<Vector2i, TileMapCell>();
@@ -1034,7 +1032,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start
Ref<TileMapPattern> erase_pattern;
erase_pattern.instantiate();
erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- Ref<TileMapPattern> pattern = _is_erasing() ? erase_pattern : selection_pattern;
+ Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern;
Map<Vector2i, TileMapCell> err_output;
ERR_FAIL_COND_V(pattern->is_empty(), err_output);
@@ -1046,7 +1044,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start
Map<Vector2i, TileMapCell> output;
if (!pattern->is_empty()) {
- if (!_is_erasing() && random_tile_checkbox->is_pressed()) {
+ if (!p_erase && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
for (int x = 0; x < rect.size.x; x++) {
for (int y = 0; y < rect.size.y; y++) {
@@ -1074,7 +1072,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start
return output;
}
-Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous) {
+Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return Map<Vector2i, TileMapCell>();
@@ -1095,14 +1093,14 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
Ref<TileMapPattern> erase_pattern;
erase_pattern.instantiate();
erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- Ref<TileMapPattern> pattern = _is_erasing() ? erase_pattern : selection_pattern;
+ Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern;
if (!pattern->is_empty()) {
- TileMapCell source = tile_map->get_cell(tile_map_layer, p_coords);
+ TileMapCell source_cell = tile_map->get_cell(tile_map_layer, p_coords);
// If we are filling empty tiles, compute the tilemap boundaries.
Rect2i boundaries;
- if (source.source_id == TileSet::INVALID_SOURCE) {
+ if (source_cell.source_id == TileSet::INVALID_SOURCE) {
boundaries = tile_map->get_used_rect();
}
@@ -1115,11 +1113,11 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
Vector2i coords = to_check.back()->get();
to_check.pop_back();
if (!already_checked.has(coords)) {
- if (source.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) &&
- source.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
- source.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
- (source.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
- if (!_is_erasing() && random_tile_checkbox->is_pressed()) {
+ if (source_cell.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) &&
+ source_cell.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
+ source_cell.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
+ (source_cell.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
+ if (!p_erase && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
output.insert(coords, _pick_random_tile(pattern));
} else {
@@ -1146,7 +1144,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
} else {
// Replace all tiles like the source.
TypedArray<Vector2i> to_check;
- if (source.source_id == TileSet::INVALID_SOURCE) {
+ if (source_cell.source_id == TileSet::INVALID_SOURCE) {
Rect2i rect = tile_map->get_used_rect();
if (rect.has_no_area()) {
rect = Rect2i(p_coords, Vector2i(1, 1));
@@ -1161,11 +1159,11 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
}
for (int i = 0; i < to_check.size(); i++) {
Vector2i coords = to_check[i];
- if (source.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) &&
- source.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
- source.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
- (source.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
- if (!_is_erasing() && random_tile_checkbox->is_pressed()) {
+ if (source_cell.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) &&
+ source_cell.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
+ source_cell.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
+ (source_cell.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
+ if (!p_erase && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
output.insert(coords, _pick_random_tile(pattern));
} else {
@@ -1338,10 +1336,10 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
undo_redo->commit_action(false);
} break;
case DRAG_TYPE_LINE: {
- Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, mpos);
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, mpos, drag_erasing);
undo_redo->create_action(TTR("Paint tiles"));
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
- if (!_is_erasing() && E.value.source_id == TileSet::INVALID_SOURCE) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
@@ -1350,10 +1348,10 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
undo_redo->commit_action();
} break;
case DRAG_TYPE_RECT: {
- Map<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos));
+ Map<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos), drag_erasing);
undo_redo->create_action(TTR("Paint tiles"));
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
- if (!_is_erasing() && E.value.source_id == TileSet::INVALID_SOURCE) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
@@ -1473,10 +1471,6 @@ void TileMapEditorTilesPlugin::_fix_invalid_tiles_in_tile_map_selection() {
}
}
-bool TileMapEditorTilesPlugin::_is_erasing() const {
- return erase_button->is_pressed() || rmb_erasing;
-}
-
void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
@@ -2053,10 +2047,11 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
tools_settings->add_child(tools_settings_vsep_2);
// Continuous checkbox.
- bucket_continuous_checkbox = memnew(CheckBox);
- bucket_continuous_checkbox->set_flat(true);
- bucket_continuous_checkbox->set_text(TTR("Contiguous"));
- tools_settings->add_child(bucket_continuous_checkbox);
+ bucket_contiguous_checkbox = memnew(CheckBox);
+ bucket_contiguous_checkbox->set_flat(true);
+ bucket_contiguous_checkbox->set_text(TTR("Contiguous"));
+ bucket_contiguous_checkbox->set_pressed(true);
+ tools_settings->add_child(bucket_contiguous_checkbox);
// Random tile checkbox.
random_tile_checkbox = memnew(CheckBox);
@@ -2210,6 +2205,26 @@ void TileMapEditorTerrainsPlugin::_update_toolbar() {
tools_settings_vsep->show();
picker_button->show();
erase_button->show();
+ tools_settings_vsep_2->hide();
+ bucket_contiguous_checkbox->hide();
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
+ tools_settings_vsep->show();
+ picker_button->show();
+ erase_button->show();
+ tools_settings_vsep_2->hide();
+ bucket_contiguous_checkbox->hide();
+ } else if (tool_buttons_group->get_pressed_button() == rect_tool_button) {
+ tools_settings_vsep->show();
+ picker_button->show();
+ erase_button->show();
+ tools_settings_vsep_2->hide();
+ bucket_contiguous_checkbox->hide();
+ } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
+ tools_settings_vsep->show();
+ picker_button->show();
+ erase_button->show();
+ tools_settings_vsep_2->show();
+ bucket_contiguous_checkbox->show();
}
}
@@ -2321,23 +2336,224 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
// Actually paint the tiles.
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
- output[E_to_paint.key] = tile_set->get_random_tile_from_pattern(p_terrain_set, E_to_paint.value);
+ output[E_to_paint.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E_to_paint.value);
}
// Use the WFC run for the output.
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : wfc_output) {
- output[E.key] = tile_set->get_random_tile_from_pattern(p_terrain_set, E.value);
+ output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
+ }
+
+ return output;
+}
+
+Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ TileSet::TerrainsPattern terrains_pattern;
+ if (p_erase) {
+ terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else {
+ terrains_pattern = selected_terrains_pattern;
+ }
+
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell);
+ Map<Vector2i, TileSet::TerrainsPattern> to_draw;
+ for (int i = 0; i < line.size(); i++) {
+ to_draw[line[i]] = terrains_pattern;
+ }
+ return _draw_terrains(to_draw, selected_terrain_set);
+}
+
+Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ TileSet::TerrainsPattern terrains_pattern;
+ if (p_erase) {
+ terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else {
+ terrains_pattern = selected_terrains_pattern;
+ }
+
+ Rect2i rect;
+ rect.set_position(p_start_cell);
+ rect.set_end(p_end_cell);
+ rect = rect.abs();
+
+ Map<Vector2i, TileSet::TerrainsPattern> to_draw;
+ for (int x = rect.position.x; x <= rect.get_end().x; x++) {
+ for (int y = rect.position.y; y <= rect.get_end().y; y++) {
+ to_draw[Vector2i(x, y)] = terrains_pattern;
+ }
}
+ return _draw_terrains(to_draw, selected_terrain_set);
+}
+
+Set<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Set<Vector2i>();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Set<Vector2i>();
+ }
+
+ TileMapCell source_cell = tile_map->get_cell(tile_map_layer, p_coords);
+
+ TileSet::TerrainsPattern source_pattern(*tile_set, selected_terrain_set);
+ if (source_cell.source_id != TileSet::INVALID_SOURCE) {
+ TileData *tile_data = nullptr;
+ Ref<TileSetSource> source = tile_set->get_source(source_cell.source_id);
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(source_cell.get_atlas_coords(), source_cell.alternative_tile));
+ }
+ if (!tile_data) {
+ return Set<Vector2i>();
+ }
+ source_pattern = tile_data->get_terrains_pattern();
+ }
+
+ // If we are filling empty tiles, compute the tilemap boundaries.
+ Rect2i boundaries;
+ if (source_cell.source_id == TileSet::INVALID_SOURCE) {
+ boundaries = tile_map->get_used_rect();
+ }
+
+ Set<Vector2i> output;
+ if (p_contiguous) {
+ // Replace continuous tiles like the source.
+ Set<Vector2i> already_checked;
+ List<Vector2i> to_check;
+ to_check.push_back(p_coords);
+ while (!to_check.is_empty()) {
+ Vector2i coords = to_check.back()->get();
+ to_check.pop_back();
+ if (!already_checked.has(coords)) {
+ // Get the candidate cell pattern.
+ TileSet::TerrainsPattern candidate_pattern(*tile_set, selected_terrain_set);
+ if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
+ TileData *tile_data = nullptr;
+ Ref<TileSetSource> source = tile_set->get_source(tile_map->get_cell_source_id(tile_map_layer, coords));
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords)));
+ }
+ if (tile_data) {
+ candidate_pattern = tile_data->get_terrains_pattern();
+ }
+ }
+
+ // Draw.
+ if (candidate_pattern == source_pattern && (!source_pattern.is_erase_pattern() || boundaries.has_point(coords))) {
+ output.insert(coords);
+
+ // Get surrounding tiles (handles different tile shapes).
+ TypedArray<Vector2i> around = tile_map->get_surrounding_tiles(coords);
+ for (int i = 0; i < around.size(); i++) {
+ to_check.push_back(around[i]);
+ }
+ }
+ already_checked.insert(coords);
+ }
+ }
+ } else {
+ // Replace all tiles like the source.
+ TypedArray<Vector2i> to_check;
+ if (source_cell.source_id == TileSet::INVALID_SOURCE) {
+ Rect2i rect = tile_map->get_used_rect();
+ if (rect.has_no_area()) {
+ rect = Rect2i(p_coords, Vector2i(1, 1));
+ }
+ for (int x = boundaries.position.x; x < boundaries.get_end().x; x++) {
+ for (int y = boundaries.position.y; y < boundaries.get_end().y; y++) {
+ to_check.append(Vector2i(x, y));
+ }
+ }
+ } else {
+ to_check = tile_map->get_used_cells(tile_map_layer);
+ }
+ for (int i = 0; i < to_check.size(); i++) {
+ Vector2i coords = to_check[i];
+ // Get the candidate cell pattern.
+ TileSet::TerrainsPattern candidate_pattern;
+ if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
+ TileData *tile_data = nullptr;
+ Ref<TileSetSource> source = tile_set->get_source(tile_map->get_cell_source_id(tile_map_layer, coords));
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords)));
+ }
+ if (tile_data) {
+ candidate_pattern = tile_data->get_terrains_pattern();
+ }
+ }
+ // Draw.
+ if (candidate_pattern == source_pattern && (!source_pattern.is_erase_pattern() || boundaries.has_point(coords))) {
+ output.insert(coords);
+ }
+ }
+ }
return output;
}
+Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ TileSet::TerrainsPattern terrains_pattern;
+ if (p_erase) {
+ terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else {
+ terrains_pattern = selected_terrains_pattern;
+ }
+
+ Set<Vector2i> cells_to_draw = _get_cells_for_bucket_fill(p_coords, p_contiguous);
+ Map<Vector2i, TileSet::TerrainsPattern> to_draw;
+ for (const Vector2i &coords : cells_to_draw) {
+ to_draw[coords] = terrains_pattern;
+ }
+
+ return _draw_terrains(to_draw, selected_terrain_set);
+}
+
void TileMapEditorTerrainsPlugin::_stop_dragging() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return;
}
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
Vector2 mpos = xform.affine_inverse().xform(CanvasItemEditor::get_singleton()->get_viewport_control()->get_local_mouse_position());
@@ -2345,12 +2561,6 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
case DRAG_TYPE_PICK: {
Vector2i coords = tile_map->world_to_map(mpos);
TileMapCell cell = tile_map->get_cell(tile_map_layer, coords);
-
- Ref<TileSet> tile_set = tile_map->get_tileset();
- if (!tile_set.is_valid()) {
- return;
- }
-
TileData *tile_data = nullptr;
Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
@@ -2360,17 +2570,19 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
}
if (tile_data) {
- Array terrains_pattern = tile_data->get_terrains_pattern();
+ TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
// Find the tree item for the right terrain set.
bool need_tree_item_switch = true;
TreeItem *tree_item = terrains_tree->get_selected();
+ int new_terrain_set = -1;
if (tree_item) {
Dictionary metadata_dict = tree_item->get_metadata(0);
if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
int terrain_set = metadata_dict["terrain_set"];
int terrain_id = metadata_dict["terrain_id"];
if (per_terrain_terrains_patterns[terrain_set][terrain_id].has(terrains_pattern)) {
+ new_terrain_set = terrain_set;
need_tree_item_switch = false;
}
}
@@ -2384,6 +2596,7 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
int terrain_id = metadata_dict["terrain_id"];
if (per_terrain_terrains_patterns[terrain_set][terrain_id].has(terrains_pattern)) {
// Found
+ new_terrain_set = terrain_set;
tree_item->select(0);
_update_tiles_list();
break;
@@ -2396,15 +2609,9 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
if (tree_item) {
for (int i = 0; i < terrains_tile_list->get_item_count(); i++) {
Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i);
- TileSet::TerrainsPattern in_meta_terrains_pattern = metadata_dict["terrains_pattern"];
- bool equals = true;
- for (int j = 0; j < terrains_pattern.size(); j++) {
- if (terrains_pattern[j] != in_meta_terrains_pattern[j]) {
- equals = false;
- break;
- }
- }
- if (equals) {
+ TileSet::TerrainsPattern in_meta_terrains_pattern(*tile_set, new_terrain_set);
+ in_meta_terrains_pattern.set_terrains_from_array(metadata_dict["terrains_pattern"]);
+ if (in_meta_terrains_pattern == terrains_pattern) {
terrains_tile_list->select(i);
break;
}
@@ -2423,12 +2630,82 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
}
undo_redo->commit_action(false);
} break;
+ case DRAG_TYPE_LINE: {
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos), drag_erasing);
+ undo_redo->create_action(TTR("Paint terrain"));
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
+ continue;
+ }
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
+ }
+ undo_redo->commit_action();
+ } break;
+ case DRAG_TYPE_RECT: {
+ Map<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos), drag_erasing);
+ undo_redo->create_action(TTR("Paint terrain"));
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
+ continue;
+ }
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
+ }
+ undo_redo->commit_action();
+ } break;
+ case DRAG_TYPE_BUCKET: {
+ undo_redo->create_action(TTR("Paint terrain"));
+ for (const KeyValue<Vector2i, TileMapCell> &E : drag_modified) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ }
+ undo_redo->commit_action(false);
+ } break;
+
default:
break;
}
drag_type = DRAG_TYPE_NONE;
}
+void TileMapEditorTerrainsPlugin::_mouse_exited_viewport() {
+ has_mouse = false;
+ CanvasItemEditor::get_singleton()->update_viewport();
+}
+
+void TileMapEditorTerrainsPlugin::_update_selection() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ // Get the selected terrain.
+ selected_terrains_pattern = TileSet::TerrainsPattern();
+ selected_terrain_set = -1;
+
+ TreeItem *selected_tree_item = terrains_tree->get_selected();
+ if (selected_tree_item && selected_tree_item->get_metadata(0)) {
+ Dictionary metadata_dict = selected_tree_item->get_metadata(0);
+ // Selected terrain
+ selected_terrain_set = metadata_dict["terrain_set"];
+
+ // Selected tile
+ if (erase_button->is_pressed()) {
+ selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else if (terrains_tile_list->is_anything_selected()) {
+ metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]);
+ selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ selected_terrains_pattern.set_terrains_from_array(metadata_dict["terrains_pattern"]);
+ }
+ }
+}
+
bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
if (!main_vbox_container->is_visible_in_tree()) {
// If the bottom editor is not visible, we ignore inputs.
@@ -2454,46 +2731,19 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
}
ERR_FAIL_COND_V(tile_map_layer >= tile_map->get_layers_count(), false);
- // Get the selected terrain.
- TileSet::TerrainsPattern selected_terrains_pattern;
- int selected_terrain_set = -1;
-
- TreeItem *selected_tree_item = terrains_tree->get_selected();
- if (selected_tree_item && selected_tree_item->get_metadata(0)) {
- Dictionary metadata_dict = selected_tree_item->get_metadata(0);
- // Selected terrain
- selected_terrain_set = metadata_dict["terrain_set"];
-
- // Selected tile
- if (erase_button->is_pressed()) {
- selected_terrains_pattern.clear();
- for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(selected_terrain_set, side)) {
- selected_terrains_pattern.push_back(-1);
- }
- }
- } else if (terrains_tile_list->is_anything_selected()) {
- metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]);
- selected_terrains_pattern = metadata_dict["terrains_pattern"];
- }
- }
+ _update_selection();
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
+ has_mouse = true;
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
Vector2 mpos = xform.affine_inverse().xform(mm->get_position());
switch (drag_type) {
case DRAG_TYPE_PAINT: {
if (selected_terrain_set >= 0) {
- Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
- Map<Vector2i, TileSet::TerrainsPattern> to_draw;
- for (int i = 0; i < line.size(); i++) {
- to_draw[line[i]] = selected_terrains_pattern;
- }
- Map<Vector2i, TileMapCell> modified = _draw_terrains(to_draw, selected_terrain_set);
- for (const KeyValue<Vector2i, TileMapCell> &E : modified) {
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos), drag_erasing);
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
if (!drag_modified.has(E.key)) {
drag_modified[E.key] = tile_map->get_cell(tile_map_layer, E.key);
}
@@ -2512,35 +2762,79 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
+ has_mouse = true;
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
Vector2 mpos = xform.affine_inverse().xform(mb->get_position());
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MOUSE_BUTTON_LEFT || mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
if (mb->is_pressed()) {
// Pressed
+ if (erase_button->is_pressed() || mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ drag_erasing = true;
+ }
+
if (picker_button->is_pressed()) {
drag_type = DRAG_TYPE_PICK;
} else {
// Paint otherwise.
- if (selected_terrain_set >= 0 && !selected_terrains_pattern.is_empty() && tool_buttons_group->get_pressed_button() == paint_tool_button) {
+ if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(KEY_CTRL) && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ return true;
+ }
+
drag_type = DRAG_TYPE_PAINT;
drag_start_mouse_pos = mpos;
drag_modified.clear();
-
- Map<Vector2i, TileSet::TerrainsPattern> terrains_to_draw;
- terrains_to_draw[tile_map->world_to_map(mpos)] = selected_terrains_pattern;
-
- Map<Vector2i, TileMapCell> to_draw = _draw_terrains(terrains_to_draw, selected_terrain_set);
+ Vector2i cell = tile_map->world_to_map(mpos);
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(cell, cell, drag_erasing);
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
drag_modified[E.key] = tile_map->get_cell(tile_map_layer, E.key);
tile_map->set_cell(tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(KEY_SHIFT) && !Input::get_singleton()->is_key_pressed(KEY_CTRL))) {
+ if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ return true;
+ }
+ drag_type = DRAG_TYPE_LINE;
+ drag_start_mouse_pos = mpos;
+ drag_modified.clear();
+ } else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(KEY_SHIFT) && Input::get_singleton()->is_key_pressed(KEY_CTRL))) {
+ if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ return true;
+ }
+ drag_type = DRAG_TYPE_RECT;
+ drag_start_mouse_pos = mpos;
+ drag_modified.clear();
+ } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
+ if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ return true;
+ }
+ drag_type = DRAG_TYPE_BUCKET;
+ drag_start_mouse_pos = mpos;
+ drag_modified.clear();
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
+ for (int i = 0; i < line.size(); i++) {
+ if (!drag_modified.has(line[i])) {
+ Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_contiguous_checkbox->is_pressed(), drag_erasing);
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
+ continue;
+ }
+ Vector2i coords = E.key;
+ if (!drag_modified.has(coords)) {
+ drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
+ }
+ tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ }
+ }
+ }
}
}
} else {
// Released
_stop_dragging();
+ drag_erasing = false;
}
CanvasItemEditor::get_singleton()->update_viewport();
@@ -2553,6 +2847,135 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
return false;
}
+void TileMapEditorTerrainsPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ if (tile_map_layer < 0) {
+ return;
+ }
+ ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count());
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ if (!tile_map->is_visible_in_tree()) {
+ return;
+ }
+
+ Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
+ Vector2i tile_shape_size = tile_set->get_tile_size();
+
+ // Handle the preview of the tiles to be placed.
+ if (main_vbox_container->is_visible_in_tree() && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered.
+ Set<Vector2i> preview;
+ Rect2i drawn_grid_rect;
+
+ if (drag_type == DRAG_TYPE_PICK) {
+ // Draw the area being picked.
+ Vector2i coords = tile_map->world_to_map(drag_last_mouse_pos);
+ if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(coords));
+ tile_xform.set_scale(tile_shape_size);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0), false);
+ }
+ } else if (!picker_button->is_pressed() && !(drag_type == DRAG_TYPE_NONE && Input::get_singleton()->is_key_pressed(KEY_CTRL) && !Input::get_singleton()->is_key_pressed(KEY_SHIFT))) {
+ bool expand_grid = false;
+ if (tool_buttons_group->get_pressed_button() == paint_tool_button && drag_type == DRAG_TYPE_NONE) {
+ // Preview for a single tile.
+ preview.insert(tile_map->world_to_map(drag_last_mouse_pos));
+ expand_grid = true;
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button || drag_type == DRAG_TYPE_LINE) {
+ if (drag_type == DRAG_TYPE_NONE) {
+ // Preview for a single tile.
+ preview.insert(tile_map->world_to_map(drag_last_mouse_pos));
+ } else if (drag_type == DRAG_TYPE_LINE) {
+ // Preview for a line.
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos));
+ for (int i = 0; i < line.size(); i++) {
+ preview.insert(line[i]);
+ }
+ expand_grid = true;
+ }
+ } else if (drag_type == DRAG_TYPE_RECT) {
+ // Preview for a rect.
+ Rect2i rect;
+ rect.set_position(tile_map->world_to_map(drag_start_mouse_pos));
+ rect.set_end(tile_map->world_to_map(drag_last_mouse_pos));
+ rect = rect.abs();
+
+ Map<Vector2i, TileSet::TerrainsPattern> to_draw;
+ for (int x = rect.position.x; x <= rect.get_end().x; x++) {
+ for (int y = rect.position.y; y <= rect.get_end().y; y++) {
+ preview.insert(Vector2i(x, y));
+ }
+ }
+ expand_grid = true;
+ } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button && drag_type == DRAG_TYPE_NONE) {
+ // Preview for a fill.
+ preview = _get_cells_for_bucket_fill(tile_map->world_to_map(drag_last_mouse_pos), bucket_contiguous_checkbox->is_pressed());
+ }
+
+ // Expand the grid if needed
+ if (expand_grid && !preview.is_empty()) {
+ drawn_grid_rect = Rect2i(preview.front()->get(), Vector2i(1, 1));
+ for (const Vector2i &E : preview) {
+ drawn_grid_rect.expand_to(E);
+ }
+ }
+ }
+
+ if (!preview.is_empty()) {
+ const int fading = 5;
+
+ // Draw the lines of the grid behind the preview.
+ bool display_grid = EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid");
+ if (display_grid) {
+ Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
+ if (drawn_grid_rect.size.x > 0 && drawn_grid_rect.size.y > 0) {
+ drawn_grid_rect = drawn_grid_rect.grow(fading);
+ for (int x = drawn_grid_rect.position.x; x < (drawn_grid_rect.position.x + drawn_grid_rect.size.x); x++) {
+ for (int y = drawn_grid_rect.position.y; y < (drawn_grid_rect.position.y + drawn_grid_rect.size.y); y++) {
+ Vector2i pos_in_rect = Vector2i(x, y) - drawn_grid_rect.position;
+
+ // Fade out the border of the grid.
+ float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
+ float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f);
+ float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
+ float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
+ float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
+
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(Vector2(x, y)));
+ tile_xform.set_scale(tile_shape_size);
+ Color color = grid_color;
+ color.a = color.a * opacity;
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, false);
+ }
+ }
+ }
+ }
+
+ // Draw the preview.
+ for (const Vector2i &E : preview) {
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(E));
+ tile_xform.set_scale(tile_set->get_tile_size());
+ if (drag_erasing || erase_button->is_pressed()) {
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(0.0, 0.0, 0.0, 0.5), true);
+ } else {
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
+ }
+ }
+ }
+ }
+}
+
void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
@@ -2595,12 +3018,14 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
cell.alternative_tile = alternative_id;
TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
-
// Terrain bits.
- for (int i = 0; i < terrains_pattern.size(); i++) {
- int terrain = terrains_pattern[i];
- if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
- per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
+ int terrain = terrains_pattern.get_terrain(bit);
+ if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
+ per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
+ }
}
}
}
@@ -2686,8 +3111,9 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
// Count the number of matching sides/terrains.
int count = 0;
- for (int i = 0; i < E->get().size(); i++) {
- if (int(E->get()[i]) == selected_terrain_id) {
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_peering_bit_terrain(selected_terrain_set, bit) && E->get().get_terrain(bit) == selected_terrain_id) {
count++;
}
}
@@ -2733,7 +3159,7 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
terrains_tile_list->set_item_icon_region(item_index, region);
terrains_tile_list->set_item_icon_transposed(item_index, transpose);
Dictionary list_metadata_dict;
- list_metadata_dict["terrains_pattern"] = terrains_pattern;
+ list_metadata_dict["terrains_pattern"] = terrains_pattern.get_terrains_as_array();
terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
}
}
@@ -2745,6 +3171,10 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
void TileMapEditorTerrainsPlugin::_update_theme() {
paint_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
+ line_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons")));
+ rect_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Rectangle"), SNAME("EditorIcons")));
+ bucket_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Bucket"), SNAME("EditorIcons")));
+
picker_button->set_icon(main_vbox_container->get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
erase_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
}
@@ -2804,6 +3234,30 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(paint_tool_button);
+ line_tool_button = memnew(Button);
+ line_tool_button->set_flat(true);
+ line_tool_button->set_toggle_mode(true);
+ line_tool_button->set_button_group(tool_buttons_group);
+ line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", "Line", KEY_L));
+ line_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
+ tilemap_tiles_tools_buttons->add_child(line_tool_button);
+
+ rect_tool_button = memnew(Button);
+ rect_tool_button->set_flat(true);
+ rect_tool_button->set_toggle_mode(true);
+ rect_tool_button->set_button_group(tool_buttons_group);
+ rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", "Rect", KEY_R));
+ rect_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
+ tilemap_tiles_tools_buttons->add_child(rect_tool_button);
+
+ bucket_tool_button = memnew(Button);
+ bucket_tool_button->set_flat(true);
+ bucket_tool_button->set_toggle_mode(true);
+ bucket_tool_button->set_button_group(tool_buttons_group);
+ bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", "Bucket", KEY_B));
+ bucket_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
+ tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
+
toolbar->add_child(tilemap_tiles_tools_buttons);
// -- TileMap tool settings --
@@ -2828,6 +3282,17 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", "Eraser", KEY_E));
erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(erase_button);
+
+ // Separator 2.
+ tools_settings_vsep_2 = memnew(VSeparator);
+ tools_settings->add_child(tools_settings_vsep_2);
+
+ // Continuous checkbox.
+ bucket_contiguous_checkbox = memnew(CheckBox);
+ bucket_contiguous_checkbox->set_flat(true);
+ bucket_contiguous_checkbox->set_text(TTR("Contiguous"));
+ bucket_contiguous_checkbox->set_pressed(true);
+ tools_settings->add_child(bucket_contiguous_checkbox);
}
TileMapEditorTerrainsPlugin::~TileMapEditorTerrainsPlugin() {
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index 0513a7b365..f462119727 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -75,14 +75,15 @@ private:
Button *line_tool_button;
Button *rect_tool_button;
Button *bucket_tool_button;
- Button *picker_button;
HBoxContainer *tools_settings;
+
VSeparator *tools_settings_vsep;
+ Button *picker_button;
Button *erase_button;
- CheckBox *bucket_continuous_checkbox;
VSeparator *tools_settings_vsep_2;
+ CheckBox *bucket_contiguous_checkbox;
CheckBox *random_tile_checkbox;
float scattering = 0.0;
Label *scatter_label;
@@ -108,17 +109,16 @@ private:
DRAG_TYPE_CLIPBOARD_PASTE,
};
DragType drag_type = DRAG_TYPE_NONE;
+ bool drag_erasing = false;
Vector2 drag_start_mouse_pos;
Vector2 drag_last_mouse_pos;
Map<Vector2i, TileMapCell> drag_modified;
- bool rmb_erasing = false;
TileMapCell _pick_random_tile(Ref<TileMapPattern> p_pattern);
- Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos);
- Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell);
- Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous);
+ Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase);
+ Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
+ Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
void _stop_dragging();
- bool _is_erasing() const;
///// Selection system. /////
Set<Vector2i> tile_map_selection;
@@ -220,32 +220,53 @@ private:
Ref<ButtonGroup> tool_buttons_group;
Button *paint_tool_button;
+ Button *line_tool_button;
+ Button *rect_tool_button;
+ Button *bucket_tool_button;
HBoxContainer *tools_settings;
+
VSeparator *tools_settings_vsep;
Button *picker_button;
Button *erase_button;
+ VSeparator *tools_settings_vsep_2;
+ CheckBox *bucket_contiguous_checkbox;
void _update_toolbar();
// Main vbox.
VBoxContainer *main_vbox_container;
// TileMap editing.
+ bool has_mouse = false;
+ void _mouse_exited_viewport();
+
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_PAINT,
+ DRAG_TYPE_LINE,
+ DRAG_TYPE_RECT,
+ DRAG_TYPE_BUCKET,
DRAG_TYPE_PICK,
};
DragType drag_type = DRAG_TYPE_NONE;
+ bool drag_erasing = false;
Vector2 drag_start_mouse_pos;
Vector2 drag_last_mouse_pos;
Map<Vector2i, TileMapCell> drag_modified;
// Painting
Map<Vector2i, TileMapCell> _draw_terrains(const Map<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const;
+ Map<Vector2i, TileMapCell> _draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
+ Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
+ Set<Vector2i> _get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous);
+ Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
void _stop_dragging();
+ int selected_terrain_set = -1;
+ TileSet::TerrainsPattern selected_terrains_pattern;
+ void _update_selection();
+
// Bottom panel.
Tree *terrains_tree;
ItemList *terrains_tile_list;
@@ -265,7 +286,7 @@ private:
public:
virtual Vector<TabData> get_tabs() const override;
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
- //virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
+ virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
TileMapEditorTerrainsPlugin();
~TileMapEditorTerrainsPlugin();
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index 0fbb9a98c7..915ce50836 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -41,10 +41,10 @@
TileSetEditor *TileSetEditor::singleton = nullptr;
-void TileSetEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+void TileSetEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
ERR_FAIL_COND(!tile_set.is_valid());
- if (!can_drop_data_fw(p_point, p_data, p_from)) {
+ if (!_can_drop_data_fw(p_point, p_data, p_from)) {
return;
}
@@ -81,7 +81,7 @@ void TileSetEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, C
}
}
-bool TileSetEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+bool TileSetEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
ERR_FAIL_COND_V(!tile_set.is_valid(), false);
if (p_from == sources_list) {
@@ -608,8 +608,8 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p
}
void TileSetEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TileSetEditor::can_drop_data_fw);
- ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TileSetEditor::drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TileSetEditor::_can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TileSetEditor::_drop_data_fw);
}
void TileSetEditor::edit(Ref<TileSet> p_tile_set) {
diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h
index cda38760cf..58312ce3df 100644
--- a/editor/plugins/tiles/tile_set_editor.h
+++ b/editor/plugins/tiles/tile_set_editor.h
@@ -59,6 +59,9 @@ private:
UndoRedo *undo_redo = EditorNode::get_undo_redo();
+ void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+ bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+
void _update_sources_list(int force_selected_id = -1);
// Sources management.
@@ -98,9 +101,6 @@ public:
void edit(Ref<TileSet> p_tile_set);
- void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
- bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
-
TileSetEditor();
~TileSetEditor();
};
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
index dc26d380b8..d687d9651d 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -385,8 +385,8 @@ void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetS
_update_tile_inspector();
}
-void TileSetScenesCollectionSourceEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
- if (!can_drop_data_fw(p_point, p_data, p_from)) {
+void TileSetScenesCollectionSourceEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+ if (!_can_drop_data_fw(p_point, p_data, p_from)) {
return;
}
@@ -412,7 +412,7 @@ void TileSetScenesCollectionSourceEditor::drop_data_fw(const Point2 &p_point, co
}
}
-bool TileSetScenesCollectionSourceEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+bool TileSetScenesCollectionSourceEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
if (p_from == scene_tiles_list) {
Dictionary d = p_data;
@@ -447,8 +447,8 @@ void TileSetScenesCollectionSourceEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("source_id_changed", PropertyInfo(Variant::INT, "source_id")));
ClassDB::bind_method(D_METHOD("_scene_thumbnail_done"), &TileSetScenesCollectionSourceEditor::_scene_thumbnail_done);
- ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &TileSetScenesCollectionSourceEditor::can_drop_data_fw);
- ClassDB::bind_method(D_METHOD("drop_data_fw"), &TileSetScenesCollectionSourceEditor::drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TileSetScenesCollectionSourceEditor::_can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TileSetScenesCollectionSourceEditor::_drop_data_fw);
}
TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
index 3be7bee714..4e33128be5 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
@@ -125,14 +125,15 @@ private:
void _update_scenes_list();
void _update_action_buttons();
+ void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+ bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id);
- void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
- bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
TileSetScenesCollectionSourceEditor();
~TileSetScenesCollectionSourceEditor();
};
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 6d023b8a7d..c06eafae50 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -104,7 +104,6 @@ void VisualShaderGraphPlugin::_bind_methods() {
ClassDB::bind_method("connect_nodes", &VisualShaderGraphPlugin::connect_nodes);
ClassDB::bind_method("disconnect_nodes", &VisualShaderGraphPlugin::disconnect_nodes);
ClassDB::bind_method("set_node_position", &VisualShaderGraphPlugin::set_node_position);
- ClassDB::bind_method("set_node_size", &VisualShaderGraphPlugin::set_node_size);
ClassDB::bind_method("update_node", &VisualShaderGraphPlugin::update_node);
ClassDB::bind_method("update_node_deferred", &VisualShaderGraphPlugin::update_node_deferred);
ClassDB::bind_method("set_input_port_default_value", &VisualShaderGraphPlugin::set_input_port_default_value);
@@ -292,12 +291,6 @@ void VisualShaderGraphPlugin::set_node_position(VisualShader::Type p_type, int p
}
}
-void VisualShaderGraphPlugin::set_node_size(VisualShader::Type p_type, int p_id, const Vector2 &p_size) {
- if (visual_shader->get_shader_type() == p_type && links.has(p_id)) {
- links[p_id].graph_node->set_size(p_size);
- }
-}
-
bool VisualShaderGraphPlugin::is_preview_visible(int p_id) const {
return links[p_id].preview_visible;
}
@@ -1047,7 +1040,6 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) {
hide();
} else {
if (changed) { // to avoid tree collapse
- _clear_buffer();
_update_options_menu();
_update_preview();
_update_graph();
@@ -1246,7 +1238,7 @@ void VisualShaderEditor::_update_options_menu() {
Color unsupported_color = get_theme_color(SNAME("error_color"), SNAME("Editor"));
Color supported_color = get_theme_color(SNAME("warning_color"), SNAME("Editor"));
- static bool low_driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "GLES2";
+ static bool low_driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "opengl3";
Map<String, TreeItem *> folders;
@@ -2765,9 +2757,6 @@ void VisualShaderEditor::_delete_nodes(int p_type, const List<int> &p_nodes) {
undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, F), F);
undo_redo->add_undo_method(graph_plugin.ptr(), "add_node", type, F);
- undo_redo->add_do_method(this, "_clear_buffer");
- undo_redo->add_undo_method(this, "_clear_buffer");
-
// restore size, inputs and outputs if node is group
VisualShaderNodeGroupBase *group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr());
if (group) {
@@ -3103,13 +3092,15 @@ void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
selected_float_constant = -1;
}
- if (to_change.is_empty() && copy_nodes_buffer.is_empty()) {
+ if (to_change.is_empty() && copy_items_buffer.is_empty()) {
_show_members_dialog(true);
} else {
+ popup_menu->set_item_disabled(NodeMenuOptions::CUT, to_change.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::COPY, to_change.is_empty());
- popup_menu->set_item_disabled(NodeMenuOptions::PASTE, copy_nodes_buffer.is_empty());
+ popup_menu->set_item_disabled(NodeMenuOptions::PASTE, copy_items_buffer.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::DELETE, to_change.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::DUPLICATE, to_change.is_empty());
+ popup_menu->set_item_disabled(NodeMenuOptions::CLEAR_COPY_BUFFER, copy_items_buffer.is_empty());
int temp = popup_menu->get_item_index(NodeMenuOptions::SEPARATOR2);
if (temp != -1) {
@@ -3329,69 +3320,88 @@ void VisualShaderEditor::_node_changed(int p_id) {
}
}
-void VisualShaderEditor::_dup_update_excluded(int p_type, Set<int> &r_excluded) {
- r_excluded.clear();
- VisualShader::Type type = (VisualShader::Type)p_type;
-
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gn) {
- int id = String(gn->get_name()).to_int();
- Ref<VisualShaderNode> node = visual_shader->get_node(type, id);
- Ref<VisualShaderNodeOutput> output = node;
- if (output.is_valid()) {
- r_excluded.insert(id);
- continue;
- }
- r_excluded.insert(id);
- }
- }
-}
-
-void VisualShaderEditor::_dup_copy_nodes(int p_type, List<int> &r_nodes, Set<int> &r_excluded) {
+void VisualShaderEditor::_dup_copy_nodes(int p_type, List<CopyItem> &r_items, List<VisualShader::Connection> &r_connections) {
VisualShader::Type type = (VisualShader::Type)p_type;
selection_center.x = 0.0f;
selection_center.y = 0.0f;
+ Set<int> nodes;
+
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
if (gn) {
int id = String(gn->get_name()).to_int();
+
Ref<VisualShaderNode> node = visual_shader->get_node(type, id);
Ref<VisualShaderNodeOutput> output = node;
if (output.is_valid()) { // can't duplicate output
- r_excluded.insert(id);
continue;
}
+
if (node.is_valid() && gn->is_selected()) {
Vector2 pos = visual_shader->get_node_position(type, id);
selection_center += pos;
- r_nodes.push_back(id);
+
+ CopyItem item;
+ item.id = id;
+ item.node = visual_shader->get_node(type, id)->duplicate();
+ item.position = visual_shader->get_node_position(type, id);
+
+ Ref<VisualShaderNodeResizableBase> resizable_base = node;
+ if (resizable_base.is_valid()) {
+ item.size = resizable_base->get_size();
+ }
+
+ Ref<VisualShaderNodeGroupBase> group = node;
+ if (group.is_valid()) {
+ item.group_inputs = group->get_inputs();
+ item.group_outputs = group->get_outputs();
+ }
+
+ Ref<VisualShaderNodeExpression> expression = node;
+ if (expression.is_valid()) {
+ item.expression = expression->get_expression();
+ }
+
+ r_items.push_back(item);
+
+ nodes.insert(id);
}
- r_excluded.insert(id);
}
}
- selection_center /= (float)r_nodes.size();
+ List<VisualShader::Connection> connections;
+ visual_shader->get_node_connections(type, &connections);
+
+ for (const VisualShader::Connection &E : connections) {
+ if (nodes.has(E.from_node) && nodes.has(E.to_node)) {
+ r_connections.push_back(E);
+ }
+ }
+
+ selection_center /= (float)r_items.size();
}
-void VisualShaderEditor::_dup_paste_nodes(int p_type, int p_pasted_type, List<int> &r_nodes, Set<int> &r_excluded, const Vector2 &p_offset, bool p_select) {
+void VisualShaderEditor::_dup_paste_nodes(int p_type, List<CopyItem> &r_items, const List<VisualShader::Connection> &p_connections, const Vector2 &p_offset, bool p_duplicate) {
+ if (p_duplicate) {
+ undo_redo->create_action(TTR("Duplicate VisualShader Node(s)"));
+ } else {
+ undo_redo->create_action(TTR("Paste VisualShader Node(s)"));
+ }
+
VisualShader::Type type = (VisualShader::Type)p_type;
- VisualShader::Type pasted_type = (VisualShader::Type)p_pasted_type;
int base_id = visual_shader->get_valid_node_id(type);
int id_from = base_id;
Map<int, int> connection_remap;
Set<int> unsupported_set;
+ Set<int> added_set;
- for (int &E : r_nodes) {
- connection_remap[E] = id_from;
- Ref<VisualShaderNode> node = visual_shader->get_node(pasted_type, E);
-
+ for (CopyItem &item : r_items) {
bool unsupported = false;
for (int i = 0; i < add_options.size(); i++) {
- if (add_options[i].type == node->get_class_name()) {
+ if (add_options[i].type == item.node->get_class_name()) {
if (!_is_available(add_options[i].mode)) {
unsupported = true;
}
@@ -3399,48 +3409,47 @@ void VisualShaderEditor::_dup_paste_nodes(int p_type, int p_pasted_type, List<in
}
}
if (unsupported) {
- unsupported_set.insert(E);
+ unsupported_set.insert(item.id);
continue;
}
+ connection_remap[item.id] = id_from;
+ Ref<VisualShaderNode> node = item.node->duplicate();
- Ref<VisualShaderNode> dupli = node->duplicate();
-
- undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, dupli, visual_shader->get_node_position(pasted_type, E) + p_offset, id_from);
- undo_redo->add_do_method(graph_plugin.ptr(), "add_node", type, id_from);
+ Ref<VisualShaderNodeResizableBase> resizable_base = Object::cast_to<VisualShaderNodeResizableBase>(node.ptr());
+ if (resizable_base.is_valid()) {
+ undo_redo->add_do_method(node.ptr(), "set_size", item.size);
+ }
- // duplicate size, inputs and outputs if node is group
Ref<VisualShaderNodeGroupBase> group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr());
- if (!group.is_null()) {
- undo_redo->add_do_method(dupli.ptr(), "set_size", group->get_size());
- undo_redo->add_do_method(graph_plugin.ptr(), "set_node_size", type, id_from, group->get_size());
- undo_redo->add_do_method(dupli.ptr(), "set_inputs", group->get_inputs());
- undo_redo->add_do_method(dupli.ptr(), "set_outputs", group->get_outputs());
+ if (group.is_valid()) {
+ undo_redo->add_do_method(node.ptr(), "set_inputs", item.group_inputs);
+ undo_redo->add_do_method(node.ptr(), "set_outputs", item.group_outputs);
}
- // duplicate expression text if node is expression
+
Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr());
- if (!expression.is_null()) {
- undo_redo->add_do_method(dupli.ptr(), "set_expression", expression->get_expression());
+ if (expression.is_valid()) {
+ undo_redo->add_do_method(node.ptr(), "set_expression", item.expression);
}
+ undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, node, item.position + p_offset, id_from);
+ undo_redo->add_do_method(graph_plugin.ptr(), "add_node", type, id_from);
+
+ added_set.insert(id_from);
id_from++;
}
- List<VisualShader::Connection> conns;
- visual_shader->get_node_connections(pasted_type, &conns);
-
- for (const VisualShader::Connection &E : conns) {
+ for (const VisualShader::Connection &E : p_connections) {
if (unsupported_set.has(E.from_node) || unsupported_set.has(E.to_node)) {
continue;
}
- if (connection_remap.has(E.from_node) && connection_remap.has(E.to_node)) {
- undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, connection_remap[E.from_node], E.from_port, connection_remap[E.to_node], E.to_port);
- undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, connection_remap[E.from_node], E.from_port, connection_remap[E.to_node], E.to_port);
- undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, connection_remap[E.from_node], E.from_port, connection_remap[E.to_node], E.to_port);
- }
+
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, connection_remap[E.from_node], E.from_port, connection_remap[E.to_node], E.to_port);
+ undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, connection_remap[E.from_node], E.from_port, connection_remap[E.to_node], E.to_port);
+ undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, connection_remap[E.from_node], E.from_port, connection_remap[E.to_node], E.to_port);
}
id_from = base_id;
- for (int i = 0; i < r_nodes.size(); i++) {
+ for (int i = 0; i < r_items.size(); i++) {
undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_from);
undo_redo->add_undo_method(graph_plugin.ptr(), "remove_node", type, id_from);
id_from++;
@@ -3448,54 +3457,61 @@ void VisualShaderEditor::_dup_paste_nodes(int p_type, int p_pasted_type, List<in
undo_redo->commit_action();
- if (p_select) {
- // reselect duplicated nodes by excluding the other ones
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gn) {
- int id = String(gn->get_name()).to_int();
- if (!r_excluded.has(id)) {
- gn->set_selected(true);
- } else {
- gn->set_selected(false);
- }
+ // reselect nodes by excluding the other ones
+ for (int i = 0; i < graph->get_child_count(); i++) {
+ GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
+ if (gn) {
+ int id = String(gn->get_name()).to_int();
+ if (added_set.has(id)) {
+ gn->set_selected(true);
+ } else {
+ gn->set_selected(false);
}
}
}
}
-void VisualShaderEditor::_clear_buffer() {
- copy_nodes_buffer.clear();
- copy_nodes_excluded_buffer.clear();
+void VisualShaderEditor::_clear_copy_buffer() {
+ copy_items_buffer.clear();
+ copy_connections_buffer.clear();
}
void VisualShaderEditor::_duplicate_nodes() {
int type = get_current_shader_type();
- List<int> nodes;
- Set<int> excluded;
+ List<CopyItem> items;
+ List<VisualShader::Connection> connections;
- _dup_copy_nodes(type, nodes, excluded);
+ _dup_copy_nodes(type, items, connections);
- if (nodes.is_empty()) {
+ if (items.is_empty()) {
return;
}
- undo_redo->create_action(TTR("Duplicate VisualShader Node(s)"));
-
- _dup_paste_nodes(type, type, nodes, excluded, Vector2(10, 10) * EDSCALE, true);
+ _dup_paste_nodes(type, items, connections, Vector2(10, 10) * EDSCALE, true);
}
-void VisualShaderEditor::_copy_nodes() {
- copy_type = get_current_shader_type();
+void VisualShaderEditor::_copy_nodes(bool p_cut) {
+ _clear_copy_buffer();
- _clear_buffer();
+ _dup_copy_nodes(get_current_shader_type(), copy_items_buffer, copy_connections_buffer);
- _dup_copy_nodes(copy_type, copy_nodes_buffer, copy_nodes_excluded_buffer);
+ if (p_cut) {
+ undo_redo->create_action(TTR("Cut VisualShader Node(s)"));
+
+ List<int> ids;
+ for (const CopyItem &E : copy_items_buffer) {
+ ids.push_back(E.id);
+ }
+
+ _delete_nodes(get_current_shader_type(), ids);
+
+ undo_redo->commit_action();
+ }
}
void VisualShaderEditor::_paste_nodes(bool p_use_custom_position, const Vector2 &p_custom_position) {
- if (copy_nodes_buffer.is_empty()) {
+ if (copy_items_buffer.is_empty()) {
return;
}
@@ -3510,11 +3526,7 @@ void VisualShaderEditor::_paste_nodes(bool p_use_custom_position, const Vector2
mpos = graph->get_local_mouse_position();
}
- undo_redo->create_action(TTR("Paste VisualShader Node(s)"));
-
- _dup_paste_nodes(type, copy_type, copy_nodes_buffer, copy_nodes_excluded_buffer, (graph->get_scroll_ofs() / scale + mpos / scale - selection_center), false);
-
- _dup_update_excluded(type, copy_nodes_excluded_buffer); // to prevent selection of previous copies at new paste
+ _dup_paste_nodes(type, copy_items_buffer, copy_connections_buffer, graph->get_scroll_ofs() / scale + mpos / scale - selection_center, false);
}
void VisualShaderEditor::_mode_selected(int p_id) {
@@ -3540,6 +3552,8 @@ void VisualShaderEditor::_mode_selected(int p_id) {
visual_shader->set_shader_type(VisualShader::Type(p_id + offset));
_update_options_menu();
_update_graph();
+
+ graph->grab_focus();
}
void VisualShaderEditor::_custom_mode_toggled(bool p_enabled) {
@@ -3742,8 +3756,11 @@ void VisualShaderEditor::_node_menu_id_pressed(int p_idx) {
case NodeMenuOptions::ADD:
_show_members_dialog(true);
break;
+ case NodeMenuOptions::CUT:
+ _copy_nodes(true);
+ break;
case NodeMenuOptions::COPY:
- _copy_nodes();
+ _copy_nodes(false);
break;
case NodeMenuOptions::PASTE:
_paste_nodes(true, menu_point);
@@ -3754,6 +3771,9 @@ void VisualShaderEditor::_node_menu_id_pressed(int p_idx) {
case NodeMenuOptions::DUPLICATE:
_duplicate_nodes();
break;
+ case NodeMenuOptions::CLEAR_COPY_BUFFER:
+ _clear_copy_buffer();
+ break;
case NodeMenuOptions::CONVERT_CONSTANTS_TO_UNIFORMS:
_convert_constants_to_uniforms(false);
break;
@@ -3968,7 +3988,7 @@ void VisualShaderEditor::_bind_methods() {
ClassDB::bind_method("_input_select_item", &VisualShaderEditor::_input_select_item);
ClassDB::bind_method("_uniform_select_item", &VisualShaderEditor::_uniform_select_item);
ClassDB::bind_method("_set_node_size", &VisualShaderEditor::_set_node_size);
- ClassDB::bind_method("_clear_buffer", &VisualShaderEditor::_clear_buffer);
+ ClassDB::bind_method("_clear_copy_buffer", &VisualShaderEditor::_clear_copy_buffer);
ClassDB::bind_method("_update_uniforms", &VisualShaderEditor::_update_uniforms);
ClassDB::bind_method("_set_mode", &VisualShaderEditor::_set_mode);
ClassDB::bind_method("_nodes_dragged", &VisualShaderEditor::_nodes_dragged);
@@ -4022,7 +4042,7 @@ VisualShaderEditor::VisualShaderEditor() {
graph->connect("node_selected", callable_mp(this, &VisualShaderEditor::_node_selected));
graph->connect("scroll_offset_changed", callable_mp(this, &VisualShaderEditor::_scroll_changed));
graph->connect("duplicate_nodes_request", callable_mp(this, &VisualShaderEditor::_duplicate_nodes));
- graph->connect("copy_nodes_request", callable_mp(this, &VisualShaderEditor::_copy_nodes));
+ graph->connect("copy_nodes_request", callable_mp(this, &VisualShaderEditor::_copy_nodes), varray(false));
graph->connect("paste_nodes_request", callable_mp(this, &VisualShaderEditor::_paste_nodes), varray(false, Point2()));
graph->connect("delete_nodes_request", callable_mp(this, &VisualShaderEditor::_delete_nodes_request));
graph->connect("gui_input", callable_mp(this, &VisualShaderEditor::_graph_gui_input));
@@ -4148,10 +4168,12 @@ VisualShaderEditor::VisualShaderEditor() {
add_child(popup_menu);
popup_menu->add_item(TTR("Add Node"), NodeMenuOptions::ADD);
popup_menu->add_separator();
+ popup_menu->add_item(TTR("Cut"), NodeMenuOptions::CUT);
popup_menu->add_item(TTR("Copy"), NodeMenuOptions::COPY);
popup_menu->add_item(TTR("Paste"), NodeMenuOptions::PASTE);
popup_menu->add_item(TTR("Delete"), NodeMenuOptions::DELETE);
popup_menu->add_item(TTR("Duplicate"), NodeMenuOptions::DUPLICATE);
+ popup_menu->add_item(TTR("Clear Copy Buffer"), NodeMenuOptions::CLEAR_COPY_BUFFER);
popup_menu->connect("id_pressed", callable_mp(this, &VisualShaderEditor::_node_menu_id_pressed));
///////////////////////////////////////
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index 58bbcb7113..c4a392469b 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -111,7 +111,6 @@ public:
void disconnect_nodes(VisualShader::Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
void show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id);
void set_node_position(VisualShader::Type p_type, int p_id, const Vector2 &p_position);
- void set_node_size(VisualShader::Type p_type, int p_id, const Vector2 &p_size);
void refresh_node_ports(VisualShader::Type p_type, int p_node);
void set_input_port_default_value(VisualShader::Type p_type, int p_node_id, int p_port_id, Variant p_value);
void update_uniform_refs();
@@ -217,10 +216,12 @@ class VisualShaderEditor : public VBoxContainer {
enum NodeMenuOptions {
ADD,
SEPARATOR, // ignore
+ CUT,
COPY,
PASTE,
DELETE,
DUPLICATE,
+ CLEAR_COPY_BUFFER,
SEPARATOR2, // ignore
FLOAT_CONSTANTS,
CONVERT_CONSTANTS_TO_UNIFORMS,
@@ -380,19 +381,27 @@ class VisualShaderEditor : public VBoxContainer {
void _port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output);
- void _dup_copy_nodes(int p_type, List<int> &r_nodes, Set<int> &r_excluded);
- void _dup_update_excluded(int p_type, Set<int> &r_excluded);
- void _dup_paste_nodes(int p_type, int p_pasted_type, List<int> &r_nodes, Set<int> &r_excluded, const Vector2 &p_offset, bool p_select);
+ struct CopyItem {
+ int id;
+ Ref<VisualShaderNode> node;
+ Vector2 position;
+ Vector2 size;
+ String group_inputs;
+ String group_outputs;
+ String expression;
+ };
+
+ void _dup_copy_nodes(int p_type, List<CopyItem> &r_nodes, List<VisualShader::Connection> &r_connections);
+ void _dup_paste_nodes(int p_type, List<CopyItem> &r_items, const List<VisualShader::Connection> &p_connections, const Vector2 &p_offset, bool p_duplicate);
void _duplicate_nodes();
Vector2 selection_center;
- int copy_type; // shader type
- List<int> copy_nodes_buffer;
- Set<int> copy_nodes_excluded_buffer;
+ List<CopyItem> copy_items_buffer;
+ List<VisualShader::Connection> copy_connections_buffer;
- void _clear_buffer();
- void _copy_nodes();
+ void _clear_copy_buffer();
+ void _copy_nodes(bool p_cut);
void _paste_nodes(bool p_use_custom_position = false, const Vector2 &p_custom_position = Vector2());
Vector<Ref<VisualShaderNodePlugin>> plugins;
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index e8fd3070c2..150dde79e5 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1297,8 +1297,7 @@ void ProjectList::update_dock_menu() {
void ProjectList::_global_menu_new_window(const Variant &p_tag) {
List<String> args;
args.push_back("-p");
- String exec = OS::get_singleton()->get_executable_path();
- OS::get_singleton()->create_process(exec, args);
+ OS::get_singleton()->create_instance(args);
}
void ProjectList::_global_menu_open_project(const Variant &p_tag) {
@@ -1308,8 +1307,7 @@ void ProjectList::_global_menu_open_project(const Variant &p_tag) {
String conf = _projects[idx].path.plus_file("project.godot");
List<String> args;
args.push_back(conf);
- String exec = OS::get_singleton()->get_executable_path();
- OS::get_singleton()->create_process(exec, args);
+ OS::get_singleton()->create_instance(args);
}
}
@@ -2055,8 +2053,7 @@ void ProjectManager::_open_selected_projects() {
args.push_back("--single-window");
}
- String exec = OS::get_singleton()->get_executable_path();
- Error err = OS::get_singleton()->create_process(exec, args);
+ Error err = OS::get_singleton()->create_instance(args);
ERR_FAIL_COND(err);
}
@@ -2141,8 +2138,7 @@ void ProjectManager::_run_project_confirm() {
args.push_back("--disable-crash-handler");
}
- String exec = OS::get_singleton()->get_executable_path();
- Error err = OS::get_singleton()->create_process(exec, args);
+ Error err = OS::get_singleton()->create_instance(args);
ERR_FAIL_COND(err);
}
}
@@ -2271,8 +2267,7 @@ void ProjectManager::_language_selected(int p_id) {
void ProjectManager::_restart_confirm() {
List<String> args = OS::get_singleton()->get_cmdline_args();
- String exec = OS::get_singleton()->get_executable_path();
- Error err = OS::get_singleton()->create_process(exec, args);
+ Error err = OS::get_singleton()->create_instance(args);
ERR_FAIL_COND(err);
_dim_window();