2#include "entt/meta/meta.hpp"
17#include <hpp/type_name.hpp>
18#include <hpp/utility.hpp>
21#include <imgui/imgui.h>
22#include <imgui/imgui_internal.h>
29auto get_component_icon() -> std::string
32 if constexpr(std::is_same<T, id_component>::value)
36 else if constexpr(std::is_same<T, tag_component>::value)
40 else if constexpr(std::is_same<T, layer_component>::value)
44 else if constexpr(std::is_same<T, prefab_component>::value)
48 else if constexpr(std::is_same<T, prefab_id_component>::value)
53 else if constexpr(std::is_same<T, transform_component>::value)
58 else if constexpr(std::is_same<T, test_component>::value)
63 else if constexpr(std::is_same<T, model_component>::value)
67 else if constexpr(std::is_same<T, submesh_component>::value)
71 else if constexpr(std::is_same<T, camera_component>::value)
75 else if constexpr(std::is_same<T, text_component>::value)
80 else if constexpr(std::is_same<T, animation_component>::value)
84 else if constexpr(std::is_same<T, bone_component>::value)
89 else if constexpr(std::is_same<T, light_component>::value)
93 else if constexpr(std::is_same<T, skylight_component>::value)
97 else if constexpr(std::is_same<T, reflection_probe_component>::value)
102 else if constexpr(std::is_same<T, physics_component>::value)
107 else if constexpr(std::is_same<T, audio_source_component>::value)
111 else if constexpr(std::is_same<T, audio_listener_component>::value)
116 else if constexpr(std::is_same<T, script_component>::value)
121 else if constexpr(std::is_same<T, tonemapping_component>::value)
125 else if constexpr(std::is_same<T, fxaa_component>::value)
129 else if constexpr(std::is_same<T, assao_component>::value)
133 else if constexpr(std::is_same<T, ssr_component>::value)
144struct inspect_callbacks
155auto inspect_component(
const std::string&
name,
const inspect_callbacks& callbacks) -> inspect_result
157 inspect_result result{};
161 ImGui::PushID(
name.c_str());
163 auto popup_str =
"COMPONENT_SETTING";
165 bool open_popup =
false;
167 if(!callbacks.can_merge())
169 ImGui::SetNextItemOpen(
true, ImGuiCond_FirstUseEver);
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);
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);
180 ImGui::PushStyleColor(ImGuiCol_Header, col_framebg);
181 ImGui::PushStyleColor(ImGuiCol_HeaderHovered, col_framebg_hovered);
182 ImGui::PushStyleColor(ImGuiCol_HeaderActive, col_framebg_active);
184 open = ImGui::CollapsingHeader(fmt::format(
" {}",
name).c_str(),
nullptr, ImGuiTreeNodeFlags_AllowOverlap);
186 ImGui::OpenPopupOnItemClick(popup_str);
187 ImGui::PopStyleColor(3);
189 ImGui::SetCursorPos(pos);
190 ImGui::AlignTextToFramePadding();
191 ImGui::Text(
" %s", callbacks.icon.c_str());
194 auto settings_size = ImGui::CalcTextSize(
ICON_MDI_COG).x + ImGui::GetStyle().FramePadding.x * 2.0f;
196 auto avail = ImGui::GetContentRegionAvail().x + ImGui::GetStyle().FramePadding.x;
197 ImGui::AlignedItem(1.0f,
211 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 8.0f);
212 ImGui::TreePush(
name.c_str());
214 result |= callbacks.on_inspect();
217 ImGui::PopStyleVar();
221 ImGui::OpenPopup(popup_str);
224 bool is_popup_open = ImGui::IsPopupOpen(popup_str);
225 if(is_popup_open && ImGui::BeginPopupContextWindowEx(popup_str))
227 bool removal_allowed = callbacks.can_remove();
228 if(ImGui::MenuItem(
"Reset",
nullptr,
false, removal_allowed))
230 callbacks.on_remove();
233 result.changed =
true;
234 result.edit_finished =
true;
238 if(ImGui::MenuItem(
"Remove Component",
nullptr,
false, removal_allowed))
240 callbacks.on_remove();
241 result.changed =
true;
242 result.edit_finished =
true;
251 callbacks.on_remove();
252 result.changed =
true;
253 result.edit_finished =
true;
259auto list_component(ImGuiTextFilter& filter,
const std::string&
name,
const inspect_callbacks& callbacks)
262 inspect_result result{};
263 if(!filter.PassFilter(
name.c_str()))
268 if(ImGui::Selectable(fmt::format(
"{} {}", callbacks.icon,
name).c_str()))
270 callbacks.on_remove();
273 result.changed =
true;
274 result.edit_finished =
true;
276 ImGui::CloseCurrentPopup();
280auto get_entity_pretty_name(entt::handle
entity) ->
const std::string&
284 static const std::string empty =
"None (Entity)";
287 auto&
tag =
entity.get_or_emplace<tag_component>();
291auto process_drag_drop_target(
rtti::context& ctx, entt::handle& obj) ->
bool
293 if(ImGui::IsDragDropPossibleTargetForType(
"entity"))
295 ImGui::SetItemFocusFrame(ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 0.0f, 1.0f)));
300 if(ImGui::BeginDragDropTarget())
302 if(ImGui::IsDragDropPayloadBeingAccepted())
304 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
308 ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
312 auto payload = ImGui::AcceptDragDropPayload(
"entity");
313 if(payload !=
nullptr)
315 entt::handle dropped{};
316 std::memcpy(&dropped, payload->Data,
size_t(payload->DataSize));
325 ImGui::EndDragDropTarget();
331auto render_entity_header(
rtti::context& ctx, entt::handle data, prefab_override_context& override_ctx) -> inspect_result
333 inspect_result result{};
340 auto tag_comp = data.try_get<tag_component>();
341 auto trans_comp = data.try_get<transform_component>();
349 if(ImGui::BeginTable(
"EntityHeader", 3, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoClip))
351 ImGui::TableSetupColumn(
"Active", ImGuiTableColumnFlags_WidthFixed, 20.0f);
352 ImGui::TableSetupColumn(
"Icon", ImGuiTableColumnFlags_WidthFixed, 22.0f);
353 ImGui::TableSetupColumn(
"Name", ImGuiTableColumnFlags_WidthStretch);
355 ImGui::TableNextRow();
358 ImGui::TableSetColumnIndex(0);
361 bool is_active = trans_comp->is_active();
364 auto type = entt::resolve<transform_component>();
367 auto prop =
type.data(
"active"_hs);
371 override_ctx.set_component_type(
name, pretty_name);
372 override_ctx.push_segment(prop_name, prop_pretty_name);
374 bool old_active = is_active;
375 if(ImGui::Checkbox(
"##active", &is_active))
377 result.changed =
true;
378 result.edit_finished =
true;
380 auto& em = ctx.get_cached<editing_manager>();
381 em.do_action<entity_set_active_action_t>({},
387 override_ctx.pop_segment();
391 ImGui::PushStyleColor(ImGuiCol_Text, col);
393 ImGui::TableSetColumnIndex(1);
394 ImGui::AlignTextToFramePadding();
398 ImGui::Text(
"%s",
icon.c_str());
401 ImGui::TableSetColumnIndex(2);
403 auto type = entt::resolve<tag_component>();
406 auto prop =
type.data(
"name"_hs);
410 override_ctx.set_component_type(type_name, pretty_name);
411 override_ctx.push_segment(prop_name, prop_pretty_name);
413 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
414 ImGui::SetNextItemWidth(-1.0f);
416 auto old_name = tag_comp->name;
417 if(ImGui::InputTextWidget(
"##name", tag_comp->name,
false))
419 result.changed =
true;
420 result.edit_finished =
true;
422 auto& em = ctx.get_cached<editing_manager>();
424 em.do_action<entity_set_name_action_t>({},
430 ImGui::PopStyleVar();
431 override_ctx.pop_segment();
433 ImGui::PopStyleColor();
440 auto type = entt::resolve<tag_component>();
444 auto prop =
type.data(
"tag"_hs);
448 override_ctx.set_component_type(type_name, pretty_name);
449 override_ctx.push_segment(prop_name, prop_pretty_name);
451 property_layout layout(prop,
true);
454 info.is_property =
true;
455 info.read_only =
false;
457 auto old_tag = tag_comp->tag;
459 auto v_var = entt::forward_as_meta(tag_comp->tag);
462 if(var_result.changed)
464 auto& em = ctx.get_cached<editing_manager>();
466 em.do_action<entity_set_tag_action_t>({},
473 result |= var_result;
475 override_ctx.pop_segment();
485 auto name = get_entity_pretty_name(data);
489 if(ImGui::Button(
ICON_MDI_DELETE, ImVec2(0.0f, ImGui::GetFrameHeight())))
501 if(ImGui::Button(
id.c_str(), ImVec2(ImGui::GetContentRegionAvail().
x, ImGui::GetFrameHeight())))
508 ImGui::SetItemTooltipEx(
"%s",
id.c_str());
510 bool drag_dropped = process_drag_drop_target(ctx, data);
511 result.
changed |= drag_dropped;
524 auto data = var.cast<entt::handle>();
528 result = inspect_as_property(ctx, data);
540 result |= render_entity_header(ctx, data, override_ctx);
548 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 8.0f);
549 ImGui::TreePush(
"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);
558 ImGui::Text(
"Id: %u, Index: %u, Version: %u",
id, idx, ver);
562 ImGui::PopStyleVar();
565 hpp::for_each_tuple_type<all_inspectable_components>(
569 auto component = data.try_get<ctype>();
577 if constexpr(std::is_same_v<ctype, tag_component>)
582 auto type = entt::resolve<ctype>();
587 override_ctx.set_component_type(std::string(
name), pretty_name);
590 inspect_callbacks callbacks;
595 if constexpr(std::is_base_of<owned_component, ctype>::value)
600 ImGui::Text(
"%u", uint32_t(component->get_owner().entity()));
605 comp_var_proxy.
impl->get_name = [parent_proxy = var_proxy, pretty_name]()
607 auto name = parent_proxy.impl->get_name();
612 return fmt::format(
"{}/{}",
name, pretty_name);
614 comp_var_proxy.
impl->getter = [parent_proxy = var_proxy](entt::meta_any& result)
617 if(parent_proxy.impl->getter(var) && var)
619 auto data = var.cast<entt::handle>();
622 auto component = data.try_get<ctype>();
625 result = entt::forward_as_meta(*component);
632 comp_var_proxy.
impl->setter = [parent_proxy = var_proxy](
meta_any_proxy& proxy,
const entt::meta_any& value, uint64_t execution_count)
mutable
634 return parent_proxy.impl->setter(parent_proxy, value, execution_count);
638 auto comp_var = entt::forward_as_meta(*component);
642 return ::unravel::inspect_var(ctx, comp_var, comp_var_proxy, comp_info);
646 callbacks.on_add = [&]()
654 callbacks.on_remove = [&]()
656 if(data.all_of<ctype>())
664 callbacks.can_remove = []()
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;
672 callbacks.can_merge = []()
674 return std::is_same<ctype, id_component>::value || std::is_same<ctype, tag_component>::value;
678 callbacks.icon = get_component_icon<ctype>();
680 result |= inspect_component(pretty_name, callbacks);
686 const auto& comps = script_comp->get_script_components();
688 int index_to_remove = -1;
689 int index_to_add = -1;
690 for(
size_t i = 0; i < comps.size(); ++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);
698 const auto& pretty_name =
name;
700 inspect_callbacks callbacks;
705 if(!source_loc.empty())
716 if(ImGui::Button(var.c_str(), ImVec2(-1.0f, ImGui::GetFrameHeight())))
720 em.
focus(source_loc);
721 em.focus_path(source_loc.parent_path());
724 if(ImGui::IsItemDoubleClicked(ImGuiMouseButton_Left))
733 obj_proxy.
impl->get_name = [parent_proxy = var_proxy, pretty_name]()
735 auto name = parent_proxy.impl->get_name();
740 return fmt::format(
"{}/{}",
name, pretty_name);
742 obj_proxy.
impl->getter = [parent_proxy = var_proxy, i](entt::meta_any& result)
745 if(parent_proxy.impl->getter(var) && var)
747 auto data = var.cast<entt::handle>();
753 const auto& comps = script_comp->get_script_components();
757 result = entt::forward_as_meta(*
script.scoped);
765 obj_proxy.
impl->setter = [parent_proxy = var_proxy](
meta_any_proxy& proxy,
const entt::meta_any& value, uint64_t execution_count)
mutable
767 return parent_proxy.impl->setter(parent_proxy, value, execution_count);
771 auto obj_var = entt::forward_as_meta(*
script.scoped);
780 callbacks.on_add = [&]()
785 callbacks.on_remove = [&]()
790 callbacks.can_remove = []()
795 callbacks.can_merge = []()
803 auto script_type = entt::resolve<script_component>();
807 override_ctx.set_component_type(std::string(script_type_name), script_type_pretty_name);
809 override_ctx.push_segment(
"script_components/" +
name,
"Scripts/" + pretty_name);
811 result |= inspect_component(pretty_name, callbacks);
813 override_ctx.pop_segment();
818 if(index_to_remove != -1)
820 auto comp_to_remove = comps[index_to_remove];
824 auto type = comp_to_remove.
scoped->object.get_type();
827 auto script_type_name =
type.get_fullname();
833 if(index_to_add != -1)
840 result.changed |=
true;
841 result.edit_finished |=
true;
847 static const auto label =
"Add Component";
848 auto avail = ImGui::GetContentRegionAvail();
849 ImVec2
size = ImGui::CalcItemSize(label);
851 ImGui::AlignedItem(0.5f,
856 auto pos = ImGui::GetCursorScreenPos();
857 if(ImGui::Button(label, size))
859 ImGui::OpenPopup(
"COMPONENT_MENU");
860 ImGui::SetNextWindowPos(pos);
864 if(ImGui::BeginPopup(
"COMPONENT_MENU"))
866 if(ImGui::IsWindowAppearing())
868 ImGui::SetKeyboardFocusHere();
872 ImGui::DrawItemActivityOutline();
875 ImGui::BeginChild(
"COMPONENT_MENU_CONTEXT", ImVec2(ImGui::GetContentRegionAvail().
x,
size.x));
878 for(
const auto&
type : scr.get_all_scriptable_components())
880 const auto&
name =
type.get_fullname();
882 inspect_callbacks callbacks;
884 callbacks.on_add = [&]()
889 result.changed |=
true;
890 result.edit_finished |=
true;
893 callbacks.on_remove = [&]()
897 callbacks.can_remove = []()
902 callbacks.can_merge = []()
909 result |= list_component(filter_,
name, callbacks);
912 hpp::for_each_tuple_type<all_addable_components>(
919 auto type = entt::resolve<ctype>();
921 inspect_callbacks callbacks;
923 callbacks.on_add = [&]()
930 result.changed |=
true;
931 result.edit_finished |=
true;
934 callbacks.on_remove = [&]()
936 if(data.all_of<ctype>())
940 result.changed |=
true;
941 result.edit_finished |=
true;
947 callbacks.can_remove = []()
952 callbacks.can_merge = []()
959 result |= list_component(filter_,
name, callbacks);
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.
Class that contains core data for audio listeners. There can only be one instance of it per scene.
#define ICON_MDI_FILTER_OUTLINE
#define ICON_MDI_CUBE_OUTLINE
#define ICON_MDI_LIGHTBULB
#define ICON_MDI_AXIS_ARROW
#define ICON_MDI_VOLUME_HIGH
#define ICON_MDI_ANIMATION
#define ICON_MDI_SHAPE_OUTLINE
#define ICON_MDI_IDENTIFIER
#define ICON_MDI_EAR_HEARING
#define ICON_MDI_WEATHER_SUNNY
#define ICON_MDI_BRIGHTNESS_5
#define ICON_MDI_REFLECT_HORIZONTAL
#define ICON_MDI_SELECT_SEARCH
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 PushReadonly(bool _enabled)
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.
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.
bool changed
Whether the value was modified during inspection.
bool edit_finished
Whether user finished editing (e.g., released mouse or pressed enter)
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
Component that holds a reference to a prefab asset and tracks property overrides.
Global context for tracking prefab override changes during inspection.
Represents a generic prefab with a buffer for serialized data.
Metadata about a variable being inspected.
bool is_property
Whether this is a property that can be overridden in prefabs.
bool read_only
Whether the variable should be displayed as read-only.