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,19 +31,23 @@ namespace GlitchyEditor.EditWindows
protected override void InternalShow()
{
ImGui.PushStyleVar(.WindowMinSize, ImGui.Vec2(1000, 100));
if(!ImGui.Begin(s_WindowTitle, &_open, .None))
{
ImGui.PopStyleVar();
ImGui.End();
return;
}
if(_entityHierarchyWindow.SelectedEntities.Count == 1)
{
Entity entity = _entityHierarchyWindow.SelectedEntities.Front;
ShowComponents(entity);
}
ImGui.PopStyleVar();
ImGui.End();
}
@@ -73,11 +77,11 @@ namespace GlitchyEditor.EditWindows
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)
{
ImGui.SameLine(ImGui.GetWindowContentRegionMax().x - ImGui.CalcTextSize("...").x);
ImGui.SameLine(ImGui.GetWindowContentRegionMax().x - ImGui.CalcTextSize("...").x - 2 * ImGui.GetStyle().FramePadding.x);
if (ImGui.SmallButton("..."))
{
@@ -139,16 +143,22 @@ namespace GlitchyEditor.EditWindows
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;
if (ImGui.EditVector3("Position", ref position))
if (ImGui.EditVector3("Position", ref position, .Zero, 0.1f, textWidth))
transform.Position = position;
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);
Vector3 scale = transform.Scale;
if (ImGui.EditVector3("Scale", ref scale, .One))
if (ImGui.EditVector3("Scale", ref scale, .One, 0.1f, textWidth))
transform.Scale = scale;
}
@@ -43,7 +43,17 @@ namespace GlitchyEditor.EditWindows
ShowEntityHierarchyMenuBar();
if (ImGui.BeginPopupContextWindow(s_WindowTitle))
{
Show_ContextMenu_Create(true, false, false);
ImGui.EndPopup();
}
ShowEntityHierarchy();
if ((ImGui.IsMouseDown(.Left) || ImGui.IsMouseDown(.Right)) && !ImGui.IsAnyItemHovered() && !ImGui.GetIO().KeyCtrl && ImGui.IsWindowHovered(.AllowWhenBlockedByPopup))
_selectedEntities.Clear();
ImGui.End();
}
@@ -88,80 +98,19 @@ namespace GlitchyEditor.EditWindows
/// Deletes all selected entities and their children.
internal void DeleteSelectedEntities()
{
List<EcsEntity> entities = scope .();
for(var entity in _selectedEntities)
for (var entity in _selectedEntities)
{
entities.Add(entity.Handle);
FindChildren(entity.Handle, entities);
_scene.DestroyEntity(entity, true);
}
for(var entityId in entities)
{
Entity entity = .(entityId, _scene);
_scene.DestroyEntity(entity);
}
_selectedEntities.Clear();
}
private void ShowEntityHierarchyMenuBar()
{
if(ImGui.BeginMenuBar())
{
if(ImGui.BeginMenu("Create"))
{
if(ImGui.MenuItem("Empty Entity"))
{
_scene.CreateEntity();
}
Show_ContextMenu_Create(true, true, true);
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity.");
if(ImGui.MenuItem("Empty Child", null, false, !_selectedEntities.IsEmpty))
{
var newEntity = _scene.CreateEntity();
var transformCmp = newEntity.GetComponent<TransformComponent>();
// Last entity in list is the entity that has been selected last.
transformCmp.Parent = _selectedEntities.Back.Handle;
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity that is a child of the currently selected entity.");
if(ImGui.MenuItem("Empty Parent", null, false, !_selectedEntities.IsEmpty && AllSelectionsOnSameLevel()))
{
var commonParent = _selectedEntities.Front.GetComponent<TransformComponent>();
var newEntity = _scene.CreateEntity();
if(commonParent != null)
{
// parent of selected entities is parent of the new entity.
// (which is why this doesn't work if the entities don't have the same parent)
var newEntityTransform = newEntity.GetComponent<TransformComponent>();
newEntityTransform.Parent = commonParent.Parent;
}
// new entity is parent of all selected entities.
for(var selectedEntity in _selectedEntities)
{
var selectedTransform = selectedEntity.GetComponent<TransformComponent>();
selectedTransform.Parent = newEntity.Handle;
}
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity that is the parent of the currently selected entities.");
ImGui.EndMenu();
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity.");
Show_ContextMenu_Delete();
if(ImGui.MenuItem("Delete", null, false, !_selectedEntities.IsEmpty) ||
(Input.IsKeyPressed(.Delete) && ImGui.IsWindowHovered()))
@@ -180,6 +129,114 @@ namespace GlitchyEditor.EditWindows
}
}
/// Creates a new entity that is a child of the given entity.
private void CreateChild(Entity? entity)
{
var newEntity = _scene.CreateEntity();
var transformCmp = newEntity.GetComponent<TransformComponent>();
// Last entity in list is the entity that has been selected last.
transformCmp.Parent = entity?.Handle ?? .InvalidEntity;
}
/// Creates a new entity that is a parent of the selected entities.
private void CreateParent()
{
if (_selectedEntities.IsEmpty || !AllSelectionsOnSameLevel())
{
Log.EngineLogger.Error("Cannot create parent entity.");
return;
}
var commonParent = _selectedEntities.Front.GetComponent<TransformComponent>();
var newEntity = _scene.CreateEntity();
if(commonParent != null)
{
// parent of selected entities is parent of the new entity.
// (which is why this doesn't work if the entities don't have the same parent)
var newEntityTransform = newEntity.GetComponent<TransformComponent>();
newEntityTransform.Parent = commonParent.Parent;
}
// new entity is parent of all selected entities.
for(var selectedEntity in _selectedEntities)
{
var selectedTransform = selectedEntity.GetComponent<TransformComponent>();
selectedTransform.Parent = newEntity.Handle;
}
}
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())
{
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();
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity.");
}
private bool Show_ContextMenu_Delete()
{
bool deleted = false;
if(ImGui.MenuItem("Delete", null, false, !_selectedEntities.IsEmpty))
{
DeleteSelectedEntities();
deleted = true;
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Deletes the selected Entities and their children.");
return deleted;
}
private void ImGuiPrintEntityTree(TreeNode<Entity> tree)
{
String name = null;
@@ -195,7 +252,7 @@ namespace GlitchyEditor.EditWindows
name = scope:: $"Entity {(tree.Value.Handle.[Friend]Index)}";
}
ImGui.TreeNodeFlags flags = .OpenOnArrow | .DefaultOpen;
ImGui.TreeNodeFlags flags = .OpenOnArrow | .DefaultOpen | .SpanAvailWidth;
if(tree.Children.Count == 0)
flags |= .Leaf;
@@ -207,6 +264,23 @@ namespace GlitchyEditor.EditWindows
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())
{
ImGui.SetDragDropPayload("DND_Entity", &tree.Value, sizeof(Entity));
@@ -259,7 +333,8 @@ namespace GlitchyEditor.EditWindows
ImGui.EndDragDropTarget();
}
bool clicked = ImGui.IsItemClicked();
bool clicked = ImGui.IsItemClicked(.Left);
bool clickedRight = ImGui.IsItemClicked(.Right);
if(isOpen)
{
@@ -271,23 +346,23 @@ namespace GlitchyEditor.EditWindows
ImGui.TreePop();
}
if(clicked)
if (clicked || clickedRight)
{
if(inSelectedList)
if (inSelectedList && !clickedRight)
{
_selectedEntities.Remove(tree.Value);
inSelectedList = false;
}
else
{
if(!ImGui.GetIO().KeyCtrl)
if (!ImGui.GetIO().KeyCtrl && !clickedRight)
{
_selectedEntities.Clear();
}
_selectedEntities.Add(tree.Value);
inSelectedList = true;
}
inSelectedList = !inSelectedList;
}
}
-1
View File
@@ -114,6 +114,5 @@ namespace GlitchyEditor
_selectedEntities.Clear();
}
}
}
+5
View File
@@ -51,6 +51,8 @@ namespace GlitchyEditor
public this()
{
_open = false;
_settings = Application.Get().Settings;
Create();
@@ -117,6 +119,9 @@ namespace GlitchyEditor
protected override void InternalShow()
{
ImGui.Begin("Settings", &_open, .NoDocking);
defer ImGui.End();
// Leave room for 1 line below us
ImGui.BeginChild("item view", ImGui.Vec2(0, -ImGui.GetFrameHeightWithSpacing()));
+1 -1
View File
@@ -107,7 +107,7 @@ namespace GlitchyEngine
class ImGuiSettings
{
[Setting("UI", "Font Size"), BonInclude]
public int32 FontSize = 16;
public int32 FontSize = 14;
[Setting("UI", "Font name"), BonInclude]
public readonly String FontName = new .("Fonts/CascadiaCode.ttf") ~ delete _;
+78 -1
View File
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using internal GlitchyEngine.World;
@@ -24,13 +25,53 @@ namespace GlitchyEngine.World
_scene = scene;
}
public ChildEnumerator EnumerateChildren => .(this);
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
{
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
@@ -51,5 +92,41 @@ namespace GlitchyEngine.World
_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.Renderer;
using System;
using System.Collections;
namespace GlitchyEngine.World
{
@@ -9,6 +10,8 @@ namespace GlitchyEngine.World
class Scene
{
internal EcsWorld _ecsWorld = new .() ~ delete _;
private Dictionary<Type, function void(Entity entity, Type componentType, void* component)> _onComponentAddedHandlers = new .() ~ delete _;
public this()
{
@@ -20,6 +23,11 @@ namespace GlitchyEngine.World
v.Sprite = new Texture2D("Textures/rocket.png");
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()
@@ -68,6 +76,7 @@ namespace GlitchyEngine.World
}
}
/// Creates a new Entity with the given name.
public Entity CreateEntity(String name = "")
{
Entity entity = Entity(_ecsWorld.NewEntity(), this);
@@ -79,8 +88,20 @@ namespace GlitchyEngine.World
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);
}
@@ -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);
}
}
}
}