Compare commits

...

20 Commits

Author SHA1 Message Date
ww-rm
b401a16002 更新至v0.10.4 2025-03-20 23:50:04 +08:00
ww-rm
523b0ce295 update changelog 2025-03-20 23:49:54 +08:00
ww-rm
abb06726f0 fix bug 2025-03-20 23:49:45 +08:00
ww-rm
d9190e9418 修复图标显示问题 2025-03-20 20:30:04 +08:00
ww-rm
9fe3761eca 修改默认大小 2025-03-20 20:22:07 +08:00
ww-rm
51824afba6 优化列表使用 2025-03-20 20:02:25 +08:00
ww-rm
160a49ad5f 更新至v0.10.3 2025-03-20 15:38:04 +08:00
ww-rm
9d4907d77e update changelog and readme 2025-03-20 15:37:49 +08:00
ww-rm
53d30e0503 修改字母快捷键 2025-03-20 15:34:17 +08:00
ww-rm
9609a2fd5d 增加拖放文件打开 2025-03-20 15:32:31 +08:00
ww-rm
66cf0efcb9 增加单独的结果包装类 2025-03-20 15:31:35 +08:00
ww-rm
0129b9df31 small change 2025-03-20 14:20:45 +08:00
ww-rm
a7a5521be1 增加自动版本 2025-03-20 14:20:26 +08:00
ww-rm
f7f7211ca2 增加自动版本 2025-03-20 14:04:19 +08:00
ww-rm
8c921a6ed5 修改错误类型 2025-03-20 14:03:33 +08:00
ww-rm
f14ab870f7 update 2025-03-20 11:05:05 +08:00
ww-rm
26e81ffdb6 update readme preview 2025-03-20 10:14:59 +08:00
ww-rm
598a88203e 更新至v0.10.2 2025-03-20 00:12:41 +08:00
ww-rm
914d02e754 优化列表右键菜单功能 2025-03-20 00:10:09 +08:00
ww-rm
5cf30f391b 增加置顶 2025-03-19 17:07:47 +08:00
28 changed files with 491 additions and 186 deletions

View File

@@ -47,8 +47,8 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tag_name: ${{ github.ref_name }} tag_name: ${{ env.VERSION }}
release_name: Release ${{ github.ref_name }} release_name: Release ${{ env.VERSION }}
draft: false draft: false
prerelease: false prerelease: false

View File

@@ -1,5 +1,20 @@
# CHANGELOG # CHANGELOG
## v0.10.4
- <20>޸<EFBFBD>һЩ<D2BB><D0A9><EFBFBD><EFBFBD>
## v0.10.3
- <20><><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6><EFBFBD><E6B1BE><EFBFBD><EFBFBD>
- <20><><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>ϷŴ<CFB7><C5B4><EFBFBD>
## v0.10.2
- <20><><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><D0B1>Ҽ<EFBFBD><D2BC>˵<EFBFBD><CBB5><EFBFBD><EFBFBD>ݼ<EFBFBD>
- <20><><EFBFBD><EFBFBD>Ԥ<EFBFBD><D4A4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><CDBC><EFBFBD><EFBFBD>
- <20><><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><D0B1><EFBFBD>ͼ<EFBFBD>л<EFBFBD>
## v0.10.1 ## v0.10.1
- <20><><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>Ԥ<EFBFBD><D4A4>ͼ - <20><><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>Ԥ<EFBFBD><D4A4>ͼ

View File

@@ -6,7 +6,7 @@
A simple and user-friendly Spine file viewer and exporter. A simple and user-friendly Spine file viewer and exporter.
![previewer](img/preview.jpg) ![previewer](img/preview.webp)
--- ---

View File

@@ -6,7 +6,7 @@
一个简单好用的 Spine 文件查看&导出程序. 一个简单好用的 Spine 文件查看&导出程序.
![previewer](img/preview.jpg) ![previewer](img/preview.webp)
--- ---
@@ -33,6 +33,9 @@
| `4.2.x` | :white_check_mark: | | | `4.2.x` | :white_check_mark: | |
| `4.3.x` | | | | `4.3.x` | | |
- 支持文件拖放打开
- 支持自动检测版本
- 支持列表缩略图预览
- 支持多骨骼文件动画预览 - 支持多骨骼文件动画预览
- 支持每个骨骼独立参数设置 - 支持每个骨骼独立参数设置
- 支持动画PNG帧序列导出 - 支持动画PNG帧序列导出
@@ -46,6 +49,8 @@
**文件**菜单可以选择**打开**或者**批量打开**进行骨骼文件导入. **文件**菜单可以选择**打开**或者**批量打开**进行骨骼文件导入.
或者直接把要打开的骨骼文件拖进列表, 这种方式只支持 `.json``.skel` 后缀的文件, 其他的会被忽略.
### 骨骼调整 ### 骨骼调整
在**模型列表**中选择一项或多项, 将会在**模型参数**面板显示可供调节的参数. 在**模型列表**中选择一项或多项, 将会在**模型参数**面板显示可供调节的参数.

View File

@@ -129,8 +129,8 @@ namespace SpineRuntime38 {
if (skeletonData.hash.Length == 0) skeletonData.hash = null; if (skeletonData.hash.Length == 0) skeletonData.hash = null;
skeletonData.version = input.ReadString(); skeletonData.version = input.ReadString();
if (skeletonData.version.Length == 0) skeletonData.version = null; if (skeletonData.version.Length == 0) skeletonData.version = null;
if ("3.8.75" == skeletonData.version) //if ("3.8.75" == skeletonData.version)
throw new Exception("Unsupported skeleton data, please export with a newer version of Spine."); // throw new Exception("Unsupported skeleton data, please export with a newer version of Spine.");
skeletonData.x = input.ReadFloat(); skeletonData.x = input.ReadFloat();
skeletonData.y = input.ReadFloat(); skeletonData.y = input.ReadFloat();
skeletonData.width = input.ReadFloat(); skeletonData.width = input.ReadFloat();

View File

@@ -100,8 +100,8 @@ namespace SpineRuntime38 {
var skeletonMap = (Dictionary<string, Object>)root["skeleton"]; var skeletonMap = (Dictionary<string, Object>)root["skeleton"];
skeletonData.hash = (string)skeletonMap["hash"]; skeletonData.hash = (string)skeletonMap["hash"];
skeletonData.version = (string)skeletonMap["spine"]; skeletonData.version = (string)skeletonMap["spine"];
if ("3.8.75" == skeletonData.version) //if ("3.8.75" == skeletonData.version)
throw new Exception("Unsupported skeleton data, please export with a newer version of Spine."); // throw new Exception("Unsupported skeleton data, please export with a newer version of Spine.");
skeletonData.x = GetFloat(skeletonMap, "x", 0); skeletonData.x = GetFloat(skeletonMap, "x", 0);
skeletonData.y = GetFloat(skeletonMap, "y", 0); skeletonData.y = GetFloat(skeletonMap, "y", 0);
skeletonData.width = GetFloat(skeletonMap, "width", 0); skeletonData.width = GetFloat(skeletonMap, "width", 0);

View File

@@ -36,15 +36,20 @@
toolStripMenuItem_Insert = new ToolStripMenuItem(); toolStripMenuItem_Insert = new ToolStripMenuItem();
toolStripMenuItem_Remove = new ToolStripMenuItem(); toolStripMenuItem_Remove = new ToolStripMenuItem();
toolStripSeparator1 = new ToolStripSeparator(); toolStripSeparator1 = new ToolStripSeparator();
toolStripMenuItem_MoveUp = new ToolStripMenuItem();
toolStripMenuItem_MoveDown = new ToolStripMenuItem();
toolStripSeparator2 = new ToolStripSeparator();
toolStripMenuItem_BatchAdd = new ToolStripMenuItem(); toolStripMenuItem_BatchAdd = new ToolStripMenuItem();
toolStripMenuItem_RemoveAll = new ToolStripMenuItem(); toolStripMenuItem_RemoveAll = new ToolStripMenuItem();
toolStripSeparator2 = new ToolStripSeparator();
toolStripMenuItem_MoveUp = new ToolStripMenuItem();
toolStripMenuItem_MoveDown = new ToolStripMenuItem();
toolStripMenuItem_MoveTop = new ToolStripMenuItem();
toolStripMenuItem_MoveBottom = new ToolStripMenuItem();
toolStripSeparator3 = new ToolStripSeparator(); toolStripSeparator3 = new ToolStripSeparator();
toolStripMenuItem_SelectAll = new ToolStripMenuItem();
toolStripMenuItem_CopyPreview = new ToolStripMenuItem();
toolStripSeparator4 = new ToolStripSeparator();
toolStripMenuItem_ChangeView = new ToolStripMenuItem(); toolStripMenuItem_ChangeView = new ToolStripMenuItem();
toolStripMenuItem_LargeIconView = new ToolStripMenuItem(); toolStripMenuItem_LargeIconView = new ToolStripMenuItem();
toolStripMenuItem_SmallIconView = new ToolStripMenuItem(); toolStripMenuItem_ListView = new ToolStripMenuItem();
toolStripMenuItem_DetailsView = new ToolStripMenuItem(); toolStripMenuItem_DetailsView = new ToolStripMenuItem();
imageList_LargeIcon = new ImageList(components); imageList_LargeIcon = new ImageList(components);
imageList_SmallIcon = new ImageList(components); imageList_SmallIcon = new ImageList(components);
@@ -53,6 +58,7 @@
// //
// listView // listView
// //
listView.Alignment = ListViewAlignment.Left;
listView.AllowDrop = true; listView.AllowDrop = true;
listView.Columns.AddRange(new ColumnHeader[] { columnHeader_Name }); listView.Columns.AddRange(new ColumnHeader[] { columnHeader_Name });
listView.ContextMenuStrip = contextMenuStrip; listView.ContextMenuStrip = contextMenuStrip;
@@ -71,8 +77,8 @@
listView.ItemDrag += listView_ItemDrag; listView.ItemDrag += listView_ItemDrag;
listView.SelectedIndexChanged += listView_SelectedIndexChanged; listView.SelectedIndexChanged += listView_SelectedIndexChanged;
listView.DragDrop += listView_DragDrop; listView.DragDrop += listView_DragDrop;
listView.DragEnter += listView_DragEnter;
listView.DragOver += listView_DragOver; listView.DragOver += listView_DragOver;
listView.KeyDown += listView_KeyDown;
// //
// columnHeader_Name // columnHeader_Name
// //
@@ -82,104 +88,145 @@
// contextMenuStrip // contextMenuStrip
// //
contextMenuStrip.ImageScalingSize = new Size(24, 24); contextMenuStrip.ImageScalingSize = new Size(24, 24);
contextMenuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_Add, toolStripMenuItem_Insert, toolStripMenuItem_Remove, toolStripSeparator1, toolStripMenuItem_MoveUp, toolStripMenuItem_MoveDown, toolStripSeparator2, toolStripMenuItem_BatchAdd, toolStripMenuItem_RemoveAll, toolStripSeparator3, toolStripMenuItem_ChangeView }); contextMenuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_Add, toolStripMenuItem_Insert, toolStripMenuItem_Remove, toolStripSeparator1, toolStripMenuItem_BatchAdd, toolStripMenuItem_RemoveAll, toolStripSeparator2, toolStripMenuItem_MoveUp, toolStripMenuItem_MoveDown, toolStripMenuItem_MoveTop, toolStripMenuItem_MoveBottom, toolStripSeparator3, toolStripMenuItem_SelectAll, toolStripMenuItem_CopyPreview, toolStripSeparator4, toolStripMenuItem_ChangeView });
contextMenuStrip.Name = "contextMenuStrip"; contextMenuStrip.Name = "contextMenuStrip";
contextMenuStrip.Size = new Size(188, 262); contextMenuStrip.Size = new Size(329, 388);
contextMenuStrip.Closed += contextMenuStrip_Closed;
contextMenuStrip.Opening += contextMenuStrip_Opening; contextMenuStrip.Opening += contextMenuStrip_Opening;
// //
// toolStripMenuItem_Add // toolStripMenuItem_Add
// //
toolStripMenuItem_Add.Name = "toolStripMenuItem_Add"; toolStripMenuItem_Add.Name = "toolStripMenuItem_Add";
toolStripMenuItem_Add.Size = new Size(187, 30); toolStripMenuItem_Add.Size = new Size(328, 30);
toolStripMenuItem_Add.Text = "添加(&A)..."; toolStripMenuItem_Add.Text = "添加...";
toolStripMenuItem_Add.Click += toolStripMenuItem_Add_Click; toolStripMenuItem_Add.Click += toolStripMenuItem_Add_Click;
// //
// toolStripMenuItem_Insert // toolStripMenuItem_Insert
// //
toolStripMenuItem_Insert.Enabled = false;
toolStripMenuItem_Insert.Name = "toolStripMenuItem_Insert"; toolStripMenuItem_Insert.Name = "toolStripMenuItem_Insert";
toolStripMenuItem_Insert.Size = new Size(187, 30); toolStripMenuItem_Insert.Size = new Size(328, 30);
toolStripMenuItem_Insert.Text = "插入(&I)..."; toolStripMenuItem_Insert.Text = "插入...";
toolStripMenuItem_Insert.Click += toolStripMenuItem_Insert_Click; toolStripMenuItem_Insert.Click += toolStripMenuItem_Insert_Click;
// //
// toolStripMenuItem_Remove // toolStripMenuItem_Remove
// //
toolStripMenuItem_Remove.Enabled = false;
toolStripMenuItem_Remove.Name = "toolStripMenuItem_Remove"; toolStripMenuItem_Remove.Name = "toolStripMenuItem_Remove";
toolStripMenuItem_Remove.Size = new Size(187, 30); toolStripMenuItem_Remove.ShortcutKeys = Keys.Delete;
toolStripMenuItem_Remove.Text = "移除(&R)"; toolStripMenuItem_Remove.Size = new Size(328, 30);
toolStripMenuItem_Remove.Text = "移除";
toolStripMenuItem_Remove.Click += toolStripMenuItem_Remove_Click; toolStripMenuItem_Remove.Click += toolStripMenuItem_Remove_Click;
// //
// toolStripSeparator1 // toolStripSeparator1
// //
toolStripSeparator1.Name = "toolStripSeparator1"; toolStripSeparator1.Name = "toolStripSeparator1";
toolStripSeparator1.Size = new Size(184, 6); toolStripSeparator1.Size = new Size(325, 6);
//
// toolStripMenuItem_BatchAdd
//
toolStripMenuItem_BatchAdd.Name = "toolStripMenuItem_BatchAdd";
toolStripMenuItem_BatchAdd.Size = new Size(328, 30);
toolStripMenuItem_BatchAdd.Text = "批量添加...";
toolStripMenuItem_BatchAdd.Click += toolStripMenuItem_BatchAdd_Click;
//
// toolStripMenuItem_RemoveAll
//
toolStripMenuItem_RemoveAll.Name = "toolStripMenuItem_RemoveAll";
toolStripMenuItem_RemoveAll.Size = new Size(328, 30);
toolStripMenuItem_RemoveAll.Text = "移除全部";
toolStripMenuItem_RemoveAll.Click += toolStripMenuItem_RemoveAll_Click;
//
// toolStripSeparator2
//
toolStripSeparator2.Name = "toolStripSeparator2";
toolStripSeparator2.Size = new Size(325, 6);
// //
// toolStripMenuItem_MoveUp // toolStripMenuItem_MoveUp
// //
toolStripMenuItem_MoveUp.Name = "toolStripMenuItem_MoveUp"; toolStripMenuItem_MoveUp.Name = "toolStripMenuItem_MoveUp";
toolStripMenuItem_MoveUp.Size = new Size(187, 30); toolStripMenuItem_MoveUp.ShortcutKeys = Keys.Alt | Keys.W;
toolStripMenuItem_MoveUp.Text = "上移(&U)"; toolStripMenuItem_MoveUp.Size = new Size(328, 30);
toolStripMenuItem_MoveUp.Text = "上移";
toolStripMenuItem_MoveUp.Click += toolStripMenuItem_MoveUp_Click; toolStripMenuItem_MoveUp.Click += toolStripMenuItem_MoveUp_Click;
// //
// toolStripMenuItem_MoveDown // toolStripMenuItem_MoveDown
// //
toolStripMenuItem_MoveDown.Name = "toolStripMenuItem_MoveDown"; toolStripMenuItem_MoveDown.Name = "toolStripMenuItem_MoveDown";
toolStripMenuItem_MoveDown.Size = new Size(187, 30); toolStripMenuItem_MoveDown.ShortcutKeys = Keys.Alt | Keys.S;
toolStripMenuItem_MoveDown.Text = "下移(&D)"; toolStripMenuItem_MoveDown.Size = new Size(328, 30);
toolStripMenuItem_MoveDown.Text = "下移";
toolStripMenuItem_MoveDown.Click += toolStripMenuItem_MoveDown_Click; toolStripMenuItem_MoveDown.Click += toolStripMenuItem_MoveDown_Click;
// //
// toolStripSeparator2 // toolStripMenuItem_MoveTop
// //
toolStripSeparator2.Name = "toolStripSeparator2"; toolStripMenuItem_MoveTop.Name = "toolStripMenuItem_MoveTop";
toolStripSeparator2.Size = new Size(184, 6); toolStripMenuItem_MoveTop.ShortcutKeys = Keys.Alt | Keys.Shift | Keys.W;
toolStripMenuItem_MoveTop.Size = new Size(328, 30);
toolStripMenuItem_MoveTop.Text = "置顶";
toolStripMenuItem_MoveTop.Click += toolStripMenuItem_MoveTop_Click;
// //
// toolStripMenuItem_BatchAdd // toolStripMenuItem_MoveBottom
// //
toolStripMenuItem_BatchAdd.Name = "toolStripMenuItem_BatchAdd"; toolStripMenuItem_MoveBottom.Name = "toolStripMenuItem_MoveBottom";
toolStripMenuItem_BatchAdd.Size = new Size(187, 30); toolStripMenuItem_MoveBottom.ShortcutKeys = Keys.Alt | Keys.Shift | Keys.S;
toolStripMenuItem_BatchAdd.Text = "批量添加(&B)..."; toolStripMenuItem_MoveBottom.Size = new Size(328, 30);
toolStripMenuItem_BatchAdd.Click += toolStripMenuItem_BatchAdd_Click; toolStripMenuItem_MoveBottom.Text = "置底";
// toolStripMenuItem_MoveBottom.Click += toolStripMenuItem_MoveBottom_Click;
// toolStripMenuItem_RemoveAll
//
toolStripMenuItem_RemoveAll.Enabled = false;
toolStripMenuItem_RemoveAll.Name = "toolStripMenuItem_RemoveAll";
toolStripMenuItem_RemoveAll.Size = new Size(187, 30);
toolStripMenuItem_RemoveAll.Text = "移除全部(&X)";
toolStripMenuItem_RemoveAll.Click += toolStripMenuItem_RemoveAll_Click;
// //
// toolStripSeparator3 // toolStripSeparator3
// //
toolStripSeparator3.Name = "toolStripSeparator3"; toolStripSeparator3.Name = "toolStripSeparator3";
toolStripSeparator3.Size = new Size(184, 6); toolStripSeparator3.Size = new Size(325, 6);
//
// toolStripMenuItem_SelectAll
//
toolStripMenuItem_SelectAll.Name = "toolStripMenuItem_SelectAll";
toolStripMenuItem_SelectAll.ShortcutKeys = Keys.Control | Keys.A;
toolStripMenuItem_SelectAll.Size = new Size(328, 30);
toolStripMenuItem_SelectAll.Text = "全选";
toolStripMenuItem_SelectAll.Click += toolStripMenuItem_SelectAll_Click;
//
// toolStripMenuItem_CopyPreview
//
toolStripMenuItem_CopyPreview.Name = "toolStripMenuItem_CopyPreview";
toolStripMenuItem_CopyPreview.ShortcutKeys = Keys.Control | Keys.C;
toolStripMenuItem_CopyPreview.Size = new Size(328, 30);
toolStripMenuItem_CopyPreview.Text = "复制预览图 (256x256)";
toolStripMenuItem_CopyPreview.Click += toolStripMenuItem_CopyPreview_Click;
//
// toolStripSeparator4
//
toolStripSeparator4.Name = "toolStripSeparator4";
toolStripSeparator4.Size = new Size(325, 6);
// //
// toolStripMenuItem_ChangeView // toolStripMenuItem_ChangeView
// //
toolStripMenuItem_ChangeView.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_LargeIconView, toolStripMenuItem_SmallIconView, toolStripMenuItem_DetailsView }); toolStripMenuItem_ChangeView.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_LargeIconView, toolStripMenuItem_ListView, toolStripMenuItem_DetailsView });
toolStripMenuItem_ChangeView.Name = "toolStripMenuItem_ChangeView"; toolStripMenuItem_ChangeView.Name = "toolStripMenuItem_ChangeView";
toolStripMenuItem_ChangeView.Size = new Size(187, 30); toolStripMenuItem_ChangeView.Size = new Size(328, 30);
toolStripMenuItem_ChangeView.Text = "切换视图"; toolStripMenuItem_ChangeView.Text = "切换视图";
// //
// toolStripMenuItem_LargeIconView // toolStripMenuItem_LargeIconView
// //
toolStripMenuItem_LargeIconView.Name = "toolStripMenuItem_LargeIconView"; toolStripMenuItem_LargeIconView.Name = "toolStripMenuItem_LargeIconView";
toolStripMenuItem_LargeIconView.Size = new Size(164, 34); toolStripMenuItem_LargeIconView.ShortcutKeys = Keys.Alt | Keys.D1;
toolStripMenuItem_LargeIconView.Size = new Size(241, 34);
toolStripMenuItem_LargeIconView.Text = "大图标"; toolStripMenuItem_LargeIconView.Text = "大图标";
toolStripMenuItem_LargeIconView.Click += toolStripMenuItem_LargeIconView_Click; toolStripMenuItem_LargeIconView.Click += toolStripMenuItem_LargeIconView_Click;
// //
// toolStripMenuItem_SmallIconView // toolStripMenuItem_ListView
// //
toolStripMenuItem_SmallIconView.Name = "toolStripMenuItem_SmallIconView"; toolStripMenuItem_ListView.Name = "toolStripMenuItem_ListView";
toolStripMenuItem_SmallIconView.Size = new Size(164, 34); toolStripMenuItem_ListView.ShortcutKeys = Keys.Alt | Keys.D2;
toolStripMenuItem_SmallIconView.Text = "小图标"; toolStripMenuItem_ListView.Size = new Size(241, 34);
toolStripMenuItem_SmallIconView.Click += toolStripMenuItem_SmallIconView_Click; toolStripMenuItem_ListView.Text = "列表";
toolStripMenuItem_ListView.Click += toolStripMenuItem_ListView_Click;
// //
// toolStripMenuItem_DetailsView // toolStripMenuItem_DetailsView
// //
toolStripMenuItem_DetailsView.Name = "toolStripMenuItem_DetailsView"; toolStripMenuItem_DetailsView.Name = "toolStripMenuItem_DetailsView";
toolStripMenuItem_DetailsView.Size = new Size(164, 34); toolStripMenuItem_DetailsView.ShortcutKeys = Keys.Alt | Keys.D3;
toolStripMenuItem_DetailsView.Text = "列表"; toolStripMenuItem_DetailsView.Size = new Size(241, 34);
toolStripMenuItem_DetailsView.Text = "详细信息";
toolStripMenuItem_DetailsView.Click += toolStripMenuItem_DetailsView_Click; toolStripMenuItem_DetailsView.Click += toolStripMenuItem_DetailsView_Click;
// //
// imageList_LargeIcon // imageList_LargeIcon
@@ -223,7 +270,12 @@
private ToolStripSeparator toolStripSeparator3; private ToolStripSeparator toolStripSeparator3;
private ToolStripMenuItem toolStripMenuItem_ChangeView; private ToolStripMenuItem toolStripMenuItem_ChangeView;
private ToolStripMenuItem toolStripMenuItem_LargeIconView; private ToolStripMenuItem toolStripMenuItem_LargeIconView;
private ToolStripMenuItem toolStripMenuItem_SmallIconView; private ToolStripMenuItem toolStripMenuItem_ListView;
private ToolStripMenuItem toolStripMenuItem_DetailsView; private ToolStripMenuItem toolStripMenuItem_DetailsView;
private ToolStripMenuItem toolStripMenuItem_MoveTop;
private ToolStripMenuItem toolStripMenuItem_MoveBottom;
private ToolStripMenuItem toolStripMenuItem_CopyPreview;
private ToolStripMenuItem toolStripMenuItem_SelectAll;
private ToolStripSeparator toolStripSeparator4;
} }
} }

View File

@@ -11,6 +11,7 @@ using System.Collections.ObjectModel;
using SpineViewer.Spine; using SpineViewer.Spine;
using System.Reflection; using System.Reflection;
using System.Diagnostics; using System.Diagnostics;
using System.Collections.Specialized;
namespace SpineViewer.Controls namespace SpineViewer.Controls
{ {
@@ -59,10 +60,13 @@ namespace SpineViewer.Controls
var progressDialog = new Dialogs.ProgressDialog(); var progressDialog = new Dialogs.ProgressDialog();
progressDialog.DoWork += BatchAdd_Work; progressDialog.DoWork += BatchAdd_Work;
progressDialog.RunWorkerAsync(openDialog); progressDialog.RunWorkerAsync(openDialog.Result);
progressDialog.ShowDialog(); progressDialog.ShowDialog();
} }
/// <summary>
/// 弹出对话框导出列表预览图
/// </summary>
public void ExportPreviews() public void ExportPreviews()
{ {
lock (Spines) lock (Spines)
@@ -102,19 +106,16 @@ namespace SpineViewer.Controls
spines[i].IsSelected = listView.SelectedIndices.Contains(i); spines[i].IsSelected = listView.SelectedIndices.Contains(i);
} }
} }
}
private void listView_KeyDown(object sender, KeyEventArgs e) // BUG: 图标显示的时候没法自动刷新顺序, 只能切换视图刷新, 不知道什么原理
{ listView.BeginUpdate();
if (e.Control && e.KeyCode == Keys.A) var tmp = listView.View;
{ listView.View = View.List;
listView.BeginUpdate(); listView.View = tmp;
foreach (ListViewItem item in listView.Items) listView.EndUpdate();
{
item.Selected = true; if (listView.SelectedItems.Count > 0)
} listView.SelectedItems[0].EnsureVisible();
listView.EndUpdate();
}
} }
private void listView_ItemDrag(object sender, ItemDragEventArgs e) private void listView_ItemDrag(object sender, ItemDragEventArgs e)
@@ -122,81 +123,125 @@ namespace SpineViewer.Controls
DoDragDrop(e.Item, DragDropEffects.Move); DoDragDrop(e.Item, DragDropEffects.Move);
} }
private void listView_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.Serializable))
e.Effect = DragDropEffects.Move;
else if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Copy;
else
e.Effect = DragDropEffects.None;
}
private void listView_DragOver(object sender, DragEventArgs e) private void listView_DragOver(object sender, DragEventArgs e)
{ {
// 检查拖放目标是否有效 if (e.Data.GetDataPresent(DataFormats.Serializable))
e.Effect = DragDropEffects.Move; {
// 获取鼠标位置并确定目标索引
var point = listView.PointToClient(new(e.X, e.Y));
var targetItem = listView.GetItemAt(point.X, point.Y);
// 获取鼠标位置并确定目标索引 // 高亮目标项
var point = listView.PointToClient(new(e.X, e.Y)); if (targetItem != null)
var targetItem = listView.GetItemAt(point.X, point.Y);
// 高亮目标项
if (targetItem != null)
{
foreach (ListViewItem item in listView.Items)
{ {
item.BackColor = listView.BackColor; foreach (ListViewItem item in listView.Items)
{
item.BackColor = listView.BackColor;
}
targetItem.BackColor = Color.LightGray;
} }
targetItem.BackColor = Color.LightGray;
} }
} }
private void listView_DragDrop(object sender, DragEventArgs e) private void listView_DragDrop(object sender, DragEventArgs e)
{ {
// 获取拖放源项和目标项 if (e.Data.GetDataPresent(DataFormats.Serializable))
var draggedItem = (ListViewItem)e.Data.GetData(typeof(ListViewItem)); {
int draggedIndex = draggedItem.Index; // 获取拖放源项和目标项
var point = listView.PointToClient(new Point(e.X, e.Y)); var draggedItem = (ListViewItem)e.Data.GetData(typeof(ListViewItem));
var targetItem = listView.GetItemAt(point.X, point.Y); int draggedIndex = draggedItem.Index;
int targetIndex = targetItem is null ? listView.Items.Count : targetItem.Index; var point = listView.PointToClient(new Point(e.X, e.Y));
var targetItem = listView.GetItemAt(point.X, point.Y);
int targetIndex = targetItem is null ? listView.Items.Count : targetItem.Index;
if (targetIndex <= draggedIndex) if (targetIndex <= draggedIndex)
{
lock (Spines)
{ {
var draggedSpine = spines[draggedIndex]; lock (Spines)
spines.RemoveAt(draggedIndex); {
spines.Insert(targetIndex, draggedSpine); var draggedSpine = spines[draggedIndex];
spines.RemoveAt(draggedIndex);
spines.Insert(targetIndex, draggedSpine);
}
listView.Items.RemoveAt(draggedIndex);
listView.Items.Insert(targetIndex, draggedItem);
} }
listView.Items.RemoveAt(draggedIndex); else
listView.Items.Insert(targetIndex, draggedItem);
}
else
{
lock (Spines)
{ {
var draggedSpine = spines[draggedIndex]; lock (Spines)
spines.RemoveAt(draggedIndex); {
spines.Insert(targetIndex - 1, draggedSpine); var draggedSpine = spines[draggedIndex];
spines.RemoveAt(draggedIndex);
spines.Insert(targetIndex - 1, draggedSpine);
}
listView.Items.RemoveAt(draggedIndex);
listView.Items.Insert(targetIndex - 1, draggedItem);
} }
listView.Items.RemoveAt(draggedIndex);
listView.Items.Insert(targetIndex - 1, draggedItem);
}
// 重置背景颜色 // 重置背景颜色
foreach (ListViewItem item in listView.Items) foreach (ListViewItem item in listView.Items)
{
item.BackColor = listView.BackColor;
}
}
else if (e.Data.GetDataPresent(DataFormats.FileDrop))
{ {
item.BackColor = listView.BackColor; var validPaths = ((string[])e.Data.GetData(DataFormats.FileDrop)).Where(
path => File.Exists(path) &&
(Path.GetExtension(path).Equals(".skel", StringComparison.OrdinalIgnoreCase) ||
Path.GetExtension(path).Equals(".json", StringComparison.OrdinalIgnoreCase))
).ToArray();
if (validPaths.Length > 1)
{
var progressDialog = new Dialogs.ProgressDialog();
progressDialog.DoWork += BatchAdd_Work;
progressDialog.RunWorkerAsync(new Dialogs.BatchOpenSpineDialogResult(Spine.Version.Auto, validPaths));
progressDialog.ShowDialog();
}
else if (validPaths.Length > 0)
{
Insert(new Dialogs.OpenSpineDialogResult(Spine.Version.Auto, validPaths[0]));
}
} }
} }
private void contextMenuStrip_Opening(object sender, CancelEventArgs e) private void contextMenuStrip_Opening(object sender, CancelEventArgs e)
{ {
var selectedCount = listView.SelectedIndices.Count; var selectedIndices = listView.SelectedIndices;
var selectedCount = selectedIndices.Count;
var itemsCount = listView.Items.Count; var itemsCount = listView.Items.Count;
toolStripMenuItem_Insert.Enabled = selectedCount == 1; toolStripMenuItem_Insert.Enabled = selectedCount == 1;
toolStripMenuItem_Remove.Enabled = selectedCount >= 1; toolStripMenuItem_Remove.Enabled = selectedCount >= 1;
toolStripMenuItem_MoveUp.Enabled = selectedCount == 1 && listView.SelectedIndices[0] != 0; toolStripMenuItem_MoveTop.Enabled = selectedCount == 1 && selectedIndices[0] != 0;
toolStripMenuItem_MoveDown.Enabled = selectedCount == 1 && listView.SelectedIndices[0] != itemsCount - 1; toolStripMenuItem_MoveUp.Enabled = selectedCount == 1 && selectedIndices[0] != 0;
toolStripMenuItem_MoveDown.Enabled = selectedCount == 1 && selectedIndices[0] != itemsCount - 1;
toolStripMenuItem_MoveBottom.Enabled = selectedCount == 1 && selectedIndices[0] != itemsCount - 1;
toolStripMenuItem_RemoveAll.Enabled = itemsCount > 0; toolStripMenuItem_RemoveAll.Enabled = itemsCount > 0;
// 视图选项 // 视图选项
toolStripMenuItem_LargeIconView.Checked = listView.View == View.LargeIcon; toolStripMenuItem_LargeIconView.Checked = listView.View == View.LargeIcon;
toolStripMenuItem_SmallIconView.Checked = listView.View == View.SmallIcon; toolStripMenuItem_ListView.Checked = listView.View == View.List;
toolStripMenuItem_DetailsView.Checked = listView.View == View.Details; toolStripMenuItem_DetailsView.Checked = listView.View == View.Details;
} }
private void contextMenuStrip_Closed(object sender, ToolStripDropDownClosedEventArgs e)
{
// 不显示菜单的时候需要把菜单的各项功能启用, 这样才能正常捕获快捷键
foreach (var item in contextMenuStrip.Items)
if (item is ToolStripMenuItem tsmi)
tsmi.Enabled = true;
}
private void toolStripMenuItem_Add_Click(object sender, EventArgs e) private void toolStripMenuItem_Add_Click(object sender, EventArgs e)
{ {
Insert(); Insert();
@@ -238,6 +283,26 @@ namespace SpineViewer.Controls
} }
} }
private void toolStripMenuItem_MoveTop_Click(object sender, EventArgs e)
{
if (listView.SelectedIndices.Count != 1)
return;
var index = listView.SelectedIndices[0];
if (index > 0)
{
lock (Spines)
{
var spine = spines[index];
spines.RemoveAt(index);
spines.Insert(0, spine);
}
var item = listView.Items[index];
listView.Items.RemoveAt(index);
listView.Items.Insert(0, item);
}
}
private void toolStripMenuItem_MoveUp_Click(object sender, EventArgs e) private void toolStripMenuItem_MoveUp_Click(object sender, EventArgs e)
{ {
if (listView.SelectedIndices.Count != 1) if (listView.SelectedIndices.Count != 1)
@@ -248,8 +313,10 @@ namespace SpineViewer.Controls
{ {
lock (Spines) { (spines[index - 1], spines[index]) = (spines[index], spines[index - 1]); } lock (Spines) { (spines[index - 1], spines[index]) = (spines[index], spines[index - 1]); }
var item = listView.Items[index]; var item = listView.Items[index];
listView.BeginUpdate();
listView.Items.RemoveAt(index); listView.Items.RemoveAt(index);
listView.Items.Insert(index - 1, item); listView.Items.Insert(index - 1, item);
listView.EndUpdate();
} }
} }
@@ -262,9 +329,34 @@ namespace SpineViewer.Controls
if (index < listView.Items.Count - 1) if (index < listView.Items.Count - 1)
{ {
lock (Spines) { (spines[index], spines[index + 1]) = (spines[index + 1], spines[index]); } lock (Spines) { (spines[index], spines[index + 1]) = (spines[index + 1], spines[index]); }
var item = listView.Items[index + 1]; var item = listView.Items[index];
listView.Items.RemoveAt(index + 1); listView.BeginUpdate();
listView.Items.Insert(index, item); listView.Items.RemoveAt(index);
listView.Items.Insert(index + 1, item);
listView.EndUpdate();
}
}
private void toolStripMenuItem_MoveBottom_Click(object sender, EventArgs e)
{
if (listView.SelectedIndices.Count != 1)
return;
var index = listView.SelectedIndices[0];
if (index < listView.Items.Count - 1)
{
lock (Spines)
{
lock (Spines)
{
var spine = spines[index];
spines.RemoveAt(index);
spines.Add(spine);
}
}
var item = listView.Items[index];
listView.Items.RemoveAt(index);
listView.Items.Add(item);
} }
} }
@@ -289,14 +381,42 @@ namespace SpineViewer.Controls
PropertyGrid.SelectedObject = null; PropertyGrid.SelectedObject = null;
} }
private void toolStripMenuItem_SelectAll_Click(object sender, EventArgs e)
{
listView.BeginUpdate();
foreach (ListViewItem item in listView.Items)
item.Selected = true;
listView.EndUpdate();
}
private void toolStripMenuItem_CopyPreview_Click(object sender, EventArgs e)
{
var fileDropList = new StringCollection();
lock (Spines)
{
foreach (int i in listView.SelectedIndices)
{
var spine = spines[i];
var image = spine.Preview;
var path = Path.Combine(Program.TempDir, $"{spine.ID}.png");
using (var clone = new Bitmap(image))
clone.Save(path);
fileDropList.Add(path);
}
}
if (fileDropList.Count > 0)
Clipboard.SetFileDropList(fileDropList);
}
private void toolStripMenuItem_LargeIconView_Click(object sender, EventArgs e) private void toolStripMenuItem_LargeIconView_Click(object sender, EventArgs e)
{ {
listView.View = View.LargeIcon; listView.View = View.LargeIcon;
} }
private void toolStripMenuItem_SmallIconView_Click(object sender, EventArgs e) private void toolStripMenuItem_ListView_Click(object sender, EventArgs e)
{ {
listView.View = View.SmallIcon; listView.View = View.List;
} }
private void toolStripMenuItem_DetailsView_Click(object sender, EventArgs e) private void toolStripMenuItem_DetailsView_Click(object sender, EventArgs e)
@@ -313,9 +433,14 @@ namespace SpineViewer.Controls
if (dialog.ShowDialog() != DialogResult.OK) if (dialog.ShowDialog() != DialogResult.OK)
return; return;
Insert(dialog.Result, index);
}
private void Insert(Dialogs.OpenSpineDialogResult result, int index = -1)
{
try try
{ {
var spine = Spine.Spine.New(dialog.Version, dialog.SkelPath, dialog.AtlasPath); var spine = Spine.Spine.New(result.Version, result.SkelPath, result.AtlasPath);
// 如果索引无效则在末尾添加 // 如果索引无效则在末尾添加
if (index < 0 || index > listView.Items.Count) if (index < 0 || index > listView.Items.Count)
@@ -337,7 +462,7 @@ namespace SpineViewer.Controls
catch (Exception ex) catch (Exception ex)
{ {
Program.Logger.Error(ex.ToString()); Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to load {} {}", dialog.SkelPath, dialog.AtlasPath); Program.Logger.Error("Failed to load {} {}", result.SkelPath, result.AtlasPath);
MessageBox.Show(ex.ToString(), "骨骼加载失败", MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBox.Show(ex.ToString(), "骨骼加载失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
@@ -347,7 +472,7 @@ namespace SpineViewer.Controls
private void BatchAdd_Work(object? sender, DoWorkEventArgs e) private void BatchAdd_Work(object? sender, DoWorkEventArgs e)
{ {
var worker = sender as BackgroundWorker; var worker = sender as BackgroundWorker;
var arguments = e.Argument as Dialogs.BatchOpenSpineDialog; var arguments = e.Argument as Dialogs.BatchOpenSpineDialogResult;
var skelPaths = arguments.SkelPaths; var skelPaths = arguments.SkelPaths;
var version = arguments.Version; var version = arguments.Version;

View File

@@ -121,9 +121,9 @@
<value>17, 17</value> <value>17, 17</value>
</metadata> </metadata>
<metadata name="imageList_LargeIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="imageList_LargeIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>552, 29</value> <value>511, 20</value>
</metadata> </metadata>
<metadata name="imageList_SmallIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="imageList_SmallIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>267, 34</value> <value>252, 19</value>
</metadata> </metadata>
</root> </root>

View File

@@ -13,8 +13,7 @@ namespace SpineViewer.Dialogs
{ {
public partial class BatchOpenSpineDialog : Form public partial class BatchOpenSpineDialog : Form
{ {
public string[] SkelPaths { get; private set; } public BatchOpenSpineDialogResult Result { get; private set; }
public Spine.Version Version { get; private set; }
public BatchOpenSpineDialog() public BatchOpenSpineDialog()
{ {
@@ -22,7 +21,7 @@ namespace SpineViewer.Dialogs
comboBox_Version.DataSource = VersionHelper.Versions.ToList(); comboBox_Version.DataSource = VersionHelper.Versions.ToList();
comboBox_Version.DisplayMember = "Value"; comboBox_Version.DisplayMember = "Value";
comboBox_Version.ValueMember = "Key"; comboBox_Version.ValueMember = "Key";
comboBox_Version.SelectedValue = Spine.Version.V38; comboBox_Version.SelectedValue = Spine.Version.Auto;
} }
private void BatchOpenSpineDialog_Load(object sender, EventArgs e) private void BatchOpenSpineDialog_Load(object sender, EventArgs e)
@@ -60,15 +59,13 @@ namespace SpineViewer.Dialogs
} }
} }
if (!Spine.Spine.ImplementedVersions.Contains(version)) if (version != Spine.Version.Auto && !Spine.Spine.ImplementedVersions.Contains(version))
{ {
MessageBox.Show($"{version.String()} 版本尚未实现(咕咕咕~", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show($"{version.String()} 版本尚未实现(咕咕咕~", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }
SkelPaths = listBox_FilePath.Items.Cast<string>().ToArray(); Result = new(version, listBox_FilePath.Items.Cast<string>().ToArray());
Version = version;
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
} }
@@ -77,4 +74,10 @@ namespace SpineViewer.Dialogs
DialogResult = DialogResult.Cancel; DialogResult = DialogResult.Cancel;
} }
} }
public class BatchOpenSpineDialogResult(Spine.Version version, string[] skelPaths)
{
public Spine.Version Version { get; } = version;
public string[] SkelPaths { get; } = skelPaths;
}
} }

View File

@@ -22,7 +22,12 @@ namespace SpineViewer.Dialogs
public ConvertFileFormatDialog() public ConvertFileFormatDialog()
{ {
InitializeComponent(); InitializeComponent();
comboBox_SourceVersion.DataSource = VersionHelper.Versions.ToList();
// XXX: 文件格式转换暂时不支持自动检测版本
var impVersions = VersionHelper.Versions.ToDictionary();
impVersions.Remove(Spine.Version.Auto);
comboBox_SourceVersion.DataSource = impVersions.ToList();
comboBox_SourceVersion.DisplayMember = "Value"; comboBox_SourceVersion.DisplayMember = "Value";
comboBox_SourceVersion.ValueMember = "Key"; comboBox_SourceVersion.ValueMember = "Key";
comboBox_SourceVersion.SelectedValue = Spine.Version.V38; comboBox_SourceVersion.SelectedValue = Spine.Version.V38;

View File

@@ -12,9 +12,7 @@ namespace SpineViewer.Dialogs
{ {
public partial class OpenSpineDialog : Form public partial class OpenSpineDialog : Form
{ {
public string SkelPath { get; private set; } public OpenSpineDialogResult Result { get; private set; }
public string? AtlasPath { get; private set; }
public Spine.Version Version { get; private set; }
public OpenSpineDialog() public OpenSpineDialog()
{ {
@@ -22,7 +20,7 @@ namespace SpineViewer.Dialogs
comboBox_Version.DataSource = VersionHelper.Versions.ToList(); comboBox_Version.DataSource = VersionHelper.Versions.ToList();
comboBox_Version.DisplayMember = "Value"; comboBox_Version.DisplayMember = "Value";
comboBox_Version.ValueMember = "Key"; comboBox_Version.ValueMember = "Key";
comboBox_Version.SelectedValue = Spine.Version.V38; comboBox_Version.SelectedValue = Spine.Version.Auto;
} }
private void OpenSpineDialog_Load(object sender, EventArgs e) private void OpenSpineDialog_Load(object sender, EventArgs e)
@@ -78,16 +76,13 @@ namespace SpineViewer.Dialogs
atlasPath = Path.GetFullPath(atlasPath); atlasPath = Path.GetFullPath(atlasPath);
} }
if (!Spine.Spine.ImplementedVersions.Contains(version)) if (version != Spine.Version.Auto && !Spine.Spine.ImplementedVersions.Contains(version))
{ {
MessageBox.Show($"{version.String()} 版本尚未实现(咕咕咕~", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show($"{version.String()} 版本尚未实现(咕咕咕~", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }
SkelPath = skelPath; Result = new(version, skelPath, atlasPath);
AtlasPath = atlasPath;
Version = version;
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
} }
@@ -96,4 +91,11 @@ namespace SpineViewer.Dialogs
DialogResult = DialogResult.Cancel; DialogResult = DialogResult.Cancel;
} }
} }
public class OpenSpineDialogResult(Spine.Version version, string skelPath, string? atlasPath = null)
{
public Spine.Version Version { get; } = version;
public string SkelPath { get; } = skelPath;
public string? AtlasPath { get; } = atlasPath;
}
} }

View File

@@ -95,7 +95,7 @@
menuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_File, toolStripMenuItem_Function, toolStripMenuItem_Tool, toolStripMenuItem_Download, toolStripMenuItem_Help }); menuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_File, toolStripMenuItem_Function, toolStripMenuItem_Tool, toolStripMenuItem_Download, toolStripMenuItem_Help });
menuStrip.Location = new Point(0, 0); menuStrip.Location = new Point(0, 0);
menuStrip.Name = "menuStrip"; menuStrip.Name = "menuStrip";
menuStrip.Size = new Size(1741, 32); menuStrip.Size = new Size(1748, 32);
menuStrip.TabIndex = 0; menuStrip.TabIndex = 0;
menuStrip.Text = "菜单"; menuStrip.Text = "菜单";
// //
@@ -158,8 +158,8 @@
// //
toolStripMenuItem_Function.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_ResetAnimation }); toolStripMenuItem_Function.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_ResetAnimation });
toolStripMenuItem_Function.Name = "toolStripMenuItem_Function"; toolStripMenuItem_Function.Name = "toolStripMenuItem_Function";
toolStripMenuItem_Function.Size = new Size(84, 28); toolStripMenuItem_Function.Size = new Size(87, 28);
toolStripMenuItem_Function.Text = "功能(&F)"; toolStripMenuItem_Function.Text = "功能(&G)";
// //
// toolStripMenuItem_ResetAnimation // toolStripMenuItem_ResetAnimation
// //
@@ -232,7 +232,7 @@
rtbLog.Margin = new Padding(3, 2, 3, 2); rtbLog.Margin = new Padding(3, 2, 3, 2);
rtbLog.Name = "rtbLog"; rtbLog.Name = "rtbLog";
rtbLog.ReadOnly = true; rtbLog.ReadOnly = true;
rtbLog.Size = new Size(1721, 106); rtbLog.Size = new Size(1728, 114);
rtbLog.TabIndex = 0; rtbLog.TabIndex = 0;
rtbLog.Text = ""; rtbLog.Text = "";
rtbLog.WordWrap = false; rtbLog.WordWrap = false;
@@ -254,8 +254,8 @@
// //
splitContainer_MainForm.Panel2.Controls.Add(rtbLog); splitContainer_MainForm.Panel2.Controls.Add(rtbLog);
splitContainer_MainForm.Panel2.Cursor = Cursors.Default; splitContainer_MainForm.Panel2.Cursor = Cursors.Default;
splitContainer_MainForm.Size = new Size(1721, 958); splitContainer_MainForm.Size = new Size(1728, 997);
splitContainer_MainForm.SplitterDistance = 848; splitContainer_MainForm.SplitterDistance = 879;
splitContainer_MainForm.TabIndex = 3; splitContainer_MainForm.TabIndex = 3;
splitContainer_MainForm.TabStop = false; splitContainer_MainForm.TabStop = false;
splitContainer_MainForm.SplitterMoved += splitContainer_SplitterMoved; splitContainer_MainForm.SplitterMoved += splitContainer_SplitterMoved;
@@ -277,8 +277,8 @@
// //
splitContainer_Functional.Panel2.Controls.Add(groupBox_Preview); splitContainer_Functional.Panel2.Controls.Add(groupBox_Preview);
splitContainer_Functional.Panel2.Cursor = Cursors.Default; splitContainer_Functional.Panel2.Cursor = Cursors.Default;
splitContainer_Functional.Size = new Size(1721, 848); splitContainer_Functional.Size = new Size(1728, 879);
splitContainer_Functional.SplitterDistance = 744; splitContainer_Functional.SplitterDistance = 747;
splitContainer_Functional.TabIndex = 2; splitContainer_Functional.TabIndex = 2;
splitContainer_Functional.TabStop = false; splitContainer_Functional.TabStop = false;
splitContainer_Functional.SplitterMoved += splitContainer_SplitterMoved; splitContainer_Functional.SplitterMoved += splitContainer_SplitterMoved;
@@ -300,8 +300,8 @@
// //
splitContainer_Information.Panel2.Controls.Add(splitContainer_Config); splitContainer_Information.Panel2.Controls.Add(splitContainer_Config);
splitContainer_Information.Panel2.Cursor = Cursors.Default; splitContainer_Information.Panel2.Cursor = Cursors.Default;
splitContainer_Information.Size = new Size(744, 848); splitContainer_Information.Size = new Size(747, 879);
splitContainer_Information.SplitterDistance = 398; splitContainer_Information.SplitterDistance = 399;
splitContainer_Information.TabIndex = 1; splitContainer_Information.TabIndex = 1;
splitContainer_Information.TabStop = false; splitContainer_Information.TabStop = false;
splitContainer_Information.SplitterMoved += splitContainer_SplitterMoved; splitContainer_Information.SplitterMoved += splitContainer_SplitterMoved;
@@ -313,7 +313,7 @@
groupBox_SkelList.Dock = DockStyle.Fill; groupBox_SkelList.Dock = DockStyle.Fill;
groupBox_SkelList.Location = new Point(0, 0); groupBox_SkelList.Location = new Point(0, 0);
groupBox_SkelList.Name = "groupBox_SkelList"; groupBox_SkelList.Name = "groupBox_SkelList";
groupBox_SkelList.Size = new Size(398, 848); groupBox_SkelList.Size = new Size(399, 879);
groupBox_SkelList.TabIndex = 0; groupBox_SkelList.TabIndex = 0;
groupBox_SkelList.TabStop = false; groupBox_SkelList.TabStop = false;
groupBox_SkelList.Text = "模型列表"; groupBox_SkelList.Text = "模型列表";
@@ -324,7 +324,7 @@
spineListView.Location = new Point(3, 26); spineListView.Location = new Point(3, 26);
spineListView.Name = "spineListView"; spineListView.Name = "spineListView";
spineListView.PropertyGrid = propertyGrid_Spine; spineListView.PropertyGrid = propertyGrid_Spine;
spineListView.Size = new Size(392, 819); spineListView.Size = new Size(393, 850);
spineListView.TabIndex = 0; spineListView.TabIndex = 0;
// //
// propertyGrid_Spine // propertyGrid_Spine
@@ -333,7 +333,7 @@
propertyGrid_Spine.HelpVisible = false; propertyGrid_Spine.HelpVisible = false;
propertyGrid_Spine.Location = new Point(3, 26); propertyGrid_Spine.Location = new Point(3, 26);
propertyGrid_Spine.Name = "propertyGrid_Spine"; propertyGrid_Spine.Name = "propertyGrid_Spine";
propertyGrid_Spine.Size = new Size(336, 470); propertyGrid_Spine.Size = new Size(338, 485);
propertyGrid_Spine.TabIndex = 0; propertyGrid_Spine.TabIndex = 0;
propertyGrid_Spine.ToolbarVisible = false; propertyGrid_Spine.ToolbarVisible = false;
propertyGrid_Spine.PropertyValueChanged += propertyGrid_PropertyValueChanged; propertyGrid_Spine.PropertyValueChanged += propertyGrid_PropertyValueChanged;
@@ -355,8 +355,8 @@
// //
splitContainer_Config.Panel2.Controls.Add(groupBox_PreviewConfig); splitContainer_Config.Panel2.Controls.Add(groupBox_PreviewConfig);
splitContainer_Config.Panel2.Cursor = Cursors.Default; splitContainer_Config.Panel2.Cursor = Cursors.Default;
splitContainer_Config.Size = new Size(342, 848); splitContainer_Config.Size = new Size(344, 879);
splitContainer_Config.SplitterDistance = 499; splitContainer_Config.SplitterDistance = 514;
splitContainer_Config.TabIndex = 0; splitContainer_Config.TabIndex = 0;
splitContainer_Config.TabStop = false; splitContainer_Config.TabStop = false;
splitContainer_Config.SplitterMoved += splitContainer_SplitterMoved; splitContainer_Config.SplitterMoved += splitContainer_SplitterMoved;
@@ -368,7 +368,7 @@
groupBox_SkelConfig.Dock = DockStyle.Fill; groupBox_SkelConfig.Dock = DockStyle.Fill;
groupBox_SkelConfig.Location = new Point(0, 0); groupBox_SkelConfig.Location = new Point(0, 0);
groupBox_SkelConfig.Name = "groupBox_SkelConfig"; groupBox_SkelConfig.Name = "groupBox_SkelConfig";
groupBox_SkelConfig.Size = new Size(342, 499); groupBox_SkelConfig.Size = new Size(344, 514);
groupBox_SkelConfig.TabIndex = 0; groupBox_SkelConfig.TabIndex = 0;
groupBox_SkelConfig.TabStop = false; groupBox_SkelConfig.TabStop = false;
groupBox_SkelConfig.Text = "模型参数"; groupBox_SkelConfig.Text = "模型参数";
@@ -379,7 +379,7 @@
groupBox_PreviewConfig.Dock = DockStyle.Fill; groupBox_PreviewConfig.Dock = DockStyle.Fill;
groupBox_PreviewConfig.Location = new Point(0, 0); groupBox_PreviewConfig.Location = new Point(0, 0);
groupBox_PreviewConfig.Name = "groupBox_PreviewConfig"; groupBox_PreviewConfig.Name = "groupBox_PreviewConfig";
groupBox_PreviewConfig.Size = new Size(342, 345); groupBox_PreviewConfig.Size = new Size(344, 361);
groupBox_PreviewConfig.TabIndex = 1; groupBox_PreviewConfig.TabIndex = 1;
groupBox_PreviewConfig.TabStop = false; groupBox_PreviewConfig.TabStop = false;
groupBox_PreviewConfig.Text = "画面参数"; groupBox_PreviewConfig.Text = "画面参数";
@@ -390,7 +390,7 @@
propertyGrid_Previewer.HelpVisible = false; propertyGrid_Previewer.HelpVisible = false;
propertyGrid_Previewer.Location = new Point(3, 26); propertyGrid_Previewer.Location = new Point(3, 26);
propertyGrid_Previewer.Name = "propertyGrid_Previewer"; propertyGrid_Previewer.Name = "propertyGrid_Previewer";
propertyGrid_Previewer.Size = new Size(336, 316); propertyGrid_Previewer.Size = new Size(338, 332);
propertyGrid_Previewer.TabIndex = 1; propertyGrid_Previewer.TabIndex = 1;
propertyGrid_Previewer.ToolbarVisible = false; propertyGrid_Previewer.ToolbarVisible = false;
propertyGrid_Previewer.PropertyValueChanged += propertyGrid_PropertyValueChanged; propertyGrid_Previewer.PropertyValueChanged += propertyGrid_PropertyValueChanged;
@@ -401,7 +401,7 @@
groupBox_Preview.Dock = DockStyle.Fill; groupBox_Preview.Dock = DockStyle.Fill;
groupBox_Preview.Location = new Point(0, 0); groupBox_Preview.Location = new Point(0, 0);
groupBox_Preview.Name = "groupBox_Preview"; groupBox_Preview.Name = "groupBox_Preview";
groupBox_Preview.Size = new Size(973, 848); groupBox_Preview.Size = new Size(977, 879);
groupBox_Preview.TabIndex = 1; groupBox_Preview.TabIndex = 1;
groupBox_Preview.TabStop = false; groupBox_Preview.TabStop = false;
groupBox_Preview.Text = "预览画面"; groupBox_Preview.Text = "预览画面";
@@ -413,7 +413,7 @@
spinePreviewer.Location = new Point(3, 26); spinePreviewer.Location = new Point(3, 26);
spinePreviewer.Name = "spinePreviewer"; spinePreviewer.Name = "spinePreviewer";
spinePreviewer.PropertyGrid = propertyGrid_Previewer; spinePreviewer.PropertyGrid = propertyGrid_Previewer;
spinePreviewer.Size = new Size(967, 819); spinePreviewer.Size = new Size(971, 850);
spinePreviewer.SpineListView = spineListView; spinePreviewer.SpineListView = spineListView;
spinePreviewer.TabIndex = 0; spinePreviewer.TabIndex = 0;
spinePreviewer.MouseUp += spinePreviewer_MouseUp; spinePreviewer.MouseUp += spinePreviewer_MouseUp;
@@ -425,7 +425,7 @@
panel_MainForm.Location = new Point(0, 32); panel_MainForm.Location = new Point(0, 32);
panel_MainForm.Name = "panel_MainForm"; panel_MainForm.Name = "panel_MainForm";
panel_MainForm.Padding = new Padding(10, 5, 10, 10); panel_MainForm.Padding = new Padding(10, 5, 10, 10);
panel_MainForm.Size = new Size(1741, 973); panel_MainForm.Size = new Size(1748, 1012);
panel_MainForm.TabIndex = 4; panel_MainForm.TabIndex = 4;
// //
// toolTip // toolTip
@@ -436,7 +436,7 @@
// //
AutoScaleDimensions = new SizeF(11F, 24F); AutoScaleDimensions = new SizeF(11F, 24F);
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1741, 1005); ClientSize = new Size(1748, 1044);
Controls.Add(panel_MainForm); Controls.Add(panel_MainForm);
Controls.Add(menuStrip); Controls.Add(menuStrip);
Icon = (Icon)resources.GetObject("$this.Icon"); Icon = (Icon)resources.GetObject("$this.Icon");

View File

@@ -5,6 +5,8 @@ namespace SpineViewer
{ {
internal static class Program internal static class Program
{ {
public const string Name = "SpineViewer";
public static readonly string TempDir = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Name)).FullName;
public static readonly Process Process = Process.GetCurrentProcess(); public static readonly Process Process = Process.GetCurrentProcess();
public static readonly Logger Logger = LogManager.GetCurrentClassLogger(); public static readonly Logger Logger = LogManager.GetCurrentClassLogger();

View File

@@ -14,7 +14,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
[SkeletonConverterImplementation(Version.V38)] [SkeletonConverterImplementation(Version.V38)]
class SkeletonConverter38 : SpineViewer.Spine.SkeletonConverter class SkeletonConverter38 : SpineViewer.Spine.SkeletonConverter
{ {
private SkeletonReader reader = null; private BinaryReader reader = null;
private JsonObject root = null; private JsonObject root = null;
private bool nonessential = false; private bool nonessential = false;
@@ -51,7 +51,9 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
{ {
JsonObject skeleton = []; JsonObject skeleton = [];
skeleton["hash"] = reader.ReadString(); skeleton["hash"] = reader.ReadString();
skeleton["spine"] = reader.ReadString(); var version = reader.ReadString();
if (version == "3.8.75") version = "3.8.76"; // replace 3.8.75 to another version to avoid detection in official runtime
skeleton["spine"] = version;
skeleton["x"] = reader.ReadFloat(); skeleton["x"] = reader.ReadFloat();
skeleton["y"] = reader.ReadFloat(); skeleton["y"] = reader.ReadFloat();
skeleton["width"] = reader.ReadFloat(); skeleton["width"] = reader.ReadFloat();
@@ -332,7 +334,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
if (nonessential) reader.ReadInt(); if (nonessential) reader.ReadInt();
break; break;
default: default:
throw new ArgumentException($"Invalid attachment type: {type}"); throw new ArgumentOutOfRangeException($"Invalid attachment type: {type}");
} }
return attachment; return attachment;
} }
@@ -435,7 +437,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
} }
break; break;
default: default:
throw new ArgumentException($"Invalid slot timeline type: {type}"); throw new ArgumentOutOfRangeException($"Invalid slot timeline type: {type}");
} }
} }
} }
@@ -515,7 +517,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
} }
break; break;
default: default:
throw new ArgumentException($"Invalid bone timeline type: {type}"); throw new ArgumentOutOfRangeException($"Invalid bone timeline type: {type}");
} }
} }
} }
@@ -633,7 +635,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
} }
break; break;
default: default:
throw new ArgumentException($"Invalid path timeline type: {type}"); throw new ArgumentOutOfRangeException($"Invalid path timeline type: {type}");
} }
} }
} }
@@ -798,11 +800,11 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
obj["c4"] = reader.ReadFloat(); obj["c4"] = reader.ReadFloat();
break; break;
default: default:
throw new ArgumentException($"Invalid curve type: {type}"); ; throw new ArgumentOutOfRangeException($"Invalid curve type: {type}"); ;
} }
} }
private SkeletonWriter writer; private BinaryWriter writer;
private readonly Dictionary<string, int> bone2idx = []; private readonly Dictionary<string, int> bone2idx = [];
private readonly Dictionary<string, int> slot2idx = []; private readonly Dictionary<string, int> slot2idx = [];
private readonly Dictionary<string, int> ik2idx = []; private readonly Dictionary<string, int> ik2idx = [];
@@ -842,7 +844,9 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
{ {
JsonObject skeleton = root["skeleton"].AsObject(); JsonObject skeleton = root["skeleton"].AsObject();
writer.WriteString((string)skeleton["hash"]); writer.WriteString((string)skeleton["hash"]);
writer.WriteString((string)skeleton["spine"]); var version = (string)skeleton["spine"];
if (version == "3.8.75") version = "3.8.76"; // replace 3.8.75 to another version to avoid detection in official runtime
writer.WriteString(version);
if (skeleton.TryGetPropertyValue("x", out var x)) writer.WriteFloat((float)x); else writer.WriteFloat(0); if (skeleton.TryGetPropertyValue("x", out var x)) writer.WriteFloat((float)x); else writer.WriteFloat(0);
if (skeleton.TryGetPropertyValue("y", out var y)) writer.WriteFloat((float)y); else writer.WriteFloat(0); if (skeleton.TryGetPropertyValue("y", out var y)) writer.WriteFloat((float)y); else writer.WriteFloat(0);
if (skeleton.TryGetPropertyValue("width", out var width)) writer.WriteFloat((float)width); else writer.WriteFloat(0); if (skeleton.TryGetPropertyValue("width", out var width)) writer.WriteFloat((float)width); else writer.WriteFloat(0);
@@ -1010,6 +1014,18 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
private void WriteSkins() private void WriteSkins()
{ {
//JsonArray skins = [];
//// default skin
//if (ReadSkin(true) is JsonObject data)
// skins.Add(data);
//// other skins
//for (int n = reader.ReadVarInt(); n > 0; n--)
// skins.Add(ReadSkin());
//root["skins"] = skins;
if (!root.ContainsKey("skins")) if (!root.ContainsKey("skins"))
{ {
writer.WriteVarInt(0); writer.WriteVarInt(0);
@@ -1023,6 +1039,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
} }
} }
private void WriteEvents() private void WriteEvents()
{ {
if (!root.ContainsKey("events")) if (!root.ContainsKey("events"))

View File

@@ -71,7 +71,7 @@ namespace SpineViewer.Spine.Implementations.Spine
catch catch
{ {
// 都不行就报错 // 都不行就报错
throw new ArgumentException($"Unknown skeleton file format {SkelPath}"); throw new InvalidDataException($"Unknown skeleton file format {SkelPath}");
} }
} }

View File

@@ -70,7 +70,7 @@ namespace SpineViewer.Spine.Implementations.Spine
catch catch
{ {
// 都不行就报错 // 都不行就报错
throw new ArgumentException($"Unknown skeleton file format {SkelPath}"); throw new InvalidDataException($"Unknown skeleton file format {SkelPath}");
} }
} }

View File

@@ -68,7 +68,7 @@ namespace SpineViewer.Spine.Implementations.Spine
catch catch
{ {
// 都不行就报错 // 都不行就报错
throw new ArgumentException($"Unknown skeleton file format {SkelPath}"); throw new InvalidDataException($"Unknown skeleton file format {SkelPath}");
} }
} }

View File

@@ -71,7 +71,7 @@ namespace SpineViewer.Spine.Implementations.Spine
catch catch
{ {
// 都不行就报错 // 都不行就报错
throw new ArgumentException($"Unknown skeleton file format {SkelPath}"); throw new InvalidDataException($"Unknown skeleton file format {SkelPath}");
} }
} }

View File

@@ -70,7 +70,7 @@ namespace SpineViewer.Spine.Implementations.Spine
catch catch
{ {
// 都不行就报错 // 都不行就报错
throw new ArgumentException($"Unknown skeleton file format {SkelPath}"); throw new InvalidDataException($"Unknown skeleton file format {SkelPath}");
} }
} }

View File

@@ -70,7 +70,7 @@ namespace SpineViewer.Spine.Implementations.Spine
catch catch
{ {
// 都不行就报错 // 都不行就报错
throw new ArgumentException($"Unknown skeleton file format {SkelPath}"); throw new InvalidDataException($"Unknown skeleton file format {SkelPath}");
} }
} }

View File

@@ -70,7 +70,7 @@ namespace SpineViewer.Spine.Implementations.Spine
catch catch
{ {
// 都不行就报错 // 都不行就报错
throw new ArgumentException($"Unknown skeleton file format {SkelPath}"); throw new InvalidDataException($"Unknown skeleton file format {SkelPath}");
} }
} }

View File

@@ -98,7 +98,7 @@ namespace SpineViewer.Spine
if (JsonNode.Parse(input) is JsonObject root) if (JsonNode.Parse(input) is JsonObject root)
return root; return root;
else else
throw new InvalidOperationException($"{jsonPath} is not a valid json object"); throw new InvalidDataException($"{jsonPath} is not a valid json object");
} }
/// <summary> /// <summary>
@@ -116,14 +116,17 @@ namespace SpineViewer.Spine
/// </summary> /// </summary>
public abstract JsonObject ToVersion(JsonObject root, Version version); public abstract JsonObject ToVersion(JsonObject root, Version version);
protected class SkeletonReader /// <summary>
/// 二进制骨骼文件读
/// </summary>
public class BinaryReader
{ {
protected byte[] buffer = new byte[32]; protected byte[] buffer = new byte[32];
protected byte[] bytesBigEndian = new byte[8]; protected byte[] bytesBigEndian = new byte[8];
public readonly List<string> StringTable = new(32); public readonly List<string> StringTable = new(32);
protected Stream input; protected Stream input;
public SkeletonReader(Stream input) { this.input = input; } public BinaryReader(Stream input) { this.input = input; }
public int Read() public int Read()
{ {
int val = input.ReadByte(); int val = input.ReadByte();
@@ -219,14 +222,17 @@ namespace SpineViewer.Spine
} }
} }
protected class SkeletonWriter /// <summary>
/// 二进制骨骼文件写
/// </summary>
protected class BinaryWriter
{ {
protected byte[] buffer = new byte[32]; protected byte[] buffer = new byte[32];
protected byte[] bytesBigEndian = new byte[8]; protected byte[] bytesBigEndian = new byte[8];
public readonly List<string> StringTable = new(32); public readonly List<string> StringTable = new(32);
protected Stream output; protected Stream output;
public SkeletonWriter(Stream output) { this.output = output; } public BinaryWriter(Stream output) { this.output = output; }
public void Write(int val) => output.WriteByte((byte)val); public void Write(int val) => output.WriteByte((byte)val);
public void WriteByte(byte val) => output.WriteByte(val); public void WriteByte(byte val) => output.WriteByte(val);
public void WriteUByte(byte val) => output.WriteByte(val); public void WriteUByte(byte val) => output.WriteByte(val);

View File

@@ -14,6 +14,7 @@ using System.ComponentModel;
using System.Reflection; using System.Reflection;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Text.Json.Nodes;
namespace SpineViewer.Spine namespace SpineViewer.Spine
{ {
@@ -106,11 +107,79 @@ namespace SpineViewer.Spine
} }
} }
/// <summary>
/// 尝试检测骨骼文件版本
/// </summary>
public static Version? GetVersion(string skelPath)
{
string versionString = null;
Version? version = null;
using var input = File.OpenRead(skelPath);
var reader = new SkeletonConverter.BinaryReader(input);
// try json format
try
{
if (JsonNode.Parse(input) is JsonObject root && root.TryGetPropertyValue("spine", out var node))
versionString = (string)node;
}
catch { }
// try v4 binary format
if (versionString is null)
{
try
{
input.Position = 0;
var hash = reader.ReadLong();
var versionPosition = input.Position;
var versionByteCount = reader.ReadVarInt();
input.Position = versionPosition;
if (versionByteCount <= 13)
versionString = reader.ReadString();
}
catch { }
}
// try v3 binary format
if (versionString is null)
{
try
{
input.Position = 0;
var hash = reader.ReadString();
versionString = reader.ReadString();
}
catch { }
}
if (versionString is not null)
{
if (versionString.StartsWith("2.1.")) version = Version.V21;
else if (versionString.StartsWith("3.6.")) version = Version.V36;
else if (versionString.StartsWith("3.7.")) version = Version.V37;
else if (versionString.StartsWith("3.8.")) version = Version.V38;
else if (versionString.StartsWith("4.0.")) version = Version.V40;
else if (versionString.StartsWith("4.1.")) version = Version.V41;
else if (versionString.StartsWith("4.2.")) version = Version.V42;
else if (versionString.StartsWith("4.3.")) version = Version.V43;
}
return version;
}
/// <summary> /// <summary>
/// 创建特定版本的 Spine /// 创建特定版本的 Spine
/// </summary> /// </summary>
public static Spine New(Version version, string skelPath, string? atlasPath = null) public static Spine New(Version version, string skelPath, string? atlasPath = null)
{ {
if (version == Version.Auto)
{
if (GetVersion(skelPath) is Version detectedVersion)
version = detectedVersion;
else
throw new InvalidDataException($"Auto version detection failed for {skelPath}, try to use a specific version");
}
if (!ImplementationTypes.TryGetValue(version, out var spineType)) if (!ImplementationTypes.TryGetValue(version, out var spineType))
{ {
throw new NotImplementedException($"Not implemented version: {version}"); throw new NotImplementedException($"Not implemented version: {version}");
@@ -133,7 +202,7 @@ namespace SpineViewer.Spine
var attr = type.GetCustomAttribute<SpineImplementationAttribute>(); var attr = type.GetCustomAttribute<SpineImplementationAttribute>();
if (attr is null) if (attr is null)
{ {
throw new InvalidOperationException($"Class {type.Name} has no SpineImplementationAttribute."); throw new InvalidOperationException($"Class {type.Name} has no SpineImplementationAttribute");
} }
atlasPath ??= Path.ChangeExtension(skelPath, ".atlas"); atlasPath ??= Path.ChangeExtension(skelPath, ".atlas");

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -13,7 +14,8 @@ namespace SpineViewer.Spine
/// <summary> /// <summary>
/// 描述缓存 /// 描述缓存
/// </summary> /// </summary>
public static readonly Dictionary<Version, string> Versions = []; public static readonly ReadOnlyDictionary<Version, string> Versions;
private static readonly Dictionary<Version, string> versions = [];
static VersionHelper() static VersionHelper()
{ {
@@ -22,8 +24,9 @@ namespace SpineViewer.Spine
{ {
var field = typeof(Version).GetField(value.ToString()); var field = typeof(Version).GetField(value.ToString());
var attribute = field?.GetCustomAttribute<DescriptionAttribute>(); var attribute = field?.GetCustomAttribute<DescriptionAttribute>();
Versions[(Version)value] = attribute?.Description ?? value.ToString(); versions[(Version)value] = attribute?.Description ?? value.ToString();
} }
Versions = versions.AsReadOnly();
} }
/// <summary> /// <summary>
@@ -40,6 +43,7 @@ namespace SpineViewer.Spine
/// </summary> /// </summary>
public enum Version public enum Version
{ {
[Description("<Auto>")] Auto = 0x0000,
[Description("2.1.x")] V21 = 0x0201, [Description("2.1.x")] V21 = 0x0201,
[Description("3.6.x")] V36 = 0x0306, [Description("3.6.x")] V36 = 0x0306,
[Description("3.7.x")] V37 = 0x0307, [Description("3.7.x")] V37 = 0x0307,

View File

@@ -8,7 +8,7 @@
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath> <BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>0.10.1</Version> <Version>0.10.4</Version>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>appicon.ico</ApplicationIcon> <ApplicationIcon>appicon.ico</ApplicationIcon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

BIN
img/preview.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB