Files
Convention-Unity/Convention/[Visual]/UIComponent/Variant/InspectorWindow/InspectorItem.cs
2025-07-21 15:58:52 +08:00

409 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using static Convention.WindowsUI.Variant.PropertiesWindow;
namespace Convention.WindowsUI.Variant
{
/// <summary>
/// enum&1==1ÔòΪ¶¯Ì¬Éú³ÉÀàÐÍ
/// </summary>
public enum InspectorDrawType
{
// Auto
Auto = -1,
// String
Text = 0,
// Bool
Toggle = 1 << 1,
// Sripte
Image = 1 << 2,
// Transform
Transform = 1 << 3,
// Container
List = 1 << 4 + 1, Dictionary = 1 << 5 + 1, Array = 1 << 6 + 1,
// Object
Reference = 1 << 7, Structure = 1 << 8,
// Method
Button = 1 << 9,
// Enum
Enum = 1 << 10
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class InspectorDrawAttribute : Attribute
{
public readonly InspectorDrawType drawType;
public readonly bool isUpdateAble = true;
public readonly bool isChangeAble = true;
public readonly string name = null;
// Get Real Inspector Name: Field
public readonly string nameGenerater = null;
// Get Real Enum Names: Method
public readonly string enumGenerater = null;
public InspectorDrawAttribute()
{
this.drawType = InspectorDrawType.Auto;
}
public InspectorDrawAttribute(InspectorDrawType drawType = InspectorDrawType.Auto, bool isUpdateAble = true,
bool isChangeAble = true, string name = null, string nameGenerater = null, string enumGenerater = null)
{
this.drawType = drawType;
this.isUpdateAble = isUpdateAble;
this.isChangeAble = isChangeAble;
this.name = name;
this.nameGenerater = nameGenerater;
this.enumGenerater = enumGenerater;
}
}
public interface IInspectorUpdater
{
void OnInspectorUpdate();
}
public abstract class InspectorDrawer : WindowUIModule
{
[Resources, SerializeField] private InspectorItem m_targetItem;
public InspectorItem targetItem { get => m_targetItem; private set => m_targetItem = value; }
public virtual void OnInspectorItemInit(InspectorItem item)
{
targetItem = item;
}
}
public class InspectorItem : PropertyListItem,ITitle
{
[Resources, OnlyNotNullMode, SerializeField] private Text Title;
[Resources, OnlyNotNullMode, SerializeField, Header("Inspector Components")]
private InspectorDrawer m_TransformModule;
[Resources, OnlyNotNullMode, SerializeField]
private InspectorDrawer m_TextModule, m_ToggleModule, m_ImageModule, m_ReferenceModule,
m_ButtonModule, m_StructureModule, m_DictionaryItemModule, m_EnumItemModule;
private Dictionary<InspectorDrawType, InspectorDrawer> m_AllUIModules = new();
private List<ItemEntry> m_DynamicSubEntries = new();
[Content, OnlyPlayMode] public object target;
public MemberInfo targetMemberInfo { get; private set; }
public ValueWrapper targetValueWrapper { get; private set; }
public Action targetFunctionCall { get; private set; }
[Setting, SerializeField] private InspectorDrawType targetDrawType;
[Setting, SerializeField] private bool targetAbleChangeMode = true;
[Setting, SerializeField] private bool targetUpdateMode = true;
[Setting, SerializeField] public InspectorDrawAttribute targetDrawer { get; private set; }
public InspectorDrawer CurrentModule => m_AllUIModules[targetDrawType];
public InspectorDrawType DrawType
{
get => targetDrawType;
set => targetDrawType = value;
}
private void EnableDrawType()
{
if ((1 & (int)targetDrawType) == 0)
m_AllUIModules[targetDrawType].gameObject.SetActive(true);
else if (targetDrawType == InspectorDrawType.List)
{
Type listType = (targetMemberInfo != null)
? ConventionUtility.SeekValue(target, targetMemberInfo).GetType().GetGenericArguments()[0]
: targetValueWrapper.GetValue().GetType().GetGenericArguments()[0];
m_DynamicSubEntries = CreateSubPropertyItem(2);
m_DynamicSubEntries[0].ref_value.GetComponent<InspectorItem>().SetTarget(null, () =>
{
(GetValue() as IList).Add(ConventionUtility.GetDefault(listType));
});
m_DynamicSubEntries[1].ref_value.GetComponent<InspectorItem>().SetTarget(null, () =>
{
var list = (IList)GetValue();
int length = list.Count;
if (length != 0)
(GetValue() as IList).RemoveAt(length - 1);
});
CreateSequenceItems(2);
}
else if (targetDrawType == InspectorDrawType.Dictionary)
{
}
else if (targetDrawType == InspectorDrawType.Array)
{
m_DynamicSubEntries = new();
CreateSequenceItems();
}
else
throw new InvalidOperationException($"Unknown {nameof(InspectorDrawType)}: {targetDrawType}");
void CreateSequenceItems(int offset = 0)
{
var array = (IList)GetValue();
Type arrayType = (targetMemberInfo != null)
? ConventionUtility.SeekValue(target, targetMemberInfo).GetType().GetGenericArguments()[0]
: targetValueWrapper.GetValue().GetType().GetGenericArguments()[0];
int length = array.Count;// (int)ConventionUtility.SeekValue(array, nameof(Array.Length), BindingFlags.Default);
m_DynamicSubEntries.AddRange(CreateSubPropertyItem(length));
int index = 0;
foreach (var item in array)
{
m_DynamicSubEntries[index + offset].ref_value.GetComponent<PropertyListItem>().title = index.ToString();
m_DynamicSubEntries[index + offset].ref_value.GetComponent<InspectorItem>().SetTarget(null, new ValueWrapper(
() => array[index],
(x) => array[index] = x,
arrayType
));
index++;
}
}
}
private void DisableDrawType()
{
if ((1 & (int)targetDrawType) == 0)
m_AllUIModules[targetDrawType].gameObject.SetActive(false);
else
{
foreach (var item in m_DynamicSubEntries)
{
item.Release();
}
m_DynamicSubEntries.Clear();
}
}
public bool AbleChangeType
{
get => targetAbleChangeMode;
set => targetAbleChangeMode = value;
}
public bool UpdateType
{
get => targetUpdateMode;
set => targetUpdateMode = value;
}
public static string BroadcastName => $"On{nameof(InspectorItem)}Init";
private void InitModules()
{
m_AllUIModules[InspectorDrawType.Text] = m_TextModule;
m_AllUIModules[InspectorDrawType.Toggle] = m_ToggleModule;
m_AllUIModules[InspectorDrawType.Image] = m_ImageModule;
m_AllUIModules[InspectorDrawType.Transform] = m_TransformModule;
m_AllUIModules[InspectorDrawType.Reference] = m_ReferenceModule;
m_AllUIModules[InspectorDrawType.Structure] = m_StructureModule;
m_AllUIModules[InspectorDrawType.Button] = m_ButtonModule;
m_AllUIModules[InspectorDrawType.Enum] = m_EnumItemModule;
MakeInspectorItemInit();
}
private void MakeInspectorItemInit()
{
foreach (var module in m_AllUIModules)
{
module.Value.OnInspectorItemInit(this);
}
}
public void SetTarget([In] object target, MemberInfo member)
{
this.target = target;
this.targetMemberInfo = member;
this.targetValueWrapper = null;
this.targetFunctionCall = null;
InitModules();
RebulidImmediate();
}
public void SetTarget([In] object target,ValueWrapper wrapper)
{
this.target = target;
this.targetMemberInfo = null;
this.targetValueWrapper = wrapper;
this.targetFunctionCall = null;
InitModules();
RebulidImmediate();
}
public void SetTarget([In] object target,Action action)
{
this.target = target;
this.targetMemberInfo = null;
this.targetValueWrapper = null;
this.targetFunctionCall = action;
InitModules();
RebulidImmediate();
}
public void SetValue([In] object value)
{
if (targetMemberInfo != null)
ConventionUtility.PushValue(target, value, targetMemberInfo);
else if (targetValueWrapper != null)
targetValueWrapper.SetValue(value);
else
throw new InvalidOperationException();
}
public object GetValue()
{
if (targetMemberInfo != null)
return ConventionUtility.SeekValue(target, targetMemberInfo);
else if (targetValueWrapper != null)
return targetValueWrapper.GetValue();
else
throw new InvalidOperationException();
}
public Type GetValueType()
{
if (targetMemberInfo != null)
return ConventionUtility.GetMemberValueType(targetMemberInfo);
else if (targetValueWrapper != null)
return targetValueWrapper.type;
else
throw new InvalidOperationException();
}
public void InvokeAction()
{
if (targetFunctionCall != null)
targetFunctionCall.Invoke();
else
ConventionUtility.InvokeMember(targetMemberInfo, target);
}
[Content, OnlyPlayMode]
public void RebulidImmediate()
{
if (targetMemberInfo != null)
{
RebuildWithMemberInfo();
}
else if (targetValueWrapper != null)
{
RebuildWithWrapper();
}
else if (targetFunctionCall != null)
{
RebuildWithFunctionCall();
}
void RebuildWithMemberInfo()
{
InspectorDrawAttribute drawAttr = null;
ArgPackageAttribute argAttr = null;
Type type = null;
// Reset AbleChangeType
this.targetDrawer = drawAttr = targetMemberInfo.GetCustomAttribute<InspectorDrawAttribute>(true);
argAttr = targetMemberInfo.GetCustomAttribute<ArgPackageAttribute>(true);
type = ConventionUtility.GetMemberValueType(targetMemberInfo);
AbleChangeType = targetMemberInfo.GetCustomAttributes(typeof(IgnoreAttribute), true).Length == 0;
// Reset DrawType
DisableDrawType();
if (drawAttr != null)
{
AbleChangeType &= drawAttr.isChangeAble;
UpdateType = drawAttr.isUpdateAble;
if (drawAttr.nameGenerater != null)
{
title = (string)ConventionUtility.SeekValue(target, drawAttr.nameGenerater, typeof(string),
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.GetField);
}
else
{
title = drawAttr.name;
}
}
if (drawAttr != null && drawAttr.drawType != InspectorDrawType.Auto)
{
DrawType = drawAttr.drawType;
}
else if (type != null)
{
if (ConventionUtility.IsEnum(type))
DrawType = InspectorDrawType.Enum;
if (ConventionUtility.IsBool(type))
DrawType = InspectorDrawType.Toggle;
else if (ConventionUtility.IsString(type) || ConventionUtility.IsNumber(type))
DrawType = InspectorDrawType.Text;
else if (ConventionUtility.IsArray(type))
DrawType = InspectorDrawType.Array;
else if (ConventionUtility.IsImage(type))
DrawType = InspectorDrawType.Image;
else if (type.GetInterface(nameof(IEnumerable)) != null && type.GetGenericArguments().Length == 1)
DrawType = InspectorDrawType.List;
else if (type.GetInterface(nameof(IEnumerable)) != null && type.GetGenericArguments().Length == 2)
DrawType = InspectorDrawType.Dictionary;
else if (type == typeof(Transform))
DrawType = InspectorDrawType.Transform;
else if (type.IsClass)
DrawType = InspectorDrawType.Reference;
else
DrawType = InspectorDrawType.Structure;
}
else if (targetMemberInfo is MethodInfo method)
{
DrawType = InspectorDrawType.Button;
}
else
{
throw new NotImplementedException("Reach this location by unknown Impl");
}
EnableDrawType();
RectTransformExtension.AdjustSizeToContainsChilds(transform as RectTransform);
RectTransformExtension.AdjustSizeToContainsChilds(this.Entry.rootWindow.TargetWindowContent);
}
void RebuildWithWrapper()
{
Type type = targetValueWrapper.type;
AbleChangeType = targetValueWrapper.IsChangeAble;
// Reset DrawType
if (ConventionUtility.IsBool(type))
DrawType = InspectorDrawType.Toggle;
else if (ConventionUtility.IsString(type) || ConventionUtility.IsNumber(type))
DrawType = InspectorDrawType.Text;
else if (ConventionUtility.IsArray(type))
DrawType = InspectorDrawType.Array;
else if (type.GetInterface(nameof(IEnumerable)) != null && type.GetGenericArguments().Length == 1)
DrawType = InspectorDrawType.List;
else if (type.GetInterface(nameof(IEnumerable)) != null && type.GetGenericArguments().Length == 2)
DrawType = InspectorDrawType.Dictionary;
else if (type == typeof(Transform))
DrawType = InspectorDrawType.Transform;
else if (type.IsSubclassOf(typeof(Texture)))
DrawType = InspectorDrawType.Image;
else if (type.IsClass)
DrawType = InspectorDrawType.Reference;
else
DrawType = InspectorDrawType.Structure;
RectTransformExtension.AdjustSizeToContainsChilds(transform as RectTransform);
RectTransformExtension.AdjustSizeToContainsChilds(this.Entry.rootWindow.TargetWindowContent);
}
void RebuildWithFunctionCall()
{
DrawType = InspectorDrawType.Button;
}
}
protected override void FoldChilds()
{
base.FoldChilds();
CurrentModule.gameObject.SetActive(false);
}
protected override void UnfoldChilds()
{
base.UnfoldChilds();
CurrentModule.gameObject.SetActive(true);
}
}
/// <summary>
/// ʹÓÃÕâ¸ö½Ó¿Ú, ½«ÔÚGameObject±»SetTargetÔÚInspectorÉÏʱֻչʾÕâ¸öÀàʵÀýµÄÄÚÈÝ,
/// ¶ø²»»áչʾComponentsÒ²²»ÄÜͨ¹ý<To GameObject>Ìø×ªµ½GameObjectµÄComponentsÁбí,
/// ¼û<see cref="InspectorWindow.BuildWindow"/>
/// </summary>
public interface IOnlyFocusThisOnInspector : IAnyClass { }
}