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 22:29:19 +02:00
using Nerfed.Runtime ;
2024-10-15 00:41:45 +02:00
using Nerfed.Runtime.Components ;
2024-10-15 22:29:19 +02:00
using Nerfed.Runtime.Util ;
2024-10-15 00:41:45 +02:00
namespace Nerfed.Editor.Systems
{
2024-10-15 21:47:23 +02:00
// Window that draws entities.
2024-10-19 23:41:05 +02:00
internal class EditorHierarchyWindow : MoonTools . ECS . System
2024-10-15 00:41:45 +02:00
{
2024-10-15 21:47:23 +02:00
private const ImGuiTreeNodeFlags baseFlags = ImGuiTreeNodeFlags . OpenOnArrow | ImGuiTreeNodeFlags . OpenOnDoubleClick | ImGuiTreeNodeFlags . SpanAvailWidth ;
2024-10-15 22:29:19 +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 )
{
2024-10-15 22:29:19 +02:00
//rootEntitiesWithTransformFilter = FilterBuilder.Include<LocalTransform>().Exclude<Child>().Build();
2024-10-15 00:41:45 +02:00
// 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 23:17:58 +02:00
if ( ImGui . BeginDragDropTarget ( ) )
{
unsafe
{
ImGuiPayloadPtr payload = ImGui . AcceptDragDropPayload ( $"{nameof(EditorHierarchyWindow)}" ) ;
if ( payload . NativePtr ! = null )
{
Entity * data = ( Entity * ) payload . Data ;
Entity child = data [ 0 ] ;
Log . Info ( $"Dropped {child.ID}" ) ;
Transform . RemoveParent ( World , child ) ;
}
}
ImGui . EndDragDropTarget ( ) ;
}
2024-10-15 21:47:23 +02:00
2024-10-15 22:29:19 +02:00
//foreach (Entity entity in rootEntitiesWithTransformFilter.Entities)
//{
// DrawEntityAndChildren(entity);
//}
2024-10-15 21:47:23 +02:00
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 ) )
{
2024-10-15 22:29:19 +02:00
// Selection.
2024-10-15 21:47:23 +02:00
if ( ImGui . IsItemClicked ( ) & & ! ImGui . IsItemToggledOpen ( ) )
{
2024-10-15 22:29:19 +02:00
World . Set ( entity , new ClickedInHierachy ( ) ) ;
}
// Drag and drop.
if ( ImGui . BeginDragDropSource ( ) )
{
2024-10-15 23:17:58 +02:00
unsafe
{
fixed ( Entity * payload = & entity )
{
ImGui . SetDragDropPayload ( $"{nameof(EditorHierarchyWindow)}" , ( IntPtr ) payload , ( uint ) sizeof ( Entity ) ) ;
}
}
2024-10-15 23:15:35 +02:00
2024-10-15 22:29:19 +02:00
ImGui . EndDragDropSource ( ) ;
}
if ( ImGui . BeginDragDropTarget ( ) )
{
unsafe
{
2024-10-15 23:17:58 +02:00
ImGuiPayloadPtr payload = ImGui . AcceptDragDropPayload ( $"{nameof(EditorHierarchyWindow)}" ) ;
2024-10-15 22:29:19 +02:00
if ( payload . NativePtr ! = null )
{
2024-10-15 23:17:58 +02:00
Entity ent = * ( Entity * ) payload . Data ;
Log . Info ( $"Dropped {ent.ID}" ) ;
Transform . SetParent ( World , ent , entity ) ;
2024-10-15 22:29:19 +02:00
}
}
ImGui . EndDragDropTarget ( ) ;
2024-10-15 21:47:23 +02:00
}
2024-10-15 23:19:46 +02:00
// Draw children.
2024-10-15 21:47:23 +02:00
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 ( ) ;
2024-10-15 22:29:19 +02:00
clickedEntities = FilterBuilder . Include < ClickedInHierachy > ( ) . Build ( ) ;
2024-10-15 21:47:23 +02:00
}
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 ( ) ) ;
}
2024-10-15 22:29:19 +02:00
Remove < ClickedInHierachy > ( entity ) ;
2024-10-15 21:47:23 +02:00
}
2024-10-15 00:41:45 +02:00
}
}
}
2024-10-15 23:17:58 +02:00
}