diff options
Diffstat (limited to 'editor')
| -rw-r--r-- | editor/create_dialog.cpp | 14 | ||||
| -rw-r--r-- | editor/editor_feature_profile.cpp | 6 | ||||
| -rw-r--r-- | editor/editor_help.cpp | 2 | ||||
| -rw-r--r-- | editor/editor_help_search.cpp | 9 | ||||
| -rw-r--r-- | editor/editor_help_search.h | 1 | ||||
| -rw-r--r-- | editor/editor_inspector.cpp | 4 | ||||
| -rw-r--r-- | editor/editor_node.cpp | 27 | ||||
| -rw-r--r-- | editor/editor_node.h | 4 | ||||
| -rw-r--r-- | editor/editor_themes.cpp | 7 | ||||
| -rw-r--r-- | editor/filesystem_dock.cpp | 159 | ||||
| -rw-r--r-- | editor/filesystem_dock.h | 6 | ||||
| -rw-r--r-- | editor/icons/BaseButton.svg | 2 | ||||
| -rw-r--r-- | editor/icons/CameraAttributes.svg | 2 | ||||
| -rw-r--r-- | editor/icons/CanvasItem.svg | 2 | ||||
| -rw-r--r-- | editor/icons/Font.svg | 2 | ||||
| -rw-r--r-- | editor/icons/GeometryInstance3D.svg | 2 | ||||
| -rw-r--r-- | editor/icons/Mesh.svg | 2 | ||||
| -rw-r--r-- | editor/icons/ObjectDisabled.svg | 1 | ||||
| -rw-r--r-- | editor/icons/Occluder3D.svg | 2 | ||||
| -rw-r--r-- | editor/icons/Range.svg | 2 | ||||
| -rw-r--r-- | editor/icons/VideoStream.svg | 2 | ||||
| -rw-r--r-- | editor/icons/Viewport.svg | 2 | ||||
| -rw-r--r-- | editor/icons/VisualInstance3D.svg | 2 | ||||
| -rw-r--r-- | editor/plugins/script_text_editor.cpp | 12 |
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(""); |
