@@ -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
239459void EditorPropertyArray::initialize_array (Variant &p_array) {
0 commit comments