ComponentWindow finalized

ScriptCore:
- ActivatorExtension: Fixed creation of default value or null class
- ImGuiExtension: added DragUIntX / DoubleX and CheckboxX
This commit is contained in:
Simon Lübeß
2024-01-30 23:40:58 +01:00
parent 09f7dfce90
commit 4830fd289e
6 changed files with 316 additions and 151 deletions
@@ -142,15 +142,7 @@ namespace GlitchyEditor.EditWindows
TComponent* component = entity.GetComponent<TComponent>();
//ImGui.PushID(header);
/*ImGui.PushClipRect(.(), .(float.MaxValue, float.MaxValue), false);
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
ImGui.PopClipRect();*/
bool nodeOpen = ImGui.CollapsingHeader(header.CStr(), .DefaultOpen | .AllowOverlap | .Framed | .SpanFullWidth);
//ImGui.TreeNodeEx(header.CStr(), .DefaultOpen | .AllowOverlap | .Framed | .SpanFullWidth | .SpanAllColumns);
if (showComponentContextMenu != null)
{
@@ -169,20 +161,13 @@ namespace GlitchyEditor.EditWindows
}
}
if (nodeOpen)
if (nodeOpen && ImGui.BeginTableEx("properties", TableId, 2, .SizingStretchSame | .BordersInner | .Resizable))
{
if (ImGui.BeginTableEx("properties", TableId, 2, .SizingStretchSame | .BordersInner | .Resizable))
{
if (entity.TryGetComponent<TComponent>(let actualComponent))
showComponentEditor(entity, actualComponent);
if (entity.TryGetComponent<TComponent>(let actualComponent))
showComponentEditor(entity, actualComponent);
ImGui.EndTable();
}
//ImGui.TreePop();
ImGui.EndTable();
}
//ImGui.PopID();
}
/// Starts a new property by creating a new table row, writing the name in the first column and entering the second column.
@@ -191,11 +176,17 @@ namespace GlitchyEditor.EditWindows
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
if (ImGui.TableGetRowIndex() == 0)
ImGui.PushItemWidth(-1);
ImGui.TextUnformatted(propertyName);
ImGui.AttachTooltip(propertyName);
ImGui.TableSetColumnIndex(1);
if (ImGui.TableGetRowIndex() == 0)
ImGui.PushItemWidth(-1);
}
private static void ShowNameComponentEditor(Entity entity)
@@ -221,8 +212,12 @@ namespace GlitchyEditor.EditWindows
// Copy name to buffer
Internal.MemCpy(&nameBuffer, name.Ptr, Math.Min(nameBuffer.Count, name.Length));
ImGui.PushItemWidth(-1);
StartNewProperty("Name");
ImGui.PushItemWidth(-1);
if(ImGui.InputText("##Name", &nameBuffer, nameBuffer.Count, .EnterReturnsTrue))
{
if(component == null)
+15 -4
View File
@@ -284,11 +284,17 @@ namespace ImGui
PushID(label);
defer PopID();
PushMultiItemsWidths(NumComponents, CalcItemWidth());
//PushMultiItemsWidths(NumComponents, CalcItemWidth());
float totalWidth = CalcItemWidth();
float componentWidth = totalWidth / NumComponents;
float lineHeight = GetFont().FontSize + GetStyle().FramePadding.y * 2.0f;
ImGui.Vec2 buttonSize = .(lineHeight + 3.0f, lineHeight);
float dragFloatWidth = componentWidth - buttonSize.x - GetStyle().FramePadding.x;
componentLoop: for (int i < NumComponents)
{
if (i > 0)
@@ -300,7 +306,9 @@ namespace ImGui
PushStyleColor(.ButtonHovered, VectorButtonColors[i].Hovered.ImGuiU32);
PushStyleColor(.ButtonActive, VectorButtonColors[i].Active.ImGuiU32);
ImGui.BeginDisabled(!componentEnabled[i]);
BeginDisabled(!componentEnabled[i]);
//PushItemWidth(buttonSize.x);
if (Button(componentNames[i], buttonSize))
{
@@ -325,16 +333,19 @@ namespace ImGui
format = numberFormat[0].ToScopeCStr!:componentLoop();
}
PushItemWidth(dragFloatWidth);
if (DragFloat(componentIds[i], &value[i], dragSpeed, minValue[i], maxValue[i], format))
{
changed = true;
}
PopItemWidth();
PopStyleVar();
ImGui.EndDisabled();
EndDisabled();
PopItemWidth();
PopStyleColor(3);
}
+1
View File
@@ -60,6 +60,7 @@ public class DictionaryEditor
_dictionaryForNewValue = dictionary;
_newDictionaryValue = new DictionaryEntry();
_newDictionaryValue.Value = ActivatorExtension.CreateDefaultValue(valueType);
}
ImGuiExtension.AttachTooltip("Add a new Entry to the dictionary.");
+222 -124
View File
@@ -13,6 +13,7 @@ using System.Text;
using GlitchyEngine.Core;
using GlitchyEngine.Extensions;
using GlitchyEngine.Math;
using GlitchyEngine.Math.Attributes;
using ImGuiNET;
using Component = GlitchyEngine.Core.Component;
@@ -66,13 +67,6 @@ internal class EntityEditor
ImGui.TableSetColumnIndex(0);
}
public static void EndTable()
{
ImGui.EndTable();
ImGui.GetItemID();
}
public static bool StartNewProperty_NewRow = true;
/// <summary>
@@ -184,8 +178,10 @@ internal class EntityEditor
return DidNotChange;
}
private static object? ShowPrimitiveEditor(object? reference, Type fieldType, string fieldName, IEnumerable<Attribute>? attributes)
private static object ShowPrimitiveEditor(object reference, Type fieldType, string fieldName, IEnumerable<Attribute>? attributes)
{
Debug.Assert(reference != null);
string fieldId = StartNewProperty(fieldName);
object newValue = DidNotChange;
@@ -260,119 +256,112 @@ internal class EntityEditor
}
}
if (reference != null)
if (fieldType == typeof(bool))
{
if (fieldType == typeof(bool))
bool value = (bool)reference;
if (ImGui.Checkbox(fieldId, ref value))
newValue = value;
}
else if (fieldType == typeof(char))
{
unsafe
{
bool value = (bool)reference;
if (ImGui.Checkbox(fieldId, ref value))
newValue = value;
}
else if (fieldType == typeof(char))
{
unsafe
// TODO: Allow escaped unicode chars \u XXXX
// TODO: Add text input validation, so that it isn't possible to type invalid chars
char* value = stackalloc char[4];
switch((char)reference)
{
// TODO: Allow escaped unicode chars \u XXXX
// TODO: Add text input validation, so that it isn't possible to type invalid chars
case '\a':
value[0] = '\\';
value[1] = 'a';
break;
case '\b':
value[0] = '\\';
value[1] = 'b';
break;
case '\f':
value[0] = '\\';
value[1] = 'f';
break;
case '\n':
value[0] = '\\';
value[1] = 'n';
break;
case '\r':
value[0] = '\\';
value[1] = 'r';
break;
case '\t':
value[0] = '\\';
value[1] = 't';
break;
case '\v':
value[0] = '\\';
value[1] = 'v';
break;
default:
value[0] = (char)reference;
value[1] = '\0';
break;
};
char* value = stackalloc char[4];
switch((char)reference)
// Enough space to store two code points
byte* buffer = stackalloc byte[8];
int encodedBytes = Encoding.UTF8.GetBytes(value, 2, buffer, 8);
// Place string delimiter after encoded UTF8 sequence.
buffer[encodedBytes] = (byte)'\0';
if (ImGui.InputText(fieldId, (IntPtr)buffer, 8))
{
if (buffer[0] == '\\' && buffer[1] != '\0')
{
case '\a':
value[0] = '\\';
value[1] = 'a';
break;
case '\b':
value[0] = '\\';
value[1] = 'b';
break;
case '\f':
value[0] = '\\';
value[1] = 'f';
break;
case '\n':
value[0] = '\\';
value[1] = 'n';
break;
case '\r':
value[0] = '\\';
value[1] = 'r';
break;
case '\t':
value[0] = '\\';
value[1] = 't';
break;
case '\v':
value[0] = '\\';
value[1] = 'v';
break;
default:
value[0] = (char)reference;
value[1] = '\0';
break;
};
// Enough space to store two code points
byte* buffer = stackalloc byte[8];
int encodedBytes = Encoding.UTF8.GetBytes(value, 2, buffer, 8);
// Place string delimiter after encoded UTF8 sequence.
buffer[encodedBytes] = (byte)'\0';
if (ImGui.InputText(fieldId, (IntPtr)buffer, 8))
newValue = (char)buffer[1] switch
{
'a' => newValue = '\a',
'b' => newValue = '\b',
'f' => newValue = '\f',
'n' => newValue = '\n',
'r' => newValue = '\r',
't' => newValue = '\t',
'v' => newValue = '\v',
_ => newValue = '\\'
};
}
else
{
if (buffer[0] == '\\' && buffer[1] != '\0')
{
newValue = (char)buffer[1] switch
{
'a' => newValue = '\a',
'b' => newValue = '\b',
'f' => newValue = '\f',
'n' => newValue = '\n',
'r' => newValue = '\r',
't' => newValue = '\t',
'v' => newValue = '\v',
_ => newValue = '\\'
};
}
else
{
// Only decode first codepoint
Encoding.UTF8.GetChars(buffer, 4, value, 4);
// Only decode first codepoint
Encoding.UTF8.GetChars(buffer, 4, value, 4);
newValue = value[0];
}
newValue = value[0];
}
}
}
else if (fieldType == typeof(byte))
DragScalar<byte>(ImGuiDataType.U8);
else if (fieldType == typeof(sbyte))
DragScalar<sbyte>(ImGuiDataType.S8);
else if (fieldType == typeof(short))
DragScalar<short>(ImGuiDataType.S16);
else if (fieldType == typeof(ushort))
DragScalar<ushort>(ImGuiDataType.U16);
else if (fieldType == typeof(int))
DragScalar<int>(ImGuiDataType.S32);
else if (fieldType == typeof(uint))
DragScalar<uint>(ImGuiDataType.U32);
else if (fieldType == typeof(long))
DragScalar<long>(ImGuiDataType.S64);
else if (fieldType == typeof(ulong))
DragScalar<ulong>(ImGuiDataType.U64);
else if (fieldType == typeof(float))
DragScalar<float>(ImGuiDataType.Float);
else if (fieldType == typeof(double))
DragScalar<double>(ImGuiDataType.Double);
else
{
ImGui.TextColored(new Vector4(1, 0, 0, 1), $"{fieldName}: Type {fieldType} is not implemented.");
}
}
else if (fieldType == typeof(byte))
DragScalar<byte>(ImGuiDataType.U8);
else if (fieldType == typeof(sbyte))
DragScalar<sbyte>(ImGuiDataType.S8);
else if (fieldType == typeof(short))
DragScalar<short>(ImGuiDataType.S16);
else if (fieldType == typeof(ushort))
DragScalar<ushort>(ImGuiDataType.U16);
else if (fieldType == typeof(int))
DragScalar<int>(ImGuiDataType.S32);
else if (fieldType == typeof(uint))
DragScalar<uint>(ImGuiDataType.U32);
else if (fieldType == typeof(long))
DragScalar<long>(ImGuiDataType.S64);
else if (fieldType == typeof(ulong))
DragScalar<ulong>(ImGuiDataType.U64);
else if (fieldType == typeof(float))
DragScalar<float>(ImGuiDataType.Float);
else if (fieldType == typeof(double))
DragScalar<double>(ImGuiDataType.Double);
else
{
return ActivatorExtension.CreateInstanceSafe(fieldType);
ImGui.TextColored(new Vector4(1, 0, 0, 1), $"{fieldName}: Type {fieldType} is not implemented.");
}
return newValue;
@@ -434,7 +423,7 @@ internal class EntityEditor
}
else if (fieldType.IsPrimitive)
{
newValue = ShowPrimitiveEditor(reference, fieldType, fieldName, attributes);
newValue = ShowPrimitiveEditor(reference!, fieldType, fieldName, attributes);
}
else if (fieldType.IsValueType)
{
@@ -442,22 +431,18 @@ internal class EntityEditor
{
newValue = ShowDecimalEditor(reference, fieldType, fieldName, attributes);
}
else if (fieldType == typeof(bool2))
else if (fieldType == typeof(ColorRGBA))
{
// TODO: The bool vector editor should not be here...
string fieldId = StartNewProperty(fieldName);
bool2 value = (bool2)reference!;
if (ImGui.Checkbox($"##{fieldName}X", ref value.X))
newValue = value;
ImGui.SameLine();
if (ImGui.Checkbox($"##{fieldName}Y", ref value.Y))
ColorRGBA value = (ColorRGBA)reference!;
if (ImGui.ColorEdit4(fieldId, ref Unsafe.As<ColorRGBA, Vector4>(ref value)))
newValue = value;
}
else if (fieldType.TryGetCustomAttribute(out VectorAttribute vectorAttribute))
{
newValue = ShowVectorEditor(reference, fieldType, fieldName, vectorAttribute);
}
else
{
newValue = ShowObjectEditor(reference, fieldType, fieldName, attributes);
@@ -485,6 +470,121 @@ internal class EntityEditor
return newValue;
}
private static object ShowVectorEditor(object? reference, Type fieldType, string fieldName, VectorAttribute vectorAttribute)
{
object newValue = DidNotChange;
string fieldId = StartNewProperty(fieldName);
if (vectorAttribute.Type == typeof(bool))
{
if (vectorAttribute.ComponentCount == 2)
{
bool2 value = (bool2)reference!;
if (ImGuiExtension.Checkbox2(fieldId, ref value))
newValue = value;
}
else if (vectorAttribute.ComponentCount == 3)
{
bool3 value = (bool3)reference!;
if (ImGuiExtension.Checkbox3(fieldId, ref value))
newValue = value;
}
else if (vectorAttribute.ComponentCount == 4)
{
bool4 value = (bool4)reference!;
if (ImGuiExtension.Checkbox4(fieldId, ref value))
newValue = value;
}
}
else if (vectorAttribute.Type == typeof(int))
{
if (vectorAttribute.ComponentCount == 2)
{
int2 value = (int2)reference!;
if (ImGui.DragInt2(fieldId, ref value.X))
newValue = value;
}
else if (vectorAttribute.ComponentCount == 3)
{
int3 value = (int3)reference!;
if (ImGui.DragInt3(fieldId, ref value.X))
newValue = value;
}
else if (vectorAttribute.ComponentCount == 4)
{
int4 value = (int4)reference!;
if (ImGui.DragInt4(fieldId, ref value.X))
newValue = value;
}
}
else if (vectorAttribute.Type == typeof(uint))
{
if (vectorAttribute.ComponentCount == 2)
{
uint2 value = (uint2)reference!;
if (ImGuiExtension.DragUInt2(fieldId, ref value))
newValue = value;
}
else if (vectorAttribute.ComponentCount == 3)
{
uint3 value = (uint3)reference!;
if (ImGuiExtension.DragUInt3(fieldId, ref value))
newValue = value;
}
else if (vectorAttribute.ComponentCount == 4)
{
uint4 value = (uint4)reference!;
if (ImGuiExtension.DragUInt4(fieldId, ref value))
newValue = value;
}
}
else if (vectorAttribute.Type == typeof(float))
{
if (vectorAttribute.ComponentCount == 2)
{
float2 value = (float2)reference!;
if (ImGui.DragFloat2(fieldId, ref Unsafe.As<float2, Vector2>(ref value)))
newValue = value;
}
else if (vectorAttribute.ComponentCount == 3)
{
float3 value = (float3)reference!;
if (ImGui.DragFloat3(fieldId, ref Unsafe.As<float3, Vector3>(ref value)))
newValue = value;
}
else if (vectorAttribute.ComponentCount == 4)
{
float4 value = (float4)reference!;
if (ImGui.DragFloat4(fieldId, ref Unsafe.As<float4, Vector4>(ref value)))
newValue = value;
}
}
else if (vectorAttribute.Type == typeof(double))
{
if (vectorAttribute.ComponentCount == 2)
{
double2 value = (double2)reference!;
if (ImGuiExtension.DragDouble2(fieldId, ref value))
newValue = value;
}
else if (vectorAttribute.ComponentCount == 3)
{
double3 value = (double3)reference!;
if (ImGuiExtension.DragDouble3(fieldId, ref value))
newValue = value;
}
else if (vectorAttribute.ComponentCount == 4)
{
double4 value = (double4)reference!;
if (ImGuiExtension.DragDouble4(fieldId, ref value))
newValue = value;
}
}
return newValue;
}
private static object? ShowObjectEditor(object? reference, Type fieldType, string fieldName, IEnumerable<Attribute> attributes)
{
object? newValue = DidNotChange;
@@ -846,9 +946,7 @@ internal class EntityEditor
try
{
// Reference values can be null
object? newElement = elementType.IsByRef ? null : ActivatorExtension.CreateInstanceSafe(elementType);
object? newElement = ActivatorExtension.CreateDefaultValue(elementType);
list?.Add(newElement);
}
catch (Exception ex)
@@ -913,7 +1011,7 @@ internal class EntityEditor
newList = newArray;
}
object newElement = Activator.CreateInstance(elementType);
object? newElement = ActivatorExtension.CreateDefaultValue(elementType!);
((IList)newList)[newIndex] = newElement;
}
@@ -66,4 +66,13 @@ public static class ActivatorExtension
return component;
}
/// <summary>
/// Returns the null for reference types and an initialized value for structs / value types.
/// </summary>
/// <param name="type">The type to create the default value for.</param>
public static object? CreateDefaultValue(Type type)
{
return type.IsClass ? null : CreateInstanceSafe(type);
}
}
+51
View File
@@ -1,4 +1,6 @@
using System;
using System.Runtime.CompilerServices;
using GlitchyEngine.Math;
using ImGuiNET;
namespace GlitchyEngine.Extensions;
@@ -71,4 +73,53 @@ public static class ImGuiExtension
{
ScriptGlue.ImGuiExtension_ListElementGrabber();
}
public static bool Checkbox2(string label, ref bool2 value) => CheckboxN(2, label, ref value.X);
public static bool Checkbox3(string label, ref bool3 value) => CheckboxN(3, label, ref value.X);
public static bool Checkbox4(string label, ref bool4 value) => CheckboxN(4, label, ref value.X);
public static bool CheckboxN(int componentCount, string label, ref bool value)
{
bool changed = false;
for (int i = 0; i < componentCount; i++)
{
if (i != 0)
ImGui.SameLine();
changed |= ImGui.Checkbox($"##{label}{i}", ref Unsafe.Add(ref value, i));
}
return changed;
}
public static unsafe bool DragUInt2(string label, ref uint2 value)
{
return ImGui.DragScalarN(label, ImGuiDataType.U32, (IntPtr)Unsafe.AsPointer(ref value), 2);
}
public static unsafe bool DragUInt3(string label, ref uint3 value)
{
return ImGui.DragScalarN(label, ImGuiDataType.U32, (IntPtr)Unsafe.AsPointer(ref value), 3);
}
public static unsafe bool DragUInt4(string label, ref uint4 value)
{
return ImGui.DragScalarN(label, ImGuiDataType.U32, (IntPtr)Unsafe.AsPointer(ref value), 4);
}
public static unsafe bool DragDouble2(string label, ref double2 value)
{
return ImGui.DragScalarN(label, ImGuiDataType.Double, (IntPtr)Unsafe.AsPointer(ref value), 2);
}
public static unsafe bool DragDouble3(string label, ref double3 value)
{
return ImGui.DragScalarN(label, ImGuiDataType.Double, (IntPtr)Unsafe.AsPointer(ref value), 3);
}
public static unsafe bool DragDouble4(string label, ref double4 value)
{
return ImGui.DragScalarN(label, ImGuiDataType.Double, (IntPtr)Unsafe.AsPointer(ref value), 4);
}
}