4#include "imgui/imgui.h"
5#include "imgui_widgets/tooltips.h"
6#include <imgui/imgui_internal.h>
34bool prev_edit_label{};
37auto update_editing() ->
void
39 prev_edit_label = edit_label_;
42auto is_just_started_editing_label() ->
bool
44 return edit_label_ && edit_label_ != prev_edit_label;
47auto is_editing_label() ->
bool
68void create_empty_entity(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
71 em.queue_action(
"Create Empty Entity",
72 [&ctx, panels, parent_entity]()
mutable
75 auto* active_scene = em.get_active_scene(ctx);
77 auto new_entity = active_scene->create_entity({}, parent_entity);
78 start_editing_label(ctx, panels, new_entity);
83void create_empty_parent_entity(
rtti::context& ctx, imgui_panels* panels, entt::handle child_entity)
86 em.queue_action(
"Create Empty Parent Entity",
87 [&ctx, panels, child_entity]()
mutable
89 auto current_parent = child_entity.get<transform_component>().get_parent();
91 auto* active_scene = em.get_active_scene(ctx);
94 auto new_entity = active_scene->create_entity({}, current_parent);
95 child_entity.get<transform_component>().set_parent(new_entity);
96 start_editing_label(ctx, panels, new_entity);
101void create_mesh_entity(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity,
const std::string& mesh_name)
104 em.queue_action(
"Create Mesh Entity",
105 [&ctx, panels, parent_entity, mesh_name]()
mutable
108 auto* active_scene = em.get_active_scene(ctx);
114 object.get<transform_component>().set_parent(parent_entity,
false);
117 start_editing_label(ctx, panels,
object);
122void create_text_entity(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
125 em.queue_action(
"Create Text Entity",
126 [&ctx, panels, parent_entity]()
mutable
129 auto* active_scene = em.get_active_scene(ctx);
134 object.get<transform_component>().set_parent(parent_entity,
false);
137 start_editing_label(ctx, panels,
object);
141void create_particle_emitter_entity(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
144 em.queue_action(
"Create Particle Emitter Entity",
145 [&ctx, panels, parent_entity]()
mutable
148 auto* active_scene = em.get_active_scene(ctx);
152 object.get<transform_component>().set_parent(parent_entity,
false);
155 start_editing_label(ctx, panels,
object);
162 em.queue_action(
"Create Light Entity",
163 [&ctx, panels, parent_entity,
type,
name]()
mutable
166 auto* active_scene = em.get_active_scene(ctx);
170 object.get<transform_component>().set_parent(parent_entity,
false);
173 start_editing_label(ctx, panels,
object);
180 em.queue_action(
"Create Reflection Probe Entity",
181 [&ctx, panels, parent_entity,
type,
name]()
mutable
184 auto* active_scene = em.get_active_scene(ctx);
188 object.get<transform_component>().set_parent(parent_entity,
false);
191 start_editing_label(ctx, panels,
object);
195void create_camera_entity(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
198 em.queue_action(
"Create Camera Entity",
199 [&ctx, panels, parent_entity]()
mutable
202 auto* active_scene = em.get_active_scene(ctx);
206 object.get<transform_component>().set_parent(parent_entity,
false);
209 start_editing_label(ctx, panels,
object);
213void create_audio_source_entity(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
216 em.queue_action(
"Create Audio Source Entity",
217 [&ctx, panels, parent_entity]()
mutable
220 auto* active_scene = em.get_active_scene(ctx);
224 object.get<transform_component>().set_parent(parent_entity,
false);
227 start_editing_label(ctx, panels,
object);
231void create_ui_document_entity(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
234 em.queue_action(
"Create UI Document Entity",
235 [&ctx, panels, parent_entity]()
mutable
238 auto* active_scene = em.get_active_scene(ctx);
242 object.get<transform_component>().set_parent(parent_entity,
false);
245 start_editing_label(ctx, panels,
object);
253auto process_drag_drop_source(entt::handle
entity) ->
bool
255 if(
entity && ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID))
258 ImGui::SetDragDropPayload(
"entity", &
entity,
sizeof(
entity));
259 ImGui::EndDragDropSource();
266void handle_entity_drop(
rtti::context& ctx, imgui_panels* panels, entt::handle target_entity, entt::handle dropped_entity)
270 auto do_action = [&](entt::handle dropped)
273 em.queue_action(
"Drop Entity",
274 [&ctx, target_entity, dropped]()
mutable
276 auto trans_comp = dropped.try_get<transform_component>();
279 trans_comp->set_parent(target_entity);
284 if(em.is_selected(dropped_entity))
286 for(
auto e : em.try_get_selections_as<entt::handle>())
296 do_action(dropped_entity);
300void handle_mesh_drop(
rtti::context& ctx,
const std::string& absolute_path)
304 em.queue_action(
"Drop Mesh",
305 [&ctx, absolute_path]()
mutable
309 auto* active_scene = em.get_active_scene(ctx);
320void handle_prefab_drop(
rtti::context& ctx,
const std::string& absolute_path)
323 em.queue_action(
"Drop Prefab",
324 [&ctx, absolute_path]()
mutable
329 auto* active_scene = em.get_active_scene(ctx);
339void process_drag_drop_target(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity)
341 if(!ImGui::BeginDragDropTarget())
346 if(ImGui::IsDragDropPayloadBeingAccepted())
348 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
352 ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
356 auto payload = ImGui::AcceptDragDropPayload(
"entity");
357 if(payload !=
nullptr)
359 entt::handle dropped{};
360 std::memcpy(&dropped, payload->Data,
size_t(payload->DataSize));
363 handle_entity_drop(ctx, panels,
entity, dropped);
370 auto mesh_payload = ImGui::AcceptDragDropPayload(
type.c_str());
371 if(mesh_payload !=
nullptr)
373 std::string absolute_path(
reinterpret_cast<const char*
>(mesh_payload->Data), std::size_t(mesh_payload->DataSize));
374 handle_mesh_drop(ctx, absolute_path);
381 auto prefab_payload = ImGui::AcceptDragDropPayload(
type.c_str());
382 if(prefab_payload !=
nullptr)
384 std::string absolute_path(
reinterpret_cast<const char*
>(prefab_payload->Data), std::size_t(prefab_payload->DataSize));
385 handle_prefab_drop(ctx, absolute_path);
389 ImGui::EndDragDropTarget();
394 if(!process_drag_drop_source(
entity))
396 process_drag_drop_target(ctx, panels,
entity);
404void draw_3d_objects_menu(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
406 if(!ImGui::BeginMenu(
"3D Objects"))
411 static const std::vector<std::pair<std::string, std::vector<std::string>>> menu_objects = {
413 {
"Cube Rounded", {
"Cube Rounded"}},
414 {
"Sphere", {
"Sphere"}},
415 {
"Plane", {
"Plane"}},
416 {
"Cylinder", {
"Cylinder"}},
417 {
"Capsule_1m", {
"Capsule_1m"}},
418 {
"Capsule_2m", {
"Capsule_2m"}},
420 {
"Torus", {
"Torus"}},
421 {
"Teapot", {
"Teapot"}},
423 {
"Polygon", {
"Icosahedron",
"Dodecahedron"}},
424 {
"Icosphere", {
"Icosphere0",
"Icosphere1",
"Icosphere2",
"Icosphere3",
"Icosphere4",
425 "Icosphere5",
"Icosphere6",
"Icosphere7",
"Icosphere8",
"Icosphere9",
426 "Icosphere10",
"Icosphere11",
"Icosphere12",
"Icosphere13",
"Icosphere14",
427 "Icosphere15",
"Icosphere16",
"Icosphere17",
"Icosphere18",
"Icosphere19"}}};
429 for(
const auto& p : menu_objects)
431 const auto&
name =
p.first;
432 const auto& objects_name =
p.second;
434 if(
name ==
"Separator")
438 else if(
name ==
"New Line")
442 else if(objects_name.size() == 1)
444 if(ImGui::MenuItem(
name.c_str()))
446 create_mesh_entity(ctx, panels, parent_entity,
name);
451 if(ImGui::BeginMenu(
name.c_str()))
453 for(
const auto& n : objects_name)
455 if(ImGui::MenuItem(
n.c_str()))
457 create_mesh_entity(ctx, panels, parent_entity, n);
468 if(ImGui::MenuItem(
"Text"))
470 create_text_entity(ctx, panels, parent_entity);
476void draw_lighting_menu(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
478 if(!ImGui::BeginMenu(
"Lighting"))
484 if(ImGui::BeginMenu(
"Light"))
486 static const std::vector<std::pair<std::string, light_type>> light_objects = {
491 for(
const auto& p : light_objects)
493 const auto&
name =
p.first;
494 const auto&
type =
p.second;
495 if(ImGui::MenuItem(
name.c_str()))
497 create_light_entity(ctx, panels, parent_entity,
type,
name);
504 if(ImGui::BeginMenu(
"Reflection Probes"))
506 static const std::vector<std::pair<std::string, probe_type>> reflection_probes = {
510 for(
const auto& p : reflection_probes)
512 const auto&
name =
p.first;
513 const auto&
type =
p.second;
515 if(ImGui::MenuItem(
name.c_str()))
517 create_reflection_probe_entity(ctx, panels, parent_entity,
type,
name);
526void draw_common_menu_items(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
528 if(ImGui::MenuItem(
"Create Empty"))
530 create_empty_entity(ctx, panels, parent_entity);
533 draw_3d_objects_menu(ctx, panels, parent_entity);
534 draw_lighting_menu(ctx, panels, parent_entity);
536 if(ImGui::MenuItem(
"Camera"))
538 create_camera_entity(ctx, panels, parent_entity);
541 if(ImGui::MenuItem(
"Audio Source"))
543 create_audio_source_entity(ctx, panels, parent_entity);
547 if(ImGui::MenuItem(
"Particle Emitter"))
549 create_particle_emitter_entity(ctx, panels, parent_entity);
552 if(ImGui::MenuItem(
"UI Document"))
554 create_ui_document_entity(ctx, panels, parent_entity);
558void draw_entity_context_menu(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity)
560 if(!ImGui::BeginPopupContextItem(
"Entity Context Menu"))
565 if(ImGui::MenuItem(
"Create Empty Parent"))
567 create_empty_parent_entity(ctx, panels,
entity);
570 draw_common_menu_items(ctx, panels,
entity);
577 em.queue_action(
"Rename Entity",
578 [ctx, panels,
entity]()
mutable
580 start_editing_label(ctx, panels,
entity);
586 panels->get_scene_panel().duplicate_entities({
entity});
591 panels->get_scene_panel().delete_entities({
entity});
596 panels->get_scene_panel().focus_entities(panels->get_scene_panel().get_camera(), {entity});
601 if(
entity.any_of<prefab_component, prefab_id_component>())
603 if(ImGui::MenuItem(
"Open Prefab"))
606 em.queue_action(
"Open Prefab",
607 [&ctx,
entity, panels]()
mutable
612 auto prefab = prefab_root.get<prefab_component>().source;
616 em.enter_prefab_mode(ctx, prefab,
true);
622 if(ImGui::MenuItem(
"Unlink from Prefab"))
625 em.queue_action(
"Unlink from Prefab",
628 entity.remove<prefab_component, prefab_id_component>();
636void draw_window_context_menu(
rtti::context& ctx, imgui_panels* panels)
638 if(!ImGui::BeginPopupContextWindowEx())
643 draw_common_menu_items(ctx, panels, {});
649 ImGui::PushStyleColor(ImGuiCol_Separator, ImGui::GetStyleColorVec4(ImGuiCol_Text));
653 draw_entity_context_menu(ctx, panels,
entity);
657 draw_window_context_menu(ctx, panels);
660 ImGui::PopStyleColor();
667void draw_activity(
rtti::context& ctx, transform_component& trans_comp)
669 bool is_active_local = trans_comp.is_active();
672 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.5f, 1.0f));
677 trans_comp.set_active(!is_active_local);
679 auto entity = trans_comp.get_owner();
682 em.push_undo_stack_enabled(
true);
684 em.queue_action<entity_set_active_action_t>({},
689 em.pop_undo_stack_enabled();
695 ImGui::PopStyleColor();
702 auto focus = em.try_get_active_focus_as<entt::handle>();
714auto get_entity_tree_node_flags(
rtti::context& ctx, entt::handle
entity,
bool has_children) -> ImGuiTreeNodeFlags
717 ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_OpenOnArrow;
719 if(em.is_selected(
entity))
721 flags |= ImGuiTreeNodeFlags_Selected;
726 flags |= ImGuiTreeNodeFlags_Leaf;
729 flags |= ImGuiTreeNodeFlags_DrawLinesToNodes;
734auto get_entity_display_label(entt::handle
entity) -> std::string
739 const auto ent =
entity.entity();
740 const auto id = entt::to_integral(ent);
742 return icon +
name +
"###" + std::to_string(
id);
748 auto mode = em.get_select_mode();
749 em.queue_action(
"Select Entity",
750 [&ctx, panels,
entity, mode]()
mutable
752 stop_editing_label(ctx, panels,
entity);
758void handle_entity_keyboard_shortcuts(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity)
763 em.queue_action(
"Rename Entity",
764 [&ctx, panels,
entity]()
mutable
766 start_editing_label(ctx, panels,
entity);
772 panels->get_scene_panel().delete_entities({
entity});
777 panels->get_scene_panel().focus_entities(panels->get_scene_panel().get_camera(), {entity});
782 panels->get_scene_panel().duplicate_entities({
entity});
786void handle_entity_mouse_interactions(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity,
bool is_item_clicked_middle,
bool is_item_double_clicked_left)
788 if(is_item_clicked_middle)
790 panels->get_scene_panel().focus_entities(panels->get_scene_panel().get_camera(), {entity});
793 if(is_item_double_clicked_left)
795 panels->get_scene_panel().focus_entities(panels->get_scene_panel().get_camera(), {entity});
799void draw_entity_name_editor(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity,
const ImVec2& pos)
802 if(!em.is_selected(
entity) || !is_editing_label())
807 if(is_just_started_editing_label())
809 ImGui::SetKeyboardFocusHere();
812 ImGui::SetCursorScreenPos(pos);
813 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().
x);
816 auto old_name = edit_name;
817 ImGui::InputTextWidget(
"##rename", edit_name,
false, ImGuiInputTextFlags_AutoSelectAll);
819 if(ImGui::IsItemDeactivatedAfterEdit())
823 em.push_undo_stack_enabled(
true);
824 em.queue_action<entity_set_name_action_t>({},
828 em.pop_undo_stack_enabled();
829 stop_editing_label(ctx, panels,
entity);
832 ImGui::PopItemWidth();
834 if(ImGui::IsItemDeactivated())
836 stop_editing_label(ctx, panels,
entity);
848 ImGui::PushID(
static_cast<int>(
entity.entity()));
850 auto& trans_comp =
entity.get<transform_component>();
851 bool has_children = !trans_comp.get_children().empty();
853 ImGuiTreeNodeFlags flags = get_entity_tree_node_flags(ctx,
entity, has_children);
855 if(is_parent_of_focused(ctx,
entity))
857 ImGui::SetNextItemOpen(
true, 0);
858 ImGui::SetScrollHereY();
861 auto pos = ImGui::GetCursorScreenPos() + ImVec2(ImGui::GetTextLineHeightWithSpacing(), 0.0f);
862 ImGui::AlignTextToFramePadding();
864 auto label = get_entity_display_label(
entity);
867 ImGui::PushStyleColor(ImGuiCol_Text, col);
868 ImGui::PushStyleVarX(ImGuiStyleVar_ItemInnerSpacing, 0.0f);
869 bool opened = ImGui::TreeNodeEx(label.c_str(), flags);
870 ImGui::PopStyleVar();
872 if(ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
874 const auto ent =
entity.entity();
875 const auto idx = entt::to_entity(ent);
876 const auto ver = entt::to_version(ent);
877 const auto id = entt::to_integral(ent);
879 ImGui::SetItemTooltipEx(
"Id: %d\nIndex: %d\nVersion: %d",
id, idx, ver);
882 ImGui::PopStyleColor();
886 ImGui::SetItemFocusFrame(ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 0.0f, 1.0f)));
889 if(!is_editing_label())
891 check_drag(ctx, panels,
entity);
892 check_context_menu(ctx, panels,
entity);
896 bool is_item_focus_changed = ImGui::IsItemFocusChanged();
897 bool is_item_released_left = ImGui::IsItemReleased(ImGuiMouseButton_Left);
898 bool is_item_clicked_middle = ImGui::IsItemClicked(ImGuiMouseButton_Middle);
899 bool is_item_double_clicked_left = ImGui::IsItemDoubleClicked(ImGuiMouseButton_Left);
900 bool activity_hovered =
false;
903 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
904 ImGui::AlignedItem(1.0f,
905 ImGui::GetContentRegionAvail().
x - ImGui::GetStyle().FramePadding.x,
906 ImGui::GetFrameHeight(),
909 draw_activity(ctx, trans_comp);
910 activity_hovered = ImGui::IsItemHovered();
914 if(!activity_hovered)
916 if(is_item_released_left || is_item_focus_changed)
918 handle_entity_selection(ctx, panels,
entity);
921 if(em.is_selected(
entity))
923 handle_entity_mouse_interactions(ctx, panels,
entity, is_item_clicked_middle, is_item_double_clicked_left);
924 handle_entity_keyboard_shortcuts(ctx, panels,
entity);
929 draw_entity_name_editor(ctx, panels,
entity, pos);
936 const auto& children = trans_comp.get_children();
937 for(
auto& child : children)
941 draw_entity(ctx, panels, child);
966void hierarchy_panel::draw_prefab_mode_header(
rtti::context& ctx)
const
970 if(!em.is_prefab_mode())
975 ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonActive));
980 ImGui::PopStyleColor();
982 if (em.edited_prefab)
985 ImGui::Text(
"Editing Prefab: %s", fs::path(em.edited_prefab.id()).filename().string().c_str());
991auto hierarchy_panel::get_scene_display_name(
const editing_manager& em, scene* target_scene)
const -> std::string
995 if (em.is_prefab_mode())
997 name = fs::path(em.edited_prefab.id()).filename().string();
1005 name = target_scene->source.name();
1012 if(em.has_unsaved_changes())
1021void hierarchy_panel::draw_scene_hierarchy(
rtti::context& ctx)
const
1023 auto& em = ctx.
get_cached<editing_manager>();
1024 scene* target_scene = em.get_active_scene(ctx);
1031 std::string scene_name = get_scene_display_name(em, target_scene);
1033 ImGui::SetNextItemOpen(
true, ImGuiCond_Appearing);
1034 if(ImGui::CollapsingHeader(scene_name.c_str()))
1038 target_scene->registry->sort<root_component>(
1039 [](
auto const& lhs,
auto const& rhs)
1042 return lhs.order < rhs.order;
1049 target_scene->registry->view<root_component, transform_component>().each(
1050 [&](
auto e,
auto&& root,
auto&& comp)
1052 draw_entity(ctx,
parent_, comp.get_owner());
1056 handle_window_empty_click(ctx);
1059void hierarchy_panel::handle_window_empty_click(
rtti::context& ctx)
const
1061 auto& em = ctx.
get_cached<editing_manager>();
1062 if(ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
1064 if(!ImGui::IsAnyItemHovered())
1075 if(ImGui::Begin(
name))
1077 draw_prefab_mode_header(ctx);
1079 ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
1080 ImGuiWindowFlags_NoSavedSettings;
1082 if(ImGui::BeginChild(
"hierarchy_content", ImGui::GetContentRegionAvail(), 0, flags))
1084 check_context_menu(ctx,
parent_, {});
1085 draw_scene_hierarchy(ctx);
static auto get_entity_icon(entt::handle entity) -> std::string
static auto get_entity_name(entt::handle entity) -> std::string
Gets the entity name from tag component.
static auto get_entity_display_color(entt::handle entity) -> ImVec4
void on_frame_ui_render()
hierarchy_panel(imgui_panels *parent)
void init(rtti::context &ctx)
#define ICON_MDI_KEYBOARD_RETURN
auto get_type() -> const std::string &
auto get_suported_formats() -> const std::vector< std::string > &
path convert_to_protocol(const path &_path)
Oposite of the resolve_protocol this function tries to convert to protocol path from an absolute one.
constexpr ImGuiKey delete_item
const ImGuiKeyCombination duplicate_item
constexpr ImGuiKey rename_item
constexpr ImGuiKey focus_selected
auto is_roots_order_changed() -> bool
void reset_roots_order_changed()
probe_type
Enum class representing the type of reflection probe.
@ sphere
Sphere type reflection probe.
@ box
Box type reflection probe.
light_type
Enum representing the type of light.
static auto create_reflection_probe_entity(rtti::context &ctx, scene &scn, probe_type type, const std::string &name) -> entt::handle
Creates a reflection probe entity.
static auto create_ui_document_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a UI document entity.
static auto create_text_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a text entity.
static auto create_audio_source_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a audio source entity.
static auto create_embedded_mesh_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates an embedded mesh entity.
static auto create_particle_emitter_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a particle emitter entity.
static auto create_light_entity(rtti::context &ctx, scene &scn, light_type type, const std::string &name) -> entt::handle
Creates a light entity.
static auto create_prefab_at(rtti::context &ctx, scene &scn, const std::string &key, const camera &cam, math::vec2 pos) -> entt::handle
Creates a prefab entity at a specified position.
static auto create_camera_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a camera entity.
static auto create_mesh_entity_at(rtti::context &ctx, scene &scn, const std::string &key, const camera &cam, math::vec2 pos) -> entt::handle
Creates a mesh entity at a specified position.
static auto find_prefab_root_entity(entt::handle entity) -> entt::handle
Finds the prefab root entity by traversing up the parent hierarchy.