Unravel Engine C++ Reference
Loading...
Searching...
No Matches
inspector_entity.cpp
Go to the documentation of this file.
1#include "inspector_entity.h"
2#include "entt/meta/meta.hpp"
3#include "inspector.h"
4#include "inspectors.h"
6
12#include <engine/engine.h>
16
17#include <hpp/type_name.hpp>
18#include <hpp/utility.hpp>
19#include <string_utils/utils.h>
20
21#include <imgui/imgui.h>
22#include <imgui/imgui_internal.h>
23namespace unravel
24{
25
26namespace
27{
28template<typename T>
29auto get_component_icon() -> std::string
30{
31 // Core components
32 if constexpr(std::is_same<T, id_component>::value)
33 {
35 }
36 else if constexpr(std::is_same<T, tag_component>::value)
37 {
38 return ICON_MDI_TAG;
39 }
40 else if constexpr(std::is_same<T, layer_component>::value)
41 {
42 return ICON_MDI_LAYERS;
43 }
44 else if constexpr(std::is_same<T, prefab_component>::value)
45 {
46 return ICON_MDI_CUBE;
47 }
48 else if constexpr(std::is_same<T, prefab_id_component>::value)
49 {
51 }
52 // Transform
53 else if constexpr(std::is_same<T, transform_component>::value)
54 {
56 }
57 // Test/Debug
58 else if constexpr(std::is_same<T, test_component>::value)
59 {
60 return ICON_MDI_BUG;
61 }
62 // Rendering components
63 else if constexpr(std::is_same<T, model_component>::value)
64 {
65 return ICON_MDI_SHAPE;
66 }
67 else if constexpr(std::is_same<T, submesh_component>::value)
68 {
70 }
71 else if constexpr(std::is_same<T, camera_component>::value)
72 {
73 return ICON_MDI_CAMERA;
74 }
75 else if constexpr(std::is_same<T, text_component>::value)
76 {
77 return ICON_MDI_TEXT;
78 }
79 // Animation
80 else if constexpr(std::is_same<T, animation_component>::value)
81 {
82 return ICON_MDI_ANIMATION;
83 }
84 else if constexpr(std::is_same<T, bone_component>::value)
85 {
86 return ICON_MDI_BONE;
87 }
88 // Lighting
89 else if constexpr(std::is_same<T, light_component>::value)
90 {
91 return ICON_MDI_LIGHTBULB;
92 }
93 else if constexpr(std::is_same<T, skylight_component>::value)
94 {
96 }
97 else if constexpr(std::is_same<T, reflection_probe_component>::value)
98 {
100 }
101 // Physics
102 else if constexpr(std::is_same<T, physics_component>::value)
103 {
104 return ICON_MDI_ATOM;
105 }
106 // Audio
107 else if constexpr(std::is_same<T, audio_source_component>::value)
108 {
110 }
111 else if constexpr(std::is_same<T, audio_listener_component>::value)
112 {
114 }
115 // Scripting
116 else if constexpr(std::is_same<T, script_component>::value)
117 {
118 return ICON_MDI_SCRIPT;
119 }
120 // Post-processing effects (using similar icons for consistency)
121 else if constexpr(std::is_same<T, tonemapping_component>::value)
122 {
124 }
125 else if constexpr(std::is_same<T, fxaa_component>::value)
126 {
127 return ICON_MDI_FILTER;
128 }
129 else if constexpr(std::is_same<T, assao_component>::value)
130 {
132 }
133 else if constexpr(std::is_same<T, ssr_component>::value)
134 {
135 return ICON_MDI_MIRROR;
136 }
137 else
138 {
139 // Default fallback icon
141 }
142}
143
144struct inspect_callbacks
145{
146 std::function<inspect_result()> on_inspect;
147 std::function<void()> on_add;
148 std::function<void()> on_remove;
149 std::function<bool()> can_remove;
150 std::function<bool()> can_merge;
151
152 std::string icon;
153};
154
155auto inspect_component(const std::string& name, const inspect_callbacks& callbacks) -> inspect_result
156{
157 inspect_result result{};
158
159 bool opened = true;
160
161 ImGui::PushID(name.c_str());
162
163 auto popup_str = "COMPONENT_SETTING";
164
165 bool open_popup = false;
166 bool open = true;
167 if(!callbacks.can_merge())
168 {
169 ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver);
170
171 auto pos = ImGui::GetCursorPos();
172 auto col_header = ImGui::GetColorU32(ImGuiCol_Header);
173 auto col_header_hovered = ImGui::GetColorU32(ImGuiCol_HeaderHovered);
174 auto col_header_active = ImGui::GetColorU32(ImGuiCol_HeaderActive);
175
176 auto col_framebg = ImGui::GetColorU32(ImGuiCol_FrameBg);
177 auto col_framebg_hovered = ImGui::GetColorU32(ImGuiCol_FrameBgHovered);
178 auto col_framebg_active = ImGui::GetColorU32(ImGuiCol_FrameBgActive);
179
180 ImGui::PushStyleColor(ImGuiCol_Header, col_framebg);
181 ImGui::PushStyleColor(ImGuiCol_HeaderHovered, col_framebg_hovered);
182 ImGui::PushStyleColor(ImGuiCol_HeaderActive, col_framebg_active);
183
184 open = ImGui::CollapsingHeader(fmt::format(" {}", name).c_str(), nullptr, ImGuiTreeNodeFlags_AllowOverlap);
185
186 ImGui::OpenPopupOnItemClick(popup_str);
187 ImGui::PopStyleColor(3);
188
189 ImGui::SetCursorPos(pos);
190 ImGui::AlignTextToFramePadding();
191 ImGui::Text(" %s", callbacks.icon.c_str());
192
193 ImGui::SameLine();
194 auto settings_size = ImGui::CalcTextSize(ICON_MDI_COG).x + ImGui::GetStyle().FramePadding.x * 2.0f;
195
196 auto avail = ImGui::GetContentRegionAvail().x + ImGui::GetStyle().FramePadding.x;
197 ImGui::AlignedItem(1.0f,
198 avail,
199 settings_size,
200 [&]()
201 {
202 if(ImGui::Button(ICON_MDI_COG))
203 {
204 open_popup = true;
205 }
206 });
207 }
208
209 if(open)
210 {
211 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 8.0f);
212 ImGui::TreePush(name.c_str());
213
214 result |= callbacks.on_inspect();
215
216 ImGui::TreePop();
217 ImGui::PopStyleVar();
218 }
219 if(open_popup)
220 {
221 ImGui::OpenPopup(popup_str);
222 }
223
224 bool is_popup_open = ImGui::IsPopupOpen(popup_str);
225 if(is_popup_open && ImGui::BeginPopupContextWindowEx(popup_str))
226 {
227 bool removal_allowed = callbacks.can_remove();
228 if(ImGui::MenuItem("Reset", nullptr, false, removal_allowed))
229 {
230 callbacks.on_remove();
231 callbacks.on_add();
232
233 result.changed = true;
234 result.edit_finished = true;
235 }
236
237 ImGui::Separator();
238 if(ImGui::MenuItem("Remove Component", nullptr, false, removal_allowed))
239 {
240 callbacks.on_remove();
241 result.changed = true;
242 result.edit_finished = true;
243 }
244
245 ImGui::EndPopup();
246 }
247
248 ImGui::PopID();
249 if(!opened)
250 {
251 callbacks.on_remove();
252 result.changed = true;
253 result.edit_finished = true;
254 }
255
256 return result;
257}
258
259auto list_component(ImGuiTextFilter& filter, const std::string& name, const inspect_callbacks& callbacks)
260 -> inspect_result
261{
262 inspect_result result{};
263 if(!filter.PassFilter(name.c_str()))
264 {
265 return result;
266 }
267
268 if(ImGui::Selectable(fmt::format("{} {}", callbacks.icon, name).c_str()))
269 {
270 callbacks.on_remove();
271 callbacks.on_add();
272
273 result.changed = true;
274 result.edit_finished = true;
275
276 ImGui::CloseCurrentPopup();
277 }
278 return result;
279}
280auto get_entity_pretty_name(entt::handle entity) -> const std::string&
281{
282 if(!entity)
283 {
284 static const std::string empty = "None (Entity)";
285 return empty;
286 }
287 auto& tag = entity.get_or_emplace<tag_component>();
288 return tag.name;
289}
290
291auto process_drag_drop_target(rtti::context& ctx, entt::handle& obj) -> bool
292{
293 if(ImGui::IsDragDropPossibleTargetForType("entity"))
294 {
295 ImGui::SetItemFocusFrame(ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 0.0f, 1.0f)));
296 }
297
298 bool result = false;
299
300 if(ImGui::BeginDragDropTarget())
301 {
302 if(ImGui::IsDragDropPayloadBeingAccepted())
303 {
304 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
305 }
306 else
307 {
308 ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
309 }
310
311 {
312 auto payload = ImGui::AcceptDragDropPayload("entity");
313 if(payload != nullptr)
314 {
315 entt::handle dropped{};
316 std::memcpy(&dropped, payload->Data, size_t(payload->DataSize));
317 if(dropped)
318 {
319 obj = dropped;
320 result = true;
321 }
322 }
323 }
324
325 ImGui::EndDragDropTarget();
326 }
327
328 return result;
329}
330
331auto render_entity_header(rtti::context& ctx, entt::handle data, prefab_override_context& override_ctx) -> inspect_result
332{
333 inspect_result result{};
334
335 if(!data)
336 {
337 return result;
338 }
339
340 auto tag_comp = data.try_get<tag_component>();
341 auto trans_comp = data.try_get<transform_component>();
342
343 if(!tag_comp)
344 {
345 return result;
346 }
347
348 // Create a table for proper alignment
349 if(ImGui::BeginTable("EntityHeader", 3, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoClip))
350 {
351 ImGui::TableSetupColumn("Active", ImGuiTableColumnFlags_WidthFixed, 20.0f);
352 ImGui::TableSetupColumn("Icon", ImGuiTableColumnFlags_WidthFixed, 22.0f);
353 ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
354
355 ImGui::TableNextRow();
356
357 // Active checkbox column
358 ImGui::TableSetColumnIndex(0);
359 if(trans_comp)
360 {
361 bool is_active = trans_comp->is_active();
362
363 // Track component type for prefab override context
364 auto type = entt::resolve<transform_component>();
365 auto name = entt::get_name(type);
366 auto pretty_name = entt::get_pretty_name(type);
367 auto prop = type.data("active"_hs);
368 auto prop_name = entt::get_name(prop);
369 auto prop_pretty_name = entt::get_pretty_name(prop);
370
371 override_ctx.set_component_type(name, pretty_name);
372 override_ctx.push_segment(prop_name, prop_pretty_name);
373
374 bool old_active = is_active;
375 if(ImGui::Checkbox("##active", &is_active))
376 {
377 result.changed = true;
378 result.edit_finished = true;
379
380 auto& em = ctx.get_cached<editing_manager>();
381 em.do_action<entity_set_active_action_t>({},
382 data,
383 old_active,
384 is_active);
385 }
386
387 override_ctx.pop_segment();
388 }
389
391 ImGui::PushStyleColor(ImGuiCol_Text, col);
392 // Icon column
393 ImGui::TableSetColumnIndex(1);
394 ImGui::AlignTextToFramePadding();
395 {
397
398 ImGui::Text("%s", icon.c_str());
399 }
400 // Name field column
401 ImGui::TableSetColumnIndex(2);
402 {
403 auto type = entt::resolve<tag_component>();
404 auto type_name = entt::get_name(type);
405 auto pretty_name = entt::get_pretty_name(type);
406 auto prop = type.data("name"_hs);
407 auto prop_name = entt::get_name(prop);
408 auto prop_pretty_name = entt::get_pretty_name(prop);
409
410 override_ctx.set_component_type(type_name, pretty_name);
411 override_ctx.push_segment(prop_name, prop_pretty_name);
412
413 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
414 ImGui::SetNextItemWidth(-1.0f);
415
416 auto old_name = tag_comp->name;
417 if(ImGui::InputTextWidget("##name", tag_comp->name, false))
418 {
419 result.changed = true;
420 result.edit_finished = true;
421
422 auto& em = ctx.get_cached<editing_manager>();
423
424 em.do_action<entity_set_name_action_t>({},
425 data,
426 old_name,
427 tag_comp->name);
428 }
429
430 ImGui::PopStyleVar();
431 override_ctx.pop_segment();
432 }
433 ImGui::PopStyleColor();
434
435 ImGui::EndTable();
436 }
437
438 // Tag field using traditional property_layout approach
439 {
440 auto type = entt::resolve<tag_component>();
441 auto type_name = entt::get_name(type);
442 auto pretty_name = entt::get_pretty_name(type);
443
444 auto prop = type.data("tag"_hs);
445 auto prop_name = entt::get_name(prop);
446 auto prop_pretty_name = entt::get_pretty_name(prop);
447
448 override_ctx.set_component_type(type_name, pretty_name);
449 override_ctx.push_segment(prop_name, prop_pretty_name);
450
451 property_layout layout(prop, true);
452
453 var_info info;
454 info.is_property = true;
455 info.read_only = false;
456
457 auto old_tag = tag_comp->tag;
458
459 auto v_var = entt::forward_as_meta(tag_comp->tag);
460 auto var_result = ::unravel::inspect_var(ctx, v_var, make_proxy(v_var), info);
461
462 if(var_result.changed)
463 {
464 auto& em = ctx.get_cached<editing_manager>();
465
466 em.do_action<entity_set_tag_action_t>({},
467 data,
468 old_tag,
469 tag_comp->tag);
470
471 }
472
473 result |= var_result;
474
475 override_ctx.pop_segment();
476 }
477
478 return result;
479}
480
481} // namespace
482
484{
485 auto name = get_entity_pretty_name(data);
486
487 inspect_result result;
488
489 if(ImGui::Button(ICON_MDI_DELETE, ImVec2(0.0f, ImGui::GetFrameHeight())))
490 {
491 if(data)
492 {
493 data = {};
494 result.changed = true;
495 result.edit_finished = true;
496 }
497 }
498
499 ImGui::SameLine();
500 auto id = fmt::format("{} {}", ICON_MDI_CUBE, name);
501 if(ImGui::Button(id.c_str(), ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetFrameHeight())))
502 {
503 auto& em = ctx.get_cached<editing_manager>();
504
505 em.focus(data);
506 }
507
508 ImGui::SetItemTooltipEx("%s", id.c_str());
509
510 bool drag_dropped = process_drag_drop_target(ctx, data);
511 result.changed |= drag_dropped;
512 result.edit_finished |= drag_dropped;
513
514 return result;
515}
516
518 entt::meta_any& var,
519 const meta_any_proxy& var_proxy,
520 const var_info& info,
521 const entt::meta_custom& custom) -> inspect_result
522{
523 inspect_result result{};
524 auto data = var.cast<entt::handle>();
525
526 if(info.is_property)
527 {
528 result = inspect_as_property(ctx, data);
529 }
530 else
531 {
532 if(!data)
533 {
534 return result;
535 }
536
537 auto& override_ctx = ctx.get_cached<prefab_override_context>();
538
539 // Render Unity-style entity header (active checkbox, icon, name, tag)
540 result |= render_entity_header(ctx, data, override_ctx);
541
542 ImGui::Spacing();
543 ImGui::Separator();
544 ImGui::Spacing();
545
546 if(is_debug_view())
547 {
548 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 8.0f);
549 ImGui::TreePush("Entity");
550 {
551 property_layout layout("Entity");
552 const auto ent = data.entity();
553 const auto idx = entt::to_entity(ent);
554 const auto ver = entt::to_version(ent);
555 const auto id = entt::to_integral(ent);
556
557 //ImGui::SetItemTooltipEx("Id: %d\nIndex: %d\nVersion: %d", id, idx, ver);
558 ImGui::Text("Id: %u, Index: %u, Version: %u", id, idx, ver);
559 }
560
561 ImGui::TreePop();
562 ImGui::PopStyleVar();
563 }
564
565 hpp::for_each_tuple_type<all_inspectable_components>(
566 [&](auto index)
567 {
568 using ctype = std::tuple_element_t<decltype(index)::value, all_inspectable_components>;
569 auto component = data.try_get<ctype>();
570
571 if(!component)
572 {
573 return;
574 }
575
576 // Skip tag_component as it's handled in the Unity-style header
577 if constexpr(std::is_same_v<ctype, tag_component>)
578 {
579 return;
580 }
581
582 auto type = entt::resolve<ctype>();
583 auto name = entt::get_name(type);
584 auto pretty_name = entt::get_pretty_name(type);
585
586 // Track component type for prefab override context
587 override_ctx.set_component_type(std::string(name), pretty_name);
588
589
590 inspect_callbacks callbacks;
591
592 callbacks.on_inspect = [&]() -> inspect_result
593 {
594
595 if constexpr(std::is_base_of<owned_component, ctype>::value)
596 {
597 if(is_debug_view())
598 {
599 property_layout layout("Owner");
600 ImGui::Text("%u", uint32_t(component->get_owner().entity()));
601 }
602 }
603
604 meta_any_proxy comp_var_proxy;
605 comp_var_proxy.impl->get_name = [parent_proxy = var_proxy, pretty_name]()
606 {
607 auto name = parent_proxy.impl->get_name();
608 if(name.empty())
609 {
610 return pretty_name;
611 }
612 return fmt::format("{}/{}", name, pretty_name);
613 };
614 comp_var_proxy.impl->getter = [parent_proxy = var_proxy](entt::meta_any& result)
615 {
616 entt::meta_any var;
617 if(parent_proxy.impl->getter(var) && var)
618 {
619 auto data = var.cast<entt::handle>();
620 if(data)
621 {
622 auto component = data.try_get<ctype>();
623 if(component)
624 {
625 result = entt::forward_as_meta(*component);
626 return true;
627 }
628 }
629 }
630 return false;
631 };
632 comp_var_proxy.impl->setter = [parent_proxy = var_proxy](meta_any_proxy& proxy, const entt::meta_any& value, uint64_t execution_count) mutable
633 {
634 return parent_proxy.impl->setter(parent_proxy, value, execution_count);
635 };
636 // entt::meta_any comp_var;
637 // call_var_getter(comp_var, comp_var_getter);
638 auto comp_var = entt::forward_as_meta(*component);
639
640 var_info comp_info;
641 comp_info.is_copyable = false;
642 return ::unravel::inspect_var(ctx, comp_var, comp_var_proxy, comp_info);
643
644 };
645
646 callbacks.on_add = [&]()
647 {
648 // data.emplace<ctype>();
649
650 auto& em = ctx.get_cached<editing_manager>();
652 };
653
654 callbacks.on_remove = [&]()
655 {
656 if(data.all_of<ctype>())
657 {
658 auto& em = ctx.get_cached<editing_manager>();
660 }
661
662 };
663
664 callbacks.can_remove = []()
665 {
666 return !std::is_same<ctype, id_component>::value && !std::is_same<ctype, tag_component>::value &&
667 !std::is_same<ctype, transform_component>::value && !std::is_same<ctype, prefab_id_component>::value &&
668 !std::is_same<ctype, layer_component>::value && !std::is_same<ctype, bone_component>::value &&
669 !std::is_same<ctype, submesh_component>::value;
670 };
671
672 callbacks.can_merge = []()
673 {
674 return std::is_same<ctype, id_component>::value || std::is_same<ctype, tag_component>::value;
675 };
676
677
678 callbacks.icon = get_component_icon<ctype>();
679
680 result |= inspect_component(pretty_name, callbacks);
681 });
682
683 auto script_comp = data.try_get<script_component>();
684 if(script_comp)
685 {
686 const auto& comps = script_comp->get_script_components();
687
688 int index_to_remove = -1;
689 int index_to_add = -1;
690 for(size_t i = 0; i < comps.size(); ++i)
691 {
692 ImGui::PushID(i);
693 const auto& script = comps[i];
694 const auto& type = script.scoped->object.get_type();
695 fs::path source_loc = script_comp->get_script_source_location(script);
696
697 auto name = type.get_fullname();
698 const auto& pretty_name = name;
699
700 inspect_callbacks callbacks;
701 callbacks.on_inspect = [&]() -> inspect_result
702 {
703 inspect_result inspect_res{};
704
705 if(!source_loc.empty())
706 {
707 var_info field_info;
708 field_info.is_property = true;
709 field_info.read_only = true;
710 ImGui::PushReadonly(field_info.read_only);
711
712 std::string var = ICON_MDI_SCRIPT " " + source_loc.stem().string();
713 {
714 property_layout layout("Script");
715
716 if(ImGui::Button(var.c_str(), ImVec2(-1.0f, ImGui::GetFrameHeight())))
717 {
718 auto& em = ctx.get_cached<editing_manager>();
719
720 em.focus(source_loc);
721 em.focus_path(source_loc.parent_path());
722 }
723
724 if(ImGui::IsItemDoubleClicked(ImGuiMouseButton_Left))
725 {
727 }
728 }
730 }
731
732 meta_any_proxy obj_proxy;
733 obj_proxy.impl->get_name = [parent_proxy = var_proxy, pretty_name]()
734 {
735 auto name = parent_proxy.impl->get_name();
736 if(name.empty())
737 {
738 return pretty_name;
739 }
740 return fmt::format("{}/{}", name, pretty_name);
741 };
742 obj_proxy.impl->getter = [parent_proxy = var_proxy, i](entt::meta_any& result)
743 {
744 entt::meta_any var;
745 if(parent_proxy.impl->getter(var) && var)
746 {
747 auto data = var.cast<entt::handle>();
748 if(data)
749 {
750 auto script_comp = data.try_get<script_component>();
751 if(script_comp)
752 {
753 const auto& comps = script_comp->get_script_components();
754 if(i < comps.size())
755 {
756 auto& script = comps[i];
757 result = entt::forward_as_meta(*script.scoped);
758 return true;
759 }
760 }
761 }
762 }
763 return false;
764 };
765 obj_proxy.impl->setter = [parent_proxy = var_proxy](meta_any_proxy& proxy, const entt::meta_any& value, uint64_t execution_count) mutable
766 {
767 return parent_proxy.impl->setter(parent_proxy, value, execution_count);
768 };
769 // entt::meta_any obj_var;
770 // call_var_getter(obj_var, obj_getter);
771 auto obj_var = entt::forward_as_meta(*script.scoped);
772
773 var_info obj_info;
774 obj_info.is_copyable = false;
775
776 inspect_res |= ::unravel::inspect_var(ctx, obj_var, obj_proxy, obj_info);
777 return inspect_res;
778 };
779
780 callbacks.on_add = [&]()
781 {
782 index_to_add = i;
783 };
784
785 callbacks.on_remove = [&]()
786 {
787 index_to_remove = i;
788 };
789
790 callbacks.can_remove = []()
791 {
792 return true;
793 };
794
795 callbacks.can_merge = []()
796 {
797 return false;
798 };
799
800 callbacks.icon = ICON_MDI_SCRIPT;
801
802
803 auto script_type = entt::resolve<script_component>();
804 auto script_type_name = entt::get_name(script_type);
805 auto script_type_pretty_name = entt::get_pretty_name(script_type);
806 // Track component type for prefab override context
807 override_ctx.set_component_type(std::string(script_type_name), script_type_pretty_name);
808
809 override_ctx.push_segment("script_components/" + name, "Scripts/" + pretty_name);
810
811 result |= inspect_component(pretty_name, callbacks);
812
813 override_ctx.pop_segment();
814
815 ImGui::PopID();
816 }
817
818 if(index_to_remove != -1)
819 {
820 auto comp_to_remove = comps[index_to_remove];
821
823
824 auto type = comp_to_remove.scoped->object.get_type();
825
826 auto& em = ctx.get_cached<editing_manager>();
827 auto script_type_name = type.get_fullname();
828 em.do_action<entity_remove_script_component_action_t>({}, data, script_type_name);
829
830 // script_comp->remove_script_component(comp_to_remove.scoped->object);
831 // script_comp->process_pending_deletions();
832
833 if(index_to_add != -1)
834 {
835 // script_comp->add_script_component(type);
836
837 em.do_action<entity_add_script_component_action_t>({}, data, script_type_name);
838 }
839
840 result.changed |= true;
841 result.edit_finished |= true;
842 }
843 }
844
845 ImGui::Separator();
847 static const auto label = "Add Component";
848 auto avail = ImGui::GetContentRegionAvail();
849 ImVec2 size = ImGui::CalcItemSize(label);
850 size.x *= 2.0f;
851 ImGui::AlignedItem(0.5f,
852 avail.x,
853 size.x,
854 [&]()
855 {
856 auto pos = ImGui::GetCursorScreenPos();
857 if(ImGui::Button(label, size))
858 {
859 ImGui::OpenPopup("COMPONENT_MENU");
860 ImGui::SetNextWindowPos(pos);
861 }
862 });
863
864 if(ImGui::BeginPopup("COMPONENT_MENU"))
865 {
866 if(ImGui::IsWindowAppearing())
867 {
868 ImGui::SetKeyboardFocusHere();
869 }
870
871 ImGui::DrawFilterWithHint(filter_, ICON_MDI_SELECT_SEARCH " Search...", size.x);
872 ImGui::DrawItemActivityOutline();
873
874 ImGui::Separator();
875 ImGui::BeginChild("COMPONENT_MENU_CONTEXT", ImVec2(ImGui::GetContentRegionAvail().x, size.x));
876
877 const auto& scr = ctx.get_cached<script_system>();
878 for(const auto& type : scr.get_all_scriptable_components())
879 {
880 const auto& name = type.get_fullname();
881
882 inspect_callbacks callbacks;
883
884 callbacks.on_add = [&]()
885 {
886 //data.get_or_emplace<script_component>().add_script_component(type);
887 auto& em = ctx.get_cached<editing_manager>();
889 result.changed |= true;
890 result.edit_finished |= true;
891 };
892
893 callbacks.on_remove = [&]()
894 {
895 };
896
897 callbacks.can_remove = []()
898 {
899 return true;
900 };
901
902 callbacks.can_merge = []()
903 {
904 return false;
905 };
906
907 callbacks.icon = ICON_MDI_SCRIPT;
908
909 result |= list_component(filter_, name, callbacks);
910 }
911
912 hpp::for_each_tuple_type<all_addable_components>(
913 [&](auto index)
914 {
915 using ctype = std::tuple_element_t<decltype(index)::value, all_addable_components>;
916
917 auto name = entt::get_pretty_name(entt::resolve<ctype>());
918
919 auto type = entt::resolve<ctype>();
920
921 inspect_callbacks callbacks;
922
923 callbacks.on_add = [&]()
924 {
925 // data.emplace<ctype>();
926 auto& em = ctx.get_cached<editing_manager>();
928
929
930 result.changed |= true;
931 result.edit_finished |= true;
932 };
933
934 callbacks.on_remove = [&]()
935 {
936 if(data.all_of<ctype>())
937 {
938 auto& em = ctx.get_cached<editing_manager>();
940 result.changed |= true;
941 result.edit_finished |= true;
942 }
943
944
945 };
946
947 callbacks.can_remove = []()
948 {
949 return true;
950 };
951
952 callbacks.can_merge = []()
953 {
954 return false;
955 };
956
957 // callbacks.icon = ICON_MDI_GRID;
958
959 result |= list_component(filter_, name, callbacks);
960 });
961
962 ImGui::EndChild();
963 ImGui::EndPopup();
964 }
965 }
966
967 if(result.changed)
968 {
969 if(data)
970 {
971 if(auto prefab = data.try_get<prefab_component>())
972 {
973 prefab->changed = true;
974 }
975 }
976
977 var = data;
978 }
979
980 return result;
981}
982} // namespace unravel
manifold_type type
static auto get_entity_icon(entt::handle entity) -> std::string
static auto get_entity_display_color(entt::handle entity) -> ImVec4
Manages ImGui layout for property inspection in the editor.
Definition inspector.h:19
Class that contains core data for audio listeners. There can only be one instance of it per scene.
std::string name
Definition hub.cpp:27
std::string tag
Definition hub.cpp:26
#define ICON_MDI_FILTER_OUTLINE
#define ICON_MDI_MIRROR
#define ICON_MDI_CUBE_OUTLINE
#define ICON_MDI_LIGHTBULB
#define ICON_MDI_AXIS_ARROW
#define ICON_MDI_VOLUME_HIGH
#define ICON_MDI_CAMERA
#define ICON_MDI_ANIMATION
#define ICON_MDI_ATOM
#define ICON_MDI_SHAPE_OUTLINE
#define ICON_MDI_LAYERS
#define ICON_MDI_IDENTIFIER
#define ICON_MDI_SHAPE
#define ICON_MDI_EAR_HEARING
#define ICON_MDI_WEATHER_SUNNY
#define ICON_MDI_BRIGHTNESS_5
#define ICON_MDI_REFLECT_HORIZONTAL
#define ICON_MDI_FILTER
#define ICON_MDI_SELECT_SEARCH
#define ICON_MDI_COG
#define ICON_MDI_DELETE
#define ICON_MDI_BONE
#define ICON_MDI_TAG
#define ICON_MDI_TEXT
#define ICON_MDI_BUG
#define ICON_MDI_SCRIPT
#define ICON_MDI_CUBE
const char * icon
std::function< bool()> can_merge
std::function< inspect_result()> on_inspect
std::function< void()> on_remove
std::function< bool()> can_remove
std::function< void()> on_add
void NextLine()
Definition imgui.h:211
void PopReadonly()
Definition imgui.cpp:658
void PushReadonly(bool _enabled)
Definition imgui.cpp:649
auto get_pretty_name(const meta_type &t) -> std::string
auto get_name(const meta_type &t) -> std::string
std::tuple< test_component, model_component, animation_component, camera_component, assao_component, tonemapping_component, fxaa_component, ssr_component, light_component, skylight_component, reflection_probe_component, physics_component, audio_source_component, audio_listener_component, text_component, ui_document_component > all_addable_components
std::tuple< tag_component, layer_component, prefab_component, prefab_id_component, transform_component, test_component, model_component, animation_component, bone_component, submesh_component, camera_component, assao_component, tonemapping_component, fxaa_component, ssr_component, light_component, skylight_component, reflection_probe_component, physics_component, audio_source_component, audio_listener_component, text_component, ui_document_component > all_inspectable_components
auto is_debug_view() -> bool
Checks if currently in debug view mode.
auto make_proxy(entt::meta_any &var, const std::string &name) -> meta_any_proxy
Creates a simple proxy for direct variable access.
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
float x
void do_action(const std::string &name, const std::function< void()> &action)
void focus(entt::meta_any object)
Selects an object. Can be anything.
static void open_workspace_on_file(const fs::path &file, int line=0)
Result of an inspection operation indicating what changes occurred.
Definition inspector.h:146
bool changed
Whether the value was modified during inspection.
Definition inspector.h:148
bool edit_finished
Whether user finished editing (e.g., released mouse or pressed enter)
Definition inspector.h:150
auto inspect(rtti::context &ctx, entt::meta_any &var, const meta_any_proxy &var_proxy, const var_info &info, const entt::meta_custom &custom) -> inspect_result override
auto inspect_as_property(rtti::context &ctx, entt::handle &data) -> inspect_result
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.
Global context for tracking prefab override changes during inspection.
Definition inspectors.h:94
Represents a generic prefab with a buffer for serialized data.
Definition prefab.h:18
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