From ffe86a18cf827b2f0c498512888361df21b56777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Wed, 22 Nov 2023 00:57:18 +0100 Subject: [PATCH] Scripts: First implementation of Range- and Readonly-Attributes + added support for all primitive types --- ScriptCore/Editor/EntityEditor.cs | 133 ++++++++++++++++++------ ScriptCore/Editor/RangeAttribute.cs | 23 ++-- ScriptCore/Editor/ReadonlyAttribute.cs | 7 ++ ScriptCore/Extensions/ImGuiExtension.cs | 76 ++++++++++++++ ScriptCore/ScriptCore.csproj | 1 + 5 files changed, 197 insertions(+), 43 deletions(-) create mode 100644 ScriptCore/Editor/ReadonlyAttribute.cs create mode 100644 ScriptCore/Extensions/ImGuiExtension.cs diff --git a/ScriptCore/Editor/EntityEditor.cs b/ScriptCore/Editor/EntityEditor.cs index c2807f0..79e0361 100644 --- a/ScriptCore/Editor/EntityEditor.cs +++ b/ScriptCore/Editor/EntityEditor.cs @@ -6,6 +6,8 @@ using System.Linq; using System.Reflection; using System.Text; using GlitchyEngine.Core; +using GlitchyEngine.Extensions; +using GlitchyEngine.Math; using ImGuiNET; namespace GlitchyEngine.Editor; @@ -38,8 +40,17 @@ internal class EntityEditor ShowEditor(entityType, instance); } - public static object ShowFieldEditor(object reference, Type fieldType, string fieldName) + public static object ShowFieldEditor(object reference, Type fieldType, string fieldName, IEnumerable attributes = null) { + T GetAttribute() where T : Attribute + { + return (T)attributes?.FirstOrDefault(a => a is T); + } + + ReadonlyAttribute readonlyAttribute = GetAttribute(); + + ImGui.BeginDisabled(readonlyAttribute != null); + object newValue = DidNotChange; if (fieldType.IsGenericType) @@ -76,52 +87,102 @@ internal class EntityEditor { ImGui.Text($"{fieldName} Primitive"); + unsafe void DragScalar(ImGuiDataType dataType) where T : unmanaged + { + RangeAttribute range = GetAttribute(); + + T min = default; + T max = default; + + if (range != null) + { + min = (T)Convert.ChangeType(range.Min, typeof(T)); + max = (T)Convert.ChangeType(range.Max, typeof(T)); + } + + var value = (T)reference; + if (ImGui.DragScalar(fieldName, dataType, (IntPtr)(&value), 1, (IntPtr)(&min), (IntPtr)(&max))) + { + newValue = value; + } + } + if (reference != null) { - if (fieldType == typeof(float)) - { - float f = (float)reference; - if (ImGui.DragFloat(fieldName, ref f)) - { - newValue = f; - } - } - else if (fieldType == typeof(int)) - { - int f = (int)reference; - if (ImGui.DragInt(fieldName, ref f)) - { - newValue = f; - } - } - else if (fieldType == typeof(bool)) + if (fieldType == typeof(bool)) { bool value = (bool)reference; if (ImGui.Checkbox(fieldName, ref value)) newValue = value; } + else if (fieldType == typeof(char)) + { + unsafe + { + char value = (char)reference; - // TODO: Do all the other types + if (ImGui.InputText(fieldName, (IntPtr)(&value), 2)) + newValue = value; + } + } + else if (fieldType == typeof(byte)) + DragScalar(ImGuiDataType.U8); + else if (fieldType == typeof(sbyte)) + DragScalar(ImGuiDataType.S8); + else if (fieldType == typeof(short)) + DragScalar(ImGuiDataType.S16); + else if (fieldType == typeof(ushort)) + DragScalar(ImGuiDataType.U16); + else if (fieldType == typeof(int)) + DragScalar(ImGuiDataType.S32); + else if (fieldType == typeof(uint)) + DragScalar(ImGuiDataType.U32); + else if (fieldType == typeof(long)) + DragScalar(ImGuiDataType.S64); + else if (fieldType == typeof(ulong)) + DragScalar(ImGuiDataType.U64); + else if (fieldType == typeof(float)) + DragScalar(ImGuiDataType.Float); + else if (fieldType == typeof(double)) + DragScalar(ImGuiDataType.Double); } } else if (fieldType.IsValueType) { + if (fieldType == typeof(bool2)) + { + bool2 value = (bool2)reference; + + ImGui.TextUnformatted(fieldName); + + ImGui.SameLine(); + + if (ImGui.Checkbox($"##{fieldName}X", ref value.X)) + newValue = value; + + ImGui.SameLine(); + + if (ImGui.Checkbox($"##{fieldName}Y", ref value.Y)) + newValue = value; + } + + ImGui.EndDisabled(); + // Struct if (ImGui.TreeNode(fieldName)) { - //object @struct = (reference != null) ? field.GetValue(reference) : null; + ImGui.BeginDisabled(readonlyAttribute != null); ShowEditor(fieldType, reference); - - //if (@struct != null) - //{ - // field.SetValue(reference, @struct); - //} - + ImGui.TreePop(); newValue = reference; } + else + { + ImGui.BeginDisabled(readonlyAttribute != null); + } } else if (fieldType.IsSubclassOf(typeof(Entity)) || fieldType == typeof(Entity)) { @@ -135,11 +196,12 @@ internal class EntityEditor } else { + ImGui.EndDisabled(); + if (ImGui.TreeNode(fieldName)) { - //object @class = (reference != null) ? field.GetValue(reference) : null; + ImGui.BeginDisabled(readonlyAttribute != null); - //if (@class == null) if (reference == null) { ImGui.SameLine(ImGui.GetWindowContentRegionMax().X - ImGui.CalcTextSize("Create").X - 2 * ImGui.GetStyle().FramePadding.X); @@ -152,7 +214,6 @@ internal class EntityEditor { object instance = Activator.CreateInstance(t); newValue = instance; - //field.SetValue(reference, instance); } } @@ -166,11 +227,9 @@ internal class EntityEditor if (ImGui.SmallButton("Remove")) { newValue = null; - //field.SetValue(reference, null); } } - //if (@class == null) if (reference == null) ImGui.Text("(NULL)"); else @@ -178,8 +237,14 @@ internal class EntityEditor ImGui.TreePop(); } + else + { + ImGui.BeginDisabled(readonlyAttribute != null); + } } + ImGui.EndDisabled(); + return newValue; } @@ -199,11 +264,13 @@ internal class EntityEditor if (ShowFieldInEditor(field)) { - Log.Info(field.Name); + //Log.Info(field.Name); object value = field.GetValue(reference); - object newValue = ShowFieldEditor(value, value?.GetType() ?? field.FieldType, field.Name); + IEnumerable attributes = field.GetCustomAttributes(); + + object newValue = ShowFieldEditor(value, value?.GetType() ?? field.FieldType, field.Name, attributes); if (newValue != DidNotChange) { diff --git a/ScriptCore/Editor/RangeAttribute.cs b/ScriptCore/Editor/RangeAttribute.cs index 68fe518..3099647 100644 --- a/ScriptCore/Editor/RangeAttribute.cs +++ b/ScriptCore/Editor/RangeAttribute.cs @@ -2,14 +2,17 @@ using System; namespace GlitchyEngine.Editor; -//public sealed class RangeAttribute : Attribute -//{ -// public float Min { get; set; } -// public float Max { get; set; } +public sealed class RangeAttribute : Attribute +{ + public double Min { get; set; } + public double Max { get; set; } -// public RangeAttribute(float min, float max) -// { -// Min = min; -// Max = max; -// } -//} \ No newline at end of file + public RangeAttribute(double min, double max) + { + if (max < min) + throw new ArgumentOutOfRangeException(nameof(max), $"{nameof(max)} must be larger than {nameof(min)}."); + + Min = min; + Max = max; + } +} \ No newline at end of file diff --git a/ScriptCore/Editor/ReadonlyAttribute.cs b/ScriptCore/Editor/ReadonlyAttribute.cs new file mode 100644 index 0000000..859c1ce --- /dev/null +++ b/ScriptCore/Editor/ReadonlyAttribute.cs @@ -0,0 +1,7 @@ +using System; + +namespace GlitchyEngine.Editor; + +public sealed class ReadonlyAttribute : Attribute +{ +} diff --git a/ScriptCore/Extensions/ImGuiExtension.cs b/ScriptCore/Extensions/ImGuiExtension.cs new file mode 100644 index 0000000..90fafea --- /dev/null +++ b/ScriptCore/Extensions/ImGuiExtension.cs @@ -0,0 +1,76 @@ +using System; +using ImGuiNET; + +namespace GlitchyEngine.Extensions; + +public static class ImGuiDataTypeExtension +{ + public static ImGuiDataType GetImGuiDataType(Type type) + { + if (type == typeof(byte)) + return ImGuiDataType.U8; + if (type == typeof(ushort)) + return ImGuiDataType.U16; + if (type == typeof(uint)) + return ImGuiDataType.U32; + if (type == typeof(ulong)) + return ImGuiDataType.U64; + + if (type == typeof(sbyte)) + return ImGuiDataType.S8; + if (type == typeof(short)) + return ImGuiDataType.S16; + if (type == typeof(int)) + return ImGuiDataType.S32; + if (type == typeof(long)) + return ImGuiDataType.S64; + + if (type == typeof(float)) + return ImGuiDataType.Float; + if (type == typeof(double)) + return ImGuiDataType.Double; + + throw new ArgumentException("The type must be an integer or float type.", nameof(type)); + } +} + +//namespace ImGuiNET; + +//public static unsafe partial class ImGui +//{ +// [DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)] +// private static extern unsafe bool ImGui_EditFloat2(byte* label, ref float2 value, float2 resetValues, float dragSpeed, float columnWidth, float2 minValue, float2 maxValue, bool2 componentEnabled); + +// public static bool EditFloat2(string label, ref float2 value, float2 resetValues = default, float dragSpeed = 0.1f, +// float columnWidth = 100f, float2 minValue = default, float2 maxValue = default) +// { +// byte* native_label; +// int label_byteCount = 0; +// if (label != null) +// { +// label_byteCount = Encoding.UTF8.GetByteCount(label); +// if (label_byteCount > Util.StackAllocationSizeLimit) +// { +// native_label = Util.Allocate(label_byteCount + 1); +// } +// else +// { +// byte* native_label_stackBytes = stackalloc byte[label_byteCount + 1]; +// native_label = native_label_stackBytes; +// } +// int native_label_offset = Util.GetUtf8(label, native_label, label_byteCount); +// native_label[native_label_offset] = 0; +// } +// else { native_label = null; } + +// return ImGui_EditFloat2(native_label, ref value, resetValues, dragSpeed, columnWidth, minValue, maxValue, true); +// } + +// public static bool EditFloat2(string label, ref float2 value, float2 resetValues, float dragSpeed, +// float columnWidth, float2 minValue, float2 maxValue, bool2 componentEnabled) +// { +// return ImGui_EditFloat2(label, ref value, resetValues, dragSpeed, columnWidth, minValue, maxValue, true); +// } + +// //ImGui_EditFloat2(char8* label, ref float2 value, float2 resetValues = .Zero, float dragSpeed = 0.1f, float columnWidth = 100f, float2 minValue = .Zero, float2 maxValue = .Zero, bool2 componentEnabled = true) +//} diff --git a/ScriptCore/ScriptCore.csproj b/ScriptCore/ScriptCore.csproj index 7176d8f..accd727 100644 --- a/ScriptCore/ScriptCore.csproj +++ b/ScriptCore/ScriptCore.csproj @@ -7,6 +7,7 @@ true GlitchyEngine true + true