diff options
Diffstat (limited to 'editor/editor_inspector.cpp')
-rw-r--r-- | editor/editor_inspector.cpp | 494 |
1 files changed, 308 insertions, 186 deletions
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 675ef808e1..0f31e3e7bb 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -36,17 +36,33 @@ #include "editor/doc_tools.h" #include "editor/editor_feature_profile.h" #include "editor/editor_node.h" +#include "editor/editor_property_name_processor.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "multi_node_edit.h" +#include "scene/gui/center_container.h" #include "scene/property_utils.h" #include "scene/resources/packed_scene.h" +static bool _property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) { + if (p_property_path.findn(p_filter) != -1) { + return true; + } + + const Vector<String> sections = p_property_path.split("/"); + for (int i = 0; i < sections.size(); i++) { + if (p_filter.is_subsequence_ofn(EditorPropertyNameProcessor::get_singleton()->process_name(sections[i], p_style))) { + return true; + } + } + return false; +} + Size2 EditorProperty::get_minimum_size() const { Size2 ms; Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Tree")); int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Tree")); - ms.height = font->get_height(font_size); + ms.height = font->get_height(font_size) + 4 * EDSCALE; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -80,11 +96,11 @@ Size2 EditorProperty::get_minimum_size() const { if (checkable) { Ref<Texture2D> check = get_theme_icon(SNAME("checked"), SNAME("CheckBox")); - ms.width += check->get_width() + get_theme_constant(SNAME("hseparation"), SNAME("CheckBox")) + get_theme_constant(SNAME("hseparator"), SNAME("Tree")); + ms.width += check->get_width() + get_theme_constant(SNAME("h_separation"), SNAME("CheckBox")) + get_theme_constant(SNAME("hseparator"), SNAME("Tree")); } if (bottom_editor != nullptr && bottom_editor->is_visible()) { - ms.height += get_theme_constant(SNAME("vseparation")); + ms.height += get_theme_constant(SNAME("v_separation")); Size2 bems = bottom_editor->get_combined_minimum_size(); //bems.width += get_constant("item_margin", "Tree"); ms.height += bems.height; @@ -99,7 +115,7 @@ void EditorProperty::emit_changed(const StringName &p_property, const Variant &p const Variant *argptrs[4] = { &args[0], &args[1], &args[2], &args[3] }; cache[p_property] = p_value; - emit_signal(SNAME("property_changed"), (const Variant **)argptrs, 4); + emit_signalp(SNAME("property_changed"), (const Variant **)argptrs, 4); } void EditorProperty::_notification(int p_what) { @@ -116,7 +132,7 @@ void EditorProperty::_notification(int p_what) { int child_room = size.width * (1.0 - split_ratio); Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Tree")); int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Tree")); - int height = font->get_height(font_size); + int height = font->get_height(font_size) + 4 * EDSCALE; bool no_children = true; //compute room needed @@ -153,7 +169,7 @@ void EditorProperty::_notification(int p_what) { if (bottom_editor) { int m = 0; //get_constant("item_margin", "Tree"); - bottom_rect = Rect2(m, rect.size.height + get_theme_constant(SNAME("vseparation")), size.width - m, bottom_editor->get_combined_minimum_size().height); + bottom_rect = Rect2(m, rect.size.height + get_theme_constant(SNAME("v_separation")), size.width - m, bottom_editor->get_combined_minimum_size().height); } if (keying) { @@ -220,30 +236,24 @@ void EditorProperty::_notification(int p_what) { case NOTIFICATION_DRAW: { Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Tree")); int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Tree")); - Color dark_color = get_theme_color(SNAME("dark_color_2"), SNAME("Editor")); bool rtl = is_layout_rtl(); Size2 size = get_size(); if (bottom_editor) { - size.height = bottom_editor->get_offset(SIDE_TOP); + size.height = bottom_editor->get_offset(SIDE_TOP) - get_theme_constant(SNAME("v_separation")); } else if (label_reference) { size.height = label_reference->get_size().height; } - Ref<StyleBox> sb; - if (selected) { - sb = get_theme_stylebox(SNAME("bg_selected")); - } else { - sb = get_theme_stylebox(SNAME("bg")); - } - + Ref<StyleBox> sb = get_theme_stylebox(selected ? SNAME("bg_selected") : SNAME("bg")); draw_style_box(sb, Rect2(Vector2(), size)); + Ref<StyleBox> bg_stylebox = get_theme_stylebox(SNAME("child_bg")); if (draw_top_bg && right_child_rect != Rect2()) { - draw_rect(right_child_rect, dark_color); + draw_style_box(bg_stylebox, right_child_rect); } if (bottom_child_rect != Rect2()) { - draw_rect(bottom_child_rect, dark_color); + draw_style_box(bg_stylebox, bottom_child_rect); } Color color; @@ -281,7 +291,7 @@ void EditorProperty::_notification(int p_what) { } else { draw_texture(checkbox, check_rect.position, color2); } - int check_ofs = get_theme_constant(SNAME("hseparator"), SNAME("Tree")) + checkbox->get_width() + get_theme_constant(SNAME("hseparation"), SNAME("CheckBox")); + int check_ofs = get_theme_constant(SNAME("hseparator"), SNAME("Tree")) + checkbox->get_width() + get_theme_constant(SNAME("h_separation"), SNAME("CheckBox")); ofs += check_ofs; text_limit -= check_ofs; } else { @@ -669,7 +679,7 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) { } } -void EditorProperty::unhandled_key_input(const Ref<InputEvent> &p_event) { +void EditorProperty::shortcut_input(const Ref<InputEvent> &p_event) { if (!selected || !p_event->is_pressed()) { return; } @@ -691,15 +701,11 @@ void EditorProperty::unhandled_key_input(const Ref<InputEvent> &p_event) { } const Color *EditorProperty::_get_property_colors() { - const Color base = get_theme_color(SNAME("accent_color"), SNAME("Editor")); - const float saturation = base.get_s() * 0.75; - const float value = base.get_v(); - static Color c[4]; - c[0].set_hsv(0.0 / 3.0 + 0.05, saturation, value); - c[1].set_hsv(1.0 / 3.0 + 0.05, saturation, value); - c[2].set_hsv(2.0 / 3.0 + 0.05, saturation, value); - c[3].set_hsv(1.5 / 3.0 + 0.05, saturation, value); + c[0] = get_theme_color(SNAME("property_color_x"), SNAME("Editor")); + c[1] = get_theme_color(SNAME("property_color_y"), SNAME("Editor")); + c[2] = get_theme_color(SNAME("property_color_z"), SNAME("Editor")); + c[3] = get_theme_color(SNAME("property_color_w"), SNAME("Editor")); return c; } @@ -827,7 +833,7 @@ void EditorProperty::_update_pin_flags() { } pin_hidden = false; { - Set<StringName> storable_properties; + HashSet<StringName> storable_properties; node->get_storable_properties(storable_properties); if (storable_properties.has(node->get_property_store_alias(property))) { can_pin = true; @@ -885,7 +891,7 @@ void EditorProperty::menu_option(int p_option) { emit_changed(property, InspectorDock::get_inspector_singleton()->get_property_clipboard()); } break; case MENU_COPY_PROPERTY_PATH: { - DisplayServer::get_singleton()->clipboard_set(property); + DisplayServer::get_singleton()->clipboard_set(property_path); } break; case MENU_PIN_VALUE: { emit_signal(SNAME("property_pinned"), property, !pinned); @@ -951,33 +957,15 @@ void EditorProperty::_bind_methods() { } EditorProperty::EditorProperty() { - draw_top_bg = true; object = nullptr; split_ratio = 0.5; - selectable = true; text_size = 0; - read_only = false; - checkable = false; - checked = false; - draw_warning = false; - keying = false; - deletable = false; - keying_hover = false; - revert_hover = false; - check_hover = false; - can_revert = false; - can_pin = false; - pin_hidden = false; - pinned = false; - use_folding = false; property_usage = 0; - selected = false; selected_focusable = -1; label_reference = nullptr; bottom_editor = nullptr; - delete_hover = false; menu = nullptr; - set_process_unhandled_key_input(true); + set_process_shortcut_input(true); } void EditorProperty::_update_popup() { @@ -1014,12 +1002,11 @@ void EditorInspectorPlugin::add_custom_control(Control *control) { added_editors.push_back(ae); } -void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop) { - ERR_FAIL_COND(Object::cast_to<EditorProperty>(p_prop) == nullptr); - +void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end) { AddedEditor ae; ae.properties.push_back(p_for_property); ae.property_editor = p_prop; + ae.add_to_end = p_add_to_end; added_editors.push_back(ae); } @@ -1065,7 +1052,7 @@ void EditorInspectorPlugin::parse_end(Object *p_object) { void EditorInspectorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("add_custom_control", "control"), &EditorInspectorPlugin::add_custom_control); - ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor"), &EditorInspectorPlugin::add_property_editor); + ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor", "add_to_end"), &EditorInspectorPlugin::add_property_editor, DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_property_editor_for_multiple_properties", "label", "properties", "editor"), &EditorInspectorPlugin::add_property_editor_for_multiple_properties); GDVIRTUAL_BIND(_can_handle, "object") @@ -1082,14 +1069,14 @@ void EditorInspectorPlugin::_bind_methods() { void EditorInspectorCategory::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { - Ref<StyleBox> sb = get_theme_stylebox(SNAME("prop_category_style"), SNAME("Editor")); + Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); draw_style_box(sb, Rect2(Vector2(), get_size())); Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); - int hs = get_theme_constant(SNAME("hseparation"), SNAME("Tree")); + int hs = get_theme_constant(SNAME("h_separation"), SNAME("Tree")); int w = font->get_string_size(label, font_size).width; if (icon.is_valid()) { @@ -1124,7 +1111,7 @@ Size2 EditorInspectorCategory::get_minimum_size() const { if (icon.is_valid()) { ms.height = MAX(icon->get_height(), ms.height); } - ms.height += get_theme_constant(SNAME("vseparation"), SNAME("Tree")); + ms.height += get_theme_constant(SNAME("v_separation"), SNAME("Tree")); return ms; } @@ -1184,7 +1171,7 @@ void EditorInspectorSection::_notification(int p_what) { if (arrow.is_valid()) { header_height = MAX(header_height, arrow->get_height()); } - header_height += get_theme_constant(SNAME("vseparation"), SNAME("Tree")); + header_height += get_theme_constant(SNAME("v_separation"), SNAME("Tree")); int inspector_margin = get_theme_constant(SNAME("inspector_margin"), SNAME("Editor")); int section_indent_size = get_theme_constant(SNAME("indent_size"), SNAME("EditorInspectorSection")); @@ -1237,7 +1224,7 @@ void EditorInspectorSection::_notification(int p_what) { if (arrow.is_valid()) { header_height = MAX(header_height, arrow->get_height()); } - header_height += get_theme_constant(SNAME("vseparation"), SNAME("Tree")); + header_height += get_theme_constant(SNAME("v_separation"), SNAME("Tree")); int section_indent = 0; int section_indent_size = get_theme_constant(SNAME("indent_size"), SNAME("EditorInspectorSection")); @@ -1366,7 +1353,7 @@ Size2 EditorInspectorSection::get_minimum_size() const { Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Tree")); int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Tree")); - ms.height += font->get_height(font_size) + get_theme_constant(SNAME("vseparation"), SNAME("Tree")); + ms.height += font->get_height(font_size) + get_theme_constant(SNAME("v_separation"), SNAME("Tree")); ms.width += get_theme_constant(SNAME("inspector_margin"), SNAME("Editor")); int section_indent_size = get_theme_constant(SNAME("indent_size"), SNAME("EditorInspectorSection")); @@ -1945,6 +1932,7 @@ void EditorInspectorArray::_setup() { // Move button. ae.move_texture_rect = memnew(TextureRect); ae.move_texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + ae.move_texture_rect->set_default_cursor_shape(Control::CURSOR_MOVE); if (is_inside_tree()) { ae.move_texture_rect->set_texture(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons"))); } @@ -2118,9 +2106,7 @@ EditorInspectorArray::EditorInspectorArray() { elements_vbox->add_theme_constant_override("separation", 0); vbox->add_child(elements_vbox); - add_button = memnew(Button); - add_button->set_text(TTR("Add Element")); - add_button->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER); + add_button = EditorInspector::create_inspector_action_button(TTR("Add Element")); add_button->connect("pressed", callable_mp(this, &EditorInspectorArray::_add_button_pressed)); vbox->add_child(add_button); @@ -2305,6 +2291,14 @@ void EditorInspector::cleanup_plugins() { inspector_plugin_count = 0; } +Button *EditorInspector::create_inspector_action_button(const String &p_text) { + Button *button = memnew(Button); + button->set_text(p_text); + button->set_theme_type_variation(SNAME("InspectorActionButton")); + button->set_h_size_flags(SIZE_SHRINK_CENTER); + return button; +} + void EditorInspector::set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } @@ -2335,6 +2329,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<Edit if (F.properties.size() == 1) { //since it's one, associate: ep->property = F.properties[0]; + ep->property_path = property_prefix + F.properties[0]; ep->property_usage = 0; } @@ -2450,8 +2445,8 @@ void EditorInspector::update_tree() { object->get_property_list(&plist, true); _update_script_class_properties(*object, plist); - Map<VBoxContainer *, HashMap<String, VBoxContainer *>> vbox_per_path; - Map<String, EditorInspectorArray *> editor_inspector_array_per_prefix; + HashMap<VBoxContainer *, HashMap<String, VBoxContainer *>> vbox_per_path; + HashMap<String, EditorInspectorArray *> editor_inspector_array_per_prefix; Color sscolor = get_theme_color(SNAME("prop_subsection"), SNAME("Editor")); @@ -2512,7 +2507,7 @@ void EditorInspector::update_tree() { List<PropertyInfo>::Element *N = E_property->next(); bool valid = true; while (N) { - if (N->get().usage & PROPERTY_USAGE_EDITOR && (!restrict_to_basic || (N->get().usage & PROPERTY_USAGE_EDITOR_BASIC_SETTING))) { + if (!N->get().name.begins_with("metadata/_") && N->get().usage & PROPERTY_USAGE_EDITOR && (!restrict_to_basic || (N->get().usage & PROPERTY_USAGE_EDITOR_BASIC_SETTING))) { break; } if (N->get().usage & PROPERTY_USAGE_CATEGORY) { @@ -2568,9 +2563,9 @@ void EditorInspector::update_tree() { if (!class_descr_cache.has(type2)) { String descr; DocTools *dd = EditorHelp::get_doc_data(); - Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(type2); + HashMap<String, DocData::ClassDoc>::Iterator E = dd->class_list.find(type2); if (E) { - descr = DTR(E->get().brief_description); + descr = DTR(E->value.brief_description); } class_descr_cache[type2] = descr; } @@ -2586,7 +2581,7 @@ void EditorInspector::update_tree() { continue; - } else if (!(p.usage & PROPERTY_USAGE_EDITOR) || _is_property_disabled_by_feature_profile(p.name) || (restrict_to_basic && !(p.usage & PROPERTY_USAGE_EDITOR_BASIC_SETTING))) { + } else if (p.name.begins_with("metadata/_") || !(p.usage & PROPERTY_USAGE_EDITOR) || _is_property_disabled_by_feature_profile(p.name) || (restrict_to_basic && !(p.usage & PROPERTY_USAGE_EDITOR_BASIC_SETTING))) { // Ignore properties that are not supposed to be in the inspector. continue; } @@ -2612,9 +2607,9 @@ void EditorInspector::update_tree() { // First check if we have an array that fits the prefix. String array_prefix = ""; int array_index = -1; - for (Map<String, EditorInspectorArray *>::Element *E = editor_inspector_array_per_prefix.front(); E; E = E->next()) { - if (p.name.begins_with(E->key()) && E->key().length() > array_prefix.length()) { - array_prefix = E->key(); + for (KeyValue<String, EditorInspectorArray *> &E : editor_inspector_array_per_prefix) { + if (p.name.begins_with(E.key) && E.key.length() > array_prefix.length()) { + array_prefix = E.key; } } @@ -2677,20 +2672,23 @@ void EditorInspector::update_tree() { } // Get the property label's string. - String property_label_string = (path.contains("/")) ? path.substr(path.rfind("/") + 1) : path; - if (capitalize_paths) { - // Capitalize paths. - int dot = property_label_string.find("."); + String name_override = (path.contains("/")) ? path.substr(path.rfind("/") + 1) : path; + String feature_tag; + { + const int dot = name_override.find("."); if (dot != -1) { - String ov = property_label_string.substr(dot); - property_label_string = property_label_string.substr(0, dot); - property_label_string = property_label_string.capitalize(); - property_label_string += ov; - } else { - property_label_string = property_label_string.capitalize(); + feature_tag = name_override.substr(dot); + name_override = name_override.substr(0, dot); } } + // Don't localize script variables. + EditorPropertyNameProcessor::Style name_style = property_name_style; + if ((p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE) && name_style == EditorPropertyNameProcessor::STYLE_LOCALIZED) { + name_style = EditorPropertyNameProcessor::STYLE_CAPITALIZED; + } + const String property_label_string = EditorPropertyNameProcessor::get_singleton()->process_name(name_override, name_style) + feature_tag; + // Remove the property from the path. int idx = path.rfind("/"); if (idx > -1) { @@ -2701,7 +2699,8 @@ void EditorInspector::update_tree() { // Ignore properties that do not fit the filter. if (use_filter && !filter.is_empty()) { - if (!filter.is_subsequence_ofn(path) && !filter.is_subsequence_ofn(property_label_string) && !property_prefix.to_lower().contains(filter.to_lower())) { + const String property_path = property_prefix + (path.is_empty() ? "" : path + "/") + name_override; + if (!_property_path_matches(property_path, filter, property_name_style)) { continue; } } @@ -2738,13 +2737,33 @@ void EditorInspector::update_tree() { current_vbox->add_child(section); sections.push_back(section); - if (capitalize_paths) { - component = component.capitalize(); + String label; + String tooltip; + + // Don't localize groups for script variables. + EditorPropertyNameProcessor::Style section_name_style = property_name_style; + if ((p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE) && section_name_style == EditorPropertyNameProcessor::STYLE_LOCALIZED) { + section_name_style = EditorPropertyNameProcessor::STYLE_CAPITALIZED; + } + + // Only process group label if this is not the group or subgroup. + if ((i == 0 && component == group) || (i == 1 && component == subgroup)) { + if (section_name_style == EditorPropertyNameProcessor::STYLE_LOCALIZED) { + label = TTRGET(component); + tooltip = component; + } else { + label = component; + tooltip = TTRGET(component); + } + } else { + label = EditorPropertyNameProcessor::get_singleton()->process_name(component, section_name_style); + tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(component, EditorPropertyNameProcessor::get_tooltip_style(section_name_style)); } Color c = sscolor; c.a /= level; - section->setup(acc_path, component, object, c, use_folding, section_depth); + section->setup(acc_path, label, object, c, use_folding, section_depth); + section->set_tooltip(tooltip); // Add editors at the start of a group. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { @@ -2776,7 +2795,7 @@ void EditorInspector::update_tree() { editor_inspector_array = memnew(EditorInspectorArray); String array_label = path.contains("/") ? path.substr(path.rfind("/") + 1) : path; - array_label = property_label_string.capitalize(); + array_label = EditorPropertyNameProcessor::get_singleton()->process_name(property_label_string, property_name_style); int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0; editor_inspector_array->setup_with_move_element_function(object, array_label, array_element_prefix, page, c, use_folding); editor_inspector_array->connect("page_change_request", callable_mp(this, &EditorInspector::_page_change_request), varray(array_element_prefix)); @@ -2832,39 +2851,39 @@ void EditorInspector::update_tree() { bool found = false; // Search for the property description in the cache. - Map<StringName, Map<StringName, String>>::Element *E = descr_cache.find(classname); + HashMap<StringName, HashMap<StringName, String>>::Iterator E = descr_cache.find(classname); if (E) { - Map<StringName, String>::Element *F = E->get().find(propname); + HashMap<StringName, String>::Iterator F = E->value.find(propname); if (F) { found = true; - descr = F->get(); + descr = F->value; } } if (!found) { // Build the property description String and add it to the cache. DocTools *dd = EditorHelp::get_doc_data(); - Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(classname); + HashMap<String, DocData::ClassDoc>::Iterator F = dd->class_list.find(classname); while (F && descr.is_empty()) { - for (int i = 0; i < F->get().properties.size(); i++) { - if (F->get().properties[i].name == propname.operator String()) { - descr = DTR(F->get().properties[i].description); + for (int i = 0; i < F->value.properties.size(); i++) { + if (F->value.properties[i].name == propname.operator String()) { + descr = DTR(F->value.properties[i].description); break; } } Vector<String> slices = propname.operator String().split("/"); if (slices.size() == 2 && slices[0].begins_with("theme_override_")) { - for (int i = 0; i < F->get().theme_properties.size(); i++) { - if (F->get().theme_properties[i].name == slices[1]) { - descr = DTR(F->get().theme_properties[i].description); + for (int i = 0; i < F->value.theme_properties.size(); i++) { + if (F->value.theme_properties[i].name == slices[1]) { + descr = DTR(F->value.theme_properties[i].description); break; } } } - if (!F->get().inherits.is_empty()) { - F = dd->class_list.find(F->get().inherits); + if (!F->value.inherits.is_empty()) { + F = dd->class_list.find(F->value.inherits); } else { break; } @@ -2875,90 +2894,110 @@ void EditorInspector::update_tree() { doc_hint = descr; } + Vector<EditorInspectorPlugin::AddedEditor> editors; + Vector<EditorInspectorPlugin::AddedEditor> late_editors; + // Search for the inspector plugin that will handle the properties. Then add the correct property editor to it. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { bool exclusive = ped->parse_property(object, p.type, p.name, p.hint, p.hint_string, p.usage, wide_editors); - List<EditorInspectorPlugin::AddedEditor> editors = ped->added_editors; // Make a copy, since plugins may be used again in a sub-inspector. + for (const EditorInspectorPlugin::AddedEditor &F : ped->added_editors) { + if (F.add_to_end) { + late_editors.push_back(F); + } else { + editors.push_back(F); + } + } + ped->added_editors.clear(); - for (const EditorInspectorPlugin::AddedEditor &F : editors) { - EditorProperty *ep = Object::cast_to<EditorProperty>(F.property_editor); + if (exclusive) { + break; + } + } - if (ep) { - // Set all this before the control gets the ENTER_TREE notification. - ep->object = object; + editors.append_array(late_editors); - if (F.properties.size()) { - if (F.properties.size() == 1) { - //since it's one, associate: - ep->property = F.properties[0]; - ep->property_usage = p.usage; - //and set label? - } + for (int i = 0; i < editors.size(); i++) { + EditorProperty *ep = Object::cast_to<EditorProperty>(editors[i].property_editor); + const Vector<String> &properties = editors[i].properties; - if (!F.label.is_empty()) { - ep->set_label(F.label); - } else { - // Use the existing one. - ep->set_label(property_label_string); - } - for (int i = 0; i < F.properties.size(); i++) { - String prop = F.properties[i]; + if (ep) { + // Set all this before the control gets the ENTER_TREE notification. + ep->object = object; - if (!editor_property_map.has(prop)) { - editor_property_map[prop] = List<EditorProperty *>(); - } - editor_property_map[prop].push_back(ep); - } + if (properties.size()) { + if (properties.size() == 1) { + //since it's one, associate: + ep->property = properties[0]; + ep->property_path = property_prefix + properties[0]; + ep->property_usage = p.usage; + //and set label? } - ep->set_draw_warning(draw_warning); - ep->set_use_folding(use_folding); - ep->set_checkable(checkable); - ep->set_checked(checked); - ep->set_keying(keying); - ep->set_read_only(property_read_only); - ep->set_deletable(deletable_properties); - } - - current_vbox->add_child(F.property_editor); - - if (ep) { - // Eventually, set other properties/signals after the property editor got added to the tree. - bool update_all = (p.usage & PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED); - ep->connect("property_changed", callable_mp(this, &EditorInspector::_property_changed), varray(update_all)); - ep->connect("property_keyed", callable_mp(this, &EditorInspector::_property_keyed)); - ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), varray(), 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_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)); - ep->connect("resource_selected", callable_mp(this, &EditorInspector::_resource_selected), varray(), CONNECT_DEFERRED); - ep->connect("object_id_selected", callable_mp(this, &EditorInspector::_object_id_selected), varray(), CONNECT_DEFERRED); - if (!doc_hint.is_empty()) { - ep->set_tooltip(property_prefix + p.name + "::" + doc_hint); + + if (!editors[i].label.is_empty()) { + ep->set_label(editors[i].label); } else { - ep->set_tooltip(property_prefix + p.name); + // Use the existing one. + ep->set_label(property_label_string); } - ep->update_property(); - ep->_update_pin_flags(); - ep->update_revert_and_pin_status(); - ep->update_cache(); + for (int j = 0; j < properties.size(); j++) { + String prop = properties[j]; - if (current_selected && ep->property == current_selected) { - ep->select(current_focusable); + if (!editor_property_map.has(prop)) { + editor_property_map[prop] = List<EditorProperty *>(); + } + editor_property_map[prop].push_back(ep); } } - } + ep->set_draw_warning(draw_warning); + ep->set_use_folding(use_folding); + ep->set_checkable(checkable); + ep->set_checked(checked); + ep->set_keying(keying); + ep->set_read_only(property_read_only); + ep->set_deletable(deletable_properties || p.name.begins_with("metadata/")); + } + + current_vbox->add_child(editors[i].property_editor); + + if (ep) { + // Eventually, set other properties/signals after the property editor got added to the tree. + bool update_all = (p.usage & PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED); + ep->connect("property_changed", callable_mp(this, &EditorInspector::_property_changed), varray(update_all)); + ep->connect("property_keyed", callable_mp(this, &EditorInspector::_property_keyed)); + ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), varray(), 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_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)); + ep->connect("resource_selected", callable_mp(this, &EditorInspector::_resource_selected), varray(), CONNECT_DEFERRED); + ep->connect("object_id_selected", callable_mp(this, &EditorInspector::_object_id_selected), varray(), CONNECT_DEFERRED); + if (!doc_hint.is_empty()) { + ep->set_tooltip(property_prefix + p.name + "::" + doc_hint); + } else { + ep->set_tooltip(property_prefix + p.name); + } + ep->update_property(); + ep->_update_pin_flags(); + ep->update_revert_and_pin_status(); + ep->update_cache(); - if (exclusive) { - // If we know the plugin is exclusive, we don't need to go through other plugins. - break; + if (current_selected && ep->property == current_selected) { + ep->select(current_focusable); + } } } } + if (!hide_metadata) { + Button *add_md = EditorInspector::create_inspector_action_button(TTR("Add Metadata")); + add_md->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); + add_md->connect(SNAME("pressed"), callable_mp(this, &EditorInspector::_show_add_meta_dialog)); + main_vbox->add_child(add_md); + } + // Get the lists of to add at the end. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { ped->parse_end(object); @@ -3030,12 +3069,15 @@ void EditorInspector::set_read_only(bool p_read_only) { update_tree(); } -bool EditorInspector::is_capitalize_paths_enabled() const { - return capitalize_paths; +EditorPropertyNameProcessor::Style EditorInspector::get_property_name_style() const { + return property_name_style; } -void EditorInspector::set_enable_capitalize_paths(bool p_capitalize) { - capitalize_paths = p_capitalize; +void EditorInspector::set_property_name_style(EditorPropertyNameProcessor::Style p_style) { + if (property_name_style == p_style) { + return; + } + property_name_style = p_style; update_tree(); } @@ -3058,6 +3100,11 @@ void EditorInspector::set_hide_script(bool p_hide) { update_tree(); } +void EditorInspector::set_hide_metadata(bool p_hide) { + hide_metadata = p_hide; + update_tree(); +} + void EditorInspector::set_use_filter(bool p_use) { use_filter = p_use; update_tree(); @@ -3318,7 +3365,7 @@ void EditorInspector::_property_keyed(const String &p_path, bool p_advance) { // The second parameter could be null, causing the event to fire with less arguments, so use the pointer call which preserves it. const Variant args[3] = { p_path, object->get(p_path), p_advance }; const Variant *argp[3] = { &args[0], &args[1], &args[2] }; - emit_signal(SNAME("property_keyed"), argp, 3); + emit_signalp(SNAME("property_keyed"), argp, 3); } void EditorInspector::_property_deleted(const String &p_path) { @@ -3326,6 +3373,14 @@ void EditorInspector::_property_deleted(const String &p_path) { return; } + if (p_path.begins_with("metadata/")) { + String name = p_path.replace_first("metadata/", ""); + undo_redo->create_action(vformat(TTR("Remove metadata %s"), name)); + undo_redo->add_do_method(object, "remove_meta", name); + undo_redo->add_undo_method(object, "set_meta", name, object->get_meta(name)); + undo_redo->commit_action(); + } + emit_signal(SNAME("property_deleted"), p_path); } @@ -3337,7 +3392,7 @@ void EditorInspector::_property_keyed_with_value(const String &p_path, const Var // The second parameter could be null, causing the event to fire with less arguments, so use the pointer call which preserves it. const Variant args[3] = { p_path, p_value, p_advance }; const Variant *argp[3] = { &args[0], &args[1], &args[2] }; - emit_signal(SNAME("property_keyed"), argp, 3); + emit_signalp(SNAME("property_keyed"), argp, 3); } void EditorInspector::_property_checked(const String &p_path, bool p_checked) { @@ -3427,7 +3482,7 @@ void EditorInspector::_object_id_selected(const String &p_path, ObjectID p_id) { emit_signal(SNAME("object_id_selected"), p_id); } -void EditorInspector::_resource_selected(const String &p_path, RES p_resource) { +void EditorInspector::_resource_selected(const String &p_path, Ref<Resource> p_resource) { emit_signal(SNAME("resource_selected"), p_resource, p_path); } @@ -3471,7 +3526,9 @@ void EditorInspector::_notification(int p_what) { get_v_scroll_bar()->call_deferred(SNAME("set_value"), update_scroll_request); update_scroll_request = -1; } - if (refresh_countdown > 0) { + if (update_tree_pending) { + refresh_countdown = float(EditorSettings::get_singleton()->get("docks/property_editor/auto_refresh_interval")); + } else if (refresh_countdown > 0) { refresh_countdown -= get_process_delta_time(); if (refresh_countdown <= 0) { for (const KeyValue<StringName, List<EditorProperty *>> &F : editor_property_map) { @@ -3496,7 +3553,7 @@ void EditorInspector::_notification(int p_what) { } else { while (pending.size()) { - StringName prop = pending.front()->get(); + StringName prop = *pending.begin(); if (editor_property_map.has(prop)) { for (EditorProperty *E : editor_property_map[prop]) { E->update_property(); @@ -3504,7 +3561,7 @@ void EditorInspector::_notification(int p_what) { E->update_cache(); } } - pending.erase(pending.front()); + pending.remove(pending.begin()); } } @@ -3514,7 +3571,9 @@ void EditorInspector::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { _update_inspector_bg(); - update_tree(); + if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/inspector")) { + update_tree(); + } } break; } } @@ -3589,7 +3648,7 @@ void EditorInspector::_update_script_class_properties(const Object &p_object, Li break; } - Set<StringName> added; + HashSet<StringName> added; for (const Ref<Script> &s : classes) { String path = s->get_path(); String name = EditorNode::get_editor_data().script_class_get_name(path); @@ -3651,6 +3710,83 @@ Variant EditorInspector::get_property_clipboard() const { return property_clipboard; } +void EditorInspector::_add_meta_confirm() { + String name = add_meta_name->get_text(); + + object->editor_set_section_unfold("metadata", true); // Ensure metadata is unfolded when adding a new metadata. + + Variant defval; + Callable::CallError ce; + Variant::construct(Variant::Type(add_meta_type->get_selected_id()), defval, nullptr, 0, ce); + undo_redo->create_action(vformat(TTR("Add metadata %s"), name)); + undo_redo->add_do_method(object, "set_meta", name, defval); + undo_redo->add_undo_method(object, "remove_meta", name); + undo_redo->commit_action(); +} + +void EditorInspector::_check_meta_name(String name) { + String error; + + if (name == "") { + error = TTR("Metadata can't be empty."); + } else if (!name.is_valid_identifier()) { + error = TTR("Invalid metadata identifier."); + } else if (object->has_meta(name)) { + error = TTR("Metadata already exists."); + } else if (name[0] == '_') { + error = TTR("Names starting with _ are reserved for editor-only metadata."); + } + + if (error != "") { + add_meta_error->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); + add_meta_error->set_text(error); + add_meta_dialog->get_ok_button()->set_disabled(true); + } else { + add_meta_error->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); + add_meta_error->set_text(TTR("Metadata name is valid.")); + add_meta_dialog->get_ok_button()->set_disabled(false); + } +} + +void EditorInspector::_show_add_meta_dialog() { + if (!add_meta_dialog) { + add_meta_dialog = memnew(ConfirmationDialog); + add_meta_dialog->set_title(TTR("Add Metadata Property")); + VBoxContainer *vbc = memnew(VBoxContainer); + add_meta_dialog->add_child(vbc); + HBoxContainer *hbc = memnew(HBoxContainer); + vbc->add_child(hbc); + hbc->add_child(memnew(Label(TTR("Name:")))); + add_meta_name = memnew(LineEdit); + add_meta_name->set_custom_minimum_size(Size2(200 * EDSCALE, 1)); + hbc->add_child(add_meta_name); + hbc->add_child(memnew(Label(TTR("Type:")))); + add_meta_type = memnew(OptionButton); + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (i == Variant::NIL || i == Variant::RID || i == Variant::CALLABLE || i == Variant::SIGNAL) { + continue; //not editable by inspector. + } + String type = i == Variant::OBJECT ? String("Resource") : Variant::get_type_name(Variant::Type(i)); + + add_meta_type->add_icon_item(get_theme_icon(type, "EditorIcons"), type, i); + } + hbc->add_child(add_meta_type); + add_meta_dialog->get_ok_button()->set_text(TTR("Add")); + add_child(add_meta_dialog); + add_meta_dialog->register_text_enter(add_meta_name); + add_meta_dialog->connect("confirmed", callable_mp(this, &EditorInspector::_add_meta_confirm)); + add_meta_error = memnew(Label); + vbc->add_child(add_meta_error); + + add_meta_name->connect("text_changed", callable_mp(this, &EditorInspector::_check_meta_name)); + } + + add_meta_dialog->popup_centered(); + add_meta_name->set_text(""); + _check_meta_name(""); + add_meta_name->grab_focus(); +} + void EditorInspector::_bind_methods() { ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change); @@ -3674,25 +3810,11 @@ EditorInspector::EditorInspector() { add_child(main_vbox); set_horizontal_scroll_mode(SCROLL_MODE_DISABLED); - wide_editors = false; - show_categories = false; - hide_script = true; - use_doc_hints = false; - capitalize_paths = true; - use_filter = false; - autoclear = false; changing = 0; - use_folding = false; - update_all_pending = false; - update_tree_pending = false; - read_only = false; search_box = nullptr; - keying = false; _prop_edited = "property_edited"; set_process(false); property_focusable = -1; - sub_inspector = false; - deletable_properties = false; property_clipboard = Variant(); get_v_scroll_bar()->connect("value_changed", callable_mp(this, &EditorInspector::_vscroll_changed)); |