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