mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
UI Tweaks, ChildEnumerator, ComponentAdded-Handlers
This commit is contained in:
@@ -31,8 +31,11 @@ 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;
|
||||
}
|
||||
@@ -44,6 +47,7 @@ namespace GlitchyEditor.EditWindows
|
||||
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,8 +43,18 @@ 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,52 +98,56 @@ namespace GlitchyEditor.EditWindows
|
||||
/// Deletes all selected entities and their children.
|
||||
internal void DeleteSelectedEntities()
|
||||
{
|
||||
List<EcsEntity> entities = scope .();
|
||||
|
||||
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"))
|
||||
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"))
|
||||
{
|
||||
_scene.CreateEntity();
|
||||
DeleteSelectedEntities();
|
||||
}
|
||||
|
||||
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 transformCmp = newEntity.GetComponent<TransformComponent>();
|
||||
// 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())
|
||||
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()))
|
||||
/// 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();
|
||||
@@ -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())
|
||||
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();
|
||||
}
|
||||
|
||||
if(ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip("Create a new Entity.");
|
||||
}
|
||||
|
||||
if(ImGui.MenuItem("Delete", null, false, !_selectedEntities.IsEmpty) ||
|
||||
(Input.IsKeyPressed(.Delete) && ImGui.IsWindowHovered()))
|
||||
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.");
|
||||
|
||||
ImGui.Text("Search:");
|
||||
|
||||
ImGui.InputText(String.Empty, &_entitySearchChars, (.)_entitySearchChars.Count);
|
||||
|
||||
ImGui.EndMenuBar();
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
private void ImGuiPrintEntityTree(TreeNode<Entity> tree)
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,5 @@ namespace GlitchyEditor
|
||||
|
||||
_selectedEntities.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
|
||||
|
||||
@@ -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 _;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using GlitchyEngine.Math;
|
||||
using GlitchyEngine.Renderer;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace GlitchyEngine.World
|
||||
{
|
||||
@@ -10,6 +11,8 @@ namespace GlitchyEngine.World
|
||||
{
|
||||
internal EcsWorld _ecsWorld = new .() ~ delete _;
|
||||
|
||||
private Dictionary<Type, function void(Entity entity, Type componentType, void* component)> _onComponentAddedHandlers = new .() ~ delete _;
|
||||
|
||||
public this()
|
||||
{
|
||||
Entity entity = CreateEntity("Green Quad");
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user