From 0257b93c7b26ec3e6aa57cdf737757ab16267c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Wed, 22 Nov 2023 19:28:02 +0100 Subject: [PATCH] Move List elements via Drag and Drop --- ScriptCore/Editor/EntityEditor.cs | 92 ++++++++++++++++++++++--- ScriptCore/Extensions/ImGuiExtension.cs | 67 ++++++++---------- 2 files changed, 113 insertions(+), 46 deletions(-) diff --git a/ScriptCore/Editor/EntityEditor.cs b/ScriptCore/Editor/EntityEditor.cs index d74bf38..1558a2e 100644 --- a/ScriptCore/Editor/EntityEditor.cs +++ b/ScriptCore/Editor/EntityEditor.cs @@ -36,6 +36,8 @@ internal class EntityEditor public static void ShowDefaultEntityEditor(UUID entityId, Type entityType) { + // TODO: Call custom editors here! + ScriptGlue.Entity_GetScriptInstance(entityId, out object instance); ShowEditor(entityType, instance); @@ -69,16 +71,17 @@ internal class EntityEditor RangeAttribute range = GetAttribute(attributes); + // Get Min and Max Values from Type T T min = ReadStaticField("MinValue"); T max = ReadStaticField("MaxValue"); - float speed = 1.0f; + float dragSpeed = 1.0f; // RangeAttribute takes precedence over Minimum- and Maximum-Attribute if (range != null) { min = ChangeTypeSafe(range.Min); max = ChangeTypeSafe(range.Max); - speed = range.Speed; + dragSpeed = range.Speed; } else { @@ -112,7 +115,7 @@ internal class EntityEditor } else { - if (ImGui.DragScalar(fieldName, dataType, (IntPtr)(&value), speed, (IntPtr)(&min), (IntPtr)(&max))) + if (ImGui.DragScalar(fieldName, dataType, (IntPtr)(&value), dragSpeed, (IntPtr)(&min), (IntPtr)(&max))) { newValue = value; } @@ -134,6 +137,7 @@ internal class EntityEditor { char value = (char)reference; + // TODO: Allow entering chars like '\0', '\n', etc... if (ImGui.InputText(fieldName, (IntPtr)(&value), 2)) newValue = value; } @@ -359,6 +363,12 @@ internal class EntityEditor } } + struct ListPayload + { + public IList List; + public int Element; + } + private static object ShowListEditor(Type fieldType, object list, string fieldName) { object newList = DidNotChange; @@ -389,33 +399,97 @@ internal class EntityEditor myList.Add(newElement); } - ImGui.SetTooltip("Add a new Element at the end of the list."); + ImGuiExtension.AttachTooltip("Add a new Element at the end of the list."); ImGui.SameLine(ImGui.GetWindowContentRegionMax().X - removeButtonWidth); - ImGui.BeginDisabled(myList == null); + ImGui.BeginDisabled(myList == null || myList.Count == 0); if (ImGui.SmallButton("-")) { myList!.RemoveAt(myList.Count - 1); } + ImGuiExtension.AttachTooltip("Remove the last Element from the list."); + ImGui.EndDisabled(); - ImGui.SetTooltip("Remove the last Element from the list."); - if (listOpen) + if (listOpen && myList != null) { - for (int i = 0; i < myList?.Count; i++) + void DropTarget(int insertIndex, string tooltip) + { + if (ImGui.BeginDragDropTarget()) + { + ImGui.SetTooltip(tooltip); + + var payloadPtr = ImGui.AcceptDragDropPayload("SCRIPT_EDITOR_LIST_ELEMENT"); + + unsafe + { + if (payloadPtr.NativePtr != null) + { + ListPayload payload = *(ListPayload*)(payloadPtr.Data); + + // Make sure the index is still correct + if (payload.Element < myList.Count) + { + object item = myList[payload.Element]; + + myList.RemoveAt(payload.Element); + + if (payload.Element >= insertIndex) + myList.Insert(insertIndex, item); + else + // If the original index is before the insert index, the insert index will be moved after removing the element + // thus we have to subtract one. + myList.Insert(insertIndex - 1, item); + } + } + } + + ImGui.EndDragDropTarget(); + } + } + + // Drop at index 0 + ImGui.Separator(); + DropTarget(0, "Drop before Element 0."); + + for (int i = 0; i < myList.Count; i++) { object element = myList[i]; - object newValue = ShowFieldEditor(element, element.GetType(), $"Element {i}"); + // A Bullet point to grab the element + ImGui.Bullet(); + if (ImGui.BeginDragDropSource(ImGuiDragDropFlags.SourceAllowNullID)) + { + unsafe + { + ListPayload payload = new() { List = myList, Element = i }; + ImGui.SetDragDropPayload("SCRIPT_EDITOR_LIST_ELEMENT", (IntPtr)(&payload), (uint)sizeof(ListPayload)); + } + + ImGui.SetTooltip($"Move Element {i}"); + + ImGui.EndDragDropSource(); + } + + DropTarget(i, $"Drop before Element {i}."); + + ImGui.SameLine(); + + object newValue = ShowFieldEditor(element, element.GetType(), $"Element {i}"); + if (newValue != DidNotChange) { myList[i] = newValue; } + + // Drop after current element + ImGui.Separator(); + DropTarget(i + 1, $"Drop after Element {i}."); } ImGui.TreePop(); diff --git a/ScriptCore/Extensions/ImGuiExtension.cs b/ScriptCore/Extensions/ImGuiExtension.cs index 90fafea..7ebfbea 100644 --- a/ScriptCore/Extensions/ImGuiExtension.cs +++ b/ScriptCore/Extensions/ImGuiExtension.cs @@ -34,43 +34,36 @@ public static class ImGuiDataTypeExtension } } -//namespace ImGuiNET; +public static class ImGuiExtension +{ + /// + /// A helper function to attach a tooltip to the previous item. It automatically checks IsItemHovered before showing a tooltip with the given text. + /// + /// The text to be displayed in the tooltip. + public static void AttachTooltip(string tooltip) + { + if (!ImGui.IsItemHovered()) + return; -//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); + ImGui.BeginTooltip(); + + ImGui.TextUnformatted(tooltip); + + ImGui.EndTooltip(); + } + /// + /// A helper function to attach a tooltip to the previous item. It automatically checks IsItemHovered before showing a tooltip with the given content. + /// + /// A method that will be called to show the content of the tooltip. + public static void AttachTooltip(Action tooltipContent) + { + if (!ImGui.IsItemHovered()) + return; -// 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; } + ImGui.BeginTooltip(); -// 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) -//} + tooltipContent(); + + ImGui.EndTooltip(); + } +}