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-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 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 23:15:35 +02:00
private readonly EditorHierachyDragAndDropSystem hierachyDragAndDropSystem ;
2024-10-15 21:47:23 +02:00
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 23:15:35 +02:00
hierachyDragAndDropSystem = new EditorHierachyDragAndDropSystem ( 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:15:35 +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 ) ;
2024-10-15 23:15:35 +02:00
hierachyDragAndDropSystem . Update ( delta ) ;
2024-10-15 21:47:23 +02:00
}
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:15:35 +02:00
ImGui . SetDragDropPayload ( $"{nameof(EditorHierarchyWindow)}" , nint . Zero , 0 ) ;
Set ( entity , new PayloadSourceInHierachy ( ) ) ;
Log . Info ( $"SetSource {entity.ID}" ) ;
//unsafe
//{
// fixed (Entity* payload = &entity)
// {
// ImGui.SetDragDropPayload($"{nameof(EditorHierarchyWindow)}", (IntPtr)payload, (uint)sizeof(Entity));
// }
//}
2024-10-15 22:29:19 +02:00
ImGui . EndDragDropSource ( ) ;
}
if ( ImGui . BeginDragDropTarget ( ) )
{
2024-10-15 23:15:35 +02:00
ImGuiPayloadPtr payload = ImGui . AcceptDragDropPayload ( $"{nameof(EditorHierarchyWindow)}" ) ;
2024-10-15 22:29:19 +02:00
unsafe
{
if ( payload . NativePtr ! = null )
{
2024-10-15 23:15:35 +02:00
Log . Info ( $"SetTarget {entity.ID}" ) ;
Set ( entity , new PayloadTargetInHierachy ( ) ) ;
2024-10-15 22:29:19 +02:00
}
}
2024-10-15 23:15:35 +02:00
//unsafe
//{
// ImGuiPayloadPtr payload = ImGui.AcceptDragDropPayload($"{nameof(EditorHierarchyWindow)}");
// if (payload.NativePtr != null)
// {
// 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
}
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 22:29:19 +02:00
private class EditorHierachyDragAndDropSystem : MoonTools . ECS . System
{
2024-10-15 23:15:35 +02:00
private readonly Filter sourceEntities ;
private readonly Filter targetEntities ;
2024-10-15 22:29:19 +02:00
public EditorHierachyDragAndDropSystem ( World world ) : base ( world )
{
2024-10-15 23:15:35 +02:00
sourceEntities = FilterBuilder . Include < PayloadSourceInHierachy > ( ) . Build ( ) ;
targetEntities = FilterBuilder . Include < PayloadTargetInHierachy > ( ) . Build ( ) ;
2024-10-15 22:29:19 +02:00
}
public override void Update ( TimeSpan delta )
{
2024-10-15 23:15:35 +02:00
if ( ! targetEntities . Empty )
{
Entity target = GetSingletonEntity < PayloadTargetInHierachy > ( ) ;
foreach ( Entity source in sourceEntities . Entities )
{
Transform . SetParent ( World , source , target ) ;
}
}
bool clear = false ;
unsafe
{
ImGuiPayloadPtr payload = ImGui . GetDragDropPayload ( ) ;
if ( payload . NativePtr = = null )
{
clear = true ;
}
}
if ( clear )
{
foreach ( Entity source in targetEntities . Entities )
{
Remove < PayloadTargetInHierachy > ( source ) ;
}
foreach ( Entity source in sourceEntities . Entities )
{
Remove < PayloadSourceInHierachy > ( source ) ;
}
}
2024-10-15 22:29:19 +02:00
}
}
2024-10-15 00:41:45 +02:00
}
}