2024-10-15 00:41:45 +02:00
using ImGuiNET ;
using MoonTools.ECS ;
2024-10-15 21:47:23 +02:00
using Nerfed.Editor.Components ;
2024-10-15 00:41:45 +02:00
using Nerfed.Runtime.Components ;
namespace Nerfed.Editor.Systems
{
2024-10-15 21:47:23 +02:00
// Window that draws entities.
2024-10-15 00:41:45 +02:00
internal class EditorHierarchyWindow : MoonTools . ECS . DebugSystem
{
2024-10-15 21:47:23 +02:00
private const ImGuiTreeNodeFlags baseFlags = ImGuiTreeNodeFlags . OpenOnArrow | ImGuiTreeNodeFlags . OpenOnDoubleClick | ImGuiTreeNodeFlags . SpanAvailWidth ;
2024-10-15 00:41:45 +02:00
private readonly Filter rootEntitiesWithTransformFilter ;
2024-10-15 21:47:23 +02:00
//private readonly Filter rootEntitiesFilterBroken;
2024-10-15 00:41:45 +02:00
private readonly Filter rootEntitiesFilter ;
2024-10-15 21:47:23 +02:00
private readonly EditorHierachySelectionSystem hierachySelectionSystem ;
2024-10-15 00:41:45 +02:00
public EditorHierarchyWindow ( World world ) : base ( world )
{
rootEntitiesWithTransformFilter = FilterBuilder . Include < LocalTransform > ( ) . Exclude < Child > ( ) . Build ( ) ;
// TODO: this doesn't work.
2024-10-15 21:47:23 +02:00
//rootEntitiesFilterBroken = FilterBuilder.Exclude<Child>().Build();
2024-10-15 00:41:45 +02:00
// Maybe the parent/child functions should add a root component when not being a child.
2024-10-15 21:47:23 +02:00
rootEntitiesFilter = FilterBuilder . Include < Root > ( ) . Build ( ) ;
2024-10-15 00:41:45 +02:00
// Maybe instead of a root, if we need a component that is always on an entity and has some use we could create something like a VersionComponent which only hold an int.
// The version would update each time something changes on the entity.
2024-10-15 21:47:23 +02:00
// Or a EditorComponent, just a component that always gets added when in editor mode.
hierachySelectionSystem = new EditorHierachySelectionSystem ( world ) ;
2024-10-15 00:41:45 +02:00
}
public override void Update ( TimeSpan delta )
{
ImGui . Begin ( "Hierarchy" ) ;
2024-10-15 21:47:23 +02:00
ImGuiTreeNodeFlags flags = baseFlags ;
flags | = ImGuiTreeNodeFlags . DefaultOpen ;
if ( ImGui . TreeNodeEx ( "World" , flags ) )
2024-10-15 00:41:45 +02:00
{
2024-10-15 21:47:23 +02:00
foreach ( Entity entity in rootEntitiesWithTransformFilter . Entities )
{
DrawEntityAndChildren ( entity ) ;
}
foreach ( Entity entity in rootEntitiesFilter . Entities )
{
DrawEntityAndChildren ( entity ) ;
}
ImGui . TreePop ( ) ;
2024-10-15 00:41:45 +02:00
}
2024-10-15 21:47:23 +02:00
ImGui . End ( ) ;
2024-10-15 00:41:45 +02:00
2024-10-15 21:47:23 +02:00
hierachySelectionSystem . Update ( delta ) ;
}
private void DrawEntityAndChildren ( in Entity entity )
{
ImGuiTreeNodeFlags flags = baseFlags ;
if ( ! World . HasInRelation < ChildParentRelation > ( entity ) )
2024-10-15 00:41:45 +02:00
{
2024-10-15 21:47:23 +02:00
flags | = ImGuiTreeNodeFlags . Leaf ;
2024-10-15 00:41:45 +02:00
}
2024-10-15 21:47:23 +02:00
if ( World . Has < SelectedInHierachy > ( entity ) )
{
flags | = ImGuiTreeNodeFlags . Selected ;
}
if ( ImGui . TreeNodeEx ( $"{entity.ID} | {GetTag(entity)}" , flags ) )
{
if ( ImGui . IsItemClicked ( ) & & ! ImGui . IsItemToggledOpen ( ) )
{
World . Set ( entity , new ClickedInHierarchy ( ) ) ;
}
ReverseSpanEnumerator < Entity > childEntities = World . InRelations < ChildParentRelation > ( entity ) ;
foreach ( Entity childEntity in childEntities )
{
DrawEntityAndChildren ( childEntity ) ;
}
ImGui . TreePop ( ) ;
}
2024-10-15 00:41:45 +02:00
}
2024-10-15 21:47:23 +02:00
// System for handling the selected entities in the hierachy.
private class EditorHierachySelectionSystem : MoonTools . ECS . System
2024-10-15 00:41:45 +02:00
{
2024-10-15 21:47:23 +02:00
private readonly Filter selectedEntities ;
private readonly Filter clickedEntities ;
2024-10-15 00:41:45 +02:00
2024-10-15 21:47:23 +02:00
public EditorHierachySelectionSystem ( World world ) : base ( world )
2024-10-15 00:41:45 +02:00
{
2024-10-15 21:47:23 +02:00
selectedEntities = FilterBuilder . Include < SelectedInHierachy > ( ) . Build ( ) ;
clickedEntities = FilterBuilder . Include < ClickedInHierarchy > ( ) . Build ( ) ;
}
public override void Update ( TimeSpan delta )
{
ImGuiIOPtr io = ImGui . GetIO ( ) ;
if ( ! clickedEntities . Empty & & ! io . KeyCtrl )
{
foreach ( Entity entity in selectedEntities . Entities )
{
Remove < SelectedInHierachy > ( entity ) ;
}
}
foreach ( Entity entity in clickedEntities . Entities )
{
// Unselect.
if ( Has < SelectedInHierachy > ( entity ) )
{
Remove < SelectedInHierachy > ( entity ) ;
}
// Select.
else
{
Set ( entity , new SelectedInHierachy ( ) ) ;
}
Remove < ClickedInHierarchy > ( entity ) ;
}
2024-10-15 00:41:45 +02:00
}
}
}
}