Make possible to favorite properties in the inspector
This commit is contained in:
parent
c3e16cda00
commit
f7bc7ce365
|
@ -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>
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "editor/editor_feature_profile.h"
|
||||
#include "editor/editor_main_screen.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_paths.h"
|
||||
#include "editor/editor_property_name_processor.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
|
@ -931,10 +932,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 +963,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 +1046,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 +1107,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,6 +1146,17 @@ void EditorProperty::_update_popup() {
|
|||
menu->set_item_disabled(MENU_PASTE_VALUE, is_read_only());
|
||||
menu->set_item_disabled(MENU_COPY_PROPERTY_PATH, internal);
|
||||
|
||||
if (can_favorite) {
|
||||
menu->add_separator();
|
||||
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) {
|
||||
menu->add_separator();
|
||||
if (can_pin) {
|
||||
|
@ -1267,6 +1295,15 @@ void EditorInspectorCategory::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorInspectorCategory::override_menu(PopupMenu *p_menu) {
|
||||
if (!p_menu) {
|
||||
menu_override = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
menu_override = p_menu;
|
||||
}
|
||||
|
||||
Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) const {
|
||||
// If it's not a doc tooltip, fallback to the default one.
|
||||
if (doc_class_name.is_empty()) {
|
||||
|
@ -1306,7 +1343,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 (!menu_override && doc_class_name.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1315,11 +1352,16 @@ void EditorInspectorCategory::gui_input(const Ref<InputEvent> &p_event) {
|
|||
return;
|
||||
}
|
||||
|
||||
PopupMenu *current_menu = menu_override ? menu_override : menu;
|
||||
if (current_menu == menu) {
|
||||
menu->set_item_disabled(menu->get_item_index(MENU_OPEN_DOCS), !EditorHelp::get_doc_data()->class_list.has(doc_class_name));
|
||||
} else {
|
||||
ERR_FAIL_COND(!current_menu->is_inside_tree());
|
||||
}
|
||||
|
||||
menu->set_position(get_screen_position() + mb_event->get_position());
|
||||
menu->reset_size();
|
||||
menu->popup();
|
||||
current_menu->set_position(get_screen_position() + mb_event->get_position());
|
||||
current_menu->reset_size();
|
||||
current_menu->popup();
|
||||
}
|
||||
|
||||
EditorInspectorCategory::EditorInspectorCategory() {
|
||||
|
@ -1622,6 +1664,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 +2721,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);
|
||||
|
||||
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 +2736,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 +2780,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();
|
||||
|
@ -2835,6 +2888,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;
|
||||
|
@ -2842,13 +2896,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;
|
||||
|
@ -2895,6 +2953,7 @@ void EditorInspector::update_tree() {
|
|||
subgroup = "";
|
||||
subgroup_base = "";
|
||||
section_depth = 0;
|
||||
disable_favorite = false;
|
||||
|
||||
vbox_per_path.clear();
|
||||
editor_inspector_array_per_prefix.clear();
|
||||
|
@ -2958,6 +3017,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3056,6 +3120,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("/");
|
||||
|
@ -3447,6 +3516,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);
|
||||
|
@ -3454,7 +3524,12 @@ void EditorInspector::update_tree() {
|
|||
ep->set_deletable(deletable_properties || p.name.begins_with("metadata/"));
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -3463,6 +3538,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));
|
||||
|
@ -3493,7 +3569,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();
|
||||
|
||||
|
@ -3504,6 +3580,88 @@ 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 (HashMap<String, HashMap<String, LocalVector<EditorProperty *>>>::Iterator I = favorites_to_add.begin(); I;) {
|
||||
HashMap<String, HashMap<String, LocalVector<EditorProperty *>>>::Iterator grp = I;
|
||||
++grp;
|
||||
|
||||
String section_name = I->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 (HashMap<String, LocalVector<EditorProperty *>>::Iterator J = I->value.begin(); J;) {
|
||||
HashMap<String, LocalVector<EditorProperty *>>::Iterator sgrp = J;
|
||||
++sgrp;
|
||||
|
||||
section_name = J->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 : J->value) {
|
||||
vbox->add_child(ep);
|
||||
}
|
||||
|
||||
J = sgrp;
|
||||
}
|
||||
|
||||
I = grp;
|
||||
}
|
||||
|
||||
// Add a separator if there's no category to clearly divide the properties.
|
||||
if (main_vbox->get_child_count() > 0) {
|
||||
EditorInspectorCategory *category = Object::cast_to<EditorInspectorCategory>(main_vbox->get_child(0));
|
||||
if (!category) {
|
||||
favorites_section->add_child(memnew(HSeparator));
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
@ -3545,6 +3703,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));
|
||||
}
|
||||
|
@ -3591,6 +3762,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();
|
||||
}
|
||||
|
||||
|
@ -4086,10 +4261,182 @@ void EditorInspector::_node_removed(Node *p_node) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorInspector::_update_current_favorites() {
|
||||
current_favorites.clear();
|
||||
if (!can_favorite) {
|
||||
return;
|
||||
}
|
||||
|
||||
HashMap<StringName, 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 (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
|
||||
PropertyInfo &p = E->get();
|
||||
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 (HashMap<String, LocalVector<String>>::Iterator I = props.begin(); I;) {
|
||||
HashMap<String, LocalVector<String>>::Iterator next = I;
|
||||
++next;
|
||||
|
||||
path = I->key;
|
||||
for (int i = 0; i < favorites[path].size(); i++) {
|
||||
String prop = favorites[path][i];
|
||||
if (I->value.has(prop)) {
|
||||
current_favorites.append(prop);
|
||||
} else {
|
||||
invalid_props = true;
|
||||
favorites[path].erase(prop);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (favorites[path].is_empty()) {
|
||||
favorites.erase(path);
|
||||
}
|
||||
|
||||
I = next;
|
||||
}
|
||||
|
||||
if (invalid_props) {
|
||||
EditorSettings::get_singleton()->set_favorite_properties(favorites);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch built-in properties.
|
||||
StringName class_name = object->get_class_name();
|
||||
for (HashMap<StringName, PackedStringArray>::Iterator I = favorites.begin(); I;) {
|
||||
HashMap<StringName, PackedStringArray>::Iterator next = I;
|
||||
++next;
|
||||
|
||||
if (ClassDB::is_parent_class(class_name, I->key)) {
|
||||
current_favorites.append_array(I->value);
|
||||
}
|
||||
|
||||
I = next;
|
||||
}
|
||||
}
|
||||
|
||||
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 (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
|
||||
PropertyInfo &p = E->get();
|
||||
if (p.usage & PROPERTY_USAGE_CATEGORY) {
|
||||
path = p.hint_string;
|
||||
} else if (p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE) {
|
||||
if (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<StringName, 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<StringName, 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 (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
|
||||
PropertyInfo &p = E->get();
|
||||
if (p.usage & PROPERTY_USAGE_CATEGORY) {
|
||||
if (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"));
|
||||
favorites_popup->set_item_icon(favorites_popup->get_item_index(MENU_UNFAVORITE_ALL), get_editor_theme_icon(SNAME("Unfavorite")));
|
||||
|
||||
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: {
|
||||
|
@ -4187,6 +4534,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());
|
||||
}
|
||||
}
|
||||
|
@ -4276,6 +4624,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);
|
||||
|
@ -4294,9 +4650,37 @@ 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_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_popup = memnew(PopupMenu);
|
||||
favorites_popup->add_item(TTR("Unfavorite All"), MENU_UNFAVORITE_ALL);
|
||||
favorites_popup->connect(SceneStringName(id_pressed), callable_mp(this, &EditorInspector::_handle_menu_option));
|
||||
favorites_category->add_child(favorites_popup);
|
||||
favorites_category->override_menu(favorites_popup);
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ public:
|
|||
MENU_COPY_VALUE,
|
||||
MENU_PASTE_VALUE,
|
||||
MENU_COPY_PROPERTY_PATH,
|
||||
MENU_FAVORITE_PROPERTY,
|
||||
MENU_PIN_VALUE,
|
||||
MENU_OPEN_DOCUMENTATION,
|
||||
};
|
||||
|
@ -112,6 +113,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 +138,7 @@ private:
|
|||
GDVIRTUAL0(_update_property)
|
||||
GDVIRTUAL1(_set_read_only, bool)
|
||||
|
||||
void _update_pin_flags();
|
||||
void _update_flags();
|
||||
|
||||
protected:
|
||||
bool has_borders = false;
|
||||
|
@ -218,6 +222,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 +292,7 @@ class EditorInspectorCategory : public Control {
|
|||
String label;
|
||||
String doc_class_name;
|
||||
PopupMenu *menu = nullptr;
|
||||
PopupMenu *menu_override = nullptr;
|
||||
|
||||
void _handle_menu_option(int p_option);
|
||||
|
||||
|
@ -293,6 +301,8 @@ protected:
|
|||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
public:
|
||||
void override_menu(PopupMenu *p_menu);
|
||||
|
||||
virtual Size2 get_minimum_size() const override;
|
||||
virtual Control *make_custom_tooltip(const String &p_text) const override;
|
||||
|
||||
|
@ -331,6 +341,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();
|
||||
|
@ -486,6 +497,21 @@ class EditorInspector : public ScrollContainer {
|
|||
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;
|
||||
PopupMenu *favorites_popup = nullptr;
|
||||
|
||||
VBoxContainer *base_vbox = nullptr;
|
||||
VBoxContainer *begin_vbox = nullptr;
|
||||
VBoxContainer *main_vbox = nullptr;
|
||||
|
||||
// Map used to cache the instantiated editors.
|
||||
|
@ -557,6 +583,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 +614,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);
|
||||
|
|
|
@ -1489,10 +1489,35 @@ void EditorSettings::set_favorites(const Vector<String> &p_favorites) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorSettings::set_favorite_properties(const HashMap<StringName, PackedStringArray> p_favorite_properties) {
|
||||
favorite_properties = p_favorite_properties;
|
||||
String favorite_properties_file;
|
||||
if (Engine::get_singleton()->is_project_manager_hint()) {
|
||||
favorite_properties_file = EditorPaths::get_singleton()->get_config_dir().path_join("favorite_properties");
|
||||
} else {
|
||||
favorite_properties_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorite_properties");
|
||||
}
|
||||
|
||||
Ref<ConfigFile> cf;
|
||||
cf.instantiate();
|
||||
for (HashMap<StringName, PackedStringArray>::ConstIterator I = p_favorite_properties.begin(); I;) {
|
||||
HashMap<StringName, PackedStringArray>::ConstIterator next = I;
|
||||
++next;
|
||||
|
||||
cf->set_value(I->key, "properties", I->value);
|
||||
I = next;
|
||||
}
|
||||
cf->save(favorite_properties_file);
|
||||
}
|
||||
|
||||
Vector<String> EditorSettings::get_favorites() const {
|
||||
return favorites;
|
||||
}
|
||||
|
||||
HashMap<StringName, 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;
|
||||
|
@ -1515,23 +1540,59 @@ 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)) {
|
||||
for (int i = 0; i < properties.size(); i++) {
|
||||
if (ClassDB::has_property(E, properties[i], true), !favorite_properties.has(properties[i])) {
|
||||
favorite_properties[E].push_back(properties[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ResourceLoader::exists(E, "Script")) {
|
||||
for (int i = 0; i < properties.size(); i++) {
|
||||
if (!favorite_properties.has(properties[i])) {
|
||||
favorite_properties[E].push_back(properties[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recent Directories
|
||||
|
||||
f = FileAccess::open(recent_dirs_file, FileAccess::READ);
|
||||
if (f.is_valid()) {
|
||||
String line = f->get_line().strip_edges();
|
||||
|
|
|
@ -95,6 +95,7 @@ private:
|
|||
HashMap<String, List<Ref<InputEvent>>> builtin_action_overrides;
|
||||
|
||||
Vector<String> favorites;
|
||||
HashMap<StringName, PackedStringArray> favorite_properties;
|
||||
Vector<String> recent_dirs;
|
||||
|
||||
bool save_changed_setting = true;
|
||||
|
@ -169,6 +170,8 @@ public:
|
|||
|
||||
void set_favorites(const Vector<String> &p_favorites);
|
||||
Vector<String> get_favorites() const;
|
||||
void set_favorite_properties(const HashMap<StringName, PackedStringArray> p_favorite_properties);
|
||||
HashMap<StringName, 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();
|
||||
|
|
|
@ -1734,21 +1734,33 @@ 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);
|
||||
|
||||
HashMap<StringName, PackedStringArray> favorite_properties = EditorSettings::get_singleton()->get_favorite_properties();
|
||||
for (HashMap<StringName, PackedStringArray>::Iterator I = favorite_properties.begin(); I;) {
|
||||
HashMap<StringName, PackedStringArray>::Iterator next = I;
|
||||
++next;
|
||||
|
||||
String old_path = I->key;
|
||||
if (p_files_renames.has(old_path)) {
|
||||
favorite_properties.replace_key(old_path, p_files_renames[old_path]);
|
||||
}
|
||||
|
||||
EditorSettings::get_singleton()->set_favorites(new_favorites);
|
||||
I = next;
|
||||
}
|
||||
EditorSettings::get_singleton()->set_favorite_properties(favorite_properties);
|
||||
}
|
||||
|
||||
void FileSystemDock::_make_scene_confirm() {
|
||||
|
@ -1891,7 +1903,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);
|
||||
|
||||
|
@ -2075,7 +2087,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);
|
||||
|
||||
|
|
|
@ -287,7 +287,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);
|
||||
|
||||
|
|
|
@ -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>
|
After Width: | Height: | Size: 533 B |
Loading…
Reference in New Issue