TEngine 6

This commit is contained in:
Alex-Rachel
2025-03-07 23:09:46 +08:00
parent aad8ff3ee5
commit 551727687f
1988 changed files with 46223 additions and 94880 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5f915322e2be56345a9d145800f7a0e0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,627 @@
#if UNITY_2021_3_OR_NEWER
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace YooAsset.Editor
{
public class ReorderableListView : VisualElement
{
public new class UxmlFactory : UxmlFactory<ReorderableListView, UxmlTraits>
{
}
/// <summary>
/// 制作元素委托
/// </summary>
/// <returns></returns>
public delegate VisualElement MakeElementDelegate();
/// <summary>
/// 绑定元素委托
/// </summary>
public delegate void BindElementDelegqate(VisualElement element, int index);
private Foldout _foldout;
private ListView _listView;
private Label _headerLabel;
private Button _addButton;
private Button _removeButton;
private string _headerName = nameof(ReorderableListView);
/// <summary>
/// 源数据
/// </summary>
public IList SourceData
{
set
{
if (value is ArrayList)
throw new Exception($"{nameof(SourceData)} not support {nameof(ArrayList)}");
_listView.Clear();
_listView.ClearSelection();
_listView.itemsSource = value;
_listView.Rebuild();
RefreshFoldoutName();
RefreshRemoveButton();
}
get
{
return _listView.itemsSource;
}
}
/// <summary>
/// 元素固定高度
/// </summary>
public float ElementHeight
{
set
{
_listView.fixedItemHeight = value;
_listView.Rebuild();
}
get
{
return _listView.fixedItemHeight;
}
}
/// <summary>
/// 增加按钮显隐
/// </summary>
public bool DisplayAdd
{
set
{
UIElementsTools.SetElementVisible(_addButton, value);
}
get
{
return _addButton.style.visibility == Visibility.Visible;
}
}
/// <summary>
/// 移除按钮显隐
/// </summary>
public bool DisplayRemove
{
set
{
UIElementsTools.SetElementVisible(_removeButton, value);
}
get
{
return _removeButton.style.visibility == Visibility.Visible;
}
}
/// <summary>
/// 标题名称
/// </summary>
public string HeaderName
{
set
{
_headerName = value;
RefreshFoldoutName();
}
get
{
return _headerName;
}
}
/// <summary>
/// 制作元素的回调
/// </summary>
public MakeElementDelegate MakeElementCallback;
/// <summary>
/// 绑定元素的回调
/// </summary>
public BindElementDelegqate BindElementCallback;
public ReorderableListView()
{
CreateView(true);
}
public ReorderableListView(bool foldout)
{
CreateView(foldout);
}
private void CreateView(bool foldout)
{
this.style.flexGrow = 1;
this.style.flexShrink = 1;
// 折叠栏
if (foldout)
{
_foldout = new Foldout();
_foldout.style.flexGrow = 1f;
_foldout.style.flexShrink = 1f;
_foldout.text = $"{nameof(ReorderableListView)}";
}
else
{
_headerLabel = new Label();
}
// 列表视图
_listView = new ListView();
_listView.style.flexGrow = 1;
_listView.style.flexShrink = 1;
_listView.reorderable = true;
_listView.reorderMode = ListViewReorderMode.Animated;
_listView.makeItem = MakeListViewElement;
_listView.bindItem = BindListViewElement;
#if UNITY_2022_3_OR_NEWER
_listView.selectionChanged += OnSelectionChanged;
#elif UNITY_2020_1_OR_NEWER
_listView.onSelectionChange += OnSelectionChanged;
#else
_listView.onSelectionChanged += OnSelectionChanged;
#endif
// 按钮组
var buttonContainer = new VisualElement();
buttonContainer.style.flexDirection = FlexDirection.RowReverse;
// 移除按钮
_removeButton = new Button();
_removeButton.text = " - ";
_removeButton.clicked += OnClickRemoveButton;
_removeButton.SetEnabled(false);
buttonContainer.Add(_removeButton);
// 增加按钮
_addButton = new Button();
_addButton.text = " + ";
_addButton.clicked += OnClickAddButton;
buttonContainer.Add(_addButton);
// 组织页面
if (foldout)
{
_foldout.Add(_listView);
_foldout.Add(buttonContainer);
this.Add(_foldout);
}
else
{
this.Add(_headerLabel);
this.Add(_listView);
this.Add(buttonContainer);
}
}
private void OnClickAddButton()
{
if (_listView.itemsSource != null)
{
object defaultValue = GetElementDefaultValue();
_listView.itemsSource.Add(defaultValue);
_listView.Rebuild();
RefreshFoldoutName();
RefreshRemoveButton();
}
else
{
Debug.LogWarning("The source data is null !");
}
}
private void OnClickRemoveButton()
{
if (_listView.itemsSource != null)
{
if (_listView.selectedIndex >= 0)
{
_listView.itemsSource.RemoveAt(_listView.selectedIndex);
_listView.Rebuild();
RefreshFoldoutName();
RefreshRemoveButton();
}
}
else
{
Debug.LogWarning("The source data is null !");
}
}
private void OnSelectionChanged(IEnumerable<object> objs)
{
RefreshRemoveButton();
}
/// <summary>
/// 生成元素
/// </summary>
private VisualElement MakeListViewElement()
{
if (MakeElementCallback != null)
{
return MakeElementCallback.Invoke();
}
Type elementType = GetElementType();
if (elementType == typeof(string))
{
TextField textField = new TextField();
textField.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)textField.userData;
_listView.itemsSource[itemIndex] = textField.value;
});
return textField;
}
else if (elementType == typeof(int))
{
IntegerField intField = new IntegerField();
intField.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)intField.userData;
_listView.itemsSource[itemIndex] = intField.value;
});
return intField;
}
else if (elementType == typeof(long))
{
LongField longField = new LongField();
longField.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)longField.userData;
_listView.itemsSource[itemIndex] = longField.value;
});
return longField;
}
else if (elementType == typeof(float))
{
FloatField floatField = new FloatField();
floatField.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)floatField.userData;
_listView.itemsSource[itemIndex] = floatField.value;
});
return floatField;
}
else if (elementType == typeof(double))
{
DoubleField doubleField = new DoubleField();
doubleField.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)doubleField.userData;
_listView.itemsSource[itemIndex] = doubleField.value;
});
return doubleField;
}
else if (elementType == typeof(bool))
{
Toggle toggle = new Toggle();
toggle.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)toggle.userData;
_listView.itemsSource[itemIndex] = toggle.value;
});
return toggle;
}
else if (elementType == typeof(Hash128))
{
Hash128Field hash128Field = new Hash128Field();
hash128Field.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)hash128Field.userData;
_listView.itemsSource[itemIndex] = hash128Field.value;
});
return hash128Field;
}
else if (elementType == typeof(Vector2))
{
Vector2Field vector2Field = new Vector2Field();
vector2Field.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)vector2Field.userData;
_listView.itemsSource[itemIndex] = vector2Field.value;
});
return vector2Field;
}
else if (elementType == typeof(Vector3))
{
Vector3Field vector3Field = new Vector3Field();
vector3Field.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)vector3Field.userData;
_listView.itemsSource[itemIndex] = vector3Field.value;
});
return vector3Field;
}
else if (elementType == typeof(Vector4))
{
Vector4Field vector4Field = new Vector4Field();
vector4Field.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)vector4Field.userData;
_listView.itemsSource[itemIndex] = vector4Field.value;
});
return vector4Field;
}
else if (elementType == typeof(Rect))
{
RectField rectField = new RectField();
rectField.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)rectField.userData;
_listView.itemsSource[itemIndex] = rectField.value;
});
return rectField;
}
else if (elementType == typeof(Bounds))
{
BoundsField boundsField = new BoundsField();
boundsField.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)boundsField.userData;
_listView.itemsSource[itemIndex] = boundsField.value;
});
return boundsField;
}
else if (elementType == typeof(Color))
{
ColorField colorField = new ColorField();
colorField.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)colorField.userData;
_listView.itemsSource[itemIndex] = colorField.value;
});
return colorField;
}
else if (elementType == typeof(Gradient))
{
GradientField gradientField = new GradientField();
gradientField.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)gradientField.userData;
_listView.itemsSource[itemIndex] = gradientField.value;
});
return gradientField;
}
else if (elementType == typeof(AnimationCurve))
{
CurveField curveField = new CurveField();
curveField.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)curveField.userData;
_listView.itemsSource[itemIndex] = curveField.value;
});
return curveField;
}
else if (elementType == typeof(UnityEngine.Object))
{
ObjectField objectField = new ObjectField();
objectField.objectType = typeof(UnityEngine.Object);
objectField.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)objectField.userData;
_listView.itemsSource[itemIndex] = objectField.value;
});
return objectField;
}
else if (elementType.IsEnum)
{
EnumField enumField = new EnumField();
enumField.RegisterValueChangedCallback(evt =>
{
int itemIndex = (int)enumField.userData;
_listView.itemsSource[itemIndex] = enumField.value;
});
return enumField;
}
else
{
Label label = new Label();
label.text = $"Not support element type : {elementType.Name}";
return label;
}
}
/// <summary>
/// 绑定元素
/// </summary>
private void BindListViewElement(VisualElement listViewElement, int index)
{
if (BindElementCallback != null)
{
BindElementCallback.Invoke(listViewElement, index);
return;
}
var elementValue = _listView.itemsSource[index];
string elementName = GetElementName(index);
Type elementType = GetElementType();
if (elementType == typeof(string))
{
TextField textField = listViewElement as TextField;
textField.userData = index;
textField.label = elementName;
textField.SetValueWithoutNotify(elementValue as string);
}
else if (elementType == typeof(int))
{
IntegerField intField = listViewElement as IntegerField;
intField.userData = index;
intField.label = elementName;
intField.SetValueWithoutNotify((int)elementValue);
}
else if (elementType == typeof(long))
{
LongField longField = listViewElement as LongField;
longField.userData = index;
longField.label = elementName;
longField.SetValueWithoutNotify((long)elementValue);
}
else if (elementType == typeof(float))
{
FloatField floatField = listViewElement as FloatField;
floatField.userData = index;
floatField.label = elementName;
floatField.SetValueWithoutNotify((float)elementValue);
}
else if (elementType == typeof(double))
{
DoubleField doubleField = listViewElement as DoubleField;
doubleField.userData = index;
doubleField.label = elementName;
doubleField.SetValueWithoutNotify((double)elementValue);
}
else if (elementType == typeof(bool))
{
Toggle toggle = listViewElement as Toggle;
toggle.userData = index;
toggle.label = elementName;
toggle.SetValueWithoutNotify((bool)elementValue);
}
else if (elementType == typeof(Hash128))
{
Hash128Field hash128Field = listViewElement as Hash128Field;
hash128Field.userData = index;
hash128Field.label = elementName;
hash128Field.SetValueWithoutNotify((Hash128)elementValue);
}
else if (elementType == typeof(Vector2))
{
Vector2Field vector2Field = listViewElement as Vector2Field;
vector2Field.userData = index;
vector2Field.label = elementName;
vector2Field.SetValueWithoutNotify((Vector2)elementValue);
}
else if (elementType == typeof(Vector3))
{
Vector3Field vector3Field = listViewElement as Vector3Field;
vector3Field.userData = index;
vector3Field.label = elementName;
vector3Field.SetValueWithoutNotify((Vector3)elementValue);
}
else if (elementType == typeof(Vector4))
{
Vector4Field vector4Field = listViewElement as Vector4Field;
vector4Field.userData = index;
vector4Field.label = elementName;
vector4Field.SetValueWithoutNotify((Vector4)elementValue);
}
else if (elementType == typeof(Rect))
{
RectField rectField = listViewElement as RectField;
rectField.userData = index;
rectField.label = elementName;
rectField.SetValueWithoutNotify((Rect)elementValue);
}
else if (elementType == typeof(Bounds))
{
BoundsField boundsField = listViewElement as BoundsField;
boundsField.userData = index;
boundsField.label = elementName;
boundsField.SetValueWithoutNotify((Bounds)elementValue);
}
else if (elementType == typeof(Color))
{
ColorField colorField = listViewElement as ColorField;
colorField.userData = index;
colorField.label = elementName;
colorField.SetValueWithoutNotify((Color)elementValue);
}
else if (elementType == typeof(Gradient))
{
GradientField gradientField = listViewElement as GradientField;
gradientField.userData = index;
gradientField.label = elementName;
gradientField.SetValueWithoutNotify((Gradient)elementValue);
}
else if (elementType == typeof(AnimationCurve))
{
CurveField curveField = listViewElement as CurveField;
curveField.userData = index;
curveField.label = elementName;
curveField.SetValueWithoutNotify((AnimationCurve)elementValue);
}
else if (elementType == typeof(UnityEngine.Object))
{
ObjectField objectField = listViewElement as ObjectField;
objectField.userData = index;
objectField.label = elementName;
objectField.SetValueWithoutNotify(elementValue as UnityEngine.Object);
}
else if (elementType.IsEnum)
{
EnumField enumField = listViewElement as EnumField;
enumField.userData = index;
enumField.label = elementName;
enumField.Init((Enum)elementValue);
enumField.SetValueWithoutNotify((Enum)elementValue);
}
else
{
}
}
private Type GetElementType()
{
Type elementType = _listView.itemsSource.GetType().GetGenericArguments()[0];
return elementType;
}
private object GetElementDefaultValue()
{
Type type = GetElementType();
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
private string GetElementName(int index)
{
return $"Element {index}";
}
private void RefreshRemoveButton()
{
if (_listView.itemsSource == null)
{
_removeButton.SetEnabled(false);
return;
}
// 注意:数据列表移除元素的时候有可能会越界!
if (_listView.selectedIndex >= _listView.itemsSource.Count)
_listView.ClearSelection();
if (_listView.selectedIndex >= 0)
_removeButton.SetEnabled(true);
else
_removeButton.SetEnabled(false);
}
private void RefreshFoldoutName()
{
if (_listView.itemsSource == null)
{
if (_foldout != null)
_foldout.text = _headerName;
if (_headerLabel != null)
_headerLabel.text = _headerName;
}
else
{
if (_foldout != null)
_foldout.text = _headerName + $" ({_listView.itemsSource.Count}) ";
if (_headerLabel != null)
_headerLabel.text = _headerName + $" ({_listView.itemsSource.Count}) ";
}
}
}
}
#endif

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0c3f4136cf7142346ae33e8a82cbdb27
guid: 111ec0d18888d7e4396b2192f7b4f347
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,108 @@
#if UNITY_2019_4_OR_NEWER
using System;
using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace YooAsset.Editor
{
public class ResizeHandle : VisualElement
{
public new class UxmlFactory : UxmlFactory<ResizeHandle, UxmlTraits>
{
}
private bool _isResizing = false;
private float _initialWidth;
private Vector2 _initialMousePos;
/// <summary>
/// 控制的UI元素
/// </summary>
public VisualElement ControlTarget { get; set; }
/// <summary>
/// 控制元素的最小宽度
/// </summary>
public int ControlMinWidth { get; set; }
/// <summary>
/// 控制元素的最大宽度
/// </summary>
public int ControlMaxWidth { get; set; }
/// <summary>
/// 尺寸发生变化
/// </summary>
public Action<float> ResizeChanged { get; set; }
public ResizeHandle()
{
int defaultWidth = 5;
this.style.width = defaultWidth;
this.style.minWidth = defaultWidth;
this.style.maxWidth = defaultWidth;
this.style.opacity = 0;
this.style.cursor = UIElementsCursor.CreateCursor(MouseCursor.ResizeHorizontal);
this.RegisterCallback<MouseDownEvent>(OnMouseDown);
this.RegisterCallback<MouseMoveEvent>(OnMouseMove);
this.RegisterCallback<MouseUpEvent>(OnMouseUp);
}
public ResizeHandle(int handleWidth, VisualElement controlTarget, int controlMinWidth, int controlMaxWidth)
{
ControlTarget = controlTarget;
ControlMinWidth = controlMinWidth;
ControlMaxWidth = controlMaxWidth;
this.style.width = handleWidth;
this.style.minWidth = handleWidth;
this.style.maxWidth = handleWidth;
this.style.opacity = 0;
this.style.cursor = UIElementsCursor.CreateCursor(MouseCursor.ResizeHorizontal);
this.RegisterCallback<MouseDownEvent>(OnMouseDown);
this.RegisterCallback<MouseMoveEvent>(OnMouseMove);
this.RegisterCallback<MouseUpEvent>(OnMouseUp);
}
private void OnMouseDown(MouseDownEvent evt)
{
// 鼠标左键按下
if (ControlTarget != null && evt.button == 0)
{
_isResizing = true;
_initialWidth = ControlTarget.resolvedStyle.width;
_initialMousePos = evt.mousePosition;
this.CaptureMouse();
}
}
private void OnMouseMove(MouseMoveEvent evt)
{
if (ControlTarget != null && _isResizing)
{
// 计算鼠标移动距离
float deltaX = evt.mousePosition.x - _initialMousePos.x;
// 更新控制元素尺寸
float newWidth = _initialWidth + deltaX;
float width = Mathf.Clamp(newWidth, ControlMinWidth, ControlMaxWidth);
ControlTarget.style.width = width;
ControlTarget.style.minWidth = width;
ControlTarget.style.maxWidth = width;
ResizeChanged?.Invoke(width);
}
}
private void OnMouseUp(MouseUpEvent evt)
{
// 鼠标左键释放
if (ControlTarget != null && evt.button == 0)
{
_isResizing = false;
this.ReleaseMouse();
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: adb565f9b5e9fb3428e54d0cfd58676d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,55 @@
#if UNITY_2019_4_OR_NEWER
using System;
using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace YooAsset.Editor
{
/// <summary>
/// 显示开关(眼睛图标)
/// </summary>
public class ToggleDisplay : Toggle
{
public new class UxmlFactory : UxmlFactory<ToggleDisplay, UxmlTraits>
{
}
private readonly VisualElement _checkbox;
public ToggleDisplay()
{
_checkbox = this.Q<VisualElement>("unity-checkmark");
RefreshIcon();
}
public override void SetValueWithoutNotify(bool newValue)
{
base.SetValueWithoutNotify(newValue);
RefreshIcon();
}
#if UNITY_2021_3_OR_NEWER
protected override void ToggleValue()
{
base.ToggleValue();
RefreshIcon();
}
#endif
private void RefreshIcon()
{
if (this.value)
{
var icon = EditorGUIUtility.IconContent(UIElementsIcon.VisibilityToggleOff).image as Texture2D;
_checkbox.style.backgroundImage = icon;
}
else
{
var icon = EditorGUIUtility.IconContent(UIElementsIcon.VisibilityToggleOn).image as Texture2D;
_checkbox.style.backgroundImage = icon;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 551a9db915a2cf744ac6088e7c634689
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,55 @@
#if UNITY_2019_4_OR_NEWER
using System;
using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace YooAsset.Editor
{
/// <summary>
/// 折叠开关
/// </summary>
public class ToggleFoldout : Toggle
{
public new class UxmlFactory : UxmlFactory<ToggleFoldout, UxmlTraits>
{
}
private readonly VisualElement _checkbox;
public ToggleFoldout()
{
_checkbox = this.Q<VisualElement>("unity-checkmark");
RefreshIcon();
}
public override void SetValueWithoutNotify(bool newValue)
{
base.SetValueWithoutNotify(newValue);
RefreshIcon();
}
#if UNITY_2021_3_OR_NEWER
protected override void ToggleValue()
{
base.ToggleValue();
RefreshIcon();
}
#endif
public void RefreshIcon()
{
if (this.value)
{
var icon = EditorGUIUtility.IconContent(UIElementsIcon.FoldoutOn).image as Texture2D;
_checkbox.style.backgroundImage = icon;
}
else
{
var icon = EditorGUIUtility.IconContent(UIElementsIcon.FoldoutOff).image as Texture2D;
_checkbox.style.backgroundImage = icon;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2619997e70d98794da26a947f9129e25
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,55 @@
#if UNITY_2019_4_OR_NEWER
using System;
using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace YooAsset.Editor
{
/// <summary>
/// 录制开关
/// </summary>
public class ToggleRecord : Toggle
{
public new class UxmlFactory : UxmlFactory<ToggleRecord, UxmlTraits>
{
}
private readonly VisualElement _checkbox;
public ToggleRecord()
{
_checkbox = this.Q<VisualElement>("unity-checkmark");
RefreshIcon();
}
public override void SetValueWithoutNotify(bool newValue)
{
base.SetValueWithoutNotify(newValue);
RefreshIcon();
}
#if UNITY_2021_3_OR_NEWER
protected override void ToggleValue()
{
base.ToggleValue();
RefreshIcon();
}
#endif
private void RefreshIcon()
{
if (this.value)
{
var icon = EditorGUIUtility.IconContent(UIElementsIcon.RecordOn).image as Texture2D;
_checkbox.style.backgroundImage = icon;
}
else
{
var icon = EditorGUIUtility.IconContent(UIElementsIcon.RecordOff).image as Texture2D;
_checkbox.style.backgroundImage = icon;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4eace285493a0844f8a8b8f4a4ea02d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e5f8691a7e0c67d4ab790b703925cd75
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,74 @@
#if UNITY_2019_4_OR_NEWER
using System;
using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace YooAsset.Editor
{
public class ColumnStyle
{
public const float MaxValue = 8388608f;
/// <summary>
/// 单元列宽度
/// </summary>
public Length Width;
/// <summary>
/// 单元列最小宽度
/// </summary>
public Length MinWidth;
/// <summary>
/// 单元列最大宽度
/// </summary>
public Length MaxWidth;
/// <summary>
/// 可伸缩
/// </summary>
public bool Stretchable = false;
/// <summary>
/// 可搜索
/// </summary>
public bool Searchable = false;
/// <summary>
/// 可排序
/// </summary>
public bool Sortable = false;
/// <summary>
/// 统计数量
/// </summary>
public bool Counter = false;
/// <summary>
/// 展示单位
/// </summary>
public string Units = string.Empty;
public ColumnStyle(Length width)
{
if (width.value > MaxValue)
width = MaxValue;
Width = width;
MinWidth = width;
MaxWidth = width;
}
public ColumnStyle(Length width, Length minWidth, Length maxWidth)
{
if (maxWidth.value > MaxValue)
maxWidth = MaxValue;
Width = width;
MinWidth = minWidth;
MaxWidth = maxWidth;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6a74bbcb5d814f94fb54042ec3b6d94c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8b25809b75e1e7141aff3c2011845aab
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
#if UNITY_2019_4_OR_NEWER
using UnityEditor;
namespace YooAsset.Editor
{
public class AssetPathCell : StringValueCell
{
public AssetPathCell(string searchTag, object cellValue) : base(searchTag, cellValue)
{
}
/// <summary>
/// 检视资源对象
/// Ping an asset object in the Scene like clicking it in an inspector.
/// </summary>
public bool PingAssetObject()
{
var assetPath = StringValue;
var assetGUID = AssetDatabase.AssetPathToGUID(assetPath);
if (string.IsNullOrEmpty(assetGUID))
return false;
UnityEngine.Object asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath);
if (asset == null)
return false;
Selection.activeObject = asset;
EditorGUIUtility.PingObject(asset);
return true;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ed1c077653e577846ba29aadb91f13ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
#if UNITY_2019_4_OR_NEWER
using System;
namespace YooAsset.Editor
{
public class BooleanValueCell : ITableCell, IComparable
{
public object CellValue { set; get; }
public string SearchTag { private set; get; }
public bool BooleanValue
{
get
{
return (bool)CellValue;
}
}
public BooleanValueCell(string searchTag, object cellValue)
{
SearchTag = searchTag;
CellValue = cellValue;
}
public object GetDisplayObject()
{
return CellValue.ToString();
}
public int CompareTo(object other)
{
if (other is BooleanValueCell cell)
{
return this.BooleanValue.CompareTo(cell.BooleanValue);
}
else
{
return 0;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7b1e743b81646b49b2c05721edcf9b4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
#if UNITY_2019_4_OR_NEWER
using System;
namespace YooAsset.Editor
{
public class ButtonCell : ITableCell, IComparable
{
public object CellValue { set; get; }
public string SearchTag { private set; get; }
public ButtonCell(string searchTag)
{
SearchTag = searchTag;
}
public object GetDisplayObject()
{
return string.Empty;
}
public int CompareTo(object other)
{
return 1;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe6c2551d70d6934da0b6afce3f1062c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
#if UNITY_2019_4_OR_NEWER
using System;
namespace YooAsset.Editor
{
public class IntegerValueCell : ITableCell, IComparable
{
public object CellValue { set; get; }
public string SearchTag { private set; get; }
public long IntegerValue
{
get
{
return (long)CellValue;
}
}
public IntegerValueCell(string searchTag, object cellValue)
{
SearchTag = searchTag;
CellValue = cellValue;
}
public object GetDisplayObject()
{
return CellValue.ToString();
}
public int CompareTo(object other)
{
if (other is IntegerValueCell cell)
{
return this.IntegerValue.CompareTo(cell.IntegerValue);
}
else
{
return 0;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be8ecb3981efa744f89ee5d3c9e7b7ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
#if UNITY_2019_4_OR_NEWER
using System;
namespace YooAsset.Editor
{
public class SingleValueCell : ITableCell, IComparable
{
public object CellValue { set; get; }
public string SearchTag { private set; get; }
public double SingleValue
{
get
{
return (double)CellValue;
}
}
public SingleValueCell(string searchTag, object cellValue)
{
SearchTag = searchTag;
CellValue = cellValue;
}
public object GetDisplayObject()
{
return CellValue.ToString();
}
public int CompareTo(object other)
{
if (other is SingleValueCell cell)
{
return this.SingleValue.CompareTo(cell.SingleValue);
}
else
{
return 0;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae299b7a930e03f458977750de9ca18e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
#if UNITY_2019_4_OR_NEWER
using System;
namespace YooAsset.Editor
{
public class StringValueCell : ITableCell, IComparable
{
public object CellValue { set; get; }
public string SearchTag { private set; get; }
public string StringValue
{
get
{
return (string)CellValue;
}
}
public StringValueCell(string searchTag, object cellValue)
{
SearchTag = searchTag;
CellValue = cellValue;
}
public object GetDisplayObject()
{
return CellValue;
}
public int CompareTo(object other)
{
if (other is StringValueCell cell)
{
return this.StringValue.CompareTo(cell.StringValue);
}
else
{
return 0;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a04fd2585c5538547b9755673aea76df
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f347ad29484952842a01a1489fbbef5d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
#if UNITY_2019_4_OR_NEWER
using System;
using System.Collections.Generic;
namespace YooAsset.Editor
{
public class DefaultTableData : ITableData
{
/// <summary>
/// 是否可见
/// </summary>
public bool Visible { set; get; } = true;
/// <summary>
/// 单元格集合
/// </summary>
public IList<ITableCell> Cells { set; get; } = new List<ITableCell>();
/// <summary>
/// 添加单元格数据
/// </summary>
public void AddCell(ITableCell cell)
{
Cells.Add(cell);
}
#region
public void AddButtonCell(string searchTag)
{
var cell = new ButtonCell(searchTag);
Cells.Add(cell);
}
public void AddAssetPathCell(string searchTag, string path)
{
var cell = new AssetPathCell(searchTag, path);
Cells.Add(cell);
}
public void AddStringValueCell(string searchTag, string value)
{
var cell = new StringValueCell(searchTag, value);
Cells.Add(cell);
}
public void AddLongValueCell(string searchTag, long value)
{
var cell = new IntegerValueCell(searchTag, value);
Cells.Add(cell);
}
public void AddDoubleValueCell(string searchTag, double value)
{
var cell = new SingleValueCell(searchTag, value);
Cells.Add(cell);
}
public void AddBoolValueCell(string searchTag, bool value)
{
var cell = new BooleanValueCell(searchTag, value);
Cells.Add(cell);
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b93bf9f1eb7024c44a1c84b299dc9622
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
#if UNITY_2019_4_OR_NEWER
namespace YooAsset.Editor
{
public interface ITableCell
{
/// <summary>
/// 单元格数值
/// </summary>
object CellValue { set; get; }
/// <summary>
/// 获取界面显示对象
/// </summary>
object GetDisplayObject();
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dcfa4e21884025c4b91db2efd543e139
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
#if UNITY_2019_4_OR_NEWER
using System;
using System.Collections.Generic;
namespace YooAsset.Editor
{
public interface ITableData
{
/// <summary>
/// 是否可见
/// </summary>
bool Visible { set; get; }
/// <summary>
/// 单元格集合
/// </summary>
IList<ITableCell> Cells { set; get; }
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 26779163e1da1ca46948287a4cc10023
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 211ee819fabdd6d4399b79e6fcf45e5c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,229 @@
#if UNITY_2019_4_OR_NEWER
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
namespace YooAsset.Editor
{
public static class DefaultSearchSystem
{
/// <summary>
/// 解析搜索命令
/// </summary>
public static List<ISearchCommand> ParseCommand(string commandContent)
{
if (string.IsNullOrEmpty(commandContent))
return new List<ISearchCommand>();
List<ISearchCommand> results = new List<ISearchCommand>(10);
string[] commands = Regex.Split(commandContent, @"\s+");
foreach (var command in commands)
{
if (command.Contains("!="))
{
var splits = command.Split(new string[] { "!=" }, StringSplitOptions.None);
if (CheckSplitsValid(command, splits) == false)
continue;
var cmd = new SearchCompare();
cmd.HeaderTitle = splits[0];
cmd.CompareValue = splits[1];
cmd.CompareOperator = "!=";
results.Add(cmd);
}
else if (command.Contains(">="))
{
var splits = command.Split(new string[] { ">=" }, StringSplitOptions.None);
if (CheckSplitsValid(command, splits) == false)
continue;
var cmd = new SearchCompare();
cmd.HeaderTitle = splits[0];
cmd.CompareValue = splits[1];
cmd.CompareOperator = ">=";
results.Add(cmd);
}
else if (command.Contains("<="))
{
var splits = command.Split(new string[] { "<=" }, StringSplitOptions.None);
if (CheckSplitsValid(command, splits) == false)
continue;
var cmd = new SearchCompare();
cmd.HeaderTitle = splits[0];
cmd.CompareValue = splits[1];
cmd.CompareOperator = "<=";
results.Add(cmd);
}
else if (command.Contains(">"))
{
var splits = command.Split('>');
if (CheckSplitsValid(command, splits) == false)
continue;
var cmd = new SearchCompare();
cmd.HeaderTitle = splits[0];
cmd.CompareValue = splits[1];
cmd.CompareOperator = ">";
results.Add(cmd);
}
else if (command.Contains("<"))
{
var splits = command.Split('<');
if (CheckSplitsValid(command, splits) == false)
continue;
var cmd = new SearchCompare();
cmd.HeaderTitle = splits[0];
cmd.CompareValue = splits[1];
cmd.CompareOperator = "<";
results.Add(cmd);
}
else if (command.Contains("="))
{
var splits = command.Split('=');
if (CheckSplitsValid(command, splits) == false)
continue;
var cmd = new SearchCompare();
cmd.HeaderTitle = splits[0];
cmd.CompareValue = splits[1];
cmd.CompareOperator = "=";
results.Add(cmd);
}
else if (command.Contains(":"))
{
var splits = command.Split(':');
if (CheckSplitsValid(command, splits) == false)
continue;
var cmd = new SearchKeyword();
cmd.SearchTag = splits[0];
cmd.Keyword = splits[1];
results.Add(cmd);
}
else
{
var cmd = new SearchKeyword();
cmd.SearchTag = string.Empty;
cmd.Keyword = command;
results.Add(cmd);
}
}
return results;
}
private static bool CheckSplitsValid(string command, string[] splits)
{
if (splits.Length != 2)
{
Debug.LogWarning($"Invalid search command : {command}");
return false;
}
if (string.IsNullOrEmpty(splits[0]))
return false;
if (string.IsNullOrEmpty(splits[1]))
return false;
return true;
}
/// <summary>
/// 搜索匹配
/// </summary>
public static void Search(List<ITableData> sourceDatas, string command)
{
var searchCmds = ParseCommand(command);
foreach (var tableData in sourceDatas)
{
tableData.Visible = Search(tableData, searchCmds);
}
}
private static bool Search(ITableData tableData, List<ISearchCommand> commands)
{
if (commands.Count == 0)
return true;
// 先匹配字符串
var searchKeywordCmds = commands.Where(cmd => cmd is SearchKeyword).ToList();
if (SearchKeyword(tableData, searchKeywordCmds) == false)
return false;
// 后匹配数值
var searchCompareCmds = commands.Where(cmd => cmd is SearchCompare).ToList();
if (SearchCompare(tableData, searchCompareCmds) == false)
return false;
return true;
}
private static bool SearchKeyword(ITableData tableData, List<ISearchCommand> commands)
{
if (commands.Count == 0)
return true;
// 匹配规则:任意字符串列匹配成果
foreach (var tableCell in tableData.Cells)
{
foreach (var cmd in commands)
{
var searchKeywordCmd = cmd as SearchKeyword;
if (tableCell is StringValueCell stringValueCell)
{
if (string.IsNullOrEmpty(searchKeywordCmd.SearchTag) == false)
{
if (searchKeywordCmd.SearchTag == stringValueCell.SearchTag)
{
if (searchKeywordCmd.CompareTo(stringValueCell.StringValue))
return true;
}
}
else
{
if (searchKeywordCmd.CompareTo(stringValueCell.StringValue))
return true;
}
}
}
}
// 匹配失败
return false;
}
private static bool SearchCompare(ITableData tableData, List<ISearchCommand> commands)
{
if (commands.Count == 0)
return true;
// 匹配规则:任意指定数值列匹配成果
foreach (var tableCell in tableData.Cells)
{
foreach (var cmd in commands)
{
var searchCompareCmd = cmd as SearchCompare;
if (tableCell is IntegerValueCell integerValueCell)
{
if (searchCompareCmd.HeaderTitle == integerValueCell.SearchTag)
{
if (searchCompareCmd.CompareTo(integerValueCell.IntegerValue))
return true;
}
}
else if (tableCell is SingleValueCell singleValueCell)
{
if (searchCompareCmd.HeaderTitle == singleValueCell.SearchTag)
{
if (searchCompareCmd.CompareTo(singleValueCell.SingleValue))
return true;
}
}
}
}
// 匹配失败
return false;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3de1e93052c2c86408ba77125da962cd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
#if UNITY_2019_4_OR_NEWER
namespace YooAsset.Editor
{
public interface ISearchCommand
{
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4228528e243224045863d8ae14a16194
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,133 @@
#if UNITY_2019_4_OR_NEWER
using System;
using UnityEditor;
using UnityEngine;
namespace YooAsset.Editor
{
/// <summary>
/// 指定标题的比较搜索
/// </summary>
public class SearchCompare : ISearchCommand
{
public string HeaderTitle;
public string CompareValue;
public string CompareOperator;
/// <summary>
/// 转换的整形数值
/// </summary>
private bool _isConvertedIntegerNum = false;
private long _convertedIntegerNumber = 0;
public long IntegerNumberValue
{
get
{
if (_isConvertedIntegerNum == false)
{
_isConvertedIntegerNum = true;
_convertedIntegerNumber = long.Parse(CompareValue.ToString());
}
return _convertedIntegerNumber;
}
}
/// <summary>
/// 转换的浮点数值
/// </summary>
private bool _isConvertedSingleNum = false;
private double _convertedSingleNumber = 0;
public double SingleNumberValue
{
get
{
if (_isConvertedSingleNum == false)
{
_isConvertedSingleNum = true;
_convertedSingleNumber = double.Parse(CompareValue.ToString());
}
return _convertedSingleNumber;
}
}
public bool CompareTo(long value)
{
if (CompareOperator == ">")
{
if (value > SingleNumberValue)
return true;
}
else if (CompareOperator == ">=")
{
if (value >= SingleNumberValue)
return true;
}
else if (CompareOperator == "<")
{
if (value < SingleNumberValue)
return true;
}
else if (CompareOperator == "<=")
{
if (value <= SingleNumberValue)
return true;
}
else if (CompareOperator == "=")
{
if (value == SingleNumberValue)
return true;
}
else if (CompareOperator == "!=")
{
if (value != SingleNumberValue)
return true;
}
else
{
Debug.LogWarning($"Can not support operator : {CompareOperator}");
}
return false;
}
public bool CompareTo(double value)
{
if (CompareOperator == ">")
{
if (value > IntegerNumberValue)
return true;
}
else if (CompareOperator == ">=")
{
if (value >= IntegerNumberValue)
return true;
}
else if (CompareOperator == "<")
{
if (value < IntegerNumberValue)
return true;
}
else if (CompareOperator == "<=")
{
if (value <= IntegerNumberValue)
return true;
}
else if (CompareOperator == "=")
{
if (value == IntegerNumberValue)
return true;
}
else if (CompareOperator == "!=")
{
if (value != IntegerNumberValue)
return true;
}
else
{
Debug.LogWarning($"Can not support operator : {CompareOperator}");
}
return false;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 345cafa16ea7ccc488c96af1a4ca33c2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
#if UNITY_2019_4_OR_NEWER
namespace YooAsset.Editor
{
/// <summary>
/// 搜索关键字
/// </summary>
public class SearchKeyword : ISearchCommand
{
public string SearchTag;
public string Keyword;
public bool CompareTo(string value)
{
return value.Contains(Keyword);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c34fa79e35e66247b64f18eafa1d230
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
#if UNITY_2019_4_OR_NEWER
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace YooAsset.Editor
{
public class TableColumn
{
/// <summary>
/// 单元列索引值
/// </summary>
internal int ColumnIndex;
/// <summary>
/// 单元元素集合
/// </summary>
internal List<VisualElement> CellElements = new List<VisualElement>(1000);
/// <summary>
/// UI元素名称
/// </summary>
public string ElementName { private set; get; }
/// <summary>
/// 标题名称
/// </summary>
public string HeaderTitle { private set; get; }
/// <summary>
/// 单元列样式
/// </summary>
public ColumnStyle ColumnStyle { private set; get; }
/// <summary>
/// 制作单元格元素
/// </summary>
public Func<VisualElement> MakeCell;
/// <summary>
/// 绑定数据到单元格
/// </summary>
public Action<VisualElement, ITableData, ITableCell> BindCell;
public TableColumn(string elementName, string headerTitle, ColumnStyle columnStyle)
{
this.ElementName = elementName;
this.HeaderTitle = headerTitle;
this.ColumnStyle = columnStyle;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 034395dad363e3a4da96881de60918a3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,386 @@
#if UNITY_2019_4_OR_NEWER
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace YooAsset.Editor
{
/// <summary>
/// Unity2022版本以上推荐官方类MultiColumnListView组件
/// </summary>
public class TableViewer : VisualElement
{
public new class UxmlFactory : UxmlFactory<TableViewer, UxmlTraits>
{
}
private readonly Toolbar _toolbar;
private readonly ListView _listView;
private readonly List<TableColumn> _columns = new List<TableColumn>(10);
private List<ITableData> _itemsSource;
private List<ITableData> _sortingDatas;
// 排序相关
private string _sortingHeader;
private bool _descendingSort = true;
/// <summary>
/// 数据源
/// </summary>
public List<ITableData> itemsSource
{
get
{
return _itemsSource;
}
set
{
if (CheckItemsSource(value))
{
_itemsSource = value;
_sortingDatas = value;
}
}
}
/// <summary>
/// 选中的数据列表
/// </summary>
public List<ITableData> selectedItems
{
get
{
#if UNITY_2020_3_OR_NEWER
return _listView.selectedItems.Cast<ITableData>().ToList();
#else
List<ITableData> result = new List<ITableData>();
result.Add(_listView.selectedItem as ITableData);
return result;
#endif
}
}
/// <summary>
/// 单元视图交互事件
/// </summary>
public Action<PointerDownEvent, ITableData> ClickTableDataEvent;
/// <summary>
/// 单元视图交互事件
/// </summary>
public Action<TableColumn> ClickTableHeadEvent;
/// <summary>
/// 单元视图变化事件
/// </summary>
public Action<ITableData> SelectionChangedEvent;
public TableViewer()
{
this.style.flexShrink = 1f;
this.style.flexGrow = 1f;
// 定义标题栏
_toolbar = new Toolbar();
// 定义列表视图
_listView = new ListView();
_listView.style.flexShrink = 1f;
_listView.style.flexGrow = 1f;
_listView.makeItem = MakeListViewElement;
_listView.bindItem = BindListViewElement;
_listView.selectionType = SelectionType.Multiple;
_listView.RegisterCallback<PointerDownEvent>(OnClickListItem);
#if UNITY_2022_3_OR_NEWER
_listView.selectionChanged += OnSelectionChanged;
#elif UNITY_2020_1_OR_NEWER
_listView.onSelectionChange += OnSelectionChanged;
#else
_listView.onSelectionChanged += OnSelectionChanged;
#endif
this.Add(_toolbar);
this.Add(_listView);
}
/// <summary>
/// 获取标题UI元素
/// </summary>
public VisualElement GetHeaderElement(string elementName)
{
return _toolbar.Q<ToolbarButton>(elementName);
}
/// <summary>
/// 添加单元列
/// </summary>
public void AddColumn(TableColumn column)
{
var toolbarBtn = new ToolbarButton();
toolbarBtn.userData = column;
toolbarBtn.name = column.ElementName;
toolbarBtn.text = column.HeaderTitle;
toolbarBtn.style.flexGrow = 0;
toolbarBtn.style.width = column.ColumnStyle.Width;
toolbarBtn.style.minWidth = column.ColumnStyle.Width;
toolbarBtn.style.maxWidth = column.ColumnStyle.Width;
toolbarBtn.clickable.clickedWithEventInfo += OnClickTableHead;
SetCellElementStyle(toolbarBtn);
_toolbar.Add(toolbarBtn);
_columns.Add(column);
// 可伸缩控制柄
if (column.ColumnStyle.Stretchable)
{
int handleWidth = 3;
int minWidth = (int)column.ColumnStyle.MinWidth.value;
int maxWidth = (int)column.ColumnStyle.MaxWidth.value;
var resizeHandle = new ResizeHandle(handleWidth, toolbarBtn, minWidth, maxWidth);
resizeHandle.ResizeChanged += (float value) =>
{
float width = Mathf.Clamp(value, column.ColumnStyle.MinWidth.value, column.ColumnStyle.MaxWidth.value);
column.ColumnStyle.Width = width;
foreach (var element in column.CellElements)
{
element.style.width = width;
element.style.minWidth = width;
element.style.maxWidth = width;
}
};
_toolbar.Add(resizeHandle);
}
// 计算索引值
column.ColumnIndex = _columns.Count - 1;
if (column.ColumnStyle.Sortable == false)
toolbarBtn.SetEnabled(false);
}
/// <summary>
/// 添加单元列集合
/// </summary>
public void AddColumns(IList<TableColumn> columns)
{
foreach (var column in columns)
{
AddColumn(column);
}
}
/// <summary>
/// 重建表格视图
/// </summary>
public void RebuildView()
{
if (_itemsSource == null)
return;
var itemsSource = _sortingDatas.Where(row => row.Visible);
_listView.Clear();
_listView.ClearSelection();
_listView.itemsSource = itemsSource.ToList();
_listView.Rebuild();
// 刷新标题栏
RefreshToobar();
}
private void RefreshToobar()
{
// 设置为原始标题
foreach (var column in _columns)
{
var toobarButton = _toolbar.Q<ToolbarButton>(column.ElementName);
toobarButton.text = column.HeaderTitle;
}
// 设置元素数量
foreach (var column in _columns)
{
if (column.ColumnStyle.Counter)
{
var toobarButton = GetHeaderElement(column.ElementName) as ToolbarButton;
toobarButton.text = $"{toobarButton.text} ({itemsSource.Count()})";
}
}
// 设置展示单位
foreach (var column in _columns)
{
if (string.IsNullOrEmpty(column.ColumnStyle.Units) == false)
{
var toobarButton = GetHeaderElement(column.ElementName) as ToolbarButton;
toobarButton.text = $"{toobarButton.text} ({column.ColumnStyle.Units})";
}
}
// 设置升降符号
if (string.IsNullOrEmpty(_sortingHeader) == false)
{
var _toobarButton = _toolbar.Q<ToolbarButton>(_sortingHeader);
if (_descendingSort)
_toobarButton.text = $"{_toobarButton.text} ↓";
else
_toobarButton.text = $"{_toobarButton.text} ↑";
}
}
/// <summary>
/// 清空所有数据
/// </summary>
public void ClearAll(bool clearColumns, bool clearSource)
{
if (clearColumns)
{
_columns.Clear();
_toolbar.Clear();
}
if (clearSource)
{
if (_itemsSource != null)
_itemsSource.Clear();
if (_sortingDatas != null)
_sortingDatas.Clear();
_listView.Clear();
_listView.ClearSelection();
}
}
private void OnClickListItem(PointerDownEvent evt)
{
var selectData = _listView.selectedItem as ITableData;
if (selectData == null)
return;
ClickTableDataEvent?.Invoke(evt, selectData);
}
private void OnClickTableHead(EventBase eventBase)
{
if (_itemsSource == null)
return;
ToolbarButton toolbarBtn = eventBase.target as ToolbarButton;
var clickedColumn = toolbarBtn.userData as TableColumn;
if (clickedColumn == null)
return;
ClickTableHeadEvent?.Invoke(clickedColumn);
if (clickedColumn.ColumnStyle.Sortable == false)
return;
if (_sortingHeader != clickedColumn.ElementName)
{
_sortingHeader = clickedColumn.ElementName;
_descendingSort = false;
}
else
{
_descendingSort = !_descendingSort;
}
// 升降排序
if (_descendingSort)
_sortingDatas = _itemsSource.OrderByDescending(tableData => tableData.Cells[clickedColumn.ColumnIndex]).ToList();
else
_sortingDatas = _itemsSource.OrderBy(tableData => tableData.Cells[clickedColumn.ColumnIndex]).ToList();
// 刷新数据表
RebuildView();
}
private void OnSelectionChanged(IEnumerable<object> items)
{
foreach (var item in items)
{
var tableData = item as ITableData;
SelectionChangedEvent?.Invoke(tableData);
break;
}
}
private bool CheckItemsSource(List<ITableData> itemsSource)
{
if (itemsSource == null)
return false;
if (itemsSource.Count > 0)
{
int cellCount = itemsSource[0].Cells.Count;
for (int i = 0; i < itemsSource.Count; i++)
{
var tableData = itemsSource[i];
if (tableData == null)
{
Debug.LogWarning($"Items source has null instance !");
return false;
}
if (tableData.Cells == null || tableData.Cells.Count == 0)
{
Debug.LogWarning($"Items source data has empty cells !");
return false;
}
if (tableData.Cells.Count != cellCount)
{
Debug.LogWarning($"Items source data has inconsisten cells count ! Item index {i}");
return false;
}
}
}
return true;
}
private VisualElement MakeListViewElement()
{
VisualElement listViewElement = new VisualElement();
listViewElement.style.flexDirection = FlexDirection.Row;
foreach (var column in _columns)
{
var cellElement = column.MakeCell.Invoke();
cellElement.name = column.ElementName;
cellElement.style.flexGrow = 0f;
cellElement.style.width = column.ColumnStyle.Width;
cellElement.style.minWidth = column.ColumnStyle.Width;
cellElement.style.maxWidth = column.ColumnStyle.Width;
SetCellElementStyle(cellElement);
listViewElement.Add(cellElement);
column.CellElements.Add(cellElement);
}
return listViewElement;
}
private void BindListViewElement(VisualElement listViewElement, int index)
{
var sourceDatas = _listView.itemsSource as List<ITableData>;
var tableData = sourceDatas[index];
foreach (var colum in _columns)
{
var cellElement = listViewElement.Q(colum.ElementName);
var tableCell = tableData.Cells[colum.ColumnIndex];
colum.BindCell.Invoke(cellElement, tableData, tableCell);
}
}
private void SetCellElementStyle(VisualElement element)
{
StyleLength defaultStyle = new StyleLength(1f);
element.style.paddingTop = defaultStyle;
element.style.paddingBottom = defaultStyle;
element.style.marginTop = defaultStyle;
element.style.marginBottom = defaultStyle;
element.style.paddingLeft = 1;
element.style.paddingRight = 1;
element.style.marginLeft = 0;
element.style.marginRight = 0;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c12f27850c4d104db1dc85cf532e586
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1797f960cdbd5aa41a96bb02d16c4998
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,77 @@
#if UNITY_2019_4_OR_NEWER
using System.IO;
using System.Linq;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace YooAsset.Editor
{
public class TreeNode
{
/// <summary>
/// 子节点集合
/// </summary>
public List<TreeNode> Children = new List<TreeNode>(10);
/// <summary>
/// 父节点
/// </summary>
public TreeNode Parent { get; set; }
/// <summary>
/// 用户数据
/// </summary>
public object UserData { get; set; }
/// <summary>
/// 是否展开
/// </summary>
public bool IsExpanded { get; set; } = false;
public TreeNode(object userData)
{
UserData = userData;
}
/// <summary>
/// 添加子节点
/// </summary>
public void AddChild(TreeNode child)
{
child.Parent = this;
Children.Add(child);
}
/// <summary>
/// 清理所有子节点
/// </summary>
public void ClearChildren()
{
foreach(var child in Children)
{
child.Parent = null;
}
Children.Clear();
}
/// <summary>
/// 计算节点的深度
/// </summary>
public int GetDepth()
{
int depth = 0;
TreeNode current = this;
while (current.Parent != null)
{
depth++;
current = current.Parent;
}
return depth;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 473cdc8e1dd7b0f43938ddb99287a2a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,152 @@
#if UNITY_2019_4_OR_NEWER
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace YooAsset.Editor
{
public class TreeViewer : VisualElement
{
public new class UxmlFactory : UxmlFactory<TreeViewer, UxmlTraits>
{
}
private readonly ListView _listView;
private readonly List<TreeNode> _flattenList = new List<TreeNode>(1000);
private readonly List<TreeNode> _rootList = new List<TreeNode>(100);
/// <summary>
/// 制作列表元素
/// </summary>
public Action<VisualElement> makeItem { get; set; }
/// <summary>
/// 绑定列表数据
/// </summary>
public Action<VisualElement, object> bindItem { get; set; }
public TreeViewer()
{
this.style.flexShrink = 1f;
this.style.flexGrow = 1f;
// 创建ListView
_listView = new ListView();
_listView.style.flexShrink = 1f;
_listView.style.flexGrow = 1f;
_listView.itemsSource = _flattenList;
_listView.makeItem = MakeItemInternal;
_listView.bindItem = BindItemInternal;
this.Add(_listView);
}
/// <summary>
/// 设置根节点数据
/// </summary>
public void SetRootItem(TreeNode rootNode)
{
_rootList.Add(rootNode);
}
/// <summary>
/// 设置根节点数据
/// </summary>
public void SetRootItems(List<TreeNode> rootNodes)
{
_rootList.AddRange(rootNodes);
}
/// <summary>
/// 清理数据
/// </summary>
public void ClearAll()
{
_rootList.Clear();
}
/// <summary>
/// 重新绘制视图
/// </summary>
public void RebuildView()
{
_flattenList.Clear();
foreach (var treeRoot in _rootList)
{
FlattenTree(treeRoot, 0);
}
_listView.Rebuild();
}
/// <summary>
/// 将树形结构扁平化为列表
/// </summary>
private void FlattenTree(TreeNode node, int depth)
{
_flattenList.Add(node);
if (node.IsExpanded)
{
foreach (var child in node.Children)
{
FlattenTree(child, depth + 1);
}
}
}
private VisualElement MakeItemInternal()
{
var container = new VisualElement();
container.style.flexDirection = FlexDirection.Row;
// 折叠按钮
var toggle = new ToggleFoldout();
toggle.text = string.Empty;
toggle.name = "foldout";
toggle.style.alignSelf = Align.Center;
toggle.style.width = 15;
toggle.style.height = 15;
toggle.RegisterValueChangedCallback((ChangeEvent<bool> callback) =>
{
var treeNode = toggle.userData as TreeNode;
treeNode.IsExpanded = toggle.value;
RebuildView();
});
container.Add(toggle);
// 用户自定义元素
if (makeItem != null)
{
makeItem.Invoke(container);
}
return container;
}
private void BindItemInternal(VisualElement item, int index)
{
var treeNode = _flattenList[index];
// 设置折叠状态
var toggle = item.Q<ToggleFoldout>("foldout");
toggle.SetValueWithoutNotify(treeNode.IsExpanded);
toggle.userData = treeNode;
toggle.style.marginLeft = treeNode.GetDepth() * 15;
// 隐藏折叠按钮
if (treeNode.Children.Count == 0)
{
toggle.style.visibility = Visibility.Hidden;
}
// 用户自定义元素
if (bindItem != null)
{
bindItem.Invoke(item, treeNode.UserData);
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db2fb30e2d4512149b615fe6b2562ecd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
#if UNITY_2019_4_OR_NEWER
using System.Reflection;
using UnityEditor;
namespace YooAsset.Editor
{
public static class UIElementsCursor
{
private static PropertyInfo _defaultCursorId;
private static PropertyInfo DefaultCursorId
{
get
{
if (_defaultCursorId != null)
return _defaultCursorId;
_defaultCursorId = typeof(UnityEngine.UIElements.Cursor).GetProperty("defaultCursorId", BindingFlags.NonPublic | BindingFlags.Instance);
return _defaultCursorId;
}
}
public static UnityEngine.UIElements.Cursor CreateCursor(MouseCursor cursorType)
{
var ret = (object)new UnityEngine.UIElements.Cursor();
DefaultCursorId.SetValue(ret, (int)cursorType);
return (UnityEngine.UIElements.Cursor)ret;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 35d99a1727f773b46bc6a7bb1a2be970
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,39 +0,0 @@
#if UNITY_2020_3_OR_NEWER
using System;
using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace YooAsset.Editor
{
/// <summary>
/// 分屏控件
/// </summary>
public class SplitView : TwoPaneSplitView
{
public new class UxmlFactory : UxmlFactory<SplitView, TwoPaneSplitView.UxmlTraits>
{
}
/// <summary>
/// 窗口分屏适配
/// </summary>
public static void Adjuster(VisualElement root)
{
var topGroup = root.Q<VisualElement>("TopGroup");
var bottomGroup = root.Q<VisualElement>("BottomGroup");
topGroup.style.minHeight = 100f;
bottomGroup.style.minHeight = 100f;
root.Remove(topGroup);
root.Remove(bottomGroup);
var spliteView = new SplitView();
spliteView.fixedPaneInitialDimension = 300;
spliteView.orientation = TwoPaneSplitViewOrientation.Vertical;
spliteView.contentContainer.Add(topGroup);
spliteView.contentContainer.Add(bottomGroup);
root.Add(spliteView);
}
}
}
#endif

View File

@@ -1,7 +1,6 @@

namespace YooAsset.Editor
{
#if UNITY_2019
public static partial class UnityEngine_UIElements_ListView_Extension
{
@@ -21,5 +20,4 @@ namespace YooAsset.Editor
}
}
#endif
}

View File

@@ -0,0 +1,25 @@
#if UNITY_2019_4_OR_NEWER
namespace YooAsset.Editor
{
/// <summary>
/// 引擎图标名称
/// </summary>
public class UIElementsIcon
{
public const string RecordOn = "d_Record On@2x";
public const string RecordOff = "d_Record Off@2x";
#if UNITY_2019
public const string FoldoutOn = "IN foldout on";
public const string FoldoutOff = "IN foldout";
#else
public const string FoldoutOn = "d_IN_foldout_on@2x";
public const string FoldoutOff = "d_IN_foldout@2x";
#endif
public const string VisibilityToggleOff = "animationvisibilitytoggleoff@2x";
public const string VisibilityToggleOn = "animationvisibilitytoggleon@2x";
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 41af337b41140c842b4b9f34a34f5cc6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -8,6 +8,9 @@ namespace YooAsset.Editor
{
public static class UIElementsTools
{
/// <summary>
/// 设置元素显隐
/// </summary>
public static void SetElementVisible(VisualElement element, bool visible)
{
if (element == null)
@@ -16,6 +19,47 @@ namespace YooAsset.Editor
element.style.display = visible ? DisplayStyle.Flex : DisplayStyle.None;
element.style.visibility = visible ? Visibility.Visible : Visibility.Hidden;
}
/// <summary>
/// 设置元素的文本最小宽度
/// </summary>
public static void SetElementLabelMinWidth(VisualElement element, int minWidth)
{
var label = element.Q<Label>();
if (label != null)
{
// 设置最小宽度
label.style.minWidth = minWidth;
}
}
/// <summary>
/// 设置按钮图标
/// </summary>
public static void SetToolbarButtonIcon(ToolbarButton element, string iconName)
{
var image = EditorGUIUtility.IconContent(iconName).image as Texture2D;
element.style.backgroundImage = image;
element.text = string.Empty;
}
/// <summary>
/// 竖版分屏
/// </summary>
public static void SplitVerticalPanel(VisualElement root, VisualElement panelA, VisualElement panelB)
{
#if UNITY_2020_3_OR_NEWER
root.Remove(panelA);
root.Remove(panelB);
var spliteView = new TwoPaneSplitView();
spliteView.fixedPaneInitialDimension = 300;
spliteView.orientation = TwoPaneSplitViewOrientation.Vertical;
spliteView.contentContainer.Add(panelA);
spliteView.contentContainer.Add(panelB);
root.Add(spliteView);
#endif
}
}
}
#endif