diff options
author | Thaddeus Crews <repiteo@outlook.com> | 2024-11-11 14:17:57 -0600 |
---|---|---|
committer | Thaddeus Crews <repiteo@outlook.com> | 2024-11-11 14:17:57 -0600 |
commit | ed3de25a9a0cdc46f6cfbd12a9ce8e2d56bbae82 (patch) | |
tree | a77da6f2e5255fda5b0b3910a360de5f9fcc545e | |
parent | bb3d5ef47c6ae4ae0377ac3bd0e60f6b2976ca7f (diff) | |
parent | d678b095f9998a9a6fb785de3e712ec7605726a4 (diff) | |
download | redot-engine-ed3de25a9a0cdc46f6cfbd12a9ce8e2d56bbae82.tar.gz |
Merge pull request #97415 from YeldhamDev/i_love_all_my_properties_equally
Make possible to favorite properties in the inspector
-rw-r--r-- | doc/classes/EditorProperty.xml | 7 | ||||
-rw-r--r-- | editor/editor_inspector.cpp | 402 | ||||
-rw-r--r-- | editor/editor_inspector.h | 40 | ||||
-rw-r--r-- | editor/editor_settings.cpp | 46 | ||||
-rw-r--r-- | editor/editor_settings.h | 3 | ||||
-rw-r--r-- | editor/filesystem_dock.cpp | 28 | ||||
-rw-r--r-- | editor/filesystem_dock.h | 2 | ||||
-rw-r--r-- | editor/icons/Unfavorite.svg | 1 |
8 files changed, 491 insertions, 38 deletions
diff --git a/doc/classes/EditorProperty.xml b/doc/classes/EditorProperty.xml index 2b1083393f..4ff541f72d 100644 --- a/doc/classes/EditorProperty.xml +++ b/doc/classes/EditorProperty.xml @@ -132,6 +132,13 @@ Emitted when a property was deleted. Used internally. </description> </signal> + <signal name="property_favorited"> + <param index="0" name="property" type="StringName" /> + <param index="1" name="favorited" type="bool" /> + <description> + Emit it if you want to mark a property as favorited, making it appear at the top of the inspector. + </description> + </signal> <signal name="property_keyed"> <param index="0" name="property" type="StringName" /> <description> diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 91dd8e5019..1c23ce8ede 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -931,10 +931,19 @@ float EditorProperty::get_name_split_ratio() const { return split_ratio; } +void EditorProperty::set_favoritable(bool p_favoritable) { + can_favorite = p_favoritable; +} + +bool EditorProperty::is_favoritable() const { + return can_favorite; +} + void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) { object = p_object; property = p_property; - _update_pin_flags(); + + _update_flags(); } static bool _is_value_potential_override(Node *p_node, const String &p_property) { @@ -953,12 +962,14 @@ static bool _is_value_potential_override(Node *p_node, const String &p_property) } } -void EditorProperty::_update_pin_flags() { +void EditorProperty::_update_flags() { can_pin = false; pin_hidden = true; + if (read_only) { return; } + if (Node *node = Object::cast_to<Node>(object)) { // Avoid errors down the road by ignoring nodes which are not part of a scene if (!node->get_owner()) { @@ -1034,6 +1045,10 @@ void EditorProperty::menu_option(int p_option) { case MENU_COPY_PROPERTY_PATH: { DisplayServer::get_singleton()->clipboard_set(property_path); } break; + case MENU_FAVORITE_PROPERTY: { + emit_signal(SNAME("property_favorited"), property, !favorited); + queue_redraw(); + } break; case MENU_PIN_VALUE: { emit_signal(SNAME("property_pinned"), property, !pinned); queue_redraw(); @@ -1091,6 +1106,7 @@ void EditorProperty::_bind_methods() { ADD_SIGNAL(MethodInfo("property_deleted", PropertyInfo(Variant::STRING_NAME, "property"))); ADD_SIGNAL(MethodInfo("property_keyed_with_value", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "checked"))); + ADD_SIGNAL(MethodInfo("property_favorited", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "favorited"))); ADD_SIGNAL(MethodInfo("property_pinned", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "pinned"))); ADD_SIGNAL(MethodInfo("property_can_revert_changed", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "can_revert"))); ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); @@ -1129,8 +1145,21 @@ void EditorProperty::_update_popup() { menu->set_item_disabled(MENU_PASTE_VALUE, is_read_only()); menu->set_item_disabled(MENU_COPY_PROPERTY_PATH, internal); - if (!pin_hidden) { + if (can_favorite || !pin_hidden) { menu->add_separator(); + } + + if (can_favorite) { + if (favorited) { + menu->add_icon_item(get_editor_theme_icon(SNAME("Unfavorite")), TTR("Unfavorite Property"), MENU_FAVORITE_PROPERTY); + menu->set_item_tooltip(menu->get_item_index(MENU_FAVORITE_PROPERTY), TTR("Make this property be put back at its original place.")); + } else { + menu->add_icon_item(get_editor_theme_icon(SNAME("Favorites")), TTR("Favorite Property"), MENU_FAVORITE_PROPERTY); + menu->set_item_tooltip(menu->get_item_index(MENU_FAVORITE_PROPERTY), TTR("Make this property be placed at the top for all objects of this class.")); + } + } + + if (!pin_hidden) { if (can_pin) { menu->add_icon_check_item(get_editor_theme_icon(SNAME("Pin")), TTR("Pin Value"), MENU_PIN_VALUE); menu->set_item_checked(menu->get_item_index(MENU_PIN_VALUE), pinned); @@ -1219,9 +1248,14 @@ void EditorInspectorPlugin::_bind_methods() { void EditorInspectorCategory::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - menu->set_item_icon(menu->get_item_index(MENU_OPEN_DOCS), get_editor_theme_icon(SNAME("Help"))); + if (menu) { + if (is_favorite) { + menu->set_item_icon(menu->get_item_index(EditorInspector::MENU_UNFAVORITE_ALL), get_editor_theme_icon(SNAME("Unfavorite"))); + } else { + menu->set_item_icon(menu->get_item_index(MENU_OPEN_DOCS), get_editor_theme_icon(SNAME("Help"))); + } + } } break; case NOTIFICATION_DRAW: { Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); @@ -1278,6 +1312,15 @@ Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) cons return memnew(Control); // Make the standard tooltip invisible. } +void EditorInspectorCategory::set_as_favorite(EditorInspector *p_for_inspector) { + is_favorite = true; + + menu = memnew(PopupMenu); + menu->add_item(TTR("Unfavorite All"), EditorInspector::MENU_UNFAVORITE_ALL); + add_child(menu); + menu->connect(SceneStringName(id_pressed), callable_mp(p_for_inspector, &EditorInspector::_handle_menu_option)); +} + Size2 EditorInspectorCategory::get_minimum_size() const { Ref<Font> font = get_theme_font(SNAME("bold"), EditorStringName(EditorFonts)); int font_size = get_theme_font_size(SNAME("bold_size"), EditorStringName(EditorFonts)); @@ -1306,7 +1349,7 @@ void EditorInspectorCategory::_handle_menu_option(int p_option) { } void EditorInspectorCategory::gui_input(const Ref<InputEvent> &p_event) { - if (doc_class_name.is_empty()) { + if (!is_favorite && doc_class_name.is_empty()) { return; } @@ -1315,20 +1358,21 @@ void EditorInspectorCategory::gui_input(const Ref<InputEvent> &p_event) { return; } - menu->set_item_disabled(menu->get_item_index(MENU_OPEN_DOCS), !EditorHelp::get_doc_data()->class_list.has(doc_class_name)); + if (!is_favorite) { + if (!menu) { + menu = memnew(PopupMenu); + menu->add_icon_item(get_editor_theme_icon(SNAME("Help")), TTR("Open Documentation"), MENU_OPEN_DOCS); + add_child(menu); + menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorInspectorCategory::_handle_menu_option)); + } + menu->set_item_disabled(menu->get_item_index(MENU_OPEN_DOCS), !EditorHelp::get_doc_data()->class_list.has(doc_class_name)); + } menu->set_position(get_screen_position() + mb_event->get_position()); menu->reset_size(); menu->popup(); } -EditorInspectorCategory::EditorInspectorCategory() { - menu = memnew(PopupMenu); - menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorInspectorCategory::_handle_menu_option)); - menu->add_item(TTR("Open Documentation"), MENU_OPEN_DOCS); - add_child(menu); -} - //////////////////////////////////////////////// //////////////////////////////////////////////// @@ -1622,6 +1666,10 @@ void EditorInspectorSection::gui_input(const Ref<InputEvent> &p_event) { } } +String EditorInspectorSection::get_section() const { + return section; +} + VBoxContainer *EditorInspectorSection::get_vbox() { return vbox; } @@ -2675,7 +2723,13 @@ String EditorInspector::get_selected_path() const { void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorInspectorSection *p_section, Ref<EditorInspectorPlugin> ped) { for (const EditorInspectorPlugin::AddedEditor &F : ped->added_editors) { EditorProperty *ep = Object::cast_to<EditorProperty>(F.property_editor); - current_vbox->add_child(F.property_editor); + + if (ep && current_favorites.has(F.properties[0])) { + ep->favorited = true; + favorites_vbox->add_child(F.property_editor); + } else { + current_vbox->add_child(F.property_editor); + } if (ep) { ep->object = object; @@ -2684,6 +2738,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorIn ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), CONNECT_DEFERRED); ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value)); ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked)); + ep->connect("property_favorited", callable_mp(this, &EditorInspector::_set_property_favorited), CONNECT_DEFERRED); ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned)); ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected)); ep->connect("multiple_properties_changed", callable_mp(this, &EditorInspector::_multiple_properties_changed)); @@ -2727,7 +2782,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorIn ep->set_read_only(read_only); ep->update_property(); - ep->_update_pin_flags(); + ep->_update_flags(); ep->update_editor_property_status(); ep->set_deletable(deletable_properties); ep->update_cache(); @@ -2837,6 +2892,7 @@ void EditorInspector::update_tree() { String subgroup; String subgroup_base; int section_depth = 0; + bool disable_favorite = false; VBoxContainer *category_vbox = nullptr; List<PropertyInfo> plist; @@ -2844,13 +2900,17 @@ void EditorInspector::update_tree() { HashMap<VBoxContainer *, HashMap<String, VBoxContainer *>> vbox_per_path; HashMap<String, EditorInspectorArray *> editor_inspector_array_per_prefix; + HashMap<String, HashMap<String, LocalVector<EditorProperty *>>> favorites_to_add; Color sscolor = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)); // Get the lists of editors to add the beginning. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { ped->parse_begin(object); - _parse_added_editors(main_vbox, nullptr, ped); + _parse_added_editors(begin_vbox, nullptr, ped); + } + if (begin_vbox->get_child_count()) { + begin_vbox->show(); } StringName doc_name; @@ -2897,6 +2957,7 @@ void EditorInspector::update_tree() { subgroup = ""; subgroup_base = ""; section_depth = 0; + disable_favorite = false; vbox_per_path.clear(); editor_inspector_array_per_prefix.clear(); @@ -2960,6 +3021,11 @@ void EditorInspector::update_tree() { } else { category_icon = EditorNode::get_singleton()->get_object_icon(scr.ptr(), "Object"); } + + // Property favorites aren't compatible with built-in scripts. + if (scr->is_built_in()) { + disable_favorite = true; + } } } @@ -3058,6 +3124,11 @@ void EditorInspector::update_tree() { } } + // Don't allow to favorite array items. + if (!disable_favorite) { + disable_favorite = !array_prefix.is_empty(); + } + if (!array_prefix.is_empty()) { path = path.trim_prefix(array_prefix); int char_index = path.find("/"); @@ -3449,6 +3520,7 @@ void EditorInspector::update_tree() { ep->set_draw_warning(draw_warning); ep->set_use_folding(use_folding); + ep->set_favoritable(can_favorite && !disable_favorite); ep->set_checkable(checkable); ep->set_checked(checked); ep->set_keying(keying); @@ -3456,7 +3528,12 @@ void EditorInspector::update_tree() { ep->set_deletable(deletable_properties || p.name.begins_with("metadata/")); } - current_vbox->add_child(editors[i].property_editor); + if (ep && ep->is_favoritable() && current_favorites.has(p.name)) { + ep->favorited = true; + favorites_to_add[group][subgroup].push_back(ep); + } else { + current_vbox->add_child(editors[i].property_editor); + } if (ep) { // Eventually, set other properties/signals after the property editor got added to the tree. @@ -3465,6 +3542,7 @@ void EditorInspector::update_tree() { ep->connect("property_keyed", callable_mp(this, &EditorInspector::_property_keyed)); ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), CONNECT_DEFERRED); ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value)); + ep->connect("property_favorited", callable_mp(this, &EditorInspector::_set_property_favorited), CONNECT_DEFERRED); ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked)); ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned)); ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected)); @@ -3495,7 +3573,7 @@ void EditorInspector::update_tree() { ep->set_internal(p.usage & PROPERTY_USAGE_INTERNAL); ep->update_property(); - ep->_update_pin_flags(); + ep->_update_flags(); ep->update_editor_property_status(); ep->update_cache(); @@ -3506,6 +3584,79 @@ void EditorInspector::update_tree() { } } + if (!current_favorites.is_empty()) { + favorites_section->show(); + + // Organize the favorited properties in their sections, to keep context and differentiate from others with the same name. + bool is_localized = property_name_style == EditorPropertyNameProcessor::STYLE_LOCALIZED; + for (const KeyValue<String, HashMap<String, LocalVector<EditorProperty *>>> &KV : favorites_to_add) { + String section_name = KV.key; + String label; + String tooltip; + VBoxContainer *parent_vbox = favorites_vbox; + if (!section_name.is_empty()) { + if (is_localized) { + label = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name); + tooltip = section_name; + } else { + label = section_name; + tooltip = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name); + } + + EditorInspectorSection *section = memnew(EditorInspectorSection); + favorites_groups_vbox->add_child(section); + parent_vbox = section->get_vbox(); + section->setup("", section_name, object, sscolor, false); + section->set_tooltip_text(tooltip); + } + + for (const KeyValue<String, LocalVector<EditorProperty *>> &KV2 : KV.value) { + section_name = KV2.key; + VBoxContainer *vbox = parent_vbox; + if (!section_name.is_empty()) { + if (is_localized) { + label = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name); + tooltip = section_name; + } else { + label = section_name; + tooltip = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name); + } + + EditorInspectorSection *section = memnew(EditorInspectorSection); + vbox->add_child(section); + vbox = section->get_vbox(); + section->setup("", section_name, object, sscolor, false); + section->set_tooltip_text(tooltip); + } + + for (EditorProperty *ep : KV2.value) { + vbox->add_child(ep); + } + } + } + + // Show a separator if there's no category to clearly divide the properties. + favorites_separator->hide(); + if (main_vbox->get_child_count() > 0) { + EditorInspectorCategory *category = Object::cast_to<EditorInspectorCategory>(main_vbox->get_child(0)); + if (!category) { + favorites_separator->show(); + } + } + + // Clean up empty sections. + for (List<EditorInspectorSection *>::Element *I = sections.back(); I; I = I->prev()) { + EditorInspectorSection *section = I->get(); + if (section->get_vbox()->get_child_count() == 0) { + I = I->prev(); + + sections.erase(section); + vbox_per_path[main_vbox].erase(section->get_section()); + memdelete(section); + } + } + } + if (!hide_metadata && !object->call("_hide_metadata_from_inspector")) { // Add 4px of spacing between the "Add Metadata" button and the content above it. Control *spacer = memnew(Control); @@ -3548,6 +3699,19 @@ void EditorInspector::update_property(const String &p_prop) { } void EditorInspector::_clear(bool p_hide_plugins) { + begin_vbox->hide(); + while (begin_vbox->get_child_count()) { + memdelete(begin_vbox->get_child(0)); + } + + favorites_section->hide(); + while (favorites_vbox->get_child_count()) { + memdelete(favorites_vbox->get_child(0)); + } + while (favorites_groups_vbox->get_child_count()) { + memdelete(favorites_groups_vbox->get_child(0)); + } + while (main_vbox->get_child_count()) { memdelete(main_vbox->get_child(0)); } @@ -3594,6 +3758,10 @@ void EditorInspector::edit(Object *p_object) { update_scroll_request = scroll_cache[object->get_instance_id()]; //done this way because wait until full size is accommodated } object->connect(CoreStringName(property_list_changed), callable_mp(this, &EditorInspector::_changed_callback)); + + can_favorite = Object::cast_to<Node>(object) || Object::cast_to<Resource>(object); + _update_current_favorites(); + update_tree(); } @@ -4088,10 +4256,164 @@ void EditorInspector::_node_removed(Node *p_node) { } } +void EditorInspector::_update_current_favorites() { + current_favorites.clear(); + if (!can_favorite) { + return; + } + + HashMap<String, PackedStringArray> favorites = EditorSettings::get_singleton()->get_favorite_properties(); + + // Fetch script properties. + Ref<Script> scr = object->get_script(); + if (scr.is_valid()) { + List<PropertyInfo> plist; + // FIXME: Only properties from a saved script will be available, unsaved ones will be ignored. + // Can cause a little wonkiness, while nothing serious, would be nice to find a way to get + // unsaved ones without needing to get the entire property list of an object. + scr->get_script_property_list(&plist); + + String path; + HashMap<String, LocalVector<String>> props; + + for (PropertyInfo &p : plist) { + if (p.usage & PROPERTY_USAGE_CATEGORY) { + path = favorites.has(p.hint_string) ? p.hint_string : String(); + } else if (p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE && !path.is_empty()) { + props[path].push_back(p.name); + } + } + + // Add favorited properties while removing invalid ones. + bool invalid_props = false; + for (const KeyValue<String, LocalVector<String>> &KV : props) { + path = KV.key; + for (int i = 0; i < favorites[path].size(); i++) { + String prop = favorites[path][i]; + if (KV.value.has(prop)) { + current_favorites.append(prop); + } else { + invalid_props = true; + favorites[path].erase(prop); + i--; + } + } + + if (favorites[path].is_empty()) { + favorites.erase(path); + } + } + + if (invalid_props) { + EditorSettings::get_singleton()->set_favorite_properties(favorites); + } + } + + // Fetch built-in properties. + StringName class_name = object->get_class_name(); + for (const KeyValue<String, PackedStringArray> &KV : favorites) { + if (ClassDB::is_parent_class(class_name, KV.key)) { + current_favorites.append_array(KV.value); + } + } +} + +void EditorInspector::_set_property_favorited(const String &p_path, bool p_favorited) { + if (!object) { + return; + } + + StringName class_name = object->get_class_name(); + while (!class_name.is_empty()) { + bool has_prop = ClassDB::has_property(class_name, p_path, true); + if (has_prop) { + break; + } + + class_name = ClassDB::get_parent_class_nocheck(class_name); + } + + if (class_name.is_empty()) { + Ref<Script> scr = object->get_script(); + if (scr.is_valid()) { + List<PropertyInfo> plist; + scr->get_script_property_list(&plist); + + String path; + for (PropertyInfo &p : plist) { + if (p.usage & PROPERTY_USAGE_CATEGORY) { + path = p.hint_string; + } else if (p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE && p.name == p_path) { + class_name = path; + break; + } + } + } + + ERR_FAIL_COND_MSG(class_name.is_empty(), "Can't favorite invalid property. If said property was from a script and recently renamed, try saving it first."); + } + + HashMap<String, PackedStringArray> favorites = EditorSettings::get_singleton()->get_favorite_properties(); + if (p_favorited) { + current_favorites.append(p_path); + favorites[class_name].append(p_path); + } else { + current_favorites.erase(p_path); + + if (favorites.has(class_name) && favorites[class_name].has(p_path)) { + if (favorites[class_name].size() > 1) { + favorites[class_name].erase(p_path); + } else { + favorites.erase(class_name); + } + } + } + EditorSettings::get_singleton()->set_favorite_properties(favorites); + + update_tree(); +} + +void EditorInspector::_clear_current_favorites() { + current_favorites.clear(); + + HashMap<String, PackedStringArray> favorites = EditorSettings::get_singleton()->get_favorite_properties(); + + Ref<Script> scr = object->get_script(); + if (scr.is_valid()) { + List<PropertyInfo> plist; + scr->get_script_property_list(&plist); + + for (PropertyInfo &p : plist) { + if (p.usage & PROPERTY_USAGE_CATEGORY && favorites.has(p.hint_string)) { + favorites.erase(p.hint_string); + } + } + } + + StringName class_name = object->get_class_name(); + while (class_name) { + if (favorites.has(class_name)) { + favorites.erase(class_name); + } + + class_name = ClassDB::get_parent_class(class_name); + } + + EditorSettings::get_singleton()->set_favorite_properties(favorites); + update_tree(); +} + void EditorInspector::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - main_vbox->add_theme_constant_override("separation", get_theme_constant(SNAME("v_separation"), SNAME("EditorInspector"))); + favorites_category->icon = get_editor_theme_icon(SNAME("Favorites")); + + int separation = get_theme_constant(SNAME("v_separation"), SNAME("EditorInspector")); + base_vbox->add_theme_constant_override("separation", separation); + begin_vbox->add_theme_constant_override("separation", separation); + favorites_section->add_theme_constant_override("separation", separation); + favorites_groups_vbox->add_theme_constant_override("separation", separation); + main_vbox->add_theme_constant_override("separation", separation); } break; case NOTIFICATION_READY: { @@ -4189,6 +4511,7 @@ void EditorInspector::_notification(int p_what) { void EditorInspector::_changed_callback() { //this is called when property change is notified via notify_property_list_changed() if (object != nullptr) { + _update_current_favorites(); _edit_request_change(object, String()); } } @@ -4278,6 +4601,14 @@ void EditorInspector::_add_meta_confirm() { undo_redo->commit_action(); } +void EditorInspector::_handle_menu_option(int p_option) { + switch (p_option) { + case MENU_UNFAVORITE_ALL: + _clear_current_favorites(); + break; + } +} + void EditorInspector::_bind_methods() { ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change); ClassDB::bind_method("get_selected_path", &EditorInspector::get_selected_path); @@ -4296,9 +4627,36 @@ void EditorInspector::_bind_methods() { EditorInspector::EditorInspector() { object = nullptr; + + base_vbox = memnew(VBoxContainer); + base_vbox->set_h_size_flags(SIZE_EXPAND_FILL); + add_child(base_vbox); + + begin_vbox = memnew(VBoxContainer); + base_vbox->add_child(begin_vbox); + begin_vbox->hide(); + + favorites_section = memnew(VBoxContainer); + base_vbox->add_child(favorites_section); + favorites_section->hide(); + + favorites_category = memnew(EditorInspectorCategory); + favorites_category->set_as_favorite(this); + favorites_section->add_child(favorites_category); + favorites_category->label = TTR("Favorites"); + + favorites_vbox = memnew(VBoxContainer); + favorites_section->add_child(favorites_vbox); + favorites_groups_vbox = memnew(VBoxContainer); + favorites_section->add_child(favorites_groups_vbox); + + favorites_separator = memnew(HSeparator); + favorites_section->add_child(favorites_separator); + favorites_separator->hide(); + main_vbox = memnew(VBoxContainer); - main_vbox->set_h_size_flags(SIZE_EXPAND_FILL); - add_child(main_vbox); + base_vbox->add_child(main_vbox); + set_horizontal_scroll_mode(SCROLL_MODE_DISABLED); set_follow_focus(true); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 0309213b76..2e4633ccea 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -41,6 +41,7 @@ class Button; class ConfirmationDialog; class EditorInspector; class EditorValidationPanel; +class HSeparator; class LineEdit; class MarginContainer; class OptionButton; @@ -64,6 +65,7 @@ public: MENU_COPY_VALUE, MENU_PASTE_VALUE, MENU_COPY_PROPERTY_PATH, + MENU_FAVORITE_PROPERTY, MENU_PIN_VALUE, MENU_OPEN_DOCUMENTATION, }; @@ -112,6 +114,9 @@ private: bool pin_hidden = false; bool pinned = false; + bool can_favorite = false; + bool favorited = false; + bool use_folding = false; bool draw_top_bg = true; @@ -134,7 +139,7 @@ private: GDVIRTUAL0(_update_property) GDVIRTUAL1(_set_read_only, bool) - void _update_pin_flags(); + void _update_flags(); protected: bool has_borders = false; @@ -218,6 +223,9 @@ public: void set_name_split_ratio(float p_ratio); float get_name_split_ratio() const; + void set_favoritable(bool p_favoritable); + bool is_favoritable() const; + void set_object_and_property(Object *p_object, const StringName &p_property); virtual Control *make_custom_tooltip(const String &p_text) const override; @@ -285,6 +293,7 @@ class EditorInspectorCategory : public Control { String label; String doc_class_name; PopupMenu *menu = nullptr; + bool is_favorite = false; void _handle_menu_option(int p_option); @@ -293,10 +302,10 @@ protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; public: + void set_as_favorite(EditorInspector *p_for_inspector); + virtual Size2 get_minimum_size() const override; virtual Control *make_custom_tooltip(const String &p_text) const override; - - EditorInspectorCategory(); }; class EditorInspectorSection : public Container { @@ -331,6 +340,7 @@ public: virtual Size2 get_minimum_size() const override; void setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable, int p_indent_depth = 0, int p_level = 1); + String get_section() const; VBoxContainer *get_vbox(); void unfold(); void fold(); @@ -480,13 +490,31 @@ public: class EditorInspector : public ScrollContainer { GDCLASS(EditorInspector, ScrollContainer); + friend class EditorInspectorCategory; + enum { MAX_PLUGINS = 1024 }; static Ref<EditorInspectorPlugin> inspector_plugins[MAX_PLUGINS]; static int inspector_plugin_count; + // Right-click context menu options. + enum ClassMenuOption { + MENU_UNFAVORITE_ALL, + }; + + bool can_favorite = false; + PackedStringArray current_favorites; + VBoxContainer *favorites_section = nullptr; + EditorInspectorCategory *favorites_category = nullptr; + VBoxContainer *favorites_vbox = nullptr; + VBoxContainer *favorites_groups_vbox = nullptr; + HSeparator *favorites_separator = nullptr; + EditorInspector *root_inspector = nullptr; + + VBoxContainer *base_vbox = nullptr; + VBoxContainer *begin_vbox = nullptr; VBoxContainer *main_vbox = nullptr; // Map used to cache the instantiated editors. @@ -557,6 +585,10 @@ class EditorInspector : public ScrollContainer { void _property_selected(const String &p_path, int p_focusable); void _object_id_selected(const String &p_path, ObjectID p_id); + void _update_current_favorites(); + void _set_property_favorited(const String &p_path, bool p_favorited); + void _clear_current_favorites(); + void _node_removed(Node *p_node); HashMap<StringName, int> per_array_page; @@ -584,6 +616,8 @@ class EditorInspector : public ScrollContainer { void _add_meta_confirm(); void _show_add_meta_dialog(); + void _handle_menu_option(int p_option); + protected: static void _bind_methods(); void _notification(int p_what); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index eeb8ceb1ed..d41cb64c6c 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -1496,10 +1496,26 @@ void EditorSettings::set_favorites(const Vector<String> &p_favorites) { } } +void EditorSettings::set_favorite_properties(const HashMap<String, PackedStringArray> &p_favorite_properties) { + favorite_properties = p_favorite_properties; + String favorite_properties_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorite_properties"); + + Ref<ConfigFile> cf; + cf.instantiate(); + for (const KeyValue<String, PackedStringArray> &kv : p_favorite_properties) { + cf->set_value(kv.key, "properties", kv.value); + } + cf->save(favorite_properties_file); +} + Vector<String> EditorSettings::get_favorites() const { return favorites; } +HashMap<String, PackedStringArray> EditorSettings::get_favorite_properties() const { + return favorite_properties; +} + void EditorSettings::set_recent_dirs(const Vector<String> &p_recent_dirs) { recent_dirs = p_recent_dirs; String recent_dirs_file; @@ -1522,23 +1538,51 @@ Vector<String> EditorSettings::get_recent_dirs() const { void EditorSettings::load_favorites_and_recent_dirs() { String favorites_file; + String favorite_properties_file; String recent_dirs_file; if (Engine::get_singleton()->is_project_manager_hint()) { favorites_file = EditorPaths::get_singleton()->get_config_dir().path_join("favorite_dirs"); + favorite_properties_file = EditorPaths::get_singleton()->get_config_dir().path_join("favorite_properties"); recent_dirs_file = EditorPaths::get_singleton()->get_config_dir().path_join("recent_dirs"); } else { favorites_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorites"); + favorite_properties_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorite_properties"); recent_dirs_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("recent_dirs"); } + + /// File Favorites + Ref<FileAccess> f = FileAccess::open(favorites_file, FileAccess::READ); if (f.is_valid()) { String line = f->get_line().strip_edges(); while (!line.is_empty()) { - favorites.push_back(line); + favorites.append(line); line = f->get_line().strip_edges(); } } + /// Inspector Favorites + + Ref<ConfigFile> cf; + cf.instantiate(); + if (cf->load(favorite_properties_file) == OK) { + List<String> secs; + cf->get_sections(&secs); + + for (String &E : secs) { + PackedStringArray properties = PackedStringArray(cf->get_value(E, "properties")); + if (EditorNode::get_editor_data().is_type_recognized(E) || ResourceLoader::exists(E, "Script")) { + for (const String &property : properties) { + if (!favorite_properties[E].has(property)) { + favorite_properties[E].push_back(property); + } + } + } + } + } + + /// Recent Directories + f = FileAccess::open(recent_dirs_file, FileAccess::READ); if (f.is_valid()) { String line = f->get_line().strip_edges(); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index d1ccedfe6c..3c8a4de866 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -102,6 +102,7 @@ private: HashMap<String, List<Ref<InputEvent>>> builtin_action_overrides; Vector<String> favorites; + HashMap<String, PackedStringArray> favorite_properties; Vector<String> recent_dirs; bool save_changed_setting = true; @@ -176,6 +177,8 @@ public: void set_favorites(const Vector<String> &p_favorites); Vector<String> get_favorites() const; + void set_favorite_properties(const HashMap<String, PackedStringArray> &p_favorite_properties); + HashMap<String, PackedStringArray> get_favorite_properties() const; void set_recent_dirs(const Vector<String> &p_recent_dirs); Vector<String> get_recent_dirs() const; void load_favorites_and_recent_dirs(); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 9b08d21bdc..a468d71568 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1641,21 +1641,27 @@ String FileSystemDock::_get_unique_name(const FileOrFolder &p_entry, const Strin return new_path; } -void FileSystemDock::_update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const { - Vector<String> favorites_list = EditorSettings::get_singleton()->get_favorites(); - Vector<String> new_favorites; - - for (const String &old_path : favorites_list) { +void FileSystemDock::_update_favorites_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const { + Vector<String> favorite_files = EditorSettings::get_singleton()->get_favorites(); + Vector<String> new_favorite_files; + for (const String &old_path : favorite_files) { if (p_folders_renames.has(old_path)) { - new_favorites.push_back(p_folders_renames[old_path]); + new_favorite_files.push_back(p_folders_renames[old_path]); } else if (p_files_renames.has(old_path)) { - new_favorites.push_back(p_files_renames[old_path]); + new_favorite_files.push_back(p_files_renames[old_path]); } else { - new_favorites.push_back(old_path); + new_favorite_files.push_back(old_path); } } + EditorSettings::get_singleton()->set_favorites(new_favorite_files); - EditorSettings::get_singleton()->set_favorites(new_favorites); + HashMap<String, PackedStringArray> favorite_properties = EditorSettings::get_singleton()->get_favorite_properties(); + for (const KeyValue<String, String> &KV : p_files_renames) { + if (favorite_properties.has(KV.key)) { + favorite_properties.replace_key(KV.key, KV.value); + } + } + EditorSettings::get_singleton()->set_favorite_properties(favorite_properties); } void FileSystemDock::_make_scene_confirm() { @@ -1798,7 +1804,7 @@ void FileSystemDock::_rename_operation_confirm() { _update_resource_paths_after_move(file_renames, uids); _update_dependencies_after_move(file_renames, file_owners); _update_project_settings_after_move(file_renames, folder_renames); - _update_favorites_list_after_move(file_renames, folder_renames); + _update_favorites_after_move(file_renames, folder_renames); EditorSceneTabs::get_singleton()->set_current_tab(current_tab); @@ -1959,7 +1965,7 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_cop _update_resource_paths_after_move(file_renames, uids); _update_dependencies_after_move(file_renames, file_owners); _update_project_settings_after_move(file_renames, folder_renames); - _update_favorites_list_after_move(file_renames, folder_renames); + _update_favorites_after_move(file_renames, folder_renames); EditorSceneTabs::get_singleton()->set_current_tab(current_tab); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 72d5ac3a98..fe83129c07 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -275,7 +275,7 @@ private: void _before_move(HashMap<String, ResourceUID::ID> &r_uids, HashSet<String> &r_file_owners) const; void _update_dependencies_after_move(const HashMap<String, String> &p_renames, const HashSet<String> &p_file_owners) const; void _update_resource_paths_after_move(const HashMap<String, String> &p_renames, const HashMap<String, ResourceUID::ID> &p_uids) const; - void _update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const; + void _update_favorites_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const; void _update_project_settings_after_move(const HashMap<String, String> &p_renames, const HashMap<String, String> &p_folders_renames); String _get_unique_name(const FileOrFolder &p_entry, const String &p_at_path); diff --git a/editor/icons/Unfavorite.svg b/editor/icons/Unfavorite.svg new file mode 100644 index 0000000000..78f1b90fd0 --- /dev/null +++ b/editor/icons/Unfavorite.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M 8 1.6992188 L 5.6269531 5.796875 L 1 6.8945312 L 4.2363281 10.302734 L 3.8769531 14.976562 L 8.0175781 12.998047 L 12.173828 14.941406 L 11.777344 10.287109 L 15 6.8945312 L 10.373047 5.796875 L 8 1.6992188 z M 8 4.2773438 L 9.4882812 6.8457031 L 12.388672 7.5332031 L 10.369141 9.6601562 L 10.617188 12.576172 L 8.0097656 11.359375 L 5.4160156 12.599609 L 5.640625 9.6699219 L 3.6113281 7.5332031 L 6.5117188 6.8457031 L 8 4.2773438 z"/></svg>
\ No newline at end of file |