Unravel Engine C++ Reference
Loading...
Searching...
No Matches
inspectors.cpp
Go to the documentation of this file.
1#include "inspectors.h"
6#include "entt/core/any.hpp"
7#include "imgui_widgets/utils.h"
8#include "inspector.h"
10
11
12#include "imgui/imgui.h"
13#include "imgui/imgui_internal.h"
15#include "string_utils/utils.h"
20#include <engine/engine.h>
26#include <functional>
27#include <unordered_map>
28#include <vector>
29
30namespace unravel
31{
32namespace
33{
34int debug_view{0};
35
36}
38{
39 auto base_inspector_type = entt::resolve<inspector>();
40 auto inspector_types = entt::get_derived_types(base_inspector_type);
41 for(auto& inspector_type : inspector_types)
42 {
43 auto inspected_type_var = entt::get_attribute(inspector_type, "inspected_type");
44 if(inspected_type_var)
45 {
46 // auto inspected_type = inspected_type_var.cast<entt::meta_type>();
47 inspector_type.invoke("create_and_register"_hs, {}, inspected_type_var, entt::forward_as_meta(type_map));
48 }
49 }
50}
51
53{
54 debug_view++;
55}
57{
58 debug_view--;
59}
60auto is_debug_view() -> bool
61{
62 return debug_view > 0;
63}
64
65
67 prefab_override_context& override_ctx,
68 inspect_result& result,
69 const meta_any_proxy& var_proxy,
70 const entt::meta_any& old_var,
71 const entt::meta_any& new_var,
72 const entt::meta_custom& custom)
73{
74 std::function<void()> on_success = nullptr;
75 if(override_ctx.record_override())
76 {
77 auto component_type_name = override_ctx.path_context.get_component_type_name();
78 auto component_type_pretty_name = override_ctx.pretty_path_context.get_component_type_name();
79 auto prop_path = override_ctx.path_context.get_current_path();
80 auto prop_pretty_path = override_ctx.pretty_path_context.get_current_path();
81 on_success = [entity = override_ctx.entity, component_type_name, component_type_pretty_name, prop_path]()
82 {
83 prefab_override_context::mark_property_as_changed(entity, component_type_name, component_type_pretty_name, prop_path);
84 };
85 }
86
87
88 //mark_property_as_changed(entity, component_type, property_path);
89 if(!result.change_recorded)
90 {
91 auto& em = ctx.get_cached<editing_manager>();
93 var_proxy,
94 old_var,
95 new_var,
96 custom,
97 on_success);
98
99 result.change_recorded = true;
100 }
101}
102
104{
105 end_prefab_inspection();
106
107 auto prefab_root = find_prefab_root_entity(e);
108
109 if(prefab_root)
110 {
111 auto prefab_comp = prefab_root.try_get<prefab_component>();
112 if(prefab_comp)
113 {
114 // Initialize override tracking for this prefab instance
115 prefab_root_entity = prefab_root;
116 entity = e;
117 is_active = true;
118
119 is_path_overridden_callback =
120 [comp_copy = *prefab_comp, entity_copy = e](const hpp::uuid& entity_uuid,
121 const std::string& component_path)
122 {
123 return comp_copy.has_override(entity_uuid, component_path);
124 };
125
126 // Get and set the entity UUID for both path contexts
127 auto entity_uuid = get_entity_prefab_uuid(e);
128
129 set_entity_uuid(entity_uuid);
130
131 return true;
132 }
133 }
134
135 return false;
136}
137
139{
140 is_active = false;
142 entity = {};
143 path_context = {};
145}
146
148{
149 if(!is_active || !prefab_root_entity)
150 {
151 return false;
152 }
153
154 auto* prefab_comp = prefab_root_entity.try_get<prefab_component>();
155 if(prefab_comp)
156 {
157 // Get the entity UUID and component paths
158 auto entity_uuid = path_context.get_entity_uuid();
159
160 if(exists_in_prefab(prefab_scene,
161 prefab_comp->source,
162 entity_uuid,
163 path_context.get_component_type_name(),
164 path_context.get_current_path()))
165 {
166 // Build technical component path: "component_type/property_path"
167 std::string component_path = path_context.get_current_path_with_component_type();
168 // Build pretty component path: "PrettyComponentType/PrettyPropertyPath"
169 std::string pretty_component_path = pretty_path_context.get_current_path_with_component_type();
170 // Add the new override with both technical and pretty paths
171 prefab_comp->add_override(entity_uuid, component_path, pretty_component_path);
172 prefab_comp->changed = true;
173 return true;
174 }
175 }
176 return true;
177}
178
180{
181 if(!is_active)
182 {
183 return;
184 }
187}
188
189void prefab_override_context::set_component_type(const std::string& type, const std::string& pretty_type)
190{
191 if(!is_active)
192 {
193 return;
194 }
195
198}
199
200void prefab_override_context::push_segment(const std::string& segment, const std::string& pretty_segment)
201{
202 if(!is_active)
203 {
204 return;
205 }
206
207 path_context.push_segment(segment);
208 pretty_path_context.push_segment(pretty_segment);
209
211 {
213 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
214 ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
215 }
216}
217
219{
220 if(!is_active)
221 {
222 return;
223 }
224
226 {
227 ImGui::PopStyleColor(2);
228 ImGui::PopFont();
229 }
230
233}
234
241{
242 if(!entity)
243 {
244 return {};
245 }
246
247 auto current_entity = entity;
248
249 // Traverse up the parent hierarchy looking for a prefab_component
250 while(current_entity)
251 {
252 if(current_entity.try_get<prefab_component>())
253 {
254 return current_entity;
255 }
256
257 // Move to parent entity
258 auto* transform = current_entity.try_get<transform_component>();
259 if(!transform)
260 {
261 break;
262 }
263
264 current_entity = transform->get_parent();
265 }
266
267 return {}; // No prefab component found in hierarchy
268}
275{
276 if(!entity)
277 {
278 return hpp::uuid{};
279 }
280
281 auto* id_comp = entity.try_get<prefab_id_component>();
282 if(!id_comp)
283 {
284 return hpp::uuid{};
285 }
286
287 return id_comp->id;
288}
290 bool position,
291 bool rotation,
292 bool scale,
293 bool skew)
294{
295 if(position)
296 {
297 mark_property_as_changed(entity, entt::resolve<transform_component>(), "local_transform/position");
298 }
299 if(rotation)
300 {
301 mark_property_as_changed(entity, entt::resolve<transform_component>(), "local_transform/rotation");
302 }
303 if(scale)
304 {
305 mark_property_as_changed(entity, entt::resolve<transform_component>(), "local_transform/scale");
306 }
307 if(skew)
308 {
309 mark_property_as_changed(entity, entt::resolve<transform_component>(), "local_transform/skew");
310 }
311}
312
313
315 bool position,
316 bool rotation,
317 bool scale,
318 bool skew)
319{
320 // also changes local transform
321 mark_transform_as_changed(entity, position, rotation, scale, skew);
322
323
324 if(position)
325 {
326 mark_property_as_changed(entity, entt::resolve<transform_component>(), "global_transform/position");
327 }
328 if(rotation)
329 {
330 mark_property_as_changed(entity, entt::resolve<transform_component>(), "global_transform/rotation");
331 }
332 if(scale)
333 {
334 mark_property_as_changed(entity, entt::resolve<transform_component>(), "global_transform/scale");
335 }
336 if(skew)
337 {
338 mark_property_as_changed(entity, entt::resolve<transform_component>(), "global_transform/skew");
339 }
340}
341
343{
344 mark_property_as_changed(entity, entt::resolve<transform_component>(), "active");
345}
346
348{
349 mark_property_as_changed(entity, entt::resolve<text_component>(), "area");
350}
351
353{
354 mark_property_as_changed(entity, entt::resolve<model_component>(), "model/materials");
355}
356
358 const entt::meta_type& component_type,
359 const std::string& property_path)
360{
361 auto component_type_name = entt::get_name(component_type);
362 auto component_pretty_type_name = entt::get_pretty_name(component_type);
363
364 mark_property_as_changed(entity, component_type_name, component_pretty_type_name, property_path);
365}
366
368 const std::string& component_type_name,
369 const std::string& component_pretty_type_name,
370 const std::string& property_path)
371{
372 auto prefab_root = find_prefab_root_entity(entity);
373 if(prefab_root)
374 {
375 auto prefab_comp = prefab_root.try_get<prefab_component>();
376 auto entity_uuid = get_entity_prefab_uuid(entity);
377
378 auto& ctx = engine::context();
379 auto& prefab_override_ctx = ctx.get_cached<prefab_override_context>();
380 if(exists_in_prefab(prefab_override_ctx.prefab_scene,
381 prefab_comp->source,
382 entity_uuid,
383 component_type_name,
384 property_path))
385 {
386 auto tokens = string_utils::tokenize(property_path, "/");
387 std::string segment;
388
389 auto type = entt::resolve(entt::hashed_string(component_type_name.c_str()));
390 std::string current_path = component_type_name;
391 std::string current_pretty_path = component_pretty_type_name;
392
393 for(auto& token : tokens)
394 {
395 auto prop = type.data(entt::hashed_string(token.c_str()));
396 auto prop_type = prop.type();
397
398 current_path += "/" + token;
399 current_pretty_path += "/" + entt::get_pretty_name(prop);
400
401 type = prop_type;
402 }
403
404 prefab_comp->add_override(entity_uuid, current_path, current_pretty_path);
405 }
406 }
407
408
409 if(component_type_name == "transform_component")
410 {
411 auto property_path_global =string_utils::replace(property_path, "global_transform", "local_transform");
412
413 if(property_path_global != property_path)
414 {
415 mark_property_as_changed(entity, component_type_name, component_pretty_type_name, property_path_global);
416 }
417 }
418}
419
421{
422 auto prefab_root = find_prefab_root_entity(entity);
423 if(prefab_root)
424 {
425 auto prefab_comp = prefab_root.try_get<prefab_component>();
426 if(prefab_comp)
427 {
428 auto entity_uuid = get_entity_prefab_uuid(entity);
429 if(entity_uuid.is_nil())
430 {
431 return;
432 }
433
434 prefab_comp->remove_entity(entity_uuid);
435 }
436 }
437}
438
441 hpp::uuid entity_uuid,
442 const std::string& component_type,
443 const std::string& property_path) -> bool
444{
445 if(!prefab)
446 {
447 return false;
448 }
449
450 if(entity_uuid.is_nil())
451 {
452 return false;
453 }
454
455 auto etype = entt::resolve(entt::hashed_string(component_type.c_str()));
456 if(!etype)
457 {
458 return false;
459 }
460 auto method = etype.func("component_exists"_hs);
461 if(!method)
462 {
463 return false;
464 }
465
466 struct prefab_version_t
467 {
468 uintptr_t version{};
469 };
470
471 entt::handle instance;
472 auto view = cache_scene.registry->view<prefab_component, prefab_version_t>();
473 view.each(
474 [&](auto e, auto&& comp, auto&& version)
475 {
476 if(comp.source == prefab && version.version == prefab.version())
477 {
478 instance = cache_scene.create_handle(e);
479 }
480 });
481 if(!instance)
482 {
483 cache_scene.unload();
484 instance = cache_scene.instantiate(prefab);
485 instance.emplace<prefab_version_t>(prefab.version());
486 }
487
488 auto entity = scene::find_entity_by_prefab_uuid(instance, entity_uuid);
489 if(!entity)
490 {
491 return false;
492 }
493
494 auto result = method.invoke({}, entity);
495
496 bool exists = result.cast<bool>();
497 if(!exists)
498 {
499 return false;
500 }
501
502 if(etype == entt::resolve<script_component>())
503 {
504 auto script_comp = entity.try_get<script_component>();
505 if(script_comp)
506 {
507 auto tokens = string_utils::tokenize(property_path, "/");
508 if(tokens.size() > 1 && tokens[0] == "script_components")
509 {
510 auto script_name = tokens[1];
511 return script_comp->has_script_components(script_name);
512 }
513 }
514 }
515
516 return true;
517}
518
519auto get_inspector(rtti::context& ctx, const entt::meta_type& type) -> std::shared_ptr<inspector>
520{
521 auto& registry = ctx.get_cached<inspector_registry>();
522 auto it = registry.type_map.find(type.info().index());
523 if(it == registry.type_map.end())
524 {
525 return nullptr;
526 }
527
528 return it->second;
529}
530
531auto is_property_visible(const entt::meta_any& object, const entt::meta_data& prop) -> bool
532{
533 auto predicate_meta = entt::get_attribute(prop, "predicate");
534 if(predicate_meta.try_cast<entt::property_predicate_t>())
535 {
536 auto pred = predicate_meta.cast<entt::property_predicate_t>();
537 return pred(object);
538 }
539
540 return true;
541}
542
543auto is_property_readonly(const entt::meta_any& object, const entt::meta_data& prop) -> bool
544{
545 auto predicate_meta = entt::get_attribute(prop, "readonly_predicate");
546 if(predicate_meta.try_cast<entt::property_predicate_t>())
547 {
548 auto pred = predicate_meta.cast<entt::property_predicate_t>();
549 return pred(object);
550 }
551
552 return false;
553}
554
555auto is_property_flattable(const entt::meta_any& object, const entt::meta_data& prop) -> bool
556{
557 auto predicate_meta = entt::get_attribute(prop, "flattable");
558 if(predicate_meta.try_cast<bool>())
559 {
560 auto pred = predicate_meta.cast<bool>();
561 return pred;
562 }
563
564 return false;
565}
566
567auto inspect_property(rtti::context& ctx, entt::meta_any& object, const meta_any_proxy& var_proxy, const entt::meta_data& prop) -> inspect_result
568{
569 if(!is_property_visible(object, prop))
570 {
571 return {};
572 }
573
574 inspect_result result{};
575 auto prop_var = prop.get(object);
576 entt::as_derived(prop_var);
577
578 auto prop_old_var = prop.get(object);
579 bool is_readonly = prop.is_const() || is_property_readonly(object, prop);
580
581 auto prop_type = prop_var.type();
582
583 bool is_flattable = is_property_flattable(object, prop);
584 bool is_array = prop_type.is_sequence_container();
585 bool is_associative_container = prop_type.is_associative_container();
586 bool is_container = is_array || is_associative_container;
587 bool is_enum = prop_type.is_enum();
588 auto prop_inspector = get_inspector(ctx, prop_type);
589
590 auto prop_name = entt::get_name(prop);
591 auto pretty_name = entt::get_pretty_name(prop);
592
593 is_readonly |= ImGui::IsReadonly();
594
595 var_info info;
596 info.read_only = is_readonly;
597 info.is_property = true;
598
599 // Push property name to path context for flattable properties too
600 auto& override_ctx = ctx.get_cached<prefab_override_context>();
601 override_ctx.push_segment(prop_name, pretty_name);
602
603 if(prop_inspector)
604 {
605 prop_inspector->before_inspect(prop);
606 }
607
608 ImGui::PushReadonly(is_readonly);
609
610
611 auto prop_proxy = make_property_proxy(var_proxy, prop);
612
613
614 {
615 if(is_array)
616 {
617 result |= inspect_array(ctx, prop_var, prop_proxy, prop, info, prop.custom());
618 }
619 else if(is_associative_container)
620 {
621 result |= inspect_associative_container(ctx, prop_var, prop_proxy, prop, info, prop.custom());
622 }
623 else if(is_enum)
624 {
625 property_layout layout(prop);
626 result |= inspect_enum(ctx, prop_var, prop_proxy, info);
627 }
628 else
629 {
630 if(prop_inspector)
631 {
632 result |= inspect_var(ctx, prop_var, prop_proxy, info, prop.custom());
633 }
634 else if(!is_flattable)
635 {
636 ImGui::AlignTextToFramePadding();
637 ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
638
639 ImGui::BeginGroup();
640
641 bool open = ImGui::TreeNodeEx(pretty_name.c_str(), ImGuiTreeNodeFlags_SpanAvailWidth);
642
643 if(open)
644 {
645 ImGui::TreePush(pretty_name.c_str());
646
647 result |= inspect_var(ctx, prop_var, prop_proxy, info, prop.custom());
648
649 ImGui::TreePop();
650 ImGui::TreePop();
651
652 ImGui::EndGroup();
653 ImGui::SetItemFocusFrame(ImGui::GetColorU32(ImGuiCol_Separator), 1.0f);
654 }
655 else
656 {
657 ImGui::EndGroup();
658 }
659
660 }
661 else
662 {
663 ImGui::PushID(pretty_name.c_str());
664
665 result |= inspect_var(ctx, prop_var, prop_proxy, info, prop.custom());
666
667 ImGui::PopID();
668 }
669 }
670 }
671
672 if(result.changed && !is_readonly)
673 {
674 prop.set(object, prop_var);
675 add_property_action(ctx, override_ctx, result, prop_proxy, prop_old_var, prop_var, prop.custom());
676
677 }
678
679 // ImGui::PopEnabled();
681
682 if(prop_inspector)
683 {
684 prop_inspector->after_inspect(prop);
685 }
686
687 override_ctx.pop_segment();
688
689 return result;
690}
691
693 entt::meta_any& var,
694 const meta_any_proxy& var_proxy,
695 const entt::meta_data& prop,
696 const var_info& info,
697 const entt::meta_custom& custom) -> inspect_result
698{
699 auto name = entt::get_pretty_name(prop);
700
701 auto tooltip = entt::get_attribute_as<std::string>(prop, "tooltip");
702
703 return inspect_array(ctx, var, var_proxy, name, tooltip, info, custom);
704}
705
707 entt::meta_any& var,
708 const meta_any_proxy& var_proxy,
709 const std::string& name,
710 const std::string& tooltip,
711 const var_info& info,
712 const entt::meta_custom& custom) -> inspect_result
713{
714 auto view = var.as_sequence_container();
715 auto size = view.size();
716 inspect_result result{};
717 auto int_size = static_cast<int>(size);
718
719 ImGui::BeginGroup();
720 property_layout layout;
721 layout.set_data(name, tooltip);
722
723 bool open = layout.push_tree_layout();
724
725 int readonly_count = entt::get_attribute_as<int>(custom, "readonly_count");
726 bool resizeable = view.resize(size);
727 {
728
729 ImGuiInputTextFlags flags = 0;
730 int step = 1;
731 int step_fast = 100;
732 bool readonly = info.read_only || !resizeable;
733 if(readonly)
734 {
735 step = 0;
736 step_fast = 0;
737 flags |= ImGuiInputTextFlags_ReadOnly;
738 }
739
740 ImGui::PushReadonly(readonly);
741
742 if(ImGui::InputInt("##array", &int_size, step, step_fast, flags))
743 {
744 int_size = std::max(readonly_count, int_size);
745
746 if(view.resize(static_cast<std::size_t>(int_size)))
747 {
748 size = static_cast<std::size_t>(int_size);
749 result.changed = true;
750 }
751 result.edit_finished = true;
752 }
754
755 ImGui::DrawItemActivityOutline();
756 }
757
758 if(open)
759 {
760 layout.pop_layout();
761
762 ImGui::TreePush("array");
763
764 int index_to_remove = -1;
765 for(std::size_t i = 0; i < size; ++i)
766 {
767 auto value = view[i];
768 std::string element = "Element ";
769 element += std::to_string(i);
770
771 ImGui::Separator();
772
773 auto item_info = info;
774 item_info.read_only |= readonly_count > 0;
775 readonly_count--;
776
777 // ImGui::SameLine();
778 auto pos_before = ImGui::GetCursorPos();
779 {
780 property_layout layout;
781 layout.set_data(element, {}, true);
782 layout.push_tree_layout(ImGuiTreeNodeFlags_Leaf);
783 ImGui::PushReadonly(item_info.read_only);
784
785 // Track array index in property path
786 // auto& override_ctx = ctx.get_cached<prefab_override_context>();
787 // if(override_ctx.is_active)
788 // {
789 // auto array_index_segment = "[" + std::to_string(i) + "]";
790 // override_ctx.path_context.push_segment(array_index_segment);
791 // override_ctx.pretty_path_context.push_segment(array_index_segment);
792 // }
793
794 meta_any_proxy value_proxy;
795 value_proxy.impl->get_name = [var_proxy, element]()
796 {
797 auto name = var_proxy.impl->get_name();
798 if(name.empty())
799 {
800 return element;
801 }
802 return fmt::format("{}/{}", name, element);
803 };
804 value_proxy.impl->getter = [parent_proxy = var_proxy, i](entt::meta_any& result)
805 {
806 entt::meta_any var;
807 if(parent_proxy.impl->getter(var) && var)
808 {
809 auto view = var.as_sequence_container();
810 if(view.size() > static_cast<std::size_t>(i))
811 {
812 auto value = view[i];
813 result = value;
814 return true;
815 }
816 }
817 return false;
818 };
819 value_proxy.impl->setter = [parent_proxy = var_proxy, i](meta_any_proxy& proxy, const entt::meta_any& value, uint64_t execution_count) mutable
820 {
821 entt::meta_any var;
822 if(parent_proxy.impl->getter(var) && var)
823 {
824 auto view = var.as_sequence_container();
825 if(view.size() > static_cast<std::size_t>(i))
826 {
827 // get iterator to i
828 auto it = view.begin();
829 std::advance(it, static_cast<std::ptrdiff_t>(i));
830
831 // remove old element
832 it = view.erase(it);
833
834 // insert new element at position i
835 view.insert(it, value);
836
837 // If the getter returned a copy, write back; if it was a ref, this is harmless.
838 return parent_proxy.impl->setter(parent_proxy, var, execution_count);
839
840 }
841 }
842 return false;
843 };
844
845 result |= inspect_var(ctx, value, value_proxy, item_info, custom);
846
847 // Pop array index from property path
848 // if(override_ctx.is_active)
849 // {
850 // override_ctx.path_context.pop_segment();
851 // override_ctx.pretty_path_context.pop_segment();
852 // }
853
855 }
856 auto pos_after = ImGui::GetCursorPos();
857
858 // if(result.changed)
859 // {
860 // view[i] = value;
861 // }
862
863 if(!item_info.read_only && !resizeable)
864 {
865 ImGui::SetCursorPos(pos_before);
866
867 ImGui::PushID(i);
868 ImGui::AlignTextToFramePadding();
869 if(ImGui::Button(ICON_MDI_DELETE, ImVec2(0.0f, ImGui::GetFrameHeightWithSpacing())))
870 {
871 index_to_remove = i;
872 }
873 ImGui::SetItemTooltipEx("Remove element.");
874 ImGui::PopID();
875 ImGui::SetCursorPos(pos_after);
876 ImGui::Dummy({});
877 }
878 }
879
880 if(index_to_remove != -1)
881 {
882 auto it = view.begin();
883 std::advance(it, index_to_remove);
884 view.erase(it);
885 result.changed = true;
886 result.edit_finished = true;
887 }
888
889 ImGui::TreePop();
890 }
891 ImGui::EndGroup();
892 ImGui::RenderFrameEx(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());
893
894 return result;
895}
896
898 entt::meta_any& var,
899 const meta_any_proxy& var_proxy,
900 const entt::meta_data& prop,
901 const var_info& info,
902 const entt::meta_custom& custom) -> inspect_result
903{
904 auto view = var.as_associative_container();
905 auto size = view.size();
906 auto int_size = static_cast<int>(size);
907
908 inspect_result result{};
909
910 // property_layout layout;
911 // layout.set_data(prop);
912
913 // bool open = true;
914 // {
915 // open = layout.push_tree_layout();
916 // {
917 // ImGuiInputTextFlags flags = 0;
918
919 // if(info.read_only)
920 // {
921 // flags |= ImGuiInputTextFlags_ReadOnly;
922 // }
923
924 // if(ImGui::InputInt("##assoc", &int_size, 1, 100, flags))
925 // {
926 // if(int_size < 0)
927 // int_size = 0;
928 // size = static_cast<std::size_t>(int_size);
929 // result.changed |= view.insert(view.get_key_type().create()).second;
930 // result.edit_finished = true;
931 // }
932
933 // ImGui::DrawItemActivityOutline();
934 // }
935 // }
936
937 // if(open)
938 // {
939 // layout.pop_layout();
940
941 // int i = 0;
942 // int index_to_remove = -1;
943 // rttr::argument key_to_remove{};
944 // for(const auto& item : view)
945 // {
946 // auto key = item.first.extract_wrapped_value();
947 // auto value = item.second.extract_wrapped_value();
948
949 // ImGui::Separator();
950
951 // // ImGui::SameLine();
952 // auto pos_before = ImGui::GetCursorPos();
953 // {
954 // property_layout layout;
955 // layout.set_data(key.to_string(), {}, true);
956 // layout.push_tree_layout(ImGuiTreeNodeFlags_Leaf);
957
958 // result |= inspect_var(ctx, value, info, get_metadata);
959 // }
960 // auto pos_after = ImGui::GetCursorPos();
961
962 // // if(result.changed)
963 // // view.set_value(i, value);
964
965 // if(!info.read_only)
966 // {
967 // ImGui::SetCursorPos(pos_before);
968
969 // ImGui::PushID(i);
970 // ImGui::AlignTextToFramePadding();
971 // if(ImGui::Button(ICON_MDI_DELETE, ImVec2(0.0f, ImGui::GetFrameHeightWithSpacing())))
972 // {
973 // key_to_remove = key;
974 // index_to_remove = i;
975 // }
976 // ImGui::SetItemTooltipCurrentViewport("Remove element.");
977 // ImGui::PopID();
978 // ImGui::SetCursorPos(pos_after);
979 // ImGui::Dummy({});
980
981 // }
982
983 // i++;
984 // }
985
986 // if(index_to_remove != -1)
987 // {
988 // view.erase(key_to_remove);
989 // result.changed = true;
990 // result.edit_finished = true;
991 // }
992 // }
993 return result;
994}
995
996auto inspect_enum(rtti::context& ctx, entt::meta_any& var, const meta_any_proxy& var_proxy, const var_info& info) -> inspect_result
997{
998 auto edited = var;
999 if(!edited.allow_cast<int64_t>())
1000 {
1001 return {};
1002 }
1003 auto current_value = edited.cast<int64_t>();
1004
1005 int current_idx = 0;
1006 int i = 0;
1007
1008 auto type = var.type();
1009
1010 struct enum_value
1011 {
1012 std::string name;
1013 std::string pretty_name;
1014 int64_t value{};
1015 };
1016
1017 std::vector<enum_value> names;
1018 for(auto kvp : type.data())
1019 {
1020 const auto& data = kvp.second;
1021 auto name = entt::get_name(data);
1022 auto pretty_name = entt::get_pretty_name(data);
1023 auto value = data.get(var);
1024
1025 int64_t value64 = 0;
1026 if(value.allow_cast<int64_t>())
1027 {
1028 value64 = value.cast<int64_t>();
1029 }
1030 else
1031 {
1032 continue;
1033 }
1034 names.emplace_back(enum_value{name, pretty_name, value64});
1035
1036 if(value64 == current_value)
1037 {
1038 current_idx = i;
1039 }
1040 ++i;
1041 }
1042
1043 if(current_idx >= static_cast<int>(names.size()))
1044 {
1045 return {};
1046 }
1047
1048 inspect_result result{};
1049
1050 if(info.read_only)
1051 {
1052 ImGui::LabelText("##enum", "%s", names[current_idx].pretty_name.c_str());
1053 }
1054 else
1055 {
1056 int listbox_item_size = static_cast<int>(names.size());
1057
1058 ImGuiComboFlags flags = 0;
1059
1060 if(ImGui::BeginCombo("##enum", names[current_idx].pretty_name.c_str(), flags))
1061 {
1062 for(int n = 0; n < listbox_item_size; n++)
1063 {
1064 const bool is_selected = (current_idx == n);
1065
1066 if(ImGui::Selectable(names[n].pretty_name.c_str(), is_selected))
1067 {
1068 current_idx = n;
1069 result.changed = true;
1070 result.edit_finished |= true;
1071
1072 edited = names[n].value;
1073
1074 if(edited.allow_cast(var.type()))
1075 {
1076 var = std::move(edited);
1077 }
1078 }
1079
1080 ImGui::DrawItemActivityOutline();
1081
1082 if(is_selected)
1083 ImGui::SetItemDefaultFocus();
1084 }
1085
1086 ImGui::EndCombo();
1087 }
1088 ImGui::DrawItemActivityOutline();
1089 }
1090
1091 return result;
1092}
1093
1095 entt::meta_any& var,
1096 const meta_any_proxy& var_proxy,
1097 const var_info& info,
1098 const entt::meta_custom& custom) -> inspect_result
1099{
1100
1101 entt::as_derived(var);
1102 auto type = var.type();
1103
1104 meta_any_proxy derived_var_proxy;
1105 derived_var_proxy.impl->get_name = [parent_proxy = var_proxy]()
1106 {
1107 return parent_proxy.impl->get_name();
1108 };
1109 derived_var_proxy.impl->getter = [parent_proxy = var_proxy](entt::meta_any& result)
1110 {
1111 if(parent_proxy.impl->getter(result) && result)
1112 {
1113 entt::as_derived(result);
1114 return true;
1115 }
1116 return false;
1117 };
1118 derived_var_proxy.impl->setter = [parent_proxy = var_proxy](meta_any_proxy& proxy, const entt::meta_any& value, uint64_t execution_count) mutable
1119 {
1120 entt::meta_any var;
1121 if(proxy.impl->getter(var) && var)
1122 {
1123 var.assign(value);
1124 return parent_proxy.impl->setter(parent_proxy, var, execution_count);
1125 }
1126 return false;
1127 };
1128
1129 entt::meta_any old_var;
1130 if(info.is_copyable)
1131 {
1132 old_var = var;
1133 }
1134
1135 inspect_result result{};
1136
1137 ImGui::PushReadonly(info.read_only);
1138
1139 auto inspector = get_inspector(ctx, type);
1140 if(inspector)
1141 {
1142 result |= inspector->inspect(ctx, var, derived_var_proxy, info, custom);
1143 }
1144 else if(type.is_enum())
1145 {
1146 result |= inspect_enum(ctx, var, derived_var_proxy, info);
1147 }
1148 else
1149 {
1150 result |= inspect_var_properties(ctx, var, derived_var_proxy, info, custom);
1151 }
1152
1153 // Record override if this was a property change in a prefab instance
1154 if(result.changed && info.is_property)
1155 {
1156 auto& override_ctx = ctx.get_cached<prefab_override_context>();
1157
1158 add_property_action(ctx, override_ctx, result, derived_var_proxy, old_var, var, custom);
1159 }
1160
1162
1163 return result;
1164}
1165
1167 entt::meta_any& var,
1168 const meta_any_proxy& var_proxy,
1169 const entt::meta_type& type,
1170 const var_info& info,
1171 const entt::meta_custom& custom) -> inspect_result
1172{
1173 auto properties = type.data();
1174
1175 inspect_result result{};
1176 if(properties.begin() == properties.end())
1177 {
1178 if(type.is_enum())
1179 {
1180 result |= inspect_enum(ctx, var, var_proxy, info);
1181 }
1182
1183 if(type.is_sequence_container())
1184 {
1185 result |= inspect_array(ctx, var, var_proxy, "", "", info, custom);
1186 }
1187 }
1188 else
1189 {
1190 std::vector<std::pair<std::string, std::vector<entt::meta_data>>> grouped_props;
1191 for(auto kvp : properties)
1192 {
1193 const auto& prop = kvp.second;
1194 // figure out the group name ("" if none)
1195 auto group = entt::get_attribute_as<std::string>(prop, "group");
1196
1197 // try to find an existing entry for this group
1198 auto it = std::find_if(grouped_props.begin(),
1199 grouped_props.end(),
1200 [&](auto& kv)
1201 {
1202 return kv.first == group;
1203 });
1204
1205 if(it == grouped_props.end())
1206 {
1207 // first time we see this group: append a new pair
1208 grouped_props.emplace_back(group, std::vector<entt::meta_data>{prop});
1209 }
1210 else
1211 {
1212 // already have this group: just push into its vector
1213 it->second.emplace_back(prop);
1214 }
1215 }
1216 size_t i = 0;
1217 for(auto& kvp : grouped_props)
1218 {
1219 auto& props = kvp.second;
1220
1221 const auto& group_name = kvp.first;
1222
1223 if(group_name.empty())
1224 {
1225 for(auto&& prop : props)
1226 {
1227 ImGui::PushID(i);
1228 result |= inspect_property(ctx, var, var_proxy, prop);
1229 ImGui::PopID();
1230 i++;
1231 }
1232 }
1233 else
1234 {
1235 ImGui::AlignTextToFramePadding();
1236 ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
1237 ImGui::BeginGroup();
1238 if(ImGui::TreeNodeEx(kvp.first.c_str(), ImGuiTreeNodeFlags_SpanAvailWidth))
1239 {
1240 ImGui::TreePush(kvp.first.c_str());
1241 ImGui::PushID(kvp.first.c_str());
1242
1243 for(auto& prop : props)
1244 {
1245 ImGui::PushID(i);
1246 result |= inspect_property(ctx, var, var_proxy, prop);
1247 ImGui::PopID();
1248 i++;
1249 }
1250 ImGui::PopID();
1251 ImGui::TreePop();
1252
1253 ImGui::TreePop();
1254
1255 ImGui::EndGroup();
1256 ImGui::SetItemFocusFrame(ImGui::GetColorU32(ImGuiCol_Separator), 1.0f);
1257 }
1258 else
1259 {
1260 ImGui::EndGroup();
1261 }
1262 }
1263 }
1264 }
1265
1266 return result;
1267}
1268
1270 entt::meta_any& var,
1271 const meta_any_proxy& var_proxy,
1272 const var_info& info,
1273 const entt::meta_custom& custom) -> inspect_result
1274{
1275 inspect_result result{};
1276 for(auto base : var.type().base())
1277 {
1278 result |= inspect_var_properties_impl(ctx, var, var_proxy, base.second, info, custom);
1279 }
1280
1281 entt::as_derived(var);
1282 result |= inspect_var_properties_impl(ctx, var, var_proxy, var.type(), info, custom);
1283
1284 return result;
1285}
1286
1287} // namespace unravel
manifold_type type
Manages ImGui layout for property inspection in the editor.
Definition inspector.h:19
void set_data(const entt::meta_data &prop, bool columns=true)
Updates layout data from meta property.
Definition inspector.cpp:76
void pop_layout()
Cleans up ImGui state (IDs, tables, tree nodes)
auto push_tree_layout(ImGuiTreeNodeFlags flags=0) -> bool
Creates a collapsible tree node layout for nested properties.
void push_segment(const std::string &segment)
Push a new path segment onto the context stack.
void set_component_type(const std::string &type)
Set the component type for this context.
auto get_component_type_name() const -> std::string
Get the component type name.
void pop_segment()
Pop the last path segment from the context stack.
void set_entity_uuid(const hpp::uuid &entity_uuid)
Set the entity UUID for nested entity tracking.
auto get_current_path() const -> std::string
Get the current full property path.
Class that contains core data for audio listeners. There can only be one instance of it per scene.
Component that handles transformations (position, rotation, scale, etc.) in the ACE framework.
float scale
Definition hub.cpp:25
std::string name
Definition hub.cpp:27
#define ICON_MDI_DELETE
const char * tooltip
void PushFont(Font::Enum _font)
Definition imgui.cpp:617
bool IsReadonly()
Definition imgui.cpp:643
void PopReadonly()
Definition imgui.cpp:658
void PushReadonly(bool _enabled)
Definition imgui.cpp:649
auto get_derived_types(const meta_type &t) -> std::vector< meta_type >
auto get_pretty_name(const meta_type &t) -> std::string
auto as_derived(meta_any &obj) -> bool
auto get_attribute_as< std::string >(const meta_custom &custom, const char *name) -> std::string
Definition reflection.h:35
auto get_attribute_as(const meta_custom &custom, const char *name) -> T
Definition reflection.h:24
auto get_attribute(const meta_custom &custom, const char *name) -> const meta_any &
std::function< bool(const meta_any &)> property_predicate_t
Definition reflection.h:85
auto get_name(const meta_type &t) -> std::string
Provides utilities for inspecting and converting sequence-related types to strings.
auto tokenize(const std::string &str, const std::string &delimiters) -> string_tokens_t
Definition utils.cpp:153
auto replace(const std::string &str, const std::string &search, const std::string &replace) -> std::string
Definition utils.cpp:28
auto inspect_property(rtti::context &ctx, entt::meta_any &object, const meta_any_proxy &var_proxy, const entt::meta_data &prop) -> inspect_result
void pop_debug_view()
Pops debug view mode (decreases debug view counter)
auto make_property_proxy(const meta_any_proxy &var_proxy, const entt::meta_data &prop) -> meta_any_proxy
auto is_property_readonly(const entt::meta_any &object, const entt::meta_data &prop) -> bool
auto is_property_visible(const entt::meta_any &object, const entt::meta_data &prop) -> bool
void add_property_action(rtti::context &ctx, prefab_override_context &override_ctx, inspect_result &result, const meta_any_proxy &var_proxy, const entt::meta_any &old_var, const entt::meta_any &new_var, const entt::meta_custom &custom)
auto inspect_associative_container(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const entt::meta_data &prop, const var_info &info, const entt::meta_custom &custom) -> inspect_result
Inspects associative containers like maps and sets.
auto inspect_var_properties_impl(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const entt::meta_type &type, const var_info &info, const entt::meta_custom &custom) -> inspect_result
auto inspect_var_properties(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result
Inspects all properties of a complex object recursively.
void push_debug_view()
Pushes debug view mode (increases debug view counter)
auto is_debug_view() -> bool
Checks if currently in debug view mode.
auto get_inspector(rtti::context &ctx, const entt::meta_type &type) -> std::shared_ptr< inspector >
auto inspect_array(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const entt::meta_data &prop, const var_info &info, const entt::meta_custom &custom) -> inspect_result
Inspects array-like containers with add/remove functionality.
auto inspect_enum(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info) -> inspect_result
Inspects enumeration types with dropdown selection.
auto is_property_flattable(const entt::meta_any &object, const entt::meta_data &prop) -> bool
auto inspect_var(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result
Main entry point for inspecting any variable with automatic type resolution.
entt::entity entity
Represents a handle to an asset, providing access and management functions.
auto get_cached() -> T &
Definition context.hpp:49
size()=default
void do_action(const std::string &name, const std::function< void()> &action)
static auto context() -> rtti::context &
Definition engine.cpp:116
Result of an inspection operation indicating what changes occurred.
Definition inspector.h:146
bool change_recorded
Whether the change was recorded for undo/redo system.
Definition inspector.h:152
Registry for managing type-specific inspector instances.
Definition inspectors.h:19
inspector_registry()
Constructor that auto-discovers and registers all inspector types.
std::unordered_map< entt::id_type, std::shared_ptr< inspector > > type_map
Map from type ID to inspector instance for fast lookup.
Definition inspectors.h:29
Safe deferred property access proxy for arbitrary object properties.
Definition inspector.h:198
std::shared_ptr< meta_any_proxy_impl > impl
Definition inspector.h:221
Component that holds a reference to a prefab asset and tracks property overrides.
Component that provides a unique identifier (UUID) for a prefab.
hpp::uuid id
The unique identifier for the entity.
Global context for tracking prefab override changes during inspection.
Definition inspectors.h:94
void end_prefab_inspection()
End prefab inspection context and clean up state.
entt::handle prefab_root_entity
The prefab root entity that contains the prefab_component.
Definition inspectors.h:109
static auto exists_in_prefab(scene &cache_scene, const asset_handle< prefab > &prefab, hpp::uuid entity_uuid, const std::string &component_type, const std::string &property_path) -> bool
Checks if a property exists in the original prefab.
void set_entity_uuid(const hpp::uuid &uuid)
Sets the entity UUID for both path contexts.
property_path_context pretty_path_context
Current property path context for human-readable paths.
Definition inspectors.h:106
entt::handle entity
The specific entity currently being inspected.
Definition inspectors.h:111
auto is_path_overridden() const -> bool
Check if the current property path is already overridden.
Definition inspectors.h:171
static void mark_property_as_changed(entt::handle entity, const entt::meta_type &component_type, const std::string &property_path)
Marks a specific property as changed using component type.
static void mark_transform_global_as_changed(entt::handle entity, bool position, bool rotation, bool scale, bool skew)
static auto find_prefab_root_entity(entt::handle entity) -> entt::handle
Finds the prefab root entity by traversing up the parent hierarchy.
static void mark_material_as_changed(entt::handle entity)
Marks material as changed in prefab override system.
static void mark_text_area_as_changed(entt::handle entity)
Marks text area as changed in prefab override system.
property_path_context path_context
Current property path context for serialization paths.
Definition inspectors.h:104
void set_component_type(const std::string &type, const std::string &pretty_type)
Sets the component type for both path contexts.
auto record_override() -> bool
Record a property override when a change is detected.
static void mark_entity_as_removed(entt::handle entity)
Marks an entity as removed from the prefab instance.
bool is_active
Whether we're currently inspecting a prefab instance.
Definition inspectors.h:119
void push_segment(const std::string &segment, const std::string &pretty_segment)
Pushes a new path segment onto both contexts and applies override styling.
static void mark_active_as_changed(entt::handle entity)
Marks entity active state as changed in prefab override system.
static void mark_transform_as_changed(entt::handle entity, bool position, bool rotation, bool scale, bool skew)
Marks transform properties as changed in prefab override system.
void pop_segment()
Pops the last path segment and removes override styling if needed.
static auto get_entity_prefab_uuid(entt::handle entity) -> hpp::uuid
Gets the prefab UUID of an entity from its prefab_id_component.
auto begin_prefab_inspection(entt::handle entity) -> bool
Initialize context for inspecting a prefab instance.
Represents a generic prefab with a buffer for serialized data.
Definition prefab.h:18
Represents a scene in the ACE framework, managing entities and their relationships.
Definition scene.h:21
static auto find_entity_by_prefab_uuid(entt::handle root_entity, const hpp::uuid &target_uuid) -> entt::handle
Finds an entity by UUID in the scene.
Definition scene.cpp:319
Metadata about a variable being inspected.
Definition inspector.h:133
bool is_property
Whether this is a property that can be overridden in prefabs.
Definition inspector.h:137
bool read_only
Whether the variable should be displayed as read-only.
Definition inspector.h:135