Skip to content

Commit

Permalink
implement C# interface export
Browse files Browse the repository at this point in the history
fix: EditorResourcePicker node path not disappearing
  • Loading branch information
poohcom1 committed May 18, 2024
1 parent f58a96c commit 6178f3d
Show file tree
Hide file tree
Showing 27 changed files with 553 additions and 25 deletions.
1 change: 1 addition & 0 deletions core/core_constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_TYPE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_HIDE_QUATERNION_EDIT);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PASSWORD);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INTERFACE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX);

BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NONE);
Expand Down
1 change: 1 addition & 0 deletions core/object/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ enum PropertyHint {
PROPERTY_HINT_HIDE_QUATERNION_EDIT, /// Only Node3D::transform should hide the quaternion editor.
PROPERTY_HINT_PASSWORD,
PROPERTY_HINT_LAYERS_AVOIDANCE,
PROPERTY_HINT_INTERFACE, ///< hint_text="language_name,interface1,interface2,..."
PROPERTY_HINT_MAX,
};

Expand Down
1 change: 1 addition & 0 deletions core/object/script_language.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ class Script : public Resource {

virtual void get_constants(HashMap<StringName, Variant> *p_constants) {}
virtual void get_members(HashSet<StringName> *p_constants) {}
virtual void get_interfaces(HashSet<StringName> *p_list) {}

virtual bool is_placeholder_fallback_enabled() const { return false; }

Expand Down
7 changes: 6 additions & 1 deletion doc/classes/@GlobalScope.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2935,7 +2935,12 @@
<constant name="PROPERTY_HINT_PASSWORD" value="36" enum="PropertyHint">
Hints that a string property is a password, and every character is replaced with the secret character.
</constant>
<constant name="PROPERTY_HINT_MAX" value="38" enum="PropertyHint">
<constant name="PROPERTY_HINT_INTERFACE" value="38" enum="PropertyHint">
Hints that an Object property is an instance of a type that implements the specified interface.
The language and interfaces must be specified by a comma separated hint string, where the first token is the language name and subsequent tokens are interfaces (e.g. [code]"C#,InterfaceA"[/code]).
The inspector will allow any Resource or Node (if the edited object is also a Node) that implements the interface to be selected.
</constant>
<constant name="PROPERTY_HINT_MAX" value="39" enum="PropertyHint">
Represents the size of the [enum PropertyHint] enum.
</constant>
<constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags" is_bitfield="true">
Expand Down
30 changes: 30 additions & 0 deletions editor/editor_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,36 @@ bool EditorData::script_class_is_parent(const String &p_class, const String &p_i
return true;
}

bool EditorData::script_class_implements_interface(const String &p_class, const String &p_interface_hint_string) {
Ref<Script> script = script_class_load_script(p_class);
return script_object_implements_interface(script, p_interface_hint_string);
}

bool EditorData::script_object_implements_interface(const Ref<Script> &p_script, const String &p_interface_hint_string) {
if (p_script.is_null()) {
return false;
}

Vector<String> hint_tokens = p_interface_hint_string.split(",");
ERR_FAIL_COND_V_MSG(hint_tokens.size() <= 1, false, "Invalid hint string for PROPERTY_HINT_INTERFACE; should contain the language name and at least one interface name.");

String language_name = hint_tokens[0];
Vector<String> interface_names = hint_tokens.slice(1);

if (language_name != p_script->get_language()->get_name()) {
return false;
}

HashSet<StringName> script_interfaces;
p_script->get_interfaces(&script_interfaces);
for (const String &E : interface_names) {
if (!script_interfaces.has(E)) {
return false;
}
}
return true;
}

StringName EditorData::script_class_get_base(const String &p_class) const {
Ref<Script> script = script_class_load_script(p_class);
if (script.is_null()) {
Expand Down
2 changes: 2 additions & 0 deletions editor/editor_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ class EditorData {
bool script_class_is_parent(const String &p_class, const String &p_inherits);
StringName script_class_get_base(const String &p_class) const;
Variant script_class_instance(const String &p_class);
bool script_class_implements_interface(const String &p_class, const String &p_interface);
bool script_object_implements_interface(const Ref<Script> &p_script, const String &p_interface); // For non global classes

Ref<Script> script_class_load_script(const String &p_class) const;

Expand Down
49 changes: 45 additions & 4 deletions editor/editor_properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3198,6 +3198,34 @@ void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource)
}
}

void EditorPropertyResource::_node_changed(const NodePath &p_path) {
// Similar to EditorPropertyNodePath::_node_selected
EditorInterfacePicker *interface_picker = Object::cast_to<EditorInterfacePicker>(resource_picker);

if (!interface_picker) {
return;
}
Node *base_node = Object::cast_to<Node>(get_edited_object());
NodePath path = p_path;

if (!base_node && Object::cast_to<RefCounted>(get_edited_object())) {
Node *to_node = get_node(p_path);
ERR_FAIL_NULL(to_node);
path = get_tree()->get_edited_scene_root()->get_path_to(to_node);
}

if (base_node) { // for AnimationTrackKeyEdit
path = base_node->get_path().rel_path_to(p_path);
}

if (!base_node) {
emit_changed(get_edited_property(), get_tree()->get_edited_scene_root()->get_node(path));
} else {
emit_changed(get_edited_property(), base_node->get_node(path));
}
update_property();
}

void EditorPropertyResource::_sub_inspector_property_keyed(const String &p_property, const Variant &p_value, 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] = { String(get_edited_property()) + ":" + p_property, p_value, p_advance };
Expand Down Expand Up @@ -3265,13 +3293,18 @@ void EditorPropertyResource::_viewport_selected(const NodePath &p_path) {
update_property();
}

void EditorPropertyResource::setup(Object *p_object, const String &p_path, const String &p_base_type) {
void EditorPropertyResource::setup(Object *p_object, const String &p_path, const String &p_base_type, const String &p_interface_hint_string) {
if (resource_picker) {
memdelete(resource_picker);
resource_picker = nullptr;
}

if (p_path == "script" && p_base_type == "Script" && Object::cast_to<Node>(p_object)) {
if (!p_interface_hint_string.is_empty()) {
EditorInterfacePicker *interface_picker = memnew(EditorInterfacePicker);
interface_picker->set_is_editing_node(Object::cast_to<Node>(p_object));
interface_picker->connect("node_changed", callable_mp(this, &EditorPropertyResource::_node_changed));
resource_picker = interface_picker;
} else if (p_path == "script" && p_base_type == "Script" && Object::cast_to<Node>(p_object)) {
EditorScriptPicker *script_picker = memnew(EditorScriptPicker);
script_picker->set_script_owner(Object::cast_to<Node>(p_object));
resource_picker = script_picker;
Expand All @@ -3288,6 +3321,7 @@ void EditorPropertyResource::setup(Object *p_object, const String &p_path, const
}

resource_picker->set_base_type(p_base_type);
resource_picker->set_interface_hint_string(p_interface_hint_string);
resource_picker->set_editable(true);
resource_picker->set_h_size_flags(SIZE_EXPAND_FILL);
add_child(resource_picker);
Expand Down Expand Up @@ -3367,6 +3401,13 @@ void EditorPropertyResource::update_property() {
}
}

// For interface picker, also set node or null if possible
EditorInterfacePicker *interface_picker = Object::cast_to<EditorInterfacePicker>(resource_picker);
if (interface_picker) {
Node *n = Object::cast_to<Node>(get_edited_property_value());
interface_picker->set_edited_property(n); // or null
}

resource_picker->set_edited_resource_no_check(res);
}

Expand Down Expand Up @@ -3817,9 +3858,9 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
return editor;
} else {
EditorPropertyResource *editor = memnew(EditorPropertyResource);
editor->setup(p_object, p_path, p_hint == PROPERTY_HINT_RESOURCE_TYPE ? p_hint_text : "Resource");
editor->setup(p_object, p_path, p_hint == PROPERTY_HINT_RESOURCE_TYPE ? p_hint_text : "Resource", p_hint == PROPERTY_HINT_INTERFACE ? p_hint_text : String());

if (p_hint == PROPERTY_HINT_RESOURCE_TYPE) {
if (p_hint == PROPERTY_HINT_RESOURCE_TYPE || p_hint == PROPERTY_HINT_INTERFACE) {
const PackedStringArray open_in_new_inspector = EDITOR_GET("interface/inspector/resources_to_open_in_new_inspector");

for (const String &type : open_in_new_inspector) {
Expand Down
3 changes: 2 additions & 1 deletion editor/editor_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,7 @@ class EditorPropertyResource : public EditorProperty {

void _resource_selected(const Ref<Resource> &p_resource, bool p_inspect);
void _resource_changed(const Ref<Resource> &p_resource);
void _node_changed(const NodePath &p_node_path); // For interface exports

void _viewport_selected(const NodePath &p_path);

Expand All @@ -719,7 +720,7 @@ class EditorPropertyResource : public EditorProperty {

public:
virtual void update_property() override;
void setup(Object *p_object, const String &p_path, const String &p_base_type);
void setup(Object *p_object, const String &p_path, const String &p_base_type, const String &p_interface_hint_string = String());

void collapse_all_folding() override;
void expand_all_folding() override;
Expand Down
2 changes: 1 addition & 1 deletion editor/editor_properties_array_dict.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ void EditorPropertyArray::update_property() {
String array_type_name = Variant::get_type_name(array_type);
if (array_type == Variant::ARRAY && subtype != Variant::NIL) {
String type_name;
if (subtype == Variant::OBJECT && (subtype_hint == PROPERTY_HINT_RESOURCE_TYPE || subtype_hint == PROPERTY_HINT_NODE_TYPE)) {
if (subtype == Variant::OBJECT && (subtype_hint == PROPERTY_HINT_RESOURCE_TYPE || subtype_hint == PROPERTY_HINT_NODE_TYPE || subtype_hint == PROPERTY_HINT_INTERFACE)) {
type_name = subtype_hint_string;
} else {
type_name = Variant::get_type_name(subtype);
Expand Down
7 changes: 6 additions & 1 deletion editor/editor_quick_open.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@
Rect2i EditorQuickOpen::prev_rect = Rect2i();
bool EditorQuickOpen::was_showed = false;

void EditorQuickOpen::popup_dialog(const String &p_base, bool p_enable_multi, bool p_dont_clear) {
void EditorQuickOpen::popup_dialog(const String &p_base, bool p_enable_multi, bool p_dont_clear, const String &p_interface_hint) {
base_type = p_base;
allow_multi_select = p_enable_multi;
search_options->set_select_mode(allow_multi_select ? Tree::SELECT_MULTI : Tree::SELECT_SINGLE);
interface_hint_string = p_interface_hint;

if (was_showed) {
popup(prev_rect);
Expand Down Expand Up @@ -75,6 +76,10 @@ void EditorQuickOpen::_build_search_cache(EditorFileSystemDirectory *p_efsd) {
// Iterate all possible base types.
for (String &parent_type : base_types) {
if (ClassDB::is_parent_class(engine_type, parent_type) || EditorNode::get_editor_data().script_class_is_parent(script_type, parent_type)) {
if (!interface_hint_string.is_empty() && !EditorNode::get_editor_data().script_class_implements_interface(script_type, interface_hint_string)) {
continue;
}

files.push_back(file.substr(6, file.length()));

// Store refs to used icons.
Expand Down
3 changes: 2 additions & 1 deletion editor/editor_quick_open.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class EditorQuickOpen : public ConfirmationDialog {
LineEdit *search_box = nullptr;
Tree *search_options = nullptr;
String base_type;
String interface_hint_string;
bool allow_multi_select = false;

Vector<String> files;
Expand Down Expand Up @@ -82,7 +83,7 @@ class EditorQuickOpen : public ConfirmationDialog {
String get_selected() const;
Vector<String> get_selected_files() const;

void popup_dialog(const String &p_base, bool p_enable_multi = false, bool p_dontclear = false);
void popup_dialog(const String &p_base, bool p_enable_multi = false, bool p_dontclear = false, const String &p_interface_hint = String());
EditorQuickOpen();
};

Expand Down
Loading

0 comments on commit 6178f3d

Please sign in to comment.