diff --git a/SpineViewer/Controls/SkelFileListBox.cs b/SpineViewer/Controls/SkelFileListBox.cs
index 512f8f3..ed41523 100644
--- a/SpineViewer/Controls/SkelFileListBox.cs
+++ b/SpineViewer/Controls/SkelFileListBox.cs
@@ -34,14 +34,14 @@ namespace SpineViewer.Controls
{
if (File.Exists(path))
{
- if (SpineHelper.CommonSkelSuffix.Contains(Path.GetExtension(path).ToLower()))
+ if (SpineUtils.CommonSkelSuffix.Contains(Path.GetExtension(path).ToLower()))
listBox.Items.Add(Path.GetFullPath(path));
}
else if (Directory.Exists(path))
{
foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
- if (SpineHelper.CommonSkelSuffix.Contains(Path.GetExtension(file).ToLower()))
+ if (SpineUtils.CommonSkelSuffix.Contains(Path.GetExtension(file).ToLower()))
listBox.Items.Add(file);
}
}
@@ -58,7 +58,7 @@ namespace SpineViewer.Controls
{
foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
- if (SpineHelper.CommonSkelSuffix.Contains(Path.GetExtension(file).ToLower()))
+ if (SpineUtils.CommonSkelSuffix.Contains(Path.GetExtension(file).ToLower()))
listBox.Items.Add(file);
}
}
diff --git a/SpineViewer/Controls/SpineListView.cs b/SpineViewer/Controls/SpineListView.cs
index 430e875..98f500a 100644
--- a/SpineViewer/Controls/SpineListView.cs
+++ b/SpineViewer/Controls/SpineListView.cs
@@ -39,7 +39,7 @@ namespace SpineViewer.Controls
///
/// 用于属性页显示模型参数的包装类
///
- private readonly Dictionary spinePropertyWrappers = [];
+ private readonly Dictionary spinePropertyWrappers = [];
public SpineListView()
{
@@ -205,14 +205,14 @@ namespace SpineViewer.Controls
{
if (File.Exists(path))
{
- if (SpineHelper.CommonSkelSuffix.Contains(Path.GetExtension(path).ToLower()))
+ if (SpineUtils.CommonSkelSuffix.Contains(Path.GetExtension(path).ToLower()))
validPaths.Add(path);
}
else if (Directory.Exists(path))
{
foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
- if (SpineHelper.CommonSkelSuffix.Contains(Path.GetExtension(file).ToLower()))
+ if (SpineUtils.CommonSkelSuffix.Contains(Path.GetExtension(file).ToLower()))
validPaths.Add(file);
}
}
diff --git a/SpineViewer/Controls/SpinePropertyGrid.cs b/SpineViewer/Controls/SpinePropertyGrid.cs
index 1302635..ff8f78d 100644
--- a/SpineViewer/Controls/SpinePropertyGrid.cs
+++ b/SpineViewer/Controls/SpinePropertyGrid.cs
@@ -22,7 +22,7 @@ namespace SpineViewer.Controls
///
/// 设置选中的对象列表, 可以赋值 null 来清空选中, 行为与 PropertyGrid.SelectedObjects 类似
///
- public SpineWrapper[] SelectedSpines
+ public SpineObjectProperty[] SelectedSpines
{
get => selectedSpines ?? [];
set
@@ -49,14 +49,14 @@ namespace SpineViewer.Controls
}
}
}
- private SpineWrapper[]? selectedSpines = null;
+ private SpineObjectProperty[]? selectedSpines = null;
private void contextMenuStrip_Skin_Opening(object sender, CancelEventArgs e)
{
if (selectedSpines?.Length == 1)
{
toolStripMenuItem_AddSkin.Enabled = true;
- toolStripMenuItem_RemoveSkin.Enabled = propertyGrid_Skin.SelectedGridItem.Value is SkinWrapper;
+ toolStripMenuItem_RemoveSkin.Enabled = propertyGrid_Skin.SelectedGridItem.Value is SkinNameProperty;
}
else
{
@@ -70,7 +70,7 @@ namespace SpineViewer.Controls
if (selectedSpines?.Length == 1)
{
toolStripMenuItem_AddAnimation.Enabled = true;
- toolStripMenuItem_RemoveAnimation.Enabled = propertyGrid_Animation.SelectedGridItem.Value is TrackWrapper;
+ toolStripMenuItem_RemoveAnimation.Enabled = propertyGrid_Animation.SelectedGridItem.Value is TrackAnimationProperty;
}
else
{
@@ -99,7 +99,7 @@ namespace SpineViewer.Controls
{
if (selectedSpines?.Length != 1) return;
- if (propertyGrid_Skin.SelectedGridItem.Value is SkinWrapper wrapper)
+ if (propertyGrid_Skin.SelectedGridItem.Value is SkinNameProperty wrapper)
{
selectedSpines[0].Skin.Spine.UnloadSkin(wrapper.Index);
propertyGrid_Skin.Refresh();
@@ -119,7 +119,7 @@ namespace SpineViewer.Controls
{
if (selectedSpines?.Length != 1) return;
- if (propertyGrid_Animation.SelectedGridItem.Value is TrackWrapper wrapper)
+ if (propertyGrid_Animation.SelectedGridItem.Value is TrackAnimationProperty wrapper)
{
selectedSpines[0].Animation.Spine.ClearTrack(wrapper.Index);
propertyGrid_Animation.Refresh();
diff --git a/SpineViewer/Dialogs/BatchOpenSpineDialog.cs b/SpineViewer/Dialogs/BatchOpenSpineDialog.cs
index 0e7ee71..7b2ba0a 100644
--- a/SpineViewer/Dialogs/BatchOpenSpineDialog.cs
+++ b/SpineViewer/Dialogs/BatchOpenSpineDialog.cs
@@ -22,7 +22,7 @@ namespace SpineViewer.Dialogs
public BatchOpenSpineDialog()
{
InitializeComponent();
- comboBox_Version.DataSource = SpineHelper.Names.ToList();
+ comboBox_Version.DataSource = SpineUtils.Names.ToList();
comboBox_Version.DisplayMember = "Value";
comboBox_Version.ValueMember = "Key";
comboBox_Version.SelectedValue = SpineVersion.Auto;
diff --git a/SpineViewer/Dialogs/ConvertFileFormatDialog.cs b/SpineViewer/Dialogs/ConvertFileFormatDialog.cs
index 5124015..969c226 100644
--- a/SpineViewer/Dialogs/ConvertFileFormatDialog.cs
+++ b/SpineViewer/Dialogs/ConvertFileFormatDialog.cs
@@ -23,13 +23,13 @@ namespace SpineViewer.Dialogs
{
InitializeComponent();
- comboBox_SourceVersion.DataSource = SpineHelper.Names.ToList();
+ comboBox_SourceVersion.DataSource = SpineUtils.Names.ToList();
comboBox_SourceVersion.DisplayMember = "Value";
comboBox_SourceVersion.ValueMember = "Key";
comboBox_SourceVersion.SelectedValue = SpineVersion.Auto;
// 目标版本不包含自动
- var versionsWithoutAuto = SpineHelper.Names.ToDictionary();
+ var versionsWithoutAuto = SpineUtils.Names.ToDictionary();
versionsWithoutAuto.Remove(SpineVersion.Auto);
comboBox_TargetVersion.DataSource = versionsWithoutAuto.ToList();
comboBox_TargetVersion.DisplayMember = "Value";
diff --git a/SpineViewer/Dialogs/OpenSpineDialog.cs b/SpineViewer/Dialogs/OpenSpineDialog.cs
index 2525818..b69bae1 100644
--- a/SpineViewer/Dialogs/OpenSpineDialog.cs
+++ b/SpineViewer/Dialogs/OpenSpineDialog.cs
@@ -21,7 +21,7 @@ namespace SpineViewer.Dialogs
public OpenSpineDialog()
{
InitializeComponent();
- comboBox_Version.DataSource = SpineHelper.Names.ToList();
+ comboBox_Version.DataSource = SpineUtils.Names.ToList();
comboBox_Version.DisplayMember = "Value";
comboBox_Version.ValueMember = "Key";
comboBox_Version.SelectedValue = SpineVersion.Auto;
diff --git a/SpineViewer/Forms/SpineViewerForm.cs b/SpineViewer/Forms/SpineViewerForm.cs
index 9cec6e7..c7b5a09 100644
--- a/SpineViewer/Forms/SpineViewerForm.cs
+++ b/SpineViewer/Forms/SpineViewerForm.cs
@@ -379,7 +379,7 @@ namespace SpineViewer
{
try
{
- srcCvter = SkeletonConverter.New(SpineHelper.GetVersion(skelPath));
+ srcCvter = SkeletonConverter.New(SpineUtils.GetVersion(skelPath));
}
catch (Exception ex)
{
diff --git a/SpineViewer/Spine/SpineObject.cs b/SpineViewer/Spine/SpineObject.cs
index 59e11a6..5a033dd 100644
--- a/SpineViewer/Spine/SpineObject.cs
+++ b/SpineViewer/Spine/SpineObject.cs
@@ -38,7 +38,7 @@ namespace SpineViewer.Spine
skelPath = Path.GetFullPath(skelPath);
atlasPath = Path.GetFullPath(atlasPath);
- if (version == SpineVersion.Auto) version = SpineHelper.GetVersion(skelPath);
+ if (version == SpineVersion.Auto) version = SpineUtils.GetVersion(skelPath);
if (!File.Exists(atlasPath)) throw new FileNotFoundException($"atlas file {atlasPath} not found");
return New(version, [skelPath, atlasPath]).PostInit();
}
diff --git a/SpineViewer/Spine/SpineHelper.cs b/SpineViewer/Spine/SpineUtils.cs
similarity index 86%
rename from SpineViewer/Spine/SpineHelper.cs
rename to SpineViewer/Spine/SpineUtils.cs
index 4b60700..9ef3ae2 100644
--- a/SpineViewer/Spine/SpineHelper.cs
+++ b/SpineViewer/Spine/SpineUtils.cs
@@ -12,35 +12,10 @@ using System.Threading.Tasks;
namespace SpineViewer.Spine
{
- ///
- /// 支持的 Spine 版本
- ///
- public enum SpineVersion
- {
- [Description("")] Auto = 0x0000,
- [Description("2.1.x")] V21 = 0x0201,
- [Description("3.6.x")] V36 = 0x0306,
- [Description("3.7.x")] V37 = 0x0307,
- [Description("3.8.x")] V38 = 0x0308,
- [Description("4.0.x")] V40 = 0x0400,
- [Description("4.1.x")] V41 = 0x0401,
- [Description("4.2.x")] V42 = 0x0402,
- [Description("4.3.x")] V43 = 0x0403,
- }
-
- ///
- /// Spine 实现类标记
- ///
- [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
- public class SpineImplementationAttribute(SpineVersion version) : Attribute, IImplementationKey
- {
- public SpineVersion ImplementationKey { get; private set; } = version;
- }
-
///
/// Spine 版本静态辅助类
///
- public static class SpineHelper
+ public static class SpineUtils
{
///
/// 版本名称
@@ -53,7 +28,7 @@ namespace SpineViewer.Spine
///
private static readonly Dictionary runtimes = [];
- static SpineHelper()
+ static SpineUtils()
{
// 初始化缓存
foreach (var value in Enum.GetValues(typeof(SpineVersion)))
diff --git a/SpineViewer/Spine/SpineVersion.cs b/SpineViewer/Spine/SpineVersion.cs
new file mode 100644
index 0000000..1d37e80
--- /dev/null
+++ b/SpineViewer/Spine/SpineVersion.cs
@@ -0,0 +1,52 @@
+using SpineViewer.Utils;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.Json.Nodes;
+using System.Threading.Tasks;
+
+namespace SpineViewer.Spine
+{
+ ///
+ /// 支持的 Spine 版本
+ ///
+ public enum SpineVersion
+ {
+ [Description("")] Auto = 0x0000,
+ [Description("2.1.x")] V21 = 0x0201,
+ [Description("3.6.x")] V36 = 0x0306,
+ [Description("3.7.x")] V37 = 0x0307,
+ [Description("3.8.x")] V38 = 0x0308,
+ [Description("4.0.x")] V40 = 0x0400,
+ [Description("4.1.x")] V41 = 0x0401,
+ [Description("4.2.x")] V42 = 0x0402,
+ [Description("4.3.x")] V43 = 0x0403,
+ }
+
+ ///
+ /// Spine 实现类标记
+ ///
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
+ public class SpineImplementationAttribute(SpineVersion version) : Attribute, IImplementationKey
+ {
+ public SpineVersion ImplementationKey { get; private set; } = version;
+ }
+
+ public class SpineVersionConverter : EnumConverter
+ {
+ public SpineVersionConverter() : base(typeof(SpineVersion)) { }
+
+ public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type? destinationType)
+ {
+ if (destinationType == typeof(string) && value is SpineVersion version)
+ return version.GetName();
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ }
+}
diff --git a/SpineViewer/Spine/SpineView/SpineAnimationWrapper.cs b/SpineViewer/Spine/SpineView/SpineAnimationProperty.cs
similarity index 63%
rename from SpineViewer/Spine/SpineView/SpineAnimationWrapper.cs
rename to SpineViewer/Spine/SpineView/SpineAnimationProperty.cs
index 66616aa..e46506f 100644
--- a/SpineViewer/Spine/SpineView/SpineAnimationWrapper.cs
+++ b/SpineViewer/Spine/SpineView/SpineAnimationProperty.cs
@@ -9,85 +9,11 @@ using System.Threading.Tasks;
namespace SpineViewer.Spine.SpineView
{
- ///
- /// 对轨道索引属性的包装类, 能够在面板上显示例如时长的属性, 但是处理该属性时按字符串去处理, 例如 ToString 和判断对象相等都是用动画名称实现逻辑
- ///
- ///
- ///
- [TypeConverter(typeof(TrackWrapperConverter))]
- public class TrackWrapper(SpineObject spine, int i)
- {
- private readonly SpineObject spine = spine;
-
- [Browsable(false)]
- public int Index { get; } = i;
-
- [DisplayName("时长")]
- public float Duration => spine.GetAnimationDuration(spine.GetAnimation(Index));
-
- ///
- /// 实现了默认的转为字符串的方式
- ///
- public override string ToString() => spine.GetAnimation(Index);
-
- ///
- /// 影响了属性面板的判断, 当动画名称相同的时候认为两个对象是相同的, 这样属性面板可以在多选的时候正确显示相同取值的内容
- ///
- public override bool Equals(object? obj)
- {
- if (obj is TrackWrapper) return ToString() == obj.ToString();
- return base.Equals(obj);
- }
-
- ///
- /// 哈希码需要和 Equals 行为类似
- ///
- public override int GetHashCode() => HashCode.Combine(typeof(TrackWrapper).FullName.GetHashCode(), ToString().GetHashCode());
- }
-
///
/// 用于在 PropertyGrid 上显示 Spine 动画列表的包装类
///
- public class SpineAnimationWrapper(SpineObject spine) : ICustomTypeDescriptor
+ public class SpineAnimationProperty(SpineObject spine) : ICustomTypeDescriptor
{
- ///
- /// 轨道属性描述符, 实现对属性的读取和赋值
- ///
- /// 轨道索引
- private class TrackWrapperPropertyDescriptor(int i, Attribute[]? attributes) : PropertyDescriptor($"Track{i}", attributes)
- {
- private readonly int idx = i;
-
- public override Type ComponentType => typeof(SpineAnimationWrapper);
- public override bool IsReadOnly => false;
- public override Type PropertyType => typeof(TrackWrapper);
- public override bool CanResetValue(object component) => false;
- public override void ResetValue(object component) { }
- public override bool ShouldSerializeValue(object component) => false;
-
- ///
- /// 得到一个轨道包装类, 允许用户查看或者修改具体的属性值, 这个地方决定了在面板上看到的是一个对象及其属性
- ///
- public override object? GetValue(object? component)
- {
- if (component is SpineAnimationWrapper tracks)
- return tracks.GetTrackWrapper(idx);
- return null;
- }
-
- ///
- /// 允许通过字符串赋值修改该轨道的动画, 这里决定了当其他地方的调用 (比如 Converter) 通过 value 来设置属性值的时候应该怎么处理
- ///
- public override void SetValue(object? component, object? value)
- {
- if (component is SpineAnimationWrapper tracks)
- {
- if (value is string s)
- tracks.SetTrackWrapper(idx, s);
- }
- }
- }
-
[Browsable(false)]
public SpineObject Spine { get; } = spine;
@@ -98,24 +24,24 @@ namespace SpineViewer.Spine.SpineView
public float AnimationTracksMaxDuration => Spine.GetTrackIndices().Select(i => Spine.GetAnimationDuration(Spine.GetAnimation(i))).Max();
///
- /// TrackWrapper 属性对象缓存
+ /// 属性对象缓存
///
- private readonly Dictionary trackWrapperProperties = [];
+ private readonly Dictionary trackAnimationProperties = [];
///
- /// 访问 TrackWrapper 属性 AnimationTracks.Track{i}
+ /// this.Track{i}
///
- public TrackWrapper GetTrackWrapper(int i)
+ public TrackAnimationProperty GetTrackAnimation(int i)
{
- if (!trackWrapperProperties.ContainsKey(i))
- trackWrapperProperties[i] = new TrackWrapper(Spine, i);
- return trackWrapperProperties[i];
+ if (!trackAnimationProperties.ContainsKey(i))
+ trackAnimationProperties[i] = new TrackAnimationProperty(Spine, i);
+ return trackAnimationProperties[i];
}
///
- /// 设置 TrackWrapper 属性 AnimationTracks.Track{i} =
+ /// this.Track{i} =
///
- public void SetTrackWrapper(int i, string value)
+ public void SetTrackAnimation(int i, string value)
{
Spine.SetAnimation(i, value);
TypeDescriptor.Refresh(this);
@@ -128,11 +54,11 @@ namespace SpineViewer.Spine.SpineView
public override bool Equals(object? obj)
{
- if (obj is SpineAnimationWrapper wrapper) return ToString() == wrapper.ToString();
+ if (obj is SpineAnimationProperty prop) return ToString() == prop.ToString();
return base.Equals(obj);
}
- public override int GetHashCode() => HashCode.Combine(typeof(SpineAnimationWrapper).FullName.GetHashCode(), ToString().GetHashCode());
+ public override int GetHashCode() => HashCode.Combine(typeof(SpineAnimationProperty).FullName.GetHashCode(), ToString().GetHashCode());
#region ICustomTypeDescriptor 接口实现
@@ -166,6 +92,113 @@ namespace SpineViewer.Spine.SpineView
return props;
}
+ ///
+ /// 轨道属性描述符, 实现对属性的读取和赋值
+ ///
+ /// 轨道索引
+ private class TrackWrapperPropertyDescriptor(int i, Attribute[]? attributes) : PropertyDescriptor($"Track{i}", attributes)
+ {
+ private readonly int idx = i;
+
+ public override Type ComponentType => typeof(SpineAnimationProperty);
+ public override bool IsReadOnly => false;
+ public override Type PropertyType => typeof(TrackAnimationProperty);
+ public override bool CanResetValue(object component) => false;
+ public override void ResetValue(object component) { }
+ public override bool ShouldSerializeValue(object component) => false;
+
+ ///
+ /// 得到一个轨道包装类, 允许用户查看或者修改具体的属性值, 这个地方决定了在面板上看到的是一个对象及其属性
+ ///
+ public override object? GetValue(object? component)
+ {
+ if (component is SpineAnimationProperty tracks)
+ return tracks.GetTrackAnimation(idx);
+ return null;
+ }
+
+ ///
+ /// 允许通过字符串赋值修改该轨道的动画, 这里决定了当其他地方的调用 (比如 Converter) 通过 value 来设置属性值的时候应该怎么处理
+ ///
+ public override void SetValue(object? component, object? value)
+ {
+ if (component is SpineAnimationProperty tracks)
+ {
+ if (value is string s)
+ tracks.SetTrackAnimation(idx, s);
+ }
+ }
+ }
+
#endregion
}
+
+ ///
+ /// 对 .Track{i} 属性的包装类
+ ///
+ [TypeConverter(typeof(TrackAnimationPropertyConverter))]
+ public class TrackAnimationProperty(SpineObject spine, int i)
+ {
+ private readonly SpineObject spine = spine;
+
+ [Browsable(false)]
+ public int Index { get; } = i;
+
+ [DisplayName("时长")]
+ public float Duration => spine.GetAnimationDuration(spine.GetAnimation(Index));
+
+ ///
+ /// 实现了默认的转为字符串的方式
+ ///
+ public override string ToString() => spine.GetAnimation(Index);
+
+ ///
+ /// 影响了属性面板的判断, 当动画名称相同的时候认为两个对象是相同的, 这样属性面板可以在多选的时候正确显示相同取值的内容
+ ///
+ public override bool Equals(object? obj)
+ {
+ if (obj is TrackAnimationProperty) return ToString() == obj.ToString();
+ return base.Equals(obj);
+ }
+
+ ///
+ /// 哈希码需要和 Equals 行为类似
+ ///
+ public override int GetHashCode() => HashCode.Combine(typeof(TrackAnimationProperty).FullName.GetHashCode(), ToString().GetHashCode());
+ }
+
+ ///
+ /// 轨道索引包装类转换器, 实现字符串和包装类的互相转换, 并且提供标准值列表对属性进行设置, 同时还提供在面板上显示包装类属性的能力
+ ///
+ public class TrackAnimationPropertyConverter : ExpandableObjectConverter
+ {
+ // NOTE: 可以不用实现 ConvertTo/ConvertFrom, 因为属性实现了与字符串之间的互转
+ // ToString 实现了 ConvertTo
+ // SetValue 实现了从字符串设置属性
+
+ public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true;
+
+ public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => true;
+
+ public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context)
+ {
+ if (context?.Instance is SpineAnimationProperty tracks)
+ {
+ return new StandardValuesCollection(tracks.Spine.AnimationNames);
+ }
+ else if (context?.Instance is object[] instances && instances.All(x => x is SpineAnimationProperty))
+ {
+ // XXX: 这里不知道为啥总是会得到 object[] 类型而不是具体的类型
+ var animTracks = instances.Cast().ToArray();
+ if (animTracks.Length > 0)
+ {
+ IEnumerable common = animTracks[0].Spine.AnimationNames;
+ foreach (var t in animTracks.Skip(1))
+ common = common.Union(t.Spine.AnimationNames);
+ return new StandardValuesCollection(common.ToArray());
+ }
+ }
+ return base.GetStandardValues(context);
+ }
+ }
}
diff --git a/SpineViewer/Spine/SpineView/SpineBaseInfoWrapper.cs b/SpineViewer/Spine/SpineView/SpineBaseInfoProperty.cs
similarity index 96%
rename from SpineViewer/Spine/SpineView/SpineBaseInfoWrapper.cs
rename to SpineViewer/Spine/SpineView/SpineBaseInfoProperty.cs
index 8ce37ab..c2b6fab 100644
--- a/SpineViewer/Spine/SpineView/SpineBaseInfoWrapper.cs
+++ b/SpineViewer/Spine/SpineView/SpineBaseInfoProperty.cs
@@ -12,7 +12,7 @@ namespace SpineViewer.Spine.SpineView
///
/// 用于在 PropertyGrid 上显示 Spine 基本信息的包装类
///
- public class SpineBaseInfoWrapper(SpineObject spine)
+ public class SpineBaseInfoProperty(SpineObject spine)
{
[Browsable(false)]
public SpineObject Spine { get; } = spine;
diff --git a/SpineViewer/Spine/SpineView/SpineDebugWrapper.cs b/SpineViewer/Spine/SpineView/SpineDebugProperty.cs
similarity index 94%
rename from SpineViewer/Spine/SpineView/SpineDebugWrapper.cs
rename to SpineViewer/Spine/SpineView/SpineDebugProperty.cs
index 848a43e..c56db9d 100644
--- a/SpineViewer/Spine/SpineView/SpineDebugWrapper.cs
+++ b/SpineViewer/Spine/SpineView/SpineDebugProperty.cs
@@ -11,7 +11,7 @@ namespace SpineViewer.Spine.SpineView
///
/// 用于在 PropertyGrid 上显示 Spine 调试属性的包装类
///
- public class SpineDebugWrapper(SpineObject spine)
+ public class SpineDebugProperty(SpineObject spine)
{
[Browsable(false)]
public SpineObject Spine { get; } = spine;
diff --git a/SpineViewer/Spine/SpineView/SpineWrapper.cs b/SpineViewer/Spine/SpineView/SpineObjectProperty.cs
similarity index 59%
rename from SpineViewer/Spine/SpineView/SpineWrapper.cs
rename to SpineViewer/Spine/SpineView/SpineObjectProperty.cs
index 7128c61..9ffae04 100644
--- a/SpineViewer/Spine/SpineView/SpineWrapper.cs
+++ b/SpineViewer/Spine/SpineView/SpineObjectProperty.cs
@@ -9,29 +9,29 @@ using SpineViewer.Spine;
namespace SpineViewer.Spine.SpineView
{
- public class SpineWrapper(SpineObject spine)
+ public class SpineObjectProperty(SpineObject spine)
{
[Browsable(false)]
public SpineObject Spine { get; } = spine;
[DisplayName("基本信息")]
- public SpineBaseInfoWrapper BaseInfo { get; } = new(spine);
+ public SpineBaseInfoProperty BaseInfo { get; } = new(spine);
[DisplayName("渲染")]
- public SpineRenderWrapper Render { get; } = new(spine);
+ public SpineRenderProperty Render { get; } = new(spine);
[DisplayName("变换")]
- public SpineTransformWrapper Transform { get; } = new(spine);
+ public SpineTransformProperty Transform { get; } = new(spine);
[TypeConverter(typeof(ExpandableObjectConverter))]
[DisplayName("皮肤")]
- public SpineSkinWrapper Skin { get; } = new(spine);
+ public SpineSkinProperty Skin { get; } = new(spine);
[TypeConverter(typeof(ExpandableObjectConverter))]
[DisplayName("动画")]
- public SpineAnimationWrapper Animation { get; } = new(spine);
+ public SpineAnimationProperty Animation { get; } = new(spine);
[DisplayName("调试")]
- public SpineDebugWrapper Debug { get; } = new(spine);
+ public SpineDebugProperty Debug { get; } = new(spine);
}
}
diff --git a/SpineViewer/Spine/SpineView/SpineRenderWrapper.cs b/SpineViewer/Spine/SpineView/SpineRenderProperty.cs
similarity index 94%
rename from SpineViewer/Spine/SpineView/SpineRenderWrapper.cs
rename to SpineViewer/Spine/SpineView/SpineRenderProperty.cs
index f378e4f..6445a74 100644
--- a/SpineViewer/Spine/SpineView/SpineRenderWrapper.cs
+++ b/SpineViewer/Spine/SpineView/SpineRenderProperty.cs
@@ -11,7 +11,7 @@ namespace SpineViewer.Spine.SpineView
///
/// 用于在 PropertyGrid 上显示 Spine 渲染设置的包装类
///
- public class SpineRenderWrapper(SpineObject spine)
+ public class SpineRenderProperty(SpineObject spine)
{
[Browsable(false)]
public SpineObject Spine { get; } = spine;
diff --git a/SpineViewer/Spine/SpineView/SpineSkinWrapper.cs b/SpineViewer/Spine/SpineView/SpineSkinProperty.cs
similarity index 57%
rename from SpineViewer/Spine/SpineView/SpineSkinWrapper.cs
rename to SpineViewer/Spine/SpineView/SpineSkinProperty.cs
index 02d47a7..cc9b2b7 100644
--- a/SpineViewer/Spine/SpineView/SpineSkinWrapper.cs
+++ b/SpineViewer/Spine/SpineView/SpineSkinProperty.cs
@@ -9,99 +9,34 @@ using System.Threading.Tasks;
namespace SpineViewer.Spine.SpineView
{
- ///
- /// 对皮肤属性的包装类
- ///
- [TypeConverter(typeof(SkinWrapperConverter))]
- public class SkinWrapper(SpineObject spine, int i)
- {
- private readonly SpineObject spine = spine;
-
- [Browsable(false)]
- public int Index { get; } = i;
-
- public override string ToString()
- {
- var loadedSkins = spine.GetLoadedSkins();
- if (Index >= 0 && Index < loadedSkins.Length)
- return loadedSkins[Index];
- return "!NULL"; // XXX: 预期应该不会发生
- }
-
- public override bool Equals(object? obj)
- {
- if (obj is SkinWrapper) return ToString() == obj.ToString();
- return base.Equals(obj);
- }
-
- public override int GetHashCode() => HashCode.Combine(typeof(SkinWrapper).FullName.GetHashCode(), ToString().GetHashCode());
- }
-
///
/// 皮肤列表动态类型包装类, 用于提供对 Spine 皮肤列表的管理能力
///
/// 关联的 Spine 对象
- public class SpineSkinWrapper(SpineObject spine) : ICustomTypeDescriptor
+ public class SpineSkinProperty(SpineObject spine) : ICustomTypeDescriptor
{
- ///
- /// 皮肤属性描述符, 实现对属性的读取和赋值
- ///
- private class SkinWrapperPropertyDescriptor(int i, Attribute[]? attributes) : PropertyDescriptor($"Skin{i}", attributes)
- {
- private readonly int idx = i;
-
- public override Type ComponentType => typeof(SpineSkinWrapper);
- public override bool IsReadOnly => false;
- public override Type PropertyType => typeof(SkinWrapper);
- public override bool CanResetValue(object component) => false;
- public override void ResetValue(object component) { }
- public override bool ShouldSerializeValue(object component) => false;
-
- ///
- /// 得到一个 SkinWrapper, 允许用户查看或者修改具体的属性值, 这个地方决定了在面板上看到的是一个对象及其属性
- ///
- public override object? GetValue(object? component)
- {
- if (component is SpineSkinWrapper manager)
- return manager.GetSkinWrapper(idx);
- return null;
- }
-
- ///
- /// 允许通过字符串赋值修改该位置的皮肤
- ///
- public override void SetValue(object? component, object? value)
- {
- if (component is SpineSkinWrapper manager)
- {
- if (value is string s)
- manager.SetSkinWrapper(idx, s);
- }
- }
- }
-
[Browsable(false)]
public SpineObject Spine { get; } = spine;
///
- /// SkinWrapper 属性缓存
+ /// 属性缓存
///
- private readonly Dictionary skinWrapperProperties = [];
+ private readonly Dictionary skinNameProperties = [];
///
- /// 访问 SkinWrapper 属性 SkinManager.Skin{i}
+ /// this.Skin{i}
///
- public SkinWrapper GetSkinWrapper(int i)
+ public SkinNameProperty GetSkinName(int i)
{
- if (!skinWrapperProperties.ContainsKey(i))
- skinWrapperProperties[i] = new SkinWrapper(Spine, i);
- return skinWrapperProperties[i];
+ if (!skinNameProperties.ContainsKey(i))
+ skinNameProperties[i] = new SkinNameProperty(Spine, i);
+ return skinNameProperties[i];
}
///
- /// 设置 SkinWrapper 属性 SkinManager.Skin{i} =
+ /// this.Skin{i} =
///
- public void SetSkinWrapper(int i, string value)
+ public void SetSkinName(int i, string value)
{
Spine.ReplaceSkin(i, value);
TypeDescriptor.Refresh(this);
@@ -114,17 +49,17 @@ namespace SpineViewer.Spine.SpineView
public override bool Equals(object? obj)
{
- if (obj is SpineSkinWrapper wrapper) return ToString() == wrapper.ToString();
+ if (obj is SpineSkinProperty prop) return ToString() == prop.ToString();
return base.Equals(obj);
}
- public override int GetHashCode() => HashCode.Combine(typeof(SpineSkinWrapper).FullName.GetHashCode(), ToString().GetHashCode());
+ public override int GetHashCode() => HashCode.Combine(typeof(SpineSkinProperty).FullName.GetHashCode(), ToString().GetHashCode());
#region ICustomTypeDescriptor 接口实现
// XXX: 必须实现 ICustomTypeDescriptor 接口, 不能继承 CustomTypeDescriptor, 似乎继承下来的东西会有问题, 导致某些调用不正确
- private static readonly Dictionary pdCache = [];
+ private static readonly Dictionary pdCache = [];
public AttributeCollection GetAttributes() => TypeDescriptor.GetAttributes(this, true);
public string? GetClassName() => TypeDescriptor.GetClassName(this, true);
@@ -143,12 +78,109 @@ namespace SpineViewer.Spine.SpineView
for (var i = 0; i < Spine.GetLoadedSkins().Length; i++)
{
if (!pdCache.ContainsKey(i))
- pdCache[i] = new SkinWrapperPropertyDescriptor(i, [new DisplayNameAttribute($"皮肤 {i}")]);
+ pdCache[i] = new SkinNamePropertyDescriptor(i, [new DisplayNameAttribute($"皮肤 {i}")]);
props.Add(pdCache[i]);
}
return props;
}
+ ///
+ /// 皮肤属性描述符, 实现对属性的读取和赋值
+ ///
+ private class SkinNamePropertyDescriptor(int i, Attribute[]? attributes) : PropertyDescriptor($"Skin{i}", attributes)
+ {
+ private readonly int idx = i;
+
+ public override Type ComponentType => typeof(SpineSkinProperty);
+ public override bool IsReadOnly => false;
+ public override Type PropertyType => typeof(SkinNameProperty);
+ public override bool CanResetValue(object component) => false;
+ public override void ResetValue(object component) { }
+ public override bool ShouldSerializeValue(object component) => false;
+
+ ///
+ /// 得到一个 , 允许用户查看或者修改具体的属性值, 这个地方决定了在面板上看到的是一个对象及其属性
+ ///
+ public override object? GetValue(object? component)
+ {
+ if (component is SpineSkinProperty prop)
+ return prop.GetSkinName(idx);
+ return null;
+ }
+
+ ///
+ /// 允许通过字符串赋值修改该位置的皮肤
+ ///
+ public override void SetValue(object? component, object? value)
+ {
+ if (component is SpineSkinProperty prop)
+ {
+ if (value is string s)
+ prop.SetSkinName(idx, s);
+ }
+ }
+ }
+
#endregion
}
+
+ ///
+ /// 对 .Skin{i} 属性的包装类
+ ///
+ [TypeConverter(typeof(SkinNamePropertyConverter))]
+ public class SkinNameProperty(SpineObject spine, int i)
+ {
+ private readonly SpineObject spine = spine;
+
+ [Browsable(false)]
+ public int Index { get; } = i;
+
+ public override string ToString()
+ {
+ var loadedSkins = spine.GetLoadedSkins();
+ if (Index >= 0 && Index < loadedSkins.Length)
+ return loadedSkins[Index];
+ return "!NULL"; // XXX: 预期应该不会发生
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (obj is SkinNameProperty) return ToString() == obj.ToString();
+ return base.Equals(obj);
+ }
+
+ public override int GetHashCode() => HashCode.Combine(typeof(SkinNameProperty).FullName.GetHashCode(), ToString().GetHashCode());
+ }
+
+ public class SkinNamePropertyConverter : StringConverter
+ {
+ // NOTE: 可以不用实现 ConvertTo/ConvertFrom, 因为属性实现了与字符串之间的互转
+ // ToString 实现了 ConvertTo
+ // SetValue 实现了从字符串设置属性
+
+ public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true;
+
+ public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => true;
+
+ public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context)
+ {
+ if (context?.Instance is SpineSkinProperty manager)
+ {
+ return new StandardValuesCollection(manager.Spine.SkinNames);
+ }
+ else if (context?.Instance is object[] instances && instances.All(x => x is SpineSkinProperty))
+ {
+ // XXX: 这里不知道为啥总是会得到 object[] 类型而不是具体的 SpineSkinWrapper[] 类型
+ var managers = instances.Cast().ToArray();
+ if (managers.Length > 0)
+ {
+ IEnumerable common = managers[0].Spine.SkinNames;
+ foreach (var t in managers.Skip(1))
+ common = common.Union(t.Spine.SkinNames);
+ return new StandardValuesCollection(common.ToArray());
+ }
+ }
+ return base.GetStandardValues(context);
+ }
+ }
}
diff --git a/SpineViewer/Spine/SpineView/SpineTransformWrapper.cs b/SpineViewer/Spine/SpineView/SpineTransformProperty.cs
similarity index 95%
rename from SpineViewer/Spine/SpineView/SpineTransformWrapper.cs
rename to SpineViewer/Spine/SpineView/SpineTransformProperty.cs
index d93898f..cba2d1c 100644
--- a/SpineViewer/Spine/SpineView/SpineTransformWrapper.cs
+++ b/SpineViewer/Spine/SpineView/SpineTransformProperty.cs
@@ -12,7 +12,7 @@ namespace SpineViewer.Spine.SpineView
///
/// 用于在 PropertyGrid 上显示 Spine 空间变换的包装类
///
- public class SpineTransformWrapper(SpineObject spine)
+ public class SpineTransformProperty(SpineObject spine)
{
[Browsable(false)]
public SpineObject Spine { get; } = spine;
diff --git a/SpineViewer/Utils/TypeConverter.cs b/SpineViewer/Utils/TypeConverter.cs
index 98994bb..5da003f 100644
--- a/SpineViewer/Utils/TypeConverter.cs
+++ b/SpineViewer/Utils/TypeConverter.cs
@@ -100,140 +100,6 @@ namespace SpineViewer.Utils
}
}
- public class SpineVersionConverter : EnumConverter
- {
- public SpineVersionConverter() : base(typeof(SpineVersion)) { }
-
- public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type? destinationType)
- {
- if (destinationType == typeof(string) && value is SpineVersion version)
- return version.GetName();
- return base.ConvertTo(context, culture, value, destinationType);
- }
- }
-
- public class SpineSkinNameConverter : StringConverter
- {
- public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true;
-
- public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => true;
-
- public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context)
- {
- if (context?.Instance is SpineObject obj)
- {
- return new StandardValuesCollection(obj.SkinNames);
- }
- else if (context?.Instance is SpineObject[] spines)
- {
- if (spines.Length > 0)
- {
- IEnumerable common = spines[0].SkinNames;
- foreach (var spine in spines.Skip(1))
- common = common.Union(spine.SkinNames);
- return new StandardValuesCollection(common.ToArray());
- }
- }
- return base.GetStandardValues(context);
- }
- }
-
- public class SpineAnimationNameConverter : StringConverter
- {
- public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true;
-
- public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => true;
-
- public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context)
- {
- if (context?.Instance is SpineObject obj)
- {
- return new StandardValuesCollection(obj.AnimationNames);
- }
- else if (context?.Instance is SpineObject[] spines)
- {
- if (spines.Length > 0)
- {
- IEnumerable common = spines[0].AnimationNames;
- foreach (var spine in spines.Skip(1))
- common = common.Union(spine.AnimationNames);
- return new StandardValuesCollection(common.ToArray());
- }
- }
- return base.GetStandardValues(context);
- }
- }
-
- ///
- /// 皮肤位包装类转换器, 实现字符串和包装类的互相转换, 并且提供标准值列表对属性进行设置, 同时还提供在面板上显示包装类属性的能力
- ///
- public class SkinWrapperConverter : StringConverter
- {
- // NOTE: 可以不用实现 ConvertTo/ConvertFrom, 因为属性实现了与字符串之间的互转
- // ToString 实现了 ConvertTo
- // SetValue 实现了从字符串设置属性
-
- public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true;
-
- public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => true;
-
- public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context)
- {
- if (context?.Instance is SpineSkinWrapper manager)
- {
- return new StandardValuesCollection(manager.Spine.SkinNames);
- }
- else if (context?.Instance is object[] instances && instances.All(x => x is SpineSkinWrapper))
- {
- // XXX: 这里不知道为啥总是会得到 object[] 类型而不是具体的 SpineSkinWrapper[] 类型
- var managers = instances.Cast().ToArray();
- if (managers.Length > 0)
- {
- IEnumerable common = managers[0].Spine.SkinNames;
- foreach (var t in managers.Skip(1))
- common = common.Union(t.Spine.SkinNames);
- return new StandardValuesCollection(common.ToArray());
- }
- }
- return base.GetStandardValues(context);
- }
- }
-
- ///
- /// 轨道索引包装类转换器, 实现字符串和包装类的互相转换, 并且提供标准值列表对属性进行设置, 同时还提供在面板上显示包装类属性的能力
- ///
- public class TrackWrapperConverter : ExpandableObjectConverter
- {
- // NOTE: 可以不用实现 ConvertTo/ConvertFrom, 因为属性实现了与字符串之间的互转
- // ToString 实现了 ConvertTo
- // SetValue 实现了从字符串设置属性
-
- public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true;
-
- public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => true;
-
- public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context)
- {
- if (context?.Instance is SpineAnimationWrapper tracks)
- {
- return new StandardValuesCollection(tracks.Spine.AnimationNames);
- }
- else if (context?.Instance is object[] instances && instances.All(x => x is SpineAnimationWrapper))
- {
- // XXX: 这里不知道为啥总是会得到 object[] 类型而不是具体的类型
- var animTracks = instances.Cast().ToArray();
- if (animTracks.Length > 0)
- {
- IEnumerable common = animTracks[0].Spine.AnimationNames;
- foreach (var t in animTracks.Skip(1))
- common = common.Union(t.Spine.AnimationNames);
- return new StandardValuesCollection(common.ToArray());
- }
- }
- return base.GetStandardValues(context);
- }
- }
-
public class SFMLColorConverter : ExpandableObjectConverter
{
private class SFMLColorPropertyDescriptor : SimplePropertyDescriptor
diff --git a/SpineViewer/Utils/UITypeEditor.cs b/SpineViewer/Utils/UITypeEditor.cs
index c4eb140..17c2b13 100644
--- a/SpineViewer/Utils/UITypeEditor.cs
+++ b/SpineViewer/Utils/UITypeEditor.cs
@@ -36,33 +36,33 @@ namespace SpineViewer.Utils
}
}
- ///
- /// skel 文件路径编辑器
- ///
- public class SkelFileNameEditor : FileNameEditor
- {
- protected override void InitializeDialog(OpenFileDialog openFileDialog)
- {
- base.InitializeDialog(openFileDialog);
- openFileDialog.Title = "选择 skel 文件";
- openFileDialog.AddExtension = false;
- openFileDialog.Filter = "skel 文件 (*.skel; *.json)|*.skel;*.json|二进制文件 (*.skel)|*.skel|文本文件 (*.json)|*.json|所有文件 (*.*)|*.*";
- }
- }
+ /////
+ ///// skel 文件路径编辑器
+ /////
+ //public class SkelFileNameEditor : FileNameEditor
+ //{
+ // protected override void InitializeDialog(OpenFileDialog openFileDialog)
+ // {
+ // base.InitializeDialog(openFileDialog);
+ // openFileDialog.Title = "选择 skel 文件";
+ // openFileDialog.AddExtension = false;
+ // openFileDialog.Filter = "skel 文件 (*.skel; *.json)|*.skel;*.json|二进制文件 (*.skel)|*.skel|文本文件 (*.json)|*.json|所有文件 (*.*)|*.*";
+ // }
+ //}
- ///
- /// atlas 文件路径编辑器
- ///
- public class AtlasFileNameEditor : FileNameEditor
- {
- protected override void InitializeDialog(OpenFileDialog openFileDialog)
- {
- base.InitializeDialog(openFileDialog);
- openFileDialog.Title = "选择 atlas 文件";
- openFileDialog.AddExtension = false;
- openFileDialog.Filter = "atlas 文件 (*.atlas)|*.atlas|所有文件 (*.*)|*.*";
- }
- }
+ /////
+ ///// atlas 文件路径编辑器
+ /////
+ //public class AtlasFileNameEditor : FileNameEditor
+ //{
+ // protected override void InitializeDialog(OpenFileDialog openFileDialog)
+ // {
+ // base.InitializeDialog(openFileDialog);
+ // openFileDialog.Title = "选择 atlas 文件";
+ // openFileDialog.AddExtension = false;
+ // openFileDialog.Filter = "atlas 文件 (*.atlas)|*.atlas|所有文件 (*.*)|*.*";
+ // }
+ //}
class SFMLColorEditor : UITypeEditor
{