UI Tweaks, ChildEnumerator, ComponentAdded-Handlers

This commit is contained in:
Simon Lübeß
2022-04-12 00:48:20 +02:00
parent bec202e23a
commit 863c264001
7 changed files with 278 additions and 83 deletions
@@ -31,8 +31,11 @@ namespace GlitchyEditor.EditWindows
protected override void InternalShow() protected override void InternalShow()
{ {
ImGui.PushStyleVar(.WindowMinSize, ImGui.Vec2(1000, 100));
if(!ImGui.Begin(s_WindowTitle, &_open, .None)) if(!ImGui.Begin(s_WindowTitle, &_open, .None))
{ {
ImGui.PopStyleVar();
ImGui.End(); ImGui.End();
return; return;
} }
@@ -44,6 +47,7 @@ namespace GlitchyEditor.EditWindows
ShowComponents(entity); ShowComponents(entity);
} }
ImGui.PopStyleVar();
ImGui.End(); ImGui.End();
} }
@@ -73,11 +77,11 @@ namespace GlitchyEditor.EditWindows
ImGui.PushID(header); ImGui.PushID(header);
bool nodeOpen = ImGui.TreeNodeEx(header.CStr(), .DefaultOpen | .AllowItemOverlap | .Framed); bool nodeOpen = ImGui.TreeNodeEx(header.CStr(), .DefaultOpen | .AllowItemOverlap | .Framed | .SpanFullWidth);
if (showComponentContextMenu != null) if (showComponentContextMenu != null)
{ {
ImGui.SameLine(ImGui.GetWindowContentRegionMax().x - ImGui.CalcTextSize("...").x); ImGui.SameLine(ImGui.GetWindowContentRegionMax().x - ImGui.CalcTextSize("...").x - 2 * ImGui.GetStyle().FramePadding.x);
if (ImGui.SmallButton("...")) if (ImGui.SmallButton("..."))
{ {
@@ -139,16 +143,22 @@ namespace GlitchyEditor.EditWindows
private static void ShowTransformComponentEditor(Entity entity, TransformComponent* transform) private static void ShowTransformComponentEditor(Entity entity, TransformComponent* transform)
{ {
float textWidth = ImGui.CalcTextSize("Position".CStr()).x;
textWidth = Math.Max(textWidth, ImGui.CalcTextSize("Rotation".CStr()).x);
textWidth = Math.Max(textWidth, ImGui.CalcTextSize("Scale".CStr()).x);
textWidth += ImGui.GetStyle().FramePadding.x * 3.0f;
Vector3 position = transform.Position; Vector3 position = transform.Position;
if (ImGui.EditVector3("Position", ref position)) if (ImGui.EditVector3("Position", ref position, .Zero, 0.1f, textWidth))
transform.Position = position; transform.Position = position;
Vector3 rotationEuler = MathHelper.ToDegrees(transform.EditorRotationEuler); Vector3 rotationEuler = MathHelper.ToDegrees(transform.EditorRotationEuler);
if (ImGui.EditVector3("Rotation", ref rotationEuler)) if (ImGui.EditVector3("Rotation", ref rotationEuler, .Zero, 0.1f, textWidth))
transform.EditorRotationEuler = MathHelper.ToRadians(rotationEuler); transform.EditorRotationEuler = MathHelper.ToRadians(rotationEuler);
Vector3 scale = transform.Scale; Vector3 scale = transform.Scale;
if (ImGui.EditVector3("Scale", ref scale, .One)) if (ImGui.EditVector3("Scale", ref scale, .One, 0.1f, textWidth))
transform.Scale = scale; transform.Scale = scale;
} }
@@ -43,8 +43,18 @@ namespace GlitchyEditor.EditWindows
ShowEntityHierarchyMenuBar(); ShowEntityHierarchyMenuBar();
if (ImGui.BeginPopupContextWindow(s_WindowTitle))
{
Show_ContextMenu_Create(true, false, false);
ImGui.EndPopup();
}
ShowEntityHierarchy(); ShowEntityHierarchy();
if ((ImGui.IsMouseDown(.Left) || ImGui.IsMouseDown(.Right)) && !ImGui.IsAnyItemHovered() && !ImGui.GetIO().KeyCtrl && ImGui.IsWindowHovered(.AllowWhenBlockedByPopup))
_selectedEntities.Clear();
ImGui.End(); ImGui.End();
} }
@@ -88,52 +98,56 @@ namespace GlitchyEditor.EditWindows
/// Deletes all selected entities and their children. /// Deletes all selected entities and their children.
internal void DeleteSelectedEntities() internal void DeleteSelectedEntities()
{ {
List<EcsEntity> entities = scope .();
for (var entity in _selectedEntities) for (var entity in _selectedEntities)
{ {
entities.Add(entity.Handle); _scene.DestroyEntity(entity, true);
FindChildren(entity.Handle, entities);
} }
for(var entityId in entities)
{
Entity entity = .(entityId, _scene);
_scene.DestroyEntity(entity);
}
_selectedEntities.Clear();
} }
private void ShowEntityHierarchyMenuBar() private void ShowEntityHierarchyMenuBar()
{ {
if(ImGui.BeginMenuBar()) if(ImGui.BeginMenuBar())
{ {
if(ImGui.BeginMenu("Create")) Show_ContextMenu_Create(true, true, true);
Show_ContextMenu_Delete();
if(ImGui.MenuItem("Delete", null, false, !_selectedEntities.IsEmpty) ||
(Input.IsKeyPressed(.Delete) && ImGui.IsWindowHovered()))
{ {
if(ImGui.MenuItem("Empty Entity")) DeleteSelectedEntities();
{
_scene.CreateEntity();
} }
if(ImGui.IsItemHovered()) if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity."); ImGui.SetTooltip("Deletes the selected Entities and their children.");
if(ImGui.MenuItem("Empty Child", null, false, !_selectedEntities.IsEmpty)) ImGui.Text("Search:");
ImGui.InputText(String.Empty, &_entitySearchChars, (.)_entitySearchChars.Count);
ImGui.EndMenuBar();
}
}
/// Creates a new entity that is a child of the given entity.
private void CreateChild(Entity? entity)
{ {
var newEntity = _scene.CreateEntity(); var newEntity = _scene.CreateEntity();
var transformCmp = newEntity.GetComponent<TransformComponent>(); var transformCmp = newEntity.GetComponent<TransformComponent>();
// Last entity in list is the entity that has been selected last. // Last entity in list is the entity that has been selected last.
transformCmp.Parent = _selectedEntities.Back.Handle; transformCmp.Parent = entity?.Handle ?? .InvalidEntity;
} }
if(ImGui.IsItemHovered()) /// Creates a new entity that is a parent of the selected entities.
ImGui.SetTooltip("Create a new Entity that is a child of the currently selected entity."); private void CreateParent()
if(ImGui.MenuItem("Empty Parent", null, false, !_selectedEntities.IsEmpty && AllSelectionsOnSameLevel()))
{ {
if (_selectedEntities.IsEmpty || !AllSelectionsOnSameLevel())
{
Log.EngineLogger.Error("Cannot create parent entity.");
return;
}
var commonParent = _selectedEntities.Front.GetComponent<TransformComponent>(); var commonParent = _selectedEntities.Front.GetComponent<TransformComponent>();
var newEntity = _scene.CreateEntity(); var newEntity = _scene.CreateEntity();
@@ -154,30 +168,73 @@ namespace GlitchyEditor.EditWindows
} }
} }
private void Show_ContextMenu_Create(bool allowEmpty = true, bool allowChild = true, bool allowParent = true)
{
if (ImGui.BeginMenu("Create"))
{
if (allowEmpty)
{
if(ImGui.MenuItem("Empty Entity"))
{
if (_selectedEntities.IsEmpty)
_scene.CreateEntity();
else
{
Entity? parent = _selectedEntities.Back.Parent;
CreateChild(parent);
}
}
if(ImGui.IsItemHovered()) if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity that is the parent of the currently selected entities."); {
ImGui.SetTooltip("Create a new Entity.");
}
}
if (allowChild)
{
if(ImGui.MenuItem("Empty Child", null, false, !_selectedEntities.IsEmpty))
{
CreateChild(_selectedEntities.Back);
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity that is a child of the selected entity.");
}
if (allowParent)
{
if(ImGui.MenuItem("Parent", null, false, !_selectedEntities.IsEmpty && AllSelectionsOnSameLevel()))
{
CreateParent();
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity that is the parent of the selected entities.");
}
ImGui.EndMenu(); ImGui.EndMenu();
} }
if(ImGui.IsItemHovered()) if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity."); ImGui.SetTooltip("Create a new Entity.");
}
if(ImGui.MenuItem("Delete", null, false, !_selectedEntities.IsEmpty) || private bool Show_ContextMenu_Delete()
(Input.IsKeyPressed(.Delete) && ImGui.IsWindowHovered())) {
bool deleted = false;
if(ImGui.MenuItem("Delete", null, false, !_selectedEntities.IsEmpty))
{ {
DeleteSelectedEntities(); DeleteSelectedEntities();
deleted = true;
} }
if(ImGui.IsItemHovered()) if(ImGui.IsItemHovered())
ImGui.SetTooltip("Deletes the selected Entities and their children."); ImGui.SetTooltip("Deletes the selected Entities and their children.");
ImGui.Text("Search:"); return deleted;
ImGui.InputText(String.Empty, &_entitySearchChars, (.)_entitySearchChars.Count);
ImGui.EndMenuBar();
}
} }
private void ImGuiPrintEntityTree(TreeNode<Entity> tree) private void ImGuiPrintEntityTree(TreeNode<Entity> tree)
@@ -195,7 +252,7 @@ namespace GlitchyEditor.EditWindows
name = scope:: $"Entity {(tree.Value.Handle.[Friend]Index)}"; name = scope:: $"Entity {(tree.Value.Handle.[Friend]Index)}";
} }
ImGui.TreeNodeFlags flags = .OpenOnArrow | .DefaultOpen; ImGui.TreeNodeFlags flags = .OpenOnArrow | .DefaultOpen | .SpanAvailWidth;
if(tree.Children.Count == 0) if(tree.Children.Count == 0)
flags |= .Leaf; flags |= .Leaf;
@@ -207,6 +264,23 @@ namespace GlitchyEditor.EditWindows
bool isOpen = ImGui.TreeNodeEx((void*)(uint)tree.Value.Handle.[Friend]Index, flags, $"{name}"); bool isOpen = ImGui.TreeNodeEx((void*)(uint)tree.Value.Handle.[Friend]Index, flags, $"{name}");
ImGui.PushID((void*)(uint)tree.Value.Handle.[Friend]Index);
bool deleted = false;
if (ImGui.BeginPopupContextItem("treeNodePopup"))
{
Show_ContextMenu_Create(true, true, true);
deleted = Show_ContextMenu_Delete();
ImGui.EndPopup();
}
ImGui.PopID();
if (deleted)
return;
if(ImGui.BeginDragDropSource()) if(ImGui.BeginDragDropSource())
{ {
ImGui.SetDragDropPayload("DND_Entity", &tree.Value, sizeof(Entity)); ImGui.SetDragDropPayload("DND_Entity", &tree.Value, sizeof(Entity));
@@ -259,7 +333,8 @@ namespace GlitchyEditor.EditWindows
ImGui.EndDragDropTarget(); ImGui.EndDragDropTarget();
} }
bool clicked = ImGui.IsItemClicked(); bool clicked = ImGui.IsItemClicked(.Left);
bool clickedRight = ImGui.IsItemClicked(.Right);
if(isOpen) if(isOpen)
{ {
@@ -271,23 +346,23 @@ namespace GlitchyEditor.EditWindows
ImGui.TreePop(); ImGui.TreePop();
} }
if(clicked) if (clicked || clickedRight)
{ {
if(inSelectedList) if (inSelectedList && !clickedRight)
{ {
_selectedEntities.Remove(tree.Value); _selectedEntities.Remove(tree.Value);
inSelectedList = false;
} }
else else
{ {
if(!ImGui.GetIO().KeyCtrl) if (!ImGui.GetIO().KeyCtrl && !clickedRight)
{ {
_selectedEntities.Clear(); _selectedEntities.Clear();
} }
_selectedEntities.Add(tree.Value); _selectedEntities.Add(tree.Value);
inSelectedList = true;
} }
inSelectedList = !inSelectedList;
} }
} }
-1
View File
@@ -114,6 +114,5 @@ namespace GlitchyEditor
_selectedEntities.Clear(); _selectedEntities.Clear();
} }
} }
} }
+5
View File
@@ -51,6 +51,8 @@ namespace GlitchyEditor
public this() public this()
{ {
_open = false;
_settings = Application.Get().Settings; _settings = Application.Get().Settings;
Create(); Create();
@@ -117,6 +119,9 @@ namespace GlitchyEditor
protected override void InternalShow() protected override void InternalShow()
{ {
ImGui.Begin("Settings", &_open, .NoDocking);
defer ImGui.End();
// Leave room for 1 line below us // Leave room for 1 line below us
ImGui.BeginChild("item view", ImGui.Vec2(0, -ImGui.GetFrameHeightWithSpacing())); ImGui.BeginChild("item view", ImGui.Vec2(0, -ImGui.GetFrameHeightWithSpacing()));
+1 -1
View File
@@ -107,7 +107,7 @@ namespace GlitchyEngine
class ImGuiSettings class ImGuiSettings
{ {
[Setting("UI", "Font Size"), BonInclude] [Setting("UI", "Font Size"), BonInclude]
public int32 FontSize = 16; public int32 FontSize = 14;
[Setting("UI", "Font name"), BonInclude] [Setting("UI", "Font name"), BonInclude]
public readonly String FontName = new .("Fonts/CascadiaCode.ttf") ~ delete _; public readonly String FontName = new .("Fonts/CascadiaCode.ttf") ~ delete _;
+78 -1
View File
@@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using internal GlitchyEngine.World; using internal GlitchyEngine.World;
@@ -24,13 +25,53 @@ namespace GlitchyEngine.World
_scene = scene; _scene = scene;
} }
public ChildEnumerator EnumerateChildren => .(this);
public bool IsValid => _entity.IsValid; public bool IsValid => _entity.IsValid;
public Entity? Parent
{
get
{
var cmp = GetComponent<TransformComponent>();
if (cmp.Parent == .InvalidEntity)
return null;
return .(cmp.Parent, _scene);
}
set
{
if (value == null)
{
var cmp = GetComponent<TransformComponent>();
cmp.Parent = .InvalidEntity;
}
else
{
Entity parent = value.Value;
if (parent.Scene != _scene)
{
Log.EngineLogger.AssertDebug(false);
return;
}
var cmp = GetComponent<TransformComponent>();
cmp.Parent = parent._entity;
}
}
}
public T* AddComponent<T>(T value = T()) where T: struct, new public T* AddComponent<T>(T value = T()) where T: struct, new
{ {
Log.EngineLogger.AssertDebug(!HasComponent<T>(), scope $"Entity already has component."); Log.EngineLogger.AssertDebug(!HasComponent<T>(), scope $"Entity already has component.");
return _scene._ecsWorld.AssignComponent<T>(_entity, value); T* component = _scene._ecsWorld.AssignComponent<T>(_entity, value);
_scene.[Friend]OnComponentAdded(this, typeof(T), component);
return component;
} }
public T* GetComponent<T>() where T: struct, new public T* GetComponent<T>() where T: struct, new
@@ -51,5 +92,41 @@ namespace GlitchyEngine.World
_scene._ecsWorld.RemoveComponent<T>(_entity); _scene._ecsWorld.RemoveComponent<T>(_entity);
} }
public struct ChildEnumerator : IEnumerator<Entity>, IDisposable
{
private WorldEnumerator<TransformComponent> _transformEnum;
private EcsEntity _entity;
private Entity _currentChild;
public this(Entity entity)
{
_entity = entity.Handle;
_transformEnum = entity.Scene._ecsWorld.Enumerate<TransformComponent>();
_currentChild = .(.InvalidEntity, entity.Scene);
}
public Entity Current => _currentChild;
public Result<Entity> GetNext() mut
{
while (true)
{
(EcsEntity entity, TransformComponent* transform) = Try!(_transformEnum.GetNext());
if (transform.Parent == _entity)
{
_currentChild.[Friend]_entity = entity;
return .Ok(_currentChild);
}
}
}
public void Dispose()
{
_transformEnum.Dispose();
}
}
} }
} }
+30 -1
View File
@@ -1,6 +1,7 @@
using GlitchyEngine.Math; using GlitchyEngine.Math;
using GlitchyEngine.Renderer; using GlitchyEngine.Renderer;
using System; using System;
using System.Collections;
namespace GlitchyEngine.World namespace GlitchyEngine.World
{ {
@@ -10,6 +11,8 @@ namespace GlitchyEngine.World
{ {
internal EcsWorld _ecsWorld = new .() ~ delete _; internal EcsWorld _ecsWorld = new .() ~ delete _;
private Dictionary<Type, function void(Entity entity, Type componentType, void* component)> _onComponentAddedHandlers = new .() ~ delete _;
public this() public this()
{ {
Entity entity = CreateEntity("Green Quad"); Entity entity = CreateEntity("Green Quad");
@@ -20,6 +23,11 @@ namespace GlitchyEngine.World
v.Sprite = new Texture2D("Textures/rocket.png"); v.Sprite = new Texture2D("Textures/rocket.png");
v.Sprite.SamplerState = SamplerStateManager.PointClamp; v.Sprite.SamplerState = SamplerStateManager.PointClamp;
_onComponentAddedHandlers.Add(typeof(CameraComponent), (e, t, c) => {
CameraComponent* cameraComponent = (.)c;
cameraComponent.Camera.SetViewportSize(e.Scene.ViewportWidth, e.Scene.ViewportHeight);
});
} }
public ~this() public ~this()
@@ -68,6 +76,7 @@ namespace GlitchyEngine.World
} }
} }
/// Creates a new Entity with the given name.
public Entity CreateEntity(String name = "") public Entity CreateEntity(String name = "")
{ {
Entity entity = Entity(_ecsWorld.NewEntity(), this); Entity entity = Entity(_ecsWorld.NewEntity(), this);
@@ -79,8 +88,20 @@ namespace GlitchyEngine.World
return entity; return entity;
} }
public void DestroyEntity(Entity entity) /** Deletes the given entity.
* @param entity The entity to delete.
* @param destroyChildren If set to true all children of entity will be destroyed.
*/
public void DestroyEntity(Entity entity, bool destroyChildren = false)
{ {
if (destroyChildren)
{
for (Entity child in entity.EnumerateChildren)
{
DestroyEntity(child, true);
}
}
_ecsWorld.RemoveEntity(entity.Handle); _ecsWorld.RemoveEntity(entity.Handle);
} }
@@ -100,5 +121,13 @@ namespace GlitchyEngine.World
} }
} }
} }
private void OnComponentAdded(Entity entity, Type componentType, void* component)
{
if (_onComponentAddedHandlers.TryGetValue(componentType, let handler))
{
handler(entity, componentType, component);
}
}
} }
} }