Skip to content

Commit 283246a

Browse files
authored
Merge pull request #59452 from reduz/refactor-metadata
2 parents 1d4b1b6 + 09b951b commit 283246a

File tree

9 files changed

+175
-33
lines changed

9 files changed

+175
-33
lines changed

core/object/object.cpp

+49-21
Original file line numberDiff line numberDiff line change
@@ -416,12 +416,22 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid
416416
}
417417
return;
418418

419-
} else if (p_name == CoreStringNames::get_singleton()->_meta) {
420-
metadata = p_value.duplicate();
421-
if (r_valid) {
422-
*r_valid = true;
419+
} else {
420+
OrderedHashMap<StringName, Variant>::Element *E = metadata_properties.getptr(p_name);
421+
if (E) {
422+
E->get() = p_value;
423+
if (r_valid) {
424+
*r_valid = true;
425+
}
426+
return;
427+
} else if (p_name.operator String().begins_with("metadata/")) {
428+
// Must exist, otherwise duplicate() will not work.
429+
set_meta(p_name.operator String().replace_first("metadata/", ""), p_value);
430+
if (r_valid) {
431+
*r_valid = true;
432+
}
433+
return;
423434
}
424-
return;
425435
}
426436

427437
// Something inside the object... :|
@@ -496,9 +506,12 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const {
496506
*r_valid = true;
497507
}
498508
return ret;
509+
}
510+
511+
const OrderedHashMap<StringName, Variant>::Element *E = metadata_properties.getptr(p_name);
499512

500-
} else if (p_name == CoreStringNames::get_singleton()->_meta) {
501-
ret = metadata;
513+
if (E) {
514+
ret = E->get();
502515
if (r_valid) {
503516
*r_valid = true;
504517
}
@@ -648,13 +661,20 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
648661
if (!is_class("Script")) { // can still be set, but this is for user-friendliness
649662
p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT));
650663
}
651-
if (!metadata.is_empty()) {
652-
p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
653-
}
664+
654665
if (script_instance && !p_reversed) {
655666
p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
656667
script_instance->get_property_list(p_list);
657668
}
669+
670+
for (OrderedHashMap<StringName, Variant>::ConstElement K = metadata.front(); K; K = K.next()) {
671+
PropertyInfo pi = PropertyInfo(K.value().get_type(), "metadata/" + K.key().operator String());
672+
if (K.value().get_type() == Variant::OBJECT) {
673+
pi.hint = PROPERTY_HINT_RESOURCE_TYPE;
674+
pi.hint_string = "Resource";
675+
}
676+
p_list->push_back(pi);
677+
}
658678
}
659679

660680
void Object::_validate_property(PropertyInfo &property) const {
@@ -915,11 +935,23 @@ bool Object::has_meta(const StringName &p_name) const {
915935

916936
void Object::set_meta(const StringName &p_name, const Variant &p_value) {
917937
if (p_value.get_type() == Variant::NIL) {
918-
metadata.erase(p_name);
938+
if (metadata.has(p_name)) {
939+
metadata.erase(p_name);
940+
metadata_properties.erase("metadata/" + p_name.operator String());
941+
notify_property_list_changed();
942+
}
919943
return;
920944
}
921945

922-
metadata[p_name] = p_value;
946+
OrderedHashMap<StringName, Variant>::Element E = metadata.find(p_name);
947+
if (E) {
948+
E.value() = p_value;
949+
} else {
950+
ERR_FAIL_COND(!p_name.operator String().is_valid_identifier());
951+
E = metadata.insert(p_name, p_value);
952+
metadata_properties["metadata/" + p_name.operator String()] = E;
953+
notify_property_list_changed();
954+
}
923955
}
924956

925957
Variant Object::get_meta(const StringName &p_name) const {
@@ -928,7 +960,7 @@ Variant Object::get_meta(const StringName &p_name) const {
928960
}
929961

930962
void Object::remove_meta(const StringName &p_name) {
931-
metadata.erase(p_name);
963+
set_meta(p_name, Variant());
932964
}
933965

934966
Array Object::_get_property_list_bind() const {
@@ -954,20 +986,16 @@ Array Object::_get_method_list_bind() const {
954986
Vector<StringName> Object::_get_meta_list_bind() const {
955987
Vector<StringName> _metaret;
956988

957-
List<Variant> keys;
958-
metadata.get_key_list(&keys);
959-
for (const Variant &E : keys) {
960-
_metaret.push_back(E);
989+
for (OrderedHashMap<StringName, Variant>::ConstElement K = metadata.front(); K; K = K.next()) {
990+
_metaret.push_back(K.key());
961991
}
962992

963993
return _metaret;
964994
}
965995

966996
void Object::get_meta_list(List<StringName> *p_list) const {
967-
List<Variant> keys;
968-
metadata.get_key_list(&keys);
969-
for (const Variant &E : keys) {
970-
p_list->push_back(E);
997+
for (OrderedHashMap<StringName, Variant>::ConstElement K = metadata.front(); K; K = K.next()) {
998+
p_list->push_back(K.key());
971999
}
9721000
}
9731001

core/object/object.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "core/templates/hash_map.h"
4040
#include "core/templates/list.h"
4141
#include "core/templates/map.h"
42+
#include "core/templates/ordered_hash_map.h"
4243
#include "core/templates/safe_refcount.h"
4344
#include "core/templates/set.h"
4445
#include "core/templates/vmap.h"
@@ -530,7 +531,8 @@ class Object {
530531
#endif
531532
ScriptInstance *script_instance = nullptr;
532533
Variant script; // Reference does not exist yet, store it in a Variant.
533-
Dictionary metadata;
534+
OrderedHashMap<StringName, Variant> metadata;
535+
HashMap<StringName, OrderedHashMap<StringName, Variant>::Element> metadata_properties;
534536
mutable StringName _class_name;
535537
mutable const StringName *_class_ptr = nullptr;
536538

core/variant/variant_construct.h

+2-4
Original file line numberDiff line numberDiff line change
@@ -543,14 +543,12 @@ class VariantConstructNoArgsNil {
543543
class VariantConstructNoArgsObject {
544544
public:
545545
static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
546-
VariantInternal::clear(&r_ret);
547-
VariantInternal::object_assign_null(&r_ret);
546+
r_ret = (Object *)nullptr; // Must construct a TYPE_OBJECT containing nullptr.
548547
r_error.error = Callable::CallError::CALL_OK;
549548
}
550549

551550
static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
552-
VariantInternal::clear(r_ret);
553-
VariantInternal::object_assign_null(r_ret);
551+
*r_ret = (Object *)nullptr; // Must construct a TYPE_OBJECT containing nullptr.
554552
}
555553
static void ptr_construct(void *base, const void **p_args) {
556554
PtrConstruct<Object *>::construct(nullptr, base);

editor/editor_inspector.cpp

+101-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "editor/editor_scale.h"
4141
#include "editor/editor_settings.h"
4242
#include "multi_node_edit.h"
43+
#include "scene/gui/center_container.h"
4344
#include "scene/property_utils.h"
4445
#include "scene/resources/packed_scene.h"
4546

@@ -2506,7 +2507,7 @@ void EditorInspector::update_tree() {
25062507
List<PropertyInfo>::Element *N = E_property->next();
25072508
bool valid = true;
25082509
while (N) {
2509-
if (N->get().usage & PROPERTY_USAGE_EDITOR && (!restrict_to_basic || (N->get().usage & PROPERTY_USAGE_EDITOR_BASIC_SETTING))) {
2510+
if (!N->get().name.begins_with("metadata/_") && N->get().usage & PROPERTY_USAGE_EDITOR && (!restrict_to_basic || (N->get().usage & PROPERTY_USAGE_EDITOR_BASIC_SETTING))) {
25102511
break;
25112512
}
25122513
if (N->get().usage & PROPERTY_USAGE_CATEGORY) {
@@ -2580,7 +2581,7 @@ void EditorInspector::update_tree() {
25802581

25812582
continue;
25822583

2583-
} 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))) {
2584+
} 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))) {
25842585
// Ignore properties that are not supposed to be in the inspector.
25852586
continue;
25862587
}
@@ -2915,7 +2916,7 @@ void EditorInspector::update_tree() {
29152916
ep->set_checked(checked);
29162917
ep->set_keying(keying);
29172918
ep->set_read_only(property_read_only);
2918-
ep->set_deletable(deletable_properties);
2919+
ep->set_deletable(deletable_properties || p.name.begins_with("metadata/"));
29192920
}
29202921

29212922
current_vbox->add_child(F.property_editor);
@@ -2956,6 +2957,15 @@ void EditorInspector::update_tree() {
29562957
}
29572958
}
29582959

2960+
if (!hide_metadata) {
2961+
Button *add_md = memnew(Button);
2962+
add_md->set_text(TTR("Add Metadata"));
2963+
add_md->set_focus_mode(Control::FOCUS_NONE);
2964+
add_md->set_icon(get_theme_icon("Add", "EditorIcons"));
2965+
add_md->connect("pressed", callable_mp(this, &EditorInspector::_show_add_meta_dialog));
2966+
main_vbox->add_child(add_md);
2967+
}
2968+
29592969
// Get the lists of to add at the end.
29602970
for (Ref<EditorInspectorPlugin> &ped : valid_plugins) {
29612971
ped->parse_end(object);
@@ -3055,6 +3065,11 @@ void EditorInspector::set_hide_script(bool p_hide) {
30553065
update_tree();
30563066
}
30573067

3068+
void EditorInspector::set_hide_metadata(bool p_hide) {
3069+
hide_metadata = p_hide;
3070+
update_tree();
3071+
}
3072+
30583073
void EditorInspector::set_use_filter(bool p_use) {
30593074
use_filter = p_use;
30603075
update_tree();
@@ -3323,6 +3338,14 @@ void EditorInspector::_property_deleted(const String &p_path) {
33233338
return;
33243339
}
33253340

3341+
if (p_path.begins_with("metadata/")) {
3342+
String name = p_path.replace_first("metadata/", "");
3343+
undo_redo->create_action(vformat(TTR("Remove metadata %s"), name));
3344+
undo_redo->add_do_method(object, "remove_meta", name);
3345+
undo_redo->add_undo_method(object, "set_meta", name, object->get_meta(name));
3346+
undo_redo->commit_action();
3347+
}
3348+
33263349
emit_signal(SNAME("property_deleted"), p_path);
33273350
}
33283351

@@ -3650,6 +3673,81 @@ Variant EditorInspector::get_property_clipboard() const {
36503673
return property_clipboard;
36513674
}
36523675

3676+
void EditorInspector::_add_meta_confirm() {
3677+
String name = add_meta_name->get_text();
3678+
3679+
object->editor_set_section_unfold("metadata", true); // Ensure metadata is unfolded when adding a new metadata.
3680+
3681+
Variant defval;
3682+
Callable::CallError ce;
3683+
Variant::construct(Variant::Type(add_meta_type->get_selected_id()), defval, nullptr, 0, ce);
3684+
undo_redo->create_action(vformat(TTR("Add metadata %s"), name));
3685+
undo_redo->add_do_method(object, "set_meta", name, defval);
3686+
undo_redo->add_undo_method(object, "remove_meta", name);
3687+
undo_redo->commit_action();
3688+
}
3689+
3690+
void EditorInspector::_check_meta_name(String name) {
3691+
String error;
3692+
3693+
if (name == "") {
3694+
error = TTR("Metadata can't be empty.");
3695+
} else if (!name.is_valid_identifier()) {
3696+
error = TTR("Invalid metadata identifier.");
3697+
} else if (object->has_meta(name)) {
3698+
error = TTR("Metadata already exists.");
3699+
}
3700+
3701+
if (error != "") {
3702+
add_meta_error->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
3703+
add_meta_error->set_text(error);
3704+
add_meta_dialog->get_ok_button()->set_disabled(true);
3705+
} else {
3706+
add_meta_error->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
3707+
add_meta_error->set_text(TTR("Metadata name is valid."));
3708+
add_meta_dialog->get_ok_button()->set_disabled(false);
3709+
}
3710+
}
3711+
3712+
void EditorInspector::_show_add_meta_dialog() {
3713+
if (!add_meta_dialog) {
3714+
add_meta_dialog = memnew(ConfirmationDialog);
3715+
add_meta_dialog->set_title(TTR("Add Metadata Property"));
3716+
VBoxContainer *vbc = memnew(VBoxContainer);
3717+
add_meta_dialog->add_child(vbc);
3718+
HBoxContainer *hbc = memnew(HBoxContainer);
3719+
vbc->add_child(hbc);
3720+
hbc->add_child(memnew(Label(TTR("Name:"))));
3721+
add_meta_name = memnew(LineEdit);
3722+
add_meta_name->set_custom_minimum_size(Size2(200 * EDSCALE, 1));
3723+
hbc->add_child(add_meta_name);
3724+
hbc->add_child(memnew(Label(TTR("Type:"))));
3725+
add_meta_type = memnew(OptionButton);
3726+
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
3727+
if (i == Variant::NIL || i == Variant::RID || i == Variant::CALLABLE || i == Variant::SIGNAL) {
3728+
continue; //not editable by inspector.
3729+
}
3730+
String type = i == Variant::OBJECT ? String("Resource") : Variant::get_type_name(Variant::Type(i));
3731+
3732+
add_meta_type->add_icon_item(get_theme_icon(type, "EditorIcons"), type, i);
3733+
}
3734+
hbc->add_child(add_meta_type);
3735+
add_meta_dialog->get_ok_button()->set_text(TTR("Add"));
3736+
add_child(add_meta_dialog);
3737+
add_meta_dialog->register_text_enter(add_meta_name);
3738+
add_meta_dialog->connect("confirmed", callable_mp(this, &EditorInspector::_add_meta_confirm));
3739+
add_meta_error = memnew(Label);
3740+
vbc->add_child(add_meta_error);
3741+
3742+
add_meta_name->connect("text_changed", callable_mp(this, &EditorInspector::_check_meta_name));
3743+
}
3744+
3745+
add_meta_dialog->popup_centered();
3746+
add_meta_name->set_text("");
3747+
_check_meta_name("");
3748+
add_meta_name->grab_focus();
3749+
}
3750+
36533751
void EditorInspector::_bind_methods() {
36543752
ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change);
36553753

editor/editor_inspector.h

+12
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "scene/gui/button.h"
3636
#include "scene/gui/dialogs.h"
3737
#include "scene/gui/line_edit.h"
38+
#include "scene/gui/option_button.h"
3839
#include "scene/gui/panel_container.h"
3940
#include "scene/gui/scroll_container.h"
4041
#include "scene/gui/texture_rect.h"
@@ -445,6 +446,7 @@ class EditorInspector : public ScrollContainer {
445446
LineEdit *search_box;
446447
bool show_categories = false;
447448
bool hide_script = true;
449+
bool hide_metadata = true;
448450
bool use_doc_hints = false;
449451
bool capitalize_paths = true;
450452
bool use_filter = false;
@@ -511,6 +513,15 @@ class EditorInspector : public ScrollContainer {
511513

512514
void _update_inspector_bg();
513515

516+
ConfirmationDialog *add_meta_dialog = nullptr;
517+
LineEdit *add_meta_name = nullptr;
518+
OptionButton *add_meta_type = nullptr;
519+
Label *add_meta_error = nullptr;
520+
521+
void _add_meta_confirm();
522+
void _show_add_meta_dialog();
523+
void _check_meta_name(String name);
524+
514525
protected:
515526
static void _bind_methods();
516527
void _notification(int p_what);
@@ -541,6 +552,7 @@ class EditorInspector : public ScrollContainer {
541552
void set_show_categories(bool p_show);
542553
void set_use_doc_hints(bool p_enable);
543554
void set_hide_script(bool p_hide);
555+
void set_hide_metadata(bool p_hide);
544556

545557
void set_use_filter(bool p_use);
546558
void register_text_enter(Node *p_line_edit);

editor/inspector_dock.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,7 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
679679
inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);
680680
inspector->set_use_doc_hints(true);
681681
inspector->set_hide_script(false);
682+
inspector->set_hide_metadata(false);
682683
inspector->set_enable_capitalize_paths(bool(EDITOR_GET("interface/inspector/capitalize_properties")));
683684
inspector->set_use_folding(!bool(EDITOR_GET("interface/inspector/disable_folding")));
684685
inspector->register_text_enter(search);

scene/gui/dialogs.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ void AcceptDialog::_notification(int p_what) {
9393
}
9494

9595
void AcceptDialog::_text_submitted(const String &p_text) {
96+
if (get_ok_button() && get_ok_button()->is_disabled()) {
97+
return; // Do not allow submission if OK button is disabled.
98+
}
9699
_ok_pressed();
97100
}
98101

0 commit comments

Comments
 (0)