using System;
using System.Collections;
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
public class MetadataDictionaryAdaptor : MetadataCollectionAdaptor
{
public MetadataDictionaryAdaptor(Metadata metadata, Inspector parentDrawer) : base(metadata, parentDrawer)
{
this.metadata = metadata;
metadata.valueChanged += (previousValue) =>
{
if (!metadata.isDictionary)
{
throw new InvalidOperationException("Metadata for dictionary adaptor is not a dictionary: " + metadata);
}
if (metadata.value == null)
{
metadata.value = ConstructDictionary();
}
newKeyMetadata?.Unlink();
newValueMetadata?.Unlink();
newKeyMetadata = metadata.Object("newKey", ConstructKey(), metadata.dictionaryKeyType);
newValueMetadata = metadata.Object("newValue", ConstructValue(), metadata.dictionaryValueType);
};
}
public event Action<object, object> itemAdded;
private Metadata newKeyMetadata;
private Metadata newValueMetadata;
protected Metadata metadata { get; private set; }
protected virtual IDictionary ConstructDictionary()
{
try
{
return (IDictionary)Activator.CreateInstance(metadata.dictionaryType);
}
catch (Exception ex)
{
throw new InvalidOperationException($"Could not create dictionary instance of type '{metadata.dictionaryType}'.", ex);
}
}
protected virtual object ConstructKey()
{
if (metadata.dictionaryKeyType.IsValueType)
{
return Activator.CreateInstance(metadata.dictionaryKeyType);
}
else
{
return null;
}
}
protected virtual object ConstructValue()
{
if (metadata.dictionaryValueType.IsValueType)
{
return Activator.CreateInstance(metadata.dictionaryValueType);
}
else
{
return null;
}
}
private const float spaceBetweenKeyAndValue = 5;
private const float itemPadding = 2;
#region Manipulation
public object this[object key]
{
get
{
return ((IDictionary)metadata)[key];
}
set
{
metadata.RecordUndo();
((IDictionary)metadata)[key] = value;
}
}
public override int Count => metadata.Count + 1;
public override void Add()
{
if (!CanAdd())
{
return;
}
var newKey = newKeyMetadata.value;
var newValue = newValueMetadata.value;
metadata.RecordUndo();
metadata.Add(newKey, newValue);
itemAdded?.Invoke(newKey, newValue);
parentInspector.SetHeightDirty();
}
public override void Clear()
{
metadata.RecordUndo();
metadata.Clear();
parentInspector.SetHeightDirty();
}
public override void Insert(int index)
{
}
public override void Remove(int index)
{
metadata.RecordUndo();
metadata.Remove(metadata.KeyMetadata(index).value);
parentInspector.SetHeightDirty();
}
public override void Move(int sourceIndex, int destinationIndex)
{
}
public override void Duplicate(int index)
{
}
protected bool CanAdd()
{
var newKey = newKeyMetadata.value;
if (newKey == null)
{
EditorUtility.DisplayDialog("New Dictionary Item", "A dictionary key cannot be null.", "OK");
return false;
}
if (metadata.Contains(newKeyMetadata.value))
{
EditorUtility.DisplayDialog("New Dictionary Item", "An item with the same key already exists.", "OK");
return false;
}
return true;
}
public override bool CanDrag(int index)
{
return metadata.isOrderedDictionary && (index != Count - 1);
}
public override bool CanRemove(int index)
{
return index != Count - 1;
}
#endregion
#region Drawing
public override float GetItemAdaptiveWidth(int index)
{
// TODO
return 100;
}
public override float GetItemHeight(float width, int index)
{
if (index == Count - 1)
{
return GetNewItemHeight(width);
}
else
{
return GetItemHeight(metadata.KeyMetadata(index), metadata.ValueMetadata(index), width);
}
}
public override void DrawItem(Rect position, int index)
{
if (index == Count - 1)
{
DrawNewItem(position);
}
else
{
OnItemGUI(metadata.KeyMetadata(index), metadata.ValueMetadata(index), position, false);
}
}
private float GetNewItemHeight(float width)
{
var height = 0f;
height += EditorGUIUtility.singleLineHeight;
height += GetItemHeight(newKeyMetadata, newValueMetadata, width);
return height;
}
private void DrawNewItem(Rect position)
{
var newLabelPosition = new Rect
(
position.x,
position.y,
position.width,
EditorGUIUtility.singleLineHeight
);
var newItemPosition = new Rect
(
position.x,
newLabelPosition.yMax,
position.width,
GetItemHeight(newKeyMetadata, newValueMetadata, position.width)
);
GUI.Label(newLabelPosition, "New Item: ");
OnItemGUI(newKeyMetadata, newValueMetadata, newItemPosition, true);
}
private float GetKeyHeight(Metadata keyMetadata, float keyWidth)
{
return LudiqGUI.GetInspectorHeight(parentInspector, keyMetadata, keyWidth, GUIContent.none);
}
private float GetValueHeight(Metadata valueMetadata, float valueWidth)
{
return LudiqGUI.GetInspectorHeight(parentInspector, valueMetadata, valueWidth, GUIContent.none);
}
private float GetKeyWidth(float width)
{
return (width - spaceBetweenKeyAndValue) / 2;
}
private float GetValueWidth(float width)
{
return (width - spaceBetweenKeyAndValue) / 2;
}
private float GetItemHeight(Metadata keyMetadata, Metadata valueMetadata, float width)
{
return Mathf.Max(GetKeyHeight(keyMetadata, GetKeyWidth(width)), GetValueHeight(valueMetadata, GetValueWidth(width))) + (itemPadding * 2);
}
private void OnKeyGUI(Metadata keyMetadata, Rect keyPosition)
{
LudiqGUI.Inspector(keyMetadata, keyPosition, GUIContent.none);
}
private void OnValueGUI(Metadata valueMetadata, Rect valuePosition)
{
LudiqGUI.Inspector(valueMetadata, valuePosition, GUIContent.none);
}
private void OnItemGUI(Metadata keyMetadata, Metadata valueMetadata, Rect position, bool editableKey)
{
var keyPosition = new Rect
(
position.x + itemPadding,
position.y + itemPadding,
GetKeyWidth(position.width),
GetKeyHeight(keyMetadata, GetKeyWidth(position.width))
);
var valuePosition = new Rect
(
keyPosition.xMax + spaceBetweenKeyAndValue,
position.y + itemPadding,
GetValueWidth(position.width),
GetValueHeight(valueMetadata, GetValueWidth(position.width))
);
EditorGUI.BeginDisabledGroup(!editableKey);
OnKeyGUI(keyMetadata, keyPosition);
EditorGUI.EndDisabledGroup();
OnValueGUI(valueMetadata, valuePosition);
}
#endregion
}
}