diff options
Diffstat (limited to 'editor/editor_inspector.cpp')
-rw-r--r-- | editor/editor_inspector.cpp | 493 |
1 files changed, 241 insertions, 252 deletions
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 9f049a0e58..75e518e050 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,6 +39,7 @@ #include "editor_scale.h" #include "editor_settings.h" #include "multi_node_edit.h" +#include "scene/property_utils.h" #include "scene/resources/packed_scene.h" Size2 EditorProperty::get_minimum_size() const { @@ -305,11 +306,25 @@ void EditorProperty::_notification(int p_what) { revert_rect = Rect2(); } + if (!pin_hidden && pinned) { + Ref<Texture2D> pinned_icon = get_theme_icon(SNAME("Pin"), SNAME("EditorIcons")); + int margin_w = get_theme_constant(SNAME("hseparator"), SNAME("Tree")) * 2; + int total_icon_w = margin_w + pinned_icon->get_width(); + int text_w = font->get_string_size(label, font_size, rtl ? HORIZONTAL_ALIGNMENT_RIGHT : HORIZONTAL_ALIGNMENT_LEFT, text_limit - total_icon_w).x; + int y = (size.height - pinned_icon->get_height()) / 2; + if (rtl) { + draw_texture(pinned_icon, Vector2(size.width - ofs - text_w - total_icon_w, y), color); + } else { + draw_texture(pinned_icon, Vector2(ofs + text_w + margin_w, y), color); + } + text_limit -= total_icon_w; + } + int v_ofs = (size.height - font->get_height(font_size)) / 2; if (rtl) { - draw_string(font, Point2(size.width - ofs - text_limit, v_ofs + font->get_ascent(font_size)), label, HALIGN_RIGHT, text_limit, font_size, color); + draw_string(font, Point2(size.width - ofs - text_limit, v_ofs + font->get_ascent(font_size)), label, HORIZONTAL_ALIGNMENT_RIGHT, text_limit, font_size, color); } else { - draw_string(font, Point2(ofs, v_ofs + font->get_ascent(font_size)), label, HALIGN_LEFT, text_limit, font_size, color); + draw_string(font, Point2(ofs, v_ofs + font->get_ascent(font_size)), label, HORIZONTAL_ALIGNMENT_LEFT, text_limit, font_size, color); } if (keying) { @@ -398,197 +413,43 @@ bool EditorProperty::is_read_only() const { return read_only; } -bool EditorPropertyRevert::may_node_be_in_instance(Node *p_node) { - Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); - - bool might_be = false; - Node *node = p_node; - - while (node) { - if (node == edited_scene) { - if (node->get_scene_inherited_state().is_valid()) { - might_be = true; - break; - } - might_be = false; - break; - } - if (node->get_scene_instance_state().is_valid()) { - might_be = true; - break; - } - node = node->get_owner(); - } - - return might_be; // or might not be -} - -bool EditorPropertyRevert::get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value, bool p_check_class_default) { - Node *node = p_node; - Node *orig = node; - - Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); - - bool found = false; - - while (node) { - Ref<SceneState> ss; - - if (node == edited_scene) { - ss = node->get_scene_inherited_state(); - - } else { - ss = node->get_scene_instance_state(); - } - - if (ss.is_valid()) { - NodePath np = node->get_path_to(orig); - int node_idx = ss->find_node_by_path(np); - if (node_idx >= 0) { - bool lfound = false; - Variant lvar; - lvar = ss->get_property_value(node_idx, p_prop, lfound); - if (lfound) { - found = true; - value = lvar; - } - } - } - if (node == edited_scene) { - //just in case - break; - } - node = node->get_owner(); - } - - if (p_check_class_default && !found && p_node) { - //if not found, try default class value - Variant attempt = ClassDB::class_get_default_property_value(p_node->get_class_name(), p_prop); - if (attempt.get_type() != Variant::NIL) { - found = true; - value = attempt; - } - } - - return found; -} - -bool EditorPropertyRevert::is_node_property_different(Node *p_node, const Variant &p_current, const Variant &p_orig) { - // this is a pretty difficult function, because a property may not be saved but may have - // the flag to not save if one or if zero - - //make sure there is an actual state - { - Node *node = p_node; - if (!node) { - return false; - } - - Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); - bool found_state = false; - - while (node) { - Ref<SceneState> ss; - - if (node == edited_scene) { - ss = node->get_scene_inherited_state(); - - } else { - ss = node->get_scene_instance_state(); - } - - if (ss.is_valid()) { - found_state = true; - break; - } - if (node == edited_scene) { - //just in case - break; - } - node = node->get_owner(); - } - - if (!found_state) { - return false; //pointless to check if we are not comparing against anything. - } - } - - return is_property_value_different(p_current, p_orig); -} - -bool EditorPropertyRevert::is_property_value_different(const Variant &p_a, const Variant &p_b) { - if (p_a.get_type() == Variant::FLOAT && p_b.get_type() == Variant::FLOAT) { - //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error - return !Math::is_equal_approx((float)p_a, (float)p_b); - } else { - return p_a != p_b; - } -} - -Variant EditorPropertyRevert::get_property_revert_value(Object *p_object, const StringName &p_property) { - // If the object implements property_can_revert, rely on that completely - // (i.e. don't then try to revert to default value - the property_get_revert implementation - // can do that if so desired) +Variant EditorPropertyRevert::get_property_revert_value(Object *p_object, const StringName &p_property, bool *r_is_valid) { if (p_object->has_method("property_can_revert") && p_object->call("property_can_revert", p_property)) { - return p_object->call("property_get_revert", p_property); - } - - Ref<Script> scr = p_object->get_script(); - Node *node = Object::cast_to<Node>(p_object); - if (node && EditorPropertyRevert::may_node_be_in_instance(node)) { - //if this node is an instance or inherits, but it has a script attached which is unrelated - //to the one set for the parent and also has a default value for the property, consider that - //has precedence over the value from the parent, because that is an explicit source of defaults - //closer in the tree to the current node - bool ignore_parent = false; - if (scr.is_valid()) { - Variant sorig; - if (EditorPropertyRevert::get_instantiated_node_original_property(node, "script", sorig) && !scr->inherits_script(sorig)) { - Variant dummy; - if (scr->get_property_default_value(p_property, dummy)) { - ignore_parent = true; - } - } - } - - if (!ignore_parent) { - //check for difference including instantiation - Variant vorig; - if (EditorPropertyRevert::get_instantiated_node_original_property(node, p_property, vorig, false)) { - return vorig; - } - } - } - - if (scr.is_valid()) { - Variant orig_value; - if (scr->get_property_default_value(p_property, orig_value)) { - return orig_value; + if (r_is_valid) { + *r_is_valid = true; } + return p_object->call("property_get_revert", p_property); } - //report default class value instead - return ClassDB::class_get_default_property_value(p_object->get_class_name(), p_property); + return PropertyUtils::get_property_default_value(p_object, p_property, r_is_valid); } bool EditorPropertyRevert::can_property_revert(Object *p_object, const StringName &p_property) { - Variant revert_value = EditorPropertyRevert::get_property_revert_value(p_object, p_property); - if (revert_value.get_type() == Variant::NIL) { + bool is_valid_revert = false; + Variant revert_value = EditorPropertyRevert::get_property_revert_value(p_object, p_property, &is_valid_revert); + if (!is_valid_revert) { return false; } Variant current_value = p_object->get(p_property); - return EditorPropertyRevert::is_property_value_different(current_value, revert_value); + return PropertyUtils::is_property_value_different(current_value, revert_value); } -void EditorProperty::update_reload_status() { +void EditorProperty::update_revert_and_pin_status() { if (property == StringName()) { return; //no property, so nothing to do } - bool has_reload = EditorPropertyRevert::can_property_revert(object, property); + bool new_pinned = false; + if (can_pin) { + Node *node = Object::cast_to<Node>(object); + CRASH_COND(!node); + new_pinned = node->is_property_pinned(property); + } + bool new_can_revert = EditorPropertyRevert::can_property_revert(object, property) && !is_read_only(); - if (has_reload != can_revert) { - can_revert = has_reload; + if (new_can_revert != can_revert || new_pinned != pinned) { + can_revert = new_can_revert; + pinned = new_pinned; update(); } } @@ -714,7 +575,7 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) { if (is_layout_rtl()) { mpos.x = get_size().x - mpos.x; } - bool button_left = me->get_button_mask() & MOUSE_BUTTON_MASK_LEFT; + bool button_left = (me->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE; bool new_keying_hover = keying_rect.has_point(mpos) && !button_left; if (new_keying_hover != keying_hover) { @@ -743,7 +604,7 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { Vector2 mpos = mb->get_position(); if (is_layout_rtl()) { mpos.x = get_size().x - mpos.x; @@ -780,7 +641,9 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) { } if (revert_rect.has_point(mpos)) { - Variant revert_value = EditorPropertyRevert::get_property_revert_value(object, property); + bool is_valid_revert = false; + Variant revert_value = EditorPropertyRevert::get_property_revert_value(object, property, &is_valid_revert); + ERR_FAIL_COND(!is_valid_revert); emit_changed(property, revert_value); update_property(); } @@ -790,10 +653,10 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) { update(); emit_signal(SNAME("property_checked"), property, checked); } - } else if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_RIGHT) { - _ensure_popup(); + } else if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) { + _update_popup(); menu->set_position(get_screen_position() + get_local_mouse_position()); - menu->set_size(Vector2(1, 1)); + menu->reset_size(); menu->popup(); select(); return; @@ -914,6 +777,57 @@ float EditorProperty::get_name_split_ratio() const { void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) { object = p_object; property = p_property; + _update_pin_flags(); +} + +static bool _is_value_potential_override(Node *p_node, const String &p_property) { + // Consider a value is potentially overriding another if either of the following is true: + // a) The node is foreign (inheriting or an instance), so the original value may come from another scene. + // b) The node belongs to the scene, but the original value comes from somewhere but the builtin class (i.e., a script). + Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); + Vector<SceneState::PackState> states_stack = PropertyUtils::get_node_states_stack(p_node, edited_scene); + if (states_stack.size()) { + return true; + } else { + bool is_valid_default = false; + bool is_class_default = false; + PropertyUtils::get_property_default_value(p_node, p_property, &is_valid_default, &states_stack, false, nullptr, &is_class_default); + return !is_class_default; + } +} + +void EditorProperty::_update_pin_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()) { + bool is_scene_root = false; + for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_edited_scene_count(); ++i) { + if (EditorNode::get_singleton()->get_editor_data().get_edited_scene_root(i) == node) { + is_scene_root = true; + break; + } + } + if (!is_scene_root) { + return; + } + } + if (!_is_value_potential_override(node, property)) { + return; + } + pin_hidden = false; + { + Set<StringName> storable_properties; + node->get_storable_properties(storable_properties); + if (storable_properties.has(node->get_property_store_alias(property))) { + can_pin = true; + } + } + } } Control *EditorProperty::make_custom_tooltip(const String &p_text) const { @@ -955,6 +869,10 @@ void EditorProperty::menu_option(int p_option) { case MENU_COPY_PROPERTY_PATH: { DisplayServer::get_singleton()->clipboard_set(property); } break; + case MENU_PIN_VALUE: { + emit_signal(SNAME("property_pinned"), property, !pinned); + update(); + } break; } } @@ -1003,12 +921,14 @@ void EditorProperty::_bind_methods() { ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING_NAME, "property"))); 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::STRING, "bool"))); + ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "checked"))); + ADD_SIGNAL(MethodInfo("property_pinned", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "pinned"))); ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "focusable_idx"))); GDVIRTUAL_BIND(_update_property) + ClassDB::bind_method(D_METHOD("_update_revert_and_pin_status"), &EditorProperty::update_revert_and_pin_status); } EditorProperty::EditorProperty() { @@ -1027,6 +947,9 @@ EditorProperty::EditorProperty() { 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; @@ -1038,17 +961,29 @@ EditorProperty::EditorProperty() { set_process_unhandled_key_input(true); } -void EditorProperty::_ensure_popup() { +void EditorProperty::_update_popup() { if (menu) { - return; + menu->clear(); + } else { + menu = memnew(PopupMenu); + add_child(menu); + menu->connect("id_pressed", callable_mp(this, &EditorProperty::menu_option)); } - menu = memnew(PopupMenu); menu->add_shortcut(ED_GET_SHORTCUT("property_editor/copy_property"), MENU_COPY_PROPERTY); menu->add_shortcut(ED_GET_SHORTCUT("property_editor/paste_property"), MENU_PASTE_PROPERTY); menu->add_shortcut(ED_GET_SHORTCUT("property_editor/copy_property_path"), MENU_COPY_PROPERTY_PATH); - menu->connect("id_pressed", callable_mp(this, &EditorProperty::menu_option)); menu->set_item_disabled(MENU_PASTE_PROPERTY, is_read_only()); - add_child(menu); + if (!pin_hidden) { + menu->add_separator(); + if (can_pin) { + menu->add_check_item(TTR("Pin value"), MENU_PIN_VALUE); + menu->set_item_checked(menu->get_item_index(MENU_PIN_VALUE), pinned); + menu->set_item_tooltip(menu->get_item_index(MENU_PIN_VALUE), TTR("Pinning a value forces it to be saved even if it's equal to the default.")); + } else { + menu->add_check_item(vformat(TTR("Pin value [Disabled because '%s' is editor-only]"), property), MENU_PIN_VALUE); + menu->set_item_disabled(menu->get_item_index(MENU_PIN_VALUE), true); + } + } } //////////////////////////////////////////////// @@ -1086,11 +1021,15 @@ bool EditorInspectorPlugin::can_handle(Object *p_object) { } void EditorInspectorPlugin::parse_begin(Object *p_object) { - GDVIRTUAL_CALL(_parse_begin); + GDVIRTUAL_CALL(_parse_begin, p_object); +} + +void EditorInspectorPlugin::parse_category(Object *p_object, const String &p_category) { + GDVIRTUAL_CALL(_parse_category, p_object, p_category); } -void EditorInspectorPlugin::parse_category(Object *p_object, const String &p_parse_category) { - GDVIRTUAL_CALL(_parse_category, p_object, p_parse_category); +void EditorInspectorPlugin::parse_group(Object *p_object, const String &p_group) { + GDVIRTUAL_CALL(_parse_group, p_object, p_group); } bool EditorInspectorPlugin::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) { @@ -1101,8 +1040,8 @@ bool EditorInspectorPlugin::parse_property(Object *p_object, const Variant::Type return false; } -void EditorInspectorPlugin::parse_end() { - GDVIRTUAL_CALL(_parse_end); +void EditorInspectorPlugin::parse_end(Object *p_object) { + GDVIRTUAL_CALL(_parse_end, p_object); } void EditorInspectorPlugin::_bind_methods() { @@ -1111,10 +1050,11 @@ void EditorInspectorPlugin::_bind_methods() { 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") - GDVIRTUAL_BIND(_parse_begin) + GDVIRTUAL_BIND(_parse_begin, "object") GDVIRTUAL_BIND(_parse_category, "object", "category") + GDVIRTUAL_BIND(_parse_group, "object", "group") GDVIRTUAL_BIND(_parse_property, "object", "type", "name", "hint_type", "hint_string", "usage_flags", "wide"); - GDVIRTUAL_BIND(_parse_end) + GDVIRTUAL_BIND(_parse_end, "object") } //////////////////////////////////////////////// @@ -1144,7 +1084,7 @@ void EditorInspectorCategory::_notification(int p_what) { } Color color = get_theme_color(SNAME("font_color"), SNAME("Tree")); - draw_string(font, Point2(ofs, font->get_ascent(font_size) + (get_size().height - font->get_height(font_size)) / 2).floor(), label, HALIGN_LEFT, get_size().width, font_size, color); + draw_string(font, Point2(ofs, font->get_ascent(font_size) + (get_size().height - font->get_height(font_size)) / 2).floor(), label, HORIZONTAL_ALIGNMENT_LEFT, get_size().width, font_size, color); } } @@ -1211,7 +1151,7 @@ void EditorInspectorSection::_test_unfold() { void EditorInspectorSection::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - minimum_size_changed(); + update_minimum_size(); } break; case NOTIFICATION_SORT_CHILDREN: { if (!vbox_added) { @@ -1289,7 +1229,7 @@ void EditorInspectorSection::_notification(int p_what) { Color c = bg_color; c.a *= 0.4; if (foldable && header_rect.has_point(get_local_mouse_position())) { - c = c.lightened(Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT) ? -0.05 : 0.2); + c = c.lightened(Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT) ? -0.05 : 0.2); } draw_rect(header_rect, c); @@ -1297,7 +1237,7 @@ void EditorInspectorSection::_notification(int p_what) { const int arrow_width = arrow.is_valid() ? arrow->get_width() : 0; Color color = get_theme_color(SNAME("font_color")); float text_width = get_size().width - Math::round(arrow_width + arrow_margin * EDSCALE); - draw_string(font, Point2(rtl ? 0 : Math::round(arrow_width + arrow_margin * EDSCALE), font->get_ascent(font_size) + (header_height - font->get_height(font_size)) / 2).floor(), label, rtl ? HALIGN_RIGHT : HALIGN_LEFT, text_width, font_size, color); + draw_string(font, Point2(rtl ? 0 : Math::round(arrow_width + arrow_margin * EDSCALE), font->get_ascent(font_size) + (header_height - font->get_height(font_size)) / 2).floor(), label, rtl ? HORIZONTAL_ALIGNMENT_RIGHT : HORIZONTAL_ALIGNMENT_LEFT, text_width, font_size, color); if (arrow.is_valid()) { if (rtl) { @@ -1407,7 +1347,7 @@ void EditorInspectorSection::gui_input(const Ref<InputEvent> &p_event) { } Ref<InputEventMouseButton> mb = p_event; - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Tree")); int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Tree")); if (mb->get_position().y > font->get_height(font_size)) { //clicked outside @@ -1610,7 +1550,7 @@ void EditorInspectorArray::_panel_gui_input(Ref<InputEvent> p_event, int p_index if (key_ref.is_valid()) { const InputEventKey &key = **key_ref; - if (array_elements[p_index].panel->has_focus() && key.is_pressed() && key.get_keycode() == KEY_DELETE) { + if (array_elements[p_index].panel->has_focus() && key.is_pressed() && key.get_keycode() == Key::KEY_DELETE) { _move_element(begin_array_index + p_index, -1); array_elements[p_index].panel->accept_event(); } @@ -1618,12 +1558,12 @@ void EditorInspectorArray::_panel_gui_input(Ref<InputEvent> p_event, int p_index Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) { + if (mb->get_button_index() == MouseButton::RIGHT) { popup_array_index_pressed = begin_array_index + p_index; rmb_popup->set_item_disabled(OPTION_MOVE_UP, popup_array_index_pressed == 0); rmb_popup->set_item_disabled(OPTION_MOVE_DOWN, popup_array_index_pressed == count - 1); - rmb_popup->set_position(mb->get_global_position()); - rmb_popup->set_size(Vector2()); + rmb_popup->set_position(get_screen_position() + mb->get_position()); + rmb_popup->reset_size(); rmb_popup->popup(); } } @@ -1675,11 +1615,11 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) { properties_as_array.insert(p_to_pos < 0 ? properties_as_array.size() : p_to_pos, Dictionary()); } else if (p_to_pos < 0) { // Delete the element. - properties_as_array.remove(p_element_index); + properties_as_array.remove_at(p_element_index); } else { // Move the element. properties_as_array.insert(p_to_pos, properties_as_array[p_element_index].duplicate()); - properties_as_array.remove(p_to_pos < p_element_index ? p_element_index + 1 : p_element_index); + properties_as_array.remove_at(p_to_pos < p_element_index ? p_element_index + 1 : p_element_index); } // Change the array size then set the properties. @@ -1859,7 +1799,7 @@ Array EditorInspectorArray::_extract_properties_as_array(const List<PropertyInfo dict[format_string] = object->get(pi.name); output[array_index] = dict; } else { - WARN_PRINT(vformat("Array element %s has an index too high. Array allocaiton failed.", pi.name)); + WARN_PRINT(vformat("Array element %s has an index too high. Array allocation failed.", pi.name)); } } } @@ -2062,7 +2002,7 @@ void EditorInspectorArray::_notification(int p_what) { prev_page_button->set_icon(get_theme_icon(SNAME("PagePrevious"), SNAME("EditorIcons"))); next_page_button->set_icon(get_theme_icon(SNAME("PageNext"), SNAME("EditorIcons"))); last_page_button->set_icon(get_theme_icon(SNAME("PageLast"), SNAME("EditorIcons"))); - minimum_size_changed(); + update_minimum_size(); } break; case NOTIFICATION_DRAG_BEGIN: { Dictionary dict = get_viewport()->gui_get_drag_data(); @@ -2150,13 +2090,13 @@ EditorInspectorArray::EditorInspectorArray() { add_button = memnew(Button); add_button->set_text(TTR("Add Element")); - add_button->set_text_align(Button::ALIGN_CENTER); + add_button->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER); add_button->connect("pressed", callable_mp(this, &EditorInspectorArray::_add_button_pressed)); vbox->add_child(add_button); hbox_pagination = memnew(HBoxContainer); hbox_pagination->set_h_size_flags(SIZE_EXPAND_FILL); - hbox_pagination->set_alignment(HBoxContainer::ALIGN_CENTER); + hbox_pagination->set_alignment(BoxContainer::ALIGNMENT_CENTER); vbox->add_child(hbox_pagination); first_page_button = memnew(Button); @@ -2261,10 +2201,7 @@ void EditorInspector::remove_inspector_plugin(const Ref<EditorInspectorPlugin> & for (int i = idx; i < inspector_plugin_count - 1; i++) { inspector_plugins[i] = inspector_plugins[i + 1]; } - - if (idx == inspector_plugin_count - 1) { - inspector_plugins[idx] = Ref<EditorInspectorPlugin>(); - } + inspector_plugins[inspector_plugin_count - 1] = Ref<EditorInspectorPlugin>(); inspector_plugin_count--; } @@ -2296,6 +2233,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<Edit 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); @@ -2308,7 +2246,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<Edit ep->property_usage = 0; } - if (F.label != String()) { + if (!F.label.is_empty()) { ep->set_label(F.label); } @@ -2324,7 +2262,8 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<Edit ep->set_read_only(read_only); ep->update_property(); - ep->update_reload_status(); + ep->_update_pin_flags(); + ep->update_revert_and_pin_status(); ep->set_deletable(deletable_properties); ep->update_cache(); } @@ -2506,7 +2445,7 @@ void EditorInspector::update_tree() { } } if (category->icon.is_null()) { - if (type != String()) { // Can happen for built-in scripts. + if (!type.is_empty()) { // Can happen for built-in scripts. category->icon = EditorNode::get_singleton()->get_class_icon(type, "Object"); } } @@ -2527,7 +2466,7 @@ void EditorInspector::update_tree() { class_descr_cache[type2] = descr; } - category->set_tooltip(p.name + "::" + (class_descr_cache[type2] == "" ? "" : class_descr_cache[type2])); + category->set_tooltip(p.name + "::" + (class_descr_cache[type2].is_empty() ? "" : class_descr_cache[type2])); } // Add editors at the start of a category. @@ -2597,7 +2536,7 @@ void EditorInspector::update_tree() { } } else { // Check if we exit or not a subgroup. If there is a prefix, remove it from the property label string. - if (subgroup != "" && subgroup_base != "") { + if (!subgroup.is_empty() && !subgroup_base.is_empty()) { if (path.begins_with(subgroup_base)) { path = path.trim_prefix(subgroup_base); } else if (subgroup_base.begins_with(path)) { @@ -2608,7 +2547,7 @@ void EditorInspector::update_tree() { } // Check if we exit or not a group. If there is a prefix, remove it from the property label string. - if (group != "" && group_base != "" && subgroup == "") { + if (!group.is_empty() && !group_base.is_empty() && subgroup.is_empty()) { if (path.begins_with(group_base)) { path = path.trim_prefix(group_base); } else if (group_base.begins_with(path)) { @@ -2620,10 +2559,10 @@ void EditorInspector::update_tree() { } // Add the group and subgroup to the path. - if (subgroup != "") { + if (!subgroup.is_empty()) { path = subgroup + "/" + path; } - if (group != "") { + if (!group.is_empty()) { path = group + "/" + path; } } @@ -2652,7 +2591,7 @@ void EditorInspector::update_tree() { } // Ignore properties that do not fit the filter. - if (use_filter && filter != "") { + if (use_filter && !filter.is_empty()) { if (!filter.is_subsequence_ofi(path) && !filter.is_subsequence_ofi(property_label_string) && property_prefix.to_lower().find(filter.to_lower()) == -1) { continue; } @@ -2698,6 +2637,12 @@ void EditorInspector::update_tree() { c.a /= level; section->setup(acc_path, component, object, c, use_folding); + // Add editors at the start of a group. + for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { + ped->parse_group(object, path); + _parse_added_editors(section->get_vbox(), ped); + } + vbox_per_path[root_vbox][acc_path] = section->get_vbox(); } @@ -2769,7 +2714,7 @@ void EditorInspector::update_tree() { // Get the class name. StringName classname = object->get_class_name(); - if (object_class != String()) { + if (!object_class.is_empty()) { classname = object_class; } @@ -2791,7 +2736,7 @@ void EditorInspector::update_tree() { // 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); - while (F && descr == String()) { + 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); @@ -2821,7 +2766,7 @@ void EditorInspector::update_tree() { doc_hint = descr; } - // Seach for the inspector plugin that will handle the properties. Then add the correct property editor to it. + // 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); @@ -2843,7 +2788,7 @@ void EditorInspector::update_tree() { //and set label? } - if (F.label != String()) { + if (!F.label.is_empty()) { ep->set_label(F.label); } else { // Use the existing one. @@ -2877,17 +2822,19 @@ void EditorInspector::update_tree() { 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 != String()) { + 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_reload_status(); + ep->_update_pin_flags(); + ep->update_revert_and_pin_status(); ep->update_cache(); if (current_selected && ep->property == current_selected) { @@ -2905,7 +2852,7 @@ void EditorInspector::update_tree() { // Get the lists of to add at the end. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { - ped->parse_end(); + ped->parse_end(object); _parse_added_editors(main_vbox, ped); } } @@ -2917,7 +2864,7 @@ void EditorInspector::update_property(const String &p_prop) { for (EditorProperty *E : editor_property_map[p_prop]) { E->update_property(); - E->update_reload_status(); + E->update_revert_and_pin_status(); E->update_cache(); } } @@ -3110,7 +3057,7 @@ void EditorInspector::_edit_request_change(Object *p_object, const String &p_pro return; } - if (p_property == String()) { + if (p_property.is_empty()) { update_tree_pending = true; } else { pending.insert(p_property); @@ -3143,12 +3090,20 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo } else { undo_redo->create_action(vformat(TTR("Set %s"), p_name), UndoRedo::MERGE_ENDS); undo_redo->add_do_property(object, p_name, p_value); - undo_redo->add_undo_property(object, p_name, object->get(p_name)); + bool valid = false; + Variant value = object->get(p_name, &valid); + if (valid) { + undo_redo->add_undo_property(object, p_name, value); + } PropertyInfo prop_info; if (ClassDB::get_property_info(object->get_class_name(), p_name, &prop_info)) { for (const String &linked_prop : prop_info.linked_properties) { - undo_redo->add_undo_property(object, linked_prop, object->get(linked_prop)); + valid = false; + value = object->get(linked_prop, &valid); + if (valid) { + undo_redo->add_undo_property(object, linked_prop, value); + } } } @@ -3196,7 +3151,7 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo if (editor_property_map.has(p_name)) { for (EditorProperty *E : editor_property_map[p_name]) { - E->update_reload_status(); + E->update_revert_and_pin_status(); } } } @@ -3250,7 +3205,10 @@ void EditorInspector::_property_keyed(const String &p_path, bool p_advance) { return; } - emit_signal(SNAME("property_keyed"), p_path, object->get(p_path), p_advance); //second param is deprecated + // 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); } void EditorInspector::_property_deleted(const String &p_path) { @@ -3258,7 +3216,7 @@ void EditorInspector::_property_deleted(const String &p_path) { return; } - emit_signal(SNAME("property_deleted"), p_path); //second param is deprecated + emit_signal(SNAME("property_deleted"), p_path); } void EditorInspector::_property_keyed_with_value(const String &p_path, const Variant &p_value, bool p_advance) { @@ -3266,7 +3224,10 @@ void EditorInspector::_property_keyed_with_value(const String &p_path, const Var return; } - emit_signal(SNAME("property_keyed"), p_path, p_value, p_advance); //second param is deprecated + // 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); } void EditorInspector::_property_checked(const String &p_path, bool p_checked) { @@ -3295,7 +3256,7 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) { if (editor_property_map.has(p_path)) { for (EditorProperty *E : editor_property_map[p_path]) { E->update_property(); - E->update_reload_status(); + E->update_revert_and_pin_status(); E->update_cache(); } } @@ -3305,6 +3266,35 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) { } } +void EditorInspector::_property_pinned(const String &p_path, bool p_pinned) { + if (!object) { + return; + } + + Node *node = Object::cast_to<Node>(object); + ERR_FAIL_COND(!node); + + if (undo_redo) { + undo_redo->create_action(vformat(p_pinned ? TTR("Pinned %s") : TTR("Unpinned %s"), p_path)); + undo_redo->add_do_method(node, "_set_property_pinned", p_path, p_pinned); + undo_redo->add_undo_method(node, "_set_property_pinned", p_path, !p_pinned); + if (editor_property_map.has(p_path)) { + for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) { + undo_redo->add_do_method(E->get(), "_update_revert_and_pin_status"); + undo_redo->add_undo_method(E->get(), "_update_revert_and_pin_status"); + } + } + undo_redo->commit_action(); + } else { + node->set_property_pinned(p_path, p_pinned); + if (editor_property_map.has(p_path)) { + for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) { + E->get()->update_revert_and_pin_status(); + } + } + } +} + void EditorInspector::_property_selected(const String &p_path, int p_focusable) { property_selected = p_path; property_focusable = p_focusable; @@ -3365,7 +3355,7 @@ void EditorInspector::_notification(int p_what) { if (p_what == NOTIFICATION_PROCESS) { if (update_scroll_request >= 0) { - get_v_scrollbar()->call_deferred(SNAME("set_value"), update_scroll_request); + get_v_scroll_bar()->call_deferred(SNAME("set_value"), update_scroll_request); update_scroll_request = -1; } if (refresh_countdown > 0) { @@ -3375,7 +3365,7 @@ void EditorInspector::_notification(int p_what) { for (EditorProperty *E : F.value) { if (!E->is_cache_valid()) { E->update_property(); - E->update_reload_status(); + E->update_revert_and_pin_status(); E->update_cache(); } } @@ -3397,7 +3387,7 @@ void EditorInspector::_notification(int p_what) { if (editor_property_map.has(prop)) { for (EditorProperty *E : editor_property_map[prop]) { E->update_property(); - E->update_reload_status(); + E->update_revert_and_pin_status(); E->update_cache(); } } @@ -3490,7 +3480,7 @@ void EditorInspector::_update_script_class_properties(const Object &p_object, Li String path = s->get_path(); String name = EditorNode::get_editor_data().script_class_get_name(path); if (name.is_empty()) { - if (!path.is_empty() && path.find("::") == -1) { + if (!s->is_built_in()) { name = path.get_file(); } else { name = TTR("Built-in script"); @@ -3547,7 +3537,7 @@ void EditorInspector::_bind_methods() { ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change); ADD_SIGNAL(MethodInfo("property_selected", PropertyInfo(Variant::STRING, "property"))); - ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property"))); + ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::BOOL, "advance"))); ADD_SIGNAL(MethodInfo("property_deleted", PropertyInfo(Variant::STRING, "property"))); ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop"))); ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id"))); @@ -3563,8 +3553,7 @@ EditorInspector::EditorInspector() { main_vbox->set_h_size_flags(SIZE_EXPAND_FILL); main_vbox->add_theme_constant_override("separation", 0); add_child(main_vbox); - set_enable_h_scroll(false); - set_enable_v_scroll(true); + set_horizontal_scroll_mode(SCROLL_MODE_DISABLED); wide_editors = false; show_categories = false; @@ -3587,7 +3576,7 @@ EditorInspector::EditorInspector() { deletable_properties = false; property_clipboard = Variant(); - get_v_scrollbar()->connect("value_changed", callable_mp(this, &EditorInspector::_vscroll_changed)); + get_v_scroll_bar()->connect("value_changed", callable_mp(this, &EditorInspector::_vscroll_changed)); update_scroll_request = -1; if (EditorSettings::get_singleton()) { refresh_countdown = float(EditorSettings::get_singleton()->get("docks/property_editor/auto_refresh_interval")); @@ -3596,7 +3585,7 @@ EditorInspector::EditorInspector() { refresh_countdown = 0.33; } - ED_SHORTCUT("property_editor/copy_property", TTR("Copy Property"), KEY_MASK_CMD | KEY_C); - ED_SHORTCUT("property_editor/paste_property", TTR("Paste Property"), KEY_MASK_CMD | KEY_V); - ED_SHORTCUT("property_editor/copy_property_path", TTR("Copy Property Path"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_C); + ED_SHORTCUT("property_editor/copy_property", TTR("Copy Property"), KeyModifierMask::CMD | Key::C); + ED_SHORTCUT("property_editor/paste_property", TTR("Paste Property"), KeyModifierMask::CMD | Key::V); + ED_SHORTCUT("property_editor/copy_property_path", TTR("Copy Property Path"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::C); } |