summaryrefslogtreecommitdiffstats
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/create_dialog.cpp14
-rw-r--r--editor/editor_feature_profile.cpp6
-rw-r--r--editor/editor_help.cpp2
-rw-r--r--editor/editor_help_search.cpp9
-rw-r--r--editor/editor_help_search.h1
-rw-r--r--editor/editor_inspector.cpp4
-rw-r--r--editor/editor_node.cpp27
-rw-r--r--editor/editor_node.h4
-rw-r--r--editor/editor_themes.cpp7
-rw-r--r--editor/filesystem_dock.cpp159
-rw-r--r--editor/filesystem_dock.h6
-rw-r--r--editor/icons/BaseButton.svg2
-rw-r--r--editor/icons/CameraAttributes.svg2
-rw-r--r--editor/icons/CanvasItem.svg2
-rw-r--r--editor/icons/Font.svg2
-rw-r--r--editor/icons/GeometryInstance3D.svg2
-rw-r--r--editor/icons/Mesh.svg2
-rw-r--r--editor/icons/ObjectDisabled.svg1
-rw-r--r--editor/icons/Occluder3D.svg2
-rw-r--r--editor/icons/Range.svg2
-rw-r--r--editor/icons/VideoStream.svg2
-rw-r--r--editor/icons/Viewport.svg2
-rw-r--r--editor/icons/VisualInstance3D.svg2
-rw-r--r--editor/plugins/script_text_editor.cpp12
24 files changed, 218 insertions, 56 deletions
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 8b71d7586f..e2cb989e6a 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -289,14 +289,12 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String
bool can_instantiate = (p_type_category == TypeCategory::CPP_TYPE && ClassDB::can_instantiate(p_type)) ||
p_type_category == TypeCategory::OTHER_TYPE;
- bool is_virtual = ClassDB::class_exists(p_type) && ClassDB::is_virtual(p_type);
+ bool instantiable = can_instantiate && !(ClassDB::class_exists(p_type) && ClassDB::is_virtual(p_type));
- r_item->set_meta(SNAME("__instantiable"), can_instantiate && !is_virtual);
+ r_item->set_meta(SNAME("__instantiable"), instantiable);
- if (can_instantiate && !is_virtual) {
- r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback));
- } else {
- r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, "NodeDisabled"));
+ r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type));
+ if (!instantiable) {
r_item->set_custom_color(0, search_options->get_theme_color(SNAME("disabled_font_color"), SNAME("Editor")));
}
@@ -714,7 +712,7 @@ void CreateDialog::_save_and_update_favorite_list() {
TreeItem *ti = favorites->create_item(root);
ti->set_text(0, l);
- ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(name, icon_fallback));
+ ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(name));
}
}
}
@@ -731,7 +729,7 @@ void CreateDialog::_load_favorites_and_history() {
String name = l.get_slicec(' ', 0);
if (EditorNode::get_editor_data().is_type_recognized(name) && !_is_class_disabled_by_feature_profile(name)) {
- recent->add_item(l, EditorNode::get_singleton()->get_class_icon(name, icon_fallback));
+ recent->add_item(l, EditorNode::get_singleton()->get_class_icon(name));
}
}
}
diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp
index 308bf33da5..6019e9e895 100644
--- a/editor/editor_feature_profile.cpp
+++ b/editor/editor_feature_profile.cpp
@@ -495,7 +495,7 @@ void EditorFeatureProfileManager::_profile_selected(int p_what) {
void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected) {
TreeItem *class_item = class_list->create_item(p_parent);
class_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
- class_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_class, "Node"));
+ class_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_class));
String text = p_class;
bool disabled = edited->is_class_disabled(p_class);
@@ -522,11 +522,11 @@ void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const S
class_item->select(0);
}
if (disabled) {
- //class disabled, do nothing else (do not show further)
+ // Class disabled, do nothing else (do not show further).
return;
}
- class_item->set_checked(0, true); // if its not disabled, its checked
+ class_item->set_checked(0, true); // If it's not disabled, it's checked.
List<StringName> child_classes;
ClassDB::get_direct_inheriters_from_class(p_class, &child_classes);
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 7573fcd21e..14d80334b3 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -367,7 +367,7 @@ void EditorHelp::_add_type_icon(const String &p_type, int p_size, const String &
Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(p_type, p_fallback);
Vector2i size = Vector2i(icon->get_width(), icon->get_height());
if (p_size > 0) {
- // Ensures icon scales proportionally on both axis, based on icon height.
+ // Ensures icon scales proportionally on both axes, based on icon height.
float ratio = p_size / float(size.height);
size.width *= ratio;
size.height *= ratio;
diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp
index 6aa508f40e..f06a23607e 100644
--- a/editor/editor_help_search.cpp
+++ b/editor/editor_help_search.cpp
@@ -593,16 +593,10 @@ TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const ClassMatch &p_
}
TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray) {
- Ref<Texture2D> icon = empty_icon;
- if (ui_service->has_theme_icon(p_doc->name, SNAME("EditorIcons"))) {
- icon = ui_service->get_theme_icon(p_doc->name, SNAME("EditorIcons"));
- } else if (ClassDB::class_exists(p_doc->name) && ClassDB::is_parent_class(p_doc->name, "Object")) {
- icon = ui_service->get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
- }
String tooltip = DTR(p_doc->brief_description.strip_edges());
TreeItem *item = results_tree->create_item(p_parent);
- item->set_icon(0, icon);
+ item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_doc->name));
item->set_text(0, p_doc->name);
item->set_text(1, TTR("Class"));
item->set_tooltip_text(0, tooltip);
@@ -706,6 +700,5 @@ EditorHelpSearch::Runner::Runner(Control *p_icon_service, Tree *p_results_tree,
results_tree(p_results_tree),
term((p_search_flags & SEARCH_CASE_SENSITIVE) == 0 ? p_term.strip_edges().to_lower() : p_term.strip_edges()),
search_flags(p_search_flags),
- empty_icon(ui_service->get_theme_icon(SNAME("ArrowRight"), SNAME("EditorIcons"))),
disabled_color(ui_service->get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))) {
}
diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h
index 6e71d788e7..e8056ce6ac 100644
--- a/editor/editor_help_search.h
+++ b/editor/editor_help_search.h
@@ -122,7 +122,6 @@ class EditorHelpSearch::Runner : public RefCounted {
Vector<String> terms;
int search_flags;
- Ref<Texture2D> empty_icon;
Color disabled_color;
HashMap<String, DocData::ClassDoc>::Iterator iterator_doc;
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index db61700c00..7fc9806bd2 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -2824,7 +2824,7 @@ void EditorInspector::update_tree() {
// Find the icon corresponding to the script.
if (script_name != StringName()) {
- category->icon = EditorNode::get_singleton()->get_class_icon(script_name, "Object");
+ category->icon = EditorNode::get_singleton()->get_class_icon(script_name);
} else {
category->icon = EditorNode::get_singleton()->get_object_icon(scr.ptr(), "Object");
}
@@ -2832,7 +2832,7 @@ void EditorInspector::update_tree() {
}
if (category->icon.is_null() && !type.is_empty()) {
- category->icon = EditorNode::get_singleton()->get_class_icon(type, "Object");
+ category->icon = EditorNode::get_singleton()->get_class_icon(type);
}
// Set the category label.
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 645d6dd0f9..8be4b94062 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -553,6 +553,7 @@ void EditorNode::_notification(int p_what) {
case NOTIFICATION_READY: {
{
+ started_timestamp = Time::get_singleton()->get_unix_time_from_system();
_initializing_plugins = true;
Vector<String> addons;
if (ProjectSettings::get_singleton()->has_setting("editor_plugins/enabled")) {
@@ -4236,9 +4237,19 @@ Ref<Texture2D> EditorNode::_get_class_or_script_icon(const String &p_class, cons
return gui_base->get_theme_icon(p_class, SNAME("EditorIcons"));
}
- if (p_fallback.length() && gui_base->has_theme_icon(p_fallback, SNAME("EditorIcons"))) {
+ if (!p_fallback.is_empty() && gui_base->has_theme_icon(p_fallback, SNAME("EditorIcons"))) {
return gui_base->get_theme_icon(p_fallback, SNAME("EditorIcons"));
}
+
+ // If the fallback is empty or wasn't found, use the default fallback.
+ if (ClassDB::class_exists(p_class)) {
+ bool instantiable = !ClassDB::is_virtual(p_class) && ClassDB::can_instantiate(p_class);
+ if (ClassDB::is_parent_class(p_class, SNAME("Node"))) {
+ return gui_base->get_theme_icon(instantiable ? "Node" : "NodeDisabled", SNAME("EditorIcons"));
+ } else {
+ return gui_base->get_theme_icon(instantiable ? "Object" : "ObjectDisabled", SNAME("EditorIcons"));
+ }
+ }
}
return nullptr;
@@ -5517,7 +5528,19 @@ void EditorNode::_scene_tab_closed(int p_tab) {
if (scene_filename.is_empty()) {
unsaved_message = TTR("This scene was never saved.");
} else {
- unsaved_message = vformat(TTR("Scene \"%s\" has unsaved changes."), scene_filename);
+ // Consider editor startup to be a point of saving, so that when you
+ // close and reopen the editor, you don't get an excessively long
+ // "modified X hours ago".
+ const uint64_t last_modified_seconds = Time::get_singleton()->get_unix_time_from_system() - MAX(started_timestamp, FileAccess::get_modified_time(scene->get_scene_file_path()));
+ String last_modified_string;
+ if (last_modified_seconds < 120) {
+ last_modified_string = vformat(TTRN("%d second ago", "%d seconds ago", last_modified_seconds), last_modified_seconds);
+ } else if (last_modified_seconds < 7200) {
+ last_modified_string = vformat(TTRN("%d minute ago", "%d minutes ago", last_modified_seconds / 60), last_modified_seconds / 60);
+ } else {
+ last_modified_string = vformat(TTRN("%d hour ago", "%d hours ago", last_modified_seconds / 3600), last_modified_seconds / 3600);
+ }
+ unsaved_message = vformat(TTR("Scene \"%s\" has unsaved changes.\nLast saved: %s."), scene_filename, last_modified_string);
}
} else {
// Check if any plugin has unsaved changes in that scene.
diff --git a/editor/editor_node.h b/editor/editor_node.h
index d39e848de8..4a529afc50 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -355,6 +355,8 @@ private:
Timer *screenshot_timer = nullptr;
+ uint64_t started_timestamp = 0;
+
PluginConfigDialog *plugin_config_dialog = nullptr;
RichTextLabel *load_errors = nullptr;
@@ -852,7 +854,7 @@ public:
Ref<Script> get_object_custom_type_base(const Object *p_object) const;
StringName get_object_custom_type_name(const Object *p_object) const;
Ref<Texture2D> get_object_icon(const Object *p_object, const String &p_fallback = "Object");
- Ref<Texture2D> get_class_icon(const String &p_class, const String &p_fallback = "Object");
+ Ref<Texture2D> get_class_icon(const String &p_class, const String &p_fallback = "");
bool is_object_of_custom_type(const Object *p_object, const StringName &p_class);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 7446a5b3c7..9712297ec8 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -81,10 +81,13 @@ void EditorColorMap::create() {
add_conversion_color_pair("#f9f9f9", "#606060"); // Scrollbar grabber highlight color
add_conversion_color_pair("#c38ef1", "#a85de9"); // Animation
- add_conversion_color_pair("#fc7f7f", "#cd3838"); // Spatial
add_conversion_color_pair("#8da5f3", "#3d64dd"); // 2D
add_conversion_color_pair("#4b70ea", "#1a3eac"); // 2D Dark
- add_conversion_color_pair("#8eef97", "#2fa139"); // Control
+ add_conversion_color_pair("#7582a8", "#6d83c8"); // 2D Abstract
+ add_conversion_color_pair("#fc7f7f", "#cd3838"); // 3D
+ add_conversion_color_pair("#b56d6d", "#be6a6a"); // 3D Abstract
+ add_conversion_color_pair("#8eef97", "#2fa139"); // GUI Control
+ add_conversion_color_pair("#76ad7b", "#64a66a"); // GUI Control Abstract
add_conversion_color_pair("#5fb2ff", "#0079f0"); // Selection (blue)
add_conversion_color_pair("#003e7a", "#2b74bb"); // Selection (darker blue)
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 74345a6bcc..3c010112ad 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -183,16 +183,37 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
// Create a tree item for the subdirectory.
TreeItem *subdirectory_item = tree->create_item(p_parent);
String dname = p_dir->get_name();
+ String lpath = p_dir->get_path();
+
if (dname.is_empty()) {
dname = "res://";
}
+ // Set custom folder color (if applicable).
+ bool has_custom_color = assigned_folder_colors.has(lpath);
+ Color custom_color = has_custom_color ? folder_colors[assigned_folder_colors[lpath]] : Color();
+
+ if (has_custom_color) {
+ subdirectory_item->set_icon_modulate(0, editor_is_dark_theme ? custom_color : custom_color * 1.75);
+ subdirectory_item->set_custom_bg_color(0, Color(custom_color, editor_is_dark_theme ? 0.1 : 0.15));
+ } else {
+ TreeItem *parent = subdirectory_item->get_parent();
+ if (parent) {
+ Color parent_bg_color = parent->get_custom_bg_color(0);
+ if (parent_bg_color != Color()) {
+ bool parent_has_custom_color = assigned_folder_colors.has(parent->get_metadata(0));
+ subdirectory_item->set_custom_bg_color(0, parent_has_custom_color ? parent_bg_color.darkened(0.3) : parent_bg_color);
+ subdirectory_item->set_icon_modulate(0, parent->get_icon_modulate(0));
+ } else {
+ subdirectory_item->set_icon_modulate(0, get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog")));
+ }
+ }
+ }
+
subdirectory_item->set_text(0, dname);
subdirectory_item->set_structured_text_bidi_override(0, TextServer::STRUCTURED_TEXT_FILE);
subdirectory_item->set_icon(0, get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
- subdirectory_item->set_icon_modulate(0, get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog")));
subdirectory_item->set_selectable(0, true);
- String lpath = p_dir->get_path();
subdirectory_item->set_metadata(0, lpath);
if (!p_select_in_favorites && (current_path == lpath || ((display_mode == DISPLAY_MODE_SPLIT) && current_path.get_base_dir() == lpath))) {
subdirectory_item->select(0);
@@ -260,6 +281,12 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
file_item->set_text(0, fi.name);
file_item->set_structured_text_bidi_override(0, TextServer::STRUCTURED_TEXT_FILE);
file_item->set_icon(0, _get_tree_item_icon(!fi.import_broken, fi.type));
+ Color parent_bg_color = subdirectory_item->get_custom_bg_color(0);
+ if (has_custom_color) {
+ file_item->set_custom_bg_color(0, parent_bg_color.darkened(0.3));
+ } else if (parent_bg_color != Color()) {
+ file_item->set_custom_bg_color(0, parent_bg_color);
+ }
String file_metadata = lpath.path_join(fi.name);
file_item->set_metadata(0, file_metadata);
if (!p_select_in_favorites && current_path == file_metadata) {
@@ -355,26 +382,26 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo
EditorSettings::get_singleton()->set_favorites(favorite_paths);
}
+ Ref<Texture2D> folder_icon = get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"));
+ const Color default_folder_color = get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog"));
+
for (int i = 0; i < favorite_paths.size(); i++) {
String favorite = favorite_paths[i];
if (!favorite.begins_with("res://")) {
continue;
}
- Ref<Texture2D> folder_icon = get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"));
- const Color folder_color = get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog"));
-
String text;
Ref<Texture2D> icon;
Color color;
if (favorite == "res://") {
text = "/";
icon = folder_icon;
- color = folder_color;
+ color = default_folder_color;
} else if (favorite.ends_with("/")) {
text = favorite.substr(0, favorite.length() - 1).get_file();
icon = folder_icon;
- color = folder_color;
+ color = assigned_folder_colors.has(favorite) ? folder_colors[assigned_folder_colors[favorite]] : default_folder_color;
} else {
text = favorite.get_file();
int index;
@@ -581,11 +608,24 @@ void FileSystemDock::_notification(int p_what) {
file_list_search_box->set_clear_button_enabled(true);
file_list_button_sort->set_icon(get_theme_icon(SNAME("Sort"), SNAME("EditorIcons")));
- // Update always show folders.
+ // Update editor dark theme & always show folders states from editor settings, redraw if needed.
+ bool do_redraw = false;
+
+ bool new_editor_is_dark_theme = EditorSettings::get_singleton()->is_dark_theme();
+ if (new_editor_is_dark_theme != editor_is_dark_theme) {
+ editor_is_dark_theme = new_editor_is_dark_theme;
+ do_redraw = true;
+ }
+
bool new_always_show_folders = bool(EDITOR_GET("docks/filesystem/always_show_folders"));
if (new_always_show_folders != always_show_folders) {
always_show_folders = new_always_show_folders;
+ do_redraw = true;
+ }
+
+ if (do_redraw) {
_update_file_list(true);
+ _update_tree(get_uncollapsed_paths());
}
// Change full tree mode.
@@ -900,7 +940,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
}
Ref<Texture2D> folder_icon = (use_thumbnails) ? folder_thumbnail : get_theme_icon(SNAME("folder"), SNAME("FileDialog"));
- const Color folder_color = get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog"));
+ const Color default_folder_color = get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog"));
// Build the FileInfo list.
List<FileInfo> file_list;
@@ -967,6 +1007,21 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
_search(EditorFileSystem::get_singleton()->get_filesystem(), &file_list, 10000);
} else {
if (display_mode == DISPLAY_MODE_TREE_ONLY || always_show_folders) {
+ // Check for a folder color to inherit (if one is assigned).
+ Color inherited_folder_color = default_folder_color;
+ String color_scan_dir = directory;
+ while (color_scan_dir != "res://" && inherited_folder_color == default_folder_color) {
+ if (!color_scan_dir.ends_with("/")) {
+ color_scan_dir += "/";
+ }
+
+ if (assigned_folder_colors.has(color_scan_dir)) {
+ inherited_folder_color = folder_colors[assigned_folder_colors[color_scan_dir]];
+ }
+
+ color_scan_dir = color_scan_dir.rstrip("/").get_base_dir();
+ }
+
// Display folders in the list.
if (directory != "res://") {
files->add_item("..", folder_icon, true);
@@ -978,7 +1033,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
files->set_item_metadata(-1, bd);
files->set_item_selectable(-1, false);
- files->set_item_icon_modulate(-1, folder_color);
+ files->set_item_icon_modulate(-1, editor_is_dark_theme ? inherited_folder_color : inherited_folder_color * 1.75);
}
bool reversed = file_sort == FILE_SORT_NAME_REVERSE;
@@ -986,10 +1041,13 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
reversed ? i >= 0 : i < efd->get_subdir_count();
reversed ? i-- : i++) {
String dname = efd->get_subdir(i)->get_name();
+ String dpath = directory.path_join(dname) + "/";
+ bool has_custom_color = assigned_folder_colors.has(dpath);
files->add_item(dname, folder_icon, true);
- files->set_item_metadata(-1, directory.path_join(dname) + "/");
- files->set_item_icon_modulate(-1, folder_color);
+ files->set_item_metadata(-1, dpath);
+ Color this_folder_color = has_custom_color ? folder_colors[assigned_folder_colors[dpath]] : inherited_folder_color;
+ files->set_item_icon_modulate(-1, editor_is_dark_theme ? this_folder_color : this_folder_color * 1.75);
if (cselection.has(dname)) {
files->select(files->get_item_count() - 1, false);
@@ -2706,6 +2764,42 @@ void FileSystemDock::_get_drag_target_folder(String &target, bool &target_favori
}
}
+void FileSystemDock::_folder_color_index_pressed(int p_index, PopupMenu *p_menu) {
+ Variant chosen_color_name = p_menu->get_item_metadata(p_index);
+ Vector<String> selected;
+
+ // Get all selected folders based on whether the files panel or tree panel is currently focused.
+ if (files->has_focus()) {
+ Vector<int> files_selected_ids = files->get_selected_items();
+ for (int i = 0; i < files_selected_ids.size(); i++) {
+ selected.push_back(files->get_item_metadata(files_selected_ids[i]));
+ }
+ } else {
+ TreeItem *tree_selected = tree->get_root();
+ tree_selected = tree->get_next_selected(tree_selected);
+ while (tree_selected) {
+ selected.push_back(tree_selected->get_metadata(0));
+ tree_selected = tree->get_next_selected(tree_selected);
+ }
+ }
+
+ // Update project settings with new folder colors.
+ for (int i = 0; i < selected.size(); i++) {
+ String fpath = selected[i];
+
+ if (chosen_color_name) {
+ assigned_folder_colors[fpath] = chosen_color_name;
+ } else {
+ assigned_folder_colors.erase(fpath);
+ }
+ }
+
+ ProjectSettings::get_singleton()->save();
+
+ _update_tree(get_uncollapsed_paths());
+ _update_file_list(true);
+}
+
void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options) {
// Add options for files and folders.
ERR_FAIL_COND_MSG(p_paths.is_empty(), "Path cannot be empty.");
@@ -2798,6 +2892,27 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str
}
p_popup->add_separator();
+
+ if (p_paths[0] != "res://") {
+ PopupMenu *folder_colors_menu = memnew(PopupMenu);
+ folder_colors_menu->set_name("FolderColor");
+ folder_colors_menu->connect("id_pressed", callable_mp(this, &FileSystemDock::_folder_color_index_pressed).bind(folder_colors_menu));
+
+ p_popup->add_child(folder_colors_menu);
+ p_popup->add_submenu_item(TTR("Set Folder Color..."), "FolderColor");
+ p_popup->set_item_icon(-1, get_theme_icon(SNAME("CanvasItem"), SNAME("EditorIcons")));
+
+ folder_colors_menu->add_icon_item(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")), TTR("Default (Reset)"));
+ folder_colors_menu->set_item_icon_modulate(0, get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog")));
+ folder_colors_menu->add_separator();
+
+ for (const KeyValue<String, Color> &E : folder_colors) {
+ folder_colors_menu->add_icon_item(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")), TTR(E.key.capitalize()));
+
+ folder_colors_menu->set_item_icon_modulate(-1, editor_is_dark_theme ? E.value : E.value * 2);
+ folder_colors_menu->set_item_metadata(-1, E.key);
+ }
+ }
}
if (p_paths.size() == 1) {
@@ -2882,6 +2997,8 @@ void FileSystemDock::_tree_rmb_select(const Vector2 &p_pos, MouseButton p_button
if (p_button != MouseButton::RIGHT) {
return;
}
+ tree->grab_focus();
+
// Right click is pressed in the tree.
Vector<String> paths = _tree_get_selected(false);
@@ -2929,6 +3046,7 @@ void FileSystemDock::_file_list_item_clicked(int p_item, const Vector2 &p_pos, M
if (p_mouse_button_index != MouseButton::RIGHT) {
return;
}
+ files->grab_focus();
// Right click is pressed in the file list.
Vector<String> paths;
@@ -3332,6 +3450,8 @@ FileSystemDock::FileSystemDock() {
set_name("FileSystem");
current_path = "res://";
+ ProjectSettings::get_singleton()->add_hidden_prefix("file_customization/");
+
// `KeyModifierMask::CMD_OR_CTRL | Key::C` conflicts with other editor shortcuts.
ED_SHORTCUT("filesystem_dock/copy_path", TTR("Copy Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::C);
ED_SHORTCUT("filesystem_dock/copy_uid", TTR("Copy UID"));
@@ -3345,6 +3465,21 @@ FileSystemDock::FileSystemDock() {
ED_SHORTCUT("filesystem_dock/open_in_external_program", TTR("Open in External Program"));
#endif
+ folder_colors = HashMap<String, Color>();
+ folder_colors["red"] = Color(1.0, 0.271, 0.271);
+ folder_colors["orange"] = Color(1.0, 0.561, 0.271);
+ folder_colors["yellow"] = Color(1.0, 0.890, 0.271);
+ folder_colors["green"] = Color(0.502, 1.0, 0.271);
+ folder_colors["teal"] = Color(0.271, 1.0, 0.635);
+ folder_colors["blue"] = Color(0.271, 0.843, 1.0);
+ folder_colors["purple"] = Color(0.502, 0.271, 1.0);
+ folder_colors["pink"] = Color(1.0, 0.271, 0.588);
+ folder_colors["gray"] = Color(0.616, 0.616, 0.616);
+
+ assigned_folder_colors = ProjectSettings::get_singleton()->get_setting("file_customization/folder_colors");
+
+ editor_is_dark_theme = EditorSettings::get_singleton()->is_dark_theme();
+
VBoxContainer *top_vbc = memnew(VBoxContainer);
add_child(top_vbc);
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 0af3193aa5..95b78f14c9 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -137,6 +137,9 @@ private:
OVERWRITE_RENAME,
};
+ HashMap<String, Color> folder_colors;
+ Dictionary assigned_folder_colors;
+
FileSortOption file_sort = FILE_SORT_NAME;
VBoxContainer *scanning_vb = nullptr;
@@ -195,6 +198,8 @@ private:
bool always_show_folders = false;
+ bool editor_is_dark_theme = false;
+
class FileOrFolder {
public:
String path;
@@ -299,6 +304,7 @@ private:
MenuButton *_create_file_menu_button();
void _file_sort_popup(int p_id);
+ void _folder_color_index_pressed(int p_index, PopupMenu *p_menu);
void _file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options = true);
void _tree_rmb_select(const Vector2 &p_pos, MouseButton p_button);
void _file_list_item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
diff --git a/editor/icons/BaseButton.svg b/editor/icons/BaseButton.svg
index 9aa0ae1c07..039becb131 100644
--- a/editor/icons/BaseButton.svg
+++ b/editor/icons/BaseButton.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5.5 9c-.831 0-1.5.669-1.5 1.5v1.5h-2v2h12v-2h-2v-1.5c0-.831-.669-1.5-1.5-1.5z" fill="#8eef97"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M5.5 9C4.669 9 4 9.669 4 10.5V12H2v2h12v-2h-2v-1.5c0-.831-.669-1.5-1.5-1.5z" fill="#76ad7b"/></svg>
diff --git a/editor/icons/CameraAttributes.svg b/editor/icons/CameraAttributes.svg
index 459c64e11c..5e82205d92 100644
--- a/editor/icons/CameraAttributes.svg
+++ b/editor/icons/CameraAttributes.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M6 2a3 3 0 0 0-2 5.23V9L1 7v6l3-2v1a1 1 0 0 0 1 1h3a2 2 0 0 1 .73-1.24 1.83 1.83 0 0 1 1.828-3.143 1.8 1.8 0 0 1 3.313-.75 3 3 0 0 0-4.883-3.09A3 3 0 0 0 6 2z" fill="#e0e0e0"/><path d="M12.36 8.598a.533 3.2 0 0 0-.51 2.275 3.2.533 30 0 0-.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0-.515-.887.533 3.2 0 0 0-.51-2.275z" fill="#c38ef1"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M6 2a3 3 0 0 0-2 5.23V9L1 7v6l3-2v1a1 1 0 0 0 1 1h3a2 2 0 0 1 .73-1.24 1.83 1.83 0 0 1 1.828-3.143 1.8 1.8 0 0 1 3.313-.75 3 3 0 0 0-4.883-3.09A3 3 0 0 0 6 2z" fill="#808080"/><path d="M12.36 8.598a.533 3.2 0 0 0-.51 2.275 3.2.533 30 0 0-.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0-.515-.887.533 3.2 0 0 0-.51-2.275z" fill="#c38ef1"/></svg>
diff --git a/editor/icons/CanvasItem.svg b/editor/icons/CanvasItem.svg
index 1868a3d632..ec36a3b1f5 100644
--- a/editor/icons/CanvasItem.svg
+++ b/editor/icons/CanvasItem.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2.92 10c-.263.3-.42.73-.42 1.238 0 1.628-3.138-.178-.337 2.67.884.9 2.654.67 3.538-.228a2.33 2.33 0 0 0 0-3.256c-1.1-1.119-2.2-1.084-2.78-.424zm2.381-1.61 2.4 2.441 6.802-6.917a1.7 1.7 0 0 0-2.4-2.442z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2.92 10c-.263.3-.42.73-.42 1.238 0 1.628-3.138-.178-.337 2.67.884.9 2.654.67 3.538-.228a2.33 2.33 0 0 0 0-3.256c-1.1-1.119-2.2-1.084-2.78-.424zm2.3-1.64 2.4 2.4 6.8-6.8a1.7 1.7 0 0 0-2.4-2.45z" fill="#808080"/></svg>
diff --git a/editor/icons/Font.svg b/editor/icons/Font.svg
index afdb9123e8..f66f65822c 100644
--- a/editor/icons/Font.svg
+++ b/editor/icons/Font.svg
@@ -1 +1 @@
-<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M1 1h14v4h-1a2 2 0 0 0-2-2H9v9a2 2 0 0 0 2 2v1H5v-1a2 2 0 0 0 2-2V3H4a2 2 0 0 0-2 2H1z" fill="#e0e0e0"/></svg>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M1 1h14v4h-1a2 2 0 0 0-2-2H9v9a2 2 0 0 0 2 2v1H5v-1a2 2 0 0 0 2-2V3H4a2 2 0 0 0-2 2H1z" fill="#808080"/></svg>
diff --git a/editor/icons/GeometryInstance3D.svg b/editor/icons/GeometryInstance3D.svg
index 8a3e2553cf..a211490d4a 100644
--- a/editor/icons/GeometryInstance3D.svg
+++ b/editor/icons/GeometryInstance3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.729 2A2 2 0 1 0 2 4.73v6.541A2 2 0 1 0 4.73 14h6.541a2 2 0 1 0 2.698-2.75H14V4.729A2 2 0 1 0 11.27 2H4.729zm6.542 2a2 2 0 0 0 .729.729v6.542a2 2 0 0 0-.729.729H4.729A2 2 0 0 0 4 11.271V4.729A2 2 0 0 0 4.729 4z" fill="#fc7f7f"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.729 2A2 2 0 1 0 2 4.73v6.541A2 2 0 1 0 4.73 14h6.541a2 2 0 1 0 2.698-2.75H14V4.729A2 2 0 1 0 11.27 2H4.729zm6.542 2a2 2 0 0 0 .729.729v6.542a2 2 0 0 0-.729.729H4.729A2 2 0 0 0 4 11.271V4.729A2 2 0 0 0 4.729 4z" fill="#b56d6d"/></svg>
diff --git a/editor/icons/Mesh.svg b/editor/icons/Mesh.svg
index 90d6fe895c..9aeb8408c5 100644
--- a/editor/icons/Mesh.svg
+++ b/editor/icons/Mesh.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.73 2A2 2 0 1 0 2 4.73v6.541A2 2 0 1 0 4.73 14h6.541A2 2 0 1 0 14 11.27V4.729A2 2 0 1 0 11.27 2Zm.683 2h5.857a2 2 0 0 0 .729.73v5.856L5.412 4zM3.999 5.414 10.584 12H4.727a2 2 0 0 0-.729-.73V5.414z" fill="#ffca5f"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.73 2A2 2 0 1 0 2 4.73v6.541A2 2 0 1 0 4.73 14h6.541A2 2 0 1 0 14 11.27V4.729A2 2 0 1 0 11.27 2Zm.683 2h5.857a2 2 0 0 0 .729.73v5.856L5.412 4zM3.999 5.414 10.584 12H4.727a2 2 0 0 0-.729-.73V5.414z" fill="#808080"/></svg>
diff --git a/editor/icons/ObjectDisabled.svg b/editor/icons/ObjectDisabled.svg
new file mode 100644
index 0000000000..7388a97910
--- /dev/null
+++ b/editor/icons/ObjectDisabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1.553 4.104A1 1 0 0 0 1 5v6a1 1 0 0 0 .553.895l6 3a1 1 0 0 0 .894 0l6-3A1 1 0 0 0 15 11V5a1 1 0 0 0-.553-.894l-6-3a1 1 0 0 0-.894 0zm6.447-1 3.764 1.882L8 6.868 4.236 4.986zm-5 3.5 4 2v3.766l-4-2z" fill="#808080"/></svg>
diff --git a/editor/icons/Occluder3D.svg b/editor/icons/Occluder3D.svg
index c91a77781b..5cdcb899e6 100644
--- a/editor/icons/Occluder3D.svg
+++ b/editor/icons/Occluder3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m13 1a2 2 0 0 0 -1.730469 1h-3.0273435a4.5 4.5 0 0 1 .7285156 2h2.3007809a2 2 0 0 0 .728516.7304688v5.8554692l-3.6933594-3.6933599a4.5 4.5 0 0 1 -1.4140625 1.4140625l3.6933599 3.6933594h-5.8574224a2 2 0 0 0 -.7285156-.730469v-2.3046872a4.5 4.5 0 0 1 -2-.7285157v3.0351559a2 2 0 0 0 -1 1.728516 2 2 0 0 0 2 2 2 2 0 0 0 1.7304688-1h6.5410152a2 2 0 0 0 1.728516 1 2 2 0 0 0 2-2 2 2 0 0 0 -1.03125-1.75h.03125v-6.5214844a2 2 0 0 0 1-1.7285156 2 2 0 0 0 -2-2z" fill="#ffca5f"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8.242 2a4.5 4.5 0 0 1 .729 2h2.3a2 2 0 0 0 .729.73v5.856L8.307 6.893a4.5 4.5 0 0 1-1.414 1.414L10.586 12H4.729A2 2 0 0 0 4 11.27V8.965a4.5 4.5 0 0 1-2-.729v3.035A2 2 0 1 0 4.73 14h6.541a2 2 0 1 0 2.698-2.75H14V4.729A2 2 0 1 0 11.27 2z" fill="#808080"/></svg>
diff --git a/editor/icons/Range.svg b/editor/icons/Range.svg
index e3f42cdedb..a171922824 100644
--- a/editor/icons/Range.svg
+++ b/editor/icons/Range.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.75 8 0.75 3.5 3.5 0.75 M8 3.75 l3.5 0.75 0.75 3.5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke="#8eef97" fill="none"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 8 1 4 4 1M8 3l4 1 1 4" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" stroke="#76ad7b" fill="none"/></svg>
diff --git a/editor/icons/VideoStream.svg b/editor/icons/VideoStream.svg
index e3459d5b58..0e7163f42b 100644
--- a/editor/icons/VideoStream.svg
+++ b/editor/icons/VideoStream.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" x2="0" y1="2" y2="14" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ff8dbc"/><stop offset=".4" stop-color="#7260ff"/><stop offset=".6" stop-color="#7260ff"/><stop offset="1" stop-color="#74c9ff"/></linearGradient><path d="m12 2 a-1 1 0 0 1 0 2a 8 8 0 0 0-8 8a-1 1 0 0 1-2 0a10 10 0 0 1 10-10zm0 4A-1 1 0 0 1 12 8a4 4 0 0 0-4 4A-1 1 0 0 1 6 12a6 6 0 0 1 6-6zm-2 4.5 a.667.667 0 0 1 1-0.566l2.667 1.674a.667.667 0 0 1 0 1.149l-2.667 1.674a.667 0.667 0 0 1-1-.566z" fill="url(#a)"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12 2 a-1 1 0 0 1 0 2a 8 8 0 0 0-8 8a-1 1 0 0 1-2 0a10 10 0 0 1 10-10zm0 4A-1 1 0 0 1 12 8a4 4 0 0 0-4 4A-1 1 0 0 1 6 12a6 6 0 0 1 6-6zm-2 4.5 a.667.667 0 0 1 1-0.566l2.667 1.674a.667.667 0 0 1 0 1.149l-2.667 1.674a.667 0.667 0 0 1-1-.566z" fill="#808080"/></svg>
diff --git a/editor/icons/Viewport.svg b/editor/icons/Viewport.svg
index bbb001563c..49d9993174 100644
--- a/editor/icons/Viewport.svg
+++ b/editor/icons/Viewport.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><rect x="1.5" y="2.5" height="11" width="13" rx="1.5" stroke="#e0e0e0" fill="none"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><rect x="1.75" y="2.75" height="10.5" width="12.5" rx="1.5" stroke="#808080" stroke-width="1.5" fill="none"/></svg>
diff --git a/editor/icons/VisualInstance3D.svg b/editor/icons/VisualInstance3D.svg
index e5e43b59dd..95eb6325af 100644
--- a/editor/icons/VisualInstance3D.svg
+++ b/editor/icons/VisualInstance3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><circle cx="3" cy="3" r="2"/><circle cx="13" cy="3" r="2"/><circle cx="13" cy="13" r="2"/><circle cx="3" cy="13" r="2"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#b56d6d"><circle cx="3" cy="3" r="2"/><circle cx="13" cy="3" r="2"/><circle cx="13" cy="13" r="2"/><circle cx="3" cy="13" r="2"/></g></svg>
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 1dfabf70fd..f8da47b45e 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -480,16 +480,18 @@ void ScriptTextEditor::_validate_script() {
if (!script->get_language()->validate(text, script->get_path(), &fnc, &errors, &warnings, &safe_lines)) {
for (List<ScriptLanguage::ScriptError>::Element *E = errors.front(); E; E = E->next()) {
- if (E->get().path.is_empty() || E->get().path != script->get_path()) {
+ if ((E->get().path.is_empty() && !script->get_path().is_empty()) || E->get().path != script->get_path()) {
depended_errors[E->get().path].push_back(E->get());
E->erase();
}
}
- // TRANSLATORS: Script error pointing to a line and column number.
- String error_text = vformat(TTR("Error at (%d, %d):"), errors[0].line, errors[0].column) + " " + errors[0].message;
- code_editor->set_error(error_text);
- code_editor->set_error_pos(errors[0].line - 1, errors[0].column - 1);
+ if (errors.size() > 0) {
+ // TRANSLATORS: Script error pointing to a line and column number.
+ String error_text = vformat(TTR("Error at (%d, %d):"), errors[0].line, errors[0].column) + " " + errors[0].message;
+ code_editor->set_error(error_text);
+ code_editor->set_error_pos(errors[0].line - 1, errors[0].column - 1);
+ }
script_is_valid = false;
} else {
code_editor->set_error("");