Skip to content

Commit 00abe9e

Browse files
committed
Adding exporting of structs
1 parent e6ccee4 commit 00abe9e

File tree

9 files changed

+322
-4
lines changed

9 files changed

+322
-4
lines changed

core/object/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ enum PropertyHint {
9494
PROPERTY_HINT_INPUT_NAME,
9595
PROPERTY_HINT_FILE_PATH,
9696
PROPERTY_HINT_MAX,
97+
PROPERTY_HINT_STRUCT, // The hint string would be like "script_path::struct_name"
9798
};
9899

99100
enum PropertyUsageFlags {

core/variant/array.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -994,13 +994,16 @@ void Array::set_named(const StringName &p_member, const Variant &p_value) {
994994

995995
void Array::set_as_struct(const Vector<StringName> &p_names, const Vector<Variant> &p_default_values) {
996996
ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
997-
ERR_FAIL_COND_MSG(_p->array.size() > 0, "Type can only be set when array is empty.");
997+
//ERR_FAIL_COND_MSG(_p->array.size() > 0, "Type can only be set when array is empty.");
998998
ERR_FAIL_COND_MSG(_p->refcount.get() > 1, "Type can only be set when array has no more than one user.");
999999
ERR_FAIL_COND_MSG(_p->typed.type != Variant::NIL || is_struct(), "Type can only be set once.");
10001000

10011001
uint32_t p_member_count = p_names.size();
10021002
ERR_FAIL_COND_MSG(p_default_values.size() != p_member_count, "Struct member vectors must have the same size.");
10031003

1004+
// Added this, and commented out the line above as a temporary fix for the exporting.
1005+
_p->array.clear();
1006+
10041007
_p->struct_size = p_member_count;
10051008
if (p_member_count > 0) {
10061009
_p->struct_member_names = memnew_arr(StringName, p_member_count);

editor/inspector/editor_properties.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4253,6 +4253,11 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
42534253
}
42544254
} break;
42554255
case Variant::ARRAY: {
4256+
if (p_hint == PROPERTY_HINT_STRUCT) {
4257+
EditorPropertyStruct *editor = memnew(EditorPropertyStruct);
4258+
editor->setup(p_hint_text);
4259+
return editor;
4260+
}
42564261
EditorPropertyArray *editor = memnew(EditorPropertyArray);
42574262
editor->setup(Variant::ARRAY, p_hint_text);
42584263
return editor;

editor/inspector/editor_properties_array_dict.cpp

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,226 @@ String EditorPropertyDictionaryObject::get_label_for_index(int p_index) {
234234
}
235235
}
236236

237+
///////////////////// STRUCT ///////////////////////////
238+
239+
bool EditorPropertyStructObject::_set(const StringName &p_name, const Variant &p_value) {
240+
String name = p_name;
241+
if (!name.begins_with("indices/")) {
242+
return false;
243+
}
244+
int index = name.get_slicec('/', 1).to_int();
245+
array.set(index, p_value);
246+
return true;
247+
}
248+
249+
bool EditorPropertyStructObject::_get(const StringName &p_name, Variant &r_ret) const {
250+
String name = p_name;
251+
if (!name.begins_with("indices/")) {
252+
return false;
253+
}
254+
int index = name.get_slicec('/', 1).to_int();
255+
bool valid;
256+
r_ret = array.get(index, &valid);
257+
if (r_ret.get_type() == Variant::OBJECT && Object::cast_to<EncodedObjectAsID>(r_ret)) {
258+
r_ret = Object::cast_to<EncodedObjectAsID>(r_ret)->get_object_id();
259+
}
260+
return valid;
261+
}
262+
263+
void EditorPropertyStructObject::set_array(const Variant &p_array) {
264+
array = p_array;
265+
}
266+
267+
Variant EditorPropertyStructObject::get_array() {
268+
return array;
269+
}
270+
271+
void EditorPropertyStruct::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
272+
if (!p_property.begins_with("indices/")) {
273+
return;
274+
}
275+
if (p_value.get_type() == Variant::OBJECT && p_value.is_null()) {
276+
p_value = Variant();
277+
}
278+
int index = p_property.get_slicec('/', 1).to_int();
279+
Variant array = object->get_array().duplicate();
280+
array.set(index, p_value);
281+
emit_changed(get_edited_property(), array, p_name, p_changing);
282+
if (p_changing) {
283+
object->set_array(array);
284+
}
285+
}
286+
287+
void EditorPropertyStruct::_edit_pressed() {
288+
Variant array = get_edited_property_value();
289+
if (!array.is_array() && edit->is_pressed()) {
290+
Array arr;
291+
arr.resize(fields.size());
292+
Vector<StringName> names;
293+
Vector<Variant> default_values;
294+
for (int i = 0; i < fields.size(); i++) {
295+
Dictionary d = fields[i];
296+
arr[i] = d["default_value"];
297+
names.push_back(d["name"]);
298+
default_values.push_back(d["default_value"]);
299+
}
300+
arr.set_as_struct(names, default_values);
301+
array = arr;
302+
emit_changed(get_edited_property(), array);
303+
}
304+
305+
get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed());
306+
update_property();
307+
}
308+
309+
void EditorPropertyStruct::update_property() {
310+
Variant array = get_edited_property_value();
311+
312+
if (!array.is_array()) {
313+
edit->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER);
314+
edit->set_button_icon(Ref<Texture2D>());
315+
edit->set_text(vformat(TTR("(Nil) %s"), struct_name));
316+
edit->set_pressed(false);
317+
if (container) {
318+
set_bottom_editor(nullptr);
319+
memdelete(container);
320+
container = nullptr;
321+
slots.clear();
322+
}
323+
return;
324+
}
325+
326+
object->set_array(array);
327+
328+
edit->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER);
329+
edit->set_button_icon(Ref<Texture2D>());
330+
edit->set_text(struct_name);
331+
332+
bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
333+
if (edit->is_pressed() != unfolded) {
334+
edit->set_pressed(unfolded);
335+
}
336+
337+
if (unfolded) {
338+
updating = true;
339+
340+
if (!container) {
341+
container = memnew(PanelContainer);
342+
add_child(container);
343+
set_bottom_editor(container);
344+
345+
VBoxContainer *vbox = memnew(VBoxContainer);
346+
vbox->set_theme_type_variation(SNAME("EditorPropertyContainer"));
347+
container->add_child(vbox);
348+
349+
property_vbox = memnew(VBoxContainer);
350+
property_vbox->set_theme_type_variation(SNAME("EditorPropertyContainer"));
351+
property_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
352+
vbox->add_child(property_vbox);
353+
354+
for (int i = 0; i < fields.size(); i++) {
355+
Dictionary d = fields[i];
356+
StringName field_name = d["name"];
357+
Variant::Type field_type = (Variant::Type)(int)d["type"];
358+
PropertyHint field_hint = (PropertyHint)(int)d["hint"];
359+
String field_hint_string = d["hint_string"];
360+
uint32_t field_usage = d["usage"];
361+
362+
HBoxContainer *hbox = memnew(HBoxContainer);
363+
property_vbox->add_child(hbox);
364+
365+
EditorProperty *prop = EditorInspector::instantiate_property_editor(this, field_type, "", field_hint, field_hint_string, field_usage);
366+
if (!prop) {
367+
prop = memnew(EditorPropertyNil);
368+
}
369+
prop->set_h_size_flags(SIZE_EXPAND_FILL);
370+
hbox->add_child(prop);
371+
372+
String prop_name = "indices/" + itos(i);
373+
prop->set_object_and_property(object.ptr(), prop_name);
374+
prop->set_label(field_name);
375+
prop->set_selectable(false);
376+
prop->set_use_folding(is_using_folding());
377+
prop->connect(SNAME("property_changed"), callable_mp(this, &EditorPropertyStruct::_property_changed));
378+
prop->connect(SNAME("object_id_selected"), callable_mp(this, &EditorPropertyStruct::_object_id_selected));
379+
if (field_type == Variant::OBJECT) {
380+
prop->connect("resource_selected", callable_mp(this, &EditorPropertyStruct::_resource_selected), CONNECT_DEFERRED);
381+
}
382+
prop->set_read_only(is_read_only());
383+
384+
Slot slot;
385+
slot.prop = prop;
386+
slot.prop_name = prop_name;
387+
slot.index = i;
388+
slots.push_back(slot);
389+
}
390+
}
391+
392+
for (Slot &slot : slots) {
393+
slot.prop->update_property();
394+
}
395+
396+
updating = false;
397+
} else {
398+
if (container) {
399+
set_bottom_editor(nullptr);
400+
memdelete(container);
401+
container = nullptr;
402+
slots.clear();
403+
}
404+
}
405+
}
406+
407+
void EditorPropertyStruct::_object_id_selected(const StringName &p_property, ObjectID p_id) {
408+
emit_signal(SNAME("object_id_selected"), p_property, p_id);
409+
}
410+
411+
void EditorPropertyStruct::_resource_selected(const String &p_path, Ref<Resource> p_resource) {
412+
emit_signal(SNAME("resource_selected"), get_edited_property(), p_resource);
413+
}
414+
415+
bool EditorPropertyStruct::is_colored(ColorationMode p_mode) {
416+
return p_mode == COLORATION_CONTAINER_RESOURCE;
417+
}
418+
419+
void EditorPropertyStruct::_notification(int p_what) {
420+
}
421+
422+
void EditorPropertyStruct::setup(const String &p_hint_string) {
423+
int sep = p_hint_string.find("::");
424+
if (sep != -1) {
425+
struct_path = p_hint_string.substr(0, sep);
426+
struct_name = p_hint_string.substr(sep + 2);
427+
Ref<Script> scr = ResourceLoader::load(struct_path);
428+
if (scr.is_valid()) {
429+
HashMap<StringName, Variant> constants;
430+
scr->get_constants(&constants);
431+
if (constants.has(struct_name)) {
432+
Variant struct_def = constants[struct_name];
433+
if (struct_def.get_type() == Variant::OBJECT) {
434+
fields = struct_def.call("get_fields");
435+
}
436+
}
437+
}
438+
}
439+
}
440+
441+
EditorPropertyStruct::EditorPropertyStruct() {
442+
object.instantiate();
443+
edit = memnew(Button);
444+
edit->set_accessibility_name(TTRC("Edit"));
445+
edit->set_h_size_flags(SIZE_EXPAND_FILL);
446+
edit->set_clip_text(true);
447+
edit->set_theme_type_variation(SNAME("EditorInspectorButton"));
448+
edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyStruct::_edit_pressed));
449+
edit->set_toggle_mode(true);
450+
add_child(edit);
451+
add_focusable(edit);
452+
453+
container = nullptr;
454+
has_borders = true;
455+
}
456+
237457
///////////////////// ARRAY ///////////////////////////
238458

239459
void EditorPropertyArray::initialize_array(Variant &p_array) {

editor/inspector/editor_properties_array_dict.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,56 @@ class EditorPropertyDictionaryObject : public RefCounted {
8989
String get_key_name_for_index(int p_index);
9090
};
9191

92+
class EditorPropertyStructObject : public RefCounted {
93+
GDCLASS(EditorPropertyStructObject, RefCounted);
94+
95+
Variant array;
96+
97+
protected:
98+
bool _set(const StringName &p_name, const Variant &p_value);
99+
bool _get(const StringName &p_name, Variant &r_ret) const;
100+
101+
public:
102+
void set_array(const Variant &p_array);
103+
Variant get_array();
104+
};
105+
106+
class EditorPropertyStruct : public EditorProperty {
107+
GDCLASS(EditorPropertyStruct, EditorProperty);
108+
109+
struct Slot {
110+
EditorProperty *prop = nullptr;
111+
String prop_name;
112+
int index = -1;
113+
};
114+
115+
Ref<EditorPropertyStructObject> object;
116+
VBoxContainer *property_vbox = nullptr;
117+
Button *edit = nullptr;
118+
PanelContainer *container = nullptr;
119+
bool updating = false;
120+
121+
String struct_path;
122+
String struct_name;
123+
124+
LocalVector<Slot> slots;
125+
Array fields;
126+
127+
void _edit_pressed();
128+
void _property_changed(const String &p_property, Variant p_value, const String &p_name = "", bool p_changing = false);
129+
void _object_id_selected(const StringName &p_property, ObjectID p_id);
130+
void _resource_selected(const String &p_path, Ref<Resource> p_resource);
131+
132+
protected:
133+
void _notification(int p_what);
134+
135+
public:
136+
void setup(const String &p_hint_string);
137+
virtual void update_property() override;
138+
virtual bool is_colored(ColorationMode p_mode) override;
139+
EditorPropertyStruct();
140+
};
141+
92142
class EditorPropertyArray : public EditorProperty {
93143
GDCLASS(EditorPropertyArray, EditorProperty);
94144

modules/gdscript/gdscript.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,29 @@
6363

6464
///////////////////////////
6565

66+
void GDScriptStruct::_bind_methods() {
67+
ClassDB::bind_method(D_METHOD("get_fields"), &GDScriptStruct::get_fields);
68+
}
69+
70+
Array GDScriptStruct::get_fields() const {
71+
Array arr;
72+
for (int i = 0; i < fields.size(); i++) {
73+
PropertyInfo prop_info = fields[i].property_info;
74+
Dictionary dic;
75+
dic["name"] = fields[i].name;
76+
dic["type"] = prop_info.type;
77+
dic["hint"] = prop_info.hint;
78+
dic["hint_string"] = prop_info.hint_string;
79+
dic["usage"] = prop_info.usage;
80+
dic["class_name"] = prop_info.class_name;
81+
dic["default_value"] = fields[i].default_value;
82+
arr.push_back(dic);
83+
}
84+
return arr;
85+
}
86+
87+
///////////////////////////
88+
6689
GDScriptNativeClass::GDScriptNativeClass(const StringName &p_name) {
6790
name = p_name;
6891
}

modules/gdscript/gdscript.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,22 @@ class GDScriptStruct : public RefCounted {
6161
GDCLASS(GDScriptStruct, RefCounted);
6262

6363
protected:
64-
static void _bind_methods() {}
64+
static void _bind_methods();
6565

6666
public:
6767
StringName name;
6868

6969
struct Field {
7070
StringName name;
7171
GDScriptDataType data_type;
72+
PropertyInfo property_info;
7273
Variant default_value;
7374
};
7475

7576
Vector<Field> fields;
7677
GDScriptStruct() {}
78+
79+
Array get_fields() const;
7780
};
7881

7982
class GDScript : public Script {

modules/gdscript/gdscript_compiler.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3032,6 +3032,7 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
30323032
GDScriptStruct::Field field;
30333033
field.name = var_node->identifier->name;
30343034
field.data_type = _gdtype_from_datatype(var_node->get_datatype(), p_script);
3035+
field.property_info = var_node->get_datatype().to_property_info(field.name);
30353036

30363037
if (var_node->initializer != nullptr && var_node->initializer->is_constant) {
30373038
field.default_value = var_node->initializer->reduced_value;

0 commit comments

Comments
 (0)