using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
public class ImplementationInspector<T> : Inspector where T : class
{
public ImplementationInspector(Metadata metadata) : base(metadata) { }
private bool compactSelector = false;
private bool hideSelector = false;
private bool indentImplementation = true;
public Metadata implementationMetadata => metadata.value != null ? metadata.Cast(metadata.valueType) : null;
protected virtual GUIContent nullSelectorLabel { get; } = new GUIContent("(Nothing)");
protected virtual GUIContent selectorLabel => new GUIContent(" " + metadata.value, metadata.valueType.Icon()?[IconSize.Small]);
protected virtual EditorTexture typeSelectorIcon => implementationMetadata.definedType.Icon();
protected sealed override float GetHeight(float width, GUIContent label)
{
var height = 0f;
var widthWithoutLabel = WidthWithoutLabel(metadata, width, label);
if (metadata.value == null)
{
height = GetNullSelectorHeight(widthWithoutLabel);
}
else if (compactSelector)
{
height = Mathf.Max(GetCompactSelectorHeight(), GetImplementationHeight(GetCompactedImplementationWidth(widthWithoutLabel)));
}
else
{
height += GetSelectorHeight(widthWithoutLabel);
height += EditorGUIUtility.standardVerticalSpacing;
height += GetImplementationHeight(widthWithoutLabel);
}
return HeightWithLabel(metadata, width, height, label);
}
private float GetNullSelectorHeight(float width)
{
return EditorGUIUtility.singleLineHeight;
}
private float GetSelectorHeight(float width)
{
return EditorGUIUtility.singleLineHeight;
}
private float GetCompactSelectorHeight()
{
return Styles.compactSelector.fixedHeight;
}
private float GetImplementationHeight(float implementationWidth)
{
return LudiqGUI.GetInspectorHeight(this, implementationMetadata, implementationWidth, GUIContent.none);
}
private float GetCompactedImplementationWidth(float width)
{
return width - Styles.spaceAroundCompactSelector - compactSelectorWidth;
}
protected sealed override void OnGUI(Rect position, GUIContent label)
{
if (metadata.value == null)
{
position = BeginLabeledBlock(metadata, position, label);
var nullSelectorPosition = new Rect
(
position.x,
y,
position.width,
GetNullSelectorHeight(position.width)
);
OnNullSelectorGUI(nullSelectorPosition);
}
else if (hideSelector)
{
position = BeginLabeledBlock(metadata, position, label);
OnImplementationGUI(position);
}
else if (compactSelector)
{
position = BeginLabeledBlock(metadata, position, label);
var implementationWidth = GetCompactedImplementationWidth(position.width);
var compactSelectorPosition = new Rect
(
position.x,
y,
compactSelectorWidth,
GetCompactSelectorHeight()
);
var implementationPosition = new Rect
(
compactSelectorPosition.xMax + Styles.spaceAroundCompactSelector,
y,
implementationWidth,
GetImplementationHeight(implementationWidth)
);
OnCompactSelectorGUI(compactSelectorPosition);
OnImplementationGUI(implementationPosition);
}
else
{
if (indentImplementation)
{
position = BeginLabeledBlock(metadata, position, label);
}
else
{
position = BeginLabeledBlock(metadata, position, GUIContent.none);
}
var selectorPosition = position.VerticalSection(ref y, GetSelectorHeight(position.width));
if (!indentImplementation)
{
selectorPosition = PrefixLabel(metadata, selectorPosition, label);
}
y += EditorGUIUtility.standardVerticalSpacing;
var implementationPosition = position.VerticalSection(ref y, GetImplementationHeight(position.width));
OnSelectorGUI(selectorPosition);
OnImplementationGUI(implementationPosition);
}
EndBlock(metadata);
}
private void OnImplementationGUI(Rect implementationPosition)
{
LudiqGUI.Inspector(implementationMetadata, implementationPosition, GUIContent.none);
}
private void OnNullSelectorGUI(Rect nullSelectorPosition)
{
EditorGUI.BeginChangeCheck();
var newImplementation = LudiqGUI.FuzzyPopup
(
nullSelectorPosition,
GetImplementationOptions,
null,
nullSelectorLabel
);
if (EditorGUI.EndChangeCheck())
{
ChangeImplementation(newImplementation);
}
}
private void OnSelectorGUI(Rect selectorPosition)
{
EditorGUI.BeginChangeCheck();
var newImplementation = LudiqGUI.FuzzyPopup
(
selectorPosition,
GetImplementationOptions,
null,
selectorLabel
);
if (EditorGUI.EndChangeCheck())
{
ChangeImplementation(newImplementation);
}
}
private void OnCompactSelectorGUI(Rect selectorPosition)
{
EditorGUI.BeginChangeCheck();
var newImplementation = LudiqGUI.FuzzyPopup
(
selectorPosition,
GetImplementationOptions,
null,
Styles.showCompactSelectorIcon ? new GUIContent(typeSelectorIcon?[IconSize.Small]) : Styles.compactSelectorWithoutIconContent,
Styles.compactSelector
);
if (EditorGUI.EndChangeCheck())
{
ChangeImplementation(newImplementation);
}
}
private void ChangeImplementation(object newImplementation)
{
metadata.RecordUndo();
metadata.UnlinkChildren();
metadata.value = CreateImplementation(newImplementation);
metadata.InferOwnerFromParent();
}
protected virtual IFuzzyOptionTree GetImplementationOptions()
{
return new TypeOptionTree(implementors) { rootMode = TypeOptionTree.RootMode.Types };
}
protected virtual T CreateImplementation(object option)
{
return (T)(((Type)option).Instantiate());
}
static ImplementationInspector()
{
implementors = Codebase.editorTypes
.Where(type => type.HasAttribute<InspectorAttribute>())
.OrderBy(type => type.GetAttribute<InspectorImplementationOrderAttribute>()?.order ?? int.MaxValue)
.SelectMany(type => type.GetAttributes<InspectorAttribute>())
.Select(attribute => attribute.type)
.Where(type => type != typeof(T) && typeof(T).IsAssignableFrom(type))
.ToList();
}
private static readonly List<Type> implementors;
private static float compactSelectorWidth => Styles.compactSelector.fixedWidth;
public static float compactSelectorSubtractedWidth => compactSelectorWidth + Styles.spaceAroundCompactSelector;
public static class Styles
{
static Styles()
{
compactSelectorWithoutIcon = new GUIStyle
{
imagePosition = ImagePosition.ImageOnly,
contentOffset = new Vector2(1, -1),
clipping = TextClipping.Clip,
alignment = TextAnchor.MiddleCenter,
fixedWidth = 9,
fixedHeight = EditorGUIUtility.singleLineHeight
};
compactSelectorWithoutIconContent = new GUIContent(BoltCore.Resources.LoadTexture("TypeSelector.png", new TextureResolution[] { 9, 18 }, CreateTextureOptions.PixelPerfect)?[9]);
compactSelectorWithIcon = new GUIStyle
{
imagePosition = ImagePosition.ImageOnly,
contentOffset = new Vector2(0, 0),
alignment = TextAnchor.MiddleCenter,
fixedWidth = 16,
fixedHeight = EditorGUIUtility.singleLineHeight
};
}
public static readonly GUIStyle compactSelectorWithoutIcon;
public static readonly GUIStyle compactSelectorWithIcon;
public static readonly GUIContent compactSelectorWithoutIconContent;
public static readonly int spaceAroundCompactSelector = 3;
public static readonly bool showCompactSelectorIcon = true;
public static GUIStyle compactSelector => showCompactSelectorIcon ? compactSelectorWithIcon : compactSelectorWithoutIcon;
}
}
}