summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/SCsub21
-rw-r--r--editor/code_editor.h2
-rw-r--r--editor/editor_command_palette.cpp10
-rw-r--r--editor/editor_data.h2
-rw-r--r--editor/editor_help.h3
-rw-r--r--editor/editor_inspector.cpp35
-rw-r--r--editor/editor_inspector.h10
-rw-r--r--editor/editor_log.cpp6
-rw-r--r--editor/editor_log.h2
-rw-r--r--editor/editor_node.cpp150
-rw-r--r--editor/editor_node.h22
-rw-r--r--editor/editor_path.h1
-rw-r--r--editor/editor_settings.cpp1
-rw-r--r--editor/editor_themes.cpp39
-rw-r--r--editor/editor_toaster.cpp513
-rw-r--r--editor/editor_toaster.h116
-rw-r--r--editor/filesystem_dock.h4
-rw-r--r--editor/groups_editor.cpp100
-rw-r--r--editor/groups_editor.h15
-rw-r--r--editor/icons/Notification.svg1
-rw-r--r--editor/icons/NotificationDisabled.svg1
-rw-r--r--editor/icons/TabBar.svg (renamed from editor/icons/Tabs.svg)0
-rw-r--r--editor/import/resource_importer_layered_texture.h2
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.h1
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.h2
-rw-r--r--editor/plugins/animation_player_editor_plugin.h2
-rw-r--r--editor/plugins/asset_library_editor_plugin.h1
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h10
-rw-r--r--editor/plugins/editor_preview_plugins.h18
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp10
-rw-r--r--editor/plugins/node_3d_editor_plugin.h2
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.h1
-rw-r--r--editor/plugins/script_editor_plugin.h4
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.h2
-rw-r--r--editor/plugins/theme_editor_plugin.cpp6
-rw-r--r--editor/plugins/theme_editor_plugin.h5
-rw-r--r--editor/plugins/tiles/atlas_merging_dialog.cpp1
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp609
-rw-r--r--editor/plugins/tiles/tile_map_editor.h62
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp27
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp108
-rw-r--r--editor/plugins/tiles/tile_set_editor.h16
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp28
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.h1
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp311
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.h61
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp21
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h2
-rw-r--r--editor/project_settings_editor.h2
-rw-r--r--editor/rename_dialog.cpp2
-rw-r--r--editor/rename_dialog.h2
-rw-r--r--editor/reparent_dialog.h2
-rw-r--r--editor/scene_tree_dock.h2
-rw-r--r--editor/scene_tree_editor.h3
-rw-r--r--editor/translations/Makefile21
55 files changed, 1740 insertions, 661 deletions
diff --git a/editor/SCsub b/editor/SCsub
index e8d417319a..6b18eeae93 100644
--- a/editor/SCsub
+++ b/editor/SCsub
@@ -5,7 +5,6 @@ Import("env")
env.editor_sources = []
import os
-import os.path
import glob
import editor_builders
@@ -59,7 +58,7 @@ if env["tools"]:
else:
docs += Glob(d + "/*.xml") # Custom.
- _make_doc_data_class_path(os.path.join(env.Dir("#").abspath, "editor"))
+ _make_doc_data_class_path(env.Dir("#editor").abspath)
docs = sorted(docs)
env.Depends("#editor/doc_data_compressed.gen.h", docs)
@@ -69,10 +68,17 @@ if env["tools"]:
env.Run(editor_builders.make_doc_header, "Generating documentation header."),
)
- path = env.Dir(".").abspath
+ # Editor interface and class reference translations incur a significant size
+ # cost for the editor binary (see godot-proposals#3421).
+ # To limit it, we only include translations with a high enough completion
+ # ratio (30% for the editor UI, 10% for the class reference).
+ # Generated with `make include-list` for each resource.
# Editor translations
- tlist = glob.glob(path + "/translations/*.po")
+ to_include = (
+ "ar,bg,bn,ca,cs,de,el,eo,es_AR,es,fi,fr,gl,he,hu,id,it,ja,ko,ms,nb,nl,pl,pt_BR,pt,ro,ru,sk,sv,th,tr,uk,vi,zh_CN,zh_TW"
+ ).split(",")
+ tlist = [env.Dir("#editor/translations").abspath + "/" + f + ".po" for f in to_include]
env.Depends("#editor/editor_translations.gen.h", tlist)
env.CommandNoCache(
"#editor/editor_translations.gen.h",
@@ -81,7 +87,8 @@ if env["tools"]:
)
# Documentation translations
- tlist = glob.glob(env.Dir("#doc").abspath + "/translations/*.po")
+ to_include = "es,fr,ja,zh_CN".split(",")
+ tlist = [env.Dir("#doc/translations").abspath + "/" + f + ".po" for f in to_include]
env.Depends("#editor/doc_translations.gen.h", tlist)
env.CommandNoCache(
"#editor/doc_translations.gen.h",
@@ -90,8 +97,8 @@ if env["tools"]:
)
# Fonts
- flist = glob.glob(path + "/../thirdparty/fonts/*.ttf")
- flist.extend(glob.glob(path + "/../thirdparty/fonts/*.otf"))
+ flist = glob.glob(env.Dir("#thirdparty").abspath + "/fonts/*.ttf")
+ flist.extend(glob.glob(env.Dir("#thirdparty").abspath + "/fonts/*.otf"))
flist.sort()
env.Depends("#editor/builtin_fonts.gen.h", flist)
env.CommandNoCache(
diff --git a/editor/code_editor.h b/editor/code_editor.h
index 3c52a0c6e8..6e3bd88112 100644
--- a/editor/code_editor.h
+++ b/editor/code_editor.h
@@ -53,7 +53,6 @@ public:
void popup_find_line(CodeEdit *p_edit);
int get_line() const;
- void set_text_editor(CodeEdit *p_text_editor);
GotoLineDialog();
};
@@ -101,7 +100,6 @@ class FindReplaceBar : public HBoxContainer {
void _search_text_changed(const String &p_text);
void _search_text_submitted(const String &p_text);
void _replace_text_submitted(const String &p_text);
- void _update_size();
protected:
void _notification(int p_what);
diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp
index e69ced8522..71e9fac219 100644
--- a/editor/editor_command_palette.cpp
+++ b/editor/editor_command_palette.cpp
@@ -212,6 +212,12 @@ void EditorCommandPalette::_add_command(String p_command_name, String p_key_name
command.callable = p_binded_action;
command.shortcut = p_shortcut_text;
+ // Commands added from plugins don't exist yet when the history is loaded, so we assign the last use time here if it was recorded.
+ Dictionary command_history = EditorSettings::get_singleton()->get_project_metadata("command_palette", "command_history", Dictionary());
+ if (command_history.has(p_key_name)) {
+ command.last_used = command_history[p_key_name];
+ }
+
commands[p_key_name] = command;
}
@@ -242,7 +248,9 @@ void EditorCommandPalette::register_shortcuts_as_command() {
Array history_entries = command_history.keys();
for (int i = 0; i < history_entries.size(); i++) {
const String &history_key = history_entries[i];
- commands[history_key].last_used = command_history[history_key];
+ if (commands.has(history_key)) {
+ commands[history_key].last_used = command_history[history_key];
+ }
}
}
diff --git a/editor/editor_data.h b/editor/editor_data.h
index 9184ddcf39..976d718b8e 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -135,8 +135,6 @@ private:
Vector<Callable> undo_redo_callbacks;
Map<StringName, Callable> move_element_functions;
- void _cleanup_history();
-
Vector<EditedScene> edited_scene;
int current_edited_scene;
diff --git a/editor/editor_help.h b/editor/editor_help.h
index 7a45b1abc1..46605b6763 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -57,7 +57,6 @@ class FindBar : public HBoxContainer {
int results_count;
- void _show_search();
void _hide_bar();
void _search_text_changed(const String &p_text);
@@ -66,8 +65,6 @@ class FindBar : public HBoxContainer {
void _update_results_count();
void _update_matches_label();
- void _update_size();
-
protected:
void _notification(int p_what);
virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 4d0f27c5d4..9f049a0e58 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -245,13 +245,15 @@ void EditorProperty::_notification(int p_what) {
}
Color color;
- if (draw_red) {
- color = get_theme_color(is_read_only() ? SNAME("readonly_error_color") : SNAME("error_color"));
+ if (draw_warning) {
+ color = get_theme_color(is_read_only() ? SNAME("readonly_warning_color") : SNAME("warning_color"));
} else {
color = get_theme_color(is_read_only() ? SNAME("readonly_color") : SNAME("property_color"));
}
if (label.find(".") != -1) {
- color.a = 0.5; //this should be un-hacked honestly, as it's used for editor overrides
+ // FIXME: Move this to the project settings editor, as this is only used
+ // for project settings feature tag overrides.
+ color.a = 0.5;
}
int ofs = get_theme_constant(SNAME("font_offset"));
@@ -625,8 +627,8 @@ bool EditorProperty::is_checked() const {
return checked;
}
-void EditorProperty::set_draw_red(bool p_draw_red) {
- draw_red = p_draw_red;
+void EditorProperty::set_draw_warning(bool p_draw_warning) {
+ draw_warning = p_draw_warning;
update();
}
@@ -650,8 +652,8 @@ bool EditorProperty::is_keying() const {
return keying;
}
-bool EditorProperty::is_draw_red() const {
- return draw_red;
+bool EditorProperty::is_draw_warning() const {
+ return draw_warning;
}
void EditorProperty::_focusable_focused(int p_index) {
@@ -969,8 +971,8 @@ void EditorProperty::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_checked", "checked"), &EditorProperty::set_checked);
ClassDB::bind_method(D_METHOD("is_checked"), &EditorProperty::is_checked);
- ClassDB::bind_method(D_METHOD("set_draw_red", "draw_red"), &EditorProperty::set_draw_red);
- ClassDB::bind_method(D_METHOD("is_draw_red"), &EditorProperty::is_draw_red);
+ ClassDB::bind_method(D_METHOD("set_draw_warning", "draw_warning"), &EditorProperty::set_draw_warning);
+ ClassDB::bind_method(D_METHOD("is_draw_warning"), &EditorProperty::is_draw_warning);
ClassDB::bind_method(D_METHOD("set_keying", "keying"), &EditorProperty::set_keying);
ClassDB::bind_method(D_METHOD("is_keying"), &EditorProperty::is_keying);
@@ -993,7 +995,7 @@ void EditorProperty::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_only"), "set_read_only", "is_read_only");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "checkable"), "set_checkable", "is_checkable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "checked"), "set_checked", "is_checked");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_red"), "set_draw_red", "is_draw_red");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_warning"), "set_draw_warning", "is_draw_warning");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keying"), "set_keying", "is_keying");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deletable"), "set_deletable", "is_deletable");
ADD_SIGNAL(MethodInfo("property_changed", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
@@ -1018,7 +1020,7 @@ EditorProperty::EditorProperty() {
read_only = false;
checkable = false;
checked = false;
- draw_red = false;
+ draw_warning = false;
keying = false;
deletable = false;
keying_hover = false;
@@ -2393,14 +2395,15 @@ void EditorInspector::update_tree() {
valid_plugins.push_back(inspector_plugins[i]);
}
- // Decide if properties should be drawn in red.
- bool draw_red = false;
+ // Decide if properties should be drawn with the warning color (yellow).
+ bool draw_warning = false;
if (is_inside_tree()) {
Node *nod = Object::cast_to<Node>(object);
Node *es = EditorNode::get_singleton()->get_edited_scene();
if (nod && es != nod && nod->get_owner() != es) {
- // Draw in red edited nodes that are not in the currently edited scene.
- draw_red = true;
+ // Draw in warning color edited nodes that are not in the currently edited scene,
+ // as changes may be lost in the future.
+ draw_warning = true;
}
}
@@ -2855,7 +2858,7 @@ void EditorInspector::update_tree() {
editor_property_map[prop].push_back(ep);
}
}
- ep->set_draw_red(draw_red);
+ ep->set_draw_warning(draw_warning);
ep->set_use_folding(use_folding);
ep->set_checkable(checkable);
ep->set_checked(checked);
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index b71efe8f19..124ea31302 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -74,7 +74,7 @@ private:
bool read_only;
bool checkable;
bool checked;
- bool draw_red;
+ bool draw_warning;
bool keying;
bool deletable;
@@ -96,8 +96,6 @@ private:
bool draw_top_bg;
void _ensure_popup();
- bool _is_property_different(const Variant &p_current, const Variant &p_orig);
- bool _get_instantiated_node_original_property(const StringName &p_prop, Variant &value);
void _focusable_focused(int p_index);
bool selectable;
@@ -150,8 +148,8 @@ public:
void set_checked(bool p_checked);
bool is_checked() const;
- void set_draw_red(bool p_draw_red);
- bool is_draw_red() const;
+ void set_draw_warning(bool p_draw_warning);
+ bool is_draw_warning() const;
void set_keying(bool p_keying);
bool is_keying() const;
@@ -281,8 +279,6 @@ public:
void unfold();
void fold();
- Object *get_edited_object();
-
EditorInspectorSection();
~EditorInspectorSection();
};
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 346b93a87c..251e1c2385 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -37,7 +37,7 @@
#include "scene/gui/center_container.h"
#include "scene/resources/font.h"
-void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type) {
+void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
EditorLog *self = (EditorLog *)p_self;
if (self->current != Thread::get_caller_id()) {
return;
@@ -50,6 +50,10 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f
err_str = String(p_file) + ":" + itos(p_line) + " - " + String(p_error);
}
+ if (p_editor_notify) {
+ err_str += " (User)";
+ }
+
if (p_type == ERR_HANDLER_WARNING) {
self->add_message(err_str, MSG_TYPE_WARNING);
} else {
diff --git a/editor/editor_log.h b/editor/editor_log.h
index 6cbf4bedee..43cc5680bd 100644
--- a/editor/editor_log.h
+++ b/editor/editor_log.h
@@ -136,7 +136,7 @@ private:
bool is_loading_state = false; // Used to disable saving requests while loading (some signals from buttons will try trigger a save, which happens during loading).
Timer *save_state_timer;
- static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type);
+ static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type);
ErrorHandlerList eh;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 2f92f60d5e..891705da98 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -59,8 +59,8 @@
#include "scene/gui/panel.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/split_container.h"
+#include "scene/gui/tab_bar.h"
#include "scene/gui/tab_container.h"
-#include "scene/gui/tabs.h"
#include "scene/gui/texture_progress_bar.h"
#include "scene/main/window.h"
#include "scene/resources/packed_scene.h"
@@ -95,6 +95,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_spin_slider.h"
#include "editor/editor_themes.h"
+#include "editor/editor_toaster.h"
#include "editor/editor_translation_parser.h"
#include "editor/export_template_manager.h"
#include "editor/filesystem_dock.h"
@@ -657,7 +658,7 @@ void EditorNode::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- scene_tabs->set_tab_close_display_policy((bool(EDITOR_GET("interface/scene_tabs/always_show_close_button")) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY));
+ scene_tabs->set_tab_close_display_policy((bool(EDITOR_GET("interface/scene_tabs/always_show_close_button")) ? TabBar::CLOSE_BUTTON_SHOW_ALWAYS : TabBar::CLOSE_BUTTON_SHOW_ACTIVE_ONLY));
theme = create_custom_theme(theme_base->get_theme());
theme_base->set_theme(theme);
@@ -837,7 +838,7 @@ void EditorNode::_resources_changed(const Vector<String> &p_resources) {
}
if (res->get_import_path() != String()) {
- //this is an imported resource, will be reloaded if reimported via the _resources_reimported() callback
+ // this is an imported resource, will be reloaded if reimported via the _resources_reimported() callback
continue;
}
@@ -941,21 +942,21 @@ void EditorNode::_fs_changed() {
}
void EditorNode::_resources_reimported(const Vector<String> &p_resources) {
- List<String> scenes; //will load later
+ List<String> scenes; // will load later
int current_tab = scene_tabs->get_current_tab();
for (int i = 0; i < p_resources.size(); i++) {
String file_type = ResourceLoader::get_resource_type(p_resources[i]);
if (file_type == "PackedScene") {
scenes.push_back(p_resources[i]);
- //reload later if needed, first go with normal resources
+ // reload later if needed, first go with normal resources
continue;
}
if (!ResourceCache::has(p_resources[i])) {
- continue; //not loaded, no need to reload
+ continue; // not loaded, no need to reload
}
- //reload normally
+ // reload normally
Resource *resource = ResourceCache::get(p_resources[i]);
if (resource) {
resource->reload_from_file();
@@ -1139,7 +1140,7 @@ Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_d
ERR_FAIL_COND_V(!res.is_valid(), ERR_CANT_OPEN);
if (!p_ignore_broken_deps && dependency_errors.has(p_resource)) {
- //current_option = -1;
+ // current_option = -1;
Vector<String> errors;
for (Set<String>::Element *E = dependency_errors[p_resource].front(); E; E = E->next()) {
errors.push_back(E->get());
@@ -1215,7 +1216,7 @@ void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String
List<String> preferred;
for (const String &E : extensions) {
if (p_resource->is_class("Script") && (E == "tres" || E == "res")) {
- //this serves no purpose and confused people
+ // this serves no purpose and confused people
continue;
}
file->add_filter("*." + E + " ; " + E.to_upper());
@@ -1324,7 +1325,7 @@ void EditorNode::_get_scene_metadata(const String &p_file) {
Error err = cf->load(path);
if (err != OK || !cf->has_section("editor_states")) {
- return; //must not exist
+ return; // must not exist
}
List<String> esl;
@@ -1348,7 +1349,7 @@ void EditorNode::_set_scene_metadata(const String &p_file, int p_idx) {
return;
}
- scene->set_meta("__editor_run_settings__", Variant()); //clear it (no point in keeping it)
+ scene->set_meta("__editor_run_settings__", Variant()); // clear it (no point in keeping it)
scene->set_meta("__editor_plugin_states__", Variant());
String path = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(p_file.get_file() + "-editstate-" + p_file.md5_text() + ".cfg");
@@ -1391,10 +1392,10 @@ bool EditorNode::_find_and_save_resource(RES p_res, Map<RES, bool> &processed, i
if (p_res->get_path().is_resource_file()) {
if (changed || subchanged) {
- //save
+ // save
ResourceSaver::save(p_res->get_path(), p_res, flags);
}
- processed[p_res] = false; //because it's a file
+ processed[p_res] = false; // because it's a file
return false;
} else {
processed[p_res] = changed;
@@ -1492,7 +1493,7 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) {
_find_node_types(editor_data.get_edited_scene_root(), c2d, c3d);
save.step(TTR("Creating Thumbnail"), 1);
- //current view?
+ // current view?
Ref<Image> img;
// If neither 3D or 2D nodes are present, make a 1x1 black texture.
@@ -1545,12 +1546,12 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) {
}
img->convert(Image::FORMAT_RGB8);
- //save thumbnail directly, as thumbnailer may not update due to actual scene not changing md5
+ // save thumbnail directly, as thumbnailer may not update due to actual scene not changing md5
String temp_path = EditorPaths::get_singleton()->get_cache_dir();
String cache_base = ProjectSettings::get_singleton()->globalize_path(p_file).md5_text();
cache_base = temp_path.plus_file("resthumb-" + cache_base);
- //does not have it, try to load a cached thumbnail
+ // does not have it, try to load a cached thumbnail
String file = cache_base + ".png";
@@ -1598,7 +1599,7 @@ static bool _find_edited_resources(const Ref<Resource> &p_resource, Set<Ref<Reso
if (res.is_null()) {
continue;
}
- if (res->get_path().is_resource_file()) { //not a subresource, continue
+ if (res->get_path().is_resource_file()) { // not a subresource, continue
continue;
}
if (_find_edited_resources(res, edited_resources)) {
@@ -1611,7 +1612,7 @@ static bool _find_edited_resources(const Ref<Resource> &p_resource, Set<Ref<Reso
}
int EditorNode::_save_external_resources() {
- //save external resources and its subresources if any was modified
+ // save external resources and its subresources if any was modified
int flg = 0;
if (EditorSettings::get_singleton()->get("filesystem/on_save/compress_binary_resources")) {
@@ -1627,7 +1628,7 @@ int EditorNode::_save_external_resources() {
if (!res->get_path().is_resource_file()) {
continue;
}
- //not only check if this resource is edited, check contained subresources too
+ // not only check if this resource is edited, check contained subresources too
if (_find_edited_resources(res, edited_subresources)) {
ResourceSaver::save(res->get_path(), res, flg);
saved++;
@@ -1836,7 +1837,7 @@ void EditorNode::_dialog_action(String p_file) {
case SETTINGS_PICK_MAIN_SCENE: {
ProjectSettings::get_singleton()->set("application/run/main_scene", p_file);
ProjectSettings::get_singleton()->save();
- //would be nice to show the project manager opened with the highlighted field..
+ // would be nice to show the project manager opened with the highlighted field..
if (pick_main_scene->has_meta("from_native") && (bool)pick_main_scene->get_meta("from_native")) {
run_native->resume_run_native();
@@ -1979,7 +1980,7 @@ void EditorNode::_dialog_action(String p_file) {
}
} break;
- default: { //save scene?
+ default: { // save scene?
if (file->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
_save_scene_with_preview(p_file);
@@ -2140,7 +2141,7 @@ void EditorNode::_edit_current() {
bool is_resource = current_obj->is_class("Resource");
bool is_node = current_obj->is_class("Node");
- String editable_warning; //none by default
+ String editable_warning; // none by default
if (is_resource) {
Resource *current_res = Object::cast_to<Resource>(current_obj);
@@ -2242,7 +2243,7 @@ void EditorNode::_edit_current() {
for (; plugin_index < editor_table.size(); plugin_index++) {
if (editor_table[plugin_index] == main_plugin) {
if (!main_editor_buttons[plugin_index]->is_visible()) {
- main_plugin = nullptr; //if button is not visible, then no plugin active
+ main_plugin = nullptr; // if button is not visible, then no plugin active
}
break;
@@ -2328,7 +2329,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
}
if (run_filename == "") {
- //evidently, run the scene
+ // evidently, run the scene
if (!ensure_main_scene(false)) {
return;
}
@@ -2411,7 +2412,7 @@ void EditorNode::_android_build_source_selected(const String &p_file) {
export_template_manager->install_android_template_from_file(p_file);
}
void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
- if (!p_confirmed) { //this may be a hack..
+ if (!p_confirmed) { // this may be a hack..
current_option = (MenuOptions)p_option;
}
@@ -2980,7 +2981,7 @@ int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) {
void EditorNode::_exit_editor() {
exiting = true;
- resource_preview->stop(); //stop early to avoid crashes
+ resource_preview->stop(); // stop early to avoid crashes
_save_docks();
// Dim the editor window while it's quitting to make it clearer that it's busy
@@ -3086,7 +3087,7 @@ void EditorNode::_editor_select(int p_which) {
ERR_FAIL_INDEX(p_which, editor_table.size());
- if (!main_editor_buttons[p_which]->is_visible()) { //button hidden, no editor
+ if (!main_editor_buttons[p_which]->is_visible()) { // button hidden, no editor
return;
}
@@ -3228,7 +3229,7 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled,
if (!p_enabled) {
EditorPlugin *addon = plugin_addons[p_addon];
remove_editor_plugin(addon, p_config_changed);
- memdelete(addon); //bye
+ memdelete(addon); // bye
plugin_addons.erase(p_addon);
_update_addon_config();
return;
@@ -3320,10 +3321,10 @@ void EditorNode::_remove_edited_scene(bool p_change_tab) {
void EditorNode::_remove_scene(int index, bool p_change_tab) {
if (editor_data.get_edited_scene() == index) {
- //Scene to remove is current scene
+ // Scene to remove is current scene
_remove_edited_scene(p_change_tab);
} else {
- //Scene to remove is not active scene
+ // Scene to remove is not active scene
editor_data.remove_scene(index);
}
}
@@ -3337,7 +3338,7 @@ void EditorNode::set_edited_scene(Node *p_scene) {
get_editor_data().set_edited_scene_root(p_scene);
if (Object::cast_to<Popup>(p_scene)) {
- Object::cast_to<Popup>(p_scene)->show(); //show popups
+ Object::cast_to<Popup>(p_scene)->show(); // show popups
}
scene_tree_dock->set_edited_scene(p_scene);
if (get_tree()) {
@@ -3373,7 +3374,7 @@ Dictionary EditorNode::_get_main_scene_state() {
void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
if (get_edited_scene() != p_for_scene && p_for_scene != nullptr) {
- return; //not for this scene
+ return; // not for this scene
}
changing_scene = false;
@@ -3388,7 +3389,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
if (p_state.has("editor_index")) {
int index = p_state["editor_index"];
- if (current < 2) { //if currently in spatial/2d, only switch to spatial/2d. if currently in script, stay there
+ if (current < 2) { // if currently in spatial/2d, only switch to spatial/2d. if currently in script, stay there
if (index < 2 || !get_edited_scene()) {
_editor_select(index);
}
@@ -3397,7 +3398,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
if (get_edited_scene()) {
if (current < 2) {
- //use heuristic instead
+ // use heuristic instead
int n2d = 0, n3d = 0;
_find_node_types(get_edited_scene(), n2d, n3d);
if (n2d > n3d) {
@@ -3419,7 +3420,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
scene_tree_dock->set_filter(p_state["node_filter"]);
}
- //this should only happen at the very end
+ // this should only happen at the very end
EditorDebuggerNode::get_singleton()->update_live_edit_root();
ScriptEditor::get_singleton()->set_scene_root_script(editor_data.get_scene_root_script(editor_data.get_edited_scene()));
@@ -3440,7 +3441,7 @@ void EditorNode::_clear_undo_history() {
}
void EditorNode::set_current_scene(int p_idx) {
- //Save the folding in case the scene gets reloaded.
+ // Save the folding in case the scene gets reloaded.
if (editor_data.get_scene_path(p_idx) != "" && editor_data.get_edited_scene_root(p_idx)) {
editor_folding.save_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx));
}
@@ -3468,7 +3469,7 @@ void EditorNode::set_current_scene(int p_idx) {
Node *new_scene = editor_data.get_edited_scene_root();
if (Object::cast_to<Popup>(new_scene)) {
- Object::cast_to<Popup>(new_scene)->show(); //show popups
+ Object::cast_to<Popup>(new_scene)->show(); // show popups
}
scene_tree_dock->set_edited_scene(new_scene);
@@ -3487,7 +3488,7 @@ void EditorNode::set_current_scene(int p_idx) {
_update_title();
- call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); //do after everything else is done setting up
+ call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); // do after everything else is done setting up
}
bool EditorNode::is_scene_open(const String &p_path) {
@@ -3589,7 +3590,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
return ERR_FILE_MISSING_DEPENDENCIES;
}
- dependency_errors.erase(lpath); //at least not self path
+ dependency_errors.erase(lpath); // at least not self path
for (KeyValue<String, Set<String>> &E : dependency_errors) {
String txt = vformat(TTR("Scene '%s' has broken dependencies:"), E.key) + "\n";
@@ -3600,7 +3601,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
}
if (ResourceCache::has(lpath)) {
- //used from somewhere else? no problem! update state and replace sdata
+ // used from somewhere else? no problem! update state and replace sdata
Ref<PackedScene> ps = Ref<PackedScene>(Object::cast_to<PackedScene>(ResourceCache::get(lpath)));
if (ps.is_valid()) {
ps->replace_state(sdata->get_state());
@@ -3609,7 +3610,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
}
} else {
- sdata->set_path(lpath, true); //take over path
+ sdata->set_path(lpath, true); // take over path
}
Node *new_scene = sdata->instantiate(PackedScene::GEN_EDIT_STATE_MAIN);
@@ -4223,7 +4224,7 @@ void EditorNode::_dock_make_float() {
ERR_FAIL_COND(!dock);
const Size2i borders = Size2i(4, 4) * EDSCALE;
- Size2 dock_size = dock->get_size() + borders * 2; //remember size
+ Size2 dock_size = dock->get_size() + borders * 2; // remember size
Point2 dock_screen_pos = dock->get_global_position() + get_tree()->get_root()->get_position() - borders;
print_line("dock pos: " + dock->get_global_position() + " window pos: " + get_tree()->get_root()->get_position());
@@ -4463,7 +4464,7 @@ void EditorNode::_dock_select_draw() {
void EditorNode::_save_docks() {
if (waiting_for_first_scan) {
- return; //scanning, do not touch docks
+ return; // scanning, do not touch docks
}
Ref<ConfigFile> config;
config.instantiate();
@@ -4534,7 +4535,7 @@ void EditorNode::_load_docks() {
config.instantiate();
Error err = config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg"));
if (err != OK) {
- //no config
+ // no config
if (overridden_default_layout >= 0) {
_layout_menu_option(overridden_default_layout);
}
@@ -4647,7 +4648,7 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String
for (int j = 0; j < names.size(); j++) {
String name = names[j];
- //find it, in a horribly inefficient way
+ // find it, in a horribly inefficient way
int atidx = -1;
Control *node = nullptr;
for (int k = 0; k < DOCK_SLOT_MAX; k++) {
@@ -4661,7 +4662,7 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String
atidx = k;
break;
}
- if (atidx == -1) { //well, it's not anywhere
+ if (atidx == -1) { // well, it's not anywhere
continue;
}
@@ -4777,7 +4778,7 @@ bool EditorNode::has_scenes_in_session() {
}
bool EditorNode::ensure_main_scene(bool p_from_native) {
- pick_main_scene->set_meta("from_native", p_from_native); //whether from play button or native run
+ pick_main_scene->set_meta("from_native", p_from_native); // whether from play button or native run
String main_scene = GLOBAL_DEF("application/run/main_scene", "");
if (main_scene == "") {
@@ -4898,7 +4899,7 @@ void EditorNode::_update_layouts_menu() {
config.instantiate();
Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config());
if (err != OK) {
- return; //no config
+ return; // no config
}
List<String> layouts;
@@ -4939,7 +4940,7 @@ void EditorNode::_layout_menu_option(int p_id) {
config.instantiate();
Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config());
if (err != OK) {
- return; //no config
+ return; // no config
}
_load_docks_from_config(config, editor_layouts->get_item_text(p_id));
@@ -5080,7 +5081,7 @@ void EditorNode::_scene_tab_changed(int p_tab) {
bool unsaved = (saved_version != editor_data.get_undo_redo().get_version());
if (p_tab == editor_data.get_edited_scene()) {
- return; //pointless
+ return; // pointless
}
uint64_t next_scene_version = editor_data.get_scene_version(p_tab);
@@ -5283,11 +5284,11 @@ Variant EditorNode::drag_resource(const Ref<Resource> &p_res, Control *p_from) {
Ref<Texture2D> preview;
{
- //todo make proper previews
+ // todo make proper previews
Ref<ImageTexture> texture = gui_base->get_theme_icon(SNAME("FileBigThumb"), SNAME("EditorIcons"));
Ref<Image> img = texture->get_image();
img = img->duplicate();
- img->resize(48, 48); //meh
+ img->resize(48, 48); // meh
Ref<ImageTexture> resized_pic = Ref<ImageTexture>(memnew(ImageTexture));
resized_pic->create_from_image(img);
preview = resized_pic;
@@ -5305,7 +5306,7 @@ Variant EditorNode::drag_resource(const Ref<Resource> &p_res, Control *p_from) {
drag_control->add_child(label);
- p_from->set_drag_preview(drag_control); //wait until it enters scene
+ p_from->set_drag_preview(drag_control); // wait until it enters scene
label->set_position(Point2((preview->get_width() - label->get_minimum_size().width) / 2, preview->get_height()));
@@ -5359,7 +5360,7 @@ Variant EditorNode::drag_files_and_dirs(const Vector<String> &p_paths, Control *
}
vbox->add_child(label);
}
- p_from->set_drag_preview(vbox); //wait until it enters scene
+ p_from->set_drag_preview(vbox); // wait until it enters scene
Dictionary drag_data;
drag_data["type"] = has_folder ? "files_and_dirs" : "files";
@@ -5476,7 +5477,7 @@ void EditorNode::reload_scene(const String &p_path) {
if (scene_idx == -1) {
if (get_edited_scene()) {
- //scene is not open, so at it might be instantiated. We'll refresh the whole scene later.
+ // scene is not open, so at it might be instantiated. We'll refresh the whole scene later.
editor_data.get_undo_redo().clear_history();
}
return;
@@ -5487,17 +5488,17 @@ void EditorNode::reload_scene(const String &p_path) {
_set_scene_metadata(p_path);
}
- //remove scene
+ // remove scene
_remove_scene(scene_idx, false);
- //reload scene
+ // reload scene
load_scene(p_path, true, false, true, true);
- //adjust index so tab is back a the previous position
+ // adjust index so tab is back a the previous position
editor_data.move_edited_scene_to_index(scene_idx);
get_undo_redo()->clear_history();
- //recover the tab
+ // recover the tab
scene_tabs->set_current_tab(current_tab);
}
@@ -5597,7 +5598,7 @@ void EditorNode::_update_video_driver_color() {
void EditorNode::_video_driver_selected(int p_which) {
String driver = video_driver->get_item_metadata(p_which);
- String current = ""; //OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver());
+ String current = ""; // OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver());
if (driver == current) {
return;
@@ -5786,9 +5787,9 @@ EditorNode::EditorNode() {
PhysicsServer2D::get_singleton()->set_active(false); // no physics by default if editor
ScriptServer::set_scripting_enabled(false); // no scripting by default if editor
- EditorHelp::generate_doc(); //before any editor classes are created
+ EditorHelp::generate_doc(); // before any editor classes are created
SceneState::set_disable_placeholders(true);
- ResourceLoader::clear_translation_remaps(); //no remaps using during editor
+ ResourceLoader::clear_translation_remaps(); // no remaps using during editor
ResourceLoader::clear_path_remaps();
Input *id = Input::get_singleton();
@@ -5802,8 +5803,8 @@ EditorNode::EditorNode() {
}
if (!found_touchscreen && Input::get_singleton()) {
- //only if no touchscreen ui hint, set emulation
- id->set_emulate_touch_from_mouse(false); //just disable just in case
+ // only if no touchscreen ui hint, set emulation
+ id->set_emulate_touch_from_mouse(false); // just disable just in case
}
DisplayServer::get_singleton()->cursor_set_custom_image(RES());
}
@@ -5870,7 +5871,7 @@ EditorNode::EditorNode() {
ResourceLoader::set_error_notify_func(this, _load_error_notify);
ResourceLoader::set_dependency_error_notify_func(this, _dependency_error_report);
- { //register importers at the beginning, so dialogs are created with the right extensions
+ { // register importers at the beginning, so dialogs are created with the right extensions
Ref<ResourceImporterTexture> import_texture;
import_texture.instantiate();
ResourceFormatImporter::get_singleton()->add_importer(import_texture);
@@ -5973,7 +5974,7 @@ EditorNode::EditorNode() {
EditorFileSystem *efs = memnew(EditorFileSystem);
add_child(efs);
- //used for previews
+ // used for previews
FileDialog::get_icon_func = _file_dialog_get_icon;
FileDialog::register_func = _file_dialog_register;
FileDialog::unregister_func = _file_dialog_unregister;
@@ -5992,7 +5993,7 @@ EditorNode::EditorNode() {
ClassDB::set_class_enabled("RootMotionView", true);
- //defs here, use EDITOR_GET in logic
+ // defs here, use EDITOR_GET in logic
EDITOR_DEF_RST("interface/scene_tabs/always_show_close_button", false);
EDITOR_DEF_RST("interface/scene_tabs/resize_if_many_tabs", true);
EDITOR_DEF_RST("interface/scene_tabs/minimum_width", 50);
@@ -6216,13 +6217,13 @@ EditorNode::EditorNode() {
tab_preview->set_position(Point2(2, 2) * EDSCALE);
tab_preview_panel->add_child(tab_preview);
- scene_tabs = memnew(Tabs);
+ scene_tabs = memnew(TabBar);
scene_tabs->add_theme_style_override("tab_selected", gui_base->get_theme_stylebox(SNAME("SceneTabFG"), SNAME("EditorStyles")));
scene_tabs->add_theme_style_override("tab_unselected", gui_base->get_theme_stylebox(SNAME("SceneTabBG"), SNAME("EditorStyles")));
scene_tabs->set_select_with_rmb(true);
scene_tabs->add_tab("unsaved");
- scene_tabs->set_tab_align(Tabs::ALIGN_LEFT);
- scene_tabs->set_tab_close_display_policy((bool(EDITOR_DEF("interface/scene_tabs/always_show_close_button", false)) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY));
+ scene_tabs->set_tab_align(TabBar::ALIGN_LEFT);
+ scene_tabs->set_tab_close_display_policy((bool(EDITOR_DEF("interface/scene_tabs/always_show_close_button", false)) ? TabBar::CLOSE_BUTTON_SHOW_ALWAYS : TabBar::CLOSE_BUTTON_SHOW_ACTIVE_ONLY));
scene_tabs->set_min_width(int(EDITOR_DEF("interface/scene_tabs/minimum_width", 50)) * EDSCALE);
scene_tabs->set_drag_to_rearrange_enabled(true);
scene_tabs->connect("tab_changed", callable_mp(this, &EditorNode::_scene_tab_changed));
@@ -6748,6 +6749,9 @@ EditorNode::EditorNode() {
bottom_panel_hb_editors->set_h_size_flags(Control::SIZE_EXPAND_FILL);
bottom_panel_hb->add_child(bottom_panel_hb_editors);
+ editor_toaster = memnew(EditorToaster);
+ bottom_panel_hb->add_child(editor_toaster);
+
VBoxContainer *version_info_vbc = memnew(VBoxContainer);
bottom_panel_hb->add_child(version_info_vbc);
@@ -6920,7 +6924,7 @@ EditorNode::EditorNode() {
EditorAudioBuses *audio_bus_editor = EditorAudioBuses::register_editor();
- ScriptTextEditor::register_editor(); //register one for text scripts
+ ScriptTextEditor::register_editor(); // register one for text scripts
TextEditor::register_editor();
if (StreamPeerSSL::is_available()) {
@@ -6929,12 +6933,12 @@ EditorNode::EditorNode() {
WARN_PRINT("Asset Library not available, as it requires SSL to work.");
}
- //add interface before adding plugins
+ // add interface before adding plugins
editor_interface = memnew(EditorInterface);
add_child(editor_interface);
- //more visually meaningful to have this later
+ // more visually meaningful to have this later
raise_bottom_panel_item(AnimationPlayerEditor::get_singleton());
add_editor_plugin(VersionControlEditorPlugin::get_singleton());
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 73feeecfee..c64bcc0460 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -37,6 +37,7 @@
#include "editor/editor_folding.h"
#include "editor/editor_native_shader_source_visualizer.h"
#include "editor/editor_run.h"
+#include "editor/editor_toaster.h"
#include "editor/inspector_dock.h"
#include "editor/property_editor.h"
#include "editor/scene_tree_dock.h"
@@ -84,7 +85,7 @@ class ProjectSettingsEditor;
class RunSettingsDialog;
class ScriptCreateDialog;
class TabContainer;
-class Tabs;
+class TabBar;
class TextureProgressBar;
class Button;
class VSplitContainer;
@@ -215,7 +216,7 @@ private:
TOOL_MENU_BASE = 1000
};
- SubViewport *scene_root; //root of the scene being edited
+ SubViewport *scene_root; // root of the scene being edited
PanelContainer *scene_root_parent;
Control *theme_base;
@@ -249,7 +250,7 @@ private:
// Main tabs
- Tabs *scene_tabs;
+ TabBar *scene_tabs;
PopupMenu *scene_tabs_context_menu;
Panel *tab_preview_panel;
TextureRect *tab_preview;
@@ -323,7 +324,7 @@ private:
EditorSettingsDialog *settings_config_dialog;
ProjectSettingsEditor *project_settings;
- bool settings_changed = true; //make it update settings on first frame
+ bool settings_changed = true; // make it update settings on first frame
void _update_from_settings();
PopupMenu *vcs_actions_menu;
@@ -438,6 +439,7 @@ private:
HBoxContainer *bottom_panel_hb;
HBoxContainer *bottom_panel_hb_editors;
VBoxContainer *bottom_panel_vb;
+ EditorToaster *editor_toaster;
LinkButton *version_btn;
Button *bottom_panel_raise;
@@ -518,9 +520,6 @@ private:
void _run(bool p_current = false, const String &p_custom = "");
void _run_native(const Ref<EditorExportPreset> &p_preset);
- void _save_optimized();
- void _import_action(const String &p_action);
- void _import(const String &p_file);
void _add_to_recent_scenes(const String &p_scene);
void _update_recent_scenes();
void _open_recent_scene(int p_idx);
@@ -558,7 +557,6 @@ private:
static void _editor_file_dialog_register(EditorFileDialog *p_dialog);
static void _editor_file_dialog_unregister(EditorFileDialog *p_dialog);
- void _cleanup_scene();
void _remove_edited_scene(bool p_change_tab = true);
void _remove_scene(int index, bool p_change_tab = true);
bool _find_and_save_resource(RES p_res, Map<RES, bool> &processed, int32_t flags);
@@ -656,8 +654,6 @@ private:
static int build_callback_count;
static EditorBuildCallback build_callbacks[MAX_BUILD_CALLBACKS];
- void _license_tree_selected();
-
void _update_update_spinner();
Vector<Ref<EditorResourceConversionPlugin>> resource_conversion_plugins;
@@ -773,10 +769,9 @@ public:
Node *get_edited_scene() { return editor_data.get_edited_scene_root(); }
- SubViewport *get_scene_root() { return scene_root; } //root of the scene being edited
+ SubViewport *get_scene_root() { return scene_root; } // root of the scene being edited
void fix_dependencies(const String &p_for_file);
- void clear_scene() { _cleanup_scene(); }
int new_scene();
Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false, bool p_silent_change_tab = false);
Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false);
@@ -849,8 +844,6 @@ public:
bool is_scene_in_use(const String &p_path);
- void scan_import_changes();
-
void save_layout();
void open_export_template_manager();
@@ -892,7 +885,6 @@ public:
EditorNode();
~EditorNode();
- void get_singleton(const char *arg1, bool arg2);
void add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
void remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
diff --git a/editor/editor_path.h b/editor/editor_path.h
index cabfa931d6..07f8b7244e 100644
--- a/editor/editor_path.h
+++ b/editor/editor_path.h
@@ -47,7 +47,6 @@ class EditorPath : public Button {
PopupMenu *sub_objects_menu;
Vector<ObjectID> objects;
- EditorPath();
void _show_popup();
void _id_pressed(int p_idx);
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 223cc6650c..1da9213b89 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -442,6 +442,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("interface/editor/hide_console_window", false);
_initial_set("interface/editor/mouse_extra_buttons_navigate_history", true);
_initial_set("interface/editor/save_each_scene_on_quit", true); // Regression
+ EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/show_internal_errors_in_toast_notifications", 0, "Auto,Enabled,Disabled")
// Inspector
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/inspector/max_array_dictionary_items_per_page", 20, "10,100,1")
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 2d4db48f2a..fdcc0438ae 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -403,7 +403,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
float prev_icon_saturation = theme->has_color("icon_saturation", "Editor") ? theme->get_color("icon_saturation", "Editor").r : 1.0;
- theme->set_color("icon_saturation", "Editor", Color(icon_saturation, icon_saturation, icon_saturation)); //can't save single float in theme, so using color
+ theme->set_color("icon_saturation", "Editor", Color(icon_saturation, icon_saturation, icon_saturation)); // can't save single float in theme, so using color
theme->set_color("accent_color", "Editor", accent_color);
theme->set_color("highlight_color", "Editor", highlight_color);
theme->set_color("disabled_highlight_color", "Editor", disabled_highlight_color);
@@ -431,7 +431,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
Color error_color = Color(1, 0.47, 0.42);
Color property_color = font_color.lerp(Color(0.5, 0.5, 0.5), 0.5);
Color readonly_color = property_color.lerp(dark_theme ? Color(0, 0, 0) : Color(1, 1, 1), 0.5);
- Color readonly_error_color = error_color.lerp(dark_theme ? Color(0, 0, 0) : Color(1, 1, 1), 0.5);
+ Color readonly_warning_color = error_color.lerp(dark_theme ? Color(0, 0, 0) : Color(1, 1, 1), 0.5);
if (!dark_theme) {
// Darken some colors to be readable on a light background
@@ -445,7 +445,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("error_color", "Editor", error_color);
theme->set_color("property_color", "Editor", property_color);
theme->set_color("readonly_color", "Editor", readonly_color);
- theme->set_color("readonly_error_color", "EditorProperty", readonly_error_color);
if (!dark_theme) {
theme->set_color("vulkan_color", "Editor", Color::hex(0xad1128ff));
@@ -549,7 +548,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
Ref<StyleBoxEmpty> style_empty = make_empty_stylebox(default_margin_size, default_margin_size, default_margin_size, default_margin_size);
- // Tabs
+ // TabBar
Ref<StyleBoxFlat> style_tab_selected = style_widget->duplicate();
@@ -819,10 +818,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("bg_selected", "EditorProperty", style_property_bg);
theme->set_stylebox("bg", "EditorProperty", Ref<StyleBoxEmpty>(memnew(StyleBoxEmpty)));
theme->set_constant("vseparation", "EditorProperty", (extra_spacing + default_margin_size) * EDSCALE);
- theme->set_color("error_color", "EditorProperty", error_color);
+ theme->set_color("warning_color", "EditorProperty", warning_color);
theme->set_color("property_color", "EditorProperty", property_color);
theme->set_color("readonly_color", "EditorProperty", readonly_color);
- theme->set_color("readonly_error_color", "EditorProperty", readonly_error_color);
+ theme->set_color("readonly_warning_color", "EditorProperty", readonly_warning_color);
Color inspector_section_color = font_color.lerp(Color(0.5, 0.5, 0.5), 0.35);
theme->set_color("font_color", "EditorInspectorSection", inspector_section_color);
@@ -950,33 +949,33 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("icon_margin", "ItemList", 6 * EDSCALE);
theme->set_constant("line_separation", "ItemList", 3 * EDSCALE);
- // Tabs & TabContainer
+ // TabBar & TabContainer
theme->set_stylebox("tab_selected", "TabContainer", style_tab_selected);
theme->set_stylebox("tab_unselected", "TabContainer", style_tab_unselected);
theme->set_stylebox("tab_disabled", "TabContainer", style_tab_disabled);
- theme->set_stylebox("tab_selected", "Tabs", style_tab_selected);
- theme->set_stylebox("tab_unselected", "Tabs", style_tab_unselected);
- theme->set_stylebox("tab_disabled", "Tabs", style_tab_disabled);
+ theme->set_stylebox("tab_selected", "TabBar", style_tab_selected);
+ theme->set_stylebox("tab_unselected", "TabBar", style_tab_unselected);
+ theme->set_stylebox("tab_disabled", "TabBar", style_tab_disabled);
theme->set_color("font_selected_color", "TabContainer", font_color);
theme->set_color("font_unselected_color", "TabContainer", font_disabled_color);
- theme->set_color("font_selected_color", "Tabs", font_color);
- theme->set_color("font_unselected_color", "Tabs", font_disabled_color);
+ theme->set_color("font_selected_color", "TabBar", font_color);
+ theme->set_color("font_unselected_color", "TabBar", font_disabled_color);
theme->set_icon("menu", "TabContainer", theme->get_icon("GuiTabMenu", "EditorIcons"));
theme->set_icon("menu_highlight", "TabContainer", theme->get_icon("GuiTabMenuHl", "EditorIcons"));
theme->set_stylebox("SceneTabFG", "EditorStyles", style_tab_selected);
theme->set_stylebox("SceneTabBG", "EditorStyles", style_tab_unselected);
- theme->set_icon("close", "Tabs", theme->get_icon("GuiClose", "EditorIcons"));
- theme->set_stylebox("close_bg_pressed", "Tabs", style_menu);
- theme->set_stylebox("close_bg_highlight", "Tabs", style_menu);
+ theme->set_icon("close", "TabBar", theme->get_icon("GuiClose", "EditorIcons"));
+ theme->set_stylebox("close_bg_pressed", "TabBar", style_menu);
+ theme->set_stylebox("close_bg_highlight", "TabBar", style_menu);
theme->set_icon("increment", "TabContainer", theme->get_icon("GuiScrollArrowRight", "EditorIcons"));
theme->set_icon("decrement", "TabContainer", theme->get_icon("GuiScrollArrowLeft", "EditorIcons"));
- theme->set_icon("increment", "Tabs", theme->get_icon("GuiScrollArrowRight", "EditorIcons"));
- theme->set_icon("decrement", "Tabs", theme->get_icon("GuiScrollArrowLeft", "EditorIcons"));
- theme->set_icon("increment_highlight", "Tabs", theme->get_icon("GuiScrollArrowRightHl", "EditorIcons"));
- theme->set_icon("decrement_highlight", "Tabs", theme->get_icon("GuiScrollArrowLeftHl", "EditorIcons"));
+ theme->set_icon("increment", "TabBar", theme->get_icon("GuiScrollArrowRight", "EditorIcons"));
+ theme->set_icon("decrement", "TabBar", theme->get_icon("GuiScrollArrowLeft", "EditorIcons"));
+ theme->set_icon("increment_highlight", "TabBar", theme->get_icon("GuiScrollArrowRightHl", "EditorIcons"));
+ theme->set_icon("decrement_highlight", "TabBar", theme->get_icon("GuiScrollArrowLeftHl", "EditorIcons"));
theme->set_icon("increment_highlight", "TabContainer", theme->get_icon("GuiScrollArrowRightHl", "EditorIcons"));
theme->set_icon("decrement_highlight", "TabContainer", theme->get_icon("GuiScrollArrowLeftHl", "EditorIcons"));
- theme->set_constant("hseparation", "Tabs", 4 * EDSCALE);
+ theme->set_constant("hseparation", "TabBar", 4 * EDSCALE);
// Content of each tab
Ref<StyleBoxFlat> style_content_panel = style_default->duplicate();
diff --git a/editor/editor_toaster.cpp b/editor/editor_toaster.cpp
new file mode 100644
index 0000000000..9de0ea40fe
--- /dev/null
+++ b/editor/editor_toaster.cpp
@@ -0,0 +1,513 @@
+/*************************************************************************/
+/* editor_toaster.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "scene/gui/label.h"
+#include "scene/gui/panel_container.h"
+
+#include "editor_toaster.h"
+
+EditorToaster *EditorToaster::singleton = nullptr;
+
+void EditorToaster::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ double delta = get_process_delta_time();
+
+ // Check if one element is hovered, if so, don't elapse time.
+ bool hovered = false;
+ for (const KeyValue<Control *, Toast> &element : toasts) {
+ if (Rect2(Vector2(), element.key->get_size()).has_point(element.key->get_local_mouse_position())) {
+ hovered = true;
+ break;
+ }
+ }
+
+ // Elapses the time and remove toasts if needed.
+ if (!hovered) {
+ for (const KeyValue<Control *, Toast> &element : toasts) {
+ if (!element.value.popped || element.value.duration <= 0) {
+ continue;
+ }
+ toasts[element.key].remaining_time -= delta;
+ if (toasts[element.key].remaining_time < 0) {
+ close(element.key);
+ }
+ element.key->update();
+ }
+ } else {
+ // Reset the timers when hovered.
+ for (const KeyValue<Control *, Toast> &element : toasts) {
+ if (!element.value.popped || element.value.duration <= 0) {
+ continue;
+ }
+ toasts[element.key].remaining_time = element.value.duration;
+ element.key->update();
+ }
+ }
+
+ // Change alpha over time.
+ bool needs_update = false;
+ for (const KeyValue<Control *, Toast> &element : toasts) {
+ Color modulate = element.key->get_modulate();
+
+ // Change alpha over time.
+ if (element.value.popped && modulate.a < 1.0) {
+ modulate.a += delta * 3;
+ element.key->set_modulate(modulate);
+ } else if (!element.value.popped && modulate.a > 0.0) {
+ modulate.a -= delta * 2;
+ element.key->set_modulate(modulate);
+ }
+
+ // Hide element if it is not visible anymore.
+ if (modulate.a <= 0) {
+ if (element.key->is_visible()) {
+ element.key->hide();
+ needs_update = true;
+ }
+ }
+ }
+
+ if (needs_update) {
+ _update_vbox_position();
+ _update_disable_notifications_button();
+ main_button->update();
+ }
+ } break;
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ if (vbox_container->is_visible()) {
+ main_button->set_icon(get_theme_icon(SNAME("Notification"), SNAME("EditorIcons")));
+ } else {
+ main_button->set_icon(get_theme_icon(SNAME("NotificationDisabled"), SNAME("EditorIcons")));
+ }
+ disable_notifications_button->set_icon(get_theme_icon(SNAME("NotificationDisabled"), SNAME("EditorIcons")));
+
+ // Styleboxes background.
+ info_panel_style_background->set_bg_color(get_theme_color("base_color", "Editor"));
+
+ warning_panel_style_background->set_bg_color(get_theme_color("base_color", "Editor"));
+ warning_panel_style_background->set_border_color(get_theme_color("warning_color", "Editor"));
+
+ error_panel_style_background->set_bg_color(get_theme_color("base_color", "Editor"));
+ error_panel_style_background->set_border_color(get_theme_color("error_color", "Editor"));
+
+ // Styleboxes progress.
+ info_panel_style_progress->set_bg_color(get_theme_color("base_color", "Editor").lightened(0.03));
+
+ warning_panel_style_progress->set_bg_color(get_theme_color("base_color", "Editor").lightened(0.03));
+ warning_panel_style_progress->set_border_color(get_theme_color("warning_color", "Editor"));
+
+ error_panel_style_progress->set_bg_color(get_theme_color("base_color", "Editor").lightened(0.03));
+ error_panel_style_progress->set_border_color(get_theme_color("error_color", "Editor"));
+
+ main_button->update();
+ disable_notifications_button->update();
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ _update_vbox_position();
+ _update_disable_notifications_button();
+ } break;
+ default:
+ break;
+ }
+}
+
+void EditorToaster::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
+ if (!EditorToaster::get_singleton() || !EditorToaster::get_singleton()->is_inside_tree()) {
+ return;
+ }
+
+#ifdef DEV_ENABLED
+ bool in_dev = true;
+#else
+ bool in_dev = false;
+#endif
+
+ int show_all_setting = EDITOR_GET("interface/editor/show_internal_errors_in_toast_notifications");
+
+ if (p_editor_notify || (show_all_setting == 0 && in_dev) || show_all_setting == 1) {
+ String err_str;
+ if (p_errorexp && p_errorexp[0]) {
+ err_str = p_errorexp;
+ } else {
+ err_str = String(p_error);
+ }
+ String tooltip_str = String(p_file) + ":" + itos(p_line);
+
+ if (!p_editor_notify) {
+ if (p_type == ERR_HANDLER_WARNING) {
+ err_str = "INTERNAL WARNING: " + err_str;
+ } else {
+ err_str = "INTERNAL ERROR: " + err_str;
+ }
+ }
+
+ if (p_type == ERR_HANDLER_WARNING) {
+ EditorToaster::get_singleton()->popup_str(err_str, SEVERITY_WARNING, tooltip_str);
+ } else {
+ EditorToaster::get_singleton()->popup_str(err_str, SEVERITY_ERROR, tooltip_str);
+ }
+ }
+}
+
+void EditorToaster::_update_vbox_position() {
+ // This is kind of a workaround because it's hard to keep the VBox anchroed to the bottom.
+ vbox_container->set_size(Vector2());
+ vbox_container->set_position(get_global_position() - vbox_container->get_size() + Vector2(get_size().x, -5 * EDSCALE));
+}
+
+void EditorToaster::_update_disable_notifications_button() {
+ bool any_visible = false;
+ for (KeyValue<Control *, Toast> element : toasts) {
+ if (element.key->is_visible()) {
+ any_visible = true;
+ break;
+ }
+ }
+
+ if (!any_visible || !vbox_container->is_visible()) {
+ disable_notifications_panel->hide();
+ } else {
+ disable_notifications_panel->show();
+ disable_notifications_panel->set_position(get_global_position() + Vector2(5 * EDSCALE, -disable_notifications_panel->get_minimum_size().y) + Vector2(get_size().x, -5 * EDSCALE));
+ }
+}
+
+void EditorToaster::_auto_hide_or_free_toasts() {
+ // Hide or free old temporary items.
+ int visible_temporary = 0;
+ int temporary = 0;
+ LocalVector<Control *> to_delete;
+ for (int i = vbox_container->get_child_count() - 1; i >= 0; i--) {
+ Control *control = Object::cast_to<Control>(vbox_container->get_child(i));
+ if (toasts[control].duration <= 0) {
+ continue; // Ignore non-temporary toasts.
+ }
+
+ temporary++;
+ if (control->is_visible()) {
+ visible_temporary++;
+ }
+
+ // Hide
+ if (visible_temporary > max_temporary_count) {
+ close(control);
+ }
+
+ // Free
+ if (temporary > max_temporary_count * 2) {
+ to_delete.push_back(control);
+ }
+ }
+
+ // Delete the control right away (removed as child) as it might cause issues otherwise when iterative over the vbox_container children.
+ for (unsigned int i = 0; i < to_delete.size(); i++) {
+ vbox_container->remove_child(to_delete[i]);
+ to_delete[i]->queue_delete();
+ toasts.erase(to_delete[i]);
+ }
+}
+
+void EditorToaster::_draw_button() {
+ bool has_one = false;
+ Severity highest_severity = SEVERITY_INFO;
+ for (const KeyValue<Control *, Toast> &element : toasts) {
+ if (!element.key->is_visible()) {
+ continue;
+ }
+ has_one = true;
+ if (element.value.severity > highest_severity) {
+ highest_severity = element.value.severity;
+ }
+ }
+
+ if (!has_one) {
+ return;
+ }
+
+ Color color;
+ real_t button_radius = main_button->get_size().x / 8;
+ switch (highest_severity) {
+ case SEVERITY_INFO:
+ color = get_theme_color("accent_color", "Editor");
+ break;
+ case SEVERITY_WARNING:
+ color = get_theme_color("warning_color", "Editor");
+ break;
+ case SEVERITY_ERROR:
+ color = get_theme_color("error_color", "Editor");
+ break;
+ default:
+ break;
+ }
+ main_button->draw_circle(Vector2(button_radius * 2, button_radius * 2), button_radius, color);
+}
+
+void EditorToaster::_draw_progress(Control *panel) {
+ if (toasts.has(panel) && toasts[panel].remaining_time > 0 && toasts[panel].duration > 0) {
+ Size2 size = panel->get_size();
+ size.x *= MIN(1, Math::range_lerp(toasts[panel].remaining_time, 0, toasts[panel].duration, 0, 2));
+
+ Ref<StyleBoxFlat> stylebox;
+ switch (toasts[panel].severity) {
+ case SEVERITY_INFO:
+ stylebox = info_panel_style_progress;
+ break;
+ case SEVERITY_WARNING:
+ stylebox = warning_panel_style_progress;
+ break;
+ case SEVERITY_ERROR:
+ stylebox = error_panel_style_progress;
+ break;
+ default:
+ break;
+ }
+ panel->draw_style_box(stylebox, Rect2(Vector2(), size));
+ }
+}
+
+void EditorToaster::_set_notifications_enabled(bool p_enabled) {
+ vbox_container->set_visible(p_enabled);
+ if (p_enabled) {
+ main_button->set_icon(get_theme_icon(SNAME("Notification"), SNAME("EditorIcons")));
+ } else {
+ main_button->set_icon(get_theme_icon(SNAME("NotificationDisabled"), SNAME("EditorIcons")));
+ }
+ _update_disable_notifications_button();
+}
+
+void EditorToaster::_repop_old() {
+ // Repop olds, up to max_temporary_count
+ bool needs_update = false;
+ int visible = 0;
+ for (int i = vbox_container->get_child_count() - 1; i >= 0; i--) {
+ Control *control = Object::cast_to<Control>(vbox_container->get_child(i));
+ if (!control->is_visible()) {
+ control->show();
+ toasts[control].remaining_time = toasts[control].duration;
+ toasts[control].popped = true;
+ needs_update = true;
+ }
+ visible++;
+ if (visible >= max_temporary_count) {
+ break;
+ }
+ }
+ if (needs_update) {
+ _update_vbox_position();
+ _update_disable_notifications_button();
+ main_button->update();
+ }
+}
+
+Control *EditorToaster::popup(Control *p_control, Severity p_severity, double p_time, String p_tooltip) {
+ // Create the panel according to the severity.
+ PanelContainer *panel = memnew(PanelContainer);
+ panel->set_tooltip(p_tooltip);
+ switch (p_severity) {
+ case SEVERITY_INFO:
+ panel->add_theme_style_override("panel", info_panel_style_background);
+ break;
+ case SEVERITY_WARNING:
+ panel->add_theme_style_override("panel", warning_panel_style_background);
+ break;
+ case SEVERITY_ERROR:
+ panel->add_theme_style_override("panel", error_panel_style_background);
+ break;
+ default:
+ break;
+ }
+ panel->set_modulate(Color(1, 1, 1, 0));
+ panel->connect("draw", callable_bind(callable_mp(this, &EditorToaster::_draw_progress), panel));
+
+ // Horizontal container.
+ HBoxContainer *hbox_container = memnew(HBoxContainer);
+ hbox_container->set_h_size_flags(SIZE_EXPAND_FILL);
+ panel->add_child(hbox_container);
+
+ // Content control.
+ p_control->set_h_size_flags(SIZE_EXPAND_FILL);
+ hbox_container->add_child(p_control);
+
+ // Close button.
+ if (p_time > 0.0) {
+ Button *close_button = memnew(Button);
+ close_button->set_flat(true);
+ close_button->set_icon(get_theme_icon("Close", "EditorIcons"));
+ close_button->connect("pressed", callable_bind(callable_mp(this, &EditorToaster::close), panel));
+ hbox_container->add_child(close_button);
+ }
+
+ toasts[panel].severity = p_severity;
+ if (p_time > 0.0) {
+ toasts[panel].duration = p_time;
+ toasts[panel].remaining_time = p_time;
+ } else {
+ toasts[panel].duration = -1.0;
+ }
+ toasts[panel].popped = true;
+ vbox_container->add_child(panel);
+ _auto_hide_or_free_toasts();
+ _update_vbox_position();
+ _update_disable_notifications_button();
+ main_button->update();
+
+ return panel;
+}
+
+void EditorToaster::popup_str(String p_message, Severity p_severity, String p_tooltip) {
+ // Check if we already have a popup with the given message.
+ Control *control = nullptr;
+ for (KeyValue<Control *, Toast> element : toasts) {
+ if (element.value.message == p_message && element.value.severity == p_severity && element.value.tooltip == p_tooltip) {
+ control = element.key;
+ break;
+ }
+ }
+
+ // Create a new message if needed.
+ if (control == nullptr) {
+ Label *label = memnew(Label);
+
+ control = popup(label, p_severity, default_message_duration, p_tooltip);
+ toasts[control].message = p_message;
+ toasts[control].tooltip = p_tooltip;
+ toasts[control].count = 1;
+ } else {
+ if (toasts[control].popped) {
+ toasts[control].count += 1;
+ } else {
+ toasts[control].count = 1;
+ }
+ toasts[control].remaining_time = toasts[control].duration;
+ toasts[control].popped = true;
+ control->show();
+ vbox_container->move_child(control, vbox_container->get_child_count());
+ _auto_hide_or_free_toasts();
+ _update_vbox_position();
+ _update_disable_notifications_button();
+ main_button->update();
+ }
+
+ // Retrieve the label back then update the text.
+ Label *label = Object::cast_to<Label>(control->get_child(0)->get_child(0));
+ ERR_FAIL_COND(!label);
+ if (toasts[control].count == 1) {
+ label->set_text(p_message);
+ } else {
+ label->set_text(vformat("%s (%d)", p_message, toasts[control].count));
+ }
+}
+
+void EditorToaster::close(Control *p_control) {
+ ERR_FAIL_COND(!toasts.has(p_control));
+ toasts[p_control].remaining_time = -1.0;
+ toasts[p_control].popped = false;
+}
+
+EditorToaster *EditorToaster::get_singleton() {
+ return singleton;
+}
+
+EditorToaster::EditorToaster() {
+ set_notify_transform(true);
+ set_process_internal(true);
+
+ // VBox.
+ vbox_container = memnew(VBoxContainer);
+ vbox_container->set_as_top_level(true);
+ vbox_container->connect("resized", callable_mp(this, &EditorToaster::_update_vbox_position));
+ add_child(vbox_container);
+
+ // Theming (background).
+ info_panel_style_background.instantiate();
+ info_panel_style_background->set_corner_radius_all(stylebox_radius * EDSCALE);
+
+ warning_panel_style_background.instantiate();
+ warning_panel_style_background->set_border_width(SIDE_LEFT, stylebox_radius * EDSCALE);
+ warning_panel_style_background->set_corner_radius_all(stylebox_radius * EDSCALE);
+
+ error_panel_style_background.instantiate();
+ error_panel_style_background->set_border_width(SIDE_LEFT, stylebox_radius * EDSCALE);
+ error_panel_style_background->set_corner_radius_all(stylebox_radius * EDSCALE);
+
+ Ref<StyleBoxFlat> boxes[] = { info_panel_style_background, warning_panel_style_background, error_panel_style_background };
+ for (int i = 0; i < 3; i++) {
+ boxes[i]->set_default_margin(SIDE_LEFT, int(stylebox_radius * 2.5));
+ boxes[i]->set_default_margin(SIDE_RIGHT, int(stylebox_radius * 2.5));
+ boxes[i]->set_default_margin(SIDE_TOP, 3);
+ boxes[i]->set_default_margin(SIDE_BOTTOM, 3);
+ }
+
+ // Theming (progress).
+ info_panel_style_progress.instantiate();
+ info_panel_style_progress->set_corner_radius_all(stylebox_radius * EDSCALE);
+
+ warning_panel_style_progress.instantiate();
+ warning_panel_style_progress->set_border_width(SIDE_LEFT, stylebox_radius * EDSCALE);
+ warning_panel_style_progress->set_corner_radius_all(stylebox_radius * EDSCALE);
+
+ error_panel_style_progress.instantiate();
+ error_panel_style_progress->set_border_width(SIDE_LEFT, stylebox_radius * EDSCALE);
+ error_panel_style_progress->set_corner_radius_all(stylebox_radius * EDSCALE);
+
+ // Main button.
+ main_button = memnew(Button);
+ main_button->set_flat(true);
+ main_button->connect("pressed", callable_mp(this, &EditorToaster::_set_notifications_enabled), varray(true));
+ main_button->connect("pressed", callable_mp(this, &EditorToaster::_repop_old));
+ main_button->connect("draw", callable_mp(this, &EditorToaster::_draw_button));
+ add_child(main_button);
+
+ // Disable notification button.
+ disable_notifications_panel = memnew(PanelContainer);
+ disable_notifications_panel->set_as_top_level(true);
+ disable_notifications_panel->add_theme_style_override("panel", info_panel_style_background);
+ add_child(disable_notifications_panel);
+
+ disable_notifications_button = memnew(Button);
+ disable_notifications_button->set_flat(true);
+ disable_notifications_button->connect("pressed", callable_mp(this, &EditorToaster::_set_notifications_enabled), varray(false));
+ disable_notifications_panel->add_child(disable_notifications_button);
+
+ // Other
+ singleton = this;
+
+ eh.errfunc = _error_handler;
+ add_error_handler(&eh);
+};
+
+EditorToaster::~EditorToaster() {
+ singleton = nullptr;
+ remove_error_handler(&eh);
+}
diff --git a/editor/editor_toaster.h b/editor/editor_toaster.h
new file mode 100644
index 0000000000..aac80d8fb1
--- /dev/null
+++ b/editor/editor_toaster.h
@@ -0,0 +1,116 @@
+/*************************************************************************/
+/* editor_toaster.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef EDITOR_TOASTER_H
+#define EDITOR_TOASTER_H
+
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/popup.h"
+
+#include "core/string/ustring.h"
+#include "core/templates/local_vector.h"
+
+class EditorToaster : public HBoxContainer {
+ GDCLASS(EditorToaster, HBoxContainer);
+
+public:
+ enum Severity {
+ SEVERITY_INFO = 0,
+ SEVERITY_WARNING,
+ SEVERITY_ERROR,
+ };
+
+private:
+ ErrorHandlerList eh;
+
+ const int stylebox_radius = 3;
+
+ Ref<StyleBoxFlat> info_panel_style_background;
+ Ref<StyleBoxFlat> warning_panel_style_background;
+ Ref<StyleBoxFlat> error_panel_style_background;
+
+ Ref<StyleBoxFlat> info_panel_style_progress;
+ Ref<StyleBoxFlat> warning_panel_style_progress;
+ Ref<StyleBoxFlat> error_panel_style_progress;
+
+ Button *main_button;
+ PanelContainer *disable_notifications_panel;
+ Button *disable_notifications_button;
+
+ VBoxContainer *vbox_container;
+ const int max_temporary_count = 5;
+ struct Toast {
+ Severity severity = SEVERITY_INFO;
+
+ // Timing.
+ real_t duration = -1.0;
+ real_t remaining_time = 0.0;
+ bool popped = false;
+
+ // Messages
+ String message;
+ String tooltip;
+ int count = 0;
+ };
+ Map<Control *, Toast> toasts;
+
+ const double default_message_duration = 5.0;
+
+ static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type);
+ void _update_vbox_position();
+ void _update_disable_notifications_button();
+ void _auto_hide_or_free_toasts();
+
+ void _draw_button();
+ void _draw_progress(Control *panel);
+
+ void _set_notifications_enabled(bool p_enabled);
+ void _repop_old();
+
+protected:
+ static EditorToaster *singleton;
+
+ void _notification(int p_what);
+
+public:
+ static EditorToaster *get_singleton();
+
+ Control *popup(Control *p_control, Severity p_severity = SEVERITY_INFO, double p_time = 0.0, String p_tooltip = String());
+ void popup_str(String p_message, Severity p_severity = SEVERITY_INFO, String p_tooltip = String());
+ void close(Control *p_control);
+
+ EditorToaster();
+ ~EditorToaster();
+};
+
+VARIANT_ENUM_CAST(EditorToaster::Severity);
+
+#endif // EDITOR_TOASTER_H
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 73bdd685b7..7c3851b94f 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -205,8 +205,6 @@ private:
void _set_file_display(bool p_active);
void _fs_changed();
- void _tree_toggle_collapsed();
-
void _select_file(const String &p_path, bool p_select_in_favorites = false);
void _tree_activate_file();
void _file_list_activate_file(int p_idx);
@@ -228,8 +226,6 @@ private:
void _file_removed(String p_file);
void _folder_removed(String p_folder);
- void _files_moved(String p_old_file, String p_new_file);
- void _folder_moved(String p_old_folder, String p_new_folder);
void _resource_created();
void _make_dir_confirm();
diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp
index 113306fc8a..f01c7f50f7 100644
--- a/editor/groups_editor.cpp
+++ b/editor/groups_editor.cpp
@@ -204,7 +204,8 @@ void GroupDialog::_add_group(String p_name) {
TreeItem *new_group = groups->create_item(groups_root);
new_group->set_text(0, name);
- new_group->add_button(0, groups->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), 0);
+ new_group->add_button(0, groups->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), DELETE_GROUP);
+ new_group->add_button(0, groups->get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), COPY_GROUP);
new_group->set_editable(0, true);
new_group->select(0);
groups->ensure_cursor_is_visible();
@@ -297,43 +298,50 @@ void GroupDialog::_load_groups(Node *p_current) {
}
}
-void GroupDialog::_delete_group_pressed(Object *p_item, int p_column, int p_id) {
+void GroupDialog::_modify_group_pressed(Object *p_item, int p_column, int p_id) {
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
if (!ti) {
return;
}
- String name = ti->get_text(0);
+ switch (p_id) {
+ case DELETE_GROUP: {
+ String name = ti->get_text(0);
- undo_redo->create_action(TTR("Delete Group"));
+ undo_redo->create_action(TTR("Delete Group"));
- List<Node *> nodes;
- scene_tree->get_nodes_in_group(name, &nodes);
- bool removed_all = true;
- for (Node *E : nodes) {
- if (_can_edit(E, name)) {
- undo_redo->add_do_method(E, "remove_from_group", name);
- undo_redo->add_undo_method(E, "add_to_group", name, true);
- } else {
- removed_all = false;
- }
- }
+ List<Node *> nodes;
+ scene_tree->get_nodes_in_group(name, &nodes);
+ bool removed_all = true;
+ for (Node *E : nodes) {
+ if (_can_edit(E, name)) {
+ undo_redo->add_do_method(E, "remove_from_group", name);
+ undo_redo->add_undo_method(E, "add_to_group", name, true);
+ } else {
+ removed_all = false;
+ }
+ }
- if (removed_all) {
- undo_redo->add_do_method(this, "_delete_group_item", name);
- undo_redo->add_undo_method(this, "_add_group", name);
- }
+ if (removed_all) {
+ undo_redo->add_do_method(this, "_delete_group_item", name);
+ undo_redo->add_undo_method(this, "_add_group", name);
+ }
- undo_redo->add_do_method(this, "_group_selected");
- undo_redo->add_undo_method(this, "_group_selected");
- undo_redo->add_do_method(this, "emit_signal", "group_edited");
- undo_redo->add_undo_method(this, "emit_signal", "group_edited");
+ undo_redo->add_do_method(this, "_group_selected");
+ undo_redo->add_undo_method(this, "_group_selected");
+ undo_redo->add_do_method(this, "emit_signal", "group_edited");
+ undo_redo->add_undo_method(this, "emit_signal", "group_edited");
- // To force redraw of scene tree.
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ // To force redraw of scene tree.
+ undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->commit_action();
+ undo_redo->commit_action();
+ } break;
+ case COPY_GROUP: {
+ DisplayServer::get_singleton()->clipboard_set(ti->get_text(p_column));
+ } break;
+ }
}
void GroupDialog::_delete_group_item(const String &p_name) {
@@ -437,7 +445,7 @@ GroupDialog::GroupDialog() {
groups->set_v_size_flags(Control::SIZE_EXPAND_FILL);
groups->add_theme_constant_override("draw_guides", 1);
groups->connect("item_selected", callable_mp(this, &GroupDialog::_group_selected));
- groups->connect("button_pressed", callable_mp(this, &GroupDialog::_delete_group_pressed));
+ groups->connect("button_pressed", callable_mp(this, &GroupDialog::_modify_group_pressed));
groups->connect("item_edited", callable_mp(this, &GroupDialog::_group_renamed));
HBoxContainer *chbc = memnew(HBoxContainer);
@@ -582,7 +590,7 @@ void GroupsEditor::_add_group(const String &p_group) {
group_name->clear();
}
-void GroupsEditor::_remove_group(Object *p_item, int p_column, int p_id) {
+void GroupsEditor::_modify_group(Object *p_item, int p_column, int p_id) {
if (!node) {
return;
}
@@ -591,21 +599,26 @@ void GroupsEditor::_remove_group(Object *p_item, int p_column, int p_id) {
if (!ti) {
return;
}
+ switch (p_id) {
+ case DELETE_GROUP: {
+ String name = ti->get_text(0);
+ undo_redo->create_action(TTR("Remove from Group"));
- String name = ti->get_text(0);
-
- undo_redo->create_action(TTR("Remove from Group"));
-
- undo_redo->add_do_method(node, "remove_from_group", name);
- undo_redo->add_undo_method(node, "add_to_group", name, true);
- undo_redo->add_do_method(this, "update_tree");
- undo_redo->add_undo_method(this, "update_tree");
+ undo_redo->add_do_method(node, "remove_from_group", name);
+ undo_redo->add_undo_method(node, "add_to_group", name, true);
+ undo_redo->add_do_method(this, "update_tree");
+ undo_redo->add_undo_method(this, "update_tree");
- // To force redraw of scene tree.
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ // To force redraw of scene tree.
+ undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->commit_action();
+ undo_redo->commit_action();
+ } break;
+ case COPY_GROUP: {
+ DisplayServer::get_singleton()->clipboard_set(ti->get_text(p_column));
+ } break;
+ }
}
struct _GroupInfoComparator {
@@ -653,7 +666,8 @@ void GroupsEditor::update_tree() {
TreeItem *item = tree->create_item(root);
item->set_text(0, gi.name);
if (can_be_deleted) {
- item->add_button(0, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), 0);
+ item->add_button(0, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), DELETE_GROUP);
+ item->add_button(0, get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), COPY_GROUP);
} else {
item->set_selectable(0, false);
}
@@ -706,7 +720,7 @@ GroupsEditor::GroupsEditor() {
tree->set_hide_root(true);
tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vbc->add_child(tree);
- tree->connect("button_pressed", callable_mp(this, &GroupsEditor::_remove_group));
+ tree->connect("button_pressed", callable_mp(this, &GroupsEditor::_modify_group));
tree->add_theme_constant_override("draw_guides", 1);
add_theme_constant_override("separation", 3 * EDSCALE);
}
diff --git a/editor/groups_editor.h b/editor/groups_editor.h
index 69f746801f..f0a5b4a794 100644
--- a/editor/groups_editor.h
+++ b/editor/groups_editor.h
@@ -82,7 +82,7 @@ class GroupDialog : public AcceptDialog {
void _rename_group_item(const String &p_old_name, const String &p_new_name);
void _add_group(String p_name);
- void _delete_group_pressed(Object *p_item, int p_column, int p_id);
+ void _modify_group_pressed(Object *p_item, int p_column, int p_id);
void _delete_group_item(const String &p_name);
bool _can_edit(Node *p_node, String p_group);
@@ -95,6 +95,11 @@ protected:
static void _bind_methods();
public:
+ enum ModifyButton {
+ DELETE_GROUP,
+ COPY_GROUP,
+ };
+
void edit();
void set_undo_redo(UndoRedo *p_undoredo) { undo_redo = p_undoredo; }
@@ -116,8 +121,7 @@ class GroupsEditor : public VBoxContainer {
void update_tree();
void _add_group(const String &p_group = "");
- void _remove_group(Object *p_item, int p_column, int p_id);
- void _close();
+ void _modify_group(Object *p_item, int p_column, int p_id);
void _show_group_dialog();
@@ -125,6 +129,11 @@ protected:
static void _bind_methods();
public:
+ enum ModifyButton {
+ DELETE_GROUP,
+ COPY_GROUP,
+ };
+
void set_undo_redo(UndoRedo *p_undoredo) { undo_redo = p_undoredo; }
void set_current(Node *p_node);
diff --git a/editor/icons/Notification.svg b/editor/icons/Notification.svg
new file mode 100644
index 0000000000..1f1f9c3e15
--- /dev/null
+++ b/editor/icons/Notification.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12.089506 1.2353795c-.498141-.2384823-1.095292-.027987-1.333775.4701537-.01372.028981-.02341.059557-.03428.089693-.01467.023016-.02777.046925-.04071.071459-.04899-.00527-.09728-.00862-.146087-.011473-1.4730257-.6255101-3.1777024.0153376-3.8738627 1.4563251l-1.7272425 3.6078572s-.3364181.7034345-.8079671 1.1268133c-.00105.0009371-.00239.00174-.00344.00268-.2721193.1337295-.5707545.185826-.8605632.0470816-.4981411-.2384824-1.0952924-.0279876-1.3337749.4701537-.01605.033526-.029907.066894-.041944.1011828-.018769.030371-.036749.06319-.052515.096122-.2384825.4981412-.027988 1.0952923.4701537 1.3337751l9.0196437 4.318106c.498141.238482 1.095292.02799 1.333775-.470154.01577-.03293.0301-.0675.04191-.1012.0192-.03086.0365-.06257.05255-.0961.238483-.498141.02799-1.095292-.470153-1.333775-.901965-.43181-.03834-2.235739-.03834-2.235739l1.727237-3.6078618c.715447-1.4944233.08396-3.2858776-1.410461-4.0013247.238482-.4981411.02799-1.0952926-.470154-1.333775zm-5.5145786 11.3714015c-.322341.673306-.037829 1.480435.6354753 1.802775.6733031.32234 1.4804334.03783 1.8027749-.635476z" fill="#e6e6e6"/></svg>
diff --git a/editor/icons/NotificationDisabled.svg b/editor/icons/NotificationDisabled.svg
new file mode 100644
index 0000000000..0e4465bee8
--- /dev/null
+++ b/editor/icons/NotificationDisabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m11.705078 1.1386719c-.389281-.0180576-.770356.1928007-.949219.5664062-.01372.028981-.024286.0597078-.035156.0898438-.01467.023016-.026122.0477316-.039062.0722656-.04899-.00527-.097678-.0088657-.146485-.0117187-1.4730253-.6255102-3.1788394.0160437-3.8749998 1.4570312l-1.7265624 3.6074219s-.3370448.7035743-.8085938 1.1269531l-.0019531.0019531c-.2721193.1337295-.5715196.1856194-.8613281.046875-.4981413-.2384824-1.0955019-.0274382-1.3339844.4707031-.01605.0335262-.0289787.0672737-.0410156.1015626-.0187691.0303709-.0369684.0627711-.0527344.0957031-.2384825.4981412-.0293917 1.0955019.46875 1.3339841l.3398437.16211 10.8984379-6.9394535c-.263272-.3070418-.592225-.5660832-.980469-.7519531.238482-.4981411.027441-1.0935489-.470703-1.3320313-.124536-.0596206-.255006-.091637-.384766-.0976562zm2.435547 2.8652343a.94188849 1 0 0 0 -.566406.1386719l-12.1171878 7.7148439a.94188849 1 0 0 0 -.3222656 1.373047.94188849 1 0 0 0 1.2910156.341797l12.1171878-7.7148441a.94188849 1 0 0 0 .322265-1.3710938.94188849 1 0 0 0 -.724609-.4824219zm-.509766 3.2753907-7.3867184 4.7050781 5.0781254 2.431641c.498141.238482 1.095501.027442 1.333984-.470704.01577-.03293.031159-.067862.042969-.101562.0192-.03086.036684-.062173.052734-.095703.238483-.498141.02744-1.095501-.470703-1.333985-.901965-.431809-.039063-2.236328-.039062-2.236328zm-7.0566402 5.3281251c-.322341.673306-.0365856 1.480394.6367187 1.802734.6733031.32234 1.4803929.036588 1.8027344-.636718z" fill="#e6e6e6"/></svg>
diff --git a/editor/icons/Tabs.svg b/editor/icons/TabBar.svg
index e20a1a0131..e20a1a0131 100644
--- a/editor/icons/Tabs.svg
+++ b/editor/icons/TabBar.svg
diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h
index 86e9c5bde8..7d8fb3cac5 100644
--- a/editor/import/resource_importer_layered_texture.h
+++ b/editor/import/resource_importer_layered_texture.h
@@ -91,8 +91,6 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
- void update_imports();
-
virtual bool are_import_settings_valid(const String &p_path) const override;
virtual String get_import_settings_string() const override;
diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h
index 4f9adfff25..5fea8b75d6 100644
--- a/editor/plugins/abstract_polygon_2d_editor.h
+++ b/editor/plugins/abstract_polygon_2d_editor.h
@@ -106,7 +106,6 @@ protected:
void _wip_changed();
void _wip_close();
void _wip_cancel();
- bool _delete_point(const Vector2 &p_gpoint);
void _notification(int p_what);
void _node_removed(Node *p_node);
diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h
index fe98a91ab3..503e066894 100644
--- a/editor/plugins/animation_blend_space_1d_editor.h
+++ b/editor/plugins/animation_blend_space_1d_editor.h
@@ -109,8 +109,6 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
void _edit_point_pos(double);
void _open_editor();
- void _goto_parent();
-
EditorFileDialog *open_file;
Ref<AnimationNode> file_loaded;
void _file_opened(const String &p_file);
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index eb8db2eaba..26bcff891d 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -187,7 +187,6 @@ class AnimationPlayerEditor : public VBoxContainer {
void _scale_changed(const String &p_scale);
void _save_animation(String p_file);
void _load_animations(Vector<String> p_files);
- void _seek_frame_changed(const String &p_frame);
void _seek_value_changed(float p_value, bool p_set = false, bool p_timeline_only = false);
void _blend_editor_next_changed(const int p_idx);
@@ -217,7 +216,6 @@ class AnimationPlayerEditor : public VBoxContainer {
void _pin_pressed();
- AnimationPlayerEditor();
~AnimationPlayerEditor();
protected:
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index 286546f962..5fbf2833b2 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -285,7 +285,6 @@ class EditorAssetLibrary : public PanelContainer {
void _search_text_submitted(const String &p_text = "");
void _api_request(const String &p_request, RequestType p_request_type, const String &p_arguments = "");
void _http_request_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
- void _http_download_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
void _filter_debounce_timer_timeout();
void _repository_changed(int p_repository_id);
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 1965efbf30..286771ee08 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -421,8 +421,6 @@ private:
CanvasItem *ref_item;
- void _add_canvas_item(CanvasItem *p_canvas_item);
-
void _save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones = false);
void _restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones = false);
void _commit_canvas_item_state(List<CanvasItem *> p_canvas_items, String action_name, bool commit_bones = false);
@@ -434,7 +432,6 @@ private:
bool updating_scroll;
void _update_scroll(real_t);
void _update_scrollbars();
- void _append_canvas_item(CanvasItem *p_item);
void _snap_changed();
void _selection_result_pressed(int);
void _selection_menu_hide();
@@ -519,7 +516,6 @@ private:
const Node *p_current);
void _set_anchors_preset(Control::LayoutPreset p_preset);
- void _set_offsets_preset(Control::LayoutPreset p_preset);
void _set_anchors_and_offsets_preset(Control::LayoutPreset p_preset);
void _set_anchors_and_offsets_to_keep_ratio();
@@ -551,15 +547,9 @@ protected:
void _notification(int p_what);
static void _bind_methods();
- void end_drag();
- void box_selection_start(Point2 &click);
- bool box_selection_end();
HBoxContainer *get_panel_hb() { return hb; }
- template <class P, class C>
- void space_selected_items();
-
static CanvasItemEditor *singleton;
public:
diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h
index 6e8b9a34cf..091feae5fb 100644
--- a/editor/plugins/editor_preview_plugins.h
+++ b/editor/plugins/editor_preview_plugins.h
@@ -173,4 +173,22 @@ public:
EditorFontPreviewPlugin();
~EditorFontPreviewPlugin();
};
+
+class EditorTileMapPatternPreviewPlugin : public EditorResourcePreviewGenerator {
+ GDCLASS(EditorTileMapPatternPreviewPlugin, EditorResourcePreviewGenerator);
+
+ mutable SafeFlag preview_done;
+
+ void _preview_done(const Variant &p_udata);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual bool handles(const String &p_type) const override;
+ virtual Ref<Texture2D> generate(const RES &p_from, const Size2 &p_size) const override;
+
+ EditorTileMapPatternPreviewPlugin();
+ ~EditorTileMapPatternPreviewPlugin();
+};
#endif // EDITORPREVIEWPLUGINS_H
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 32a8bbf406..0096152cfe 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -2228,18 +2228,28 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_down", p_event)) {
cursor.x_rot -= Math_PI / 12.0;
+ view_type = VIEW_TYPE_USER;
+ _update_name();
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_up", p_event)) {
cursor.x_rot += Math_PI / 12.0;
+ view_type = VIEW_TYPE_USER;
+ _update_name();
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_right", p_event)) {
cursor.y_rot -= Math_PI / 12.0;
+ view_type = VIEW_TYPE_USER;
+ _update_name();
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_left", p_event)) {
cursor.y_rot += Math_PI / 12.0;
+ view_type = VIEW_TYPE_USER;
+ _update_name();
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_180", p_event)) {
cursor.y_rot += Math_PI;
+ view_type = VIEW_TYPE_USER;
+ _update_name();
}
if (ED_IS_SHORTCUT("spatial_editor/focus_origin", p_event)) {
_menu_option(VIEW_CENTER_TO_ORIGIN);
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 2d5aeaa981..5290a8804b 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -677,8 +677,6 @@ private:
void _register_all_gizmos();
- Node3DEditor();
-
void _selection_changed();
void _refresh_menu_icons();
diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h
index 04ab458eb5..943765d4e0 100644
--- a/editor/plugins/resource_preloader_editor_plugin.h
+++ b/editor/plugins/resource_preloader_editor_plugin.h
@@ -59,7 +59,6 @@ class ResourcePreloaderEditor : public PanelContainer {
ResourcePreloader *preloader;
void _load_pressed();
- void _load_scene_pressed();
void _files_load_request(const Vector<String> &p_paths);
void _paste_pressed();
void _remove_resource(const String &p_to_remove);
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 8caebc1c8c..2b0bdfd109 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -371,14 +371,11 @@ class ScriptEditor : public PanelContainer {
bool use_space_indentation;
bool convert_indent_on_save;
- void _trim_trailing_whitespace(TextEdit *tx);
-
void _goto_script_line2(int p_line);
void _goto_script_line(REF p_script, int p_line);
void _set_execution(REF p_script, int p_line);
void _clear_execution(REF p_script);
void _breaked(bool p_breaked, bool p_can_debug);
- void _update_window_menu();
void _script_created(Ref<Script> p_script);
void _set_breakpoint(REF p_scrpt, int p_line, bool p_enabled);
void _clear_breakpoints();
@@ -430,7 +427,6 @@ class ScriptEditor : public PanelContainer {
void _make_script_list_context_menu();
void _help_search(String p_text);
- void _help_index(String p_text);
void _history_forward();
void _history_back();
diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h
index 5e3b2fb8c1..9732384000 100644
--- a/editor/plugins/sprite_frames_editor_plugin.h
+++ b/editor/plugins/sprite_frames_editor_plugin.h
@@ -100,7 +100,6 @@ class SpriteFramesEditor : public HSplitContainer {
float min_sheet_zoom;
void _load_pressed();
- void _load_scene_pressed();
void _file_load_request(const Vector<String> &p_path, int p_at_pos = -1);
void _copy_pressed();
void _paste_pressed();
@@ -128,7 +127,6 @@ class SpriteFramesEditor : public HSplitContainer {
UndoRedo *undo_redo;
- bool _is_drop_valid(const Dictionary &p_drag_data, const Dictionary &p_item_data) const;
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 19e1b40a0d..127219546d 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -3204,7 +3204,7 @@ void ThemeEditor::_add_preview_tab(ThemeEditorPreview *p_preview_tab, const Stri
preview_tabs->add_tab(p_preview_name, p_icon);
preview_tabs_content->add_child(p_preview_tab);
- preview_tabs->set_tab_right_button(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("close"), SNAME("Tabs")));
+ preview_tabs->set_tab_right_button(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("close"), SNAME("TabBar")));
p_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked));
preview_tabs->set_current_tab(preview_tabs->get_tab_count() - 1);
@@ -3328,8 +3328,8 @@ ThemeEditor::ThemeEditor() {
preview_tabs_content->set_draw_behind_parent(true);
preview_tabs_vb->add_child(preview_tabs_content);
- preview_tabs = memnew(Tabs);
- preview_tabs->set_tab_align(Tabs::ALIGN_LEFT);
+ preview_tabs = memnew(TabBar);
+ preview_tabs->set_tab_align(TabBar::ALIGN_LEFT);
preview_tabs->set_h_size_flags(SIZE_EXPAND_FILL);
preview_tabbar_hb->add_child(preview_tabs);
preview_tabs->connect("tab_changed", callable_mp(this, &ThemeEditor::_change_preview_tab));
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index 5b0357e3f8..b6becbb1c7 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -34,7 +34,7 @@
#include "scene/gui/margin_container.h"
#include "scene/gui/option_button.h"
#include "scene/gui/scroll_container.h"
-#include "scene/gui/tabs.h"
+#include "scene/gui/tab_bar.h"
#include "scene/gui/texture_rect.h"
#include "scene/resources/theme.h"
#include "theme_editor_preview.h"
@@ -347,7 +347,6 @@ class ThemeTypeEditor : public MarginContainer {
void _update_type_items();
void _list_type_selected(int p_index);
- void _select_type(String p_type_name);
void _add_type_button_cbk();
void _add_default_type_items();
@@ -391,7 +390,7 @@ class ThemeEditor : public VBoxContainer {
Ref<Theme> theme;
- Tabs *preview_tabs;
+ TabBar *preview_tabs;
PanelContainer *preview_tabs_content;
Button *add_preview_button;
EditorFileDialog *preview_scene_dialog;
diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp
index 2a8a3216ed..efccac7b74 100644
--- a/editor/plugins/tiles/atlas_merging_dialog.cpp
+++ b/editor/plugins/tiles/atlas_merging_dialog.cpp
@@ -118,6 +118,7 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
output_image_texture.instantiate();
output_image_texture->create_from_image(output_image);
+ merged->set_name(p_atlas_sources[0]->get_name());
merged->set_texture(output_image_texture);
merged->set_texture_region_size(new_texture_region_size);
}
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 5ca39b82b2..5dfa44a353 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -36,6 +36,7 @@
#include "editor/editor_scale.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
+#include "scene/2d/camera_2d.h"
#include "scene/gui/center_container.h"
#include "scene/gui/split_container.h"
@@ -43,31 +44,11 @@
#include "core/math/geometry_2d.h"
#include "core/os/keyboard.h"
-void TileMapEditorTilesPlugin::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
- case NOTIFICATION_THEME_CHANGED:
- select_tool_button->set_icon(get_theme_icon(SNAME("ToolSelect"), SNAME("EditorIcons")));
- paint_tool_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
- line_tool_button->set_icon(get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons")));
- rect_tool_button->set_icon(get_theme_icon(SNAME("Rectangle"), SNAME("EditorIcons")));
- bucket_tool_button->set_icon(get_theme_icon(SNAME("Bucket"), SNAME("EditorIcons")));
-
- picker_button->set_icon(get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
- erase_button->set_icon(get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
-
- missing_atlas_texture_icon = get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons"));
- break;
- case NOTIFICATION_VISIBILITY_CHANGED:
- _stop_dragging();
- break;
- }
-}
-
void TileMapEditorTilesPlugin::tile_set_changed() {
_update_fix_selected_and_hovered();
_update_tile_set_sources_list();
- _update_bottom_panel();
+ _update_source_display();
+ _update_patterns_list();
}
void TileMapEditorTilesPlugin::_on_random_tile_checkbox_toggled(bool p_pressed) {
@@ -125,8 +106,19 @@ void TileMapEditorTilesPlugin::_update_toolbar() {
}
}
-Control *TileMapEditorTilesPlugin::get_toolbar() const {
- return toolbar;
+Vector<TileMapEditorPlugin::TabData> TileMapEditorTilesPlugin::get_tabs() const {
+ Vector<TileMapEditorPlugin::TabData> tabs;
+ tabs.push_back({ toolbar, tiles_bottom_panel });
+ tabs.push_back({ toolbar, patterns_bottom_panel });
+ return tabs;
+}
+
+void TileMapEditorTilesPlugin::_tab_changed() {
+ if (tiles_bottom_panel->is_visible_in_tree()) {
+ _update_selection_pattern_from_tileset_tiles_selection();
+ } else if (patterns_bottom_panel->is_visible_in_tree()) {
+ _update_selection_pattern_from_tileset_pattern_selection();
+ }
}
void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
@@ -152,22 +144,31 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
Ref<Texture2D> texture;
String item_text;
+ // Common to all type of sources.
+ if (!source->get_name().is_empty()) {
+ item_text = vformat(TTR("%s (id:%d)"), source->get_name(), source_id);
+ }
+
// Atlas source.
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
texture = atlas_source->get_texture();
- if (texture.is_valid()) {
- item_text = vformat("%s (ID: %d)", texture->get_path().get_file(), source_id);
- } else {
- item_text = vformat("No Texture Atlas Source (ID: %d)", source_id);
+ if (item_text.is_empty()) {
+ if (texture.is_valid()) {
+ item_text = vformat("%s (ID: %d)", texture->get_path().get_file(), source_id);
+ } else {
+ item_text = vformat("No Texture Atlas Source (ID: %d)", source_id);
+ }
}
}
// Scene collection source.
TileSetScenesCollectionSource *scene_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
if (scene_collection_source) {
- texture = get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
- item_text = vformat(TTR("Scene Collection Source (ID: %d)"), source_id);
+ texture = tiles_bottom_panel->get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
+ if (item_text.is_empty()) {
+ item_text = vformat(TTR("Scene Collection Source (ID: %d)"), source_id);
+ }
}
// Use default if not valid.
@@ -193,10 +194,10 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
}
// Synchronize
- TilesEditor::get_singleton()->set_sources_lists_current(sources_list->get_current());
+ TilesEditorPlugin::get_singleton()->set_sources_lists_current(sources_list->get_current());
}
-void TileMapEditorTilesPlugin::_update_bottom_panel() {
+void TileMapEditorTilesPlugin::_update_source_display() {
// Update the atlas display.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
@@ -243,6 +244,81 @@ void TileMapEditorTilesPlugin::_update_bottom_panel() {
}
}
+void TileMapEditorTilesPlugin::_patterns_item_list_gui_input(const Ref<InputEvent> &p_event) {
+ 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;
+ }
+
+ if (ED_IS_SHORTCUT("tiles_editor/paste", p_event) && p_event->is_pressed() && !p_event->is_echo()) {
+ select_last_pattern = true;
+ int new_pattern_index = tile_set->get_patterns_count();
+ undo_redo->create_action(TTR("Add TileSet pattern"));
+ undo_redo->add_do_method(*tile_set, "add_pattern", tile_map_clipboard, new_pattern_index);
+ undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index);
+ undo_redo->commit_action();
+ patterns_item_list->accept_event();
+ }
+
+ if (ED_IS_SHORTCUT("tiles_editor/delete", p_event) && p_event->is_pressed() && !p_event->is_echo()) {
+ Vector<int> selected = patterns_item_list->get_selected_items();
+ undo_redo->create_action(TTR("Remove TileSet patterns"));
+ for (int i = 0; i < selected.size(); i++) {
+ int pattern_index = selected[i];
+ undo_redo->add_do_method(*tile_set, "remove_pattern", pattern_index);
+ undo_redo->add_undo_method(*tile_set, "add_pattern", tile_set->get_pattern(pattern_index), pattern_index);
+ }
+ undo_redo->commit_action();
+ patterns_item_list->accept_event();
+ }
+}
+
+void TileMapEditorTilesPlugin::_pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture) {
+ // TODO optimize ?
+ for (int i = 0; i < patterns_item_list->get_item_count(); i++) {
+ if (patterns_item_list->get_item_metadata(i) == p_pattern) {
+ patterns_item_list->set_item_icon(i, p_texture);
+ break;
+ }
+ }
+}
+
+void TileMapEditorTilesPlugin::_update_patterns_list() {
+ 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;
+ }
+
+ // Recreate the items.
+ patterns_item_list->clear();
+ for (int i = 0; i < tile_set->get_patterns_count(); i++) {
+ int id = patterns_item_list->add_item("");
+ patterns_item_list->set_item_metadata(id, tile_set->get_pattern(i));
+ TilesEditorPlugin::get_singleton()->queue_pattern_preview(tile_set, tile_set->get_pattern(i), callable_mp(this, &TileMapEditorTilesPlugin::_pattern_preview_done));
+ }
+
+ // Update the label visibility.
+ patterns_help_label->set_visible(patterns_item_list->get_item_count() == 0);
+
+ // Added a new pattern, thus select the last one.
+ if (select_last_pattern) {
+ patterns_item_list->select(tile_set->get_patterns_count() - 1);
+ patterns_item_list->grab_focus();
+ _update_selection_pattern_from_tileset_pattern_selection();
+ }
+ select_last_pattern = false;
+}
+
void TileMapEditorTilesPlugin::_update_atlas_view() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
@@ -260,7 +336,7 @@ void TileMapEditorTilesPlugin::_update_atlas_view() {
ERR_FAIL_COND(!atlas_source);
tile_atlas_view->set_atlas_source(*tile_map->get_tileset(), atlas_source, source_id);
- TilesEditor::get_singleton()->synchronize_atlas_view(tile_atlas_view);
+ TilesEditorPlugin::get_singleton()->synchronize_atlas_view(tile_atlas_view);
tile_atlas_control->update();
}
@@ -295,7 +371,7 @@ void TileMapEditorTilesPlugin::_update_scenes_collection_view() {
Variant udata = i;
EditorResourcePreview::get_singleton()->queue_edited_resource_preview(scene, this, "_scene_thumbnail_done", udata);
} else {
- item_index = scene_tiles_list->add_item(TTR("Tile with Invalid Scene"), get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons")));
+ item_index = scene_tiles_list->add_item(TTR("Tile with Invalid Scene"), tiles_bottom_panel->get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons")));
}
scene_tiles_list->set_item_metadata(item_index, scene_id);
@@ -351,19 +427,32 @@ void TileMapEditorTilesPlugin::_scenes_list_multi_selected(int p_index, bool p_s
}
}
- _update_selection_pattern_from_tileset_selection();
+ _update_selection_pattern_from_tileset_tiles_selection();
}
void TileMapEditorTilesPlugin::_scenes_list_nothing_selected() {
scene_tiles_list->deselect_all();
tile_set_selection.clear();
tile_map_selection.clear();
- selection_pattern->clear();
- _update_selection_pattern_from_tileset_selection();
+ selection_pattern.instantiate();
+ _update_selection_pattern_from_tileset_tiles_selection();
+}
+
+void TileMapEditorTilesPlugin::_update_theme() {
+ select_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("ToolSelect"), SNAME("EditorIcons")));
+ paint_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
+ line_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons")));
+ rect_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Rectangle"), SNAME("EditorIcons")));
+ bucket_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Bucket"), SNAME("EditorIcons")));
+
+ picker_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
+ erase_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
+
+ missing_atlas_texture_icon = tiles_bottom_panel->get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons"));
}
bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
- if (!is_visible_in_tree()) {
+ if (!(tiles_bottom_panel->is_visible_in_tree() || patterns_bottom_panel->is_visible_in_tree())) {
// If the bottom editor is not visible, we ignore inputs.
return false;
}
@@ -391,7 +480,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
if (ED_IS_SHORTCUT("tiles_editor/cut", p_event) || ED_IS_SHORTCUT("tiles_editor/copy", p_event)) {
// Fill in the clipboard.
if (!tile_map_selection.is_empty()) {
- memdelete(tile_map_clipboard);
+ tile_map_clipboard.instantiate();
TypedArray<Vector2i> coords_array;
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
coords_array.push_back(E->get());
@@ -624,7 +713,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
Vector2i tile_shape_size = tile_set->get_tile_size();
// Draw the selection.
- if (is_visible_in_tree() && tool_buttons_group->get_pressed_button() == select_tool_button) {
+ if ((tiles_bottom_panel->is_visible_in_tree() || patterns_bottom_panel->is_visible_in_tree()) && tool_buttons_group->get_pressed_button() == select_tool_button) {
// In select mode, we only draw the current selection if we are modifying it (pressing control or shift).
if (drag_type == DRAG_TYPE_MOVE || (drag_type == DRAG_TYPE_SELECT && !Input::get_singleton()->is_key_pressed(KEY_CTRL) && !Input::get_singleton()->is_key_pressed(KEY_SHIFT))) {
// Do nothing
@@ -636,7 +725,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
}
// Handle the preview of the tiles to be placed.
- if (is_visible_in_tree() && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered.
+ if ((tiles_bottom_panel->is_visible_in_tree() || patterns_bottom_panel->is_visible_in_tree()) && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered.
Map<Vector2i, TileMapCell> preview;
Rect2i drawn_grid_rect;
@@ -670,21 +759,23 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
}
tile_map->draw_cells_outline(p_overlay, to_draw, Color(1.0, 1.0, 1.0), xform);
} else if (drag_type == DRAG_TYPE_MOVE) {
- // Preview when moving.
- Vector2i top_left;
- if (!tile_map_selection.is_empty()) {
- top_left = tile_map_selection.front()->get();
- }
- for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
- top_left = top_left.min(E->get());
- }
- Vector2i offset = drag_start_mouse_pos - tile_map->map_to_world(top_left);
- offset = tile_map->world_to_map(drag_last_mouse_pos - offset) - tile_map->world_to_map(drag_start_mouse_pos - offset);
+ if (!(patterns_item_list->is_visible_in_tree() && patterns_item_list->has_point(patterns_item_list->get_local_mouse_position()))) {
+ // Preview when moving.
+ Vector2i top_left;
+ if (!tile_map_selection.is_empty()) {
+ top_left = tile_map_selection.front()->get();
+ }
+ for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
+ top_left = top_left.min(E->get());
+ }
+ Vector2i offset = drag_start_mouse_pos - tile_map->map_to_world(top_left);
+ offset = tile_map->world_to_map(drag_last_mouse_pos - offset) - tile_map->world_to_map(drag_start_mouse_pos - offset);
- TypedArray<Vector2i> selection_used_cells = selection_pattern->get_used_cells();
- for (int i = 0; i < selection_used_cells.size(); i++) {
- Vector2i coords = tile_map->map_pattern(offset + top_left, selection_used_cells[i], selection_pattern);
- preview[coords] = TileMapCell(selection_pattern->get_cell_source_id(selection_used_cells[i]), selection_pattern->get_cell_atlas_coords(selection_used_cells[i]), selection_pattern->get_cell_alternative_tile(selection_used_cells[i]));
+ TypedArray<Vector2i> selection_used_cells = selection_pattern->get_used_cells();
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ Vector2i coords = tile_map->map_pattern(offset + top_left, selection_used_cells[i], selection_pattern);
+ preview[coords] = TileMapCell(selection_pattern->get_cell_source_id(selection_used_cells[i]), selection_pattern->get_cell_atlas_coords(selection_used_cells[i]), selection_pattern->get_cell_alternative_tile(selection_used_cells[i]));
+ }
}
} else if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) {
// Preview when pasting.
@@ -823,7 +914,7 @@ void TileMapEditorTilesPlugin::_mouse_exited_viewport() {
CanvasItemEditor::get_singleton()->update_viewport();
}
-TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(const TileMapPattern *p_pattern) {
+TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(Ref<TileMapPattern> p_pattern) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return TileMapCell();
@@ -887,9 +978,10 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_
}
// Get or create the pattern.
- TileMapPattern erase_pattern;
- erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- TileMapPattern *pattern = _is_erasing() ? &erase_pattern : selection_pattern;
+ 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;
Map<Vector2i, TileMapCell> output;
if (!pattern->is_empty()) {
@@ -939,9 +1031,11 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start
rect.size += Vector2i(1, 1);
// Get or create the pattern.
- TileMapPattern erase_pattern;
- erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- TileMapPattern *pattern = _is_erasing() ? &erase_pattern : selection_pattern;
+ 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;
+
Map<Vector2i, TileMapCell> err_output;
ERR_FAIL_COND_V(pattern->is_empty(), err_output);
@@ -998,9 +1092,10 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
}
// Get or create the pattern.
- TileMapPattern erase_pattern;
- erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- TileMapPattern *pattern = _is_erasing() ? &erase_pattern : selection_pattern;
+ 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;
if (!pattern->is_empty()) {
TileMapCell source = tile_map->get_cell(tile_map_layer, p_coords);
@@ -1144,60 +1239,80 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
_update_tileset_selection_from_selection_pattern();
} break;
case DRAG_TYPE_MOVE: {
- Vector2i top_left;
- if (!tile_map_selection.is_empty()) {
- top_left = tile_map_selection.front()->get();
- }
- for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
- top_left = top_left.min(E->get());
- }
+ if (patterns_item_list->is_visible_in_tree() && patterns_item_list->has_point(patterns_item_list->get_local_mouse_position())) {
+ // Restore the cells.
+ for (KeyValue<Vector2i, TileMapCell> kv : drag_modified) {
+ tile_map->set_cell(tile_map_layer, kv.key, kv.value.source_id, kv.value.get_atlas_coords(), kv.value.alternative_tile);
+ }
+
+ // Creating a pattern in the pattern list.
+ select_last_pattern = true;
+ int new_pattern_index = tile_set->get_patterns_count();
+ undo_redo->create_action(TTR("Add TileSet pattern"));
+ undo_redo->add_do_method(*tile_set, "add_pattern", selection_pattern, new_pattern_index);
+ undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index);
+ undo_redo->commit_action();
+ } else {
+ // Get the top-left cell.
+ Vector2i top_left;
+ if (!tile_map_selection.is_empty()) {
+ top_left = tile_map_selection.front()->get();
+ }
+ for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
+ top_left = top_left.min(E->get());
+ }
- Vector2i offset = drag_start_mouse_pos - tile_map->map_to_world(top_left);
- offset = tile_map->world_to_map(mpos - offset) - tile_map->world_to_map(drag_start_mouse_pos - offset);
+ // Get the offset from the mouse.
+ Vector2i offset = drag_start_mouse_pos - tile_map->map_to_world(top_left);
+ offset = tile_map->world_to_map(mpos - offset) - tile_map->world_to_map(drag_start_mouse_pos - offset);
- TypedArray<Vector2i> selection_used_cells = selection_pattern->get_used_cells();
+ TypedArray<Vector2i> selection_used_cells = selection_pattern->get_used_cells();
- Vector2i coords;
- Map<Vector2i, TileMapCell> cells_undo;
- for (int i = 0; i < selection_used_cells.size(); i++) {
- coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
- cells_undo[coords] = TileMapCell(drag_modified[coords].source_id, drag_modified[coords].get_atlas_coords(), drag_modified[coords].alternative_tile);
- coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
- cells_undo[coords] = TileMapCell(tile_map->get_cell_source_id(tile_map_layer, coords), tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords));
- }
+ // Build the list of cells to undo.
+ Vector2i coords;
+ Map<Vector2i, TileMapCell> cells_undo;
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
+ cells_undo[coords] = TileMapCell(drag_modified[coords].source_id, drag_modified[coords].get_atlas_coords(), drag_modified[coords].alternative_tile);
+ coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
+ cells_undo[coords] = TileMapCell(tile_map->get_cell_source_id(tile_map_layer, coords), tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords));
+ }
- Map<Vector2i, TileMapCell> cells_do;
- for (int i = 0; i < selection_used_cells.size(); i++) {
- coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
- cells_do[coords] = TileMapCell();
- }
- for (int i = 0; i < selection_used_cells.size(); i++) {
- coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
- cells_do[coords] = TileMapCell(selection_pattern->get_cell_source_id(selection_used_cells[i]), selection_pattern->get_cell_atlas_coords(selection_used_cells[i]), selection_pattern->get_cell_alternative_tile(selection_used_cells[i]));
- }
- undo_redo->create_action(TTR("Move tiles"));
- // Move the tiles.
- for (const KeyValue<Vector2i, TileMapCell> &E : cells_do) {
- 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);
- }
- for (const KeyValue<Vector2i, TileMapCell> &E : cells_undo) {
- 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);
- }
+ // Build the list of cells to do.
+ Map<Vector2i, TileMapCell> cells_do;
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
+ cells_do[coords] = TileMapCell();
+ }
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
+ cells_do[coords] = TileMapCell(selection_pattern->get_cell_source_id(selection_used_cells[i]), selection_pattern->get_cell_atlas_coords(selection_used_cells[i]), selection_pattern->get_cell_alternative_tile(selection_used_cells[i]));
+ }
- // Update the selection.
- undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
- tile_map_selection.clear();
- for (int i = 0; i < selection_used_cells.size(); i++) {
- coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
- tile_map_selection.insert(coords);
+ // Move the tiles.
+ undo_redo->create_action(TTR("Move tiles"));
+ for (Map<Vector2i, TileMapCell>::Element *E = cells_do.front(); E; E = E->next()) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ for (Map<Vector2i, TileMapCell>::Element *E = cells_undo.front(); E; E = E->next()) {
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+
+ // Update the selection.
+ undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
+ tile_map_selection.clear();
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
+ tile_map_selection.insert(coords);
+ }
+ undo_redo->add_do_method(this, "_set_tile_map_selection", _get_tile_map_selection());
+ undo_redo->commit_action();
}
- undo_redo->add_do_method(this, "_set_tile_map_selection", _get_tile_map_selection());
- undo_redo->commit_action();
} break;
case DRAG_TYPE_PICK: {
Rect2i rect = Rect2i(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos) - tile_map->world_to_map(drag_start_mouse_pos)).abs();
rect.size += Vector2i(1, 1);
- memdelete(selection_pattern);
+
TypedArray<Vector2i> coords_array;
for (int x = rect.position.x; x < rect.get_end().x; x++) {
for (int y = rect.position.y; y < rect.get_end().y; y++) {
@@ -1207,11 +1322,10 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
}
}
}
- selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
- if (!selection_pattern->is_empty()) {
+ Ref<TileMapPattern> new_selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
+ if (!new_selection_pattern->is_empty()) {
+ selection_pattern = new_selection_pattern;
_update_tileset_selection_from_selection_pattern();
- } else {
- _update_selection_pattern_from_tileset_selection();
}
picker_button->set_pressed(false);
} break;
@@ -1279,8 +1393,9 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
tile_set_selection.clear();
+ patterns_item_list->deselect_all();
tile_map_selection.clear();
- selection_pattern->clear();
+ selection_pattern.instantiate();
return;
}
@@ -1290,8 +1405,9 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
tile_set_selection.clear();
+ patterns_item_list->deselect_all();
tile_map_selection.clear();
- selection_pattern->clear();
+ selection_pattern.instantiate();
return;
}
@@ -1301,8 +1417,9 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
tile_set_selection.clear();
+ patterns_item_list->deselect_all();
tile_map_selection.clear();
- selection_pattern->clear();
+ selection_pattern.instantiate();
return;
}
@@ -1330,8 +1447,10 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
if (!tile_map_selection.is_empty()) {
_update_selection_pattern_from_tilemap_selection();
+ } else if (tiles_bottom_panel->is_visible_in_tree()) {
+ _update_selection_pattern_from_tileset_tiles_selection();
} else {
- _update_selection_pattern_from_tileset_selection();
+ _update_selection_pattern_from_tileset_pattern_selection();
}
}
@@ -1364,9 +1483,14 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection(
return;
}
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count());
- memdelete(selection_pattern);
+ selection_pattern.instantiate();
TypedArray<Vector2i> coords_array;
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
@@ -1375,7 +1499,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection(
selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
}
-void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection() {
+void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_tiles_selection() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return;
@@ -1390,7 +1514,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
tile_map_selection.clear();
// Clear the selected pattern.
- selection_pattern->clear();
+ selection_pattern.instantiate();
// Group per source.
Map<int, List<const TileMapCell *>> per_source;
@@ -1448,6 +1572,30 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
CanvasItemEditor::get_singleton()->update_viewport();
}
+void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_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;
+ }
+
+ // Clear the tilemap selection.
+ tile_map_selection.clear();
+
+ // Clear the selected pattern.
+ selection_pattern.instantiate();
+
+ if (patterns_item_list->get_selected_items().size() >= 1) {
+ selection_pattern = patterns_item_list->get_item_metadata(patterns_item_list->get_selected_items()[0]);
+ }
+
+ CanvasItemEditor::get_singleton()->update_viewport();
+}
+
void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern() {
tile_set_selection.clear();
TypedArray<Vector2i> used_cells = selection_pattern->get_used_cells();
@@ -1457,7 +1605,7 @@ void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern(
tile_set_selection.insert(TileMapCell(selection_pattern->get_cell_source_id(coords), selection_pattern->get_cell_atlas_coords(coords), selection_pattern->get_cell_alternative_tile(coords)));
}
}
- _update_bottom_panel();
+ _update_source_display();
tile_atlas_control->update();
alternative_tiles_control->update();
}
@@ -1607,7 +1755,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven
tile_set_selection.insert(TileMapCell(source_id, hovered_tile.get_atlas_coords(), 0));
}
}
- _update_selection_pattern_from_tileset_selection();
+ _update_selection_pattern_from_tileset_tiles_selection();
} else { // Released
if (tile_set_dragging_selection) {
if (!mb->is_shift_pressed()) {
@@ -1644,7 +1792,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven
}
}
}
- _update_selection_pattern_from_tileset_selection();
+ _update_selection_pattern_from_tileset_tiles_selection();
}
tile_set_dragging_selection = false;
}
@@ -1764,7 +1912,7 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input(const Ref<In
tile_set_selection.insert(TileMapCell(source_id, hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile));
}
}
- _update_selection_pattern_from_tileset_selection();
+ _update_selection_pattern_from_tileset_tiles_selection();
}
tile_atlas_control->update();
alternative_tiles_control->update();
@@ -1797,8 +1945,9 @@ void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer
// Clear the selection.
tile_set_selection.clear();
+ patterns_item_list->deselect_all();
tile_map_selection.clear();
- selection_pattern->clear();
+ selection_pattern.instantiate();
}
tile_map_layer = p_tile_map_layer;
@@ -1820,9 +1969,13 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
ED_SHORTCUT("tiles_editor/cancel", TTR("Cancel"), KEY_ESCAPE);
ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), KEY_DELETE);
+ // --- Initialize references ---
+ tile_map_clipboard.instantiate();
+ selection_pattern.instantiate();
+
// --- Toolbar ---
toolbar = memnew(HBoxContainer);
- toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
+ toolbar->set_h_size_flags(Control::SIZE_EXPAND_FILL);
HBoxContainer *tilemap_tiles_tools_buttons = memnew(HBoxContainer);
@@ -1933,42 +2086,47 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
paint_tool_button->set_pressed(true);
_update_toolbar();
- // --- Bottom panel ---
- set_name("Tiles");
+ // --- Bottom panel tiles ---
+ tiles_bottom_panel = memnew(VBoxContainer);
+ tiles_bottom_panel->connect("tree_entered", callable_mp(this, &TileMapEditorTilesPlugin::_update_theme));
+ tiles_bottom_panel->connect("theme_changed", callable_mp(this, &TileMapEditorTilesPlugin::_update_theme));
+ tiles_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_stop_dragging));
+ tiles_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_tab_changed));
+ tiles_bottom_panel->set_name(TTR("Tiles"));
missing_source_label = memnew(Label);
missing_source_label->set_text(TTR("This TileMap's TileSet has no source configured. Edit the TileSet resource to add one."));
- missing_source_label->set_h_size_flags(SIZE_EXPAND_FILL);
- missing_source_label->set_v_size_flags(SIZE_EXPAND_FILL);
+ missing_source_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ missing_source_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
missing_source_label->set_align(Label::ALIGN_CENTER);
missing_source_label->set_valign(Label::VALIGN_CENTER);
missing_source_label->hide();
- add_child(missing_source_label);
+ tiles_bottom_panel->add_child(missing_source_label);
atlas_sources_split_container = memnew(HSplitContainer);
- atlas_sources_split_container->set_h_size_flags(SIZE_EXPAND_FILL);
- atlas_sources_split_container->set_v_size_flags(SIZE_EXPAND_FILL);
- add_child(atlas_sources_split_container);
+ atlas_sources_split_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ atlas_sources_split_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ tiles_bottom_panel->add_child(atlas_sources_split_container);
sources_list = memnew(ItemList);
sources_list->set_fixed_icon_size(Size2i(60, 60) * EDSCALE);
- sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
+ sources_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
sources_list->set_stretch_ratio(0.25);
sources_list->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_fix_selected_and_hovered).unbind(1));
- sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_bottom_panel).unbind(1));
- sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_sources_lists_current));
- sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_sources_list), varray(sources_list));
+ sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_source_display).unbind(1));
+ sources_list->connect("item_selected", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_sources_lists_current));
+ sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list), varray(sources_list));
atlas_sources_split_container->add_child(sources_list);
// Tile atlas source.
tile_atlas_view = memnew(TileAtlasView);
- tile_atlas_view->set_h_size_flags(SIZE_EXPAND_FILL);
- tile_atlas_view->set_v_size_flags(SIZE_EXPAND_FILL);
+ tile_atlas_view->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ tile_atlas_view->set_v_size_flags(Control::SIZE_EXPAND_FILL);
tile_atlas_view->set_texture_grid_visible(false);
tile_atlas_view->set_tile_shape_grid_visible(false);
- tile_atlas_view->connect("transform_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_view_transform));
+ tile_atlas_view->connect("transform_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_atlas_view_transform));
atlas_sources_split_container->add_child(tile_atlas_view);
tile_atlas_control = memnew(Control);
@@ -1985,8 +2143,8 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
// Scenes collection source.
scene_tiles_list = memnew(ItemList);
- scene_tiles_list->set_h_size_flags(SIZE_EXPAND_FILL);
- scene_tiles_list->set_v_size_flags(SIZE_EXPAND_FILL);
+ scene_tiles_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ scene_tiles_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
scene_tiles_list->set_drag_forwarding(this);
scene_tiles_list->set_select_mode(ItemList::SELECT_MULTI);
scene_tiles_list->connect("multi_selected", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_multi_selected));
@@ -1997,30 +2155,42 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
// Invalid source label.
invalid_source_label = memnew(Label);
invalid_source_label->set_text(TTR("Invalid source selected."));
- invalid_source_label->set_h_size_flags(SIZE_EXPAND_FILL);
- invalid_source_label->set_v_size_flags(SIZE_EXPAND_FILL);
+ invalid_source_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ invalid_source_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
invalid_source_label->set_align(Label::ALIGN_CENTER);
invalid_source_label->set_valign(Label::VALIGN_CENTER);
invalid_source_label->hide();
atlas_sources_split_container->add_child(invalid_source_label);
- _update_bottom_panel();
+ // --- Bottom panel patterns ---
+ patterns_bottom_panel = memnew(VBoxContainer);
+ patterns_bottom_panel->set_name(TTR("Patterns"));
+ patterns_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_tab_changed));
+
+ int thumbnail_size = 64;
+ patterns_item_list = memnew(ItemList);
+ patterns_item_list->set_max_columns(0);
+ patterns_item_list->set_icon_mode(ItemList::ICON_MODE_TOP);
+ patterns_item_list->set_fixed_column_width(thumbnail_size * 3 / 2);
+ patterns_item_list->set_max_text_lines(2);
+ patterns_item_list->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size));
+ patterns_item_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ patterns_item_list->connect("gui_input", callable_mp(this, &TileMapEditorTilesPlugin::_patterns_item_list_gui_input));
+ patterns_item_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection).unbind(1));
+ patterns_item_list->connect("item_activated", callable_mp(this, &TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection));
+ patterns_item_list->connect("nothing_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection));
+ patterns_bottom_panel->add_child(patterns_item_list);
+
+ patterns_help_label = memnew(Label);
+ patterns_help_label->set_text(TTR("Drag and drop or paste a TileMap selection here to store a pattern."));
+ patterns_help_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER);
+ patterns_item_list->add_child(patterns_help_label);
+
+ // Update.
+ _update_source_display();
}
TileMapEditorTilesPlugin::~TileMapEditorTilesPlugin() {
- memdelete(selection_pattern);
- memdelete(tile_map_clipboard);
-}
-
-void TileMapEditorTerrainsPlugin::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
- case NOTIFICATION_THEME_CHANGED:
- paint_tool_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
- picker_button->set_icon(get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
- erase_button->set_icon(get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
- break;
- }
}
void TileMapEditorTerrainsPlugin::tile_set_changed() {
@@ -2043,8 +2213,10 @@ void TileMapEditorTerrainsPlugin::_update_toolbar() {
}
}
-Control *TileMapEditorTerrainsPlugin::get_toolbar() const {
- return toolbar;
+Vector<TileMapEditorPlugin::TabData> TileMapEditorTerrainsPlugin::get_tabs() const {
+ Vector<TileMapEditorPlugin::TabData> tabs;
+ tabs.push_back({ toolbar, main_vbox_container });
+ return tabs;
}
Map<Vector2i, TileSet::CellNeighbor> TileMapEditorTerrainsPlugin::Constraint::get_overlapping_coords_and_peering_bits() const {
@@ -2802,7 +2974,7 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
}
bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
- if (!is_visible_in_tree()) {
+ if (!main_vbox_container->is_visible_in_tree()) {
// If the bottom editor is not visible, we ignore inputs.
return false;
}
@@ -3074,13 +3246,13 @@ void TileMapEditorTerrainsPlugin::_update_terrains_tree() {
TreeItem *terrain_set_tree_item = terrains_tree->create_item();
String matches;
if (tile_set->get_terrain_set_mode(terrain_set_index) == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
- terrain_set_tree_item->set_icon(0, get_theme_icon(SNAME("TerrainMatchCornersAndSides"), SNAME("EditorIcons")));
+ terrain_set_tree_item->set_icon(0, main_vbox_container->get_theme_icon(SNAME("TerrainMatchCornersAndSides"), SNAME("EditorIcons")));
matches = String(TTR("Matches Corners and Sides"));
} else if (tile_set->get_terrain_set_mode(terrain_set_index) == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
- terrain_set_tree_item->set_icon(0, get_theme_icon(SNAME("TerrainMatchCorners"), SNAME("EditorIcons")));
+ terrain_set_tree_item->set_icon(0, main_vbox_container->get_theme_icon(SNAME("TerrainMatchCorners"), SNAME("EditorIcons")));
matches = String(TTR("Matches Corners Only"));
} else {
- terrain_set_tree_item->set_icon(0, get_theme_icon(SNAME("TerrainMatchSides"), SNAME("EditorIcons")));
+ terrain_set_tree_item->set_icon(0, main_vbox_container->get_theme_icon(SNAME("TerrainMatchSides"), SNAME("EditorIcons")));
matches = String(TTR("Matches Sides Only"));
}
terrain_set_tree_item->set_text(0, vformat("Terrain Set %d (%s)", terrain_set_index, matches));
@@ -3185,6 +3357,12 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
}
}
+void TileMapEditorTerrainsPlugin::_update_theme() {
+ paint_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Edit"), 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")));
+}
+
void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) {
_stop_dragging(); // Avoids staying in a wrong drag state.
@@ -3197,15 +3375,18 @@ void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_la
}
TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
- set_name("Terrains");
+ main_vbox_container = memnew(VBoxContainer);
+ main_vbox_container->connect("tree_entered", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_theme));
+ main_vbox_container->connect("theme_changed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_theme));
+ main_vbox_container->set_name("Terrains");
HSplitContainer *tilemap_tab_terrains = memnew(HSplitContainer);
- tilemap_tab_terrains->set_h_size_flags(SIZE_EXPAND_FILL);
- tilemap_tab_terrains->set_v_size_flags(SIZE_EXPAND_FILL);
- add_child(tilemap_tab_terrains);
+ tilemap_tab_terrains->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ tilemap_tab_terrains->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ main_vbox_container->add_child(tilemap_tab_terrains);
terrains_tree = memnew(Tree);
- terrains_tree->set_h_size_flags(SIZE_EXPAND_FILL);
+ terrains_tree->set_h_size_flags(Control::SIZE_EXPAND_FILL);
terrains_tree->set_stretch_ratio(0.25);
terrains_tree->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
terrains_tree->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
@@ -3214,7 +3395,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
tilemap_tab_terrains->add_child(terrains_tree);
terrains_tile_list = memnew(ItemList);
- terrains_tile_list->set_h_size_flags(SIZE_EXPAND_FILL);
+ terrains_tile_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
terrains_tile_list->set_max_columns(0);
terrains_tile_list->set_same_column_width(true);
terrains_tile_list->set_fixed_icon_size(Size2(30, 30) * EDSCALE);
@@ -3281,7 +3462,7 @@ void TileMapEditor::_notification(int p_what) {
if (is_visible_in_tree() && tileset_changed_needs_update) {
_update_bottom_panel();
_update_layers_selection();
- tile_map_editor_plugins[tabs->get_current_tab()]->tile_set_changed();
+ tabs_plugins[tabs_bar->get_current_tab()]->tile_set_changed();
CanvasItemEditor::get_singleton()->update_viewport();
tileset_changed_needs_update = false;
}
@@ -3405,14 +3586,11 @@ void TileMapEditor::_update_bottom_panel() {
// Update the visibility of controls.
missing_tileset_label->set_visible(!tile_set.is_valid());
- if (!tile_set.is_valid()) {
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tile_map_editor_plugins[i]->hide();
- }
- } else {
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tile_map_editor_plugins[i]->set_visible(i == tabs->get_current_tab());
- }
+ for (unsigned int tab_index = 0; tab_index < tabs_data.size(); tab_index++) {
+ tabs_data[tab_index].panel->hide();
+ }
+ if (tile_set.is_valid()) {
+ tabs_data[tabs_bar->get_current_tab()].panel->show();
}
}
@@ -3495,27 +3673,25 @@ void TileMapEditor::_tile_map_changed() {
void TileMapEditor::_tab_changed(int p_tab_id) {
// Make the plugin edit the correct tilemap.
- tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer);
+ tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer);
// Update toolbar.
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tile_map_editor_plugins[i]->get_toolbar()->set_visible(i == p_tab_id);
+ for (unsigned int tab_index = 0; tab_index < tabs_data.size(); tab_index++) {
+ tabs_data[tab_index].toolbar->hide();
}
+ tabs_data[p_tab_id].toolbar->show();
// Update visible panel.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (!tile_map || !tile_map->get_tileset().is_valid()) {
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tile_map_editor_plugins[i]->hide();
- }
- } else {
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tile_map_editor_plugins[i]->set_visible(i == tabs->get_current_tab());
- }
+ for (unsigned int tab_index = 0; tab_index < tabs_data.size(); tab_index++) {
+ tabs_data[tab_index].panel->hide();
+ }
+ if (tile_map && tile_map->get_tileset().is_valid()) {
+ tabs_data[tabs_bar->get_current_tab()].panel->show();
}
// Graphical update.
- tile_map_editor_plugins[tabs->get_current_tab()]->update();
+ tabs_data[tabs_bar->get_current_tab()].panel->update();
CanvasItemEditor::get_singleton()->update_viewport();
}
@@ -3602,7 +3778,7 @@ void TileMapEditor::_update_layers_selection() {
layers_selection_button->set_custom_minimum_size(min_button_size);
layers_selection_button->update();
- tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer);
+ tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer);
}
void TileMapEditor::_move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
@@ -3688,7 +3864,7 @@ bool TileMapEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
return true;
}
- return tile_map_editor_plugins[tabs->get_current_tab()]->forward_canvas_gui_input(p_event);
+ return tabs_plugins[tabs_bar->get_current_tab()]->forward_canvas_gui_input(p_event);
}
void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
@@ -3823,7 +3999,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
}*/
// Draw the plugins.
- tile_map_editor_plugins[tabs->get_current_tab()]->forward_canvas_draw_over_viewport(p_overlay);
+ tabs_plugins[tabs_bar->get_current_tab()]->forward_canvas_draw_over_viewport(p_overlay);
}
void TileMapEditor::edit(TileMap *p_tile_map) {
@@ -3857,7 +4033,7 @@ void TileMapEditor::edit(TileMap *p_tile_map) {
_update_layers_selection();
// Call the plugins.
- tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer);
+ tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer);
_tile_map_changed();
}
@@ -3873,25 +4049,33 @@ TileMapEditor::TileMapEditor() {
tile_map_editor_plugins.push_back(memnew(TileMapEditorTilesPlugin));
tile_map_editor_plugins.push_back(memnew(TileMapEditorTerrainsPlugin));
- // Tabs.
- tabs = memnew(Tabs);
- tabs->set_clip_tabs(false);
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tabs->add_tab(tile_map_editor_plugins[i]->get_name());
+ // TabBar.
+ tabs_bar = memnew(TabBar);
+ tabs_bar->set_clip_tabs(false);
+ for (int plugin_index = 0; plugin_index < tile_map_editor_plugins.size(); plugin_index++) {
+ Vector<TileMapEditorPlugin::TabData> tabs_vector = tile_map_editor_plugins[plugin_index]->get_tabs();
+ for (int tab_index = 0; tab_index < tabs_vector.size(); tab_index++) {
+ tabs_bar->add_tab(tabs_vector[tab_index].panel->get_name());
+ tabs_data.push_back(tabs_vector[tab_index]);
+ tabs_plugins.push_back(tile_map_editor_plugins[plugin_index]);
+ }
}
- tabs->connect("tab_changed", callable_mp(this, &TileMapEditor::_tab_changed));
+ tabs_bar->connect("tab_changed", callable_mp(this, &TileMapEditor::_tab_changed));
// --- TileMap toolbar ---
tile_map_toolbar = memnew(HBoxContainer);
tile_map_toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_child(tile_map_toolbar);
// Tabs.
- tile_map_toolbar->add_child(tabs);
+ tile_map_toolbar->add_child(tabs_bar);
// Tabs toolbars.
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tile_map_editor_plugins[i]->get_toolbar()->hide();
- tile_map_toolbar->add_child(tile_map_editor_plugins[i]->get_toolbar());
+ for (unsigned int tab_index = 0; tab_index < tabs_data.size(); tab_index++) {
+ tabs_data[tab_index].toolbar->hide();
+ if (!tabs_data[tab_index].toolbar->get_parent()) {
+ tile_map_toolbar->add_child(tabs_data[tab_index].toolbar);
+ }
}
// Wide empty separation control.
@@ -3947,11 +4131,11 @@ TileMapEditor::TileMapEditor() {
missing_tileset_label->hide();
add_child(missing_tileset_label);
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- add_child(tile_map_editor_plugins[i]);
- tile_map_editor_plugins[i]->set_h_size_flags(SIZE_EXPAND_FILL);
- tile_map_editor_plugins[i]->set_v_size_flags(SIZE_EXPAND_FILL);
- tile_map_editor_plugins[i]->set_visible(i == 0);
+ for (unsigned int tab_index = 0; tab_index < tabs_data.size(); tab_index++) {
+ add_child(tabs_data[tab_index].panel);
+ tabs_data[tab_index].panel->set_v_size_flags(SIZE_EXPAND_FILL);
+ tabs_data[tab_index].panel->set_visible(tab_index == 0);
+ tabs_data[tab_index].panel->set_h_size_flags(SIZE_EXPAND_FILL);
}
_tab_changed(0);
@@ -3961,4 +4145,7 @@ TileMapEditor::TileMapEditor() {
}
TileMapEditor::~TileMapEditor() {
+ for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
+ memdelete(tile_map_editor_plugins[i]);
+ }
}
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index 5fbd9cada8..1f1a560113 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -33,17 +33,24 @@
#include "tile_atlas_view.h"
+#include "core/os/thread.h"
#include "core/typedefs.h"
#include "editor/editor_node.h"
#include "scene/2d/tile_map.h"
#include "scene/gui/box_container.h"
-#include "scene/gui/tabs.h"
+#include "scene/gui/tab_bar.h"
-class TileMapEditorPlugin : public VBoxContainer {
+class TileMapEditorPlugin : public Object {
public:
- virtual Control *get_toolbar() const {
- return memnew(Control);
+ struct TabData {
+ Control *toolbar;
+ Control *panel;
};
+
+ virtual Vector<TabData> get_tabs() const {
+ return Vector<TabData>();
+ };
+
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; };
virtual void forward_canvas_draw_over_viewport(Control *p_overlay){};
virtual void tile_set_changed(){};
@@ -106,7 +113,7 @@ private:
Map<Vector2i, TileMapCell> drag_modified;
bool rmb_erasing = false;
- TileMapCell _pick_random_tile(const TileMapPattern *p_pattern);
+ 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);
@@ -115,20 +122,25 @@ private:
///// Selection system. /////
Set<Vector2i> tile_map_selection;
- TileMapPattern *tile_map_clipboard = memnew(TileMapPattern);
- TileMapPattern *selection_pattern = memnew(TileMapPattern);
+ Ref<TileMapPattern> tile_map_clipboard;
+ Ref<TileMapPattern> selection_pattern;
void _set_tile_map_selection(const TypedArray<Vector2i> &p_selection);
TypedArray<Vector2i> _get_tile_map_selection() const;
Set<TileMapCell> tile_set_selection;
void _update_selection_pattern_from_tilemap_selection();
- void _update_selection_pattern_from_tileset_selection();
+ void _update_selection_pattern_from_tileset_tiles_selection();
+ void _update_selection_pattern_from_tileset_pattern_selection();
void _update_tileset_selection_from_selection_pattern();
void _update_fix_selected_and_hovered();
void _fix_invalid_tiles_in_tile_map_selection();
- ///// Bottom panel. ////.
+ ///// Bottom panel common ////
+ void _tab_changed();
+
+ ///// Bottom panel tiles ////
+ VBoxContainer *tiles_bottom_panel;
Label *missing_source_label;
Label *invalid_source_label;
@@ -137,7 +149,7 @@ private:
Ref<Texture2D> missing_atlas_texture_icon;
void _update_tile_set_sources_list();
- void _update_bottom_panel();
+ void _update_source_display();
// Atlas sources.
TileMapCell hovered_tile;
@@ -167,15 +179,26 @@ private:
void _scenes_list_multi_selected(int p_index, bool p_selected);
void _scenes_list_nothing_selected();
+ ///// Bottom panel patterns ////
+ VBoxContainer *patterns_bottom_panel;
+ ItemList *patterns_item_list;
+ Label *patterns_help_label;
+ void _patterns_item_list_gui_input(const Ref<InputEvent> &p_event);
+ void _pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture);
+ bool select_last_pattern = false;
+ void _update_patterns_list();
+
+ // General
+ void _update_theme();
+
// Update callback
virtual void tile_set_changed() override;
protected:
- void _notification(int p_what);
static void _bind_methods();
public:
- virtual Control *get_toolbar() const override;
+ 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;
@@ -205,6 +228,9 @@ private:
void _update_toolbar();
+ // Main vbox.
+ VBoxContainer *main_vbox_container;
+
// TileMap editing.
enum DragType {
DRAG_TYPE_NONE = 0,
@@ -281,16 +307,13 @@ private:
void _update_terrains_cache();
void _update_terrains_tree();
void _update_tiles_list();
+ void _update_theme();
// Update callback
virtual void tile_set_changed() override;
-protected:
- void _notification(int p_what);
- // static void _bind_methods();
-
public:
- virtual Control *get_toolbar() const override;
+ 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;
@@ -328,7 +351,9 @@ private:
// Bottom panel.
Label *missing_tileset_label;
- Tabs *tabs;
+ TabBar *tabs_bar;
+ LocalVector<TileMapEditorPlugin::TabData> tabs_data;
+ LocalVector<TileMapEditorPlugin *> tabs_plugins;
void _update_bottom_panel();
// TileMap.
@@ -355,7 +380,6 @@ public:
void forward_canvas_draw_over_viewport(Control *p_overlay);
void edit(TileMap *p_tile_map);
- Control *get_toolbar() { return tile_map_toolbar; };
TileMapEditor();
~TileMapEditor();
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index c43a854d9a..c8892bceaa 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -66,10 +66,15 @@ int TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::get_id() {
}
bool TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_set(const StringName &p_name, const Variant &p_value) {
+ String name = p_name;
+ if (name == "name") {
+ // Use the resource_name property to store the source's name.
+ name = "resource_name";
+ }
bool valid = false;
- tile_set_atlas_source->set(p_name, p_value, &valid);
+ tile_set_atlas_source->set(name, p_value, &valid);
if (valid) {
- emit_signal(SNAME("changed"), String(p_name).utf8().get_data());
+ emit_signal(SNAME("changed"), String(name).utf8().get_data());
}
return valid;
}
@@ -78,12 +83,18 @@ bool TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get(const StringN
if (!tile_set_atlas_source) {
return false;
}
+ String name = p_name;
+ if (name == "name") {
+ // Use the resource_name property to store the source's name.
+ name = "resource_name";
+ }
bool valid = false;
- r_ret = tile_set_atlas_source->get(p_name, &valid);
+ r_ret = tile_set_atlas_source->get(name, &valid);
return valid;
}
void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"));
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, ""));
@@ -106,6 +117,10 @@ void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::edit(Ref<TileSet>
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
+ if (p_tile_set == tile_set && p_tile_set_atlas_source == tile_set_atlas_source && p_source_id == source_id) {
+ return;
+ }
+
// Disconnect to changes.
if (tile_set_atlas_source) {
tile_set_atlas_source->disconnect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
@@ -157,7 +172,7 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
} else if (p_name == "size_in_atlas") {
Vector2i as_vector2i = Vector2i(p_value);
bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, as_vector2i, tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords);
- ERR_FAIL_COND_V(!has_room_for_tile, false);
+ ERR_FAIL_COND_V_EDMSG(!has_room_for_tile, false, "Invalid size or not enough room in the atlas for the tile.");
tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i);
emit_signal(SNAME("changed"), "size_in_atlas");
return true;
@@ -909,7 +924,7 @@ void TileSetAtlasSourceEditor::_update_atlas_view() {
tile_atlas_view->update();
// Synchronize atlas view.
- TilesEditor::get_singleton()->synchronize_atlas_view(tile_atlas_view);
+ TilesEditorPlugin::get_singleton()->synchronize_atlas_view(tile_atlas_view);
}
void TileSetAtlasSourceEditor::_update_toolbar() {
@@ -2461,7 +2476,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_view = memnew(TileAtlasView);
tile_atlas_view->set_h_size_flags(SIZE_EXPAND_FILL);
tile_atlas_view->set_v_size_flags(SIZE_EXPAND_FILL);
- tile_atlas_view->connect("transform_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_view_transform));
+ tile_atlas_view->connect("transform_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_atlas_view_transform));
tile_atlas_view->connect("transform_changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_view_transform_changed).unbind(2));
right_panel->add_child(tile_atlas_view);
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index 48d0d9b333..0fbb9a98c7 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -145,14 +145,21 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
Ref<Texture2D> texture;
String item_text;
+ // Common to all type of sources.
+ if (!source->get_name().is_empty()) {
+ item_text = vformat(TTR("%s (id:%d)"), source->get_name(), source_id);
+ }
+
// Atlas source.
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
texture = atlas_source->get_texture();
- if (texture.is_valid()) {
- item_text = vformat("%s (id:%d)", texture->get_path().get_file(), source_id);
- } else {
- item_text = vformat(TTR("No Texture Atlas Source (id:%d)"), source_id);
+ if (item_text.is_empty()) {
+ if (texture.is_valid()) {
+ item_text = vformat("%s (ID:%d)", texture->get_path().get_file(), source_id);
+ } else {
+ item_text = vformat(TTR("No Texture Atlas Source (ID:%d)"), source_id);
+ }
}
}
@@ -160,12 +167,14 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
TileSetScenesCollectionSource *scene_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
if (scene_collection_source) {
texture = get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
- item_text = vformat(TTR("Scene Collection Source (id:%d)"), source_id);
+ if (item_text.is_empty()) {
+ item_text = vformat(TTR("Scene Collection Source (ID:%d)"), source_id);
+ }
}
// Use default if not valid.
if (item_text.is_empty()) {
- item_text = vformat(TTR("Unknown Type Source (id:%d)"), source_id);
+ item_text = vformat(TTR("Unknown Type Source (ID:%d)"), source_id);
}
if (!texture.is_valid()) {
texture = missing_texture_texture;
@@ -200,7 +209,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
_source_selected(sources_list->get_current());
// Synchronize the lists.
- TilesEditor::get_singleton()->set_sources_lists_current(sources_list->get_current());
+ TilesEditorPlugin::get_singleton()->set_sources_lists_current(sources_list->get_current());
}
void TileSetEditor::_source_selected(int p_source_index) {
@@ -318,6 +327,7 @@ void TileSetEditor::_notification(int p_what) {
tile_set->set_edited(true);
}
_update_sources_list();
+ _update_patterns_list();
tile_set_changed_needs_update = false;
}
break;
@@ -326,10 +336,56 @@ void TileSetEditor::_notification(int p_what) {
}
}
+void TileSetEditor::_patterns_item_list_gui_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ if (ED_IS_SHORTCUT("tiles_editor/delete", p_event) && p_event->is_pressed() && !p_event->is_echo()) {
+ Vector<int> selected = patterns_item_list->get_selected_items();
+ undo_redo->create_action(TTR("Remove TileSet patterns"));
+ for (int i = 0; i < selected.size(); i++) {
+ int pattern_index = selected[i];
+ undo_redo->add_do_method(*tile_set, "remove_pattern", pattern_index);
+ undo_redo->add_undo_method(*tile_set, "add_pattern", tile_set->get_pattern(pattern_index), pattern_index);
+ }
+ undo_redo->commit_action();
+ patterns_item_list->accept_event();
+ }
+}
+
+void TileSetEditor::_pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture) {
+ // TODO optimize ?
+ for (int i = 0; i < patterns_item_list->get_item_count(); i++) {
+ if (patterns_item_list->get_item_metadata(i) == p_pattern) {
+ patterns_item_list->set_item_icon(i, p_texture);
+ break;
+ }
+ }
+}
+
+void TileSetEditor::_update_patterns_list() {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ // Recreate the items.
+ patterns_item_list->clear();
+ for (int i = 0; i < tile_set->get_patterns_count(); i++) {
+ int id = patterns_item_list->add_item("");
+ patterns_item_list->set_item_metadata(id, tile_set->get_pattern(i));
+ TilesEditorPlugin::get_singleton()->queue_pattern_preview(tile_set, tile_set->get_pattern(i), callable_mp(this, &TileSetEditor::_pattern_preview_done));
+ }
+
+ // Update the label visibility.
+ patterns_help_label->set_visible(patterns_item_list->get_item_count() == 0);
+}
+
void TileSetEditor::_tile_set_changed() {
tile_set_changed_needs_update = true;
}
+void TileSetEditor::_tab_changed(int p_tab_changed) {
+ split_container->set_visible(p_tab_changed == 0);
+ patterns_item_list->set_visible(p_tab_changed == 1);
+}
+
void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
ERR_FAIL_COND(!undo_redo);
@@ -573,6 +629,7 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) {
if (tile_set.is_valid()) {
tile_set->connect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
_update_sources_list();
+ _update_patterns_list();
}
tile_set_atlas_source_editor->hide();
@@ -585,8 +642,21 @@ TileSetEditor::TileSetEditor() {
set_process_internal(true);
+ // TabBar.
+ tabs_bar = memnew(TabBar);
+ tabs_bar->set_clip_tabs(false);
+ tabs_bar->add_tab(TTR("Tiles"));
+ tabs_bar->add_tab(TTR("Patterns"));
+ tabs_bar->connect("tab_changed", callable_mp(this, &TileSetEditor::_tab_changed));
+
+ tile_set_toolbar = memnew(HBoxContainer);
+ tile_set_toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
+ tile_set_toolbar->add_child(tabs_bar);
+ add_child(tile_set_toolbar);
+
+ //// Tiles ////
// Split container.
- HSplitContainer *split_container = memnew(HSplitContainer);
+ split_container = memnew(HSplitContainer);
split_container->set_name(TTR("Tiles"));
split_container->set_h_size_flags(SIZE_EXPAND_FILL);
split_container->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -605,8 +675,8 @@ TileSetEditor::TileSetEditor() {
sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
sources_list->set_v_size_flags(SIZE_EXPAND_FILL);
sources_list->connect("item_selected", callable_mp(this, &TileSetEditor::_source_selected));
- sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_sources_lists_current));
- sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_sources_list), varray(sources_list));
+ sources_list->connect("item_selected", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_sources_lists_current));
+ sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list), varray(sources_list));
sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
sources_list->set_drag_forwarding(this);
split_container_left_side->add_child(sources_list);
@@ -672,6 +742,24 @@ TileSetEditor::TileSetEditor() {
split_container_right_side->add_child(tile_set_scenes_collection_source_editor);
tile_set_scenes_collection_source_editor->hide();
+ //// Patterns ////
+ int thumbnail_size = 64;
+ patterns_item_list = memnew(ItemList);
+ patterns_item_list->set_max_columns(0);
+ patterns_item_list->set_icon_mode(ItemList::ICON_MODE_TOP);
+ patterns_item_list->set_fixed_column_width(thumbnail_size * 3 / 2);
+ patterns_item_list->set_max_text_lines(2);
+ patterns_item_list->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size));
+ patterns_item_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ patterns_item_list->connect("gui_input", callable_mp(this, &TileSetEditor::_patterns_item_list_gui_input));
+ add_child(patterns_item_list);
+ patterns_item_list->hide();
+
+ patterns_help_label = memnew(Label);
+ patterns_help_label->set_text(TTR("Add new patterns in the TileMap editing mode."));
+ patterns_help_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER);
+ patterns_item_list->add_child(patterns_help_label);
+
// Registers UndoRedo inspector callback.
EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileSet"), callable_mp(this, &TileSetEditor::_move_tile_set_array_element));
EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback));
diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h
index fe854b2281..cda38760cf 100644
--- a/editor/plugins/tiles/tile_set_editor.h
+++ b/editor/plugins/tiles/tile_set_editor.h
@@ -46,7 +46,13 @@ class TileSetEditor : public VBoxContainer {
private:
Ref<TileSet> tile_set;
bool tile_set_changed_needs_update = false;
+ HSplitContainer *split_container;
+ // TabBar.
+ HBoxContainer *tile_set_toolbar;
+ TabBar *tabs_bar;
+
+ // Tiles.
Label *no_source_selected_label;
TileSetAtlasSourceEditor *tile_set_atlas_source_editor;
TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor;
@@ -69,7 +75,16 @@ private:
AtlasMergingDialog *atlas_merging_dialog;
TileProxiesManagerDialog *tile_proxies_manager_dialog;
+ // Patterns.
+ ItemList *patterns_item_list;
+ Label *patterns_help_label;
+ void _patterns_item_list_gui_input(const Ref<InputEvent> &p_event);
+ void _pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture);
+ bool select_last_pattern = false;
+ void _update_patterns_list();
+
void _tile_set_changed();
+ void _tab_changed(int p_tab_changed);
void _move_tile_set_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos);
void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value);
@@ -82,6 +97,7 @@ public:
_FORCE_INLINE_ static TileSetEditor *get_singleton() { return singleton; }
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;
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 f74b3bf9c2..dc26d380b8 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -56,10 +56,15 @@ int TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::get
}
bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_set(const StringName &p_name, const Variant &p_value) {
+ String name = p_name;
+ if (name == "name") {
+ // Use the resource_name property to store the source's name.
+ name = "resource_name";
+ }
bool valid = false;
- tile_set_scenes_collection_source->set(p_name, p_value, &valid);
+ tile_set_scenes_collection_source->set(name, p_value, &valid);
if (valid) {
- emit_signal(SNAME("changed"), String(p_name).utf8().get_data());
+ emit_signal(SNAME("changed"), String(name).utf8().get_data());
}
return valid;
}
@@ -68,11 +73,20 @@ bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_g
if (!tile_set_scenes_collection_source) {
return false;
}
+ String name = p_name;
+ if (name == "name") {
+ // Use the resource_name property to store the source's name.
+ name = "resource_name";
+ }
bool valid = false;
- r_ret = tile_set_scenes_collection_source->get(p_name, &valid);
+ r_ret = tile_set_scenes_collection_source->get(name, &valid);
return valid;
}
+void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, ""));
+}
+
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_bind_methods() {
// -- Shape and layout --
ClassDB::bind_method(D_METHOD("set_id", "id"), &TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::set_id);
@@ -89,6 +103,10 @@ void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::ed
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source);
+ if (tile_set == p_tile_set && tile_set_scenes_collection_source == p_tile_set_scenes_collection_source && source_id == p_source_id) {
+ return;
+ }
+
// Disconnect to changes.
if (tile_set_scenes_collection_source) {
tile_set_scenes_collection_source->disconnect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
@@ -174,6 +192,10 @@ void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::edit(TileSetScen
ERR_FAIL_COND(!p_tile_set_scenes_collection_source);
ERR_FAIL_COND(!p_tile_set_scenes_collection_source->has_scene_tile_id(p_scene_id));
+ if (tile_set_scenes_collection_source == p_tile_set_scenes_collection_source && scene_id == p_scene_id) {
+ return;
+ }
+
tile_set_scenes_collection_source = p_tile_set_scenes_collection_source;
scene_id = p_scene_id;
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 195aa79bc4..3be7bee714 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
@@ -51,6 +51,7 @@ private:
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index d0d01a8d49..f1918073fb 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -30,99 +30,183 @@
#include "tiles_editor_plugin.h"
+#include "core/os/mutex.h"
+
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "scene/2d/tile_map.h"
-#include "scene/resources/tile_set.h"
-
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/control.h"
#include "scene/gui/separator.h"
+#include "scene/resources/tile_set.h"
#include "tile_set_editor.h"
-TilesEditor *TilesEditor::singleton = nullptr;
+TilesEditorPlugin *TilesEditorPlugin::singleton = nullptr;
+
+void TilesEditorPlugin::_pattern_preview_done(const Variant &p_udata) {
+ pattern_preview_done.set();
+}
+
+void TilesEditorPlugin::_thread_func(void *ud) {
+ TilesEditorPlugin *te = (TilesEditorPlugin *)ud;
+ te->_thread();
+}
+
+void TilesEditorPlugin::_thread() {
+ pattern_thread_exited.clear();
+ while (!pattern_thread_exit.is_set()) {
+ pattern_preview_sem.wait();
+
+ pattern_preview_mutex.lock();
+ if (pattern_preview_queue.size()) {
+ QueueItem item = pattern_preview_queue.front()->get();
+ pattern_preview_queue.pop_front();
+ pattern_preview_mutex.unlock();
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
+ thumbnail_size *= EDSCALE;
+ Vector2 thumbnail_size2 = Vector2(thumbnail_size, thumbnail_size);
+
+ if (item.pattern.is_valid() && !item.pattern->is_empty()) {
+ // Generate the pattern preview
+ SubViewport *viewport = memnew(SubViewport);
+ viewport->set_size(thumbnail_size2);
+ viewport->set_disable_input(true);
+ viewport->set_transparent_background(true);
+ viewport->set_update_mode(SubViewport::UPDATE_ONCE);
+
+ TileMap *tile_map = memnew(TileMap);
+ tile_map->set_tileset(item.tile_set);
+ tile_map->set_pattern(0, Vector2(), item.pattern);
+ viewport->add_child(tile_map);
+
+ TypedArray<Vector2i> used_cells = tile_map->get_used_cells(0);
+
+ Rect2 encompassing_rect = Rect2();
+ encompassing_rect.set_position(tile_map->map_to_world(used_cells[0]));
+ for (int i = 0; i < used_cells.size(); i++) {
+ Vector2i cell = used_cells[i];
+ Vector2 world_pos = tile_map->map_to_world(cell);
+ encompassing_rect.expand_to(world_pos);
+
+ // Texture.
+ Ref<TileSetAtlasSource> atlas_source = tile_set->get_source(tile_map->get_cell_source_id(0, cell));
+ if (atlas_source.is_valid()) {
+ Vector2i coords = tile_map->get_cell_atlas_coords(0, cell);
+ int alternative = tile_map->get_cell_alternative_tile(0, cell);
+
+ Vector2 center = world_pos - atlas_source->get_tile_effective_texture_offset(coords, alternative);
+ encompassing_rect.expand_to(center - atlas_source->get_tile_texture_region(coords).size / 2);
+ encompassing_rect.expand_to(center + atlas_source->get_tile_texture_region(coords).size / 2);
+ }
+ }
+
+ Vector2 scale = thumbnail_size2 / MAX(encompassing_rect.size.x, encompassing_rect.size.y);
+ tile_map->set_scale(scale);
+ tile_map->set_position(-(scale * encompassing_rect.get_center()) + thumbnail_size2 / 2);
-void TilesEditor::_notification(int p_what) {
+ // Add the viewport at the lasst moment to avoid rendering too early.
+ EditorNode::get_singleton()->add_child(viewport);
+
+ pattern_preview_done.clear();
+ RS::get_singleton()->request_frame_drawn_callback(const_cast<TilesEditorPlugin *>(this), "_pattern_preview_done", Variant());
+
+ while (!pattern_preview_done.is_set()) {
+ OS::get_singleton()->delay_usec(10);
+ }
+
+ Ref<Image> image = viewport->get_texture()->get_image();
+ Ref<ImageTexture> image_texture;
+ image_texture.instantiate();
+ image_texture->create_from_image(image);
+
+ // Find the index for the given pattern. TODO: optimize.
+ Variant args[] = { item.pattern, image_texture };
+ const Variant *args_ptr[] = { &args[0], &args[1] };
+ Variant r;
+ Callable::CallError error;
+ item.callback.call(args_ptr, 2, r, error);
+
+ viewport->queue_delete();
+ } else {
+ pattern_preview_mutex.unlock();
+ }
+ }
+ }
+ pattern_thread_exited.set();
+}
+
+void TilesEditorPlugin::_tile_map_changed() {
+ tile_map_changed_needs_update = true;
+}
+
+void TilesEditorPlugin::_update_editors() {
+ // If tile_map is not edited, we change the edited only if we are not editing a tile_set.
+ tileset_editor->edit(tile_set);
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (tile_map) {
+ tilemap_editor->edit(tile_map);
+ } else {
+ tilemap_editor->edit(nullptr);
+ }
+
+ // Update the viewport.
+ CanvasItemEditor::get_singleton()->update_viewport();
+}
+
+void TilesEditorPlugin::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
- case NOTIFICATION_THEME_CHANGED: {
- tileset_tilemap_switch_button->set_icon(get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons")));
- } break;
case NOTIFICATION_INTERNAL_PROCESS: {
if (tile_map_changed_needs_update) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map) {
tile_set = tile_map->get_tileset();
}
- _update_switch_button();
_update_editors();
+ tile_map_changed_needs_update = false;
}
} break;
}
}
-void TilesEditor::_tile_map_changed() {
- tile_map_changed_needs_update = true;
-}
-
-void TilesEditor::_update_switch_button() {
- // Force the buttons status if needed.
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (tile_map && !tile_set.is_valid()) {
- tileset_tilemap_switch_button->set_pressed(false);
- } else if (!tile_map && tile_set.is_valid()) {
- tileset_tilemap_switch_button->set_pressed(true);
- }
-}
-
-void TilesEditor::_update_editors() {
- // Set editors visibility.
- tilemap_toolbar->set_visible(!tileset_tilemap_switch_button->is_pressed());
- tilemap_editor->set_visible(!tileset_tilemap_switch_button->is_pressed());
- tileset_editor->set_visible(tileset_tilemap_switch_button->is_pressed());
-
- // Enable/disable the switch button.
- if (!tileset_tilemap_switch_button->is_pressed()) {
- if (!tile_set.is_valid()) {
- tileset_tilemap_switch_button->set_disabled(true);
- tileset_tilemap_switch_button->set_tooltip(TTR("This TileMap has no assigned TileSet, assign a TileSet to this TileMap to edit it."));
- } else {
- tileset_tilemap_switch_button->set_disabled(false);
- tileset_tilemap_switch_button->set_tooltip(TTR("Switch between TileSet/TileMap editor."));
- }
- } else {
+void TilesEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ // Disable and hide invalid editors.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (!tile_map) {
- tileset_tilemap_switch_button->set_disabled(true);
- tileset_tilemap_switch_button->set_tooltip(TTR("You are editing a TileSet resource. Select a TileMap node to paint."));
+ tileset_editor_button->set_visible(tile_set.is_valid());
+ tilemap_editor_button->set_visible(tile_map);
+ if (tile_map) {
+ editor_node->make_bottom_panel_item_visible(tilemap_editor);
} else {
- tileset_tilemap_switch_button->set_disabled(false);
- tileset_tilemap_switch_button->set_tooltip(TTR("Switch between TileSet/TileMap editor."));
+ editor_node->make_bottom_panel_item_visible(tileset_editor);
}
- }
- // If tile_map is not edited, we change the edited only if we are not editing a tile_set.
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (tile_map) {
- tilemap_editor->edit(tile_map);
} else {
- tilemap_editor->edit(nullptr);
+ tileset_editor_button->hide();
+ tilemap_editor_button->hide();
+ editor_node->hide_bottom_panel();
}
- tileset_editor->edit(tile_set);
+}
- // Update the viewport
- CanvasItemEditor::get_singleton()->update_viewport();
+void TilesEditorPlugin::queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback) {
+ ERR_FAIL_COND(!p_tile_set.is_valid());
+ ERR_FAIL_COND(!p_pattern.is_valid());
+ {
+ MutexLock lock(pattern_preview_mutex);
+ pattern_preview_queue.push_back({ p_tile_set, p_pattern, p_callback });
+ }
+ pattern_preview_sem.post();
}
-void TilesEditor::set_sources_lists_current(int p_current) {
+void TilesEditorPlugin::set_sources_lists_current(int p_current) {
atlas_sources_lists_current = p_current;
}
-void TilesEditor::synchronize_sources_list(Object *p_current) {
+void TilesEditorPlugin::synchronize_sources_list(Object *p_current) {
ItemList *item_list = Object::cast_to<ItemList>(p_current);
ERR_FAIL_COND(!item_list);
@@ -136,12 +220,12 @@ void TilesEditor::synchronize_sources_list(Object *p_current) {
}
}
-void TilesEditor::set_atlas_view_transform(float p_zoom, Vector2 p_scroll) {
+void TilesEditorPlugin::set_atlas_view_transform(float p_zoom, Vector2 p_scroll) {
atlas_view_zoom = p_zoom;
atlas_view_scroll = p_scroll;
}
-void TilesEditor::synchronize_atlas_view(Object *p_current) {
+void TilesEditorPlugin::synchronize_atlas_view(Object *p_current) {
TileAtlasView *tile_atlas_view = Object::cast_to<TileAtlasView>(p_current);
ERR_FAIL_COND(!tile_atlas_view);
@@ -150,11 +234,11 @@ void TilesEditor::synchronize_atlas_view(Object *p_current) {
}
}
-void TilesEditor::edit(Object *p_object) {
+void TilesEditorPlugin::edit(Object *p_object) {
// Disconnect to changes.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map) {
- tile_map->disconnect("changed", callable_mp(this, &TilesEditor::_tile_map_changed));
+ tile_map->disconnect("changed", callable_mp(this, &TilesEditorPlugin::_tile_map_changed));
}
// Update edited objects.
@@ -164,112 +248,79 @@ void TilesEditor::edit(Object *p_object) {
tile_map_id = p_object->get_instance_id();
tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
tile_set = tile_map->get_tileset();
+ editor_node->make_bottom_panel_item_visible(tilemap_editor);
} else if (p_object->is_class("TileSet")) {
tile_set = Ref<TileSet>(p_object);
if (tile_map) {
- if (tile_map->get_tileset() != tile_set) {
+ if (tile_map->get_tileset() != tile_set || !tile_map->is_inside_tree()) {
tile_map = nullptr;
+ tile_map_id = ObjectID();
}
}
- }
-
- // Update pressed status button.
- if (p_object->is_class("TileMap")) {
- tileset_tilemap_switch_button->set_pressed(false);
- } else if (p_object->is_class("TileSet")) {
- tileset_tilemap_switch_button->set_pressed(true);
+ editor_node->make_bottom_panel_item_visible(tileset_editor);
}
}
// Update the editors.
- _update_switch_button();
_update_editors();
// Add change listener.
if (tile_map) {
- tile_map->connect("changed", callable_mp(this, &TilesEditor::_tile_map_changed));
+ tile_map->connect("changed", callable_mp(this, &TilesEditorPlugin::_tile_map_changed));
}
}
-void TilesEditor::_bind_methods() {
+bool TilesEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("TileMap") || p_object->is_class("TileSet");
+}
+
+void TilesEditorPlugin::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_pattern_preview_done", "pattern"), &TilesEditorPlugin::_pattern_preview_done);
}
-TilesEditor::TilesEditor(EditorNode *p_editor) {
+TilesEditorPlugin::TilesEditorPlugin(EditorNode *p_node) {
set_process_internal(true);
// Update the singleton.
singleton = this;
- // Toolbar.
- HBoxContainer *toolbar = memnew(HBoxContainer);
- toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
- add_child(toolbar);
+ editor_node = p_node;
- // Switch button.
- tileset_tilemap_switch_button = memnew(Button);
- tileset_tilemap_switch_button->set_flat(true);
- tileset_tilemap_switch_button->set_toggle_mode(true);
- tileset_tilemap_switch_button->connect("toggled", callable_mp(this, &TilesEditor::_update_editors).unbind(1));
- toolbar->add_child(tileset_tilemap_switch_button);
+ // Tileset editor.
+ tileset_editor = memnew(TileSetEditor);
+ tileset_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ tileset_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ tileset_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
+ tileset_editor->hide();
// Tilemap editor.
tilemap_editor = memnew(TileMapEditor);
- tilemap_editor->set_h_size_flags(SIZE_EXPAND_FILL);
- tilemap_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ tilemap_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ tilemap_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ tilemap_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
tilemap_editor->hide();
- add_child(tilemap_editor);
- tilemap_toolbar = tilemap_editor->get_toolbar();
- toolbar->add_child(tilemap_toolbar);
+ // Pattern preview generation thread.
+ pattern_preview_thread.start(_thread_func, this);
- // Tileset editor.
- tileset_editor = memnew(TileSetEditor);
- tileset_editor->set_h_size_flags(SIZE_EXPAND_FILL);
- tileset_editor->set_v_size_flags(SIZE_EXPAND_FILL);
- tileset_editor->hide();
- add_child(tileset_editor);
+ // Bottom buttons.
+ tileset_editor_button = p_node->add_bottom_panel_item(TTR("TileSet"), tileset_editor);
+ tileset_editor_button->hide();
+ tilemap_editor_button = p_node->add_bottom_panel_item(TTR("TileMap"), tilemap_editor);
+ tilemap_editor_button->hide();
// Initialization.
- _update_switch_button();
_update_editors();
}
-TilesEditor::~TilesEditor() {
-}
-
-///////////////////////////////////////////////////////////////
-
-void TilesEditorPlugin::_notification(int p_what) {
-}
-
-void TilesEditorPlugin::make_visible(bool p_visible) {
- if (p_visible) {
- tiles_editor_button->show();
- editor_node->make_bottom_panel_item_visible(tiles_editor);
- } else {
- editor_node->hide_bottom_panel();
- tiles_editor_button->hide();
- }
-}
-
-void TilesEditorPlugin::edit(Object *p_object) {
- tiles_editor->edit(p_object);
-}
-
-bool TilesEditorPlugin::handles(Object *p_object) const {
- return p_object->is_class("TileMap") || p_object->is_class("TileSet");
-}
-
-TilesEditorPlugin::TilesEditorPlugin(EditorNode *p_node) {
- editor_node = p_node;
-
- tiles_editor = memnew(TilesEditor(p_node));
- tiles_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
- tiles_editor->hide();
-
- tiles_editor_button = p_node->add_bottom_panel_item(TTR("Tiles"), tiles_editor);
- tiles_editor_button->hide();
-}
-
TilesEditorPlugin::~TilesEditorPlugin() {
+ if (pattern_preview_thread.is_started()) {
+ pattern_thread_exit.set();
+ pattern_preview_sem.post();
+ while (!pattern_thread_exited.is_set()) {
+ OS::get_singleton()->delay_usec(10000);
+ RenderingServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server
+ }
+ pattern_preview_thread.wait_to_finish();
+ }
}
diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h
index f976d68938..dd52bdc31a 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.h
+++ b/editor/plugins/tiles/tiles_editor_plugin.h
@@ -38,24 +38,24 @@
#include "tile_map_editor.h"
#include "tile_set_editor.h"
-class TilesEditor : public VBoxContainer {
- GDCLASS(TilesEditor, VBoxContainer);
+class TilesEditorPlugin : public EditorPlugin {
+ GDCLASS(TilesEditorPlugin, EditorPlugin);
- static TilesEditor *singleton;
+ static TilesEditorPlugin *singleton;
private:
+ EditorNode *editor_node;
+
bool tile_map_changed_needs_update = false;
ObjectID tile_map_id;
Ref<TileSet> tile_set;
- Button *tileset_tilemap_switch_button;
-
- Control *tilemap_toolbar;
+ Button *tilemap_editor_button;
TileMapEditor *tilemap_editor;
+ Button *tileset_editor_button;
TileSetEditor *tileset_editor;
- void _update_switch_button();
void _update_editors();
// For synchronization.
@@ -65,15 +65,35 @@ private:
void _tile_map_changed();
+ // Patterns preview generation.
+ struct QueueItem {
+ Ref<TileSet> tile_set;
+ Ref<TileMapPattern> pattern;
+ Callable callback;
+ };
+ List<QueueItem> pattern_preview_queue;
+ Mutex pattern_preview_mutex;
+ Semaphore pattern_preview_sem;
+ Thread pattern_preview_thread;
+ SafeFlag pattern_thread_exit;
+ SafeFlag pattern_thread_exited;
+ mutable SafeFlag pattern_preview_done;
+ void _pattern_preview_done(const Variant &p_udata);
+ static void _thread_func(void *ud);
+ void _thread();
+
protected:
void _notification(int p_what);
static void _bind_methods();
public:
- _FORCE_INLINE_ static TilesEditor *get_singleton() { return singleton; }
+ _FORCE_INLINE_ static TilesEditorPlugin *get_singleton() { return singleton; }
- bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return tilemap_editor->forward_canvas_gui_input(p_event); }
- void forward_canvas_draw_over_viewport(Control *p_overlay) { tilemap_editor->forward_canvas_draw_over_viewport(p_overlay); }
+ virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return tilemap_editor->forward_canvas_gui_input(p_event); }
+ virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { tilemap_editor->forward_canvas_draw_over_viewport(p_overlay); }
+
+ // Pattern preview API.
+ void queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback);
// To synchronize the atlas sources lists.
void set_sources_lists_current(int p_current);
@@ -82,27 +102,6 @@ public:
void set_atlas_view_transform(float p_zoom, Vector2 p_scroll);
void synchronize_atlas_view(Object *p_current);
- void edit(Object *p_object);
-
- TilesEditor(EditorNode *p_editor);
- ~TilesEditor();
-};
-
-class TilesEditorPlugin : public EditorPlugin {
- GDCLASS(TilesEditorPlugin, EditorPlugin);
-
-private:
- EditorNode *editor_node;
- TilesEditor *tiles_editor;
- Button *tiles_editor_button;
-
-protected:
- void _notification(int p_what);
-
-public:
- virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return tiles_editor->forward_canvas_gui_input(p_event); }
- virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { tiles_editor->forward_canvas_draw_over_viewport(p_overlay); }
-
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 0a067dfc17..09ca1f7608 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -31,6 +31,7 @@
#include "visual_shader_editor_plugin.h"
#include "core/config/project_settings.h"
+#include "core/core_string_names.h"
#include "core/input/input.h"
#include "core/io/resource_loader.h"
#include "core/math/math_defs.h"
@@ -212,19 +213,27 @@ void VisualShaderGraphPlugin::set_uniform_name(VisualShader::Type p_type, int p_
void VisualShaderGraphPlugin::update_curve(int p_node_id) {
if (links.has(p_node_id) && links[p_node_id].curve_editors[0]) {
- if (((VisualShaderNodeCurveTexture *)links[p_node_id].visual_node)->get_texture().is_valid()) {
- links[p_node_id].curve_editors[0]->set_curve(((VisualShaderNodeCurveTexture *)links[p_node_id].visual_node)->get_texture()->get_curve());
+ Ref<VisualShaderNodeCurveTexture> tex = Object::cast_to<VisualShaderNodeCurveTexture>(links[p_node_id].visual_node);
+ ERR_FAIL_COND(!tex.is_valid());
+
+ if (tex->get_texture().is_valid()) {
+ links[p_node_id].curve_editors[0]->set_curve(tex->get_texture()->get_curve());
}
+ tex->emit_signal(CoreStringNames::get_singleton()->changed);
}
}
void VisualShaderGraphPlugin::update_curve_xyz(int p_node_id) {
if (links.has(p_node_id) && links[p_node_id].curve_editors[0] && links[p_node_id].curve_editors[1] && links[p_node_id].curve_editors[2]) {
- if (((VisualShaderNodeCurveXYZTexture *)links[p_node_id].visual_node)->get_texture().is_valid()) {
- links[p_node_id].curve_editors[0]->set_curve(((VisualShaderNodeCurveXYZTexture *)links[p_node_id].visual_node)->get_texture()->get_curve_x());
- links[p_node_id].curve_editors[1]->set_curve(((VisualShaderNodeCurveXYZTexture *)links[p_node_id].visual_node)->get_texture()->get_curve_y());
- links[p_node_id].curve_editors[2]->set_curve(((VisualShaderNodeCurveXYZTexture *)links[p_node_id].visual_node)->get_texture()->get_curve_z());
+ Ref<VisualShaderNodeCurveXYZTexture> tex = Object::cast_to<VisualShaderNodeCurveXYZTexture>(links[p_node_id].visual_node);
+ ERR_FAIL_COND(!tex.is_valid());
+
+ if (tex->get_texture().is_valid()) {
+ links[p_node_id].curve_editors[0]->set_curve(tex->get_texture()->get_curve_x());
+ links[p_node_id].curve_editors[1]->set_curve(tex->get_texture()->get_curve_y());
+ links[p_node_id].curve_editors[2]->set_curve(tex->get_texture()->get_curve_z());
}
+ tex->emit_signal(CoreStringNames::get_singleton()->changed);
}
}
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index 9f24c5af72..5788959a77 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -335,8 +335,6 @@ class VisualShaderEditor : public VBoxContainer {
void _delete_node_request(int p_type, int p_node);
void _delete_nodes_request();
- void _removed_from_graph();
-
void _node_changed(int p_id);
void _edit_port_default_input(Object *p_button, int p_node, int p_port);
diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h
index eb6c300d5b..f90db02c46 100644
--- a/editor/project_settings_editor.h
+++ b/editor/project_settings_editor.h
@@ -100,8 +100,6 @@ class ProjectSettingsEditor : public AcceptDialog {
void _action_reordered(const String &p_action_name, const String &p_relative_to, bool p_before);
void _update_action_map_editor();
- ProjectSettingsEditor();
-
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp
index 9063b5c6f8..a5e1b0eab8 100644
--- a/editor/rename_dialog.cpp
+++ b/editor/rename_dialog.cpp
@@ -459,7 +459,7 @@ String RenameDialog::_substitute(const String &subject, const Node *node, int co
return result;
}
-void RenameDialog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type) {
+void RenameDialog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
RenameDialog *self = (RenameDialog *)p_self;
String source_file(p_file);
diff --git a/editor/rename_dialog.h b/editor/rename_dialog.h
index 7990862b37..4db3ef6740 100644
--- a/editor/rename_dialog.h
+++ b/editor/rename_dialog.h
@@ -63,7 +63,7 @@ class RenameDialog : public ConfirmationDialog {
String _postprocess(const String &subject);
void _update_preview(String new_text = "");
void _update_preview_int(int new_value = 0);
- static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type);
+ static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type);
SceneTreeEditor *scene_tree_editor;
UndoRedo *undo_redo;
diff --git a/editor/reparent_dialog.h b/editor/reparent_dialog.h
index 5c3a65a522..3fcdda7bed 100644
--- a/editor/reparent_dialog.h
+++ b/editor/reparent_dialog.h
@@ -46,7 +46,6 @@ class ReparentDialog : public ConfirmationDialog {
SceneTreeEditor *tree;
CheckBox *keep_transform;
- void update_tree();
void _reparent();
void _cancel();
@@ -56,7 +55,6 @@ protected:
public:
void set_current(const Set<Node *> &p_selection);
- String get_selected_type();
ReparentDialog();
~ReparentDialog();
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 255e026887..66000602fa 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -246,8 +246,6 @@ class SceneTreeDock : public VBoxContainer {
void _perform_instantiate_scenes(const Vector<String> &p_files, Node *parent, int p_pos);
void _replace_with_branch_scene(const String &p_file, Node *base);
- void _file_selected(String p_file);
-
void _remote_tree_selected();
void _local_tree_selected();
diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h
index 4acd5d8486..b4c40ab17a 100644
--- a/editor/scene_tree_editor.h
+++ b/editor/scene_tree_editor.h
@@ -114,8 +114,6 @@ class SceneTreeEditor : public Control {
void _node_visibility_changed(Node *p_node);
void _update_visibility_color(Node *p_node, TreeItem *p_item);
- void _node_replace_owner(Node *p_base, Node *p_node, Node *p_root);
-
void _selection_changed();
Node *get_scene_node();
@@ -173,7 +171,6 @@ class SceneTreeDialog : public ConfirmationDialog {
//Button *cancel;
LineEdit *filter;
- void update_tree();
void _select();
void _cancel();
void _filter_changed(const String &p_filter);
diff --git a/editor/translations/Makefile b/editor/translations/Makefile
index 1843114f06..82b3d49c59 100644
--- a/editor/translations/Makefile
+++ b/editor/translations/Makefile
@@ -18,3 +18,24 @@ merge:
check:
@for po in $(POFILES); do msgfmt -c $$po -o /dev/null; done
+
+# Generate completion ratio from statistics string such as:
+# 2775 translated messages, 272 fuzzy translations, 151 untranslated messages.
+# First number can be 0, second and third numbers are only present if non-zero.
+include-list:
+ @list=""; \
+ threshold=0.30; \
+ for po in $(POFILES); do \
+ res=`msgfmt --statistics $$po -o /dev/null 2>&1 | sed 's/[^0-9,]*//g'`; \
+ complete=`cut -d',' -f1 <<< $$res`; \
+ fuzzy_or_untranslated=`cut -d',' -f2 <<< $$res`; \
+ untranslated_maybe=`cut -d',' -f3 <<< $$res`; \
+ if [ -z "$$fuzzy_or_untranslated" ]; then fuzzy_or_untranslated=0; fi; \
+ if [ -z "$$untranslated_maybe" ]; then untranslated_maybe=0; fi; \
+ incomplete=`expr $$fuzzy_or_untranslated + $$untranslated_maybe`; \
+ if `awk "BEGIN {exit !($$complete / ($$complete + $$incomplete) > $$threshold)}"`; then \
+ lang=`basename $$po .po`; \
+ list+="$$lang,"; \
+ fi; \
+ done; \
+ echo $$list;