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);
144 em.queue_action(
"Create Light Entity",
145 [&ctx, panels, parent_entity,
type,
name]()
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 Reflection Probe 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);
177void create_camera_entity(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
180 em.queue_action(
"Create Camera Entity",
181 [&ctx, panels, parent_entity]()
mutable
184 auto* active_scene = em.get_active_scene(ctx);
187 start_editing_label(ctx, panels,
object);
191void create_ui_document_entity(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
194 em.queue_action(
"Create UI Document Entity",
195 [&ctx, panels, parent_entity]()
mutable
198 auto* active_scene = em.get_active_scene(ctx);
201 start_editing_label(ctx, panels,
object);
209auto process_drag_drop_source(entt::handle
entity) ->
bool
211 if(
entity && ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID))
214 ImGui::SetDragDropPayload(
"entity", &
entity,
sizeof(
entity));
215 ImGui::EndDragDropSource();
222void handle_entity_drop(
rtti::context& ctx, imgui_panels* panels, entt::handle target_entity, entt::handle dropped_entity)
226 auto do_action = [&](entt::handle dropped)
229 em.queue_action(
"Drop Entity",
230 [&ctx, target_entity, dropped]()
mutable
232 auto trans_comp = dropped.try_get<transform_component>();
235 trans_comp->set_parent(target_entity);
240 if(em.is_selected(dropped_entity))
242 for(
auto e : em.try_get_selections_as<entt::handle>())
252 do_action(dropped_entity);
256void handle_mesh_drop(
rtti::context& ctx,
const std::string& absolute_path)
260 em.queue_action(
"Drop Mesh",
261 [&ctx, absolute_path]()
mutable
265 auto* active_scene = em.get_active_scene(ctx);
276void handle_prefab_drop(
rtti::context& ctx,
const std::string& absolute_path)
279 em.queue_action(
"Drop Prefab",
280 [&ctx, absolute_path]()
mutable
285 auto* active_scene = em.get_active_scene(ctx);
295void process_drag_drop_target(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity)
297 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));
319 handle_entity_drop(ctx, panels,
entity, dropped);
326 auto mesh_payload = ImGui::AcceptDragDropPayload(
type.c_str());
327 if(mesh_payload !=
nullptr)
329 std::string absolute_path(
reinterpret_cast<const char*
>(mesh_payload->Data), std::size_t(mesh_payload->DataSize));
330 handle_mesh_drop(ctx, absolute_path);
337 auto prefab_payload = ImGui::AcceptDragDropPayload(
type.c_str());
338 if(prefab_payload !=
nullptr)
340 std::string absolute_path(
reinterpret_cast<const char*
>(prefab_payload->Data), std::size_t(prefab_payload->DataSize));
341 handle_prefab_drop(ctx, absolute_path);
345 ImGui::EndDragDropTarget();
350 if(!process_drag_drop_source(
entity))
352 process_drag_drop_target(ctx, panels,
entity);
360void draw_3d_objects_menu(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
362 if(!ImGui::BeginMenu(
"3D Objects"))
367 static const std::vector<std::pair<std::string, std::vector<std::string>>> menu_objects = {
369 {
"Cube Rounded", {
"Cube Rounded"}},
370 {
"Sphere", {
"Sphere"}},
371 {
"Plane", {
"Plane"}},
372 {
"Cylinder", {
"Cylinder"}},
373 {
"Capsule", {
"Capsule"}},
375 {
"Torus", {
"Torus"}},
376 {
"Teapot", {
"Teapot"}},
378 {
"Polygon", {
"Icosahedron",
"Dodecahedron"}},
379 {
"Icosphere", {
"Icosphere0",
"Icosphere1",
"Icosphere2",
"Icosphere3",
"Icosphere4",
380 "Icosphere5",
"Icosphere6",
"Icosphere7",
"Icosphere8",
"Icosphere9",
381 "Icosphere10",
"Icosphere11",
"Icosphere12",
"Icosphere13",
"Icosphere14",
382 "Icosphere15",
"Icosphere16",
"Icosphere17",
"Icosphere18",
"Icosphere19"}}};
384 for(
const auto& p : menu_objects)
386 const auto&
name =
p.first;
387 const auto& objects_name =
p.second;
389 if(
name ==
"Separator")
393 else if(
name ==
"New Line")
397 else if(objects_name.size() == 1)
399 if(ImGui::MenuItem(
name.c_str()))
401 create_mesh_entity(ctx, panels, parent_entity,
name);
406 if(ImGui::BeginMenu(
name.c_str()))
408 for(
const auto& n : objects_name)
410 if(ImGui::MenuItem(
n.c_str()))
412 create_mesh_entity(ctx, panels, parent_entity, n);
423 if(ImGui::MenuItem(
"Text"))
425 create_text_entity(ctx, panels, parent_entity);
431void draw_lighting_menu(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
433 if(!ImGui::BeginMenu(
"Lighting"))
439 if(ImGui::BeginMenu(
"Light"))
441 static const std::vector<std::pair<std::string, light_type>> light_objects = {
446 for(
const auto& p : light_objects)
448 const auto&
name =
p.first;
449 const auto&
type =
p.second;
450 if(ImGui::MenuItem(
name.c_str()))
452 create_light_entity(ctx, panels, parent_entity,
type,
name);
459 if(ImGui::BeginMenu(
"Reflection Probes"))
461 static const std::vector<std::pair<std::string, probe_type>> reflection_probes = {
465 for(
const auto& p : reflection_probes)
467 const auto&
name =
p.first;
468 const auto&
type =
p.second;
470 if(ImGui::MenuItem(
name.c_str()))
472 create_reflection_probe_entity(ctx, panels, parent_entity,
type,
name);
481void draw_common_menu_items(
rtti::context& ctx, imgui_panels* panels, entt::handle parent_entity)
483 if(ImGui::MenuItem(
"Create Empty"))
485 create_empty_entity(ctx, panels, parent_entity);
488 draw_3d_objects_menu(ctx, panels, parent_entity);
489 draw_lighting_menu(ctx, panels, parent_entity);
491 if(ImGui::MenuItem(
"Camera"))
493 create_camera_entity(ctx, panels, parent_entity);
496 if(ImGui::MenuItem(
"UI Document"))
498 create_ui_document_entity(ctx, panels, parent_entity);
502void draw_entity_context_menu(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity)
504 if(!ImGui::BeginPopupContextItem(
"Entity Context Menu"))
509 if(ImGui::MenuItem(
"Create Empty Parent"))
511 create_empty_parent_entity(ctx, panels,
entity);
514 draw_common_menu_items(ctx, panels,
entity);
521 em.queue_action(
"Rename Entity",
522 [ctx, panels,
entity]()
mutable
524 start_editing_label(ctx, panels,
entity);
530 panels->get_scene_panel().duplicate_entities({
entity});
535 panels->get_scene_panel().delete_entities({
entity});
540 panels->get_scene_panel().focus_entities(panels->get_scene_panel().get_camera(), {entity});
545 if(
entity.any_of<prefab_component, prefab_id_component>())
547 if(ImGui::MenuItem(
"Open Prefab"))
550 em.queue_action(
"Open Prefab",
551 [&ctx,
entity, panels]()
mutable
556 auto prefab = prefab_root.get<prefab_component>().source;
560 em.enter_prefab_mode(ctx, prefab,
true);
566 if(ImGui::MenuItem(
"Unlink from Prefab"))
569 em.queue_action(
"Unlink from Prefab",
572 entity.remove<prefab_component, prefab_id_component>();
580void draw_window_context_menu(
rtti::context& ctx, imgui_panels* panels)
582 if(!ImGui::BeginPopupContextWindowEx())
587 draw_common_menu_items(ctx, panels, {});
593 ImGui::PushStyleColor(ImGuiCol_Separator, ImGui::GetStyleColorVec4(ImGuiCol_Text));
597 draw_entity_context_menu(ctx, panels,
entity);
601 draw_window_context_menu(ctx, panels);
604 ImGui::PopStyleColor();
611void draw_activity(
rtti::context& ctx, transform_component& trans_comp)
613 bool is_active_local = trans_comp.is_active();
616 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.5f, 1.0f));
621 trans_comp.set_active(!is_active_local);
623 auto entity = trans_comp.get_owner();
626 em.push_undo_stack_enabled(
true);
628 em.queue_action<entity_set_active_action_t>({},
633 em.pop_undo_stack_enabled();
639 ImGui::PopStyleColor();
646 auto focus = em.try_get_active_focus_as<entt::handle>();
658auto get_entity_tree_node_flags(
rtti::context& ctx, entt::handle
entity,
bool has_children) -> ImGuiTreeNodeFlags
661 ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_OpenOnArrow;
663 if(em.is_selected(
entity))
665 flags |= ImGuiTreeNodeFlags_Selected;
670 flags |= ImGuiTreeNodeFlags_Leaf;
673 flags |= ImGuiTreeNodeFlags_DrawLinesToNodes;
678auto get_entity_display_label(entt::handle
entity) -> std::string
683 const auto ent =
entity.entity();
684 const auto id = entt::to_integral(ent);
686 return icon +
name +
"###" + std::to_string(
id);
692 auto mode = em.get_select_mode();
693 em.queue_action(
"Select Entity",
694 [&ctx, panels,
entity, mode]()
mutable
696 stop_editing_label(ctx, panels,
entity);
702void handle_entity_keyboard_shortcuts(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity)
707 em.queue_action(
"Rename Entity",
708 [&ctx, panels,
entity]()
mutable
710 start_editing_label(ctx, panels,
entity);
716 panels->get_scene_panel().delete_entities({
entity});
721 panels->get_scene_panel().focus_entities(panels->get_scene_panel().get_camera(), {entity});
726 panels->get_scene_panel().duplicate_entities({
entity});
730void handle_entity_mouse_interactions(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity,
bool is_item_clicked_middle,
bool is_item_double_clicked_left)
732 if(is_item_clicked_middle)
734 panels->get_scene_panel().focus_entities(panels->get_scene_panel().get_camera(), {entity});
737 if(is_item_double_clicked_left)
739 panels->get_scene_panel().focus_entities(panels->get_scene_panel().get_camera(), {entity});
743void draw_entity_name_editor(
rtti::context& ctx, imgui_panels* panels, entt::handle
entity,
const ImVec2& pos)
746 if(!em.is_selected(
entity) || !is_editing_label())
751 if(is_just_started_editing_label())
753 ImGui::SetKeyboardFocusHere();
756 ImGui::SetCursorScreenPos(pos);
757 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().
x);
760 auto old_name = edit_name;
761 ImGui::InputTextWidget(
"##rename", edit_name,
false, ImGuiInputTextFlags_AutoSelectAll);
763 if(ImGui::IsItemDeactivatedAfterEdit())
767 em.push_undo_stack_enabled(
true);
768 em.queue_action<entity_set_name_action_t>({},
772 em.pop_undo_stack_enabled();
773 stop_editing_label(ctx, panels,
entity);
776 ImGui::PopItemWidth();
778 if(ImGui::IsItemDeactivated())
780 stop_editing_label(ctx, panels,
entity);
792 ImGui::PushID(
static_cast<int>(
entity.entity()));
794 auto& trans_comp =
entity.get<transform_component>();
795 bool has_children = !trans_comp.get_children().empty();
797 ImGuiTreeNodeFlags flags = get_entity_tree_node_flags(ctx,
entity, has_children);
799 if(is_parent_of_focused(ctx,
entity))
801 ImGui::SetNextItemOpen(
true, 0);
802 ImGui::SetScrollHereY();
805 auto pos = ImGui::GetCursorScreenPos() + ImVec2(ImGui::GetTextLineHeightWithSpacing(), 0.0f);
806 ImGui::AlignTextToFramePadding();
808 auto label = get_entity_display_label(
entity);
811 ImGui::PushStyleColor(ImGuiCol_Text, col);
812 ImGui::PushStyleVarX(ImGuiStyleVar_ItemInnerSpacing, 0.0f);
813 bool opened = ImGui::TreeNodeEx(label.c_str(), flags);
814 ImGui::PopStyleVar();
816 if(ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
818 const auto ent =
entity.entity();
819 const auto idx = entt::to_entity(ent);
820 const auto ver = entt::to_version(ent);
821 const auto id = entt::to_integral(ent);
823 ImGui::SetItemTooltipEx(
"Id: %d\nIndex: %d\nVersion: %d",
id, idx, ver);
826 ImGui::PopStyleColor();
830 ImGui::SetItemFocusFrame(ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 0.0f, 1.0f)));
833 if(!is_editing_label())
835 check_drag(ctx, panels,
entity);
836 check_context_menu(ctx, panels,
entity);
840 bool is_item_focus_changed = ImGui::IsItemFocusChanged();
841 bool is_item_released_left = ImGui::IsItemReleased(ImGuiMouseButton_Left);
842 bool is_item_clicked_middle = ImGui::IsItemClicked(ImGuiMouseButton_Middle);
843 bool is_item_double_clicked_left = ImGui::IsItemDoubleClicked(ImGuiMouseButton_Left);
844 bool activity_hovered =
false;
847 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
848 ImGui::AlignedItem(1.0f,
849 ImGui::GetContentRegionAvail().
x - ImGui::GetStyle().FramePadding.x,
850 ImGui::GetFrameHeight(),
853 draw_activity(ctx, trans_comp);
854 activity_hovered = ImGui::IsItemHovered();
858 if(!activity_hovered)
860 if(is_item_released_left || is_item_focus_changed)
862 handle_entity_selection(ctx, panels,
entity);
865 if(em.is_selected(
entity))
867 handle_entity_mouse_interactions(ctx, panels,
entity, is_item_clicked_middle, is_item_double_clicked_left);
868 handle_entity_keyboard_shortcuts(ctx, panels,
entity);
873 draw_entity_name_editor(ctx, panels,
entity, pos);
880 const auto& children = trans_comp.get_children();
881 for(
auto& child : children)
885 draw_entity(ctx, panels, child);
910void hierarchy_panel::draw_prefab_mode_header(
rtti::context& ctx)
const
914 if(!em.is_prefab_mode())
919 ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonActive));
924 ImGui::PopStyleColor();
926 if (em.edited_prefab)
929 ImGui::Text(
"Editing Prefab: %s", fs::path(em.edited_prefab.id()).filename().string().c_str());
935auto hierarchy_panel::get_scene_display_name(
const editing_manager& em, scene* target_scene)
const -> std::string
939 if (em.is_prefab_mode())
941 name = fs::path(em.edited_prefab.id()).filename().string();
949 name = target_scene->source.name();
956 if(em.has_unsaved_changes())
965void hierarchy_panel::draw_scene_hierarchy(
rtti::context& ctx)
const
968 scene* target_scene = em.get_active_scene(ctx);
975 std::string scene_name = get_scene_display_name(em, target_scene);
977 ImGui::SetNextItemOpen(
true, ImGuiCond_Appearing);
978 if(ImGui::CollapsingHeader(scene_name.c_str()))
982 target_scene->registry->sort<root_component>(
983 [](
auto const& lhs,
auto const& rhs)
986 return lhs.order < rhs.order;
993 target_scene->registry->view<root_component, transform_component>().each(
994 [&](
auto e,
auto&& root,
auto&& comp)
996 draw_entity(ctx,
parent_, comp.get_owner());
1000 handle_window_empty_click(ctx);
1003void hierarchy_panel::handle_window_empty_click(
rtti::context& ctx)
const
1005 auto& em = ctx.
get_cached<editing_manager>();
1006 if(ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
1008 if(!ImGui::IsAnyItemHovered())
1019 if(ImGui::Begin(
name))
1021 draw_prefab_mode_header(ctx);
1023 ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
1024 ImGuiWindowFlags_NoSavedSettings;
1026 if(ImGui::BeginChild(
"hierarchy_content", ImGui::GetContentRegionAvail(), 0, flags))
1028 check_context_menu(ctx,
parent_, {});
1029 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_embedded_mesh_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates an embedded mesh 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.