Files
Hasebe/Library/PackageCache/com.annulusgames.alchemy@1402075f473d/Editor/Elements/HashMapFieldBase.cs
T
2026-09-21 12:15:57 +03:00

171 lines
4.8 KiB
C#

using System;
using System.Collections;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace Alchemy.Editor.Elements
{
public abstract class HashMapFieldBase : VisualElement
{
public HashMapFieldBase(object collection, string label)
{
this.collection = collection;
var foldout = new Foldout()
{
text = label
};
Add(foldout);
foldout.Q<Label>().style.unityFontStyleAndWeight = FontStyle.Bold;
contents = new();
foldout.Add(contents);
inputForm = new();
foldout.Add(inputForm);
addButton = new Button(() =>
{
if (isInputting) EndInput();
else StartInput();
})
{
style = {
alignSelf = Align.FlexEnd,
minWidth = 60f
},
text = "+ Add"
};
foldout.Add(addButton);
foldout.Add(new VisualElement() { style = { minHeight = 4f } });
Rebuild();
}
readonly VisualElement inputForm;
readonly VisualElement contents;
readonly Button addButton;
public object Collection => collection;
readonly object collection;
bool isInputting;
public event Action<object> OnValueChanged;
public void RegisterOnValueChangedCallback(Action<object> callback)
{
OnValueChanged += callback;
}
void StartInput()
{
void ValidateValue(object x)
{
var contains = CheckElement(x);
addButton.SetEnabled(!contains);
addButton.text = contains ? "(Invalid key)" : "Done";
}
if (isInputting) return;
isInputting = true;
var initValue = CreateElement();
var form = CreateItem(collection, initValue, "New Value");
inputForm.Clear();
inputForm.Add(form);
form.OnValueChanged += ValidateValue;
form.OnClose += () =>
{
CancelInput();
};
ValidateValue(initValue);
}
void EndInput()
{
if (!isInputting) return;
isInputting = false;
addButton.text = "+ Add";
addButton.SetEnabled(true);
AddElement(inputForm.Q<HashMapItemBase>().Value);
OnValueChanged?.Invoke(collection);
Rebuild();
}
void CancelInput()
{
isInputting = false;
addButton.text = "+ Add";
addButton.SetEnabled(true);
Rebuild();
}
// Rebuild GUI contents
public void Rebuild()
{
contents.Clear();
inputForm.Clear();
if (collection == null) return;
var i = 0;
foreach (var item in (IEnumerable)collection)
{
var element = CreateItem(collection, item, "Element " + i);
element.OnValueChanged += _ => OnValueChanged?.Invoke(collection);
element.OnClose += () =>
{
if (isInputting) return;
var remove = RemoveElement(item);
if (remove)
{
OnValueChanged?.Invoke(collection);
Rebuild();
}
};
element.Lock();
contents.Add(element);
i++;
}
if (i == 0)
{
var box = new Box();
box.style.minHeight = EditorGUIUtility.singleLineHeight * 1.2f;
box.style.paddingLeft = 6f;
var label = new Label(CollectionTypeName + " is empty.");
label.style.height = Length.Percent(100f);
label.style.unityTextAlign = TextAnchor.MiddleLeft;
box.Add(label);
inputForm.Add(box);
}
}
public abstract HashMapItemBase CreateItem(object collection, object elementObj, string label);
public abstract bool CheckElement(object element);
public abstract object CreateElement();
public abstract void AddElement(object element);
public abstract bool RemoveElement(object element);
public abstract void ClearElements();
public abstract string CollectionTypeName { get; }
public abstract class HashMapItemBase : VisualElement
{
public Action OnClose;
public Action<object> OnValueChanged;
public abstract object Value { get; }
public abstract void Lock();
}
}
}