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